@sladg/apex-state 3.11.2 → 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/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
@@ -509,7 +784,7 @@ type ConcernRegistrationMap<DATA extends object, CONCERNS extends readonly any[]
509
784
  * 1. `{ path: string, fn }` — scope defaults to path → scoped state
510
785
  * 2. `{ path: string, scope: null, fn }` — explicit null scope → full DATA
511
786
  * 3. `{ path: string, scope: string, fn }` — scope must be prefix of path
512
- * 4. `{ path: null, fn }` — watch everything, scope defaults to null → full DATA
787
+ * 4. `{ path: null, fn }` — always fires on any change, scope defaults to null → full DATA
513
788
  * 5. `{ path: null, scope: null, fn }` — same as above, explicit
514
789
  *
515
790
  * **Inline fn restriction**: Fns with untyped (any) parameters are rejected.
@@ -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 (alias for SyncPair).
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> = SyncPair<DATA, Depth>;
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 [P1, P2] sync/flip pairs lazily — O(K) where K = pairs written.
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][], Depth extends number = DefaultDepth> = {
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
- ] ? CheckPairValueMatch<DATA, P1, P2, Depth> : T[I];
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
- string,
856
- string
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,46 +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
-
932
- /**
933
- * Debug Timing Utilities
934
- *
935
- * Provides timing measurement for concerns and listeners
936
- * to detect slow operations during development.
937
- */
938
- type TimingType = 'concerns' | 'listeners' | 'registration';
939
- interface TimingMeta {
940
- path: string;
941
- name: string;
942
- }
943
- interface Timing {
944
- run: <T>(type: TimingType, fn: () => T, meta: TimingMeta) => T;
945
- reportBatch: (type: TimingType) => void;
946
- }
947
-
948
1213
  /**
949
1214
  * WASM Bridge — Thin namespace over Rust/WASM exports.
950
1215
  *
@@ -959,211 +1224,132 @@ interface Timing {
959
1224
  * @module wasm/bridge
960
1225
  */
961
1226
 
