@sladg/apex-state 4.0.0 → 4.6.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 +106 -215
- package/dist/index.d.ts +191 -363
- package/dist/index.js +2 -3404
- package/dist/index.js.map +1 -1
- package/dist/preload.d.ts +2 -0
- package/dist/preload.js +3 -0
- package/dist/preload.js.map +1 -0
- package/dist/testing/index.js +2 -2015
- package/dist/testing/index.js.map +1 -1
- package/package.json +12 -2
package/dist/index.d.ts
CHANGED
|
@@ -284,269 +284,6 @@ 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
|
-
|
|
550
287
|
/**
|
|
551
288
|
* GenericMeta interface
|
|
552
289
|
*
|
|
@@ -597,6 +334,16 @@ interface GenericMeta {
|
|
|
597
334
|
* Used to track changes triggered by computation logic.
|
|
598
335
|
*/
|
|
599
336
|
isComputationChange?: boolean;
|
|
337
|
+
/**
|
|
338
|
+
* Indicates if the change originated from an expression evaluation (BoolLogic/ValueLogic).
|
|
339
|
+
* Used to track changes produced by the expression stage in the pipeline.
|
|
340
|
+
*/
|
|
341
|
+
isExpressionChange?: boolean;
|
|
342
|
+
/**
|
|
343
|
+
* Indicates if the change is a concern output (BoolLogic, ValueLogic, or validator result).
|
|
344
|
+
* Stamped by WASM from the registration_meta provided at expression/listener registration.
|
|
345
|
+
*/
|
|
346
|
+
isConcern?: boolean;
|
|
600
347
|
/**
|
|
601
348
|
* Identifies the originator of the change.
|
|
602
349
|
* Can be a user ID, component name, listener function name, or any identifier string.
|
|
@@ -639,13 +386,6 @@ interface GenericMeta {
|
|
|
639
386
|
type ArrayOfChanges<DATA, META extends GenericMeta = GenericMeta> = {
|
|
640
387
|
[K in DeepKey<DATA>]: [K, DeepValue<DATA, K>, META] | [K, DeepValue<DATA, K>];
|
|
641
388
|
}[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
|
-
}
|
|
649
389
|
|
|
650
390
|
/**
|
|
651
391
|
* Concern type utilities
|
|
@@ -1190,25 +930,105 @@ type ValidatedListeners<DATA extends object = object, META extends GenericMeta =
|
|
|
1190
930
|
[STORE_DATA]: DATA;
|
|
1191
931
|
};
|
|
1192
932
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
933
|
+
type Stage = "input" | "aggregation_write" | "diff" | "sync" | "flip" | "clear_path" | "aggregation_read" | "computation" | "expression" | "listeners" | "apply";
|
|
934
|
+
type ChangeKind = "real" | "redundant" | "breakdown" | "consumed";
|
|
935
|
+
type ChangeContext = "Initial" | "Mutation";
|
|
936
|
+
type Lineage = "Input" | {
|
|
937
|
+
"Derived": {
|
|
938
|
+
parent_id: number;
|
|
939
|
+
via: Stage;
|
|
940
|
+
context: ChangeContext;
|
|
941
|
+
};
|
|
942
|
+
};
|
|
943
|
+
type ChangeAudit = {
|
|
944
|
+
source_path?: string;
|
|
945
|
+
logic_id?: number;
|
|
946
|
+
};
|
|
947
|
+
type TraceEntry = {
|
|
948
|
+
/**
|
|
949
|
+
* Monotonic counter. Parents numbered pipeline-globally, children within parent.
|
|
950
|
+
*/
|
|
951
|
+
seq: number;
|
|
952
|
+
/**
|
|
953
|
+
* Stage or operation name (e.g. "input", "sync", "sync_pair", "listener", "input:skip").
|
|
954
|
+
*/
|
|
955
|
+
stage: string;
|
|
956
|
+
/**
|
|
957
|
+
* Actual input values as JSON. Per-child: only what that operation matched.
|
|
958
|
+
*/
|
|
959
|
+
inputs: Array<unknown>;
|
|
960
|
+
/**
|
|
961
|
+
* Actual output values as JSON. What this operation produced.
|
|
962
|
+
*/
|
|
963
|
+
outputs: Array<unknown>;
|
|
964
|
+
/**
|
|
965
|
+
* Microseconds. Parent = total stage time. Child = per-operation time. 0 when timing unavailable.
|
|
966
|
+
*/
|
|
967
|
+
duration_us: number;
|
|
968
|
+
/**
|
|
969
|
+
* Extra metadata (registration_id, reason, etc.). Omitted when empty.
|
|
970
|
+
*/
|
|
971
|
+
meta: Record<string, unknown>;
|
|
972
|
+
/**
|
|
973
|
+
* Per-operation breakdown. Omitted when empty. Recursive (same struct).
|
|
974
|
+
*/
|
|
975
|
+
children: Array<TraceEntry>;
|
|
976
|
+
};
|
|
977
|
+
type PipelineTrace = {
|
|
978
|
+
entries: Array<TraceEntry>;
|
|
979
|
+
};
|
|
980
|
+
type ListenerError = {
|
|
981
|
+
subscriber_id: number;
|
|
982
|
+
name: string;
|
|
983
|
+
error: string;
|
|
984
|
+
};
|
|
985
|
+
type ClearPathInput = {
|
|
986
|
+
triggers: Array<string>;
|
|
987
|
+
targets: Array<string>;
|
|
988
|
+
};
|
|
989
|
+
type SideEffectsRegistration = {
|
|
990
|
+
registration_id: string;
|
|
991
|
+
sync_pairs: Array<[string, string]>;
|
|
992
|
+
directed_sync_pairs: Array<[string, string]>;
|
|
993
|
+
flip_pairs: Array<[string, string]>;
|
|
994
|
+
aggregation_pairs: Array<unknown>;
|
|
995
|
+
clear_paths: Array<ClearPathInput>;
|
|
996
|
+
computation_pairs: Array<unknown>;
|
|
997
|
+
listeners: Array<ListenerRegistration$1>;
|
|
998
|
+
anchor_path: string | null;
|
|
999
|
+
};
|
|
1000
|
+
type ListenerRegistration$1 = {
|
|
1001
|
+
subscriber_id: number;
|
|
1002
|
+
topic_paths: Array<string>;
|
|
1003
|
+
scope_path: string;
|
|
1004
|
+
name?: string;
|
|
1005
|
+
/**
|
|
1006
|
+
* Whether to dispatch this subscriber immediately on registration with current shadow state.
|
|
1007
|
+
* Used by validators to produce initial validation results without requiring a state change.
|
|
1008
|
+
*/
|
|
1009
|
+
run_on_registration: boolean | null;
|
|
1010
|
+
};
|
|
1011
|
+
type ConcernsRegistration = {
|
|
1012
|
+
/**
|
|
1013
|
+
* Expression registrations (logic, value, options — WASM auto-detects export type from tree_json).
|
|
1014
|
+
*/
|
|
1015
|
+
expressions: Array<ExpressionRegistration>;
|
|
1016
|
+
/**
|
|
1017
|
+
* Subscriber registrations (validators, custom concerns, etc.).
|
|
1018
|
+
* Same format as SideEffectsRegistration.listeners — WASM treats them
|
|
1019
|
+
* identically. TS controls wave_priority at registration time.
|
|
1020
|
+
*/
|
|
1021
|
+
listeners: Array<ListenerRegistration$1>;
|
|
1022
|
+
};
|
|
1023
|
+
type ExpressionRegistration = {
|
|
1024
|
+
output_path: string;
|
|
1025
|
+
tree_json: string;
|
|
1026
|
+
/**
|
|
1027
|
+
* Opaque meta stamped on every change produced by this expression.
|
|
1028
|
+
* TS uses this for concern tagging (e.g. `{ isConcern: true }`).
|
|
1029
|
+
*/
|
|
1030
|
+
meta: unknown | null;
|
|
1031
|
+
};
|
|
1212
1032
|
|
|
1213
1033
|
/**
|
|
1214
1034
|
* WASM Bridge — Thin namespace over Rust/WASM exports.
|
|
@@ -1224,12 +1044,21 @@ interface ConcernRegistration {
|
|
|
1224
1044
|
* @module wasm/bridge
|
|
1225
1045
|
*/
|
|
1226
1046
|
|
|
1227
|
-
/**
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1047
|
+
/**
|
|
1048
|
+
* JS-facing change — derived from Wasm.Change with `value_json` replaced by
|
|
1049
|
+
* parsed `value: unknown` and `meta` typed as GenericMeta.
|
|
1050
|
+
*
|
|
1051
|
+
* `kind`, `lineage`, and `audit` are optional: they are populated on changes
|
|
1052
|
+
* returned from WASM (output) but absent on changes constructed in JS (input).
|
|
1053
|
+
*/
|
|
1054
|
+
interface Change {
|
|
1055
|
+
path: string;
|
|
1056
|
+
value: unknown;
|
|
1057
|
+
meta: GenericMeta;
|
|
1058
|
+
kind?: ChangeKind;
|
|
1059
|
+
lineage?: Lineage;
|
|
1060
|
+
audit?: ChangeAudit;
|
|
1061
|
+
}
|
|
1233
1062
|
/**
|
|
1234
1063
|
* Create a new isolated WASM pipeline instance.
|
|
1235
1064
|
* Each store should call this once and store the result.
|
|
@@ -1243,36 +1072,33 @@ declare const createWasmPipeline: (options?: {
|
|
|
1243
1072
|
shadowInit: (state: object) => void;
|
|
1244
1073
|
shadowDump: () => unknown;
|
|
1245
1074
|
processChanges: (changes: Change[]) => {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
has_work: boolean;
|
|
1075
|
+
changes: Change[];
|
|
1076
|
+
listener_errors: ListenerError[];
|
|
1077
|
+
trace: PipelineTrace | undefined;
|
|
1250
1078
|
};
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1079
|
+
registerSideEffects: (reg: Partial<SideEffectsRegistration>, subscriberFns?: Map<number, (...args: unknown[]) => unknown>) => {
|
|
1080
|
+
sync_changes: Change[];
|
|
1081
|
+
aggregation_changes: Change[];
|
|
1082
|
+
computation_changes: Change[];
|
|
1083
|
+
registered_side_effects: Record<string, ListenerRegistration$1[]>;
|
|
1254
1084
|
};
|
|
1255
|
-
|
|
1085
|
+
unregisterSideEffects: (registrationId: string) => {
|
|
1256
1086
|
sync_changes: Change[];
|
|
1257
1087
|
aggregation_changes: Change[];
|
|
1258
1088
|
computation_changes: Change[];
|
|
1259
|
-
|
|
1089
|
+
registered_side_effects: Record<string, ListenerRegistration$1[]>;
|
|
1260
1090
|
};
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
value_logic_changes: Change[];
|
|
1267
|
-
registered_value_logic_ids: number[];
|
|
1091
|
+
registerConcerns: (reg: Partial<ConcernsRegistration>, subscriberFns?: Map<number, (...args: unknown[]) => unknown>) => {
|
|
1092
|
+
expression_changes: Change[];
|
|
1093
|
+
registered_expression_ids: number[];
|
|
1094
|
+
registered_subscriber_ids: number[];
|
|
1095
|
+
initial_listener_changes: Change[];
|
|
1268
1096
|
};
|
|
1269
1097
|
unregisterConcerns: (registrationId: string) => void;
|
|
1270
1098
|
registerBoolLogic: (outputPath: string, tree: unknown) => number;
|
|
1271
1099
|
unregisterBoolLogic: (logicId: number) => void;
|
|
1272
1100
|
pipelineReset: () => void;
|
|
1273
1101
|
destroy: () => void;
|
|
1274
|
-
getGraphSnapshot: () => GraphSnapshot;
|
|
1275
|
-
validatorSchemas: Map<number, ValidationSchema<unknown>>;
|
|
1276
1102
|
};
|
|
1277
1103
|
type WasmPipeline = ReturnType<typeof createWasmPipeline>;
|
|
1278
1104
|
|
|
@@ -1280,57 +1106,47 @@ type WasmPipeline = ReturnType<typeof createWasmPipeline>;
|
|
|
1280
1106
|
* Apex State Logger — Simplified debug logging with colored console output.
|
|
1281
1107
|
*
|
|
1282
1108
|
* Two log functions:
|
|
1283
|
-
* 1. logPipeline — called once per processChanges with
|
|
1109
|
+
* 1. logPipeline — called once per processChanges with WASM pipeline trace
|
|
1284
1110
|
* 2. logRegistration — called once per register/unregister with graph snapshot
|
|
1285
1111
|
*
|
|
1286
1112
|
* Zero runtime cost when log flag is false (returns no-op logger).
|
|
1113
|
+
*
|
|
1114
|
+
* Responsible for:
|
|
1115
|
+
* - Rendering PipelineTrace entries recursively with [NN]/[NN.MM] numbering
|
|
1116
|
+
* - Color-coding entries by registration_id (deterministic palette) or stage
|
|
1117
|
+
* - Formatting timing as ms from duration_us
|
|
1118
|
+
*
|
|
1119
|
+
* Out of scope:
|
|
1120
|
+
* - Trace collection (handled by Rust pipeline_trace.rs)
|
|
1121
|
+
* - DevTools integration (handled by store/devtools.ts)
|
|
1287
1122
|
*/
|
|
1288
1123
|
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
-
}
|
|
1124
|
+
type GraphSnapshot$1 = Record<string, unknown>;
|
|
1125
|
+
type SideEffectsResult = ReturnType<WasmPipeline['registerSideEffects']>;
|
|
1313
1126
|
interface PipelineLogData {
|
|
1314
1127
|
initialChanges: Change[];
|
|
1315
|
-
trace
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
stateSnapshot?: unknown;
|
|
1128
|
+
/** WASM pipeline trace (undefined if not traced). */
|
|
1129
|
+
trace: PipelineTrace | undefined;
|
|
1130
|
+
/** Final applied changes: always present (can be empty). */
|
|
1131
|
+
appliedChanges: Change[];
|
|
1132
|
+
/** State snapshot after changes (undefined if not captured). */
|
|
1133
|
+
stateSnapshot: unknown | undefined;
|
|
1322
1134
|
}
|
|
1323
1135
|
interface RegistrationLogData {
|
|
1324
|
-
result
|
|
1325
|
-
appliedChanges
|
|
1326
|
-
stateSnapshot
|
|
1327
|
-
durationMs
|
|
1328
|
-
|
|
1329
|
-
|
|
1136
|
+
result: SideEffectsResult | undefined;
|
|
1137
|
+
appliedChanges: Change[];
|
|
1138
|
+
stateSnapshot: unknown | undefined;
|
|
1139
|
+
durationMs: number;
|
|
1140
|
+
}
|
|
1141
|
+
interface RegistrationLogParams {
|
|
1142
|
+
type: 'register' | 'unregister';
|
|
1143
|
+
id: string;
|
|
1144
|
+
snapshot: GraphSnapshot$1;
|
|
1145
|
+
data?: RegistrationLogData;
|
|
1330
1146
|
}
|
|
1331
1147
|
interface ApexLogger {
|
|
1332
1148
|
logPipeline: (data: PipelineLogData) => void;
|
|
1333
|
-
logRegistration: (
|
|
1149
|
+
logRegistration: (params: RegistrationLogParams) => void;
|
|
1334
1150
|
destroy: () => void;
|
|
1335
1151
|
}
|
|
1336
1152
|
|
|
@@ -1345,6 +1161,7 @@ interface ApexLogger {
|
|
|
1345
1161
|
* Singleton connections per proxy — survives StrictMode remounts and HMR reloads.
|
|
1346
1162
|
*/
|
|
1347
1163
|
|
|
1164
|
+
type GraphSnapshot = Record<string, unknown>;
|
|
1348
1165
|
interface DevToolsNotifier {
|
|
1349
1166
|
notifyPipeline: (data: PipelineLogData) => void;
|
|
1350
1167
|
notifyRegistration: (type: 'register' | 'unregister', id: string, snapshot: GraphSnapshot) => void;
|
|
@@ -1518,18 +1335,9 @@ interface MultiPathListener<DATA extends object = object, META extends GenericMe
|
|
|
1518
1335
|
}
|
|
1519
1336
|
/** Listener registration — single-path or multi-path. */
|
|
1520
1337
|
type ListenerRegistration<DATA extends object = object, META extends GenericMeta = GenericMeta, Depth extends number = DefaultDepth> = SinglePathListener<DATA, META, Depth> | MultiPathListener<DATA, META, Depth>;
|
|
1521
|
-
interface ListenerHandlerRef {
|
|
1522
|
-
scope: string | null;
|
|
1523
|
-
fn: (...args: any[]) => unknown;
|
|
1524
|
-
name: string;
|
|
1525
|
-
registrationId: string;
|
|
1526
|
-
}
|
|
1527
1338
|
interface Registrations {
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
sideEffectCleanups: Map<string, () => void>;
|
|
1531
|
-
/** O(1) lookup: subscriber_id -> handler ref. Populated by registerListener. */
|
|
1532
|
-
listenerHandlers: Map<number, ListenerHandlerRef>;
|
|
1339
|
+
/** Cleanup functions keyed by registration ID. Multiple cleanups may exist per registration. */
|
|
1340
|
+
cleanups: Record<string, (() => void)[]>;
|
|
1533
1341
|
}
|
|
1534
1342
|
/** Internal store state (NOT tracked - wrapped in ref()) */
|
|
1535
1343
|
interface InternalState {
|
|
@@ -1551,6 +1359,26 @@ interface StoreInstance<DATA extends object> {
|
|
|
1551
1359
|
_debug: DebugTrack | null;
|
|
1552
1360
|
}
|
|
1553
1361
|
|
|
1362
|
+
interface BaseConcernProps<STATE, PATH extends string> {
|
|
1363
|
+
state: STATE;
|
|
1364
|
+
path: PATH;
|
|
1365
|
+
value: unknown;
|
|
1366
|
+
}
|
|
1367
|
+
interface ConcernType<NAME extends string = string, EXTRA_PROPS = Record<string, unknown>, RETURN_TYPE = unknown> {
|
|
1368
|
+
name: NAME;
|
|
1369
|
+
description: string;
|
|
1370
|
+
/** Evaluated inside effect() - all state accesses are tracked */
|
|
1371
|
+
evaluate: (props: BaseConcernProps<Record<string, unknown>, string> & EXTRA_PROPS) => RETURN_TYPE;
|
|
1372
|
+
}
|
|
1373
|
+
interface ConcernRegistration {
|
|
1374
|
+
id: string;
|
|
1375
|
+
path: string;
|
|
1376
|
+
concernName: string;
|
|
1377
|
+
concern: ConcernType;
|
|
1378
|
+
config: Record<string, unknown>;
|
|
1379
|
+
dispose: () => void;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1554
1382
|
interface ValidationError {
|
|
1555
1383
|
field?: string;
|
|
1556
1384
|
message: string;
|