@typeonce/effect-machine 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +410 -17
- package/dist/Machine.d.ts +1191 -510
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +371 -565
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/activities.d.ts +57 -0
- package/dist/internal/machine/activities.d.ts.map +1 -0
- package/dist/internal/machine/activities.js +49 -0
- package/dist/internal/machine/activities.js.map +1 -0
- package/dist/internal/machine/atom.d.ts +69 -0
- package/dist/internal/machine/atom.d.ts.map +1 -0
- package/dist/{AtomMachine.js → internal/machine/atom.js} +20 -95
- package/dist/internal/machine/atom.js.map +1 -0
- package/dist/internal/machine/cluster.d.ts +63 -0
- package/dist/internal/machine/cluster.d.ts.map +1 -0
- package/dist/{ClusterMachine.js → internal/machine/cluster.js} +15 -119
- package/dist/internal/machine/cluster.js.map +1 -0
- package/dist/internal/machine/command.d.ts +16 -0
- package/dist/internal/machine/command.d.ts.map +1 -0
- package/dist/internal/machine/command.js +45 -0
- package/dist/internal/machine/command.js.map +1 -0
- package/dist/internal/machine/commandRuntime.d.ts +13 -0
- package/dist/internal/machine/commandRuntime.d.ts.map +1 -0
- package/dist/internal/machine/commandRuntime.js +16 -0
- package/dist/internal/machine/commandRuntime.js.map +1 -0
- package/dist/internal/{machineModel.d.ts → machine/configuration.d.ts} +12 -61
- package/dist/internal/machine/configuration.d.ts.map +1 -0
- package/dist/internal/{machineModel.js → machine/configuration.js} +250 -545
- package/dist/internal/machine/configuration.js.map +1 -0
- package/dist/internal/{machineErrors.d.ts → machine/errors.d.ts} +8 -8
- package/dist/internal/machine/errors.d.ts.map +1 -0
- package/dist/internal/{machineErrors.js → machine/errors.js} +8 -8
- package/dist/internal/machine/errors.js.map +1 -0
- package/dist/internal/machine/executionPlan.d.ts +49 -0
- package/dist/internal/machine/executionPlan.d.ts.map +1 -0
- package/dist/internal/machine/executionPlan.js +676 -0
- package/dist/internal/machine/executionPlan.js.map +1 -0
- package/dist/internal/machine/invocation.d.ts +23 -0
- package/dist/internal/machine/invocation.d.ts.map +1 -0
- package/dist/internal/machine/invocation.js +77 -0
- package/dist/internal/machine/invocation.js.map +1 -0
- package/dist/internal/machine/machine.d.ts +183 -0
- package/dist/internal/machine/machine.d.ts.map +1 -0
- package/dist/internal/machine/machine.js +552 -0
- package/dist/internal/machine/machine.js.map +1 -0
- package/dist/internal/machine/planner.d.ts +182 -0
- package/dist/internal/machine/planner.d.ts.map +1 -0
- package/dist/internal/machine/planner.js +1082 -0
- package/dist/internal/machine/planner.js.map +1 -0
- package/dist/internal/{machineProcess.d.ts → machine/process.d.ts} +11 -5
- package/dist/internal/machine/process.d.ts.map +1 -0
- package/dist/internal/machine/process.js +498 -0
- package/dist/internal/machine/process.js.map +1 -0
- package/dist/internal/machine/protocol.d.ts +34 -0
- package/dist/internal/machine/protocol.d.ts.map +1 -0
- package/dist/internal/machine/protocol.js +182 -0
- package/dist/internal/machine/protocol.js.map +1 -0
- package/dist/internal/machine/readiness.d.ts +4 -0
- package/dist/internal/machine/readiness.d.ts.map +1 -0
- package/dist/internal/machine/readiness.js +2 -0
- package/dist/internal/machine/readiness.js.map +1 -0
- package/dist/internal/machine/runtime.d.ts +255 -0
- package/dist/internal/machine/runtime.d.ts.map +1 -0
- package/dist/internal/machine/runtime.js +1489 -0
- package/dist/internal/machine/runtime.js.map +1 -0
- package/dist/internal/machine/serialization.d.ts +14 -0
- package/dist/internal/machine/serialization.d.ts.map +1 -0
- package/dist/internal/machine/serialization.js +338 -0
- package/dist/internal/machine/serialization.js.map +1 -0
- package/dist/internal/machine/stateDefinition.d.ts +15 -0
- package/dist/internal/machine/stateDefinition.d.ts.map +1 -0
- package/dist/internal/machine/stateDefinition.js +210 -0
- package/dist/internal/machine/stateDefinition.js.map +1 -0
- package/dist/internal/machine/symbols.d.ts +3 -0
- package/dist/internal/machine/symbols.d.ts.map +1 -0
- package/dist/internal/machine/symbols.js +3 -0
- package/dist/internal/machine/symbols.js.map +1 -0
- package/dist/internal/machine/topology.d.ts +98 -0
- package/dist/internal/machine/topology.d.ts.map +1 -0
- package/dist/internal/machine/topology.js +347 -0
- package/dist/internal/machine/topology.js.map +1 -0
- package/dist/internal/testing/machine/arbitrary.d.ts +36 -0
- package/dist/internal/testing/machine/arbitrary.d.ts.map +1 -0
- package/dist/internal/testing/machine/arbitrary.js +68 -0
- package/dist/internal/testing/machine/arbitrary.js.map +1 -0
- package/dist/internal/testing/machine/exploration.d.ts +30 -0
- package/dist/internal/testing/machine/exploration.d.ts.map +1 -0
- package/dist/internal/testing/machine/exploration.js +237 -0
- package/dist/internal/testing/machine/exploration.js.map +1 -0
- package/dist/internal/testing/machine/finiteModel.d.ts +322 -0
- package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -0
- package/dist/internal/testing/machine/finiteModel.js +976 -0
- package/dist/internal/testing/machine/finiteModel.js.map +1 -0
- package/dist/internal/testing/machine/invariant.d.ts +31 -0
- package/dist/internal/testing/machine/invariant.d.ts.map +1 -0
- package/dist/internal/testing/machine/invariant.js +214 -0
- package/dist/internal/testing/machine/invariant.js.map +1 -0
- package/dist/internal/testing/machine/probe.d.ts +26 -0
- package/dist/internal/testing/machine/probe.d.ts.map +1 -0
- package/dist/internal/testing/machine/probe.js +45 -0
- package/dist/internal/testing/machine/probe.js.map +1 -0
- package/dist/internal/testing/machine/referenceModel.d.ts +203 -0
- package/dist/internal/testing/machine/referenceModel.d.ts.map +1 -0
- package/dist/internal/testing/machine/referenceModel.js +1012 -0
- package/dist/internal/testing/machine/referenceModel.js.map +1 -0
- package/dist/internal/testing/machine/runtime.d.ts +631 -0
- package/dist/internal/testing/machine/runtime.d.ts.map +1 -0
- package/dist/internal/testing/machine/runtime.js +726 -0
- package/dist/internal/testing/machine/runtime.js.map +1 -0
- package/dist/internal/testing/machine/runtimeInvariant.d.ts +32 -0
- package/dist/internal/testing/machine/runtimeInvariant.d.ts.map +1 -0
- package/dist/internal/testing/machine/runtimeInvariant.js +334 -0
- package/dist/internal/testing/machine/runtimeInvariant.js.map +1 -0
- package/dist/internal/testing/machine/trace.d.ts +17 -0
- package/dist/internal/testing/machine/trace.d.ts.map +1 -0
- package/dist/internal/testing/machine/trace.js +94 -0
- package/dist/internal/testing/machine/trace.js.map +1 -0
- package/dist/internal/testing/machine/verification.d.ts +36 -0
- package/dist/internal/testing/machine/verification.d.ts.map +1 -0
- package/dist/internal/testing/machine/verification.js +1375 -0
- package/dist/internal/testing/machine/verification.js.map +1 -0
- package/dist/testing/MachineTest.d.ts +1588 -0
- package/dist/testing/MachineTest.d.ts.map +1 -0
- package/dist/testing/MachineTest.js +406 -0
- package/dist/testing/MachineTest.js.map +1 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +7 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/{ClusterMachine.d.ts → unstable/cluster/ClusterMachine.d.ts} +56 -45
- package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -0
- package/dist/unstable/cluster/ClusterMachine.js +144 -0
- package/dist/unstable/cluster/ClusterMachine.js.map +1 -0
- package/dist/{cluster.js → unstable/cluster/index.d.ts} +1 -1
- package/dist/unstable/cluster/index.d.ts.map +1 -0
- package/dist/{cluster.d.ts → unstable/cluster/index.js} +1 -1
- package/dist/unstable/cluster/index.js.map +1 -0
- package/dist/{AtomMachine.d.ts → unstable/reactivity/AtomMachine.d.ts} +116 -50
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -0
- package/dist/unstable/reactivity/AtomMachine.js +164 -0
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -0
- package/dist/{reactivity.js → unstable/reactivity/index.d.ts} +1 -1
- package/dist/unstable/reactivity/index.d.ts.map +1 -0
- package/dist/{reactivity.d.ts → unstable/reactivity/index.js} +1 -1
- package/dist/unstable/reactivity/index.js.map +1 -0
- package/docs/agent-guide.md +316 -31
- package/package.json +28 -10
- package/dist/AtomMachine.d.ts.map +0 -1
- package/dist/AtomMachine.js.map +0 -1
- package/dist/ClusterMachine.d.ts.map +0 -1
- package/dist/ClusterMachine.js.map +0 -1
- package/dist/cluster.d.ts.map +0 -1
- package/dist/cluster.js.map +0 -1
- package/dist/internal/machineErrors.d.ts.map +0 -1
- package/dist/internal/machineErrors.js.map +0 -1
- package/dist/internal/machineModel.d.ts.map +0 -1
- package/dist/internal/machineModel.js.map +0 -1
- package/dist/internal/machinePlanner.d.ts +0 -74
- package/dist/internal/machinePlanner.d.ts.map +0 -1
- package/dist/internal/machinePlanner.js +0 -797
- package/dist/internal/machinePlanner.js.map +0 -1
- package/dist/internal/machineProcess.d.ts.map +0 -1
- package/dist/internal/machineProcess.js +0 -181
- package/dist/internal/machineProcess.js.map +0 -1
- package/dist/internal/machineRuntime.d.ts +0 -125
- package/dist/internal/machineRuntime.d.ts.map +0 -1
- package/dist/internal/machineRuntime.js +0 -365
- package/dist/internal/machineRuntime.js.map +0 -1
- package/dist/reactivity.d.ts.map +0 -1
- package/dist/reactivity.js.map +0 -1
|
@@ -0,0 +1,1588 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property-based scenario generation and planner trace utilities.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.4.0
|
|
5
|
+
*/
|
|
6
|
+
import type * as Effect from "effect/Effect";
|
|
7
|
+
import type * as Graph from "effect/Graph";
|
|
8
|
+
import type * as Schema from "effect/Schema";
|
|
9
|
+
import type { FastCheck } from "effect/testing";
|
|
10
|
+
import type { EnsureExecutable } from "../internal/machine/readiness.js";
|
|
11
|
+
import type { SchemaArbitraryReport } from "../internal/testing/machine/arbitrary.js";
|
|
12
|
+
import type { FiniteModel } from "../internal/testing/machine/finiteModel.js";
|
|
13
|
+
import type * as ReferenceModel from "../internal/testing/machine/referenceModel.js";
|
|
14
|
+
import * as internal from "../internal/testing/machine/verification.js";
|
|
15
|
+
import type { VerificationError } from "../internal/testing/machine/verification.js";
|
|
16
|
+
import type * as Machine from "../Machine.js";
|
|
17
|
+
export { advanceCommand, type CausalRuntimeAssertionContext, type CausalRuntimeCommandActual, CausalRuntimeCommandFailure, type CausalRuntimeCommandRecord, type CausalRuntimeCommandResult, type CausalRuntimeInspectionContext, type CausalRuntimeModelOptions, type CausalRuntimeModelStep, type CausalRuntimeTranscript, type CausalVerificationAwaitContext, type CausalVerificationOptions, type CausalVerificationTranscript, checkpointCommand, type EnqueuedRuntimeAssertionContext, type EnqueuedRuntimeCommandActual, type EnqueuedRuntimeCommandRecord, type EnqueuedRuntimeInspectionContext, type EnqueuedRuntimeModelOptions, type EnqueuedRuntimeModelStep, type EnqueuedRuntimeTranscript, formatCausalTranscript, formatEnqueuedTranscript, formatRuntimeTranscript, runCausalCommands, runEnqueuedCommands, runRuntimeCommands, type RuntimeAssertionContext, type RuntimeAwait, type RuntimeCommand, type RuntimeCommandActual, RuntimeCommandFailure, type RuntimeCommandRecord, type RuntimeCommandResult, type RuntimeCommands, runtimeCommands, type RuntimeCommandsDiagnostics, type RuntimeCommandsOptions, type RuntimeInspectionContext, type RuntimeModelOptions, type RuntimeModelStep, RuntimeObservationError, RuntimeSynchronization, type RuntimeTranscript, sendCommand, stopCommand, verifyCausalCommands } from "../internal/testing/machine/verification.js";
|
|
18
|
+
export type { SchemaArbitraryOpaqueFilterWarning, SchemaArbitraryReport, SchemaArbitraryWarning } from "../internal/testing/machine/verification.js";
|
|
19
|
+
export { compileModel, type FiniteAtomicState, type FiniteAutomaticTransition, type FiniteCompoundState, type FiniteEventTransition, type FiniteFinalState, type FiniteHistoryMutation, type FiniteHistoryScenario, type FiniteHistoryState, type FiniteHistoryTransfer, type FiniteModel, type FiniteModelDiagnostics, type FiniteModelOptions, type FiniteModels, finiteModels, type FiniteParallelState, type FiniteState, type FiniteTransition, type FiniteTransitionTrigger } from "../internal/testing/machine/verification.js";
|
|
20
|
+
export { ModelVerificationError, type ModelVerificationField, type ModelVerificationLocation, type ModelVerificationMismatch, type ReferenceCompletion, type ReferenceHistoryRecord, type ReferenceInitialStep, type ReferenceMicrostep, type ReferenceState, type ReferenceStateValue, type ReferenceStep, type ReferenceTrace, type ReferenceTransition } from "../internal/testing/machine/verification.js";
|
|
21
|
+
/**
|
|
22
|
+
* Purely interprets a finite hierarchical model without compiling a
|
|
23
|
+
* machine.
|
|
24
|
+
*
|
|
25
|
+
* @category verification
|
|
26
|
+
* @since 0.4.0
|
|
27
|
+
*/
|
|
28
|
+
export declare const interpretModel: (model: FiniteModel, events: ReadonlyArray<string>) => ReferenceModel.ReferenceTrace;
|
|
29
|
+
type AnyMachine = Machine.Machine.Any;
|
|
30
|
+
type InputValue<M extends AnyMachine> = Machine.Machine.Input<M>["Type"];
|
|
31
|
+
type StatePath<M extends AnyMachine> = Machine.Machine.StateIdentifier<Machine.Machine.States<M>>;
|
|
32
|
+
type StateNodePath<M extends AnyMachine> = Machine.Machine.StateNodeIdentifier<Machine.Machine.States<M>>;
|
|
33
|
+
type IsAny<A> = 0 extends (1 & A) ? true : false;
|
|
34
|
+
type ReadyMachine<M extends AnyMachine> = M & EnsureExecutable<Machine.Machine.States<M>, Machine.Machine.UnhandledStates<M>, Machine.Machine.OutputStates<M>>;
|
|
35
|
+
/**
|
|
36
|
+
* A generated public-input scenario for a machine.
|
|
37
|
+
*
|
|
38
|
+
* Machines without an input schema omit `input`; machines with one retain its
|
|
39
|
+
* exact decoded type. Events use only the public input protocol.
|
|
40
|
+
*
|
|
41
|
+
* @category models
|
|
42
|
+
* @since 0.4.0
|
|
43
|
+
*/
|
|
44
|
+
export type Scenario<M extends AnyMachine> = Machine.Machine.Input<M> extends typeof Schema.Void ? {
|
|
45
|
+
readonly events: ReadonlyArray<Machine.Machine.InputEvent<M>>;
|
|
46
|
+
} : {
|
|
47
|
+
readonly input: InputValue<M>;
|
|
48
|
+
readonly events: ReadonlyArray<Machine.Machine.InputEvent<M>>;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Options for schema-derived scenario generation.
|
|
52
|
+
*
|
|
53
|
+
* `inputArbitrary` and `eventsArbitrary` replace their complete generated
|
|
54
|
+
* value. An events override therefore owns its own length distribution.
|
|
55
|
+
*
|
|
56
|
+
* @category models
|
|
57
|
+
* @since 0.4.0
|
|
58
|
+
*/
|
|
59
|
+
export type ScenarioOptions<M extends AnyMachine> = {
|
|
60
|
+
readonly minEvents?: number;
|
|
61
|
+
readonly maxEvents?: number;
|
|
62
|
+
readonly eventsArbitrary?: FastCheck.Arbitrary<ReadonlyArray<Machine.Machine.InputEvent<M>>>;
|
|
63
|
+
} & (Machine.Machine.Input<M> extends typeof Schema.Void ? {
|
|
64
|
+
readonly inputArbitrary?: never;
|
|
65
|
+
} : {
|
|
66
|
+
readonly inputArbitrary?: FastCheck.Arbitrary<InputValue<M>>;
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* Diagnostics for one schema-derived arbitrary.
|
|
70
|
+
*
|
|
71
|
+
* @category models
|
|
72
|
+
* @since 0.4.0
|
|
73
|
+
*/
|
|
74
|
+
export interface SchemaArbitraryDiagnostic {
|
|
75
|
+
readonly boundary: "input" | "event";
|
|
76
|
+
readonly index: number | undefined;
|
|
77
|
+
readonly report: SchemaArbitraryReport;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Diagnostics describing how a scenario arbitrary was assembled.
|
|
81
|
+
*
|
|
82
|
+
* @category models
|
|
83
|
+
* @since 0.4.0
|
|
84
|
+
*/
|
|
85
|
+
export interface ScenarioDiagnostics {
|
|
86
|
+
readonly input: "none" | "schema" | "override";
|
|
87
|
+
readonly events: "empty" | "schema" | "override";
|
|
88
|
+
readonly schemas: ReadonlyArray<SchemaArbitraryDiagnostic>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A scenario arbitrary together with schema-derivation diagnostics.
|
|
92
|
+
*
|
|
93
|
+
* @category models
|
|
94
|
+
* @since 0.4.0
|
|
95
|
+
*/
|
|
96
|
+
export interface Scenarios<M extends AnyMachine> {
|
|
97
|
+
readonly arbitrary: FastCheck.Arbitrary<Scenario<M>>;
|
|
98
|
+
readonly diagnostics: ScenarioDiagnostics;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Derives valid machine inputs and public events from their schemas.
|
|
102
|
+
*
|
|
103
|
+
* Unsupported schema derivations fail immediately through `Schema.toArbitrary`.
|
|
104
|
+
* Non-fatal derivation warnings are returned instead of being hidden.
|
|
105
|
+
*
|
|
106
|
+
* **Example**
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* import { Schema } from "effect"
|
|
110
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
111
|
+
* import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
112
|
+
*
|
|
113
|
+
* class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
|
|
114
|
+
* class Reset extends Schema.TaggedClass<Reset>("Reset")("Reset", {}) {}
|
|
115
|
+
* const States = Machine.defineStates({ Idle })
|
|
116
|
+
* const machine = Machine.make({
|
|
117
|
+
* states: States.states,
|
|
118
|
+
* events: [Reset],
|
|
119
|
+
* initial: () => States.initial.Idle.from()
|
|
120
|
+
* }).handle({ Idle: { on: { Reset: () => States.initial.Idle.from() } } })
|
|
121
|
+
*
|
|
122
|
+
* const generated = MachineTest.scenarios(machine, { maxEvents: 5 })
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @category constructors
|
|
126
|
+
* @since 0.4.0
|
|
127
|
+
*/
|
|
128
|
+
export declare const scenarios: <M extends AnyMachine>(machine: M, options?: ScenarioOptions<M>) => Scenarios<M>;
|
|
129
|
+
/**
|
|
130
|
+
* Completion information retained by an initial or event plan.
|
|
131
|
+
*
|
|
132
|
+
* @category models
|
|
133
|
+
* @since 0.4.0
|
|
134
|
+
*/
|
|
135
|
+
export type PlanCompletion<M extends AnyMachine> = {
|
|
136
|
+
readonly done: true;
|
|
137
|
+
readonly output: Machine.Machine.Output<M>;
|
|
138
|
+
} | {
|
|
139
|
+
readonly done: false;
|
|
140
|
+
readonly output: undefined;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* One public planned microstep, including retained post-conflict transitions.
|
|
144
|
+
*
|
|
145
|
+
* @category models
|
|
146
|
+
* @since 0.4.0
|
|
147
|
+
*/
|
|
148
|
+
export interface Microstep<M extends AnyMachine, Requirements = Machine.Machine.Services<M>> {
|
|
149
|
+
readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
150
|
+
readonly event: Machine.Machine.Event<M> | Machine.InitialEvent;
|
|
151
|
+
readonly transitions: ReadonlyArray<Machine.Machine.RetainedTransition<StatePath<M>, Machine.Machine.TagOf<Machine.Machine.Events<M>[number]>, StateNodePath<M>>>;
|
|
152
|
+
readonly commands: ReadonlyArray<Machine.Command>;
|
|
153
|
+
readonly raisedEvents: ReadonlyArray<Machine.Machine.Event<M>>;
|
|
154
|
+
readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>;
|
|
155
|
+
readonly exitPaths: ReadonlyArray<string>;
|
|
156
|
+
readonly entryPaths: ReadonlyArray<string>;
|
|
157
|
+
readonly changed: boolean;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The complete data returned while planning machine startup.
|
|
161
|
+
*
|
|
162
|
+
* @category models
|
|
163
|
+
* @since 0.4.0
|
|
164
|
+
*/
|
|
165
|
+
export type InitialPlan<M extends AnyMachine> = {
|
|
166
|
+
readonly startingState: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
167
|
+
readonly initialEntryPaths: ReadonlyArray<StatePath<M>>;
|
|
168
|
+
readonly state: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
169
|
+
readonly commands: ReadonlyArray<Machine.Command>;
|
|
170
|
+
readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>;
|
|
171
|
+
readonly microsteps: ReadonlyArray<Microstep<M, Machine.Machine.InitialServices<M> | Machine.Machine.Services<M>>>;
|
|
172
|
+
} & PlanCompletion<M>;
|
|
173
|
+
/**
|
|
174
|
+
* The complete data returned while planning one public event.
|
|
175
|
+
*
|
|
176
|
+
* @category models
|
|
177
|
+
* @since 0.4.0
|
|
178
|
+
*/
|
|
179
|
+
export type EventPlan<M extends AnyMachine> = {
|
|
180
|
+
readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
181
|
+
readonly commands: ReadonlyArray<Machine.Command>;
|
|
182
|
+
readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>;
|
|
183
|
+
readonly microsteps: ReadonlyArray<Microstep<M>>;
|
|
184
|
+
} & PlanCompletion<M>;
|
|
185
|
+
/**
|
|
186
|
+
* Startup portion of an executable planner trace.
|
|
187
|
+
*
|
|
188
|
+
* @category models
|
|
189
|
+
* @since 0.4.0
|
|
190
|
+
*/
|
|
191
|
+
export interface InitialTrace<M extends AnyMachine> {
|
|
192
|
+
readonly plan: InitialPlan<M>;
|
|
193
|
+
readonly startingState: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
194
|
+
readonly startingConfiguration: ReadonlyArray<StatePath<M>>;
|
|
195
|
+
readonly initialEntryPaths: ReadonlyArray<StatePath<M>>;
|
|
196
|
+
readonly configuration: ReadonlyArray<StatePath<M>>;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* One event portion of an executable planner trace.
|
|
200
|
+
*
|
|
201
|
+
* @category models
|
|
202
|
+
* @since 0.4.0
|
|
203
|
+
*/
|
|
204
|
+
export interface TraceStep<M extends AnyMachine> {
|
|
205
|
+
readonly index: number;
|
|
206
|
+
readonly before: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
207
|
+
readonly beforeConfiguration: ReadonlyArray<StatePath<M>>;
|
|
208
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
209
|
+
readonly plan: EventPlan<M>;
|
|
210
|
+
readonly after: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
211
|
+
readonly afterConfiguration: ReadonlyArray<StatePath<M>>;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* A scenario and every plan produced by executing it without running actions.
|
|
215
|
+
*
|
|
216
|
+
* @category models
|
|
217
|
+
* @since 0.4.0
|
|
218
|
+
*/
|
|
219
|
+
export interface Trace<M extends AnyMachine> {
|
|
220
|
+
readonly scenario: Scenario<M>;
|
|
221
|
+
readonly initial: InitialTrace<M>;
|
|
222
|
+
readonly steps: ReadonlyArray<TraceStep<M>>;
|
|
223
|
+
readonly final: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
224
|
+
readonly finalConfiguration: ReadonlyArray<StatePath<M>>;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* One runtime microstep retained by the execution strategy used by a probe.
|
|
228
|
+
*
|
|
229
|
+
* Transition-definition metadata is intentionally not reconstructed here:
|
|
230
|
+
* optimized runtimes retain execution evidence, while `run` and `plan` remain
|
|
231
|
+
* the APIs for complete diagnostic transition metadata.
|
|
232
|
+
*
|
|
233
|
+
* @category runtime testing
|
|
234
|
+
* @since 0.4.0
|
|
235
|
+
*/
|
|
236
|
+
export type ProbeMicrostep<M extends AnyMachine> = Omit<Microstep<M>, "transitions">;
|
|
237
|
+
/**
|
|
238
|
+
* Runtime plan evidence associated with one acknowledged public event.
|
|
239
|
+
*
|
|
240
|
+
* @category runtime testing
|
|
241
|
+
* @since 0.4.0
|
|
242
|
+
*/
|
|
243
|
+
export type ProbePlan<M extends AnyMachine> = {
|
|
244
|
+
readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
245
|
+
readonly commands: ReadonlyArray<Machine.Command>;
|
|
246
|
+
readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>;
|
|
247
|
+
readonly microsteps: ReadonlyArray<ProbeMicrostep<M>>;
|
|
248
|
+
} & PlanCompletion<M>;
|
|
249
|
+
/**
|
|
250
|
+
* Causal evidence produced after one event has completed its managed runtime
|
|
251
|
+
* macrostep.
|
|
252
|
+
*
|
|
253
|
+
* `handled` distinguishes an ignored event from a retained transition that
|
|
254
|
+
* deliberately leaves the logical state unchanged. `configurationChanged`
|
|
255
|
+
* reports whether any microstep changed or reentered the active statechart
|
|
256
|
+
* configuration; compare `before` and `after` for state-value assertions.
|
|
257
|
+
*
|
|
258
|
+
* @category runtime testing
|
|
259
|
+
* @since 0.4.0
|
|
260
|
+
*/
|
|
261
|
+
export interface ProbeStep<M extends AnyMachine> {
|
|
262
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
263
|
+
readonly before: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
264
|
+
readonly plan: ProbePlan<M>;
|
|
265
|
+
readonly after: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
266
|
+
readonly handled: boolean;
|
|
267
|
+
readonly configurationChanged: boolean;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Testing-only causal access to a managed statechart reference.
|
|
271
|
+
*
|
|
272
|
+
* A probe does not change ordinary machine scheduling. `sendAndAwait` uses an
|
|
273
|
+
* acknowledged mailbox delivery so an ignored event can be proven processed
|
|
274
|
+
* without waiting for a snapshot that will never be published.
|
|
275
|
+
*
|
|
276
|
+
* @category runtime testing
|
|
277
|
+
* @since 0.4.0
|
|
278
|
+
*/
|
|
279
|
+
export interface Probe<M extends AnyMachine, Error = never, Output = never> {
|
|
280
|
+
readonly machine: M;
|
|
281
|
+
readonly ref: Machine.MachineRef<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Machine.Machine.InputEvent<M>, Error, Output>;
|
|
282
|
+
readonly sendAndAwait: (event: Machine.Machine.InputEvent<M>) => Effect.Effect<ProbeStep<M>, Error | Machine.StoppedError>;
|
|
283
|
+
/** Constructors for asynchronous observation after a causal command. */
|
|
284
|
+
readonly await: {
|
|
285
|
+
readonly none: internal.RuntimeAwait<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>;
|
|
286
|
+
readonly until: (predicate: (snapshot: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>) => boolean) => internal.RuntimeAwait<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>;
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Raised when `probe` receives a reference that is not backed by the managed
|
|
291
|
+
* statechart runtime.
|
|
292
|
+
*
|
|
293
|
+
* @category errors
|
|
294
|
+
* @since 0.4.0
|
|
295
|
+
*/
|
|
296
|
+
export { ProbeUnavailableError } from "../internal/testing/machine/verification.js";
|
|
297
|
+
/**
|
|
298
|
+
* Attaches testing-only causal event delivery to a running statechart.
|
|
299
|
+
*
|
|
300
|
+
* The returned probe exposes `sendAndAwait`; ordinary production sends remain
|
|
301
|
+
* available exclusively through `MachineRef.send` and retain their
|
|
302
|
+
* asynchronous enqueue-only semantics.
|
|
303
|
+
*
|
|
304
|
+
* **Example**
|
|
305
|
+
*
|
|
306
|
+
* ```ts
|
|
307
|
+
* import { Effect, Schema } from "effect"
|
|
308
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
309
|
+
* import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
310
|
+
*
|
|
311
|
+
* class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
|
|
312
|
+
* const States = Machine.defineStates({ Idle })
|
|
313
|
+
* const machine = Machine.make({
|
|
314
|
+
* states: States.states,
|
|
315
|
+
* events: [],
|
|
316
|
+
* initial: () => States.initial.Idle.from()
|
|
317
|
+
* }).handle({ Idle: {} })
|
|
318
|
+
*
|
|
319
|
+
* const program = Effect.gen(function*() {
|
|
320
|
+
* const ref = yield* Machine.start(machine)
|
|
321
|
+
* return yield* MachineTest.probe(machine, ref)
|
|
322
|
+
* })
|
|
323
|
+
* ```
|
|
324
|
+
*
|
|
325
|
+
* @category runtime testing
|
|
326
|
+
* @since 0.4.0
|
|
327
|
+
*/
|
|
328
|
+
export declare const probe: <M extends AnyMachine, Error, Output>(machine: ReadyMachine<M>, ref: Machine.MachineRef<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Machine.Machine.InputEvent<M>, Error, Output>) => Effect.Effect<Probe<M, Error, Output>, internal.ProbeUnavailableError>;
|
|
329
|
+
/**
|
|
330
|
+
* The runtime error channel exposed by a managed reference for a machine.
|
|
331
|
+
*
|
|
332
|
+
* @category utility types
|
|
333
|
+
* @since 0.4.0
|
|
334
|
+
*/
|
|
335
|
+
export type RuntimeInvariantErrorChannel<M extends AnyMachine> = Machine.Machine.Error<M> | Machine.ActionError<Machine.Machine.Services<M>> | Machine.InfiniteTransitionError | Machine.MachineSchemaDecodeError | Machine.StoppedError;
|
|
336
|
+
/**
|
|
337
|
+
* A causal command record projected independently of a reference model.
|
|
338
|
+
*
|
|
339
|
+
* @category models
|
|
340
|
+
* @since 0.4.0
|
|
341
|
+
*/
|
|
342
|
+
export interface CausalRuntimeEvidenceRecord<M extends AnyMachine, Error, Output> {
|
|
343
|
+
readonly index: number;
|
|
344
|
+
readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>;
|
|
345
|
+
readonly actual: internal.CausalRuntimeCommandActual<M, Error, Output, unknown>;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* The model-independent evidence shared by causal command transcripts.
|
|
349
|
+
*
|
|
350
|
+
* @category models
|
|
351
|
+
* @since 0.4.0
|
|
352
|
+
*/
|
|
353
|
+
export interface CausalRuntimeEvidence<M extends AnyMachine, Error, Output> {
|
|
354
|
+
readonly commands: ReadonlyArray<internal.RuntimeCommand<Machine.Machine.InputEvent<M>>>;
|
|
355
|
+
readonly initial: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>;
|
|
356
|
+
readonly records: ReadonlyArray<CausalRuntimeEvidenceRecord<M, Error, Output>>;
|
|
357
|
+
readonly final: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* The runtime snapshots selected by one snapshot invariant.
|
|
361
|
+
*
|
|
362
|
+
* @category invariants
|
|
363
|
+
* @since 0.4.0
|
|
364
|
+
*/
|
|
365
|
+
export type RuntimeSnapshotObservationMode = "settled" | "awaited" | "all" | "final";
|
|
366
|
+
/**
|
|
367
|
+
* The semantic location of one retained runtime snapshot.
|
|
368
|
+
*
|
|
369
|
+
* @category invariants
|
|
370
|
+
* @since 0.4.0
|
|
371
|
+
*/
|
|
372
|
+
export type RuntimeSnapshotObservation = "initial" | "command" | "awaited" | "final";
|
|
373
|
+
/**
|
|
374
|
+
* A model-independent command record supplied to runtime laws.
|
|
375
|
+
*
|
|
376
|
+
* @category models
|
|
377
|
+
* @since 0.4.0
|
|
378
|
+
*/
|
|
379
|
+
export interface RuntimeInvariantRecord<M extends AnyMachine> {
|
|
380
|
+
readonly index: number;
|
|
381
|
+
readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>;
|
|
382
|
+
readonly result: internal.CausalRuntimeCommandResult<M>;
|
|
383
|
+
readonly snapshot: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, RuntimeInvariantErrorChannel<M>, Machine.Machine.Output<M>>;
|
|
384
|
+
readonly awaited: ReadonlyArray<Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, RuntimeInvariantErrorChannel<M>, Machine.Machine.Output<M>>>;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* A model-independent causal transcript supplied to runtime laws.
|
|
388
|
+
*
|
|
389
|
+
* @category models
|
|
390
|
+
* @since 0.4.0
|
|
391
|
+
*/
|
|
392
|
+
export interface RuntimeInvariantTranscript<M extends AnyMachine> {
|
|
393
|
+
readonly commands: ReadonlyArray<internal.RuntimeCommand<Machine.Machine.InputEvent<M>>>;
|
|
394
|
+
readonly initial: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, RuntimeInvariantErrorChannel<M>, Machine.Machine.Output<M>>;
|
|
395
|
+
readonly records: ReadonlyArray<RuntimeInvariantRecord<M>>;
|
|
396
|
+
readonly final: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, RuntimeInvariantErrorChannel<M>, Machine.Machine.Output<M>>;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Evidence passed to a runtime snapshot invariant.
|
|
400
|
+
*
|
|
401
|
+
* @category models
|
|
402
|
+
* @since 0.4.0
|
|
403
|
+
*/
|
|
404
|
+
export interface RuntimeSnapshotInvariantContext<M extends AnyMachine> {
|
|
405
|
+
readonly machine: M;
|
|
406
|
+
readonly transcript: RuntimeInvariantTranscript<M>;
|
|
407
|
+
readonly snapshot: RuntimeInvariantTranscript<M>["initial"];
|
|
408
|
+
readonly observationIndex: number;
|
|
409
|
+
readonly phase: RuntimeSnapshotObservation;
|
|
410
|
+
readonly commandIndex: number | undefined;
|
|
411
|
+
readonly awaitedIndex: number | undefined;
|
|
412
|
+
readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>> | undefined;
|
|
413
|
+
readonly result: internal.CausalRuntimeCommandResult<M> | undefined;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Evidence passed to an invariant for one completed causal command.
|
|
417
|
+
*
|
|
418
|
+
* @category models
|
|
419
|
+
* @since 0.4.0
|
|
420
|
+
*/
|
|
421
|
+
export interface RuntimeCommandInvariantContext<M extends AnyMachine> {
|
|
422
|
+
readonly machine: M;
|
|
423
|
+
readonly transcript: RuntimeInvariantTranscript<M>;
|
|
424
|
+
readonly record: RuntimeInvariantRecord<M>;
|
|
425
|
+
readonly previous: RuntimeInvariantRecord<M> | undefined;
|
|
426
|
+
readonly index: number;
|
|
427
|
+
readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>;
|
|
428
|
+
readonly result: internal.CausalRuntimeCommandResult<M>;
|
|
429
|
+
readonly snapshot: RuntimeInvariantRecord<M>["snapshot"];
|
|
430
|
+
readonly awaited: RuntimeInvariantRecord<M>["awaited"];
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Evidence passed to a whole-runtime-transcript invariant.
|
|
434
|
+
*
|
|
435
|
+
* @category models
|
|
436
|
+
* @since 0.4.0
|
|
437
|
+
*/
|
|
438
|
+
export interface RuntimeTranscriptInvariantContext<M extends AnyMachine> {
|
|
439
|
+
readonly machine: M;
|
|
440
|
+
readonly transcript: RuntimeInvariantTranscript<M>;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Options for a runtime snapshot invariant.
|
|
444
|
+
*
|
|
445
|
+
* @category models
|
|
446
|
+
* @since 0.4.0
|
|
447
|
+
*/
|
|
448
|
+
export interface RuntimeSnapshotInvariantOptions<M extends AnyMachine> extends InvariantOptions<RuntimeSnapshotInvariantContext<M>> {
|
|
449
|
+
readonly observe?: RuntimeSnapshotObservationMode;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* A semantic property checked against selected live runtime snapshots.
|
|
453
|
+
*
|
|
454
|
+
* @category invariants
|
|
455
|
+
* @since 0.4.0
|
|
456
|
+
*/
|
|
457
|
+
export interface RuntimeSnapshotInvariant<M extends AnyMachine> extends InvariantOptions<RuntimeSnapshotInvariantContext<M>> {
|
|
458
|
+
readonly _tag: "RuntimeSnapshotInvariant";
|
|
459
|
+
readonly name: string;
|
|
460
|
+
readonly observe: RuntimeSnapshotObservationMode;
|
|
461
|
+
readonly check: (context: RuntimeSnapshotInvariantContext<M>) => InvariantOutcome;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* A semantic property checked after every completed causal command.
|
|
465
|
+
*
|
|
466
|
+
* @category invariants
|
|
467
|
+
* @since 0.4.0
|
|
468
|
+
*/
|
|
469
|
+
export interface RuntimeCommandInvariant<M extends AnyMachine> extends InvariantOptions<RuntimeCommandInvariantContext<M>> {
|
|
470
|
+
readonly _tag: "RuntimeCommandInvariant";
|
|
471
|
+
readonly name: string;
|
|
472
|
+
readonly check: (context: RuntimeCommandInvariantContext<M>) => InvariantOutcome;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* A semantic property checked once against a complete causal transcript.
|
|
476
|
+
*
|
|
477
|
+
* @category invariants
|
|
478
|
+
* @since 0.4.0
|
|
479
|
+
*/
|
|
480
|
+
export interface RuntimeTranscriptInvariant<M extends AnyMachine> extends InvariantOptions<RuntimeTranscriptInvariantContext<M>> {
|
|
481
|
+
readonly _tag: "RuntimeTranscriptInvariant";
|
|
482
|
+
readonly name: string;
|
|
483
|
+
readonly check: (context: RuntimeTranscriptInvariantContext<M>) => InvariantOutcome;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* A user-defined semantic property over retained live runtime evidence.
|
|
487
|
+
*
|
|
488
|
+
* @category invariants
|
|
489
|
+
* @since 0.4.0
|
|
490
|
+
*/
|
|
491
|
+
export type RuntimeInvariant<M extends AnyMachine> = RuntimeSnapshotInvariant<M> | RuntimeCommandInvariant<M> | RuntimeTranscriptInvariant<M>;
|
|
492
|
+
/**
|
|
493
|
+
* Machine-bound runtime invariant constructors with exact event inference.
|
|
494
|
+
*
|
|
495
|
+
* @category models
|
|
496
|
+
* @since 0.4.0
|
|
497
|
+
*/
|
|
498
|
+
export interface RuntimeInvariantBuilder<M extends AnyMachine> {
|
|
499
|
+
readonly snapshot: (name: string, check: (context: RuntimeSnapshotInvariantContext<M>) => InvariantOutcome, options?: RuntimeSnapshotInvariantOptions<M>) => RuntimeSnapshotInvariant<M>;
|
|
500
|
+
readonly command: (name: string, check: (context: RuntimeCommandInvariantContext<M>) => InvariantOutcome, options?: InvariantOptions<RuntimeCommandInvariantContext<M>>) => RuntimeCommandInvariant<M>;
|
|
501
|
+
readonly transcript: (name: string, check: (context: RuntimeTranscriptInvariantContext<M>) => InvariantOutcome, options?: InvariantOptions<RuntimeTranscriptInvariantContext<M>>) => RuntimeTranscriptInvariant<M>;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Creates reusable semantic laws for causal runtime evidence.
|
|
505
|
+
*
|
|
506
|
+
* @category constructors
|
|
507
|
+
* @since 0.4.0
|
|
508
|
+
*/
|
|
509
|
+
export declare const runtimeInvariants: <M extends AnyMachine>(machine: M) => RuntimeInvariantBuilder<M>;
|
|
510
|
+
/**
|
|
511
|
+
* Scope of a runtime invariant.
|
|
512
|
+
*
|
|
513
|
+
* @category invariants
|
|
514
|
+
* @since 0.4.0
|
|
515
|
+
*/
|
|
516
|
+
export type RuntimeInvariantScope = "snapshot" | "command" | "transcript";
|
|
517
|
+
/**
|
|
518
|
+
* Aggregate result for one runtime invariant.
|
|
519
|
+
*
|
|
520
|
+
* @category models
|
|
521
|
+
* @since 0.4.0
|
|
522
|
+
*/
|
|
523
|
+
export interface RuntimeInvariantCheckResult {
|
|
524
|
+
readonly invariant: string;
|
|
525
|
+
readonly scope: RuntimeInvariantScope;
|
|
526
|
+
readonly status: InvariantStatus;
|
|
527
|
+
readonly observations: number;
|
|
528
|
+
readonly failures: number;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Aggregate result for all checked runtime invariants.
|
|
532
|
+
*
|
|
533
|
+
* @category models
|
|
534
|
+
* @since 0.4.0
|
|
535
|
+
*/
|
|
536
|
+
export interface RuntimeInvariantReport {
|
|
537
|
+
readonly checks: ReadonlyArray<RuntimeInvariantCheckResult>;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* One runtime invariant violation and its exact retained location.
|
|
541
|
+
*
|
|
542
|
+
* @category models
|
|
543
|
+
* @since 0.4.0
|
|
544
|
+
*/
|
|
545
|
+
export interface RuntimeInvariantViolation<M extends AnyMachine = AnyMachine> {
|
|
546
|
+
readonly invariant: string;
|
|
547
|
+
readonly scope: RuntimeInvariantScope;
|
|
548
|
+
readonly kind: "predicate" | "observations";
|
|
549
|
+
readonly observationIndex?: number;
|
|
550
|
+
readonly commandIndex: number | undefined;
|
|
551
|
+
readonly awaitedIndex?: number;
|
|
552
|
+
readonly phase?: RuntimeSnapshotObservation;
|
|
553
|
+
readonly command?: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>;
|
|
554
|
+
readonly message: string;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* All violations found in one causal runtime transcript.
|
|
558
|
+
*
|
|
559
|
+
* @category errors
|
|
560
|
+
* @since 0.4.0
|
|
561
|
+
*/
|
|
562
|
+
export { RuntimeInvariantError } from "../internal/testing/machine/verification.js";
|
|
563
|
+
/**
|
|
564
|
+
* Checks runtime invariants and returns their complete non-vacuity report.
|
|
565
|
+
*
|
|
566
|
+
* @category verification
|
|
567
|
+
* @since 0.4.0
|
|
568
|
+
*/
|
|
569
|
+
export declare const checkRuntimeInvariants: <M extends AnyMachine, Error, Output>(machine: M, transcript: CausalRuntimeEvidence<M, Error, Output>, invariants: ReadonlyArray<RuntimeInvariant<M>>) => Effect.Effect<RuntimeInvariantReport, internal.RuntimeInvariantError<M>>;
|
|
570
|
+
/**
|
|
571
|
+
* Asserts runtime invariants against an existing causal transcript.
|
|
572
|
+
*
|
|
573
|
+
* @category verification
|
|
574
|
+
* @since 0.4.0
|
|
575
|
+
*/
|
|
576
|
+
export declare const assertRuntimeInvariants: <M extends AnyMachine, Error, Output>(machine: M, transcript: CausalRuntimeEvidence<M, Error, Output>, invariants: ReadonlyArray<RuntimeInvariant<M>>) => Effect.Effect<void, internal.RuntimeInvariantError<M>>;
|
|
577
|
+
/**
|
|
578
|
+
* One disagreement between pure planning and a causally processed send.
|
|
579
|
+
*
|
|
580
|
+
* @category models
|
|
581
|
+
* @since 0.4.0
|
|
582
|
+
*/
|
|
583
|
+
export interface PlannerRuntimeAgreementViolation<M extends AnyMachine = AnyMachine> {
|
|
584
|
+
readonly commandIndex: number;
|
|
585
|
+
readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>;
|
|
586
|
+
readonly field: "planning" | "handled" | "configurationChanged" | "planNext" | "after" | "completion" | "commands" | "emittedEvents" | "microsteps";
|
|
587
|
+
readonly message: string;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Raised when live causal evidence disagrees with a fresh pure plan.
|
|
591
|
+
*
|
|
592
|
+
* @category errors
|
|
593
|
+
* @since 0.4.0
|
|
594
|
+
*/
|
|
595
|
+
export { PlannerRuntimeAgreementError } from "../internal/testing/machine/verification.js";
|
|
596
|
+
/**
|
|
597
|
+
* Checks that every processed public send agrees with a fresh pure plan.
|
|
598
|
+
*
|
|
599
|
+
* @category verification
|
|
600
|
+
* @since 0.4.0
|
|
601
|
+
*/
|
|
602
|
+
export declare const assertPlannerRuntimeAgreement: <M extends AnyMachine, Error, Output>(machine: ReadyMachine<M>, transcript: CausalRuntimeEvidence<M, Error, Output>) => Effect.Effect<void, internal.PlannerRuntimeAgreementError<M>, RunServices<M>>;
|
|
603
|
+
/**
|
|
604
|
+
* The result of evaluating one semantic invariant.
|
|
605
|
+
*
|
|
606
|
+
* `true` passes, `false` produces a default failure message, and a string
|
|
607
|
+
* fails with that string as its counterexample explanation.
|
|
608
|
+
*
|
|
609
|
+
* @category invariants
|
|
610
|
+
* @since 0.4.0
|
|
611
|
+
*/
|
|
612
|
+
export type InvariantOutcome = boolean | string;
|
|
613
|
+
/**
|
|
614
|
+
* The portions of a trace that a state invariant may observe.
|
|
615
|
+
*
|
|
616
|
+
* - `settled` observes startup and the state after every public event.
|
|
617
|
+
* - `microsteps` observes every internal microstep.
|
|
618
|
+
* - `all` observes both settled states and microsteps.
|
|
619
|
+
* - `final` observes only the final state.
|
|
620
|
+
*
|
|
621
|
+
* @category invariants
|
|
622
|
+
* @since 0.4.0
|
|
623
|
+
*/
|
|
624
|
+
export type StateObservationMode = "settled" | "microsteps" | "all" | "final";
|
|
625
|
+
/**
|
|
626
|
+
* The semantic location of one state observation.
|
|
627
|
+
*
|
|
628
|
+
* @category invariants
|
|
629
|
+
* @since 0.4.0
|
|
630
|
+
*/
|
|
631
|
+
export type StateObservation = "initial" | "event" | "microstep" | "final";
|
|
632
|
+
/**
|
|
633
|
+
* Evidence passed to a state invariant.
|
|
634
|
+
*
|
|
635
|
+
* @category invariants
|
|
636
|
+
* @since 0.4.0
|
|
637
|
+
*/
|
|
638
|
+
export interface StateInvariantContext<M extends AnyMachine> {
|
|
639
|
+
readonly machine: M;
|
|
640
|
+
readonly trace: Trace<M>;
|
|
641
|
+
readonly snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
642
|
+
readonly configuration: ReadonlyArray<StatePath<M>>;
|
|
643
|
+
readonly observationIndex: number;
|
|
644
|
+
readonly phase: StateObservation;
|
|
645
|
+
readonly eventIndex: number | undefined;
|
|
646
|
+
readonly microstepIndex: number | undefined;
|
|
647
|
+
readonly event: Machine.Machine.Event<M> | Machine.InitialEvent | undefined;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Evidence passed to an invariant for one public event step.
|
|
651
|
+
*
|
|
652
|
+
* @category invariants
|
|
653
|
+
* @since 0.4.0
|
|
654
|
+
*/
|
|
655
|
+
export interface StepInvariantContext<M extends AnyMachine> {
|
|
656
|
+
readonly machine: M;
|
|
657
|
+
readonly trace: Trace<M>;
|
|
658
|
+
readonly step: TraceStep<M>;
|
|
659
|
+
readonly index: number;
|
|
660
|
+
readonly before: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
661
|
+
readonly beforeConfiguration: ReadonlyArray<StatePath<M>>;
|
|
662
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
663
|
+
readonly plan: EventPlan<M>;
|
|
664
|
+
readonly after: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
665
|
+
readonly afterConfiguration: ReadonlyArray<StatePath<M>>;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Evidence passed to a whole-trace invariant.
|
|
669
|
+
*
|
|
670
|
+
* @category invariants
|
|
671
|
+
* @since 0.4.0
|
|
672
|
+
*/
|
|
673
|
+
export interface TraceInvariantContext<M extends AnyMachine> {
|
|
674
|
+
readonly machine: M;
|
|
675
|
+
readonly trace: Trace<M>;
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Controls conditional invariant evaluation and optional non-vacuity checks.
|
|
679
|
+
*
|
|
680
|
+
* A condition that never matches is reported as `untested`. Set
|
|
681
|
+
* `require.minObservations` when that must fail the check instead.
|
|
682
|
+
*
|
|
683
|
+
* @category invariants
|
|
684
|
+
* @since 0.4.0
|
|
685
|
+
*/
|
|
686
|
+
export interface InvariantOptions<Context> {
|
|
687
|
+
readonly when?: (context: Context) => boolean;
|
|
688
|
+
readonly require?: {
|
|
689
|
+
readonly minObservations: number;
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Options for a state invariant.
|
|
694
|
+
*
|
|
695
|
+
* @category invariants
|
|
696
|
+
* @since 0.4.0
|
|
697
|
+
*/
|
|
698
|
+
export interface StateInvariantOptions<M extends AnyMachine> extends InvariantOptions<StateInvariantContext<M>> {
|
|
699
|
+
readonly observe?: StateObservationMode;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* A semantic property checked against selected state observations.
|
|
703
|
+
*
|
|
704
|
+
* @category invariants
|
|
705
|
+
* @since 0.4.0
|
|
706
|
+
*/
|
|
707
|
+
export interface StateInvariant<M extends AnyMachine> extends InvariantOptions<StateInvariantContext<M>> {
|
|
708
|
+
readonly _tag: "StateInvariant";
|
|
709
|
+
readonly name: string;
|
|
710
|
+
readonly observe: StateObservationMode;
|
|
711
|
+
readonly check: (context: StateInvariantContext<M>) => InvariantOutcome;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* A semantic property checked after every public event.
|
|
715
|
+
*
|
|
716
|
+
* @category invariants
|
|
717
|
+
* @since 0.4.0
|
|
718
|
+
*/
|
|
719
|
+
export interface StepInvariant<M extends AnyMachine> extends InvariantOptions<StepInvariantContext<M>> {
|
|
720
|
+
readonly _tag: "StepInvariant";
|
|
721
|
+
readonly name: string;
|
|
722
|
+
readonly check: (context: StepInvariantContext<M>) => InvariantOutcome;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* A semantic property checked once against a complete trace.
|
|
726
|
+
*
|
|
727
|
+
* @category invariants
|
|
728
|
+
* @since 0.4.0
|
|
729
|
+
*/
|
|
730
|
+
export interface TraceInvariant<M extends AnyMachine> extends InvariantOptions<TraceInvariantContext<M>> {
|
|
731
|
+
readonly _tag: "TraceInvariant";
|
|
732
|
+
readonly name: string;
|
|
733
|
+
readonly check: (context: TraceInvariantContext<M>) => InvariantOutcome;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* A user-defined semantic property over a planner trace.
|
|
737
|
+
*
|
|
738
|
+
* @category invariants
|
|
739
|
+
* @since 0.4.0
|
|
740
|
+
*/
|
|
741
|
+
export type Invariant<M extends AnyMachine> = StateInvariant<M> | StepInvariant<M> | TraceInvariant<M>;
|
|
742
|
+
/**
|
|
743
|
+
* Machine-bound invariant constructors with complete contextual inference.
|
|
744
|
+
*
|
|
745
|
+
* @category invariants
|
|
746
|
+
* @since 0.4.0
|
|
747
|
+
*/
|
|
748
|
+
export interface InvariantBuilder<M extends AnyMachine> {
|
|
749
|
+
readonly state: (name: string, check: (context: StateInvariantContext<M>) => InvariantOutcome, options?: StateInvariantOptions<M>) => StateInvariant<M>;
|
|
750
|
+
readonly step: (name: string, check: (context: StepInvariantContext<M>) => InvariantOutcome, options?: InvariantOptions<StepInvariantContext<M>>) => StepInvariant<M>;
|
|
751
|
+
readonly trace: (name: string, check: (context: TraceInvariantContext<M>) => InvariantOutcome, options?: InvariantOptions<TraceInvariantContext<M>>) => TraceInvariant<M>;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Direct invariant constructors. Prefer `invariants(machine)` when contextual
|
|
755
|
+
* machine types should be inferred without an explicit type argument.
|
|
756
|
+
*
|
|
757
|
+
* @category constructors
|
|
758
|
+
* @since 0.4.0
|
|
759
|
+
*/
|
|
760
|
+
export declare const Invariant: {
|
|
761
|
+
readonly state: <M extends AnyMachine>(name: string, check: (context: StateInvariantContext<M>) => InvariantOutcome, options?: StateInvariantOptions<M>) => StateInvariant<M>;
|
|
762
|
+
readonly step: <M extends AnyMachine>(name: string, check: (context: StepInvariantContext<M>) => InvariantOutcome, options?: InvariantOptions<StepInvariantContext<M>>) => StepInvariant<M>;
|
|
763
|
+
readonly trace: <M extends AnyMachine>(name: string, check: (context: TraceInvariantContext<M>) => InvariantOutcome, options?: InvariantOptions<TraceInvariantContext<M>>) => TraceInvariant<M>;
|
|
764
|
+
};
|
|
765
|
+
/**
|
|
766
|
+
* Creates invariant constructors bound to a machine's exact state and event
|
|
767
|
+
* types. The machine is used only for inference; invariant evaluation remains
|
|
768
|
+
* pure and reusable across traces from that machine.
|
|
769
|
+
*
|
|
770
|
+
* **Example**
|
|
771
|
+
*
|
|
772
|
+
* ```ts
|
|
773
|
+
* import { Schema } from "effect"
|
|
774
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
775
|
+
* import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
776
|
+
*
|
|
777
|
+
* class Count extends Schema.TaggedClass<Count>("Count")("Count", {
|
|
778
|
+
* value: Schema.Number
|
|
779
|
+
* }) {}
|
|
780
|
+
* const States = Machine.defineStates({ Count })
|
|
781
|
+
* const machine = Machine.make({
|
|
782
|
+
* states: States.states,
|
|
783
|
+
* events: [],
|
|
784
|
+
* initial: () => States.initial.Count(new Count({ value: 0 }))
|
|
785
|
+
* }).handle({ Count: {} })
|
|
786
|
+
*
|
|
787
|
+
* const nonNegative = MachineTest.invariants(machine).state(
|
|
788
|
+
* "count is non-negative",
|
|
789
|
+
* ({ snapshot }) => snapshot.value.value >= 0
|
|
790
|
+
* )
|
|
791
|
+
* ```
|
|
792
|
+
*
|
|
793
|
+
* @category constructors
|
|
794
|
+
* @since 0.4.0
|
|
795
|
+
*/
|
|
796
|
+
export declare const invariants: <M extends AnyMachine>(machine: M) => InvariantBuilder<M>;
|
|
797
|
+
/**
|
|
798
|
+
* The scope of a semantic invariant.
|
|
799
|
+
*
|
|
800
|
+
* @category invariants
|
|
801
|
+
* @since 0.4.0
|
|
802
|
+
*/
|
|
803
|
+
export type InvariantScope = "state" | "step" | "trace";
|
|
804
|
+
/**
|
|
805
|
+
* Result status for one invariant.
|
|
806
|
+
*
|
|
807
|
+
* `untested` is non-failing unless the invariant declares a minimum number of
|
|
808
|
+
* observations, in which case it becomes `insufficient`.
|
|
809
|
+
*
|
|
810
|
+
* @category invariants
|
|
811
|
+
* @since 0.4.0
|
|
812
|
+
*/
|
|
813
|
+
export type InvariantStatus = "passed" | "failed" | "untested" | "insufficient";
|
|
814
|
+
/**
|
|
815
|
+
* Aggregate result for one invariant.
|
|
816
|
+
*
|
|
817
|
+
* @category invariants
|
|
818
|
+
* @since 0.4.0
|
|
819
|
+
*/
|
|
820
|
+
export interface InvariantCheckResult {
|
|
821
|
+
readonly invariant: string;
|
|
822
|
+
readonly scope: InvariantScope;
|
|
823
|
+
readonly status: InvariantStatus;
|
|
824
|
+
readonly observations: number;
|
|
825
|
+
readonly failures: number;
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Aggregate result for all checked invariants.
|
|
829
|
+
*
|
|
830
|
+
* @category invariants
|
|
831
|
+
* @since 0.4.0
|
|
832
|
+
*/
|
|
833
|
+
export interface InvariantReport {
|
|
834
|
+
readonly checks: ReadonlyArray<InvariantCheckResult>;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* One semantic invariant violation with its precise trace location.
|
|
838
|
+
*
|
|
839
|
+
* @category invariants
|
|
840
|
+
* @since 0.4.0
|
|
841
|
+
*/
|
|
842
|
+
export interface InvariantViolation<M extends AnyMachine = AnyMachine> {
|
|
843
|
+
readonly invariant: string;
|
|
844
|
+
readonly scope: InvariantScope;
|
|
845
|
+
readonly kind: "predicate" | "observations";
|
|
846
|
+
readonly observationIndex?: number;
|
|
847
|
+
readonly eventIndex: number | undefined;
|
|
848
|
+
readonly microstepIndex?: number;
|
|
849
|
+
readonly phase?: StateObservation;
|
|
850
|
+
readonly configuration?: ReadonlyArray<StatePath<M>>;
|
|
851
|
+
readonly event?: Machine.Machine.Event<M> | Machine.InitialEvent;
|
|
852
|
+
readonly message: string;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* All semantic violations found in one trace, together with the complete
|
|
856
|
+
* counterexample and aggregate report.
|
|
857
|
+
*
|
|
858
|
+
* @category errors
|
|
859
|
+
* @since 0.4.0
|
|
860
|
+
*/
|
|
861
|
+
export { InvariantError } from "../internal/testing/machine/verification.js";
|
|
862
|
+
/**
|
|
863
|
+
* Checks user-defined semantic invariants against an existing planner trace.
|
|
864
|
+
*
|
|
865
|
+
* Every invariant and matching observation is evaluated so one failure
|
|
866
|
+
* contains all relevant evidence. Combine this with `scenarios` and `run` in
|
|
867
|
+
* an Effect property test to retain FastCheck shrinking.
|
|
868
|
+
*
|
|
869
|
+
* @category verification
|
|
870
|
+
* @since 0.4.0
|
|
871
|
+
*/
|
|
872
|
+
export declare const checkInvariants: <M extends AnyMachine>(machine: M, trace: Trace<M>, invariants: ReadonlyArray<Invariant<M>>) => Effect.Effect<InvariantReport, internal.InvariantError<M>>;
|
|
873
|
+
/**
|
|
874
|
+
* Asserts user-defined semantic invariants and discards the success report.
|
|
875
|
+
*
|
|
876
|
+
* This is the property-test-oriented form of `checkInvariants`: its `void`
|
|
877
|
+
* success works directly with `it.effect.prop`, while failures retain the
|
|
878
|
+
* same complete report and trace evidence.
|
|
879
|
+
*
|
|
880
|
+
* @category verification
|
|
881
|
+
* @since 0.4.0
|
|
882
|
+
*/
|
|
883
|
+
export declare const assertInvariants: <M extends AnyMachine>(machine: M, trace: Trace<M>, invariants: ReadonlyArray<Invariant<M>>) => Effect.Effect<void, internal.InvariantError<M>>;
|
|
884
|
+
/**
|
|
885
|
+
* User-defined identity for a logical exploration state.
|
|
886
|
+
*
|
|
887
|
+
* Equal keys deliberately collapse snapshots into one explored state. The key
|
|
888
|
+
* therefore defines both finiteness and the semantic precision of an
|
|
889
|
+
* exploration.
|
|
890
|
+
*
|
|
891
|
+
* @category exploration
|
|
892
|
+
* @since 0.4.0
|
|
893
|
+
*/
|
|
894
|
+
export type ExplorationKey = PropertyKey;
|
|
895
|
+
/**
|
|
896
|
+
* Hard bounds for one exploration.
|
|
897
|
+
*
|
|
898
|
+
* Defaults are 20 public events, 1,000 states, and 10,000 planned
|
|
899
|
+
* transitions. A limit never makes an incomplete result appear exhaustive.
|
|
900
|
+
*
|
|
901
|
+
* @category exploration
|
|
902
|
+
* @since 0.4.0
|
|
903
|
+
*/
|
|
904
|
+
export interface ExplorationLimits {
|
|
905
|
+
readonly maxDepth?: number;
|
|
906
|
+
readonly maxStates?: number;
|
|
907
|
+
readonly maxTransitions?: number;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Resolved bounds retained by an exploration result.
|
|
911
|
+
*
|
|
912
|
+
* @category exploration
|
|
913
|
+
* @since 0.4.0
|
|
914
|
+
*/
|
|
915
|
+
export interface ResolvedExplorationLimits {
|
|
916
|
+
readonly maxDepth: number;
|
|
917
|
+
readonly maxStates: number;
|
|
918
|
+
readonly maxTransitions: number;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Evidence available while assigning a state key.
|
|
922
|
+
*
|
|
923
|
+
* @category exploration
|
|
924
|
+
* @since 0.4.0
|
|
925
|
+
*/
|
|
926
|
+
export interface ExplorationStateContext<M extends AnyMachine> {
|
|
927
|
+
readonly machine: M;
|
|
928
|
+
readonly snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
929
|
+
readonly configuration: ReadonlyArray<StatePath<M>>;
|
|
930
|
+
readonly depth: number;
|
|
931
|
+
readonly trace: Trace<M>;
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* One logical state discovered by breadth-first exploration.
|
|
935
|
+
*
|
|
936
|
+
* `trace` is the first, and therefore shortest, trace that reached `key`.
|
|
937
|
+
*
|
|
938
|
+
* @category exploration
|
|
939
|
+
* @since 0.4.0
|
|
940
|
+
*/
|
|
941
|
+
export interface ExplorationNode<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> extends ExplorationStateContext<M> {
|
|
942
|
+
readonly key: Key;
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* One concretely planned public event in the exploration graph.
|
|
946
|
+
*
|
|
947
|
+
* @category exploration
|
|
948
|
+
* @since 0.4.0
|
|
949
|
+
*/
|
|
950
|
+
export interface ExplorationEdge<M extends AnyMachine> {
|
|
951
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
952
|
+
readonly step: TraceStep<M>;
|
|
953
|
+
readonly discovered: boolean;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* One boundary that could not be explored because a hard limit was reached.
|
|
957
|
+
*
|
|
958
|
+
* @category exploration
|
|
959
|
+
* @since 0.4.0
|
|
960
|
+
*/
|
|
961
|
+
export type ExplorationFrontier<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> = {
|
|
962
|
+
readonly _tag: "DepthLimit";
|
|
963
|
+
readonly source: Key;
|
|
964
|
+
readonly trace: Trace<M>;
|
|
965
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
966
|
+
} | {
|
|
967
|
+
readonly _tag: "StateLimit";
|
|
968
|
+
readonly source: Key;
|
|
969
|
+
readonly trace: Trace<M>;
|
|
970
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
971
|
+
readonly target: Key;
|
|
972
|
+
readonly targetTrace: Trace<M>;
|
|
973
|
+
} | {
|
|
974
|
+
readonly _tag: "TransitionLimit";
|
|
975
|
+
readonly source: Key;
|
|
976
|
+
readonly trace: Trace<M>;
|
|
977
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
978
|
+
};
|
|
979
|
+
/**
|
|
980
|
+
* Honest completeness status for the supplied event representatives and state
|
|
981
|
+
* key abstraction.
|
|
982
|
+
*
|
|
983
|
+
* @category exploration
|
|
984
|
+
* @since 0.4.0
|
|
985
|
+
*/
|
|
986
|
+
export type ExplorationCompleteness<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> = {
|
|
987
|
+
readonly _tag: "Complete";
|
|
988
|
+
} | {
|
|
989
|
+
readonly _tag: "Truncated";
|
|
990
|
+
readonly reasons: ReadonlyArray<"depth" | "states" | "transitions">;
|
|
991
|
+
readonly frontier: ReadonlyArray<ExplorationFrontier<M, Key>>;
|
|
992
|
+
};
|
|
993
|
+
/**
|
|
994
|
+
* Deterministic breadth-first exploration counts.
|
|
995
|
+
*
|
|
996
|
+
* @category exploration
|
|
997
|
+
* @since 0.4.0
|
|
998
|
+
*/
|
|
999
|
+
export interface ExplorationStats {
|
|
1000
|
+
readonly states: number;
|
|
1001
|
+
readonly plannedTransitions: number;
|
|
1002
|
+
readonly retainedEdges: number;
|
|
1003
|
+
readonly maxDepth: number;
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* A bounded logical state graph and its shortest-path evidence.
|
|
1007
|
+
*
|
|
1008
|
+
* @category exploration
|
|
1009
|
+
* @since 0.4.0
|
|
1010
|
+
*/
|
|
1011
|
+
export interface Exploration<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> {
|
|
1012
|
+
readonly graph: Graph.DirectedGraph<ExplorationNode<M, Key>, ExplorationEdge<M>>;
|
|
1013
|
+
readonly nodes: ReadonlyArray<ExplorationNode<M, Key>>;
|
|
1014
|
+
readonly nodesByKey: ReadonlyMap<Key, Graph.NodeIndex>;
|
|
1015
|
+
readonly start: Graph.NodeIndex;
|
|
1016
|
+
readonly limits: ResolvedExplorationLimits;
|
|
1017
|
+
readonly stats: ExplorationStats;
|
|
1018
|
+
readonly completeness: ExplorationCompleteness<M, Key>;
|
|
1019
|
+
}
|
|
1020
|
+
interface ExploreOptionsBase<M extends AnyMachine, Key extends ExplorationKey> {
|
|
1021
|
+
readonly events: (context: ExplorationStateContext<M>) => ReadonlyArray<Machine.Machine.InputEvent<M>>;
|
|
1022
|
+
readonly stateKey: (context: ExplorationStateContext<M>) => Key;
|
|
1023
|
+
readonly limits?: ExplorationLimits;
|
|
1024
|
+
readonly invariants?: ReadonlyArray<Invariant<M>>;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Configuration for bounded planner exploration.
|
|
1028
|
+
*
|
|
1029
|
+
* Event representatives may depend on the current state. Exploration is
|
|
1030
|
+
* exhaustive only relative to those representatives and the equivalence
|
|
1031
|
+
* relation defined by `stateKey`.
|
|
1032
|
+
*
|
|
1033
|
+
* @category exploration
|
|
1034
|
+
* @since 0.4.0
|
|
1035
|
+
*/
|
|
1036
|
+
export type ExploreOptions<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> = ExploreOptionsBase<M, Key> & (Machine.Machine.Input<M> extends typeof Schema.Void ? {
|
|
1037
|
+
readonly input?: never;
|
|
1038
|
+
} : {
|
|
1039
|
+
readonly input: InputValue<M>;
|
|
1040
|
+
});
|
|
1041
|
+
/**
|
|
1042
|
+
* Explores the bounded logical state graph in breadth-first order.
|
|
1043
|
+
*
|
|
1044
|
+
* Invariants are checked against startup and every concretely planned edge,
|
|
1045
|
+
* so a failure retains a shortest discovered counterexample. Staged actions
|
|
1046
|
+
* and runtime activities are not executed.
|
|
1047
|
+
*
|
|
1048
|
+
* **Example**
|
|
1049
|
+
*
|
|
1050
|
+
* ```ts
|
|
1051
|
+
* import { Schema } from "effect"
|
|
1052
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
1053
|
+
* import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
1054
|
+
*
|
|
1055
|
+
* class Count extends Schema.TaggedClass<Count>("Count")("Count", {
|
|
1056
|
+
* value: Schema.Number
|
|
1057
|
+
* }) {}
|
|
1058
|
+
* class Increment extends Schema.TaggedClass<Increment>("Increment")("Increment", {}) {}
|
|
1059
|
+
* const States = Machine.defineStates({ Count })
|
|
1060
|
+
* const machine = Machine.make({
|
|
1061
|
+
* states: States.states,
|
|
1062
|
+
* events: [Increment],
|
|
1063
|
+
* initial: () => States.initial.Count(new Count({ value: 0 }))
|
|
1064
|
+
* }).handle({
|
|
1065
|
+
* Count: { on: { Increment: ({ state }) =>
|
|
1066
|
+
* States.initial.Count(new Count({ value: state.value + 1 })) } }
|
|
1067
|
+
* })
|
|
1068
|
+
*
|
|
1069
|
+
* const explored = MachineTest.explore(machine, {
|
|
1070
|
+
* events: ({ snapshot }) => snapshot.value.value < 2 ? [new Increment({})] : [],
|
|
1071
|
+
* stateKey: ({ snapshot }) => snapshot.value.value
|
|
1072
|
+
* })
|
|
1073
|
+
* ```
|
|
1074
|
+
*
|
|
1075
|
+
* @category exploration
|
|
1076
|
+
* @since 0.4.0
|
|
1077
|
+
*/
|
|
1078
|
+
export declare const explore: <M extends AnyMachine, Key extends ExplorationKey>(machine: ReadyMachine<M>, options: ExploreOptions<M, Key>) => Effect.Effect<Exploration<M, Key>, RunFailure<RunError<M>, M> | internal.InvariantError<M>, RunServices<M>>;
|
|
1079
|
+
/**
|
|
1080
|
+
* A predicate over one explored logical state.
|
|
1081
|
+
*
|
|
1082
|
+
* @category exploration
|
|
1083
|
+
* @since 0.4.0
|
|
1084
|
+
*/
|
|
1085
|
+
export type ExplorationPredicate<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> = (node: ExplorationNode<M, Key>) => boolean;
|
|
1086
|
+
/**
|
|
1087
|
+
* Why a reachability assertion failed.
|
|
1088
|
+
*
|
|
1089
|
+
* @category exploration
|
|
1090
|
+
* @since 0.4.0
|
|
1091
|
+
*/
|
|
1092
|
+
export type ReachabilityFailure = "NotFound" | "UnexpectedMatch" | "Inconclusive";
|
|
1093
|
+
/**
|
|
1094
|
+
* A failed or inconclusive reachability assertion.
|
|
1095
|
+
*
|
|
1096
|
+
* @category errors
|
|
1097
|
+
* @since 0.4.0
|
|
1098
|
+
*/
|
|
1099
|
+
export { ReachabilityError } from "../internal/testing/machine/verification.js";
|
|
1100
|
+
/**
|
|
1101
|
+
* Finds the first, and therefore shortest, explored state matching a
|
|
1102
|
+
* predicate.
|
|
1103
|
+
*
|
|
1104
|
+
* @category exploration
|
|
1105
|
+
* @since 0.4.0
|
|
1106
|
+
*/
|
|
1107
|
+
export declare const findShortest: <M extends AnyMachine, Key extends ExplorationKey>(exploration: Exploration<M, Key>, predicate: ExplorationPredicate<M, Key>) => ExplorationNode<M, Key> | undefined;
|
|
1108
|
+
/**
|
|
1109
|
+
* Requires a matching state and returns its shortest witness.
|
|
1110
|
+
*
|
|
1111
|
+
* A truncated exploration without a witness fails as inconclusive rather than
|
|
1112
|
+
* claiming the state is unreachable.
|
|
1113
|
+
*
|
|
1114
|
+
* @category exploration
|
|
1115
|
+
* @since 0.4.0
|
|
1116
|
+
*/
|
|
1117
|
+
export declare const assertReachable: <M extends AnyMachine, Key extends ExplorationKey>(exploration: Exploration<M, Key>, name: string, predicate: ExplorationPredicate<M, Key>) => Effect.Effect<ExplorationNode<M, Key>, internal.ReachabilityError<M, Key>>;
|
|
1118
|
+
/**
|
|
1119
|
+
* Requires that no explored state matches a predicate.
|
|
1120
|
+
*
|
|
1121
|
+
* This assertion succeeds only for a complete exploration. A truncated
|
|
1122
|
+
* result without a witness fails as inconclusive.
|
|
1123
|
+
*
|
|
1124
|
+
* @category exploration
|
|
1125
|
+
* @since 0.4.0
|
|
1126
|
+
*/
|
|
1127
|
+
export declare const assertUnreachable: <M extends AnyMachine, Key extends ExplorationKey>(exploration: Exploration<M, Key>, name: string, predicate: ExplorationPredicate<M, Key>) => Effect.Effect<void, internal.ReachabilityError<M, Key>>;
|
|
1128
|
+
/**
|
|
1129
|
+
* Checks a real planner trace against the independent finite statechart model
|
|
1130
|
+
* interpreter.
|
|
1131
|
+
*
|
|
1132
|
+
* The oracle does not import the machine compiler, planner, snapshot helpers,
|
|
1133
|
+
* or target builders. It compares semantic projections of the public trace
|
|
1134
|
+
* and accumulates every disagreement in one structured error.
|
|
1135
|
+
*
|
|
1136
|
+
* @category verification
|
|
1137
|
+
* @since 0.4.0
|
|
1138
|
+
*/
|
|
1139
|
+
export declare const verifyModel: <M extends AnyMachine>(model: FiniteModel, actualTrace: Trace<M>) => Effect.Effect<void, ReferenceModel.ModelVerificationError>;
|
|
1140
|
+
/**
|
|
1141
|
+
* A typed planning failure together with every successfully completed trace
|
|
1142
|
+
* segment preceding it.
|
|
1143
|
+
*
|
|
1144
|
+
* @category models
|
|
1145
|
+
* @since 0.4.0
|
|
1146
|
+
*/
|
|
1147
|
+
export type RunFailure<Cause, M extends AnyMachine = AnyMachine> = {
|
|
1148
|
+
readonly _tag: "MachineTestRunFailure";
|
|
1149
|
+
readonly scenario: Scenario<M>;
|
|
1150
|
+
readonly phase: "initial";
|
|
1151
|
+
readonly eventIndex: undefined;
|
|
1152
|
+
readonly event: undefined;
|
|
1153
|
+
readonly initial: undefined;
|
|
1154
|
+
readonly steps: readonly [];
|
|
1155
|
+
readonly cause: Cause;
|
|
1156
|
+
} | {
|
|
1157
|
+
readonly _tag: "MachineTestRunFailure";
|
|
1158
|
+
readonly scenario: Scenario<M>;
|
|
1159
|
+
readonly phase: "event";
|
|
1160
|
+
readonly eventIndex: number;
|
|
1161
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
1162
|
+
readonly initial: InitialTrace<M>;
|
|
1163
|
+
readonly steps: ReadonlyArray<TraceStep<M>>;
|
|
1164
|
+
readonly cause: Cause;
|
|
1165
|
+
};
|
|
1166
|
+
/**
|
|
1167
|
+
* Errors that can be produced while planning a complete scenario.
|
|
1168
|
+
*
|
|
1169
|
+
* @category errors
|
|
1170
|
+
* @since 0.4.0
|
|
1171
|
+
*/
|
|
1172
|
+
export type RunError<M extends AnyMachine> = Machine.Machine.InitialError<M> | Machine.Machine.Error<M> | Machine.InfiniteTransitionError | Machine.MachineSchemaDecodeError | Machine.StartupError;
|
|
1173
|
+
/**
|
|
1174
|
+
* Services required while planning a complete scenario.
|
|
1175
|
+
*
|
|
1176
|
+
* Scenario execution delegates exclusively to the service-free `planInitial`
|
|
1177
|
+
* and `plan` APIs. Invoke services and managed runtime capabilities belong to
|
|
1178
|
+
* later execution, not synchronous planning.
|
|
1179
|
+
*
|
|
1180
|
+
* @category models
|
|
1181
|
+
* @since 0.4.0
|
|
1182
|
+
*/
|
|
1183
|
+
export type RunServices<M extends AnyMachine> = IsAny<Machine.PlanningServices<Machine.Machine.InitialServices<M> | Machine.Machine.Services<M>>> extends true ? Machine.PlanningServices<Machine.Machine.InitialServices<M> | Machine.Machine.Services<M>> : never;
|
|
1184
|
+
/**
|
|
1185
|
+
* Executes a generated scenario exclusively through `planInitial` and `plan`.
|
|
1186
|
+
*
|
|
1187
|
+
* Staged actions are retained in each plan but are never executed. Every event
|
|
1188
|
+
* is planned, including events that occur after a terminal configuration.
|
|
1189
|
+
* Typed planning errors retain the scenario and successfully completed prefix
|
|
1190
|
+
* in a `RunFailure`.
|
|
1191
|
+
*
|
|
1192
|
+
* **Example**
|
|
1193
|
+
*
|
|
1194
|
+
* ```ts
|
|
1195
|
+
* import { Schema } from "effect"
|
|
1196
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
1197
|
+
* import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
1198
|
+
*
|
|
1199
|
+
* class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
|
|
1200
|
+
* const States = Machine.defineStates({ Idle })
|
|
1201
|
+
* const machine = Machine.make({
|
|
1202
|
+
* states: States.states,
|
|
1203
|
+
* events: [],
|
|
1204
|
+
* initial: () => States.initial.Idle.from()
|
|
1205
|
+
* }).handle({ Idle: {} })
|
|
1206
|
+
*
|
|
1207
|
+
* const trace = MachineTest.run(machine, { events: [] })
|
|
1208
|
+
* ```
|
|
1209
|
+
*
|
|
1210
|
+
* @category constructors
|
|
1211
|
+
* @since 0.4.0
|
|
1212
|
+
*/
|
|
1213
|
+
export declare const run: <M extends AnyMachine>(machine: ReadyMachine<M>, scenario: Scenario<M>) => Effect.Effect<Trace<M>, RunFailure<RunError<M>, M>, RunServices<M>>;
|
|
1214
|
+
/**
|
|
1215
|
+
* A deterministic hit/miss summary for a finite set declared by a machine.
|
|
1216
|
+
*
|
|
1217
|
+
* @category models
|
|
1218
|
+
* @since 0.4.0
|
|
1219
|
+
*/
|
|
1220
|
+
export interface CoverageSummary<Item> {
|
|
1221
|
+
readonly total: number;
|
|
1222
|
+
readonly hit: number;
|
|
1223
|
+
readonly missing: number;
|
|
1224
|
+
readonly hits: ReadonlyArray<Item>;
|
|
1225
|
+
readonly misses: ReadonlyArray<Item>;
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* One active (non-history) state node in a state coverage summary.
|
|
1229
|
+
*
|
|
1230
|
+
* @category models
|
|
1231
|
+
* @since 0.4.0
|
|
1232
|
+
*/
|
|
1233
|
+
export interface StateCoverageItem<Path extends string = string> {
|
|
1234
|
+
readonly path: Path;
|
|
1235
|
+
readonly type: Exclude<Machine.Machine.StateNode["type"], "history">;
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* State activation and lifecycle coverage.
|
|
1239
|
+
*
|
|
1240
|
+
* @category models
|
|
1241
|
+
* @since 0.4.0
|
|
1242
|
+
*/
|
|
1243
|
+
export interface StateCoverage<Path extends string = string> {
|
|
1244
|
+
readonly activation: CoverageSummary<StateCoverageItem<Path>>;
|
|
1245
|
+
readonly entry: CoverageSummary<StateCoverageItem<Path>>;
|
|
1246
|
+
readonly exit: CoverageSummary<StateCoverageItem<Path>>;
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* One stable transition-definition identity in definition order.
|
|
1250
|
+
*
|
|
1251
|
+
* @category models
|
|
1252
|
+
* @since 0.4.0
|
|
1253
|
+
*/
|
|
1254
|
+
export interface TransitionCoverageItem<SourcePath extends string = string, EventTag extends PropertyKey = PropertyKey, TargetPath extends string = SourcePath> {
|
|
1255
|
+
readonly id: string;
|
|
1256
|
+
readonly index: number;
|
|
1257
|
+
readonly source: SourcePath;
|
|
1258
|
+
readonly trigger: Machine.Machine.TransitionTrigger<EventTag>;
|
|
1259
|
+
readonly reenter: boolean;
|
|
1260
|
+
readonly targets: Machine.Machine.TransitionTargets<TargetPath>;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* One public event tag declared by the machine.
|
|
1264
|
+
*
|
|
1265
|
+
* @category models
|
|
1266
|
+
* @since 0.4.0
|
|
1267
|
+
*/
|
|
1268
|
+
export interface EventCoverageItem<Tag extends PropertyKey = PropertyKey> {
|
|
1269
|
+
readonly tag: Tag;
|
|
1270
|
+
readonly count: number;
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Public event coverage, including events that no transition retained.
|
|
1274
|
+
*
|
|
1275
|
+
* @category models
|
|
1276
|
+
* @since 0.4.0
|
|
1277
|
+
*/
|
|
1278
|
+
export type EventCoverage<Tag extends PropertyKey = PropertyKey> = {
|
|
1279
|
+
readonly available: true;
|
|
1280
|
+
readonly total: number;
|
|
1281
|
+
readonly hit: number;
|
|
1282
|
+
readonly missing: number;
|
|
1283
|
+
readonly hits: ReadonlyArray<EventCoverageItem<Tag>>;
|
|
1284
|
+
readonly misses: ReadonlyArray<EventCoverageItem<Tag>>;
|
|
1285
|
+
readonly observed: ReadonlyArray<EventCoverageItem<Tag>>;
|
|
1286
|
+
readonly diagnostics: readonly [];
|
|
1287
|
+
} | {
|
|
1288
|
+
readonly available: false;
|
|
1289
|
+
readonly total: undefined;
|
|
1290
|
+
readonly hit: undefined;
|
|
1291
|
+
readonly missing: undefined;
|
|
1292
|
+
readonly hits: undefined;
|
|
1293
|
+
readonly misses: undefined;
|
|
1294
|
+
readonly observed: ReadonlyArray<EventCoverageItem<Tag>>;
|
|
1295
|
+
readonly diagnostics: ReadonlyArray<{
|
|
1296
|
+
readonly schemaIndex: number;
|
|
1297
|
+
readonly message: string;
|
|
1298
|
+
}>;
|
|
1299
|
+
};
|
|
1300
|
+
/**
|
|
1301
|
+
* Trace-derived scenario counts. There is no finite declared scenario space.
|
|
1302
|
+
*
|
|
1303
|
+
* @category models
|
|
1304
|
+
* @since 0.4.0
|
|
1305
|
+
*/
|
|
1306
|
+
export interface ScenarioCoverage {
|
|
1307
|
+
readonly traces: number;
|
|
1308
|
+
readonly events: number;
|
|
1309
|
+
readonly empty: number;
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Trace-derived logical configuration counts. There is no claimed exhaustive total.
|
|
1313
|
+
*
|
|
1314
|
+
* @category models
|
|
1315
|
+
* @since 0.4.0
|
|
1316
|
+
*/
|
|
1317
|
+
export interface LogicalConfigurationCoverage {
|
|
1318
|
+
readonly observations: number;
|
|
1319
|
+
readonly hit: number;
|
|
1320
|
+
readonly identities: ReadonlyArray<string>;
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Directly observed startup and microstep evidence.
|
|
1324
|
+
*
|
|
1325
|
+
* @category models
|
|
1326
|
+
* @since 0.4.0
|
|
1327
|
+
*/
|
|
1328
|
+
export interface MicrostepCoverageEvidence {
|
|
1329
|
+
readonly total: number;
|
|
1330
|
+
readonly changed: number;
|
|
1331
|
+
readonly targetless: number;
|
|
1332
|
+
readonly raised: number;
|
|
1333
|
+
readonly emitted: number;
|
|
1334
|
+
readonly eventTriggered: number;
|
|
1335
|
+
readonly alwaysTriggered: number;
|
|
1336
|
+
readonly doneTriggered: number;
|
|
1337
|
+
readonly choiceTriggered: number;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Directly observed completion evidence.
|
|
1341
|
+
*
|
|
1342
|
+
* @category models
|
|
1343
|
+
* @since 0.4.0
|
|
1344
|
+
*/
|
|
1345
|
+
export interface CompletionCoverageEvidence<Path extends string = string> {
|
|
1346
|
+
readonly donePlans: number;
|
|
1347
|
+
readonly recordObservations: number;
|
|
1348
|
+
readonly paths: ReadonlyArray<Path>;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Directly observed history records and history-target transitions.
|
|
1352
|
+
*
|
|
1353
|
+
* @category models
|
|
1354
|
+
* @since 0.4.0
|
|
1355
|
+
*/
|
|
1356
|
+
export interface HistoryCoverageEvidence<Path extends string = string> {
|
|
1357
|
+
readonly recordObservations: number;
|
|
1358
|
+
readonly recorded: ReadonlyArray<{
|
|
1359
|
+
readonly path: Path;
|
|
1360
|
+
readonly modes: ReadonlyArray<"shallow" | "deep">;
|
|
1361
|
+
}>;
|
|
1362
|
+
readonly targets: number;
|
|
1363
|
+
readonly resolvedTargets: number;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Coverage computed only from observable machine definitions and planner traces.
|
|
1367
|
+
*
|
|
1368
|
+
* @category models
|
|
1369
|
+
* @since 0.4.0
|
|
1370
|
+
*/
|
|
1371
|
+
export interface Coverage<M extends AnyMachine> {
|
|
1372
|
+
readonly states: StateCoverage<StatePath<M>>;
|
|
1373
|
+
readonly transitions: CoverageSummary<TransitionCoverageItem<StateNodePath<M>, Machine.Machine.TagOf<Machine.Machine.Events<M>[number]>, StateNodePath<M>>>;
|
|
1374
|
+
readonly events: EventCoverage<Machine.Machine.TagOf<Machine.Machine.InputEvents<M>[number]>>;
|
|
1375
|
+
readonly scenarios: ScenarioCoverage;
|
|
1376
|
+
readonly logicalConfigurations: LogicalConfigurationCoverage;
|
|
1377
|
+
readonly startup: {
|
|
1378
|
+
readonly traces: number;
|
|
1379
|
+
readonly withMicrosteps: number;
|
|
1380
|
+
};
|
|
1381
|
+
readonly microsteps: MicrostepCoverageEvidence;
|
|
1382
|
+
readonly completion: CompletionCoverageEvidence<StatePath<M>>;
|
|
1383
|
+
readonly history: HistoryCoverageEvidence<StateNodePath<M>>;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Computes deterministic, definition-aware coverage from completed planner
|
|
1387
|
+
* traces. Finite declared sets report hits and misses; scenarios and logical
|
|
1388
|
+
* configurations report observations only because their complete spaces are
|
|
1389
|
+
* generally infinite.
|
|
1390
|
+
*
|
|
1391
|
+
* **Example**
|
|
1392
|
+
*
|
|
1393
|
+
* ```ts
|
|
1394
|
+
* import { Effect, Schema } from "effect"
|
|
1395
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
1396
|
+
* import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
1397
|
+
*
|
|
1398
|
+
* class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
|
|
1399
|
+
* const States = Machine.defineStates({ Idle })
|
|
1400
|
+
* const machine = Machine.make({
|
|
1401
|
+
* states: States.states,
|
|
1402
|
+
* events: [],
|
|
1403
|
+
* initial: () => States.initial.Idle.from()
|
|
1404
|
+
* }).handle({ Idle: {} })
|
|
1405
|
+
*
|
|
1406
|
+
* const report = Effect.map(
|
|
1407
|
+
* MachineTest.run(machine, { events: [] }),
|
|
1408
|
+
* (trace) => MachineTest.coverage(machine, trace)
|
|
1409
|
+
* )
|
|
1410
|
+
* ```
|
|
1411
|
+
*
|
|
1412
|
+
* @category verification
|
|
1413
|
+
* @since 0.4.0
|
|
1414
|
+
*/
|
|
1415
|
+
export declare const coverage: <M extends AnyMachine>(machine: M, traceOrTraces: Trace<M> | ReadonlyArray<Trace<M>>) => Coverage<M>;
|
|
1416
|
+
/**
|
|
1417
|
+
* The observation roles summarized for one logical graph node.
|
|
1418
|
+
*
|
|
1419
|
+
* @category models
|
|
1420
|
+
* @since 0.4.0
|
|
1421
|
+
*/
|
|
1422
|
+
export interface ObservedGraphNodeObservations {
|
|
1423
|
+
readonly total: number;
|
|
1424
|
+
readonly startup: number;
|
|
1425
|
+
readonly event: number;
|
|
1426
|
+
readonly microstep: number;
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* One full encoded logical snapshot stored in the observed Effect graph.
|
|
1430
|
+
*
|
|
1431
|
+
* @category models
|
|
1432
|
+
* @since 0.4.0
|
|
1433
|
+
*/
|
|
1434
|
+
export interface ObservedGraphNode<M extends AnyMachine> {
|
|
1435
|
+
readonly id: string;
|
|
1436
|
+
readonly snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
1437
|
+
readonly encoded: Machine.Machine.EncodedSnapshot;
|
|
1438
|
+
readonly configuration: ReadonlyArray<StatePath<M>>;
|
|
1439
|
+
readonly observations: ObservedGraphNodeObservations;
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Retained evidence for one microstep inside an observed graph edge.
|
|
1443
|
+
*
|
|
1444
|
+
* @category models
|
|
1445
|
+
* @since 0.4.0
|
|
1446
|
+
*/
|
|
1447
|
+
export interface ObservedGraphMicrostep<M extends AnyMachine> {
|
|
1448
|
+
readonly next: string;
|
|
1449
|
+
readonly event: Machine.Machine.Event<M> | Machine.InitialEvent;
|
|
1450
|
+
readonly transitions: Microstep<M, any>["transitions"];
|
|
1451
|
+
readonly raisedEvents: ReadonlyArray<Machine.Machine.Event<M>>;
|
|
1452
|
+
readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>;
|
|
1453
|
+
readonly exitPaths: ReadonlyArray<StatePath<M>>;
|
|
1454
|
+
readonly entryPaths: ReadonlyArray<StatePath<M>>;
|
|
1455
|
+
readonly changed: boolean;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* A startup or public-event macrostep retained by the observed graph.
|
|
1459
|
+
*
|
|
1460
|
+
* @category models
|
|
1461
|
+
* @since 0.4.0
|
|
1462
|
+
*/
|
|
1463
|
+
export type ObservedGraphEdge<M extends AnyMachine> = {
|
|
1464
|
+
readonly _tag: "Startup";
|
|
1465
|
+
readonly traceIndex: number;
|
|
1466
|
+
readonly microsteps: ReadonlyArray<ObservedGraphMicrostep<M>>;
|
|
1467
|
+
readonly completion: PlanCompletion<M>;
|
|
1468
|
+
} | {
|
|
1469
|
+
readonly _tag: "Event";
|
|
1470
|
+
readonly traceIndex: number;
|
|
1471
|
+
readonly eventIndex: number;
|
|
1472
|
+
readonly event: Machine.Machine.InputEvent<M>;
|
|
1473
|
+
readonly microsteps: ReadonlyArray<ObservedGraphMicrostep<M>>;
|
|
1474
|
+
readonly completion: PlanCompletion<M>;
|
|
1475
|
+
};
|
|
1476
|
+
/**
|
|
1477
|
+
* An Effect directed graph plus stable indexes useful to graph algorithms.
|
|
1478
|
+
*
|
|
1479
|
+
* @category models
|
|
1480
|
+
* @since 0.4.0
|
|
1481
|
+
*/
|
|
1482
|
+
export interface ObservedGraph<M extends AnyMachine> {
|
|
1483
|
+
readonly graph: Graph.DirectedGraph<ObservedGraphNode<M>, ObservedGraphEdge<M>>;
|
|
1484
|
+
readonly nodesById: ReadonlyMap<string, Graph.NodeIndex>;
|
|
1485
|
+
/** Settled post-startup nodes from which public event paths begin. */
|
|
1486
|
+
readonly starts: ReadonlyArray<Graph.NodeIndex>;
|
|
1487
|
+
/** Pre-settled startup sources connected to `starts` by `Startup` edges. */
|
|
1488
|
+
readonly startupSources: ReadonlyArray<Graph.NodeIndex>;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Converts concrete planner traces into an observed logical-state graph.
|
|
1492
|
+
* Nodes are deduplicated by the public snapshot encoding and every edge is a
|
|
1493
|
+
* concrete startup or public-event macrostep. This intentionally does not
|
|
1494
|
+
* claim to be a static or exhaustive graph of the machine.
|
|
1495
|
+
*
|
|
1496
|
+
* @category verification
|
|
1497
|
+
* @since 0.4.0
|
|
1498
|
+
*/
|
|
1499
|
+
export declare const observedGraph: <M extends AnyMachine>(machine: M, traceOrTraces: Trace<M> | ReadonlyArray<Trace<M>>) => Effect.Effect<ObservedGraph<M>, Machine.MachineSchemaEncodeError, Machine.Machine.SnapshotEncodingServices<Machine.Machine.States<M>>>;
|
|
1500
|
+
/**
|
|
1501
|
+
* Independently checked families of planner laws.
|
|
1502
|
+
*
|
|
1503
|
+
* @category models
|
|
1504
|
+
* @since 0.4.0
|
|
1505
|
+
*/
|
|
1506
|
+
export type VerificationLawGroup = "configuration" | "microsteps" | "completion" | "history" | "targetBounds";
|
|
1507
|
+
/**
|
|
1508
|
+
* Stable identifiers for individual planner laws.
|
|
1509
|
+
*
|
|
1510
|
+
* @category models
|
|
1511
|
+
* @since 0.4.0
|
|
1512
|
+
*/
|
|
1513
|
+
export type VerificationLaw = "configuration.shape" | "configuration.path" | "configuration.schema" | "configuration.hierarchy" | "configuration.compound" | "configuration.parallel" | "configuration.duplicate" | "configuration.trace" | "microsteps.unique" | "microsteps.order" | "microsteps.activeBefore" | "microsteps.activeAfter" | "microsteps.changed" | "microsteps.continuity" | "microsteps.reentry" | "completion.record" | "completion.output" | "completion.done" | "history.record" | "history.mode" | "history.path" | "history.value" | "history.shallow" | "history.deep" | "targetBounds.definition" | "targetBounds.target";
|
|
1514
|
+
/**
|
|
1515
|
+
* One independently observed violation in a planner trace.
|
|
1516
|
+
*
|
|
1517
|
+
* @category models
|
|
1518
|
+
* @since 0.4.0
|
|
1519
|
+
*/
|
|
1520
|
+
export interface VerificationViolation {
|
|
1521
|
+
readonly law: VerificationLaw;
|
|
1522
|
+
/** `undefined` identifies startup; otherwise this is the scenario event index. */
|
|
1523
|
+
readonly eventIndex: number | undefined;
|
|
1524
|
+
readonly microstepIndex?: number;
|
|
1525
|
+
readonly path?: string;
|
|
1526
|
+
readonly message: string;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* All violations found while checking one trace.
|
|
1530
|
+
*
|
|
1531
|
+
* @category errors
|
|
1532
|
+
* @since 0.4.0
|
|
1533
|
+
*/
|
|
1534
|
+
export { VerificationError } from "../internal/testing/machine/verification.js";
|
|
1535
|
+
/**
|
|
1536
|
+
* Selects law families for the single canonical verifier. All run by default.
|
|
1537
|
+
*
|
|
1538
|
+
* @category models
|
|
1539
|
+
* @since 0.4.0
|
|
1540
|
+
*/
|
|
1541
|
+
export interface VerifyOptions {
|
|
1542
|
+
readonly laws?: ReadonlyArray<VerificationLawGroup>;
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Verifies an executed trace using only public machine inspection and raw
|
|
1546
|
+
* snapshot data. The verifier deliberately does not reuse planner
|
|
1547
|
+
* normalization, encoding, finality, or other internal helpers.
|
|
1548
|
+
*
|
|
1549
|
+
* Every selected law is evaluated and returned in one structured error so a
|
|
1550
|
+
* shrunk property-test counterexample retains all relevant evidence.
|
|
1551
|
+
*
|
|
1552
|
+
* **Example**
|
|
1553
|
+
*
|
|
1554
|
+
* ```ts
|
|
1555
|
+
* import { Effect, Schema } from "effect"
|
|
1556
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
1557
|
+
* import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
1558
|
+
*
|
|
1559
|
+
* class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
|
|
1560
|
+
* const States = Machine.defineStates({ Idle })
|
|
1561
|
+
* const machine = Machine.make({
|
|
1562
|
+
* states: States.states,
|
|
1563
|
+
* events: [],
|
|
1564
|
+
* initial: () => States.initial.Idle.from()
|
|
1565
|
+
* }).handle({ Idle: {} })
|
|
1566
|
+
*
|
|
1567
|
+
* const checked = Effect.gen(function*() {
|
|
1568
|
+
* const trace = yield* MachineTest.run(machine, { events: [] })
|
|
1569
|
+
* yield* MachineTest.verify(machine, trace)
|
|
1570
|
+
* })
|
|
1571
|
+
* ```
|
|
1572
|
+
*
|
|
1573
|
+
* @category verification
|
|
1574
|
+
* @since 0.4.0
|
|
1575
|
+
*/
|
|
1576
|
+
export declare const verify: <M extends AnyMachine>(machine: M, trace: Trace<M>, options?: VerifyOptions) => Effect.Effect<void, VerificationError>;
|
|
1577
|
+
/**
|
|
1578
|
+
* Formats a trace as deterministic, line-oriented counterexample evidence.
|
|
1579
|
+
*
|
|
1580
|
+
* Object keys and map/set entries are canonicalized. Deferred effects are
|
|
1581
|
+
* represented by counts so formatting never evaluates or inspects actions.
|
|
1582
|
+
* Failures include their original cause and every available successful prefix.
|
|
1583
|
+
*
|
|
1584
|
+
* @category formatting
|
|
1585
|
+
* @since 0.4.0
|
|
1586
|
+
*/
|
|
1587
|
+
export declare const formatTrace: <M extends AnyMachine, Cause>(trace: Trace<M> | RunFailure<Cause, M>) => string;
|
|
1588
|
+
//# sourceMappingURL=MachineTest.d.ts.map
|