@sladg/apex-state 3.11.4 → 4.0.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 +21 -35
- package/dist/index.d.ts +508 -303
- package/dist/index.js +1529 -1686
- package/dist/index.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +456 -658
- package/dist/testing/index.js.map +1 -1
- package/package.json +9 -4
package/dist/index.d.ts
CHANGED
|
@@ -284,6 +284,269 @@ type BoolLogic<STATE, Depth extends number = DefaultDepth> = {
|
|
|
284
284
|
CONTAINS_ALL: PathArrayElementsPair<STATE, Depth>;
|
|
285
285
|
} | PathValuePair<STATE, Depth>;
|
|
286
286
|
|
|
287
|
+
type Stage = "input" | "aggregation_write" | "diff" | "sync" | "flip" | "clear_path" | "aggregation_read" | "computation" | "bool_logic" | "value_logic" | "listeners" | "apply";
|
|
288
|
+
type ChangeKind = "real" | "redundant" | "breakdown" | "consumed";
|
|
289
|
+
type ChangeContext = "Initial" | "Mutation";
|
|
290
|
+
type Lineage = "Input" | {
|
|
291
|
+
"Derived": {
|
|
292
|
+
parent_id: number;
|
|
293
|
+
via: Stage;
|
|
294
|
+
context: ChangeContext;
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
type SkipReason = "wrong_kind" | "guard_failed" | "redundant" | "anchor_missing";
|
|
298
|
+
type SkippedChange = {
|
|
299
|
+
path: string;
|
|
300
|
+
kind: ChangeKind;
|
|
301
|
+
reason: SkipReason;
|
|
302
|
+
/**
|
|
303
|
+
* Human-readable explanation of why this change was skipped.
|
|
304
|
+
*/
|
|
305
|
+
detail: string;
|
|
306
|
+
/**
|
|
307
|
+
* Which registration owns the resource that was skipped (if applicable).
|
|
308
|
+
*/
|
|
309
|
+
registration_id: string | null;
|
|
310
|
+
/**
|
|
311
|
+
* Anchor path that caused the skip (if applicable).
|
|
312
|
+
*/
|
|
313
|
+
anchor_path: string | null;
|
|
314
|
+
};
|
|
315
|
+
type ProducedChange = {
|
|
316
|
+
path: string;
|
|
317
|
+
/**
|
|
318
|
+
* JSON-encoded value.
|
|
319
|
+
*/
|
|
320
|
+
value: string;
|
|
321
|
+
/**
|
|
322
|
+
* Which registration produced this change (if traceable).
|
|
323
|
+
*/
|
|
324
|
+
registration_id: string | null;
|
|
325
|
+
/**
|
|
326
|
+
* What input path caused this (e.g. sync source path).
|
|
327
|
+
*/
|
|
328
|
+
source_path: string | null;
|
|
329
|
+
};
|
|
330
|
+
type ChangeAudit = {
|
|
331
|
+
source_path: string | null;
|
|
332
|
+
logic_id: number | null;
|
|
333
|
+
};
|
|
334
|
+
type Change$1 = {
|
|
335
|
+
path: string;
|
|
336
|
+
value_json: string;
|
|
337
|
+
/**
|
|
338
|
+
* Opaque JS meta object — carried through the pipeline unchanged.
|
|
339
|
+
* WASM never inspects the contents. Returned on listener_changes so JS
|
|
340
|
+
* meta survives the boundary without any stringify/parse.
|
|
341
|
+
*/
|
|
342
|
+
meta: unknown | null;
|
|
343
|
+
/**
|
|
344
|
+
* Coarse classification: Real | Redundant | Breakdown | Consumed.
|
|
345
|
+
*/
|
|
346
|
+
kind: ChangeKind;
|
|
347
|
+
/**
|
|
348
|
+
* Causal chain. Always set. Carries ChangeContext for stage routing.
|
|
349
|
+
*/
|
|
350
|
+
lineage: Lineage;
|
|
351
|
+
/**
|
|
352
|
+
* Debug-mode only. None in production.
|
|
353
|
+
*/
|
|
354
|
+
audit: ChangeAudit | null;
|
|
355
|
+
};
|
|
356
|
+
type StageTrace = {
|
|
357
|
+
stage: Stage;
|
|
358
|
+
duration_us: number;
|
|
359
|
+
/**
|
|
360
|
+
* Paths (with JSON-encoded values) that this stage processed.
|
|
361
|
+
*/
|
|
362
|
+
matched: Array<[string, string]>;
|
|
363
|
+
skipped: Array<SkippedChange>;
|
|
364
|
+
/**
|
|
365
|
+
* Changes produced by this stage.
|
|
366
|
+
*/
|
|
367
|
+
produced: Array<ProducedChange>;
|
|
368
|
+
followup: Array<StageTrace>;
|
|
369
|
+
};
|
|
370
|
+
type PipelineTrace = {
|
|
371
|
+
total_duration_us: number;
|
|
372
|
+
stages: Array<StageTrace>;
|
|
373
|
+
/**
|
|
374
|
+
* Anchor path → present? Gives display layer full context on anchor-dependent resources.
|
|
375
|
+
*/
|
|
376
|
+
anchor_states: Record<string, boolean>;
|
|
377
|
+
};
|
|
378
|
+
type ValidatorDispatch = {
|
|
379
|
+
validator_id: number;
|
|
380
|
+
output_path: string;
|
|
381
|
+
dependency_values: Record<string, string>;
|
|
382
|
+
};
|
|
383
|
+
type GraphSnapshot = {
|
|
384
|
+
sync_pairs: Array<[string, string]>;
|
|
385
|
+
directed_sync_pairs: Array<[string, string]>;
|
|
386
|
+
flip_pairs: Array<[string, string]>;
|
|
387
|
+
listeners: Array<{
|
|
388
|
+
id: number;
|
|
389
|
+
topic: string;
|
|
390
|
+
scope: string;
|
|
391
|
+
}>;
|
|
392
|
+
bool_logics: Array<{
|
|
393
|
+
id: number;
|
|
394
|
+
output_path: string;
|
|
395
|
+
definition: {
|
|
396
|
+
"IS_EQUAL": [string, unknown];
|
|
397
|
+
} | {
|
|
398
|
+
"EXISTS": string;
|
|
399
|
+
} | {
|
|
400
|
+
"IS_EMPTY": string;
|
|
401
|
+
} | {
|
|
402
|
+
"AND": Array<BoolLogicNode>;
|
|
403
|
+
} | {
|
|
404
|
+
"OR": Array<BoolLogicNode>;
|
|
405
|
+
} | {
|
|
406
|
+
"NOT": BoolLogicNode;
|
|
407
|
+
} | {
|
|
408
|
+
"GT": [string, number];
|
|
409
|
+
} | {
|
|
410
|
+
"LT": [string, number];
|
|
411
|
+
} | {
|
|
412
|
+
"GTE": [string, number];
|
|
413
|
+
} | {
|
|
414
|
+
"LTE": [string, number];
|
|
415
|
+
} | {
|
|
416
|
+
"IN": [string, Array<unknown>];
|
|
417
|
+
} | {
|
|
418
|
+
"CONTAINS_ANY": [string, Array<unknown>];
|
|
419
|
+
} | {
|
|
420
|
+
"CONTAINS_ALL": [string, Array<unknown>];
|
|
421
|
+
};
|
|
422
|
+
}>;
|
|
423
|
+
value_logics: Array<{
|
|
424
|
+
id: number;
|
|
425
|
+
output_path: string;
|
|
426
|
+
}>;
|
|
427
|
+
aggregations: Array<{
|
|
428
|
+
target: string;
|
|
429
|
+
sources: Array<string>;
|
|
430
|
+
}>;
|
|
431
|
+
computations: Array<{
|
|
432
|
+
target: string;
|
|
433
|
+
op: string;
|
|
434
|
+
sources: Array<string>;
|
|
435
|
+
}>;
|
|
436
|
+
};
|
|
437
|
+
type SideEffectsResult$1 = {
|
|
438
|
+
sync_changes: Array<Change$1>;
|
|
439
|
+
aggregation_changes: Array<Change$1>;
|
|
440
|
+
computation_changes: Array<Change$1>;
|
|
441
|
+
registered_listener_ids: Array<number>;
|
|
442
|
+
};
|
|
443
|
+
type ListenerRegistration$1 = {
|
|
444
|
+
subscriber_id: number;
|
|
445
|
+
topic_paths: Array<string>;
|
|
446
|
+
scope_path: string;
|
|
447
|
+
anchor_path?: string;
|
|
448
|
+
};
|
|
449
|
+
type SideEffectsRegistration = {
|
|
450
|
+
registration_id: string;
|
|
451
|
+
sync_pairs: Array<[string, string]>;
|
|
452
|
+
/**
|
|
453
|
+
* One-way sync pairs: source → target only. Changes to target do NOT propagate back to source.
|
|
454
|
+
*/
|
|
455
|
+
directed_sync_pairs: Array<[string, string]>;
|
|
456
|
+
flip_pairs: Array<[string, string]>;
|
|
457
|
+
aggregation_pairs: Array<[string, string] | [string, string, string]>;
|
|
458
|
+
clear_paths: Array<{
|
|
459
|
+
triggers: Array<string>;
|
|
460
|
+
targets: Array<string>;
|
|
461
|
+
}>;
|
|
462
|
+
computation_pairs: Array<[string, string, string] | [string, string, string, string]>;
|
|
463
|
+
listeners: Array<ListenerRegistration$1>;
|
|
464
|
+
anchor_path?: string;
|
|
465
|
+
};
|
|
466
|
+
type BoolLogicRegistration = {
|
|
467
|
+
output_path: string;
|
|
468
|
+
tree_json: string;
|
|
469
|
+
};
|
|
470
|
+
type ValueLogicRegistration = {
|
|
471
|
+
output_path: string;
|
|
472
|
+
tree_json: string;
|
|
473
|
+
};
|
|
474
|
+
type ValidatorRegistration = {
|
|
475
|
+
validator_id: number;
|
|
476
|
+
output_path: string;
|
|
477
|
+
dependency_paths: Array<string>;
|
|
478
|
+
};
|
|
479
|
+
type ConcernsRegistration = {
|
|
480
|
+
/**
|
|
481
|
+
* Used by JS for unregister tracking; not consumed by Rust pipeline logic.
|
|
482
|
+
*/
|
|
483
|
+
registration_id: string;
|
|
484
|
+
bool_logics: Array<BoolLogicRegistration>;
|
|
485
|
+
validators: Array<ValidatorRegistration>;
|
|
486
|
+
value_logics: Array<ValueLogicRegistration>;
|
|
487
|
+
anchor_path?: string;
|
|
488
|
+
};
|
|
489
|
+
type FullExecutionPlan = {
|
|
490
|
+
/**
|
|
491
|
+
* Sequential groups of dispatches (flattened from depth levels + batches).
|
|
492
|
+
*/
|
|
493
|
+
groups: Array<{
|
|
494
|
+
dispatches: Array<{
|
|
495
|
+
dispatch_id: number;
|
|
496
|
+
subscriber_id: number;
|
|
497
|
+
scope_path: string;
|
|
498
|
+
topic_path: string;
|
|
499
|
+
/**
|
|
500
|
+
* Indexes into the PrepareResult.listener_changes array.
|
|
501
|
+
*/
|
|
502
|
+
input_change_ids: Array<number>;
|
|
503
|
+
}>;
|
|
504
|
+
}>;
|
|
505
|
+
/**
|
|
506
|
+
* propagation_map[dispatch_id] = targets to forward produced changes to.
|
|
507
|
+
* Flat array indexed by dispatch_id, empty vec = no propagation.
|
|
508
|
+
*/
|
|
509
|
+
propagation_map: Array<Array<{
|
|
510
|
+
target_dispatch_id: number;
|
|
511
|
+
/**
|
|
512
|
+
* Prefix to prepend to child's relative paths for the target's scope.
|
|
513
|
+
*/
|
|
514
|
+
remap_prefix: string;
|
|
515
|
+
}>>;
|
|
516
|
+
/**
|
|
517
|
+
* cascade_map[dispatch_id] = sibling dispatch_ids (same group, later position)
|
|
518
|
+
* that should receive this dispatch's produced changes.
|
|
519
|
+
*/
|
|
520
|
+
cascade_map: Array<Array<number>>;
|
|
521
|
+
};
|
|
522
|
+
type BoolLogicNode = {
|
|
523
|
+
"IS_EQUAL": [string, unknown];
|
|
524
|
+
} | {
|
|
525
|
+
"EXISTS": string;
|
|
526
|
+
} | {
|
|
527
|
+
"IS_EMPTY": string;
|
|
528
|
+
} | {
|
|
529
|
+
"AND": Array<BoolLogicNode>;
|
|
530
|
+
} | {
|
|
531
|
+
"OR": Array<BoolLogicNode>;
|
|
532
|
+
} | {
|
|
533
|
+
"NOT": BoolLogicNode;
|
|
534
|
+
} | {
|
|
535
|
+
"GT": [string, number];
|
|
536
|
+
} | {
|
|
537
|
+
"LT": [string, number];
|
|
538
|
+
} | {
|
|
539
|
+
"GTE": [string, number];
|
|
540
|
+
} | {
|
|
541
|
+
"LTE": [string, number];
|
|
542
|
+
} | {
|
|
543
|
+
"IN": [string, Array<unknown>];
|
|
544
|
+
} | {
|
|
545
|
+
"CONTAINS_ANY": [string, Array<unknown>];
|
|
546
|
+
} | {
|
|
547
|
+
"CONTAINS_ALL": [string, Array<unknown>];
|
|
548
|
+
};
|
|
549
|
+
|
|
287
550
|
/**
|
|
288
551
|
* GenericMeta interface
|
|
289
552
|
*
|
|
@@ -336,9 +599,14 @@ interface GenericMeta {
|
|
|
336
599
|
isComputationChange?: boolean;
|
|
337
600
|
/**
|
|
338
601
|
* Identifies the originator of the change.
|
|
339
|
-
* Can be a user ID, component name, or any identifier string.
|
|
602
|
+
* Can be a user ID, component name, listener function name, or any identifier string.
|
|
340
603
|
*/
|
|
341
604
|
sender?: string;
|
|
605
|
+
/**
|
|
606
|
+
* The pipeline stage that produced this change (e.g., 'sync', 'flip', 'aggregation_write', 'listeners').
|
|
607
|
+
* Used for debugging to understand the source of a change.
|
|
608
|
+
*/
|
|
609
|
+
stage?: string;
|
|
342
610
|
}
|
|
343
611
|
|
|
344
612
|
/**
|
|
@@ -371,6 +639,13 @@ interface GenericMeta {
|
|
|
371
639
|
type ArrayOfChanges<DATA, META extends GenericMeta = GenericMeta> = {
|
|
372
640
|
[K in DeepKey<DATA>]: [K, DeepValue<DATA, K>, META] | [K, DeepValue<DATA, K>];
|
|
373
641
|
}[DeepKey<DATA>][];
|
|
642
|
+
/** A single state change in internal pipeline form. */
|
|
643
|
+
interface Change {
|
|
644
|
+
path: string;
|
|
645
|
+
value: unknown;
|
|
646
|
+
/** Meta object threaded through the pipeline. Lineage is merged in by the bridge on WASM→JS conversion. */
|
|
647
|
+
meta: GenericMeta;
|
|
648
|
+
}
|
|
374
649
|
|
|
375
650
|
/**
|
|
376
651
|
* Concern type utilities
|
|
@@ -669,16 +944,28 @@ type PathsWithSameValueAs<DATA extends object, PATH extends ResolvableDeepKey<DA
|
|
|
669
944
|
}[ResolvableDeepKey<DATA, Depth>];
|
|
670
945
|
/**
|
|
671
946
|
* A tuple of two paths that must have the same value type.
|
|
672
|
-
* Format: [path1, path2]
|
|
947
|
+
* Format: [path1, path2] or [path1, path2, { oneWay: '[0]->[1]' | '[1]->[0]' }]
|
|
948
|
+
*
|
|
949
|
+
* `oneWay` makes sync unidirectional. The value declares the direction explicitly:
|
|
950
|
+
* - `'[0]->[1]'` — first path pushes to second path (left → right)
|
|
951
|
+
* - `'[1]->[0]'` — second path pushes to first path (right → left)
|
|
952
|
+
*
|
|
953
|
+
* Without the option, sync is bidirectional.
|
|
673
954
|
*
|
|
674
955
|
* **Scaling note**: This type is O(N²) where N = number of paths. It hits TS2589
|
|
675
956
|
* at ~1,500 paths. For large state types, use the `syncPairs()` helper function
|
|
676
957
|
* or `store.syncPairs()` (pre-warmed from `createGenericStore`) instead —
|
|
677
958
|
* they scale to ~50K–80K paths.
|
|
678
959
|
*
|
|
679
|
-
* @example
|
|
960
|
+
* @example Bidirectional
|
|
680
961
|
* const pair: SyncPair<State> = ['user.email', 'profile.email']
|
|
681
962
|
*
|
|
963
|
+
* @example One-way: user.email → profile.email only
|
|
964
|
+
* const pair: SyncPair<State> = ['user.email', 'profile.email', { oneWay: '[0]->[1]' }]
|
|
965
|
+
*
|
|
966
|
+
* @example One-way: profile.email → user.email only (reversed direction)
|
|
967
|
+
* const pair: SyncPair<State> = ['user.email', 'profile.email', { oneWay: '[1]->[0]' }]
|
|
968
|
+
*
|
|
682
969
|
* @example Custom depth — propagates to DeepKey and PathsWithSameValueAs
|
|
683
970
|
* ```typescript
|
|
684
971
|
* // Limit path traversal to 10 levels
|
|
@@ -686,13 +973,16 @@ type PathsWithSameValueAs<DATA extends object, PATH extends ResolvableDeepKey<DA
|
|
|
686
973
|
* ```
|
|
687
974
|
*/
|
|
688
975
|
type SyncPair<DATA extends object, Depth extends number = DefaultDepth> = {
|
|
689
|
-
[P1 in ResolvableDeepKey<DATA, Depth>]: [
|
|
976
|
+
[P1 in ResolvableDeepKey<DATA, Depth>]: [P1, PathsWithSameValueAs<DATA, P1, Depth>] | [
|
|
690
977
|
P1,
|
|
691
|
-
PathsWithSameValueAs<DATA, P1, Depth
|
|
978
|
+
PathsWithSameValueAs<DATA, P1, Depth>,
|
|
979
|
+
{
|
|
980
|
+
oneWay: '[0]->[1]' | '[1]->[0]';
|
|
981
|
+
}
|
|
692
982
|
];
|
|
693
983
|
}[ResolvableDeepKey<DATA, Depth>];
|
|
694
984
|
/**
|
|
695
|
-
* A tuple of two paths for flip (
|
|
985
|
+
* A tuple of two paths for flip. Always bidirectional: path1 ↔ path2 (inverted values).
|
|
696
986
|
* Format: [path1, path2]
|
|
697
987
|
*
|
|
698
988
|
* **Scaling note**: O(N²) — hits TS2589 at ~1,500 paths. Use `flipPairs()` helper
|
|
@@ -701,7 +991,12 @@ type SyncPair<DATA extends object, Depth extends number = DefaultDepth> = {
|
|
|
701
991
|
* @example
|
|
702
992
|
* const pair: FlipPair<State> = ['isActive', 'isInactive']
|
|
703
993
|
*/
|
|
704
|
-
type FlipPair<DATA extends object, Depth extends number = DefaultDepth> =
|
|
994
|
+
type FlipPair<DATA extends object, Depth extends number = DefaultDepth> = {
|
|
995
|
+
[P1 in ResolvableDeepKey<DATA, Depth>]: [
|
|
996
|
+
P1,
|
|
997
|
+
PathsWithSameValueAs<DATA, P1, Depth>
|
|
998
|
+
];
|
|
999
|
+
}[ResolvableDeepKey<DATA, Depth>];
|
|
705
1000
|
/**
|
|
706
1001
|
* A tuple for aggregation: [target, source] or [target, source, excludeWhen]
|
|
707
1002
|
* First element (left) is ALWAYS the target (aggregated) path.
|
|
@@ -779,13 +1074,24 @@ type CheckPairValueMatch<DATA extends object, P1 extends string, P2 extends stri
|
|
|
779
1074
|
NonNullable<DeepValue<DATA, P1>>
|
|
780
1075
|
] ? [P1, P2] : [P1, never] : [P1, never] : never : never;
|
|
781
1076
|
/**
|
|
782
|
-
* Validates an array of
|
|
1077
|
+
* Validates an array of sync/flip pairs lazily — O(K) where K = pairs written.
|
|
1078
|
+
* Accepts [P1, P2] or [P1, P2, { oneWay: '[0]->[1]' | '[1]->[0]' }].
|
|
1079
|
+
* The optional third element declares direction — does not affect value type validation.
|
|
783
1080
|
*/
|
|
784
|
-
type CheckSyncPairs<DATA extends object, T extends readonly [string, string][
|
|
1081
|
+
type CheckSyncPairs<DATA extends object, T extends readonly ([string, string] | [string, string, {
|
|
1082
|
+
oneWay: '[0]->[1]' | '[1]->[0]';
|
|
1083
|
+
}])[], Depth extends number = DefaultDepth> = {
|
|
785
1084
|
[I in keyof T]: T[I] extends [
|
|
786
1085
|
infer P1 extends string,
|
|
787
|
-
infer P2 extends string
|
|
788
|
-
|
|
1086
|
+
infer P2 extends string,
|
|
1087
|
+
{
|
|
1088
|
+
oneWay: '[0]->[1]' | '[1]->[0]';
|
|
1089
|
+
}
|
|
1090
|
+
] ? CheckPairValueMatch<DATA, P1, P2, Depth> extends [P1, P2] ? [P1, P2, {
|
|
1091
|
+
oneWay: '[0]->[1]' | '[1]->[0]';
|
|
1092
|
+
}] : [P1, never, {
|
|
1093
|
+
oneWay: '[0]->[1]' | '[1]->[0]';
|
|
1094
|
+
}] : T[I] extends [infer P1 extends string, infer P2 extends string] ? CheckPairValueMatch<DATA, P1, P2, Depth> : T[I];
|
|
789
1095
|
};
|
|
790
1096
|
/**
|
|
791
1097
|
* Validates that two paths both resolve to number.
|
|
@@ -851,10 +1157,9 @@ type DeepPartial<T> = T extends (infer U)[] ? DeepPartial<U>[] : T extends reado
|
|
|
851
1157
|
declare const VALIDATED: unique symbol;
|
|
852
1158
|
declare const STORE_DATA: unique symbol;
|
|
853
1159
|
/** Pre-validated sync pair array. Returned by `syncPairs()`. Branded with DATA to prevent cross-store use. */
|
|
854
|
-
type ValidatedSyncPairs<DATA extends object = object> = [
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
][] & {
|
|
1160
|
+
type ValidatedSyncPairs<DATA extends object = object> = ([string, string] | [string, string, {
|
|
1161
|
+
oneWay: '[0]->[1]' | '[1]->[0]';
|
|
1162
|
+
}])[] & {
|
|
858
1163
|
[VALIDATED]: 'sync';
|
|
859
1164
|
[STORE_DATA]: DATA;
|
|
860
1165
|
};
|
|
@@ -905,57 +1210,6 @@ interface ConcernRegistration {
|
|
|
905
1210
|
dispose: () => void;
|
|
906
1211
|
}
|
|
907
1212
|
|
|
908
|
-
/**
|
|
909
|
-
* Pipeline Observer — Unified debug logging + Redux DevTools integration.
|
|
910
|
-
*
|
|
911
|
-
* Single interface that dispatches to:
|
|
912
|
-
* - Console logging (controlled by logPipeline / logListeners / logConcerns flags)
|
|
913
|
-
* - Redux DevTools (controlled by devtools flag) with nested tree-shaped state
|
|
914
|
-
*
|
|
915
|
-
* Call sites use one `observer.xyz()` call; the observer fans out internally.
|
|
916
|
-
* Zero runtime cost when all flags are false (returns no-op object).
|
|
917
|
-
*/
|
|
918
|
-
|
|
919
|
-
interface PipelineObserver {
|
|
920
|
-
pipelineStart: (label: string, input: unknown[]) => void;
|
|
921
|
-
phase1: (stateChanges: unknown[]) => void;
|
|
922
|
-
syncExpand: (changes: unknown[]) => void;
|
|
923
|
-
flipExpand: (changes: unknown[]) => void;
|
|
924
|
-
listenerDispatch: (subscriberId: number, fnName: string, scope: string, input: unknown[], output: unknown) => void;
|
|
925
|
-
validatorResult: (outputPath: string, input: unknown, result: unknown) => void;
|
|
926
|
-
phase2: (stateChanges: unknown[]) => void;
|
|
927
|
-
pipelineEnd: () => void;
|
|
928
|
-
concernEval: (path: string, name: string, value: unknown, result: unknown) => void;
|
|
929
|
-
destroy: () => void;
|
|
930
|
-
}
|
|
931
|
-
interface DevToolsInstance {
|
|
932
|
-
init: (state: unknown) => void;
|
|
933
|
-
send: (action: {
|
|
934
|
-
type: string;
|
|
935
|
-
}, state: unknown) => void;
|
|
936
|
-
unsubscribe: () => void;
|
|
937
|
-
}
|
|
938
|
-
interface DevToolsRef {
|
|
939
|
-
prefix: string;
|
|
940
|
-
pipeline: DevToolsInstance | null;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
/**
|
|
944
|
-
* Debug Timing Utilities
|
|
945
|
-
*
|
|
946
|
-
* Provides timing measurement for concerns and listeners
|
|
947
|
-
* to detect slow operations during development.
|
|
948
|
-
*/
|
|
949
|
-
type TimingType = 'concerns' | 'listeners' | 'registration';
|
|
950
|
-
interface TimingMeta {
|
|
951
|
-
path: string;
|
|
952
|
-
name: string;
|
|
953
|
-
}
|
|
954
|
-
interface Timing {
|
|
955
|
-
run: <T>(type: TimingType, fn: () => T, meta: TimingMeta) => T;
|
|
956
|
-
reportBatch: (type: TimingType) => void;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
1213
|
/**
|
|
960
1214
|
* WASM Bridge — Thin namespace over Rust/WASM exports.
|
|
961
1215
|
*
|
|
@@ -970,211 +1224,132 @@ interface Timing {
|
|
|
970
1224
|
* @module wasm/bridge
|
|
971
1225
|
*/
|
|
972
1226
|
|
|
973
|
-
/**
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
/**
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
interface DispatchGroup {
|
|
990
|
-
dispatches: DispatchEntry[];
|
|
991
|
-
}
|
|
992
|
-
/** A target for propagating produced changes from child to parent dispatch. */
|
|
993
|
-
interface PropagationTarget {
|
|
994
|
-
target_dispatch_id: number;
|
|
995
|
-
/** Prefix to prepend to child's relative paths for the target's scope. */
|
|
996
|
-
remap_prefix: string;
|
|
997
|
-
}
|
|
998
|
-
/** Pre-computed execution plan with propagation map. */
|
|
999
|
-
interface FullExecutionPlan {
|
|
1000
|
-
groups: DispatchGroup[];
|
|
1001
|
-
/** propagation_map[dispatch_id] = targets to forward produced changes to. */
|
|
1002
|
-
propagation_map: PropagationTarget[][];
|
|
1003
|
-
}
|
|
1004
|
-
/** Validator dispatch info for JS-side execution. */
|
|
1005
|
-
interface ValidatorDispatch {
|
|
1006
|
-
validator_id: number;
|
|
1007
|
-
output_path: string;
|
|
1008
|
-
dependency_values: Record<string, string>;
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Extends a base tuple with an optional trailing BoolLogic condition (JSON-serialized).
|
|
1012
|
-
* Used by aggregation and computation pairs to share the "optional excludeWhen" pattern.
|
|
1013
|
-
*/
|
|
1014
|
-
type WasmPairWithCondition<Base extends [...string[]]> = [...Base] | [...Base, condition: string];
|
|
1015
|
-
/** WASM-side aggregation pair: [target, source] with optional excludeWhen condition. */
|
|
1016
|
-
type WasmAggregationPair = WasmPairWithCondition<[
|
|
1017
|
-
target: string,
|
|
1018
|
-
source: string
|
|
1019
|
-
]>;
|
|
1020
|
-
/** WASM-side computation pair: [operation, target, source] with optional excludeWhen condition. */
|
|
1021
|
-
type WasmComputationPair = WasmPairWithCondition<[
|
|
1022
|
-
op: string,
|
|
1023
|
-
target: string,
|
|
1024
|
-
source: string
|
|
1025
|
-
]>;
|
|
1026
|
-
/** WASM-side sync pair: [source, target] — bidirectional sync. */
|
|
1027
|
-
type WasmSyncPair = [source: string, target: string];
|
|
1028
|
-
/** WASM-side flip pair: [source, target] — inverted boolean sync. */
|
|
1029
|
-
type WasmFlipPair = [source: string, target: string];
|
|
1030
|
-
/** WASM-side clear path rule: trigger paths → target paths to null. */
|
|
1031
|
-
interface WasmClearPathRule {
|
|
1032
|
-
triggers: string[];
|
|
1033
|
-
targets: string[];
|
|
1034
|
-
}
|
|
1035
|
-
/** WASM-side listener entry for topic-based dispatch. */
|
|
1036
|
-
interface WasmListenerEntry {
|
|
1037
|
-
subscriber_id: number;
|
|
1038
|
-
topic_path: string;
|
|
1039
|
-
scope_path: string;
|
|
1040
|
-
}
|
|
1041
|
-
/** Consolidated registration input for side effects (sync, flip, aggregation, computation, clear, listeners). */
|
|
1042
|
-
interface SideEffectsRegistration {
|
|
1043
|
-
registration_id: string;
|
|
1044
|
-
sync_pairs?: WasmSyncPair[];
|
|
1045
|
-
flip_pairs?: WasmFlipPair[];
|
|
1046
|
-
aggregation_pairs?: WasmAggregationPair[];
|
|
1047
|
-
computation_pairs?: WasmComputationPair[];
|
|
1048
|
-
clear_paths?: WasmClearPathRule[];
|
|
1049
|
-
listeners?: WasmListenerEntry[];
|
|
1050
|
-
}
|
|
1051
|
-
/** Consolidated registration output from side effects registration. */
|
|
1052
|
-
interface SideEffectsResult {
|
|
1053
|
-
sync_changes: Change[];
|
|
1054
|
-
aggregation_changes: Change[];
|
|
1055
|
-
computation_changes: Change[];
|
|
1056
|
-
registered_listener_ids: number[];
|
|
1057
|
-
}
|
|
1058
|
-
/** WASM-side BoolLogic entry for declarative boolean evaluation. */
|
|
1059
|
-
interface WasmBoolLogicEntry {
|
|
1060
|
-
output_path: string;
|
|
1061
|
-
tree_json: string;
|
|
1062
|
-
}
|
|
1063
|
-
/** WASM-side validator entry for validation orchestration. */
|
|
1064
|
-
interface WasmValidatorEntry {
|
|
1065
|
-
validator_id: number;
|
|
1066
|
-
output_path: string;
|
|
1067
|
-
dependency_paths: string[];
|
|
1068
|
-
scope: string;
|
|
1069
|
-
}
|
|
1070
|
-
/** WASM-side ValueLogic entry for declarative value computation. */
|
|
1071
|
-
interface WasmValueLogicEntry {
|
|
1072
|
-
output_path: string;
|
|
1073
|
-
tree_json: string;
|
|
1074
|
-
}
|
|
1075
|
-
/** Consolidated registration input for concerns (BoolLogic, validators, and ValueLogic). */
|
|
1076
|
-
interface ConcernsRegistration {
|
|
1077
|
-
registration_id: string;
|
|
1078
|
-
bool_logics?: WasmBoolLogicEntry[];
|
|
1079
|
-
validators?: WasmValidatorEntry[];
|
|
1080
|
-
value_logics?: WasmValueLogicEntry[];
|
|
1081
|
-
}
|
|
1082
|
-
/** Consolidated registration output from concerns registration. */
|
|
1083
|
-
interface ConcernsResult {
|
|
1084
|
-
bool_logic_changes: Change[];
|
|
1085
|
-
registered_logic_ids: number[];
|
|
1086
|
-
registered_validator_ids: number[];
|
|
1087
|
-
value_logic_changes: Change[];
|
|
1088
|
-
registered_value_logic_ids: number[];
|
|
1089
|
-
}
|
|
1090
|
-
/** Result of processChanges (Phase 1). */
|
|
1091
|
-
interface ProcessChangesResult {
|
|
1092
|
-
state_changes: Change[];
|
|
1093
|
-
changes: Change[];
|
|
1094
|
-
validators_to_run: ValidatorDispatch[];
|
|
1095
|
-
execution_plan: FullExecutionPlan | null;
|
|
1096
|
-
has_work: boolean;
|
|
1097
|
-
}
|
|
1098
|
-
/**
|
|
1099
|
-
* An isolated WASM pipeline instance.
|
|
1100
|
-
* Each store gets its own pipeline so multiple Providers don't interfere.
|
|
1101
|
-
* All methods are pre-bound to the pipeline's ID — consumers never pass IDs.
|
|
1102
|
-
*/
|
|
1103
|
-
interface WasmPipeline {
|
|
1104
|
-
readonly id: number;
|
|
1227
|
+
/** Map a Wasm wire result type to JS-facing type: Wasm.Change[] → Change[], rest untouched. */
|
|
1228
|
+
type WireToJs<T> = {
|
|
1229
|
+
[K in keyof T]: T[K] extends Change$1[] ? Change[] : T[K];
|
|
1230
|
+
};
|
|
1231
|
+
/** JS-facing side effects result — same shape as Wasm wire type but with parsed Change values. */
|
|
1232
|
+
type SideEffectsResult = WireToJs<SideEffectsResult$1>;
|
|
1233
|
+
/**
|
|
1234
|
+
* Create a new isolated WASM pipeline instance.
|
|
1235
|
+
* Each store should call this once and store the result.
|
|
1236
|
+
* Call pipeline.destroy() on cleanup.
|
|
1237
|
+
*/
|
|
1238
|
+
declare const createWasmPipeline: (options?: {
|
|
1239
|
+
debug?: boolean;
|
|
1240
|
+
}) => {
|
|
1241
|
+
id: number;
|
|
1242
|
+
readonly destroyed: boolean;
|
|
1105
1243
|
shadowInit: (state: object) => void;
|
|
1106
1244
|
shadowDump: () => unknown;
|
|
1107
|
-
processChanges: (changes: Change[]) =>
|
|
1108
|
-
|
|
1245
|
+
processChanges: (changes: Change[]) => {
|
|
1246
|
+
listener_changes: Change[];
|
|
1247
|
+
validators_to_run: ValidatorDispatch[];
|
|
1248
|
+
execution_plan: FullExecutionPlan | null;
|
|
1249
|
+
has_work: boolean;
|
|
1250
|
+
};
|
|
1251
|
+
pipelineFinalize: (changes: Change[]) => {
|
|
1109
1252
|
state_changes: Change[];
|
|
1253
|
+
trace: PipelineTrace | null;
|
|
1254
|
+
};
|
|
1255
|
+
registerSideEffects: (reg: Partial<SideEffectsRegistration>) => {
|
|
1256
|
+
sync_changes: Change[];
|
|
1257
|
+
aggregation_changes: Change[];
|
|
1258
|
+
computation_changes: Change[];
|
|
1259
|
+
registered_listener_ids: number[];
|
|
1110
1260
|
};
|
|
1111
|
-
registerSideEffects: (reg: SideEffectsRegistration) => SideEffectsResult;
|
|
1112
1261
|
unregisterSideEffects: (registrationId: string) => void;
|
|
1113
|
-
registerConcerns: (reg: ConcernsRegistration) =>
|
|
1262
|
+
registerConcerns: (reg: Partial<ConcernsRegistration>) => {
|
|
1263
|
+
bool_logic_changes: Change[];
|
|
1264
|
+
registered_logic_ids: number[];
|
|
1265
|
+
registered_validator_ids: number[];
|
|
1266
|
+
value_logic_changes: Change[];
|
|
1267
|
+
registered_value_logic_ids: number[];
|
|
1268
|
+
};
|
|
1114
1269
|
unregisterConcerns: (registrationId: string) => void;
|
|
1115
1270
|
registerBoolLogic: (outputPath: string, tree: unknown) => number;
|
|
1116
1271
|
unregisterBoolLogic: (logicId: number) => void;
|
|
1117
1272
|
pipelineReset: () => void;
|
|
1118
1273
|
destroy: () => void;
|
|
1119
|
-
|
|
1120
|
-
validatorSchemas: Map<number, ValidationSchema
|
|
1121
|
-
}
|
|
1274
|
+
getGraphSnapshot: () => GraphSnapshot;
|
|
1275
|
+
validatorSchemas: Map<number, ValidationSchema<unknown>>;
|
|
1276
|
+
};
|
|
1277
|
+
type WasmPipeline = ReturnType<typeof createWasmPipeline>;
|
|
1122
1278
|
|
|
1123
1279
|
/**
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1126
|
-
* A custom data structure that provides O(1) connected component lookups
|
|
1127
|
-
* instead of O(V+E) recomputation on every change like graphology's connectedComponents.
|
|
1280
|
+
* Apex State Logger — Simplified debug logging with colored console output.
|
|
1128
1281
|
*
|
|
1129
|
-
*
|
|
1130
|
-
*
|
|
1131
|
-
*
|
|
1132
|
-
* Operations complexity:
|
|
1133
|
-
* - getAllGroups(): O(G) where G is number of groups (typically small)
|
|
1134
|
-
* - getGroupPaths(path): O(1) lookup
|
|
1135
|
-
* - addEdge(p1, p2): O(n) where n is smaller group size (merge)
|
|
1136
|
-
* - removeEdge(p1, p2): O(n) for affected component
|
|
1137
|
-
* - hasPath(path): O(1)
|
|
1138
|
-
* - hasEdge(p1, p2): O(1)
|
|
1139
|
-
*/
|
|
1140
|
-
/** Graph type for WASM mirroring. When set, addEdge/removeEdge mirror to WASM. */
|
|
1141
|
-
type WasmGraphType = 'sync' | 'flip';
|
|
1142
|
-
/**
|
|
1143
|
-
* PathGroups maintains connected components with O(1) lookups.
|
|
1282
|
+
* Two log functions:
|
|
1283
|
+
* 1. logPipeline — called once per processChanges with unified trace
|
|
1284
|
+
* 2. logRegistration — called once per register/unregister with graph snapshot
|
|
1144
1285
|
*
|
|
1145
|
-
*
|
|
1146
|
-
* - pathToGroup: Maps each path to its group ID
|
|
1147
|
-
* - groupToPaths: Maps each group ID to the set of paths in that group
|
|
1148
|
-
* - edges: Set of edge keys for edge existence checks and proper removal
|
|
1149
|
-
* - adjacency: Maps each path to its direct neighbors (for split detection on removal)
|
|
1150
|
-
* - nextGroupId: Counter for generating unique group IDs
|
|
1286
|
+
* Zero runtime cost when log flag is false (returns no-op logger).
|
|
1151
1287
|
*/
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1288
|
+
|
|
1289
|
+
/** Per-listener dispatch trace (JS-measured). */
|
|
1290
|
+
interface ListenerDispatchTrace {
|
|
1291
|
+
dispatchId: number;
|
|
1292
|
+
subscriberId: number;
|
|
1293
|
+
fnName: string;
|
|
1294
|
+
scope: string;
|
|
1295
|
+
topic: string;
|
|
1296
|
+
registrationId: string;
|
|
1297
|
+
input: [string, unknown, unknown][];
|
|
1298
|
+
output: Change[];
|
|
1299
|
+
currentState: unknown;
|
|
1300
|
+
durationMs: number;
|
|
1301
|
+
slow: boolean;
|
|
1302
|
+
}
|
|
1303
|
+
/** Universal trace — single object for console, devtools, any observability tool. */
|
|
1304
|
+
interface UnifiedPipelineTrace {
|
|
1305
|
+
wasm: PipelineTrace;
|
|
1306
|
+
listeners: ListenerDispatchTrace[];
|
|
1307
|
+
totalDurationMs: number;
|
|
1308
|
+
wasmDurationMs: number;
|
|
1309
|
+
listenerDurationMs: number;
|
|
1310
|
+
/** True when listener timing was actually measured (debug.timing: true). False → show N/A. */
|
|
1311
|
+
listenerTimingEnabled: boolean;
|
|
1312
|
+
}
|
|
1313
|
+
interface PipelineLogData {
|
|
1314
|
+
initialChanges: Change[];
|
|
1315
|
+
trace: UnifiedPipelineTrace | null;
|
|
1316
|
+
/** Pre-finalize changes: listener output + validator output before WASM merge/dedup. */
|
|
1317
|
+
preFinalizableChanges?: Change[];
|
|
1318
|
+
/** Final applied changes: after WASM finalization, partitioned to state + concerns. */
|
|
1319
|
+
appliedChanges?: Change[];
|
|
1320
|
+
/** Frozen valtio snapshot of state after all changes applied. */
|
|
1321
|
+
stateSnapshot?: unknown;
|
|
1322
|
+
}
|
|
1323
|
+
interface RegistrationLogData {
|
|
1324
|
+
result?: SideEffectsResult;
|
|
1325
|
+
appliedChanges?: Change[];
|
|
1326
|
+
stateSnapshot?: unknown;
|
|
1327
|
+
durationMs?: number;
|
|
1328
|
+
/** subscriber_id → function name, for enriching listener entries in the log. */
|
|
1329
|
+
listenerNames?: Map<number, string>;
|
|
1330
|
+
}
|
|
1331
|
+
interface ApexLogger {
|
|
1332
|
+
logPipeline: (data: PipelineLogData) => void;
|
|
1333
|
+
logRegistration: (type: 'register' | 'unregister', id: string, snapshot: GraphSnapshot, data?: RegistrationLogData) => void;
|
|
1334
|
+
destroy: () => void;
|
|
1159
1335
|
}
|
|
1160
1336
|
|
|
1161
1337
|
/**
|
|
1162
|
-
*
|
|
1338
|
+
* Valtio DevTools — Connects state and concerns proxies to Redux DevTools,
|
|
1339
|
+
* and provides a pipeline notifier for sending pipeline/registration events.
|
|
1340
|
+
*
|
|
1341
|
+
* Uses valtio/utils devtools() to expose both proxies as separate DevTools instances:
|
|
1342
|
+
* - '{prefix}:state' — application state proxy
|
|
1343
|
+
* - '{prefix}:concerns' — computed concern values proxy
|
|
1163
1344
|
*
|
|
1164
|
-
*
|
|
1165
|
-
* These aliases maintain backward compatibility with the previous graphology-based API.
|
|
1345
|
+
* Singleton connections per proxy — survives StrictMode remounts and HMR reloads.
|
|
1166
1346
|
*/
|
|
1167
1347
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
/**
|
|
1174
|
-
* Flip graph: Paths with inverted boolean values
|
|
1175
|
-
* Type alias for PathGroups - maintains backward compatibility
|
|
1176
|
-
*/
|
|
1177
|
-
type FlipGraph = PathGroups;
|
|
1348
|
+
interface DevToolsNotifier {
|
|
1349
|
+
notifyPipeline: (data: PipelineLogData) => void;
|
|
1350
|
+
notifyRegistration: (type: 'register' | 'unregister', id: string, snapshot: GraphSnapshot) => void;
|
|
1351
|
+
destroy: () => void;
|
|
1352
|
+
}
|
|
1178
1353
|
|
|
1179
1354
|
/**
|
|
1180
1355
|
* Core Store Types
|
|
@@ -1187,18 +1362,14 @@ type FlipGraph = PathGroups;
|
|
|
1187
1362
|
* Debug configuration for development tooling
|
|
1188
1363
|
*/
|
|
1189
1364
|
interface DebugConfig {
|
|
1190
|
-
/** Enable
|
|
1365
|
+
/** Enable console logging for pipeline runs and registrations */
|
|
1366
|
+
log?: boolean;
|
|
1367
|
+
/** Enable timing warnings for slow listeners */
|
|
1191
1368
|
timing?: boolean;
|
|
1192
|
-
/** Threshold in milliseconds for slow
|
|
1369
|
+
/** Threshold in milliseconds for slow listener warnings (default: 5ms) */
|
|
1193
1370
|
timingThreshold?: number;
|
|
1194
1371
|
/** Enable tracking of processChanges calls and applied changes for testing/debugging */
|
|
1195
1372
|
track?: boolean;
|
|
1196
|
-
/** Log pipeline phases, state changes, sync/flip expansions */
|
|
1197
|
-
logPipeline?: boolean;
|
|
1198
|
-
/** Log listener dispatch inputs and outputs */
|
|
1199
|
-
logListeners?: boolean;
|
|
1200
|
-
/** Log concern evaluations and validator runs */
|
|
1201
|
-
logConcerns?: boolean;
|
|
1202
1373
|
/** Connect to Redux DevTools Extension for state inspection */
|
|
1203
1374
|
devtools?: boolean;
|
|
1204
1375
|
}
|
|
@@ -1240,8 +1411,6 @@ interface StoreConfig {
|
|
|
1240
1411
|
maxIterations?: number;
|
|
1241
1412
|
/** Debug configuration for development tooling */
|
|
1242
1413
|
debug?: DebugConfig;
|
|
1243
|
-
/** Use legacy TypeScript implementation instead of WASM (default: false) */
|
|
1244
|
-
useLegacyImplementation?: boolean;
|
|
1245
1414
|
}
|
|
1246
1415
|
interface ProviderProps<DATA extends object> {
|
|
1247
1416
|
initialState: DATA;
|
|
@@ -1306,12 +1475,18 @@ type OnStateListener<DATA extends object = object, SUB_STATE = DATA, META extend
|
|
|
1306
1475
|
* }
|
|
1307
1476
|
* ```
|
|
1308
1477
|
*/
|
|
1309
|
-
|
|
1478
|
+
/**
|
|
1479
|
+
* Single-path listener — watches one path (or all paths when null).
|
|
1480
|
+
* `scope` may be omitted (defaults to `path`).
|
|
1481
|
+
*/
|
|
1482
|
+
interface SinglePathListener<DATA extends object = object, META extends GenericMeta = GenericMeta, Depth extends number = DefaultDepth> {
|
|
1310
1483
|
/**
|
|
1311
|
-
* Path to watch - only changes under this path will trigger the listener
|
|
1312
|
-
* null = watch all paths (receives every change)
|
|
1484
|
+
* Path to watch - only changes under this path will trigger the listener.
|
|
1485
|
+
* null = watch all paths (receives every change).
|
|
1486
|
+
* Array = watch multiple paths; listener fires once per run when any match,
|
|
1487
|
+
* receiving all matching changes merged into a single input array.
|
|
1313
1488
|
*/
|
|
1314
|
-
path: DeepKey<DATA, Depth> | null;
|
|
1489
|
+
path: DeepKey<DATA, Depth> | DeepKey<DATA, Depth>[] | null;
|
|
1315
1490
|
/**
|
|
1316
1491
|
* Scope for state and changes presentation
|
|
1317
1492
|
* - If omitted/undefined: defaults to `path` (scoped state matching the watched path)
|
|
@@ -1323,48 +1498,55 @@ interface ListenerRegistration<DATA extends object = object, META extends Generi
|
|
|
1323
1498
|
scope?: DeepKey<DATA, Depth> | null;
|
|
1324
1499
|
fn: OnStateListener<DATA, any, META>;
|
|
1325
1500
|
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Multi-path listener — watches multiple paths simultaneously.
|
|
1503
|
+
* Handler fires once per pipeline run with all matched changes merged.
|
|
1504
|
+
* `scope` is required (use null for full paths).
|
|
1505
|
+
*/
|
|
1506
|
+
interface MultiPathListener<DATA extends object = object, META extends GenericMeta = GenericMeta, Depth extends number = DefaultDepth> {
|
|
1507
|
+
/**
|
|
1508
|
+
* Array of paths to watch. Handler fires once with changes from any matched path.
|
|
1509
|
+
*/
|
|
1510
|
+
path: DeepKey<DATA, Depth>[];
|
|
1511
|
+
/**
|
|
1512
|
+
* Scope for state and changes presentation (required for multi-path listeners).
|
|
1513
|
+
* - null: state is full DATA, changes use FULL paths
|
|
1514
|
+
* - string: state is value at scope, changes use paths RELATIVE to scope
|
|
1515
|
+
*/
|
|
1516
|
+
scope: DeepKey<DATA, Depth> | null;
|
|
1517
|
+
fn: OnStateListener<DATA, any, META>;
|
|
1518
|
+
}
|
|
1519
|
+
/** Listener registration — single-path or multi-path. */
|
|
1520
|
+
type ListenerRegistration<DATA extends object = object, META extends GenericMeta = GenericMeta, Depth extends number = DefaultDepth> = SinglePathListener<DATA, META, Depth> | MultiPathListener<DATA, META, Depth>;
|
|
1326
1521
|
interface ListenerHandlerRef {
|
|
1327
1522
|
scope: string | null;
|
|
1328
|
-
fn: (...args:
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
sync: SyncGraph;
|
|
1332
|
-
flip: FlipGraph;
|
|
1333
|
-
listeners: Map<string, ListenerRegistration<DATA, META>[]>;
|
|
1334
|
-
sortedListenerPaths: string[];
|
|
1335
|
-
/** O(1) lookup: subscriber_id -> handler ref. Populated by registerListener. */
|
|
1336
|
-
listenerHandlers: Map<number, ListenerHandlerRef>;
|
|
1523
|
+
fn: (...args: any[]) => unknown;
|
|
1524
|
+
name: string;
|
|
1525
|
+
registrationId: string;
|
|
1337
1526
|
}
|
|
1338
1527
|
interface Registrations {
|
|
1339
1528
|
concerns: Map<string, ConcernType[]>;
|
|
1340
1529
|
effectCleanups: Set<() => void>;
|
|
1341
1530
|
sideEffectCleanups: Map<string, () => void>;
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
interface ProcessingState<DATA extends object = object, META extends GenericMeta = GenericMeta> {
|
|
1345
|
-
queue: ArrayOfChanges<DATA, META>;
|
|
1531
|
+
/** O(1) lookup: subscriber_id -> handler ref. Populated by registerListener. */
|
|
1532
|
+
listenerHandlers: Map<number, ListenerHandlerRef>;
|
|
1346
1533
|
}
|
|
1347
1534
|
/** Internal store state (NOT tracked - wrapped in ref()) */
|
|
1348
|
-
interface InternalState
|
|
1349
|
-
graphs: SideEffectGraphs<DATA, META>;
|
|
1535
|
+
interface InternalState {
|
|
1350
1536
|
registrations: Registrations;
|
|
1351
|
-
processing: ProcessingState<DATA, META>;
|
|
1352
|
-
timing: Timing;
|
|
1353
|
-
observer: PipelineObserver;
|
|
1354
1537
|
config: DeepRequired<StoreConfig>;
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1538
|
+
logger: ApexLogger;
|
|
1539
|
+
/** DevTools — pipeline notifier + proxy inspection. Null when devtools disabled. */
|
|
1540
|
+
devtools: DevToolsNotifier | null;
|
|
1541
|
+
/** Per-store WASM pipeline instance (null after destroy). */
|
|
1358
1542
|
pipeline: WasmPipeline | null;
|
|
1359
|
-
/** Pending deferred destroy timer — cancelled on StrictMode re-mount. */
|
|
1360
|
-
_destroyTimer?: ReturnType<typeof setTimeout> | undefined;
|
|
1361
1543
|
}
|
|
1362
1544
|
type ConcernValues = Record<string, Record<string, unknown>>;
|
|
1363
1545
|
/** Two-proxy pattern: state and _concerns are independent to prevent infinite loops */
|
|
1364
|
-
interface StoreInstance<DATA extends object
|
|
1546
|
+
interface StoreInstance<DATA extends object> {
|
|
1365
1547
|
state: DATA;
|
|
1366
1548
|
_concerns: ConcernValues;
|
|
1367
|
-
_internal: InternalState
|
|
1549
|
+
_internal: InternalState;
|
|
1368
1550
|
/** Debug tracking data, only populated when debug.track is enabled */
|
|
1369
1551
|
_debug: DebugTrack | null;
|
|
1370
1552
|
}
|
|
@@ -1701,6 +1883,13 @@ interface SideEffects<DATA extends object, META extends GenericMeta = GenericMet
|
|
|
1701
1883
|
* Accepts direct `ListenerRegistration<T>[]` or pre-validated result from `listeners()`.
|
|
1702
1884
|
*/
|
|
1703
1885
|
listeners?: ListenerRegistration<DATA, META>[];
|
|
1886
|
+
/**
|
|
1887
|
+
* Anchor path - guard for all resources in this registration.
|
|
1888
|
+
* When the anchor path is structurally absent from shadow state, all listeners,
|
|
1889
|
+
* BoolLogics, and validators silently skip execution for that pipeline run
|
|
1890
|
+
* (no unregistration — they resume when the path reappears).
|
|
1891
|
+
*/
|
|
1892
|
+
anchorPath?: DeepKey<DATA, Depth>;
|
|
1704
1893
|
}
|
|
1705
1894
|
|
|
1706
1895
|
/**
|
|
@@ -1735,14 +1924,34 @@ interface GenericStoreApi<DATA extends object, META extends GenericMeta = Generi
|
|
|
1735
1924
|
} & {
|
|
1736
1925
|
[K in keyof SELECTION as SELECTION[K] extends true ? K : never]?: K extends keyof EvaluatedConcerns<CONCERNS> ? EvaluatedConcerns<CONCERNS>[K] : never;
|
|
1737
1926
|
};
|
|
1927
|
+
/** Reactive snapshot of all concerns matching this selection, keyed by path. Re-renders when any concern changes. */
|
|
1928
|
+
useConcernsSnapshot: () => Record<string, {
|
|
1929
|
+
[K in keyof SELECTION as SELECTION[K] extends true ? K : never]?: K extends keyof EvaluatedConcerns<CONCERNS> ? EvaluatedConcerns<CONCERNS>[K] : never;
|
|
1930
|
+
}>;
|
|
1738
1931
|
};
|
|
1932
|
+
/**
|
|
1933
|
+
* React hook. Fires callback whenever concerns matching the selection change.
|
|
1934
|
+
* Does NOT trigger re-renders — use useConcernsSnapshot() for reactive UI instead.
|
|
1935
|
+
* Callback is stable across renders (no need to memoize).
|
|
1936
|
+
*/
|
|
1937
|
+
useWatchConcerns: <SELECTION extends Partial<Record<Extract<CONCERNS[number], {
|
|
1938
|
+
name: string;
|
|
1939
|
+
}>['name'], boolean>>>(selection: SELECTION, callback: (concerns: Record<string, {
|
|
1940
|
+
[K in keyof SELECTION as SELECTION[K] extends true ? K : never]?: K extends keyof EvaluatedConcerns<CONCERNS> ? EvaluatedConcerns<CONCERNS>[K] : never;
|
|
1941
|
+
}>) => void) => void;
|
|
1739
1942
|
withMeta: (presetMeta: Partial<META>) => {
|
|
1740
1943
|
useFieldStore: <P extends DeepKey<DATA>>(path: P) => {
|
|
1741
1944
|
value: DeepValue<DATA, P>;
|
|
1742
1945
|
setValue: (newValue: DeepValue<DATA, P>, meta?: META) => void;
|
|
1743
1946
|
};
|
|
1744
1947
|
};
|
|
1745
|
-
syncPairs: <T extends [ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>]
|
|
1948
|
+
syncPairs: <T extends ([ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>] | [
|
|
1949
|
+
ResolvableDeepKey<DATA>,
|
|
1950
|
+
ResolvableDeepKey<DATA>,
|
|
1951
|
+
{
|
|
1952
|
+
oneWay: '[0]->[1]' | '[1]->[0]';
|
|
1953
|
+
}
|
|
1954
|
+
])[]>(pairs: CheckSyncPairs<DATA, T>) => ValidatedSyncPairs<DATA>;
|
|
1746
1955
|
flipPairs: <T extends [ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>][]>(pairs: CheckSyncPairs<DATA, T>) => ValidatedFlipPairs<DATA>;
|
|
1747
1956
|
aggregationPairs: <T extends ([ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>] | [ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>, BoolLogic<DATA>])[]>(pairs: CheckAggregationPairs<DATA, T>) => ValidatedAggregationPairs<DATA>;
|
|
1748
1957
|
computationPairs: <T extends ([
|
|
@@ -1971,20 +2180,14 @@ interface FieldInput<T> {
|
|
|
1971
2180
|
*/
|
|
1972
2181
|
declare const useTransformedField: <TStored, TDisplay, TField extends FieldInput<TStored>>(field: TField, config: TransformConfig<TStored, TDisplay>) => Omit<TField, "value" | "setValue"> & FieldInput<TDisplay>;
|
|
1973
2182
|
|
|
1974
|
-
/** Legacy JS implementation - uses JS graph structure */
|
|
1975
|
-
declare const registerFlipPair: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA, META>, path1: string & {}, path2: string & {}) => (() => void);
|
|
1976
|
-
|
|
1977
|
-
/** Legacy JS implementation - uses JS listener maps and sorted paths */
|
|
1978
|
-
declare const registerListenerLegacy: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA, META>, registration: ListenerRegistration<DATA, META>) => (() => void);
|
|
1979
|
-
|
|
1980
2183
|
/**
|
|
1981
|
-
*
|
|
1982
|
-
*
|
|
1983
|
-
*
|
|
2184
|
+
* WASM Implementation - Side Effects Registration
|
|
2185
|
+
*
|
|
2186
|
+
* Consolidates all side effects (sync, flip, aggregation, listeners) into a single
|
|
2187
|
+
* WASM call for efficiency. No legacy fallback logic - assumes WASM is loaded.
|
|
1984
2188
|
*/
|
|
1985
|
-
declare const registerSyncPairsBatch: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA, META>, pairs: [string & {}, string & {}][]) => (() => void);
|
|
1986
2189
|
|
|
1987
|
-
declare const registerSideEffects: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA
|
|
2190
|
+
declare const registerSideEffects: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA>, id: string, effects: SideEffects<DATA, META>) => (() => void);
|
|
1988
2191
|
|
|
1989
2192
|
/**
|
|
1990
2193
|
* Lazy-validated pair helper functions
|
|
@@ -2042,7 +2245,9 @@ declare const registerSideEffects: <DATA extends object, META extends GenericMet
|
|
|
2042
2245
|
* useSideEffects('my-syncs', { syncPaths: syncs }) // no O(N²) re-check
|
|
2043
2246
|
* ```
|
|
2044
2247
|
*/
|
|
2045
|
-
declare const syncPairs: <DATA extends object, Depth extends number = DefaultDepth>() => <T extends [ResolvableDeepKey<DATA, Depth>, ResolvableDeepKey<DATA, Depth>][
|
|
2248
|
+
declare const syncPairs: <DATA extends object, Depth extends number = DefaultDepth>() => <T extends ([ResolvableDeepKey<DATA, Depth>, ResolvableDeepKey<DATA, Depth>] | [ResolvableDeepKey<DATA, Depth>, ResolvableDeepKey<DATA, Depth>, {
|
|
2249
|
+
oneWay: "[0]->[1]" | "[1]->[0]";
|
|
2250
|
+
}])[]>(pairs: CheckSyncPairs<DATA, T, Depth>) => ValidatedSyncPairs<DATA>;
|
|
2046
2251
|
/**
|
|
2047
2252
|
* Lazy-validated flip pairs. Curried: `flipPairs<State>()(pairs)`.
|
|
2048
2253
|
*
|
|
@@ -2340,4 +2545,4 @@ declare const applyChangesToObject: <T extends object>(obj: T, changes: AnyChang
|
|
|
2340
2545
|
*/
|
|
2341
2546
|
declare const deepClone: <T>(value: T) => T;
|
|
2342
2547
|
|
|
2343
|
-
export { type Aggregation, type AggregationPair, type ArrayOfChanges, type BaseConcernProps, type BoolLogic, type BufferedField, type CheckAggregationPairs, type CheckComputationPairs, type CheckPairValueMatch, type CheckSyncPairs, type ClearPathRule, type ComputationOp, type ComputationPair, type ConcernRegistration, type ConcernRegistrationMap, type ConcernType, type DebugConfig, type DebugTrack, type DebugTrackEntry, type DeepKey, type DeepKeyFiltered, type DeepPartial, type DeepRequired, type DeepValue, type EvaluatedConcerns, type ExtractEvaluateReturn, type FieldInput$2 as FieldInput, type FlipPair, type GenericMeta, type GenericStoreApi, type HASH_KEY, type KeyboardSelectConfig, type ListenerRegistration, type OnStateListener, type PathsWithSameValueAs, type
|
|
2548
|
+
export { type Aggregation, type AggregationPair, type ArrayOfChanges, type BaseConcernProps, type BoolLogic, type BufferedField, type CheckAggregationPairs, type CheckComputationPairs, type CheckPairValueMatch, type CheckSyncPairs, type ClearPathRule, type ComputationOp, type ComputationPair, type ConcernRegistration, type ConcernRegistrationMap, type ConcernType, type DebugConfig, type DebugTrack, type DebugTrackEntry, type DeepKey, type DeepKeyFiltered, type DeepPartial, type DeepRequired, type DeepValue, type EvaluatedConcerns, type ExtractEvaluateReturn, type FieldInput$2 as FieldInput, type FlipPair, type GenericMeta, type GenericStoreApi, type HASH_KEY, type KeyboardSelectConfig, type ListenerRegistration, type OnStateListener, type PathsWithSameValueAs, type ProviderProps, STORE_DATA, type SelectOption, type SideEffects, type StoreConfig, type StoreInstance, type SyncPair, type ThrottleConfig, type ThrottleFieldInput, type TransformConfig, VALIDATED, type ValidatedAggregationPairs, type ValidatedComputationPairs, type ValidatedFlipPairs, type ValidatedListeners, type ValidatedSyncPairs, type ValidationError, type ValidationSchema, type ValidationStateConcern, type ValidationStateInput, type ValidationStateResult, _, aggregationPairs, applyChangesToObject, computationPairs, createGenericStore, deepClone, defaultConcerns, dot, evaluateBoolLogic, extractPlaceholders, findConcern, flipPairs, hashKey, interpolateTemplate, is, listeners, index as prebuilts, registerSideEffects, syncPairs, useBufferedField, useKeyboardSelect, useThrottledField, useTransformedField };
|