962
- /** A single state change (input or output). */
963
- interface Change {
964
- path: string;
965
- value: unknown;
966
- origin?: string;
967
- }
968
- /** A single dispatch entry with sequential ID and input change references. */
969
- interface DispatchEntry {
970
- dispatch_id: number;
971
- subscriber_id: number;
972
- scope_path: string;
973
- topic_path: string;
974
- /** Indexes into ProcessResult.changes array. */
975
- input_change_ids: number[];
976
- }
977
- /** A group of dispatches to execute sequentially. */
978
- interface DispatchGroup {
979
- dispatches: DispatchEntry[];
980
- }
981
- /** A target for propagating produced changes from child to parent dispatch. */
982
- interface PropagationTarget {
983
- target_dispatch_id: number;
984
- /** Prefix to prepend to child's relative paths for the target's scope. */
985
- remap_prefix: string;
986
- }
987
- /** Pre-computed execution plan with propagation map. */
988
- interface FullExecutionPlan {
989
- groups: DispatchGroup[];
990
- /** propagation_map[dispatch_id] = targets to forward produced changes to. */
991
- propagation_map: PropagationTarget[][];
992
- }
993
- /** Validator dispatch info for JS-side execution. */
994
- interface ValidatorDispatch {
995
- validator_id: number;
996
- output_path: string;
997
- dependency_values: Record<string, string>;
998
- }
999
- /**
1000
- * Extends a base tuple with an optional trailing BoolLogic condition (JSON-serialized).
1001
- * Used by aggregation and computation pairs to share the "optional excludeWhen" pattern.
1002
- */
1003
- type WasmPairWithCondition<Base extends [...string[]]> = [...Base] | [...Base, condition: string];
1004
- /** WASM-side aggregation pair: [target, source] with optional excludeWhen condition. */
1005
- type WasmAggregationPair = WasmPairWithCondition<[
1006
- target: string,
1007
- source: string
1008
- ]>;
1009
- /** WASM-side computation pair: [operation, target, source] with optional excludeWhen condition. */
1010
- type WasmComputationPair = WasmPairWithCondition<[
1011
- op: string,
1012
- target: string,
1013
- source: string
1014
- ]>;
1015
- /** WASM-side sync pair: [source, target] — bidirectional sync. */
1016
- type WasmSyncPair = [source: string, target: string];
1017
- /** WASM-side flip pair: [source, target] — inverted boolean sync. */
1018
- type WasmFlipPair = [source: string, target: string];
1019
- /** WASM-side clear path rule: trigger paths → target paths to null. */
1020
- interface WasmClearPathRule {
1021
- triggers: string[];
1022
- targets: string[];
1023
- }
1024
- /** WASM-side listener entry for topic-based dispatch. */
1025
- interface WasmListenerEntry {
1026
- subscriber_id: number;
1027
- topic_path: string;
1028
- scope_path: string;
1029
- }
1030
- /** Consolidated registration input for side effects (sync, flip, aggregation, computation, clear, listeners). */
1031
- interface SideEffectsRegistration {
1032
- registration_id: string;
1033
- sync_pairs?: WasmSyncPair[];
1034
- flip_pairs?: WasmFlipPair[];
1035
- aggregation_pairs?: WasmAggregationPair[];
1036
- computation_pairs?: WasmComputationPair[];
1037
- clear_paths?: WasmClearPathRule[];
1038
- listeners?: WasmListenerEntry[];
1039
- }
1040
- /** Consolidated registration output from side effects registration. */
1041
- interface SideEffectsResult {
1042
- sync_changes: Change[];
1043
- aggregation_changes: Change[];
1044
- computation_changes: Change[];
1045
- registered_listener_ids: number[];
1046
- }
1047
- /** WASM-side BoolLogic entry for declarative boolean evaluation. */
1048
- interface WasmBoolLogicEntry {
1049
- output_path: string;
1050
- tree_json: string;
1051
- }
1052
- /** WASM-side validator entry for validation orchestration. */
1053
- interface WasmValidatorEntry {
1054
- validator_id: number;
1055
- output_path: string;
1056
- dependency_paths: string[];
1057
- scope: string;
1058
- }
1059
- /** WASM-side ValueLogic entry for declarative value computation. */
1060
- interface WasmValueLogicEntry {
1061
- output_path: string;
1062
- tree_json: string;
1063
- }
1064
- /** Consolidated registration input for concerns (BoolLogic, validators, and ValueLogic). */
1065
- interface ConcernsRegistration {
1066
- registration_id: string;
1067
- bool_logics?: WasmBoolLogicEntry[];
1068
- validators?: WasmValidatorEntry[];
1069
- value_logics?: WasmValueLogicEntry[];
1070
- }
1071
- /** Consolidated registration output from concerns registration. */
1072
- interface ConcernsResult {
1073
- bool_logic_changes: Change[];
1074
- registered_logic_ids: number[];
1075
- registered_validator_ids: number[];
1076
- value_logic_changes: Change[];
1077
- registered_value_logic_ids: number[];
1078
- }
1079
- /** Result of processChanges (Phase 1). */
1080
- interface ProcessChangesResult {
1081
- state_changes: Change[];
1082
- changes: Change[];
1083
- validators_to_run: ValidatorDispatch[];
1084
- execution_plan: FullExecutionPlan | null;
1085
- has_work: boolean;
1086
- }
1087
- /**
1088
- * An isolated WASM pipeline instance.
1089
- * Each store gets its own pipeline so multiple Providers don't interfere.
1090
- * All methods are pre-bound to the pipeline's ID — consumers never pass IDs.
1091
- */
1092
- interface WasmPipeline {
1093
- 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;
1094
1243
  shadowInit: (state: object) => void;
1095
1244
  shadowDump: () => unknown;
1096
- processChanges: (changes: Change[]) => ProcessChangesResult;
1097
- pipelineFinalize: (jsChanges: Change[]) => {
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[]) => {
1098
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[];
1099
1260
  };
1100
- registerSideEffects: (reg: SideEffectsRegistration) => SideEffectsResult;
1101
1261
  unregisterSideEffects: (registrationId: string) => void;
1102
- registerConcerns: (reg: ConcernsRegistration) => ConcernsResult;
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
+ };
1103
1269
  unregisterConcerns: (registrationId: string) => void;
