@typeonce/effect-machine 0.4.0 → 0.5.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/README.md +166 -588
- package/dist/Machine.d.ts +426 -240
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +219 -43
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/activities.d.ts +1 -1
- package/dist/internal/machine/activities.js +1 -1
- package/dist/internal/machine/atom.d.ts +1 -1
- package/dist/internal/machine/atom.js +1 -1
- package/dist/internal/machine/cluster.d.ts +2 -2
- package/dist/internal/machine/cluster.js +1 -1
- package/dist/internal/machine/command.d.ts +1 -1
- package/dist/internal/machine/command.js +1 -1
- package/dist/internal/machine/commandRuntime.d.ts +1 -1
- package/dist/internal/machine/commandRuntime.js +1 -1
- package/dist/internal/machine/configuration.d.ts +1 -1
- package/dist/internal/machine/configuration.js +1 -1
- package/dist/internal/machine/errors.d.ts +7 -7
- package/dist/internal/machine/errors.js +7 -7
- package/dist/internal/machine/executionPlan.d.ts +2 -2
- package/dist/internal/machine/executionPlan.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.js +17 -8
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/invocation.d.ts +1 -1
- package/dist/internal/machine/invocation.js +1 -1
- package/dist/internal/machine/planner.d.ts +1 -1
- package/dist/internal/machine/planner.js +1 -1
- package/dist/internal/machine/process.d.ts +1 -1
- package/dist/internal/machine/process.d.ts.map +1 -1
- package/dist/internal/machine/process.js +76 -24
- package/dist/internal/machine/process.js.map +1 -1
- package/dist/internal/machine/protocol.d.ts +1 -1
- package/dist/internal/machine/protocol.js +1 -1
- package/dist/internal/machine/runtime.d.ts +32 -4
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +120 -12
- package/dist/internal/machine/runtime.js.map +1 -1
- package/dist/internal/machine/serialization.d.ts +1 -1
- package/dist/internal/machine/serialization.js +1 -1
- package/dist/internal/machine/topology.d.ts +1 -1
- package/dist/internal/machine/topology.js +1 -1
- package/dist/internal/testing/machine/arbitrary.d.ts +3 -3
- 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 +20 -20
- package/dist/internal/testing/machine/finiteModel.js +2 -2
- 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 +14 -14
- package/dist/internal/testing/machine/referenceModel.js +2 -2
- package/dist/internal/testing/machine/runtime.d.ts +329 -26
- package/dist/internal/testing/machine/runtime.d.ts.map +1 -1
- package/dist/internal/testing/machine/runtime.js +303 -14
- package/dist/internal/testing/machine/runtime.js.map +1 -1
- 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 +9 -6
- package/dist/internal/testing/machine/verification.d.ts.map +1 -1
- package/dist/internal/testing/machine/verification.js +8 -79
- package/dist/internal/testing/machine/verification.js.map +1 -1
- package/dist/testing/MachineTest.d.ts +1029 -47
- package/dist/testing/MachineTest.d.ts.map +1 -1
- package/dist/testing/MachineTest.js +315 -11
- package/dist/testing/MachineTest.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/unstable/cluster/ClusterMachine.d.ts +38 -19
- package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
- package/dist/unstable/cluster/ClusterMachine.js +27 -9
- package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +82 -31
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +63 -12
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +160 -18
- package/package.json +11 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Property-based scenario generation and planner trace utilities.
|
|
3
3
|
*
|
|
4
|
-
* @since 4.0
|
|
4
|
+
* @since 0.4.0
|
|
5
5
|
*/
|
|
6
6
|
import type * as Effect from "effect/Effect";
|
|
7
7
|
import type * as Graph from "effect/Graph";
|
|
@@ -11,9 +11,10 @@ import type { EnsureExecutable } from "../internal/machine/readiness.js";
|
|
|
11
11
|
import type { SchemaArbitraryReport } from "../internal/testing/machine/arbitrary.js";
|
|
12
12
|
import type { FiniteModel } from "../internal/testing/machine/finiteModel.js";
|
|
13
13
|
import type * as ReferenceModel from "../internal/testing/machine/referenceModel.js";
|
|
14
|
+
import * as internal from "../internal/testing/machine/verification.js";
|
|
14
15
|
import type { VerificationError } from "../internal/testing/machine/verification.js";
|
|
15
16
|
import type * as Machine from "../Machine.js";
|
|
16
|
-
export { advanceCommand, checkpointCommand, formatRuntimeTranscript, runRuntimeCommands, type RuntimeAssertionContext, 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 } from "../internal/testing/machine/verification.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";
|
|
17
18
|
export type { SchemaArbitraryOpaqueFilterWarning, SchemaArbitraryReport, SchemaArbitraryWarning } from "../internal/testing/machine/verification.js";
|
|
18
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";
|
|
19
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";
|
|
@@ -22,7 +23,7 @@ export { ModelVerificationError, type ModelVerificationField, type ModelVerifica
|
|
|
22
23
|
* machine.
|
|
23
24
|
*
|
|
24
25
|
* @category verification
|
|
25
|
-
* @since 4.0
|
|
26
|
+
* @since 0.4.0
|
|
26
27
|
*/
|
|
27
28
|
export declare const interpretModel: (model: FiniteModel, events: ReadonlyArray<string>) => ReferenceModel.ReferenceTrace;
|
|
28
29
|
type AnyMachine = Machine.Machine.Any;
|
|
@@ -38,7 +39,7 @@ type ReadyMachine<M extends AnyMachine> = M & EnsureExecutable<Machine.Machine.S
|
|
|
38
39
|
* exact decoded type. Events use only the public input protocol.
|
|
39
40
|
*
|
|
40
41
|
* @category models
|
|
41
|
-
* @since 4.0
|
|
42
|
+
* @since 0.4.0
|
|
42
43
|
*/
|
|
43
44
|
export type Scenario<M extends AnyMachine> = Machine.Machine.Input<M> extends typeof Schema.Void ? {
|
|
44
45
|
readonly events: ReadonlyArray<Machine.Machine.InputEvent<M>>;
|
|
@@ -53,7 +54,7 @@ export type Scenario<M extends AnyMachine> = Machine.Machine.Input<M> extends ty
|
|
|
53
54
|
* value. An events override therefore owns its own length distribution.
|
|
54
55
|
*
|
|
55
56
|
* @category models
|
|
56
|
-
* @since 4.0
|
|
57
|
+
* @since 0.4.0
|
|
57
58
|
*/
|
|
58
59
|
export type ScenarioOptions<M extends AnyMachine> = {
|
|
59
60
|
readonly minEvents?: number;
|
|
@@ -68,7 +69,7 @@ export type ScenarioOptions<M extends AnyMachine> = {
|
|
|
68
69
|
* Diagnostics for one schema-derived arbitrary.
|
|
69
70
|
*
|
|
70
71
|
* @category models
|
|
71
|
-
* @since 4.0
|
|
72
|
+
* @since 0.4.0
|
|
72
73
|
*/
|
|
73
74
|
export interface SchemaArbitraryDiagnostic {
|
|
74
75
|
readonly boundary: "input" | "event";
|
|
@@ -79,7 +80,7 @@ export interface SchemaArbitraryDiagnostic {
|
|
|
79
80
|
* Diagnostics describing how a scenario arbitrary was assembled.
|
|
80
81
|
*
|
|
81
82
|
* @category models
|
|
82
|
-
* @since 4.0
|
|
83
|
+
* @since 0.4.0
|
|
83
84
|
*/
|
|
84
85
|
export interface ScenarioDiagnostics {
|
|
85
86
|
readonly input: "none" | "schema" | "override";
|
|
@@ -90,7 +91,7 @@ export interface ScenarioDiagnostics {
|
|
|
90
91
|
* A scenario arbitrary together with schema-derivation diagnostics.
|
|
91
92
|
*
|
|
92
93
|
* @category models
|
|
93
|
-
* @since 4.0
|
|
94
|
+
* @since 0.4.0
|
|
94
95
|
*/
|
|
95
96
|
export interface Scenarios<M extends AnyMachine> {
|
|
96
97
|
readonly arbitrary: FastCheck.Arbitrary<Scenario<M>>;
|
|
@@ -102,15 +103,34 @@ export interface Scenarios<M extends AnyMachine> {
|
|
|
102
103
|
* Unsupported schema derivations fail immediately through `Schema.toArbitrary`.
|
|
103
104
|
* Non-fatal derivation warnings are returned instead of being hidden.
|
|
104
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
|
+
*
|
|
105
125
|
* @category constructors
|
|
106
|
-
* @since 4.0
|
|
126
|
+
* @since 0.4.0
|
|
107
127
|
*/
|
|
108
128
|
export declare const scenarios: <M extends AnyMachine>(machine: M, options?: ScenarioOptions<M>) => Scenarios<M>;
|
|
109
129
|
/**
|
|
110
130
|
* Completion information retained by an initial or event plan.
|
|
111
131
|
*
|
|
112
132
|
* @category models
|
|
113
|
-
* @since 4.0
|
|
133
|
+
* @since 0.4.0
|
|
114
134
|
*/
|
|
115
135
|
export type PlanCompletion<M extends AnyMachine> = {
|
|
116
136
|
readonly done: true;
|
|
@@ -123,7 +143,7 @@ export type PlanCompletion<M extends AnyMachine> = {
|
|
|
123
143
|
* One public planned microstep, including retained post-conflict transitions.
|
|
124
144
|
*
|
|
125
145
|
* @category models
|
|
126
|
-
* @since 4.0
|
|
146
|
+
* @since 0.4.0
|
|
127
147
|
*/
|
|
128
148
|
export interface Microstep<M extends AnyMachine, Requirements = Machine.Machine.Services<M>> {
|
|
129
149
|
readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
@@ -140,7 +160,7 @@ export interface Microstep<M extends AnyMachine, Requirements = Machine.Machine.
|
|
|
140
160
|
* The complete data returned while planning machine startup.
|
|
141
161
|
*
|
|
142
162
|
* @category models
|
|
143
|
-
* @since 4.0
|
|
163
|
+
* @since 0.4.0
|
|
144
164
|
*/
|
|
145
165
|
export type InitialPlan<M extends AnyMachine> = {
|
|
146
166
|
readonly startingState: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
@@ -154,7 +174,7 @@ export type InitialPlan<M extends AnyMachine> = {
|
|
|
154
174
|
* The complete data returned while planning one public event.
|
|
155
175
|
*
|
|
156
176
|
* @category models
|
|
157
|
-
* @since 4.0
|
|
177
|
+
* @since 0.4.0
|
|
158
178
|
*/
|
|
159
179
|
export type EventPlan<M extends AnyMachine> = {
|
|
160
180
|
readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
@@ -166,7 +186,7 @@ export type EventPlan<M extends AnyMachine> = {
|
|
|
166
186
|
* Startup portion of an executable planner trace.
|
|
167
187
|
*
|
|
168
188
|
* @category models
|
|
169
|
-
* @since 4.0
|
|
189
|
+
* @since 0.4.0
|
|
170
190
|
*/
|
|
171
191
|
export interface InitialTrace<M extends AnyMachine> {
|
|
172
192
|
readonly plan: InitialPlan<M>;
|
|
@@ -179,7 +199,7 @@ export interface InitialTrace<M extends AnyMachine> {
|
|
|
179
199
|
* One event portion of an executable planner trace.
|
|
180
200
|
*
|
|
181
201
|
* @category models
|
|
182
|
-
* @since 4.0
|
|
202
|
+
* @since 0.4.0
|
|
183
203
|
*/
|
|
184
204
|
export interface TraceStep<M extends AnyMachine> {
|
|
185
205
|
readonly index: number;
|
|
@@ -194,7 +214,7 @@ export interface TraceStep<M extends AnyMachine> {
|
|
|
194
214
|
* A scenario and every plan produced by executing it without running actions.
|
|
195
215
|
*
|
|
196
216
|
* @category models
|
|
197
|
-
* @since 4.0
|
|
217
|
+
* @since 0.4.0
|
|
198
218
|
*/
|
|
199
219
|
export interface Trace<M extends AnyMachine> {
|
|
200
220
|
readonly scenario: Scenario<M>;
|
|
@@ -203,6 +223,908 @@ export interface Trace<M extends AnyMachine> {
|
|
|
203
223
|
readonly final: Machine.Machine.Snapshot<Machine.Machine.States<M>>;
|
|
204
224
|
readonly finalConfiguration: ReadonlyArray<StatePath<M>>;
|
|
205
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>>;
|
|
206
1128
|
/**
|
|
207
1129
|
* Checks a real planner trace against the independent finite statechart model
|
|
208
1130
|
* interpreter.
|
|
@@ -212,7 +1134,7 @@ export interface Trace<M extends AnyMachine> {
|
|
|
212
1134
|
* and accumulates every disagreement in one structured error.
|
|
213
1135
|
*
|
|
214
1136
|
* @category verification
|
|
215
|
-
* @since 4.0
|
|
1137
|
+
* @since 0.4.0
|
|
216
1138
|
*/
|
|
217
1139
|
export declare const verifyModel: <M extends AnyMachine>(model: FiniteModel, actualTrace: Trace<M>) => Effect.Effect<void, ReferenceModel.ModelVerificationError>;
|
|
218
1140
|
/**
|
|
@@ -220,7 +1142,7 @@ export declare const verifyModel: <M extends AnyMachine>(model: FiniteModel, act
|
|
|
220
1142
|
* segment preceding it.
|
|
221
1143
|
*
|
|
222
1144
|
* @category models
|
|
223
|
-
* @since 4.0
|
|
1145
|
+
* @since 0.4.0
|
|
224
1146
|
*/
|
|
225
1147
|
export type RunFailure<Cause, M extends AnyMachine = AnyMachine> = {
|
|
226
1148
|
readonly _tag: "MachineTestRunFailure";
|
|
@@ -245,7 +1167,7 @@ export type RunFailure<Cause, M extends AnyMachine = AnyMachine> = {
|
|
|
245
1167
|
* Errors that can be produced while planning a complete scenario.
|
|
246
1168
|
*
|
|
247
1169
|
* @category errors
|
|
248
|
-
* @since 4.0
|
|
1170
|
+
* @since 0.4.0
|
|
249
1171
|
*/
|
|
250
1172
|
export type RunError<M extends AnyMachine> = Machine.Machine.InitialError<M> | Machine.Machine.Error<M> | Machine.InfiniteTransitionError | Machine.MachineSchemaDecodeError | Machine.StartupError;
|
|
251
1173
|
/**
|
|
@@ -256,7 +1178,7 @@ export type RunError<M extends AnyMachine> = Machine.Machine.InitialError<M> | M
|
|
|
256
1178
|
* later execution, not synchronous planning.
|
|
257
1179
|
*
|
|
258
1180
|
* @category models
|
|
259
|
-
* @since 4.0
|
|
1181
|
+
* @since 0.4.0
|
|
260
1182
|
*/
|
|
261
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;
|
|
262
1184
|
/**
|
|
@@ -267,15 +1189,33 @@ export type RunServices<M extends AnyMachine> = IsAny<Machine.PlanningServices<M
|
|
|
267
1189
|
* Typed planning errors retain the scenario and successfully completed prefix
|
|
268
1190
|
* in a `RunFailure`.
|
|
269
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
|
+
*
|
|
270
1210
|
* @category constructors
|
|
271
|
-
* @since 4.0
|
|
1211
|
+
* @since 0.4.0
|
|
272
1212
|
*/
|
|
273
1213
|
export declare const run: <M extends AnyMachine>(machine: ReadyMachine<M>, scenario: Scenario<M>) => Effect.Effect<Trace<M>, RunFailure<RunError<M>, M>, RunServices<M>>;
|
|
274
1214
|
/**
|
|
275
1215
|
* A deterministic hit/miss summary for a finite set declared by a machine.
|
|
276
1216
|
*
|
|
277
1217
|
* @category models
|
|
278
|
-
* @since 4.0
|
|
1218
|
+
* @since 0.4.0
|
|
279
1219
|
*/
|
|
280
1220
|
export interface CoverageSummary<Item> {
|
|
281
1221
|
readonly total: number;
|
|
@@ -288,7 +1228,7 @@ export interface CoverageSummary<Item> {
|
|
|
288
1228
|
* One active (non-history) state node in a state coverage summary.
|
|
289
1229
|
*
|
|
290
1230
|
* @category models
|
|
291
|
-
* @since 4.0
|
|
1231
|
+
* @since 0.4.0
|
|
292
1232
|
*/
|
|
293
1233
|
export interface StateCoverageItem<Path extends string = string> {
|
|
294
1234
|
readonly path: Path;
|
|
@@ -298,7 +1238,7 @@ export interface StateCoverageItem<Path extends string = string> {
|
|
|
298
1238
|
* State activation and lifecycle coverage.
|
|
299
1239
|
*
|
|
300
1240
|
* @category models
|
|
301
|
-
* @since 4.0
|
|
1241
|
+
* @since 0.4.0
|
|
302
1242
|
*/
|
|
303
1243
|
export interface StateCoverage<Path extends string = string> {
|
|
304
1244
|
readonly activation: CoverageSummary<StateCoverageItem<Path>>;
|
|
@@ -309,7 +1249,7 @@ export interface StateCoverage<Path extends string = string> {
|
|
|
309
1249
|
* One stable transition-definition identity in definition order.
|
|
310
1250
|
*
|
|
311
1251
|
* @category models
|
|
312
|
-
* @since 4.0
|
|
1252
|
+
* @since 0.4.0
|
|
313
1253
|
*/
|
|
314
1254
|
export interface TransitionCoverageItem<SourcePath extends string = string, EventTag extends PropertyKey = PropertyKey, TargetPath extends string = SourcePath> {
|
|
315
1255
|
readonly id: string;
|
|
@@ -323,7 +1263,7 @@ export interface TransitionCoverageItem<SourcePath extends string = string, Even
|
|
|
323
1263
|
* One public event tag declared by the machine.
|
|
324
1264
|
*
|
|
325
1265
|
* @category models
|
|
326
|
-
* @since 4.0
|
|
1266
|
+
* @since 0.4.0
|
|
327
1267
|
*/
|
|
328
1268
|
export interface EventCoverageItem<Tag extends PropertyKey = PropertyKey> {
|
|
329
1269
|
readonly tag: Tag;
|
|
@@ -333,7 +1273,7 @@ export interface EventCoverageItem<Tag extends PropertyKey = PropertyKey> {
|
|
|
333
1273
|
* Public event coverage, including events that no transition retained.
|
|
334
1274
|
*
|
|
335
1275
|
* @category models
|
|
336
|
-
* @since 4.0
|
|
1276
|
+
* @since 0.4.0
|
|
337
1277
|
*/
|
|
338
1278
|
export type EventCoverage<Tag extends PropertyKey = PropertyKey> = {
|
|
339
1279
|
readonly available: true;
|
|
@@ -361,7 +1301,7 @@ export type EventCoverage<Tag extends PropertyKey = PropertyKey> = {
|
|
|
361
1301
|
* Trace-derived scenario counts. There is no finite declared scenario space.
|
|
362
1302
|
*
|
|
363
1303
|
* @category models
|
|
364
|
-
* @since 4.0
|
|
1304
|
+
* @since 0.4.0
|
|
365
1305
|
*/
|
|
366
1306
|
export interface ScenarioCoverage {
|
|
367
1307
|
readonly traces: number;
|
|
@@ -372,7 +1312,7 @@ export interface ScenarioCoverage {
|
|
|
372
1312
|
* Trace-derived logical configuration counts. There is no claimed exhaustive total.
|
|
373
1313
|
*
|
|
374
1314
|
* @category models
|
|
375
|
-
* @since 4.0
|
|
1315
|
+
* @since 0.4.0
|
|
376
1316
|
*/
|
|
377
1317
|
export interface LogicalConfigurationCoverage {
|
|
378
1318
|
readonly observations: number;
|
|
@@ -383,7 +1323,7 @@ export interface LogicalConfigurationCoverage {
|
|
|
383
1323
|
* Directly observed startup and microstep evidence.
|
|
384
1324
|
*
|
|
385
1325
|
* @category models
|
|
386
|
-
* @since 4.0
|
|
1326
|
+
* @since 0.4.0
|
|
387
1327
|
*/
|
|
388
1328
|
export interface MicrostepCoverageEvidence {
|
|
389
1329
|
readonly total: number;
|
|
@@ -400,7 +1340,7 @@ export interface MicrostepCoverageEvidence {
|
|
|
400
1340
|
* Directly observed completion evidence.
|
|
401
1341
|
*
|
|
402
1342
|
* @category models
|
|
403
|
-
* @since 4.0
|
|
1343
|
+
* @since 0.4.0
|
|
404
1344
|
*/
|
|
405
1345
|
export interface CompletionCoverageEvidence<Path extends string = string> {
|
|
406
1346
|
readonly donePlans: number;
|
|
@@ -411,7 +1351,7 @@ export interface CompletionCoverageEvidence<Path extends string = string> {
|
|
|
411
1351
|
* Directly observed history records and history-target transitions.
|
|
412
1352
|
*
|
|
413
1353
|
* @category models
|
|
414
|
-
* @since 4.0
|
|
1354
|
+
* @since 0.4.0
|
|
415
1355
|
*/
|
|
416
1356
|
export interface HistoryCoverageEvidence<Path extends string = string> {
|
|
417
1357
|
readonly recordObservations: number;
|
|
@@ -426,7 +1366,7 @@ export interface HistoryCoverageEvidence<Path extends string = string> {
|
|
|
426
1366
|
* Coverage computed only from observable machine definitions and planner traces.
|
|
427
1367
|
*
|
|
428
1368
|
* @category models
|
|
429
|
-
* @since 4.0
|
|
1369
|
+
* @since 0.4.0
|
|
430
1370
|
*/
|
|
431
1371
|
export interface Coverage<M extends AnyMachine> {
|
|
432
1372
|
readonly states: StateCoverage<StatePath<M>>;
|
|
@@ -448,15 +1388,36 @@ export interface Coverage<M extends AnyMachine> {
|
|
|
448
1388
|
* configurations report observations only because their complete spaces are
|
|
449
1389
|
* generally infinite.
|
|
450
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
|
+
*
|
|
451
1412
|
* @category verification
|
|
452
|
-
* @since 4.0
|
|
1413
|
+
* @since 0.4.0
|
|
453
1414
|
*/
|
|
454
1415
|
export declare const coverage: <M extends AnyMachine>(machine: M, traceOrTraces: Trace<M> | ReadonlyArray<Trace<M>>) => Coverage<M>;
|
|
455
1416
|
/**
|
|
456
1417
|
* The observation roles summarized for one logical graph node.
|
|
457
1418
|
*
|
|
458
1419
|
* @category models
|
|
459
|
-
* @since 4.0
|
|
1420
|
+
* @since 0.4.0
|
|
460
1421
|
*/
|
|
461
1422
|
export interface ObservedGraphNodeObservations {
|
|
462
1423
|
readonly total: number;
|
|
@@ -468,7 +1429,7 @@ export interface ObservedGraphNodeObservations {
|
|
|
468
1429
|
* One full encoded logical snapshot stored in the observed Effect graph.
|
|
469
1430
|
*
|
|
470
1431
|
* @category models
|
|
471
|
-
* @since 4.0
|
|
1432
|
+
* @since 0.4.0
|
|
472
1433
|
*/
|
|
473
1434
|
export interface ObservedGraphNode<M extends AnyMachine> {
|
|
474
1435
|
readonly id: string;
|
|
@@ -481,7 +1442,7 @@ export interface ObservedGraphNode<M extends AnyMachine> {
|
|
|
481
1442
|
* Retained evidence for one microstep inside an observed graph edge.
|
|
482
1443
|
*
|
|
483
1444
|
* @category models
|
|
484
|
-
* @since 4.0
|
|
1445
|
+
* @since 0.4.0
|
|
485
1446
|
*/
|
|
486
1447
|
export interface ObservedGraphMicrostep<M extends AnyMachine> {
|
|
487
1448
|
readonly next: string;
|
|
@@ -497,7 +1458,7 @@ export interface ObservedGraphMicrostep<M extends AnyMachine> {
|
|
|
497
1458
|
* A startup or public-event macrostep retained by the observed graph.
|
|
498
1459
|
*
|
|
499
1460
|
* @category models
|
|
500
|
-
* @since 4.0
|
|
1461
|
+
* @since 0.4.0
|
|
501
1462
|
*/
|
|
502
1463
|
export type ObservedGraphEdge<M extends AnyMachine> = {
|
|
503
1464
|
readonly _tag: "Startup";
|
|
@@ -516,7 +1477,7 @@ export type ObservedGraphEdge<M extends AnyMachine> = {
|
|
|
516
1477
|
* An Effect directed graph plus stable indexes useful to graph algorithms.
|
|
517
1478
|
*
|
|
518
1479
|
* @category models
|
|
519
|
-
* @since 4.0
|
|
1480
|
+
* @since 0.4.0
|
|
520
1481
|
*/
|
|
521
1482
|
export interface ObservedGraph<M extends AnyMachine> {
|
|
522
1483
|
readonly graph: Graph.DirectedGraph<ObservedGraphNode<M>, ObservedGraphEdge<M>>;
|
|
@@ -533,28 +1494,28 @@ export interface ObservedGraph<M extends AnyMachine> {
|
|
|
533
1494
|
* claim to be a static or exhaustive graph of the machine.
|
|
534
1495
|
*
|
|
535
1496
|
* @category verification
|
|
536
|
-
* @since 4.0
|
|
1497
|
+
* @since 0.4.0
|
|
537
1498
|
*/
|
|
538
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>>>;
|
|
539
1500
|
/**
|
|
540
1501
|
* Independently checked families of planner laws.
|
|
541
1502
|
*
|
|
542
1503
|
* @category models
|
|
543
|
-
* @since 4.0
|
|
1504
|
+
* @since 0.4.0
|
|
544
1505
|
*/
|
|
545
1506
|
export type VerificationLawGroup = "configuration" | "microsteps" | "completion" | "history" | "targetBounds";
|
|
546
1507
|
/**
|
|
547
1508
|
* Stable identifiers for individual planner laws.
|
|
548
1509
|
*
|
|
549
1510
|
* @category models
|
|
550
|
-
* @since 4.0
|
|
1511
|
+
* @since 0.4.0
|
|
551
1512
|
*/
|
|
552
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";
|
|
553
1514
|
/**
|
|
554
1515
|
* One independently observed violation in a planner trace.
|
|
555
1516
|
*
|
|
556
1517
|
* @category models
|
|
557
|
-
* @since 4.0
|
|
1518
|
+
* @since 0.4.0
|
|
558
1519
|
*/
|
|
559
1520
|
export interface VerificationViolation {
|
|
560
1521
|
readonly law: VerificationLaw;
|
|
@@ -568,14 +1529,14 @@ export interface VerificationViolation {
|
|
|
568
1529
|
* All violations found while checking one trace.
|
|
569
1530
|
*
|
|
570
1531
|
* @category errors
|
|
571
|
-
* @since 4.0
|
|
1532
|
+
* @since 0.4.0
|
|
572
1533
|
*/
|
|
573
1534
|
export { VerificationError } from "../internal/testing/machine/verification.js";
|
|
574
1535
|
/**
|
|
575
1536
|
* Selects law families for the single canonical verifier. All run by default.
|
|
576
1537
|
*
|
|
577
1538
|
* @category models
|
|
578
|
-
* @since 4.0
|
|
1539
|
+
* @since 0.4.0
|
|
579
1540
|
*/
|
|
580
1541
|
export interface VerifyOptions {
|
|
581
1542
|
readonly laws?: ReadonlyArray<VerificationLawGroup>;
|
|
@@ -588,8 +1549,29 @@ export interface VerifyOptions {
|
|
|
588
1549
|
* Every selected law is evaluated and returned in one structured error so a
|
|
589
1550
|
* shrunk property-test counterexample retains all relevant evidence.
|
|
590
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
|
+
*
|
|
591
1573
|
* @category verification
|
|
592
|
-
* @since 4.0
|
|
1574
|
+
* @since 0.4.0
|
|
593
1575
|
*/
|
|
594
1576
|
export declare const verify: <M extends AnyMachine>(machine: M, trace: Trace<M>, options?: VerifyOptions) => Effect.Effect<void, VerificationError>;
|
|
595
1577
|
/**
|
|
@@ -600,7 +1582,7 @@ export declare const verify: <M extends AnyMachine>(machine: M, trace: Trace<M>,
|
|
|
600
1582
|
* Failures include their original cause and every available successful prefix.
|
|
601
1583
|
*
|
|
602
1584
|
* @category formatting
|
|
603
|
-
* @since 4.0
|
|
1585
|
+
* @since 0.4.0
|
|
604
1586
|
*/
|
|
605
1587
|
export declare const formatTrace: <M extends AnyMachine, Cause>(trace: Trace<M> | RunFailure<Cause, M>) => string;
|
|
606
1588
|
//# sourceMappingURL=MachineTest.d.ts.map
|