@directive-run/core 0.3.0 → 0.4.1
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/adapter-utils.cjs.map +1 -1
- package/dist/adapter-utils.d.cts +1 -1
- package/dist/adapter-utils.d.ts +1 -1
- package/dist/adapter-utils.js.map +1 -1
- package/dist/index.cjs +13 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +832 -186
- package/dist/index.d.ts +832 -186
- package/dist/index.js +13 -13
- package/dist/index.js.map +1 -1
- package/dist/migration.cjs.map +1 -1
- package/dist/migration.js.map +1 -1
- package/dist/plugins/index.cjs +1 -1
- package/dist/plugins/index.cjs.map +1 -1
- package/dist/plugins/index.d.cts +86 -19
- package/dist/plugins/index.d.ts +86 -19
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/{plugins-DZljh9NJ.d.cts → plugins-cDWoL7A7.d.cts} +23 -46
- package/dist/{plugins-DZljh9NJ.d.ts → plugins-cDWoL7A7.d.ts} +23 -46
- package/dist/testing.cjs +3 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +143 -27
- package/dist/testing.d.ts +143 -27
- package/dist/testing.js +3 -3
- package/dist/testing.js.map +1 -1
- package/dist/worker.cjs +3 -3
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +3 -3
- package/dist/worker.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Schema, F as Facts, R as Requirement, a as RequirementOutput, b as RetryPolicy, B as BatchConfig, c as ResolverContext, d as SchemaType, e as FactsStore, M as ModuleSchema, T as TypedDerivationsDef, f as TypedEventsDef, E as EffectsDef, g as TypedConstraintsDef, h as TypedResolversDef, i as ModuleHooks, C as CrossModuleDeps, j as CrossModuleDerivationsDef, k as CrossModuleEffectsDef, l as CrossModuleConstraintsDef, m as ModuleDef, n as CreateSystemOptionsSingle, o as SingleModuleSystem, p as ModulesMap, q as CreateSystemOptionsNamed, N as NamespacedSystem, D as DerivationsSchema, r as TypedConstraintDef, s as RequirementOutput$1, I as InferRequirements, P as Plugin, t as DebugConfig, u as ErrorBoundaryConfig, v as InferFacts, w as ExtractSchema, x as RequirementWithId, y as RequirementKeyFn, z as ConstraintsDef, A as ConstraintState, G as ResolversDef, H as ResolverStatus, J as System, K as FactChange, L as FactsSnapshot, O as ReconcileResult, Q as Snapshot, U as DirectiveError, V as RecoveryStrategy, W as RunChangelogEntry, X as ErrorSource, Y as RetryLaterConfig, Z as TimeTravelAPI, _ as SystemConfig } from './plugins-
|
|
2
|
-
export { $ as AnySystem, a0 as BatchItemResult, a1 as BatchResolveResults, a2 as
|
|
1
|
+
import { S as Schema, F as Facts, R as Requirement, a as RequirementOutput, b as RetryPolicy, B as BatchConfig, c as ResolverContext, d as SchemaType, e as FactsStore, M as ModuleSchema, T as TypedDerivationsDef, f as TypedEventsDef, E as EffectsDef, g as TypedConstraintsDef, h as TypedResolversDef, i as ModuleHooks, C as CrossModuleDeps, j as CrossModuleDerivationsDef, k as CrossModuleEffectsDef, l as CrossModuleConstraintsDef, m as ModuleDef, n as CreateSystemOptionsSingle, o as SingleModuleSystem, p as ModulesMap, q as CreateSystemOptionsNamed, N as NamespacedSystem, D as DerivationsSchema, r as TypedConstraintDef, s as RequirementOutput$1, I as InferRequirements, P as Plugin, t as DebugConfig, u as ErrorBoundaryConfig, v as InferFacts, w as ExtractSchema, x as RequirementWithId, y as RequirementKeyFn, z as ConstraintsDef, A as ConstraintState, G as ResolversDef, H as ResolverStatus, J as System, K as FactChange, L as FactsSnapshot, O as ReconcileResult, Q as Snapshot, U as DirectiveError, V as RecoveryStrategy, W as RunChangelogEntry, X as ErrorSource, Y as RetryLaterConfig, Z as TimeTravelAPI, _ as SystemConfig } from './plugins-cDWoL7A7.cjs';
|
|
2
|
+
export { $ as AnySystem, a0 as BatchItemResult, a1 as BatchResolveResults, a2 as CrossModuleConstraintDef, a3 as CrossModuleDerivationFn, a4 as CrossModuleEffectDef, a5 as CrossModuleFactsWithSelf, a6 as DerivationKeys, a7 as DerivationReturnType, a8 as DeriveAccessor, a9 as DispatchEventsFromSchema, aa as DistributableSnapshot, ab as DistributableSnapshotOptions, ac as EffectCleanup, ad as EventPayloadSchema, ae as EventsAccessor, af as EventsAccessorFromSchema, ag as EventsDef, ah as EventsSchema, ai as FactKeys, aj as FactReturnType, ak as FlexibleEventHandler, al as InferDerivations, am as InferEventPayloadFromSchema, an as InferEvents, ao as InferRequirementPayloadFromSchema, ap as InferRequirementTypes, aq as InferSchema, ar as InferSchemaType, as as InferSelectorState, at as MutableNamespacedFacts, au as NamespacedDerivations, av as NamespacedEventsAccessor, aw as NamespacedFacts, ax as ObservableKeys, ay as RequirementExplanation, az as RequirementPayloadSchema, aA as RequirementsSchema, aB as SnapshotMeta, aC as SystemEvent, aD as SystemInspection, aE as SystemMode, aF as SystemSnapshot, aG as TimeTravelState, aH as TypedResolverContext, aI as TypedResolverDef, aJ as UnionEvents, aK as isNamespacedSystem, aL as isSingleModuleSystem } from './plugins-cDWoL7A7.cjs';
|
|
3
3
|
export { D as DistributableSnapshotLike, S as SignedSnapshot, a as SnapshotDiff, b as SnapshotDiffEntry, d as diffSnapshots, i as isSignedSnapshot, c as isSnapshotExpired, s as shallowEqual, e as signSnapshot, v as validateSnapshot, f as verifySnapshotSignature } from './utils-4JrY5fk9.cjs';
|
|
4
4
|
export { DirectiveModuleStructure, ReduxSliceConfig, XStateMachineConfig, ZustandStoreConfig, analyzeReduxSlice, analyzeXStateMachine, analyzeZustandStore, generateMigrationChecklist, generateModuleCode } from './migration.cjs';
|
|
5
5
|
|
|
@@ -252,6 +252,11 @@ interface ChainableSchemaType<T> extends ExtendedSchemaType<T> {
|
|
|
252
252
|
/**
|
|
253
253
|
* Schema type builders for defining fact types.
|
|
254
254
|
*
|
|
255
|
+
* @remarks
|
|
256
|
+
* Each builder returns a chainable {@link ExtendedSchemaType} with validation
|
|
257
|
+
* methods (`.min()`, `.max()`, `.pattern()`, etc.) and dev-mode runtime
|
|
258
|
+
* type checking. Validators are tree-shaken in production builds.
|
|
259
|
+
*
|
|
255
260
|
* @example
|
|
256
261
|
* ```typescript
|
|
257
262
|
* const module = createModule("example", {
|
|
@@ -264,6 +269,8 @@ interface ChainableSchemaType<T> extends ExtendedSchemaType<T> {
|
|
|
264
269
|
* },
|
|
265
270
|
* });
|
|
266
271
|
* ```
|
|
272
|
+
*
|
|
273
|
+
* @public
|
|
267
274
|
*/
|
|
268
275
|
declare const t: {
|
|
269
276
|
/**
|
|
@@ -511,11 +518,12 @@ interface CreateFactsStoreOptions<S extends Schema> {
|
|
|
511
518
|
* Create a reactive facts store backed by a Map with schema validation,
|
|
512
519
|
* batched mutations, and granular key-level subscriptions.
|
|
513
520
|
*
|
|
521
|
+
* @remarks
|
|
514
522
|
* The store is the low-level primitive that powers the `facts` proxy.
|
|
515
523
|
* Most users should use {@link createFacts} or `createModule` instead.
|
|
516
524
|
*
|
|
517
525
|
* @param options - Store configuration including schema, validation settings, and change callbacks
|
|
518
|
-
* @returns A
|
|
526
|
+
* @returns A {@link FactsStore} with get/set/batch/subscribe methods and automatic schema validation
|
|
519
527
|
*
|
|
520
528
|
* @example
|
|
521
529
|
* ```ts
|
|
@@ -531,26 +539,41 @@ interface CreateFactsStoreOptions<S extends Schema> {
|
|
|
531
539
|
* store.set("name", "hello");
|
|
532
540
|
* }); // listeners fire once after batch completes
|
|
533
541
|
* ```
|
|
542
|
+
*
|
|
543
|
+
* @internal
|
|
534
544
|
*/
|
|
535
545
|
declare function createFactsStore<S extends Schema>(options: CreateFactsStoreOptions<S>): FactsStore<S>;
|
|
536
546
|
/**
|
|
537
547
|
* Create a Proxy wrapper around a {@link FactsStore} for clean property-style
|
|
538
548
|
* access (`facts.phase`) with automatic dependency tracking.
|
|
539
549
|
*
|
|
550
|
+
* @remarks
|
|
540
551
|
* Reading a property calls `store.get()` (which tracks the access for
|
|
541
552
|
* auto-tracked derivations). Writing a property calls `store.set()` (which
|
|
542
553
|
* validates against the schema). The proxy also exposes `$store` for direct
|
|
543
554
|
* store access and `$snapshot()` for untracked reads.
|
|
544
555
|
*
|
|
545
556
|
* @param store - The underlying facts store to wrap
|
|
546
|
-
* @param schema - The schema definition
|
|
547
|
-
* @returns A
|
|
557
|
+
* @param schema - The schema definition used for `ownKeys` enumeration
|
|
558
|
+
* @returns A {@link Facts} proxy with property-style get/set and prototype pollution guards
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```ts
|
|
562
|
+
* const store = createFactsStore({ schema: { phase: t.string() } });
|
|
563
|
+
* const facts = createFactsProxy(store, { phase: t.string() });
|
|
564
|
+
*
|
|
565
|
+
* facts.phase = "red";
|
|
566
|
+
* console.log(facts.phase); // "red"
|
|
567
|
+
* ```
|
|
568
|
+
*
|
|
569
|
+
* @internal
|
|
548
570
|
*/
|
|
549
571
|
declare function createFactsProxy<S extends Schema>(store: FactsStore<S>, schema: S): Facts<S>;
|
|
550
572
|
/**
|
|
551
573
|
* Convenience factory that creates both a {@link FactsStore} and its
|
|
552
574
|
* {@link createFactsProxy | proxy wrapper} in a single call.
|
|
553
575
|
*
|
|
576
|
+
* @remarks
|
|
554
577
|
* This is the recommended entry point when you need low-level store access
|
|
555
578
|
* outside of `createModule` / `createSystem`.
|
|
556
579
|
*
|
|
@@ -567,6 +590,8 @@ declare function createFactsProxy<S extends Schema>(store: FactsStore<S>, schema
|
|
|
567
590
|
* console.log(facts.phase); // "red"
|
|
568
591
|
* store.subscribe(["phase"], () => console.log("phase changed"));
|
|
569
592
|
* ```
|
|
593
|
+
*
|
|
594
|
+
* @internal
|
|
570
595
|
*/
|
|
571
596
|
declare function createFacts<S extends Schema>(options: CreateFactsStoreOptions<S>): {
|
|
572
597
|
store: FactsStore<S>;
|
|
@@ -675,6 +700,10 @@ interface ModuleConfigWithDeps<M extends ModuleSchema, Deps extends CrossModuleD
|
|
|
675
700
|
* - Event dispatch (`system.dispatch({ type: "..." })` has autocomplete)
|
|
676
701
|
* - Resolver requirements (`req.payload` is typed based on requirement type)
|
|
677
702
|
*
|
|
703
|
+
* @param id - Unique module identifier (kebab-case recommended)
|
|
704
|
+
* @param config - Module configuration including schema, init, derive, constraints, resolvers, etc.
|
|
705
|
+
* @returns A frozen module definition ready for use with `createSystem`
|
|
706
|
+
*
|
|
678
707
|
* @example
|
|
679
708
|
* ```ts
|
|
680
709
|
* const trafficLight = createModule("traffic-light", {
|
|
@@ -749,6 +778,8 @@ interface ModuleConfigWithDeps<M extends ModuleSchema, Deps extends CrossModuleD
|
|
|
749
778
|
* },
|
|
750
779
|
* });
|
|
751
780
|
* ```
|
|
781
|
+
*
|
|
782
|
+
* @public
|
|
752
783
|
*/
|
|
753
784
|
declare function createModule<const M extends ModuleSchema, const Deps extends CrossModuleDeps>(id: string, config: ModuleConfigWithDeps<M, Deps>): ModuleDef<M>;
|
|
754
785
|
declare function createModule<const M extends ModuleSchema>(id: string, config: ModuleConfig<M>): ModuleDef<M>;
|
|
@@ -758,6 +789,9 @@ declare function createModule<const M extends ModuleSchema>(id: string, config:
|
|
|
758
789
|
* Useful for multi-instance UIs (tabs, panels, multi-tenant) where you need
|
|
759
790
|
* isolated state from the same schema.
|
|
760
791
|
*
|
|
792
|
+
* @param config - Module configuration (same shape as `createModule` minus the `id`)
|
|
793
|
+
* @returns A factory function that accepts a name and returns a `ModuleDef`
|
|
794
|
+
*
|
|
761
795
|
* @example
|
|
762
796
|
* ```typescript
|
|
763
797
|
* const chatRoom = createModuleFactory({
|
|
@@ -776,6 +810,8 @@ declare function createModule<const M extends ModuleSchema>(id: string, config:
|
|
|
776
810
|
* },
|
|
777
811
|
* });
|
|
778
812
|
* ```
|
|
813
|
+
*
|
|
814
|
+
* @public
|
|
779
815
|
*/
|
|
780
816
|
declare function createModuleFactory<const M extends ModuleSchema>(config: ModuleConfig<M>): (name: string) => ModuleDef<M>;
|
|
781
817
|
declare function createModuleFactory<const M extends ModuleSchema, const Deps extends CrossModuleDeps>(config: ModuleConfigWithDeps<M, Deps>): (name: string) => ModuleDef<M>;
|
|
@@ -805,6 +841,14 @@ declare function createModuleFactory<const M extends ModuleSchema, const Deps ex
|
|
|
805
841
|
* - **Single module**: Use `module` prop for direct access without namespace
|
|
806
842
|
* - **Multiple modules**: Use `modules` prop for namespaced access
|
|
807
843
|
*
|
|
844
|
+
* @remarks
|
|
845
|
+
* The system is the top-level runtime object. It owns the reconciliation loop,
|
|
846
|
+
* manages plugins, and exposes reactive accessors for facts, derivations, and events.
|
|
847
|
+
* Call `system.start()` to begin the lifecycle (init → ready → running → settled).
|
|
848
|
+
*
|
|
849
|
+
* @param options - System configuration with either `module` (single) or `modules` (namespaced)
|
|
850
|
+
* @returns A fully-typed {@link System} instance with reactive accessors
|
|
851
|
+
*
|
|
808
852
|
* @example Single module (direct access)
|
|
809
853
|
* ```ts
|
|
810
854
|
* const system = createSystem({ module: counterModule });
|
|
@@ -820,6 +864,8 @@ declare function createModuleFactory<const M extends ModuleSchema, const Deps ex
|
|
|
820
864
|
* system.facts.auth.token // Namespaced access
|
|
821
865
|
* system.events.auth.login() // Namespaced events
|
|
822
866
|
* ```
|
|
867
|
+
*
|
|
868
|
+
* @public
|
|
823
869
|
*/
|
|
824
870
|
declare function createSystem<S extends ModuleSchema>(options: CreateSystemOptionsSingle<S>): SingleModuleSystem<S>;
|
|
825
871
|
declare function createSystem<const Modules extends ModulesMap>(options: CreateSystemOptionsNamed<Modules>): NamespacedSystem<Modules>;
|
|
@@ -864,9 +910,15 @@ declare function createSystem<const Modules extends ModulesMap>(options: CreateS
|
|
|
864
910
|
*/
|
|
865
911
|
|
|
866
912
|
/**
|
|
867
|
-
*
|
|
913
|
+
* Fluent builder interface for constructing {@link ModuleDef} instances step by step.
|
|
914
|
+
*
|
|
915
|
+
* Chain methods like `.schema()`, `.init()`, `.derive()`, `.events()`, and others
|
|
916
|
+
* to configure the module, then call `.build()` to produce the final definition.
|
|
917
|
+
* The builder validates that all schema-declared derivations and events have
|
|
918
|
+
* corresponding implementations before returning.
|
|
868
919
|
*
|
|
869
|
-
* @typeParam M - The module schema type
|
|
920
|
+
* @typeParam M - The module schema type, narrowed after calling `.schema()`.
|
|
921
|
+
* @public
|
|
870
922
|
*/
|
|
871
923
|
interface ModuleBuilder<M extends ModuleSchema = ModuleSchema> {
|
|
872
924
|
/**
|
|
@@ -961,10 +1013,47 @@ interface ResolverDef<M extends ModuleSchema> {
|
|
|
961
1013
|
}) => string;
|
|
962
1014
|
}
|
|
963
1015
|
/**
|
|
964
|
-
* Create a new module builder.
|
|
1016
|
+
* Create a new module using the fluent builder pattern.
|
|
1017
|
+
*
|
|
1018
|
+
* Returns a {@link ModuleBuilder} that lets you declaratively define a module's
|
|
1019
|
+
* schema, initialization, derivations, events, effects, constraints, resolvers,
|
|
1020
|
+
* and lifecycle hooks via method chaining. Call `.build()` at the end to produce
|
|
1021
|
+
* the final {@link ModuleDef}.
|
|
1022
|
+
*
|
|
1023
|
+
* @remarks
|
|
1024
|
+
* The builder validates at build time that every derivation and event declared
|
|
1025
|
+
* in the schema has a corresponding implementation. Missing implementations
|
|
1026
|
+
* cause a descriptive error.
|
|
1027
|
+
*
|
|
1028
|
+
* @param id - Unique identifier for this module, used for namespacing in multi-module systems.
|
|
1029
|
+
* @returns A {@link ModuleBuilder} ready for configuration via method chaining.
|
|
1030
|
+
*
|
|
1031
|
+
* @example
|
|
1032
|
+
* ```typescript
|
|
1033
|
+
* import { module, t } from '@directive-run/core';
|
|
1034
|
+
*
|
|
1035
|
+
* const counter = module("counter")
|
|
1036
|
+
* .schema({
|
|
1037
|
+
* facts: { count: t.number() },
|
|
1038
|
+
* derivations: { doubled: t.number() },
|
|
1039
|
+
* events: { increment: {} },
|
|
1040
|
+
* requirements: {},
|
|
1041
|
+
* })
|
|
1042
|
+
* .init((facts) => {
|
|
1043
|
+
* facts.count = 0;
|
|
1044
|
+
* })
|
|
1045
|
+
* .derive({
|
|
1046
|
+
* doubled: (facts) => facts.count * 2,
|
|
1047
|
+
* })
|
|
1048
|
+
* .events({
|
|
1049
|
+
* increment: (facts) => {
|
|
1050
|
+
* facts.count += 1;
|
|
1051
|
+
* },
|
|
1052
|
+
* })
|
|
1053
|
+
* .build();
|
|
1054
|
+
* ```
|
|
965
1055
|
*
|
|
966
|
-
* @
|
|
967
|
-
* @returns A module builder
|
|
1056
|
+
* @public
|
|
968
1057
|
*/
|
|
969
1058
|
declare function module$1(id: string): ModuleBuilder<ModuleSchema>;
|
|
970
1059
|
|
|
@@ -1082,45 +1171,127 @@ declare function when<M extends ModuleSchema>(condition: WhenFn<M>): WhenBuilder
|
|
|
1082
1171
|
* ```
|
|
1083
1172
|
*/
|
|
1084
1173
|
|
|
1085
|
-
/**
|
|
1174
|
+
/**
|
|
1175
|
+
* Entry point of the system builder, returned by {@link system}.
|
|
1176
|
+
*
|
|
1177
|
+
* Choose `.module()` for a single-module {@link SingleModuleSystem} with direct
|
|
1178
|
+
* fact/derivation access, or `.modules()` for a namespaced {@link NamespacedSystem}
|
|
1179
|
+
* that composes multiple modules.
|
|
1180
|
+
*
|
|
1181
|
+
* @public
|
|
1182
|
+
*/
|
|
1086
1183
|
interface SystemBuilderStart {
|
|
1184
|
+
/**
|
|
1185
|
+
* Configure the system with a single module definition.
|
|
1186
|
+
*
|
|
1187
|
+
* @param mod - The module definition to use as the system's sole module.
|
|
1188
|
+
* @returns A {@link SingleModuleSystemBuilder} for further configuration.
|
|
1189
|
+
*/
|
|
1087
1190
|
module<S extends ModuleSchema>(mod: ModuleDef<S>): SingleModuleSystemBuilder<S>;
|
|
1191
|
+
/**
|
|
1192
|
+
* Configure the system with multiple named modules.
|
|
1193
|
+
*
|
|
1194
|
+
* @param mods - A map of namespace keys to module definitions.
|
|
1195
|
+
* @returns A {@link NamespacedSystemBuilder} for further configuration.
|
|
1196
|
+
*/
|
|
1088
1197
|
modules<const Modules extends ModulesMap>(mods: Modules): NamespacedSystemBuilder<Modules>;
|
|
1089
1198
|
}
|
|
1090
|
-
/**
|
|
1199
|
+
/**
|
|
1200
|
+
* Builder for a single-module system with direct access to facts, derivations, and events.
|
|
1201
|
+
*
|
|
1202
|
+
* @remarks
|
|
1203
|
+
* Use this builder when your system contains exactly one module. The resulting
|
|
1204
|
+
* {@link SingleModuleSystem} exposes facts, derivations, and dispatch without
|
|
1205
|
+
* namespace prefixes.
|
|
1206
|
+
*
|
|
1207
|
+
* @typeParam S - The module schema type.
|
|
1208
|
+
* @public
|
|
1209
|
+
*/
|
|
1091
1210
|
interface SingleModuleSystemBuilder<S extends ModuleSchema> {
|
|
1211
|
+
/** Register plugins that hook into system lifecycle events. */
|
|
1092
1212
|
plugins(plugins: Array<Plugin<ModuleSchema>>): SingleModuleSystemBuilder<S>;
|
|
1213
|
+
/** Enable debug features such as time-travel debugging and snapshot limits. */
|
|
1093
1214
|
debug(config: DebugConfig): SingleModuleSystemBuilder<S>;
|
|
1215
|
+
/** Configure error boundary behavior including recovery strategies. */
|
|
1094
1216
|
errorBoundary(config: ErrorBoundaryConfig): SingleModuleSystemBuilder<S>;
|
|
1217
|
+
/** Set the reconciliation tick interval in milliseconds. */
|
|
1095
1218
|
tickMs(ms: number): SingleModuleSystemBuilder<S>;
|
|
1219
|
+
/** Enable zero-config mode which auto-generates constraints from schema metadata. */
|
|
1096
1220
|
zeroConfig(enabled?: boolean): SingleModuleSystemBuilder<S>;
|
|
1221
|
+
/** Provide initial fact values to hydrate the system on startup. */
|
|
1097
1222
|
initialFacts(facts: Partial<InferFacts<S>>): SingleModuleSystemBuilder<S>;
|
|
1223
|
+
/** Finalize configuration and create the running {@link SingleModuleSystem}. */
|
|
1098
1224
|
build(): SingleModuleSystem<S>;
|
|
1099
1225
|
}
|
|
1100
|
-
/**
|
|
1226
|
+
/**
|
|
1227
|
+
* Builder for a namespaced multi-module system.
|
|
1228
|
+
*
|
|
1229
|
+
* @remarks
|
|
1230
|
+
* Use this builder when composing multiple modules. The resulting
|
|
1231
|
+
* {@link NamespacedSystem} prefixes facts and derivations with the module
|
|
1232
|
+
* namespace key (e.g., `system.facts.auth.token`).
|
|
1233
|
+
*
|
|
1234
|
+
* @typeParam Modules - The modules map type mapping namespace keys to module definitions.
|
|
1235
|
+
* @public
|
|
1236
|
+
*/
|
|
1101
1237
|
interface NamespacedSystemBuilder<Modules extends ModulesMap> {
|
|
1238
|
+
/** Register plugins that hook into system lifecycle events. */
|
|
1102
1239
|
plugins(plugins: Array<Plugin<ModuleSchema>>): NamespacedSystemBuilder<Modules>;
|
|
1240
|
+
/** Enable debug features such as time-travel debugging and snapshot limits. */
|
|
1103
1241
|
debug(config: DebugConfig): NamespacedSystemBuilder<Modules>;
|
|
1242
|
+
/** Configure error boundary behavior including recovery strategies. */
|
|
1104
1243
|
errorBoundary(config: ErrorBoundaryConfig): NamespacedSystemBuilder<Modules>;
|
|
1244
|
+
/** Set the reconciliation tick interval in milliseconds. */
|
|
1105
1245
|
tickMs(ms: number): NamespacedSystemBuilder<Modules>;
|
|
1246
|
+
/** Enable zero-config mode which auto-generates constraints from schema metadata. */
|
|
1106
1247
|
zeroConfig(enabled?: boolean): NamespacedSystemBuilder<Modules>;
|
|
1248
|
+
/** Provide initial fact values keyed by module namespace for hydration on startup. */
|
|
1107
1249
|
initialFacts(facts: Partial<{
|
|
1108
1250
|
[K in keyof Modules]: Partial<InferFacts<ExtractSchema<Modules[K]>>>;
|
|
1109
1251
|
}>): NamespacedSystemBuilder<Modules>;
|
|
1252
|
+
/** Control the order in which modules are initialized: automatic, declaration order, or explicit. */
|
|
1110
1253
|
initOrder(order: "auto" | "declaration" | Array<keyof Modules & string>): NamespacedSystemBuilder<Modules>;
|
|
1254
|
+
/** Finalize configuration and create the running {@link NamespacedSystem}. */
|
|
1111
1255
|
build(): NamespacedSystem<Modules>;
|
|
1112
1256
|
}
|
|
1113
1257
|
/**
|
|
1114
|
-
* Create a system using the fluent builder pattern.
|
|
1115
|
-
* Choose `.module()` for single-module or `.modules()` for namespaced.
|
|
1258
|
+
* Create a Directive system using the fluent builder pattern.
|
|
1116
1259
|
*
|
|
1117
|
-
* @
|
|
1260
|
+
* Returns a {@link SystemBuilderStart} that branches into either a
|
|
1261
|
+
* single-module path (`.module()`) or a namespaced multi-module path
|
|
1262
|
+
* (`.modules()`). Chain configuration methods like `.plugins()`, `.debug()`,
|
|
1263
|
+
* and `.errorBoundary()`, then call `.build()` to produce the running system.
|
|
1264
|
+
*
|
|
1265
|
+
* @remarks
|
|
1266
|
+
* This is a convenience wrapper around {@link createSystem}. Both produce
|
|
1267
|
+
* identical systems; the builder simply offers a more discoverable,
|
|
1268
|
+
* chainable API.
|
|
1269
|
+
*
|
|
1270
|
+
* @returns A {@link SystemBuilderStart} ready to receive a module or modules map.
|
|
1271
|
+
*
|
|
1272
|
+
* @example Single-module system
|
|
1118
1273
|
* ```typescript
|
|
1274
|
+
* import { system } from '@directive-run/core';
|
|
1275
|
+
*
|
|
1119
1276
|
* const sys = system()
|
|
1120
1277
|
* .module(counterModule)
|
|
1121
1278
|
* .plugins([loggingPlugin()])
|
|
1279
|
+
* .debug({ timeTravel: true })
|
|
1280
|
+
* .build();
|
|
1281
|
+
* ```
|
|
1282
|
+
*
|
|
1283
|
+
* @example Multi-module namespaced system
|
|
1284
|
+
* ```typescript
|
|
1285
|
+
* import { system } from '@directive-run/core';
|
|
1286
|
+
*
|
|
1287
|
+
* const sys = system()
|
|
1288
|
+
* .modules({ auth: authModule, cart: cartModule })
|
|
1289
|
+
* .plugins([loggingPlugin()])
|
|
1290
|
+
* .initOrder(["auth", "cart"])
|
|
1122
1291
|
* .build();
|
|
1123
1292
|
* ```
|
|
1293
|
+
*
|
|
1294
|
+
* @public
|
|
1124
1295
|
*/
|
|
1125
1296
|
declare function system(): SystemBuilderStart;
|
|
1126
1297
|
|
|
@@ -1278,25 +1449,37 @@ declare function createSystemWithStatus<M extends ModuleSchema>(options: CreateS
|
|
|
1278
1449
|
*/
|
|
1279
1450
|
|
|
1280
1451
|
/**
|
|
1281
|
-
* Generate a stable
|
|
1282
|
-
*
|
|
1452
|
+
* Generate a stable identity string for a requirement.
|
|
1453
|
+
*
|
|
1454
|
+
* When no custom key function is provided, the ID is formed from the
|
|
1455
|
+
* requirement's `type` plus a deterministic JSON serialization of its
|
|
1456
|
+
* remaining properties. A custom {@link RequirementKeyFn} can override
|
|
1457
|
+
* this to control deduplication granularity.
|
|
1458
|
+
*
|
|
1459
|
+
* @param req - The requirement to generate an ID for.
|
|
1460
|
+
* @param keyFn - Optional custom key function that overrides the default identity logic.
|
|
1461
|
+
* @returns A stable string that uniquely identifies this requirement for deduplication.
|
|
1462
|
+
*
|
|
1463
|
+
* @public
|
|
1283
1464
|
*/
|
|
1284
1465
|
declare function generateRequirementId(req: Requirement, keyFn?: RequirementKeyFn): string;
|
|
1285
1466
|
/**
|
|
1286
|
-
*
|
|
1467
|
+
* Create a typed requirement factory for a given requirement type string.
|
|
1287
1468
|
*
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1469
|
+
* Returns a function that, when called with a properties object, produces a
|
|
1470
|
+
* fully-typed {@link Requirement} whose `type` field is the literal `T`.
|
|
1471
|
+
* This is the recommended way to build requirements inside constraint
|
|
1472
|
+
* definitions because it keeps the type string in one place and gives you
|
|
1473
|
+
* full TypeScript inference on the payload.
|
|
1290
1474
|
*
|
|
1291
|
-
* @param type - The requirement type string
|
|
1292
|
-
* @returns A factory
|
|
1475
|
+
* @param type - The requirement type string (e.g. `"FETCH_USER"`).
|
|
1476
|
+
* @returns A factory that merges `type` with arbitrary properties into a typed requirement.
|
|
1293
1477
|
*
|
|
1294
1478
|
* @example
|
|
1295
1479
|
* ```typescript
|
|
1296
|
-
* // Create a requirement factory
|
|
1297
1480
|
* const fetchUser = req("FETCH_USER");
|
|
1298
1481
|
*
|
|
1299
|
-
* // Use
|
|
1482
|
+
* // Use inside a module's constraint definition
|
|
1300
1483
|
* constraints: {
|
|
1301
1484
|
* needsUser: {
|
|
1302
1485
|
* when: (facts) => facts.userId && !facts.user,
|
|
@@ -1304,89 +1487,149 @@ declare function generateRequirementId(req: Requirement, keyFn?: RequirementKeyF
|
|
|
1304
1487
|
* },
|
|
1305
1488
|
* }
|
|
1306
1489
|
*
|
|
1307
|
-
* //
|
|
1490
|
+
* // Produces: { type: "FETCH_USER", userId: 123, priority: "high" }
|
|
1308
1491
|
* ```
|
|
1492
|
+
*
|
|
1493
|
+
* @public
|
|
1309
1494
|
*/
|
|
1310
1495
|
declare function req<T extends string>(type: T): <P extends Record<string, unknown>>(props: P) => Requirement & {
|
|
1311
1496
|
type: T;
|
|
1312
1497
|
} & P;
|
|
1313
1498
|
/**
|
|
1314
|
-
*
|
|
1499
|
+
* Type-narrowing guard that checks whether a requirement's `type` matches the
|
|
1500
|
+
* given string literal.
|
|
1501
|
+
*
|
|
1502
|
+
* After this guard returns `true`, TypeScript narrows `req` to
|
|
1503
|
+
* `Requirement & { type: T }`, giving you access to type-specific fields.
|
|
1504
|
+
*
|
|
1505
|
+
* @param req - The requirement to test.
|
|
1506
|
+
* @param type - The expected type string to match against.
|
|
1507
|
+
* @returns `true` when `req.type === type`.
|
|
1508
|
+
*
|
|
1509
|
+
* @public
|
|
1315
1510
|
*/
|
|
1316
1511
|
declare function isRequirementType<T extends string>(req: Requirement, type: T): req is Requirement & {
|
|
1317
1512
|
type: T;
|
|
1318
1513
|
};
|
|
1319
1514
|
/**
|
|
1320
|
-
* Create a type
|
|
1321
|
-
*
|
|
1515
|
+
* Create a type-guard function suitable for a resolver's `requirement`
|
|
1516
|
+
* predicate field.
|
|
1517
|
+
*
|
|
1518
|
+
* @remarks
|
|
1519
|
+
* The returned predicate narrows any {@link Requirement} to the concrete
|
|
1520
|
+
* type `R` (or `Requirement & { type: T }` when no explicit generic is
|
|
1521
|
+
* provided). This is a cleaner alternative to writing verbose inline type
|
|
1522
|
+
* guards in every resolver definition.
|
|
1523
|
+
*
|
|
1524
|
+
* @param type - The requirement type string to match.
|
|
1525
|
+
* @returns A predicate that returns `true` for requirements whose `type` matches, narrowing the value for downstream callbacks like `key` and `resolve`.
|
|
1322
1526
|
*
|
|
1323
1527
|
* @example
|
|
1324
1528
|
* ```typescript
|
|
1325
|
-
* // With explicit requirement
|
|
1326
|
-
* interface
|
|
1327
|
-
* requirement: forType<
|
|
1328
|
-
* key: (req) => req.userId, // req is
|
|
1529
|
+
* // With an explicit requirement interface (recommended for complex payloads)
|
|
1530
|
+
* interface FetchUserReq { type: "FETCH_USER"; userId: string }
|
|
1531
|
+
* requirement: forType<FetchUserReq>("FETCH_USER"),
|
|
1532
|
+
* key: (req) => req.userId, // req is FetchUserReq
|
|
1329
1533
|
*
|
|
1330
|
-
* //
|
|
1534
|
+
* // With a simple string literal
|
|
1331
1535
|
* requirement: forType("FETCH_USER"),
|
|
1332
1536
|
* key: (req) => req.type, // req is Requirement & { type: "FETCH_USER" }
|
|
1333
1537
|
* ```
|
|
1538
|
+
*
|
|
1539
|
+
* @public
|
|
1334
1540
|
*/
|
|
1335
1541
|
declare function forType<R extends Requirement>(type: R["type"]): (req: Requirement) => req is R;
|
|
1336
1542
|
declare function forType<T extends string>(type: T): (req: Requirement) => req is Requirement & {
|
|
1337
1543
|
type: T;
|
|
1338
1544
|
};
|
|
1339
1545
|
/**
|
|
1340
|
-
* A
|
|
1546
|
+
* A deduplicated collection of {@link RequirementWithId} entries keyed by
|
|
1547
|
+
* their identity string.
|
|
1341
1548
|
*
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1549
|
+
* @remarks
|
|
1550
|
+
* Requirements are uniquely identified by their ID (generated from type +
|
|
1551
|
+
* properties via {@link generateRequirementId}). When adding a requirement
|
|
1552
|
+
* whose ID already exists, the first entry wins and the duplicate is
|
|
1553
|
+
* silently ignored. The {@link RequirementSet.diff | diff} method computes
|
|
1554
|
+
* added, removed, and unchanged entries relative to another set, which the
|
|
1555
|
+
* engine uses during reconciliation.
|
|
1344
1556
|
*
|
|
1345
1557
|
* @example
|
|
1346
1558
|
* ```typescript
|
|
1347
1559
|
* const set = new RequirementSet();
|
|
1348
|
-
*
|
|
1349
|
-
* //
|
|
1350
|
-
* set.add(createRequirementWithId({ type: "FETCH_USER", userId: 1 }, "constraint1"));
|
|
1351
|
-
* set.add(createRequirementWithId({ type: "FETCH_USER", userId: 1 }, "constraint2")); // Ignored (duplicate)
|
|
1352
|
-
*
|
|
1353
|
-
* // Check and retrieve
|
|
1560
|
+
* set.add(createRequirementWithId({ type: "FETCH_USER", userId: 1 }, "c1"));
|
|
1561
|
+
* set.add(createRequirementWithId({ type: "FETCH_USER", userId: 1 }, "c2")); // ignored
|
|
1354
1562
|
* console.log(set.size); // 1
|
|
1355
|
-
*
|
|
1356
|
-
*
|
|
1357
|
-
*
|
|
1358
|
-
* const
|
|
1359
|
-
*
|
|
1360
|
-
* const { added, removed } = newSet.diff(set);
|
|
1361
|
-
* // added: [{ type: "FETCH_USER", userId: 2 }]
|
|
1362
|
-
* // removed: [{ type: "FETCH_USER", userId: 1 }]
|
|
1563
|
+
*
|
|
1564
|
+
* const next = new RequirementSet();
|
|
1565
|
+
* next.add(createRequirementWithId({ type: "FETCH_USER", userId: 2 }, "c1"));
|
|
1566
|
+
* const { added, removed } = next.diff(set);
|
|
1567
|
+
* // added has userId: 2, removed has userId: 1
|
|
1363
1568
|
* ```
|
|
1569
|
+
*
|
|
1570
|
+
* @public
|
|
1364
1571
|
*/
|
|
1365
1572
|
declare class RequirementSet {
|
|
1366
1573
|
private map;
|
|
1367
1574
|
/**
|
|
1368
|
-
* Add a requirement to the set.
|
|
1369
|
-
*
|
|
1370
|
-
* @param req - The requirement with its computed ID
|
|
1575
|
+
* Add a requirement to the set (first-wins deduplication).
|
|
1576
|
+
*
|
|
1577
|
+
* @param req - The requirement with its computed ID to insert.
|
|
1371
1578
|
*/
|
|
1372
1579
|
add(req: RequirementWithId): void;
|
|
1373
|
-
/**
|
|
1580
|
+
/**
|
|
1581
|
+
* Remove a requirement by its identity string.
|
|
1582
|
+
*
|
|
1583
|
+
* @param id - The requirement identity string to remove.
|
|
1584
|
+
* @returns `true` if the requirement existed and was removed.
|
|
1585
|
+
*/
|
|
1374
1586
|
remove(id: string): boolean;
|
|
1375
|
-
/**
|
|
1587
|
+
/**
|
|
1588
|
+
* Check whether a requirement with the given ID is in the set.
|
|
1589
|
+
*
|
|
1590
|
+
* @param id - The requirement identity string to look up.
|
|
1591
|
+
* @returns `true` if the set contains a requirement with this ID.
|
|
1592
|
+
*/
|
|
1376
1593
|
has(id: string): boolean;
|
|
1377
|
-
/**
|
|
1594
|
+
/**
|
|
1595
|
+
* Retrieve a requirement by its identity string.
|
|
1596
|
+
*
|
|
1597
|
+
* @param id - The requirement identity string to look up.
|
|
1598
|
+
* @returns The matching requirement, or `undefined` if not found.
|
|
1599
|
+
*/
|
|
1378
1600
|
get(id: string): RequirementWithId | undefined;
|
|
1379
|
-
/**
|
|
1601
|
+
/**
|
|
1602
|
+
* Return a snapshot array of all requirements in the set.
|
|
1603
|
+
*
|
|
1604
|
+
* @returns A new array containing every {@link RequirementWithId} in insertion order.
|
|
1605
|
+
*/
|
|
1380
1606
|
all(): RequirementWithId[];
|
|
1381
|
-
/**
|
|
1607
|
+
/**
|
|
1608
|
+
* Return a snapshot array of all requirement identity strings.
|
|
1609
|
+
*
|
|
1610
|
+
* @returns A new array of ID strings in insertion order.
|
|
1611
|
+
*/
|
|
1382
1612
|
ids(): string[];
|
|
1383
|
-
/**
|
|
1613
|
+
/**
|
|
1614
|
+
* The number of requirements currently in the set.
|
|
1615
|
+
*/
|
|
1384
1616
|
get size(): number;
|
|
1385
|
-
/**
|
|
1617
|
+
/**
|
|
1618
|
+
* Remove all requirements from the set.
|
|
1619
|
+
*/
|
|
1386
1620
|
clear(): void;
|
|
1387
|
-
/**
|
|
1621
|
+
/**
|
|
1622
|
+
* Create a shallow copy of this set.
|
|
1623
|
+
*
|
|
1624
|
+
* @returns A new {@link RequirementSet} containing the same entries.
|
|
1625
|
+
*/
|
|
1388
1626
|
clone(): RequirementSet;
|
|
1389
|
-
/**
|
|
1627
|
+
/**
|
|
1628
|
+
* Compute the difference between this set and another.
|
|
1629
|
+
*
|
|
1630
|
+
* @param other - The previous set to compare against.
|
|
1631
|
+
* @returns An object with `added` (in this but not other), `removed` (in other but not this), and `unchanged` arrays.
|
|
1632
|
+
*/
|
|
1390
1633
|
diff(other: RequirementSet): {
|
|
1391
1634
|
added: RequirementWithId[];
|
|
1392
1635
|
removed: RequirementWithId[];
|
|
@@ -1491,44 +1734,126 @@ declare function createDerivationsManager<S extends Schema, D extends Derivation
|
|
|
1491
1734
|
* ```
|
|
1492
1735
|
*/
|
|
1493
1736
|
|
|
1737
|
+
/**
|
|
1738
|
+
* Manager returned by {@link createEffectsManager} that runs fire-and-forget
|
|
1739
|
+
* side effects after facts stabilize.
|
|
1740
|
+
*
|
|
1741
|
+
* @internal
|
|
1742
|
+
*/
|
|
1494
1743
|
interface EffectsManager<_S extends Schema = Schema> {
|
|
1495
|
-
/**
|
|
1744
|
+
/**
|
|
1745
|
+
* Run all effects whose tracked dependencies overlap with `changedKeys`.
|
|
1746
|
+
*
|
|
1747
|
+
* @remarks
|
|
1748
|
+
* Effects with no recorded dependencies (first run or auto-tracked with no
|
|
1749
|
+
* reads) run on any change. After execution, a snapshot of current facts is
|
|
1750
|
+
* stored for the `prev` parameter on the next invocation.
|
|
1751
|
+
*
|
|
1752
|
+
* @param changedKeys - Fact keys that changed since the last run.
|
|
1753
|
+
*/
|
|
1496
1754
|
runEffects(changedKeys: Set<string>): Promise<void>;
|
|
1497
|
-
/**
|
|
1755
|
+
/**
|
|
1756
|
+
* Run every enabled effect unconditionally, regardless of dependencies.
|
|
1757
|
+
*/
|
|
1498
1758
|
runAll(): Promise<void>;
|
|
1499
|
-
/**
|
|
1759
|
+
/**
|
|
1760
|
+
* Disable an effect so it is skipped during subsequent runs.
|
|
1761
|
+
*
|
|
1762
|
+
* @param id - The effect definition ID.
|
|
1763
|
+
*/
|
|
1500
1764
|
disable(id: string): void;
|
|
1501
|
-
/**
|
|
1765
|
+
/**
|
|
1766
|
+
* Re-enable a previously disabled effect.
|
|
1767
|
+
*
|
|
1768
|
+
* @param id - The effect definition ID.
|
|
1769
|
+
*/
|
|
1502
1770
|
enable(id: string): void;
|
|
1503
|
-
/**
|
|
1771
|
+
/**
|
|
1772
|
+
* Check whether an effect is currently enabled.
|
|
1773
|
+
*
|
|
1774
|
+
* @param id - The effect definition ID.
|
|
1775
|
+
* @returns `true` if the effect has not been disabled.
|
|
1776
|
+
*/
|
|
1504
1777
|
isEnabled(id: string): boolean;
|
|
1505
|
-
/**
|
|
1778
|
+
/**
|
|
1779
|
+
* Invoke every stored cleanup function and mark the manager as stopped.
|
|
1780
|
+
*
|
|
1781
|
+
* @remarks
|
|
1782
|
+
* After this call, any cleanup functions returned by in-flight async effects
|
|
1783
|
+
* will be invoked immediately rather than stored.
|
|
1784
|
+
*/
|
|
1506
1785
|
cleanupAll(): void;
|
|
1507
|
-
/**
|
|
1786
|
+
/**
|
|
1787
|
+
* Register additional effect definitions at runtime (used for dynamic
|
|
1788
|
+
* module registration).
|
|
1789
|
+
*
|
|
1790
|
+
* @param newDefs - New effect definitions to merge into the manager.
|
|
1791
|
+
*/
|
|
1508
1792
|
registerDefinitions(newDefs: EffectsDef<Schema>): void;
|
|
1509
1793
|
}
|
|
1510
|
-
/**
|
|
1794
|
+
/**
|
|
1795
|
+
* Configuration options accepted by {@link createEffectsManager}.
|
|
1796
|
+
*
|
|
1797
|
+
* @internal
|
|
1798
|
+
*/
|
|
1511
1799
|
interface CreateEffectsOptions<S extends Schema> {
|
|
1800
|
+
/** Effect definitions keyed by ID. */
|
|
1512
1801
|
definitions: EffectsDef<S>;
|
|
1802
|
+
/** Proxy-based facts object passed to effect `run()` functions. */
|
|
1513
1803
|
facts: Facts<S>;
|
|
1804
|
+
/** Underlying fact store used for `batch()` coalescing of mutations. */
|
|
1514
1805
|
store: FactsStore<S>;
|
|
1515
|
-
/**
|
|
1806
|
+
/** Called when an effect executes, with the fact keys that triggered it. */
|
|
1516
1807
|
onRun?: (id: string, deps: string[]) => void;
|
|
1517
|
-
/**
|
|
1808
|
+
/** Called when an effect's `run()` or cleanup function throws. */
|
|
1518
1809
|
onError?: (id: string, error: unknown) => void;
|
|
1519
1810
|
}
|
|
1520
1811
|
/**
|
|
1521
1812
|
* Create a manager for fire-and-forget side effects that run after facts
|
|
1522
1813
|
* stabilize.
|
|
1523
1814
|
*
|
|
1524
|
-
*
|
|
1525
|
-
*
|
|
1526
|
-
*
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1815
|
+
* @remarks
|
|
1816
|
+
* Effects support two dependency modes:
|
|
1817
|
+
*
|
|
1818
|
+
* - **Auto-tracked** (no `deps`): Dependencies are re-tracked on every run
|
|
1819
|
+
* via {@link withTracking}, so conditional fact reads are always captured.
|
|
1820
|
+
* Only synchronous reads are tracked; reads after an `await` are invisible.
|
|
1821
|
+
*
|
|
1822
|
+
* - **Explicit `deps`**: A fixed array of fact keys declared on the definition.
|
|
1823
|
+
* Preferred for async effects where auto-tracking cannot cross `await`
|
|
1824
|
+
* boundaries.
|
|
1825
|
+
*
|
|
1826
|
+
* Each effect can return a cleanup function that runs before the next
|
|
1827
|
+
* execution or when {@link EffectsManager.cleanupAll | cleanupAll} is called.
|
|
1828
|
+
* Errors in effects are isolated via try-catch and never break the
|
|
1829
|
+
* reconciliation loop. Synchronous fact mutations inside effects are
|
|
1830
|
+
* coalesced with `store.batch()`.
|
|
1831
|
+
*
|
|
1832
|
+
* @param options - Configuration including effect definitions, facts proxy,
|
|
1833
|
+
* store, and lifecycle callbacks.
|
|
1834
|
+
* @returns An {@link EffectsManager} for running, enabling/disabling, and
|
|
1835
|
+
* cleaning up effects.
|
|
1836
|
+
*
|
|
1837
|
+
* @example
|
|
1838
|
+
* ```typescript
|
|
1839
|
+
* const effects = createEffectsManager({
|
|
1840
|
+
* definitions: {
|
|
1841
|
+
* logPhase: {
|
|
1842
|
+
* run: (facts, prev) => {
|
|
1843
|
+
* if (prev?.phase !== facts.phase) {
|
|
1844
|
+
* console.log(`Phase changed to ${facts.phase}`);
|
|
1845
|
+
* }
|
|
1846
|
+
* },
|
|
1847
|
+
* },
|
|
1848
|
+
* },
|
|
1849
|
+
* facts: factsProxy,
|
|
1850
|
+
* store: factsStore,
|
|
1851
|
+
* });
|
|
1852
|
+
*
|
|
1853
|
+
* await effects.runEffects(new Set(["phase"]));
|
|
1854
|
+
* ```
|
|
1529
1855
|
*
|
|
1530
|
-
* @
|
|
1531
|
-
* @returns An `EffectsManager` with runEffects/runAll/enable/disable/cleanupAll methods
|
|
1856
|
+
* @internal
|
|
1532
1857
|
*/
|
|
1533
1858
|
declare function createEffectsManager<S extends Schema>(options: CreateEffectsOptions<S>): EffectsManager<S>;
|
|
1534
1859
|
|
|
@@ -1542,54 +1867,155 @@ declare function createEffectsManager<S extends Schema>(options: CreateEffectsOp
|
|
|
1542
1867
|
* - Error isolation
|
|
1543
1868
|
*/
|
|
1544
1869
|
|
|
1870
|
+
/**
|
|
1871
|
+
* Manager returned by {@link createConstraintsManager} that evaluates
|
|
1872
|
+
* constraint rules against the current facts and produces unmet
|
|
1873
|
+
* {@link RequirementWithId | requirements}.
|
|
1874
|
+
*
|
|
1875
|
+
* @internal
|
|
1876
|
+
*/
|
|
1545
1877
|
interface ConstraintsManager<_S extends Schema> {
|
|
1546
|
-
/**
|
|
1878
|
+
/**
|
|
1879
|
+
* Evaluate all enabled constraints and return unmet requirements.
|
|
1880
|
+
*
|
|
1881
|
+
* @remarks
|
|
1882
|
+
* On the first call (or when `changedKeys` is empty), every enabled
|
|
1883
|
+
* constraint is evaluated. On subsequent calls, only constraints whose
|
|
1884
|
+
* tracked dependencies overlap with `changedKeys` are re-evaluated.
|
|
1885
|
+
* Sync constraints run first, async constraints run in parallel, and
|
|
1886
|
+
* `after` ordering is respected across multiple passes.
|
|
1887
|
+
*
|
|
1888
|
+
* @param changedKeys - Fact keys that changed since the last evaluation.
|
|
1889
|
+
* When omitted or empty, all constraints are evaluated.
|
|
1890
|
+
* @returns An array of {@link RequirementWithId} representing unmet requirements.
|
|
1891
|
+
*/
|
|
1547
1892
|
evaluate(changedKeys?: Set<string>): Promise<RequirementWithId[]>;
|
|
1548
|
-
/**
|
|
1893
|
+
/**
|
|
1894
|
+
* Get the current state of a constraint by its definition ID.
|
|
1895
|
+
*
|
|
1896
|
+
* @param id - The constraint definition ID.
|
|
1897
|
+
* @returns The {@link ConstraintState}, or `undefined` if the ID is unknown.
|
|
1898
|
+
*/
|
|
1549
1899
|
getState(id: string): ConstraintState | undefined;
|
|
1550
|
-
/**
|
|
1900
|
+
/**
|
|
1901
|
+
* Get the state of every registered constraint.
|
|
1902
|
+
*
|
|
1903
|
+
* @returns An array of all {@link ConstraintState} objects.
|
|
1904
|
+
*/
|
|
1551
1905
|
getAllStates(): ConstraintState[];
|
|
1552
|
-
/**
|
|
1906
|
+
/**
|
|
1907
|
+
* Disable a constraint so it is skipped during evaluation.
|
|
1908
|
+
*
|
|
1909
|
+
* @param id - The constraint definition ID.
|
|
1910
|
+
*/
|
|
1553
1911
|
disable(id: string): void;
|
|
1554
|
-
/**
|
|
1912
|
+
/**
|
|
1913
|
+
* Re-enable a previously disabled constraint.
|
|
1914
|
+
*
|
|
1915
|
+
* @param id - The constraint definition ID.
|
|
1916
|
+
*/
|
|
1555
1917
|
enable(id: string): void;
|
|
1556
|
-
/**
|
|
1918
|
+
/**
|
|
1919
|
+
* Mark all constraints that depend on `factKey` as dirty so they are
|
|
1920
|
+
* re-evaluated on the next {@link ConstraintsManager.evaluate | evaluate} call.
|
|
1921
|
+
*
|
|
1922
|
+
* @param factKey - The fact store key that changed.
|
|
1923
|
+
*/
|
|
1557
1924
|
invalidate(factKey: string): void;
|
|
1558
|
-
/**
|
|
1925
|
+
/**
|
|
1926
|
+
* Get the auto-tracked or explicit dependency set for a constraint.
|
|
1927
|
+
*
|
|
1928
|
+
* @param id - The constraint definition ID.
|
|
1929
|
+
* @returns A `Set` of fact keys, or `undefined` if no dependencies have been recorded.
|
|
1930
|
+
*/
|
|
1559
1931
|
getDependencies(id: string): Set<string> | undefined;
|
|
1560
|
-
/**
|
|
1932
|
+
/**
|
|
1933
|
+
* Record that a constraint's resolver completed successfully, unblocking
|
|
1934
|
+
* any constraints that list it in their `after` array.
|
|
1935
|
+
*
|
|
1936
|
+
* @param constraintId - The constraint definition ID whose resolver finished.
|
|
1937
|
+
*/
|
|
1561
1938
|
markResolved(constraintId: string): void;
|
|
1562
|
-
/**
|
|
1939
|
+
/**
|
|
1940
|
+
* Check whether a constraint is currently disabled.
|
|
1941
|
+
*
|
|
1942
|
+
* @param id - The constraint definition ID.
|
|
1943
|
+
* @returns `true` if the constraint has been disabled via {@link ConstraintsManager.disable | disable}.
|
|
1944
|
+
*/
|
|
1563
1945
|
isDisabled(id: string): boolean;
|
|
1564
|
-
/**
|
|
1946
|
+
/**
|
|
1947
|
+
* Check whether a constraint has been marked as resolved.
|
|
1948
|
+
*
|
|
1949
|
+
* @param constraintId - The constraint definition ID.
|
|
1950
|
+
* @returns `true` if {@link ConstraintsManager.markResolved | markResolved} was called for this constraint.
|
|
1951
|
+
*/
|
|
1565
1952
|
isResolved(constraintId: string): boolean;
|
|
1566
|
-
/**
|
|
1953
|
+
/**
|
|
1954
|
+
* Register additional constraint definitions at runtime (used for dynamic
|
|
1955
|
+
* module registration).
|
|
1956
|
+
*
|
|
1957
|
+
* @remarks
|
|
1958
|
+
* Rebuilds the topological order and reverse dependency map so new `after`
|
|
1959
|
+
* dependencies are validated for cycles and indexed.
|
|
1960
|
+
*
|
|
1961
|
+
* @param newDefs - New constraint definitions to merge into the manager.
|
|
1962
|
+
*/
|
|
1567
1963
|
registerDefinitions(newDefs: ConstraintsDef<Schema>): void;
|
|
1568
1964
|
}
|
|
1569
|
-
/**
|
|
1965
|
+
/**
|
|
1966
|
+
* Configuration options accepted by {@link createConstraintsManager}.
|
|
1967
|
+
*
|
|
1968
|
+
* @internal
|
|
1969
|
+
*/
|
|
1570
1970
|
interface CreateConstraintsOptions<S extends Schema> {
|
|
1971
|
+
/** Constraint definitions keyed by ID. */
|
|
1571
1972
|
definitions: ConstraintsDef<S>;
|
|
1973
|
+
/** Proxy-based facts object used to evaluate `when()` predicates. */
|
|
1572
1974
|
facts: Facts<S>;
|
|
1573
|
-
/** Custom key functions for
|
|
1975
|
+
/** Custom key functions for requirement deduplication, keyed by constraint ID. */
|
|
1574
1976
|
requirementKeys?: Record<string, RequirementKeyFn>;
|
|
1575
|
-
/** Default timeout for async
|
|
1977
|
+
/** Default timeout in milliseconds for async constraint evaluation (defaults to 5 000). */
|
|
1576
1978
|
defaultTimeout?: number;
|
|
1577
|
-
/**
|
|
1979
|
+
/** Called after each constraint evaluation with the constraint ID and whether `when()` was active. */
|
|
1578
1980
|
onEvaluate?: (id: string, active: boolean) => void;
|
|
1579
|
-
/**
|
|
1981
|
+
/** Called when a constraint's `when()` or `require()` throws. */
|
|
1580
1982
|
onError?: (id: string, error: unknown) => void;
|
|
1581
1983
|
}
|
|
1582
1984
|
/**
|
|
1583
1985
|
* Create a manager that evaluates constraint rules and produces unmet
|
|
1584
1986
|
* requirements.
|
|
1585
1987
|
*
|
|
1586
|
-
*
|
|
1988
|
+
* @remarks
|
|
1989
|
+
* Constraints are evaluated in priority order (higher priority first), with
|
|
1587
1990
|
* topological ordering for same-priority constraints connected by `after`
|
|
1588
|
-
* dependencies.
|
|
1589
|
-
* evaluation based on changed fact keys, and per-constraint
|
|
1991
|
+
* dependencies. The manager supports sync and async `when()` predicates,
|
|
1992
|
+
* incremental evaluation based on changed fact keys, and per-constraint
|
|
1993
|
+
* enable/disable toggling. Cycle detection runs eagerly at construction time
|
|
1994
|
+
* to prevent deadlocks in production.
|
|
1995
|
+
*
|
|
1996
|
+
* @param options - Configuration including constraint definitions, facts proxy,
|
|
1997
|
+
* custom requirement key functions, and lifecycle callbacks.
|
|
1998
|
+
* @returns A {@link ConstraintsManager} for evaluating, invalidating, and
|
|
1999
|
+
* managing constraint lifecycle.
|
|
2000
|
+
*
|
|
2001
|
+
* @example
|
|
2002
|
+
* ```typescript
|
|
2003
|
+
* const constraints = createConstraintsManager({
|
|
2004
|
+
* definitions: {
|
|
2005
|
+
* mustTransition: {
|
|
2006
|
+
* priority: 50,
|
|
2007
|
+
* when: (facts) => facts.phase === "red" && facts.elapsed > 30,
|
|
2008
|
+
* require: { type: "TRANSITION", to: "green" },
|
|
2009
|
+
* },
|
|
2010
|
+
* },
|
|
2011
|
+
* facts: factsProxy,
|
|
2012
|
+
* onEvaluate: (id, active) => console.log(id, active),
|
|
2013
|
+
* });
|
|
1590
2014
|
*
|
|
1591
|
-
*
|
|
1592
|
-
*
|
|
2015
|
+
* const unmet = await constraints.evaluate();
|
|
2016
|
+
* ```
|
|
2017
|
+
*
|
|
2018
|
+
* @internal
|
|
1593
2019
|
*/
|
|
1594
2020
|
declare function createConstraintsManager<S extends Schema>(options: CreateConstraintsOptions<S>): ConstraintsManager<S>;
|
|
1595
2021
|
|
|
@@ -1604,62 +2030,171 @@ declare function createConstraintsManager<S extends Schema>(options: CreateConst
|
|
|
1604
2030
|
* - Cancellation via AbortController
|
|
1605
2031
|
*/
|
|
1606
2032
|
|
|
1607
|
-
/**
|
|
2033
|
+
/**
|
|
2034
|
+
* Summary of a resolver that is currently in flight.
|
|
2035
|
+
*
|
|
2036
|
+
* @internal
|
|
2037
|
+
*/
|
|
1608
2038
|
interface InflightInfo {
|
|
2039
|
+
/** The unique requirement ID being resolved. */
|
|
1609
2040
|
id: string;
|
|
2041
|
+
/** The definition ID of the resolver handling this requirement. */
|
|
1610
2042
|
resolverId: string;
|
|
2043
|
+
/** Epoch timestamp (ms) when resolution started. */
|
|
1611
2044
|
startedAt: number;
|
|
1612
2045
|
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Manager returned by {@link createResolversManager} that matches
|
|
2048
|
+
* requirements to resolver handlers and manages their execution lifecycle.
|
|
2049
|
+
*
|
|
2050
|
+
* @internal
|
|
2051
|
+
*/
|
|
1613
2052
|
interface ResolversManager<_S extends Schema> {
|
|
1614
|
-
/**
|
|
2053
|
+
/**
|
|
2054
|
+
* Start resolving a requirement by matching it to a resolver handler.
|
|
2055
|
+
*
|
|
2056
|
+
* @remarks
|
|
2057
|
+
* Duplicate in-flight requirements (same `req.id`) are silently ignored.
|
|
2058
|
+
* If the matched resolver has `batch.enabled`, the requirement is queued
|
|
2059
|
+
* for batch processing instead of being resolved immediately.
|
|
2060
|
+
*
|
|
2061
|
+
* @param req - The requirement (with a stable identity ID) to resolve.
|
|
2062
|
+
*/
|
|
1615
2063
|
resolve(req: RequirementWithId): void;
|
|
1616
|
-
/**
|
|
2064
|
+
/**
|
|
2065
|
+
* Cancel an in-flight or batch-queued resolver by requirement ID.
|
|
2066
|
+
*
|
|
2067
|
+
* @remarks
|
|
2068
|
+
* Aborts the `AbortController` for in-flight resolvers. For batch-queued
|
|
2069
|
+
* requirements, removes the requirement from the pending batch.
|
|
2070
|
+
*
|
|
2071
|
+
* @param requirementId - The unique requirement ID to cancel.
|
|
2072
|
+
*/
|
|
1617
2073
|
cancel(requirementId: string): void;
|
|
1618
|
-
/**
|
|
2074
|
+
/**
|
|
2075
|
+
* Cancel every in-flight resolver and flush all pending batch queues.
|
|
2076
|
+
*/
|
|
1619
2077
|
cancelAll(): void;
|
|
1620
|
-
/**
|
|
2078
|
+
/**
|
|
2079
|
+
* Get the current status of a resolver by requirement ID.
|
|
2080
|
+
*
|
|
2081
|
+
* @param requirementId - The unique requirement ID to look up.
|
|
2082
|
+
* @returns The {@link ResolverStatus} (idle, pending, running, success, error, or canceled).
|
|
2083
|
+
*/
|
|
1621
2084
|
getStatus(requirementId: string): ResolverStatus;
|
|
1622
|
-
/**
|
|
2085
|
+
/**
|
|
2086
|
+
* Get the requirement IDs of all currently in-flight resolvers.
|
|
2087
|
+
*
|
|
2088
|
+
* @returns An array of requirement ID strings.
|
|
2089
|
+
*/
|
|
1623
2090
|
getInflight(): string[];
|
|
1624
|
-
/**
|
|
2091
|
+
/**
|
|
2092
|
+
* Get detailed info for every in-flight resolver.
|
|
2093
|
+
*
|
|
2094
|
+
* @returns An array of {@link InflightInfo} objects.
|
|
2095
|
+
*/
|
|
1625
2096
|
getInflightInfo(): InflightInfo[];
|
|
1626
|
-
/**
|
|
2097
|
+
/**
|
|
2098
|
+
* Check whether a requirement is currently being resolved.
|
|
2099
|
+
*
|
|
2100
|
+
* @param requirementId - The unique requirement ID to check.
|
|
2101
|
+
* @returns `true` if the requirement has an active in-flight resolver.
|
|
2102
|
+
*/
|
|
1627
2103
|
isResolving(requirementId: string): boolean;
|
|
1628
|
-
/**
|
|
2104
|
+
/**
|
|
2105
|
+
* Immediately flush all pending batch queues, executing their resolvers.
|
|
2106
|
+
*/
|
|
1629
2107
|
processBatches(): void;
|
|
1630
|
-
/**
|
|
2108
|
+
/**
|
|
2109
|
+
* Check whether any batch queues have requirements waiting to be processed.
|
|
2110
|
+
*
|
|
2111
|
+
* @returns `true` if at least one batch queue is non-empty.
|
|
2112
|
+
*/
|
|
2113
|
+
hasPendingBatches(): boolean;
|
|
2114
|
+
/**
|
|
2115
|
+
* Register additional resolver definitions at runtime (used for dynamic
|
|
2116
|
+
* module registration).
|
|
2117
|
+
*
|
|
2118
|
+
* @remarks
|
|
2119
|
+
* Clears the resolver-by-type cache so newly registered resolvers are
|
|
2120
|
+
* discoverable on the next {@link ResolversManager.resolve | resolve} call.
|
|
2121
|
+
*
|
|
2122
|
+
* @param newDefs - New resolver definitions to merge into the manager.
|
|
2123
|
+
*/
|
|
1631
2124
|
registerDefinitions(newDefs: ResolversDef<Schema>): void;
|
|
1632
2125
|
}
|
|
1633
|
-
/**
|
|
2126
|
+
/**
|
|
2127
|
+
* Configuration options accepted by {@link createResolversManager}.
|
|
2128
|
+
*
|
|
2129
|
+
* @internal
|
|
2130
|
+
*/
|
|
1634
2131
|
interface CreateResolversOptions<S extends Schema> {
|
|
2132
|
+
/** Resolver definitions keyed by ID. */
|
|
1635
2133
|
definitions: ResolversDef<S>;
|
|
2134
|
+
/** Proxy-based facts object passed to resolver contexts. */
|
|
1636
2135
|
facts: Facts<S>;
|
|
2136
|
+
/** Underlying fact store used for `batch()` coalescing of mutations. */
|
|
1637
2137
|
store: FactsStore<S>;
|
|
1638
|
-
/**
|
|
2138
|
+
/** Called when a resolver begins execution. */
|
|
1639
2139
|
onStart?: (resolver: string, req: RequirementWithId) => void;
|
|
1640
|
-
/**
|
|
2140
|
+
/** Called when a resolver completes successfully, with the wall-clock duration in ms. */
|
|
1641
2141
|
onComplete?: (resolver: string, req: RequirementWithId, duration: number) => void;
|
|
1642
|
-
/**
|
|
2142
|
+
/** Called when a resolver exhausts all retry attempts. */
|
|
1643
2143
|
onError?: (resolver: string, req: RequirementWithId, error: unknown) => void;
|
|
1644
|
-
/**
|
|
2144
|
+
/** Called before each retry attempt with the upcoming attempt number. */
|
|
1645
2145
|
onRetry?: (resolver: string, req: RequirementWithId, attempt: number) => void;
|
|
1646
|
-
/**
|
|
2146
|
+
/** Called when a resolver is canceled via {@link ResolversManager.cancel | cancel}. */
|
|
1647
2147
|
onCancel?: (resolver: string, req: RequirementWithId) => void;
|
|
1648
|
-
/**
|
|
2148
|
+
/** Called after any resolver finishes (success, error, or batch completion) to trigger reconciliation. */
|
|
1649
2149
|
onResolutionComplete?: () => void;
|
|
1650
2150
|
}
|
|
1651
2151
|
/**
|
|
1652
2152
|
* Create a manager that fulfills requirements by matching them to resolver
|
|
1653
2153
|
* handlers.
|
|
1654
2154
|
*
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
2155
|
+
* @remarks
|
|
2156
|
+
* Resolvers are matched by requirement type (string equality) or a predicate
|
|
2157
|
+
* function. Each resolution runs with an `AbortController` for cancellation
|
|
2158
|
+
* and configurable retry policies (none, linear, or exponential backoff).
|
|
2159
|
+
*
|
|
2160
|
+
* **Batching:** When a resolver sets `batch.enabled`, incoming requirements
|
|
2161
|
+
* are queued and flushed either when `batch.maxSize` is reached or after
|
|
2162
|
+
* `batch.windowMs` elapses, whichever comes first. Batch resolvers can use
|
|
2163
|
+
* `resolveBatch` (all-or-nothing) or `resolveBatchWithResults` (per-item
|
|
2164
|
+
* success/failure). If only `resolve` is provided with batching enabled, the
|
|
2165
|
+
* manager falls back to individual resolution calls.
|
|
2166
|
+
*
|
|
2167
|
+
* Duplicate in-flight requirements (same requirement ID) are automatically
|
|
2168
|
+
* deduplicated. Resolver-by-type lookups are cached with FIFO eviction at
|
|
2169
|
+
* 1 000 entries to handle dynamic requirement types.
|
|
2170
|
+
*
|
|
2171
|
+
* @param options - Configuration including resolver definitions, facts proxy,
|
|
2172
|
+
* store, and lifecycle callbacks.
|
|
2173
|
+
* @returns A {@link ResolversManager} for dispatching, canceling, and
|
|
2174
|
+
* inspecting requirement resolution.
|
|
2175
|
+
*
|
|
2176
|
+
* @example
|
|
2177
|
+
* ```typescript
|
|
2178
|
+
* const resolvers = createResolversManager({
|
|
2179
|
+
* definitions: {
|
|
2180
|
+
* transition: {
|
|
2181
|
+
* requirement: "TRANSITION",
|
|
2182
|
+
* retry: { attempts: 3, backoff: "exponential" },
|
|
2183
|
+
* resolve: async (req, context) => {
|
|
2184
|
+
* context.facts.phase = req.to;
|
|
2185
|
+
* context.facts.elapsed = 0;
|
|
2186
|
+
* },
|
|
2187
|
+
* },
|
|
2188
|
+
* },
|
|
2189
|
+
* facts: factsProxy,
|
|
2190
|
+
* store: factsStore,
|
|
2191
|
+
* onComplete: (id, req, ms) => console.log(`${id} resolved in ${ms}ms`),
|
|
2192
|
+
* });
|
|
2193
|
+
*
|
|
2194
|
+
* resolvers.resolve(requirementWithId);
|
|
2195
|
+
* ```
|
|
1660
2196
|
*
|
|
1661
|
-
* @
|
|
1662
|
-
* @returns A `ResolversManager` with resolve/cancel/cancelAll/getStatus/processBatches methods
|
|
2197
|
+
* @internal
|
|
1663
2198
|
*/
|
|
1664
2199
|
declare function createResolversManager<S extends Schema>(options: CreateResolversOptions<S>): ResolversManager<S>;
|
|
1665
2200
|
|
|
@@ -1672,6 +2207,35 @@ declare function createResolversManager<S extends Schema>(options: CreateResolve
|
|
|
1672
2207
|
* - Plugins execute in registration order
|
|
1673
2208
|
*/
|
|
1674
2209
|
|
|
2210
|
+
/**
|
|
2211
|
+
* Internal manager that broadcasts lifecycle events to registered {@link Plugin} instances.
|
|
2212
|
+
*
|
|
2213
|
+
* @remarks
|
|
2214
|
+
* PluginManager uses `Schema` (flat) internally because the engine works with
|
|
2215
|
+
* flat schemas. The public API uses `ModuleSchema` (consolidated), and the
|
|
2216
|
+
* conversion happens in `createSystem`.
|
|
2217
|
+
*
|
|
2218
|
+
* Plugins execute in registration order. All hook invocations are wrapped in
|
|
2219
|
+
* try-catch so a misbehaving plugin never breaks the engine. Duplicate plugin
|
|
2220
|
+
* names are detected and the older registration is replaced with a warning.
|
|
2221
|
+
*
|
|
2222
|
+
* Lifecycle hook categories:
|
|
2223
|
+
* - **System lifecycle:** `emitInit`, `emitStart`, `emitStop`, `emitDestroy`
|
|
2224
|
+
* - **Facts:** `emitFactSet`, `emitFactDelete`, `emitFactsBatch`
|
|
2225
|
+
* - **Derivations:** `emitDerivationCompute`, `emitDerivationInvalidate`
|
|
2226
|
+
* - **Reconciliation:** `emitReconcileStart`, `emitReconcileEnd`
|
|
2227
|
+
* - **Constraints:** `emitConstraintEvaluate`, `emitConstraintError`
|
|
2228
|
+
* - **Requirements:** `emitRequirementCreated`, `emitRequirementMet`, `emitRequirementCanceled`
|
|
2229
|
+
* - **Resolvers:** `emitResolverStart`, `emitResolverComplete`, `emitResolverError`, `emitResolverRetry`, `emitResolverCancel`
|
|
2230
|
+
* - **Effects:** `emitEffectRun`, `emitEffectError`
|
|
2231
|
+
* - **Time-travel:** `emitSnapshot`, `emitTimeTravel`
|
|
2232
|
+
* - **Errors:** `emitError`, `emitErrorRecovery`
|
|
2233
|
+
* - **Run history:** `emitRunComplete`
|
|
2234
|
+
*
|
|
2235
|
+
* @typeParam _S - The flat schema type (unused at runtime).
|
|
2236
|
+
*
|
|
2237
|
+
* @internal
|
|
2238
|
+
*/
|
|
1675
2239
|
interface PluginManager<_S extends Schema = any> {
|
|
1676
2240
|
/** Register a plugin */
|
|
1677
2241
|
register(plugin: Plugin<any>): void;
|
|
@@ -1709,14 +2273,17 @@ interface PluginManager<_S extends Schema = any> {
|
|
|
1709
2273
|
emitRunComplete(run: RunChangelogEntry): void;
|
|
1710
2274
|
}
|
|
1711
2275
|
/**
|
|
1712
|
-
* Create a
|
|
2276
|
+
* Create a {@link PluginManager} that broadcasts lifecycle events to registered plugins.
|
|
1713
2277
|
*
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1717
|
-
* replaced with a
|
|
2278
|
+
* @remarks
|
|
2279
|
+
* Plugins are called in registration order. All hook invocations are wrapped
|
|
2280
|
+
* in try-catch so a misbehaving plugin never breaks the engine. Duplicate
|
|
2281
|
+
* plugin names are detected and the older registration is replaced with a
|
|
2282
|
+
* console warning.
|
|
1718
2283
|
*
|
|
1719
|
-
* @returns A
|
|
2284
|
+
* @returns A {@link PluginManager} with `register`/`unregister`/`getPlugins` and `emit*` methods for every lifecycle event.
|
|
2285
|
+
*
|
|
2286
|
+
* @internal
|
|
1720
2287
|
*/
|
|
1721
2288
|
declare function createPluginManager<S extends Schema = any>(): PluginManager<S>;
|
|
1722
2289
|
|
|
@@ -1731,7 +2298,9 @@ declare function createPluginManager<S extends Schema = any>(): PluginManager<S>
|
|
|
1731
2298
|
*/
|
|
1732
2299
|
|
|
1733
2300
|
/**
|
|
1734
|
-
*
|
|
2301
|
+
* A queued retry entry tracking its source, attempt count, and scheduled time.
|
|
2302
|
+
*
|
|
2303
|
+
* @internal
|
|
1735
2304
|
*/
|
|
1736
2305
|
interface PendingRetry {
|
|
1737
2306
|
source: ErrorSource;
|
|
@@ -1744,13 +2313,18 @@ interface PendingRetry {
|
|
|
1744
2313
|
/**
|
|
1745
2314
|
* Create a manager for deferred retry scheduling with exponential backoff.
|
|
1746
2315
|
*
|
|
1747
|
-
*
|
|
1748
|
-
*
|
|
1749
|
-
*
|
|
1750
|
-
*
|
|
2316
|
+
* @remarks
|
|
2317
|
+
* Retries are stored in a Map keyed by source ID. Each entry tracks its
|
|
2318
|
+
* attempt number and the timestamp of the next eligible retry. When
|
|
2319
|
+
* {@link createRetryLaterManager | processDueRetries} is called (typically
|
|
2320
|
+
* during reconciliation), entries whose scheduled time has elapsed are
|
|
2321
|
+
* returned and removed from the queue. The delay grows exponentially:
|
|
2322
|
+
* `delayMs * backoffMultiplier^(attempt - 1)`, capped at `maxDelayMs`.
|
|
1751
2323
|
*
|
|
1752
|
-
* @param config - Backoff configuration
|
|
1753
|
-
* @returns A manager
|
|
2324
|
+
* @param config - Backoff configuration including `delayMs`, `maxRetries`, `backoffMultiplier`, and `maxDelayMs`.
|
|
2325
|
+
* @returns A manager exposing `scheduleRetry`, `getPendingRetries`, `processDueRetries`, `cancelRetry`, and `clearAll` methods.
|
|
2326
|
+
*
|
|
2327
|
+
* @internal
|
|
1754
2328
|
*/
|
|
1755
2329
|
declare function createRetryLaterManager(config?: RetryLaterConfig): {
|
|
1756
2330
|
/** Schedule a retry */
|
|
@@ -1764,57 +2338,92 @@ declare function createRetryLaterManager(config?: RetryLaterConfig): {
|
|
|
1764
2338
|
/** Clear all pending retries */
|
|
1765
2339
|
clearAll: () => void;
|
|
1766
2340
|
};
|
|
2341
|
+
/**
|
|
2342
|
+
* Handle returned by {@link createErrorBoundaryManager} for routing errors
|
|
2343
|
+
* through configurable recovery strategies.
|
|
2344
|
+
*
|
|
2345
|
+
* @internal
|
|
2346
|
+
*/
|
|
1767
2347
|
interface ErrorBoundaryManager {
|
|
1768
|
-
/**
|
|
2348
|
+
/**
|
|
2349
|
+
* Route an error through the configured recovery strategy for its source.
|
|
2350
|
+
*
|
|
2351
|
+
* @param source - The subsystem that produced the error.
|
|
2352
|
+
* @param sourceId - Identifier of the specific constraint, resolver, effect, or derivation.
|
|
2353
|
+
* @param error - The thrown value (coerced to {@link DirectiveError} internally).
|
|
2354
|
+
* @param context - Optional context forwarded to callbacks and retry entries.
|
|
2355
|
+
* @returns The {@link RecoveryStrategy} that was applied.
|
|
2356
|
+
*/
|
|
1769
2357
|
handleError(source: ErrorSource, sourceId: string, error: unknown, context?: unknown): RecoveryStrategy;
|
|
1770
|
-
/**
|
|
2358
|
+
/**
|
|
2359
|
+
* Return the most recently recorded error, or `null` if none exist.
|
|
2360
|
+
*
|
|
2361
|
+
* @returns The last {@link DirectiveError}, or `null`.
|
|
2362
|
+
*/
|
|
1771
2363
|
getLastError(): DirectiveError | null;
|
|
1772
|
-
/**
|
|
2364
|
+
/**
|
|
2365
|
+
* Return a snapshot array of all recorded errors (up to the last 100).
|
|
2366
|
+
*
|
|
2367
|
+
* @returns A shallow copy of the internal error ring buffer.
|
|
2368
|
+
*/
|
|
1773
2369
|
getAllErrors(): DirectiveError[];
|
|
1774
|
-
/** Clear all errors */
|
|
2370
|
+
/** Clear all recorded errors. */
|
|
1775
2371
|
clearErrors(): void;
|
|
1776
|
-
/**
|
|
2372
|
+
/**
|
|
2373
|
+
* Access the underlying retry-later manager for advanced scheduling.
|
|
2374
|
+
*
|
|
2375
|
+
* @returns The {@link createRetryLaterManager} instance used internally.
|
|
2376
|
+
*/
|
|
1777
2377
|
getRetryLaterManager(): ReturnType<typeof createRetryLaterManager>;
|
|
1778
|
-
/**
|
|
2378
|
+
/**
|
|
2379
|
+
* Drain and return retry entries whose scheduled time has elapsed.
|
|
2380
|
+
*
|
|
2381
|
+
* @returns An array of {@link PendingRetry} entries that are now due.
|
|
2382
|
+
*/
|
|
1779
2383
|
processDueRetries(): PendingRetry[];
|
|
1780
|
-
/**
|
|
2384
|
+
/**
|
|
2385
|
+
* Reset retry attempt tracking for a source, typically after a successful resolution.
|
|
2386
|
+
*
|
|
2387
|
+
* @param sourceId - The source identifier whose retry counter should be cleared.
|
|
2388
|
+
*/
|
|
1781
2389
|
clearRetryAttempts(sourceId: string): void;
|
|
1782
2390
|
}
|
|
1783
|
-
/**
|
|
2391
|
+
/**
|
|
2392
|
+
* Options accepted by {@link createErrorBoundaryManager}.
|
|
2393
|
+
*
|
|
2394
|
+
* @internal
|
|
2395
|
+
*/
|
|
1784
2396
|
interface CreateErrorBoundaryOptions {
|
|
2397
|
+
/** Per-source recovery strategies and retry-later tuning. */
|
|
1785
2398
|
config?: ErrorBoundaryConfig;
|
|
1786
|
-
/**
|
|
2399
|
+
/** Invoked every time an error is recorded, before the recovery strategy runs. */
|
|
1787
2400
|
onError?: (error: DirectiveError) => void;
|
|
1788
|
-
/**
|
|
2401
|
+
/** Invoked after a recovery strategy has been selected for an error. */
|
|
1789
2402
|
onRecovery?: (error: DirectiveError, strategy: RecoveryStrategy) => void;
|
|
1790
2403
|
}
|
|
1791
2404
|
/**
|
|
1792
2405
|
* Create a manager that handles errors from constraints, resolvers, effects,
|
|
1793
2406
|
* and derivations with configurable per-source recovery strategies.
|
|
1794
2407
|
*
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1797
|
-
* and `"throw"` (re-throw). Recent errors are kept in a ring buffer
|
|
1798
|
-
* (last 100) for inspection. The retry-later strategy delegates to an
|
|
1799
|
-
* internal {@link createRetryLaterManager}.
|
|
2408
|
+
* @remarks
|
|
2409
|
+
* Five recovery strategies are available:
|
|
1800
2410
|
*
|
|
1801
|
-
*
|
|
1802
|
-
*
|
|
2411
|
+
* - `"skip"` -- Swallow the error and continue.
|
|
2412
|
+
* - `"retry"` -- Signal the caller to retry immediately.
|
|
2413
|
+
* - `"retry-later"` -- Enqueue a deferred retry with exponential backoff
|
|
2414
|
+
* (delegated to an internal {@link createRetryLaterManager}).
|
|
2415
|
+
* - `"disable"` -- Permanently disable the failing source.
|
|
2416
|
+
* - `"throw"` -- Re-throw the error as a {@link DirectiveError}.
|
|
1803
2417
|
*
|
|
1804
|
-
*
|
|
1805
|
-
*
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1808
|
-
* onResolverError: "retry-later",
|
|
1809
|
-
* onEffectError: "skip",
|
|
1810
|
-
* retryLater: { maxRetries: 5, delayMs: 500 },
|
|
1811
|
-
* },
|
|
1812
|
-
* onError: (err) => console.error(err.source, err.message),
|
|
1813
|
-
* });
|
|
2418
|
+
* Recent errors are kept in a ring buffer (last 100) for inspection via
|
|
2419
|
+
* {@link ErrorBoundaryManager.getAllErrors | getAllErrors}. Each strategy
|
|
2420
|
+
* can be configured per source type (`onConstraintError`, `onResolverError`,
|
|
2421
|
+
* etc.) or as a callback that dynamically selects a strategy.
|
|
1814
2422
|
*
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
1817
|
-
*
|
|
2423
|
+
* @param options - Error boundary configuration, plus `onError` and `onRecovery` callbacks for plugin integration.
|
|
2424
|
+
* @returns An {@link ErrorBoundaryManager} for routing errors through the configured strategies.
|
|
2425
|
+
*
|
|
2426
|
+
* @internal
|
|
1818
2427
|
*/
|
|
1819
2428
|
declare function createErrorBoundaryManager(options?: CreateErrorBoundaryOptions): ErrorBoundaryManager;
|
|
1820
2429
|
|
|
@@ -1828,6 +2437,23 @@ declare function createErrorBoundaryManager(options?: CreateErrorBoundaryOptions
|
|
|
1828
2437
|
* - Export/import state history
|
|
1829
2438
|
*/
|
|
1830
2439
|
|
|
2440
|
+
/**
|
|
2441
|
+
* Internal time-travel manager that extends the public {@link TimeTravelAPI}
|
|
2442
|
+
* with snapshot capture, restoration, and pause/resume controls.
|
|
2443
|
+
*
|
|
2444
|
+
* @remarks
|
|
2445
|
+
* - `takeSnapshot(trigger)` records the current facts into the ring buffer.
|
|
2446
|
+
* - `restore(snapshot)` deserializes a snapshot back into the facts store,
|
|
2447
|
+
* setting `isRestoring = true` so the engine skips reconciliation.
|
|
2448
|
+
* - `pause()` / `resume()` temporarily suspend snapshot recording (e.g.,
|
|
2449
|
+
* during bulk imports or programmatic state resets).
|
|
2450
|
+
* - `beginChangeset(label)` / `endChangeset()` group consecutive snapshots
|
|
2451
|
+
* so `goBack`/`goForward` treat them as a single undo/redo unit.
|
|
2452
|
+
*
|
|
2453
|
+
* @typeParam _S - The schema type (unused at runtime but preserved for type safety).
|
|
2454
|
+
*
|
|
2455
|
+
* @internal
|
|
2456
|
+
*/
|
|
1831
2457
|
interface TimeTravelManager<_S extends Schema> extends TimeTravelAPI {
|
|
1832
2458
|
/** Take a snapshot of current state */
|
|
1833
2459
|
takeSnapshot(trigger: string): Snapshot;
|
|
@@ -1842,7 +2468,13 @@ interface TimeTravelManager<_S extends Schema> extends TimeTravelAPI {
|
|
|
1842
2468
|
/** Resume snapshot taking */
|
|
1843
2469
|
resume(): void;
|
|
1844
2470
|
}
|
|
1845
|
-
/**
|
|
2471
|
+
/**
|
|
2472
|
+
* Options for creating a time-travel manager via {@link createTimeTravelManager}.
|
|
2473
|
+
*
|
|
2474
|
+
* @typeParam S - The facts schema type.
|
|
2475
|
+
*
|
|
2476
|
+
* @internal
|
|
2477
|
+
*/
|
|
1846
2478
|
interface CreateTimeTravelOptions<S extends Schema> {
|
|
1847
2479
|
config: DebugConfig;
|
|
1848
2480
|
facts: Facts<S>;
|
|
@@ -1853,24 +2485,35 @@ interface CreateTimeTravelOptions<S extends Schema> {
|
|
|
1853
2485
|
onTimeTravel?: (from: number, to: number) => void;
|
|
1854
2486
|
}
|
|
1855
2487
|
/**
|
|
1856
|
-
* Create a snapshot-based time-travel debugger
|
|
1857
|
-
* history.
|
|
2488
|
+
* Create a snapshot-based time-travel debugger backed by a ring buffer.
|
|
1858
2489
|
*
|
|
1859
|
-
*
|
|
1860
|
-
*
|
|
1861
|
-
*
|
|
1862
|
-
*
|
|
1863
|
-
*
|
|
2490
|
+
* @remarks
|
|
2491
|
+
* Snapshots are taken automatically after fact changes (during reconciliation)
|
|
2492
|
+
* and can be navigated with `goBack`/`goForward`/`goTo`. Use
|
|
2493
|
+
* `beginChangeset(label)` and `endChangeset()` to group multiple snapshots
|
|
2494
|
+
* into a single undo/redo unit. The entire history can be exported to JSON
|
|
2495
|
+
* via `export()` and re-imported with `import()` for cross-session debugging.
|
|
1864
2496
|
*
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
2497
|
+
* Call `pause()` to temporarily stop recording snapshots (e.g., during bulk
|
|
2498
|
+
* fact imports) and `resume()` to re-enable recording.
|
|
2499
|
+
*
|
|
2500
|
+
* @param options - Debug config, facts proxy, store, and optional snapshot/time-travel callbacks.
|
|
2501
|
+
* @returns A {@link TimeTravelManager} with snapshot capture, navigation, changeset, and export/import methods.
|
|
2502
|
+
*
|
|
2503
|
+
* @internal
|
|
1867
2504
|
*/
|
|
1868
2505
|
declare function createTimeTravelManager<S extends Schema>(options: CreateTimeTravelOptions<S>): TimeTravelManager<S>;
|
|
1869
2506
|
/**
|
|
1870
|
-
* Create a no-op time-travel manager
|
|
1871
|
-
*
|
|
2507
|
+
* Create a no-op time-travel manager for use when `debug.timeTravel` is disabled.
|
|
2508
|
+
*
|
|
2509
|
+
* @remarks
|
|
2510
|
+
* All methods are safe to call but perform no work. This avoids null-checks
|
|
2511
|
+
* throughout the engine -- callers can use the same {@link TimeTravelManager}
|
|
2512
|
+
* interface regardless of whether time-travel is enabled.
|
|
1872
2513
|
*
|
|
1873
|
-
* @returns A
|
|
2514
|
+
* @returns A {@link TimeTravelManager} where every method is a no-op and `isEnabled` is `false`.
|
|
2515
|
+
*
|
|
2516
|
+
* @internal
|
|
1874
2517
|
*/
|
|
1875
2518
|
declare function createDisabledTimeTravel<S extends Schema>(): TimeTravelManager<S>;
|
|
1876
2519
|
|
|
@@ -1890,12 +2533,13 @@ declare function createDisabledTimeTravel<S extends Schema>(): TimeTravelManager
|
|
|
1890
2533
|
* effects, constraints, resolvers, plugins, error boundaries, and time-travel
|
|
1891
2534
|
* into a single reactive system.
|
|
1892
2535
|
*
|
|
1893
|
-
*
|
|
1894
|
-
*
|
|
1895
|
-
*
|
|
2536
|
+
* @remarks
|
|
2537
|
+
* This is the internal factory used by {@link createSystem}. Most users should
|
|
2538
|
+
* call `createSystem` instead, which provides a friendlier API and handles
|
|
2539
|
+
* module composition.
|
|
1896
2540
|
*
|
|
1897
|
-
* @param config - Full system configuration
|
|
1898
|
-
* @returns A
|
|
2541
|
+
* @param config - Full system configuration including modules, plugins, error boundary settings, and debug options
|
|
2542
|
+
* @returns A {@link System} instance with facts, derive, events, dispatch, subscribe, watch, settle, and lifecycle methods
|
|
1899
2543
|
*
|
|
1900
2544
|
* @example
|
|
1901
2545
|
* ```ts
|
|
@@ -1911,6 +2555,8 @@ declare function createDisabledTimeTravel<S extends Schema>(): TimeTravelManager
|
|
|
1911
2555
|
* system.start();
|
|
1912
2556
|
* system.facts.count = 42;
|
|
1913
2557
|
* ```
|
|
2558
|
+
*
|
|
2559
|
+
* @internal
|
|
1914
2560
|
*/
|
|
1915
2561
|
declare function createEngine<S extends Schema>(config: SystemConfig<any>): System<any>;
|
|
1916
2562
|
|