1104
1270
  registerBoolLogic: (outputPath: string, tree: unknown) => number;
1105
1271
  unregisterBoolLogic: (logicId: number) => void;
1106
1272
  pipelineReset: () => void;
1107
1273
  destroy: () => void;
1108
- /** Per-instance storage for validation schemas (can't cross WASM boundary). */
1109
- validatorSchemas: Map<number, ValidationSchema>;
1110
- }
1274
+ getGraphSnapshot: () => GraphSnapshot;
1275
+ validatorSchemas: Map<number, ValidationSchema<unknown>>;
1276
+ };
1277
+ type WasmPipeline = ReturnType<typeof createWasmPipeline>;
1111
1278
 
1112
1279
  /**
1113
- * PathGroups Data Structure
1280
+ * Apex State Logger — Simplified debug logging with colored console output.
1114
1281
  *
1115
- * A custom data structure that provides O(1) connected component lookups
1116
- * instead of O(V+E) recomputation on every change like graphology's connectedComponents.
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
1117
1285
  *
1118
- * When wasmGraphType is set ('sync' | 'flip'), addEdge/removeEdge automatically
1119
- * mirror registrations to WASM bridge for pipeline processing.
1120
- *
1121
- * Operations complexity:
1122
- * - getAllGroups(): O(G) where G is number of groups (typically small)
1123
- * - getGroupPaths(path): O(1) lookup
1124
- * - addEdge(p1, p2): O(n) where n is smaller group size (merge)
1125
- * - removeEdge(p1, p2): O(n) for affected component
1126
- * - hasPath(path): O(1)
1127
- * - hasEdge(p1, p2): O(1)
1128
- */
1129
- /** Graph type for WASM mirroring. When set, addEdge/removeEdge mirror to WASM. */
1130
- type WasmGraphType = 'sync' | 'flip';
1131
- /**
1132
- * PathGroups maintains connected components with O(1) lookups.
1133
- *
1134
- * Internal structure:
1135
- * - pathToGroup: Maps each path to its group ID
1136
- * - groupToPaths: Maps each group ID to the set of paths in that group
1137
- * - edges: Set of edge keys for edge existence checks and proper removal
1138
- * - adjacency: Maps each path to its direct neighbors (for split detection on removal)
1139
- * - nextGroupId: Counter for generating unique group IDs
1286
+ * Zero runtime cost when log flag is false (returns no-op logger).
1140
1287
  */
1141
- interface PathGroups {
1142
- pathToGroup: Map<string, number>;
1143
- groupToPaths: Map<number, Set<string>>;
1144
- edges: Set<string>;
1145
- adjacency: Map<string, Set<string>>;
1146
- nextGroupId: number;
1147
- wasmGraphType?: WasmGraphType;
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;
1148
1335
  }
1149
1336
 
1150
1337
  /**
1151
- * Graph Type Definitions
1338
+ * Valtio DevTools — Connects state and concerns proxies to Redux DevTools,
1339
+ * and provides a pipeline notifier for sending pipeline/registration events.
1152
1340
  *
1153
- * Type aliases for the PathGroups data structure used in sync and flip operations.
1154
- * These aliases maintain backward compatibility with the previous graphology-based API.
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
1344
+ *
1345
+ * Singleton connections per proxy — survives StrictMode remounts and HMR reloads.
1155
1346
  */
1156
1347
 
1157
- /**
1158
- * Sync graph: Paths that must have synchronized values
1159
- * Type alias for PathGroups - maintains backward compatibility
1160
- */
1161
- type SyncGraph = PathGroups;
1162
- /**
1163
- * Flip graph: Paths with inverted boolean values
1164
- * Type alias for PathGroups - maintains backward compatibility
1165
- */
1166
- 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
+ }
1167
1353
 
1168
1354
  /**
1169
1355
  * Core Store Types
@@ -1176,18 +1362,14 @@ type FlipGraph = PathGroups;
1176
1362
  * Debug configuration for development tooling
1177
1363
  */
1178
1364
  interface DebugConfig {
1179
- /** Enable timing measurement for concerns and listeners */
1365
+ /** Enable console logging for pipeline runs and registrations */
1366
+ log?: boolean;
1367
+ /** Enable timing warnings for slow listeners */
1180
1368
  timing?: boolean;
1181
- /** Threshold in milliseconds for slow operation warnings (default: 5ms) */
1369
+ /** Threshold in milliseconds for slow listener warnings (default: 5ms) */
1182
1370
  timingThreshold?: number;
1183
1371
  /** Enable tracking of processChanges calls and applied changes for testing/debugging */
1184
1372
  track?: boolean;
1185
- /** Log pipeline phases, state changes, sync/flip expansions */
1186
- logPipeline?: boolean;
1187
- /** Log listener dispatch inputs and outputs */
1188
- logListeners?: boolean;
1189
- /** Log concern evaluations and validator runs */
1190
- logConcerns?: boolean;
1191
1373
  /** Connect to Redux DevTools Extension for state inspection */
1192
1374
  devtools?: boolean;
1193
1375
  }
@@ -1221,14 +1403,14 @@ interface DebugTrack {
1221
1403
  clear: () => void;
1222
1404
  }
1223
1405
  interface StoreConfig {
1406
+ /** Human-readable store name for DevTools (default: "store"). Auto-incremented ID is appended. */
1407
+ name?: string;
1224
1408
  /** Error storage path (default: "_errors") */
1225
1409
  errorStorePath?: string;
1226
1410
  /** Max iterations for change processing (default: 100) */
1227
1411
  maxIterations?: number;
1228
1412
  /** Debug configuration for development tooling */
1229
1413
  debug?: DebugConfig;
1230
- /** Use legacy TypeScript implementation instead of WASM (default: false) */
1231
- useLegacyImplementation?: boolean;
1232
1414
  }
1233
1415
  interface ProviderProps<DATA extends object> {
1234
1416
  initialState: DATA;
@@ -1281,14 +1463,30 @@ type OnStateListener<DATA extends object = object, SUB_STATE = DATA, META extend
1281
1463
  * // state: p.123.g.abc object
1282
1464
  * }
1283
1465
  * }
1466
+ *
1467
+ * // Watch ALL changes (always fires), get full state
1468
+ * {
1469
+ * path: null,
1470
+ * scope: null,
1471
+ * fn: (changes, state) => {
1472
+ * // changes: [['user.profile.name', 'Alice', {}], ['count', 5, {}]] - FULL paths, all depths
1473
+ * // state: full DATA object
1474
+ * }
1475
+ * }
1284
1476
  * ```
1285
1477
  */
1286
- interface ListenerRegistration<DATA extends object = object, META extends GenericMeta = GenericMeta, Depth extends number = DefaultDepth> {
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> {
1287
1483
  /**
1288
- * Path to watch - only changes under this path will trigger the listener
1289
- * null = watch all top-level paths
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.
1290
1488
  */
1291
- path: DeepKey<DATA, Depth> | null;
1489
+ path: DeepKey<DATA, Depth> | DeepKey<DATA, Depth>[] | null;
1292
1490
  /**
1293
1491
  * Scope for state and changes presentation
1294
1492
  * - If omitted/undefined: defaults to `path` (scoped state matching the watched path)
@@ -1300,46 +1498,55 @@ interface ListenerRegistration<DATA extends object = object, META extends Generi
1300
1498
  scope?: DeepKey<DATA, Depth> | null;
1301
1499
  fn: OnStateListener<DATA, any, META>;
1302
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>;
1303
1521
  interface ListenerHandlerRef {
1304
1522
  scope: string | null;
1305
- fn: (...args: unknown[]) => unknown;
1306
- }
1307
- interface SideEffectGraphs<DATA extends object = object, META extends GenericMeta = GenericMeta> {
1308
- sync: SyncGraph;
1309
- flip: FlipGraph;
1310
- listeners: Map<string, ListenerRegistration<DATA, META>[]>;
1311
- sortedListenerPaths: string[];
1312
- /** O(1) lookup: subscriber_id -> handler ref. Populated by registerListener. */
1313
- listenerHandlers: Map<number, ListenerHandlerRef>;
1523
+ fn: (...args: any[]) => unknown;
1524
+ name: string;
1525
+ registrationId: string;
1314
1526
  }
1315
1527
  interface Registrations {
1316
1528
  concerns: Map<string, ConcernType[]>;
1317
1529
  effectCleanups: Set<() => void>;
1318
1530
  sideEffectCleanups: Map<string, () => void>;
1319
- aggregations: Map<string, Aggregation[]>;
1320
- }
1321
- interface ProcessingState<DATA extends object = object, META extends GenericMeta = GenericMeta> {
1322
- queue: ArrayOfChanges<DATA, META>;
1531
+ /** O(1) lookup: subscriber_id -> handler ref. Populated by registerListener. */
1532
+ listenerHandlers: Map<number, ListenerHandlerRef>;
1323
1533
  }
1324
1534
  /** Internal store state (NOT tracked - wrapped in ref()) */
1325
- interface InternalState<DATA extends object = object, META extends GenericMeta = GenericMeta> {
1326
- graphs: SideEffectGraphs<DATA, META>;
1535
+ interface InternalState {
1327
1536
  registrations: Registrations;
1328
- processing: ProcessingState<DATA, META>;
1329
- timing: Timing;
1330
- observer: PipelineObserver;
1331
1537
  config: DeepRequired<StoreConfig>;
1332
- /** Per-store WASM pipeline instance (null when using legacy implementation). */
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). */
1333
1542
  pipeline: WasmPipeline | null;
1334
- /** Pending deferred destroy timer — cancelled on StrictMode re-mount. */
1335
- _destroyTimer?: ReturnType<typeof setTimeout> | undefined;
1336
1543
  }
1337
1544
  type ConcernValues = Record<string, Record<string, unknown>>;
1338
1545
  /** Two-proxy pattern: state and _concerns are independent to prevent infinite loops */
1339
- interface StoreInstance<DATA extends object, META extends GenericMeta = GenericMeta> {
1546
+ interface StoreInstance<DATA extends object> {
1340
1547
  state: DATA;
1341
1548
  _concerns: ConcernValues;
1342
- _internal: InternalState<DATA, META>;
1549
+ _internal: InternalState;
1343
1550
  /** Debug tracking data, only populated when debug.track is enabled */
1344
1551
  _debug: DebugTrack | null;
1345
1552
  }
@@ -1622,6 +1829,15 @@ type ClearPathRule<DATA extends object, Depth extends number = DefaultDepth> = [
1622
1829
  * // state: p.123.g.abc object
1623
1830
  * return [['data.computed', computed, {}]] // SCOPED path (relative to scope)
1624
1831
  * }
1832
+ * },
1833
+ * {
1834
+ * path: null, // Always fires — receives ALL changes at any depth
1835
+ * scope: null, // Get full state
1836
+ * fn: (changes, state) => {
1837
+ * // changes: [['user.profile.name', 'Alice', {}]] // FULL paths, all depths
1838
+ * // state: full DATA object
1839
+ * return undefined
1840
+ * }
1625
1841
  * }
1626
1842
  * ]
1627
1843
  * })
@@ -1667,6 +1883,13 @@ interface SideEffects<DATA extends object, META extends GenericMeta = GenericMet
1667
1883
  * Accepts direct `ListenerRegistration<T>[]` or pre-validated result from `listeners()`.
1668
1884
  */
1669
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>;
1670
1893
  }
1671
1894
 
1672
1895
  /**
@@ -1701,14 +1924,34 @@ interface GenericStoreApi<DATA extends object, META extends GenericMeta = Generi
1701
1924
  } & {
1702
1925
  [K in keyof SELECTION as SELECTION[K] extends true ? K : never]?: K extends keyof EvaluatedConcerns<CONCERNS> ? EvaluatedConcerns<CONCERNS>[K] : never;
1703
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
+ }>;
1704
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;
1705
1942
  withMeta: (presetMeta: Partial<META>) => {
1706
1943
  useFieldStore: <P extends DeepKey<DATA>>(path: P) => {
1707
1944
  value: DeepValue<DATA, P>;
1708
1945
  setValue: (newValue: DeepValue<DATA, P>, meta?: META) => void;
1709
1946
  };
1710
1947
  };
1711
- syncPairs: <T extends [ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>][]>(pairs: CheckSyncPairs<DATA, T>) => ValidatedSyncPairs<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>;
1712
1955
  flipPairs: <T extends [ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>][]>(pairs: CheckSyncPairs<DATA, T>) => ValidatedFlipPairs<DATA>;
1713
1956
  aggregationPairs: <T extends ([ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>] | [ResolvableDeepKey<DATA>, ResolvableDeepKey<DATA>, BoolLogic<DATA>])[]>(pairs: CheckAggregationPairs<DATA, T>) => ValidatedAggregationPairs<DATA>;
1714
1957
  computationPairs: <T extends ([
@@ -1937,20 +2180,14 @@ interface FieldInput<T> {
1937
2180
  */
1938
2181
  declare const useTransformedField: <TStored, TDisplay, TField extends FieldInput<TStored>>(field: TField, config: TransformConfig<TStored, TDisplay>) => Omit<TField, "value" | "setValue"> & FieldInput<TDisplay>;
1939
2182
 
1940
- /** Legacy JS implementation - uses JS graph structure */
1941
- declare const registerFlipPair: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA, META>, path1: string & {}, path2: string & {}) => (() => void);
1942
-
1943
- /** Legacy JS implementation - uses JS listener maps and sorted paths */
1944
- declare const registerListenerLegacy: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA, META>, registration: ListenerRegistration<DATA, META>) => (() => void);
1945
-
1946
2183
  /**
1947
- * Legacy batch version of registerSyncPair. Adds all edges first, then computes
1948
- * initial sync changes across all final groups and calls processChanges once.
1949
- * This avoids cascading effect re-evaluations when registering many pairs.
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.
1950
2188
  */
1951
- declare const registerSyncPairsBatch: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA, META>, pairs: [string & {}, string & {}][]) => (() => void);
1952
2189
 
1953
- declare const registerSideEffects: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA, META>, id: string, effects: SideEffects<DATA, META>) => (() => void);
2190
+ declare const registerSideEffects: <DATA extends object, META extends GenericMeta = GenericMeta>(store: StoreInstance<DATA>, id: string, effects: SideEffects<DATA, META>) => (() => void);
1954
2191
 
1955
2192
  /**
1956
2193
  * Lazy-validated pair helper functions
@@ -2008,7 +2245,9 @@ declare const registerSideEffects: <DATA extends object, META extends GenericMet
2008
2245
  * useSideEffects('my-syncs', { syncPaths: syncs }) // no O(N²) re-check
2009
2246
  * ```
2010
2247
  */
2011
- declare const syncPairs: <DATA extends object, Depth extends number = DefaultDepth>() => <T extends [ResolvableDeepKey<DATA, Depth>, ResolvableDeepKey<DATA, Depth>][]>(pairs: CheckSyncPairs<DATA, T, Depth>) => ValidatedSyncPairs<DATA>;
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>;
2012
2251
  /**
2013
2252
  * Lazy-validated flip pairs. Curried: `flipPairs<State>()(pairs)`.
2014
2253
  *
@@ -2306,4 +2545,4 @@ declare const applyChangesToObject: <T extends object>(obj: T, changes: AnyChang
2306
2545
  */
2307
2546
  declare const deepClone: <T>(value: T) => T;
2308
2547
 
2309
- 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 PipelineObserver, 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, registerFlipPair, registerListenerLegacy, registerSideEffects, registerSyncPairsBatch, syncPairs, useBufferedField, useKeyboardSelect, useThrottledField, useTransformedField };
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 };