@typeonce/effect-machine 0.6.0 → 0.6.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/package.json +5 -5
- package/src/Machine.ts +6873 -0
- package/src/index.ts +1 -0
- package/src/internal/machine/activities.ts +108 -0
- package/src/internal/machine/atom.ts +636 -0
- package/src/internal/machine/cluster.ts +394 -0
- package/src/internal/machine/command.ts +58 -0
- package/src/internal/machine/commandRuntime.ts +43 -0
- package/src/internal/machine/configuration.ts +1331 -0
- package/src/internal/machine/errors.ts +87 -0
- package/src/internal/machine/executionPlan.ts +996 -0
- package/src/internal/machine/invocation.ts +119 -0
- package/src/internal/machine/machine.ts +1747 -0
- package/src/internal/machine/planner.ts +1933 -0
- package/src/internal/machine/process.ts +906 -0
- package/src/internal/machine/protocol.ts +322 -0
- package/src/internal/machine/readiness.ts +10 -0
- package/src/internal/machine/runtime.ts +2512 -0
- package/src/internal/machine/serialization.ts +498 -0
- package/src/internal/machine/stateDefinition.ts +270 -0
- package/src/internal/machine/symbols.ts +2 -0
- package/src/internal/machine/topology.ts +479 -0
- package/src/internal/testing/machine/arbitrary.ts +102 -0
- package/src/internal/testing/machine/exploration.ts +331 -0
- package/src/internal/testing/machine/finiteModel.ts +1498 -0
- package/src/internal/testing/machine/invariant.ts +372 -0
- package/src/internal/testing/machine/probe.ts +79 -0
- package/src/internal/testing/machine/referenceModel.ts +1505 -0
- package/src/internal/testing/machine/runtime.ts +1710 -0
- package/src/internal/testing/machine/runtimeInvariant.ts +486 -0
- package/src/internal/testing/machine/trace.ts +150 -0
- package/src/internal/testing/machine/verification.ts +1890 -0
- package/src/testing/MachineTest.ts +2067 -0
- package/src/testing/index.ts +7 -0
- package/src/unstable/cluster/ClusterMachine.ts +390 -0
- package/src/unstable/cluster/index.ts +1 -0
- package/src/unstable/reactivity/AtomMachine.ts +649 -0
- package/src/unstable/reactivity/index.ts +1 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs Effect machines as persisted Cluster entities.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.4.0
|
|
5
|
+
*/
|
|
6
|
+
import type * as Effect from "effect/Effect"
|
|
7
|
+
import type * as Layer from "effect/Layer"
|
|
8
|
+
import type * as Option from "effect/Option"
|
|
9
|
+
import * as Schema from "effect/Schema"
|
|
10
|
+
import type { Entity, MessageStorage, Sharding, Snowflake } from "effect/unstable/cluster"
|
|
11
|
+
import type { Rpc } from "effect/unstable/rpc"
|
|
12
|
+
import * as internal from "../../internal/machine/cluster.js"
|
|
13
|
+
import { Accepted, Rejected, Storage } from "../../internal/machine/cluster.js"
|
|
14
|
+
import type { EnsureExecutable } from "../../internal/machine/readiness.js"
|
|
15
|
+
import type * as Machine from "../../Machine.js"
|
|
16
|
+
|
|
17
|
+
type Snowflake = Snowflake.Snowflake
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Persisted machine checkpoint owned by the Cluster bridge.
|
|
21
|
+
*
|
|
22
|
+
* **Details**
|
|
23
|
+
*
|
|
24
|
+
* Machine identity and deployment version are stored around the generic
|
|
25
|
+
* encoded snapshot. The request id records the request that produced the
|
|
26
|
+
* checkpoint.
|
|
27
|
+
*
|
|
28
|
+
* @category models
|
|
29
|
+
* @since 0.4.0
|
|
30
|
+
*/
|
|
31
|
+
export interface Checkpoint {
|
|
32
|
+
/** Stable identity of the machine definition that produced the snapshot. */
|
|
33
|
+
readonly machineId: string
|
|
34
|
+
|
|
35
|
+
/** Application-controlled deployment or migration version. */
|
|
36
|
+
readonly version: string
|
|
37
|
+
|
|
38
|
+
/** Cluster request whose accepted transition produced this checkpoint. */
|
|
39
|
+
readonly requestId: Snowflake
|
|
40
|
+
|
|
41
|
+
/** Encoded logical machine state and completed outputs. */
|
|
42
|
+
readonly snapshot: Machine.Machine.EncodedSnapshot
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Result of loading a checkpoint for a Cluster machine request.
|
|
47
|
+
*
|
|
48
|
+
* **Details**
|
|
49
|
+
*
|
|
50
|
+
* `processed` reports whether the exact Cluster request id was already
|
|
51
|
+
* committed. Storage implementations must retain enough request ids to detect
|
|
52
|
+
* redelivery even after later requests have advanced the checkpoint.
|
|
53
|
+
*
|
|
54
|
+
* @category models
|
|
55
|
+
* @since 0.4.0
|
|
56
|
+
*/
|
|
57
|
+
export interface LoadResult {
|
|
58
|
+
/** Latest checkpoint for the entity, when one has been committed. */
|
|
59
|
+
readonly checkpoint: Option.Option<Checkpoint>
|
|
60
|
+
|
|
61
|
+
/** Whether the requested id has already been committed. */
|
|
62
|
+
readonly processed: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Result of atomically committing a Cluster machine request.
|
|
67
|
+
*
|
|
68
|
+
* @category models
|
|
69
|
+
* @since 0.4.0
|
|
70
|
+
*/
|
|
71
|
+
export type CommitResult = CommitResult.Committed | CommitResult.Duplicate
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Constructors and types for Cluster machine commit results.
|
|
75
|
+
*
|
|
76
|
+
* @category models
|
|
77
|
+
* @since 0.4.0
|
|
78
|
+
*/
|
|
79
|
+
export const CommitResult = {
|
|
80
|
+
Committed: (): CommitResult.Committed => ({ _tag: "Committed" }),
|
|
81
|
+
Duplicate: (): CommitResult.Duplicate => ({ _tag: "Duplicate" })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Types for Cluster machine commit results.
|
|
86
|
+
*
|
|
87
|
+
* @category models
|
|
88
|
+
* @since 0.4.0
|
|
89
|
+
*/
|
|
90
|
+
export declare namespace CommitResult {
|
|
91
|
+
/**
|
|
92
|
+
* Indicates that the request id and checkpoint were committed atomically.
|
|
93
|
+
*
|
|
94
|
+
* @category models
|
|
95
|
+
* @since 0.4.0
|
|
96
|
+
*/
|
|
97
|
+
export interface Committed {
|
|
98
|
+
readonly _tag: "Committed"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Indicates that the request id was already committed.
|
|
103
|
+
*
|
|
104
|
+
* @category models
|
|
105
|
+
* @since 0.4.0
|
|
106
|
+
*/
|
|
107
|
+
export interface Duplicate {
|
|
108
|
+
readonly _tag: "Duplicate"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Checkpoint persistence service used by Cluster machines.
|
|
114
|
+
*
|
|
115
|
+
* **When to use**
|
|
116
|
+
*
|
|
117
|
+
* Use to connect `ClusterMachine` to a durable checkpoint store that can
|
|
118
|
+
* atomically deduplicate request ids and replace the current checkpoint.
|
|
119
|
+
*
|
|
120
|
+
* **Gotchas**
|
|
121
|
+
*
|
|
122
|
+
* For checkpoint and emitted-message persistence to commit atomically, this
|
|
123
|
+
* service must join the transaction opened through the configured
|
|
124
|
+
* `MessageStorage`. The reply is persisted after that transaction; storing the
|
|
125
|
+
* request id with the checkpoint makes a redelivery recover that reply without
|
|
126
|
+
* applying the transition again. A separate transaction or database cannot
|
|
127
|
+
* provide checkpoint-and-emission atomicity.
|
|
128
|
+
*
|
|
129
|
+
* @category services
|
|
130
|
+
* @since 0.4.0
|
|
131
|
+
*/
|
|
132
|
+
export { Storage }
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Successful result returned after a Cluster machine request is committed or
|
|
136
|
+
* recognized as a redelivery.
|
|
137
|
+
*
|
|
138
|
+
* @category models
|
|
139
|
+
* @since 0.4.0
|
|
140
|
+
*/
|
|
141
|
+
export { Accepted }
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Schema for reasons a Cluster machine request can be rejected without
|
|
145
|
+
* advancing its checkpoint.
|
|
146
|
+
*
|
|
147
|
+
* @category models
|
|
148
|
+
* @since 0.4.0
|
|
149
|
+
*/
|
|
150
|
+
export const RejectionReason = Schema.Literals([
|
|
151
|
+
"MachineIdMismatch",
|
|
152
|
+
"VersionMismatch",
|
|
153
|
+
"InvalidCheckpoint",
|
|
154
|
+
"UnsupportedProcessLocal",
|
|
155
|
+
"TransitionFailure",
|
|
156
|
+
"PersistenceFailure",
|
|
157
|
+
"EmissionFailure"
|
|
158
|
+
])
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Type of {@link RejectionReason}.
|
|
162
|
+
*
|
|
163
|
+
* @category models
|
|
164
|
+
* @since 0.4.0
|
|
165
|
+
*/
|
|
166
|
+
export type RejectionReason = typeof RejectionReason.Type
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Rejected Cluster machine request. Transaction-participating durable storage
|
|
170
|
+
* leaves the previous checkpoint in place and suppresses emitted events.
|
|
171
|
+
*
|
|
172
|
+
* @category models
|
|
173
|
+
* @since 0.4.0
|
|
174
|
+
*/
|
|
175
|
+
export { Rejected }
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Schema for Cluster machine request outcomes.
|
|
179
|
+
*
|
|
180
|
+
* @category schemas
|
|
181
|
+
* @since 0.4.0
|
|
182
|
+
*/
|
|
183
|
+
export const SendResult = Schema.Union([Accepted, Rejected])
|
|
184
|
+
|
|
185
|
+
type SendRpc<Events extends ReadonlyArray<Machine.Machine.TaggedSchema>> = Rpc.Rpc<
|
|
186
|
+
"send",
|
|
187
|
+
Schema.Union<Events>,
|
|
188
|
+
typeof SendResult
|
|
189
|
+
>
|
|
190
|
+
|
|
191
|
+
type MachineEvents<M extends Machine.Machine.Any> = Machine.Machine.InputEvents<M>
|
|
192
|
+
|
|
193
|
+
type MachineEmits<M extends Machine.Machine.Any> = Machine.Machine.Emits<M>
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Cluster adapter for one machine definition and entity type.
|
|
197
|
+
*
|
|
198
|
+
* **Details**
|
|
199
|
+
*
|
|
200
|
+
* The adapter exposes one persisted `send` RPC. Entity requests are serialized
|
|
201
|
+
* by the normal Cluster entity concurrency and every accepted request advances
|
|
202
|
+
* the checkpoint at most once.
|
|
203
|
+
*
|
|
204
|
+
* @category models
|
|
205
|
+
* @since 0.4.0
|
|
206
|
+
*/
|
|
207
|
+
export interface ClusterMachine<
|
|
208
|
+
in out Type extends string,
|
|
209
|
+
in out M extends Machine.Machine.Any,
|
|
210
|
+
out Services = MachineServices<M>
|
|
211
|
+
> {
|
|
212
|
+
/** Machine definition executed by each entity instance. */
|
|
213
|
+
readonly machine: M
|
|
214
|
+
|
|
215
|
+
/** Cluster entity exposing the persisted, schema-validated `send` RPC. */
|
|
216
|
+
readonly entity: Entity.Entity<Type, SendRpc<MachineEvents<M>>>
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Creates the Cluster entity layer for this machine.
|
|
220
|
+
*
|
|
221
|
+
* **Gotchas**
|
|
222
|
+
*
|
|
223
|
+
* `enqueue` runs after the checkpoint write in the same `MessageStorage`
|
|
224
|
+
* transaction. It must durably enqueue emitted events in that transaction;
|
|
225
|
+
* arbitrary external effects are not atomic with the checkpoint. The reply
|
|
226
|
+
* is persisted after commit and recovered through request-id deduplication if
|
|
227
|
+
* delivery is interrupted. Machines that never emit may omit `enqueue`.
|
|
228
|
+
*
|
|
229
|
+
* @since 0.4.0
|
|
230
|
+
*/
|
|
231
|
+
readonly toLayer: <R = never>(options?: {
|
|
232
|
+
readonly enqueue?: (
|
|
233
|
+
event: Machine.Machine.EmitOf<MachineEmits<M>>
|
|
234
|
+
) => Effect.Effect<void, unknown, R>
|
|
235
|
+
}) => Layer.Layer<never, never, Storage | MessageStorage.MessageStorage | Sharding.Sharding | R | Services>
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
type MachineServices<M extends Machine.Machine.Any> =
|
|
239
|
+
| ExcludeCompatibleRuntime<
|
|
240
|
+
Machine.ExecutionServices<Machine.Machine.Services<M> | Machine.Machine.InitialServices<M>>,
|
|
241
|
+
Machine.Machine.Event<M>,
|
|
242
|
+
Machine.Machine.Emit<M>
|
|
243
|
+
>
|
|
244
|
+
| Machine.Machine.SnapshotDecodingServices<Machine.Machine.States<M>>
|
|
245
|
+
| Machine.Machine.SnapshotEncodingServices<Machine.Machine.States<M>>
|
|
246
|
+
|
|
247
|
+
type IsAny<A> = 0 extends (1 & A) ? true : false
|
|
248
|
+
|
|
249
|
+
type ExcludeCompatibleRuntime<Requirements, Events, Emits> = Requirements extends Machine.Runtime.Requirement<
|
|
250
|
+
infer RequiredEvents,
|
|
251
|
+
infer RequiredEmits
|
|
252
|
+
> ? IsAny<Requirements> extends true ? Requirements
|
|
253
|
+
: [RequiredEvents] extends [Events] ? [RequiredEmits] extends [Emits] ? never : Requirements
|
|
254
|
+
: Requirements
|
|
255
|
+
: Requirements
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Creates an in-memory Cluster machine checkpoint store.
|
|
259
|
+
*
|
|
260
|
+
* **When to use**
|
|
261
|
+
*
|
|
262
|
+
* Use when you test or run a local process that does not require checkpoints
|
|
263
|
+
* to survive a restart.
|
|
264
|
+
*
|
|
265
|
+
* **Gotchas**
|
|
266
|
+
*
|
|
267
|
+
* This store is not durable and does not provide rollback with the in-memory
|
|
268
|
+
* `MessageStorage` transaction marker.
|
|
269
|
+
*
|
|
270
|
+
* @category constructors
|
|
271
|
+
* @since 0.4.0
|
|
272
|
+
*/
|
|
273
|
+
export const makeMemory: Effect.Effect<Storage["Service"]> = internal.makeMemory
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Layer providing the in-memory Cluster machine checkpoint store.
|
|
277
|
+
*
|
|
278
|
+
* @category layers
|
|
279
|
+
* @since 0.4.0
|
|
280
|
+
*/
|
|
281
|
+
export const layerMemory: Layer.Layer<Storage> = internal.layerMemory
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Creates a persisted Cluster entity adapter for a machine.
|
|
285
|
+
*
|
|
286
|
+
* **When to use**
|
|
287
|
+
*
|
|
288
|
+
* Use when each Cluster entity id should own one durable machine snapshot and
|
|
289
|
+
* accept schema-validated machine events through a persisted `send` RPC.
|
|
290
|
+
*
|
|
291
|
+
* **Details**
|
|
292
|
+
*
|
|
293
|
+
* A missing checkpoint runs initial planning before the first event. Existing
|
|
294
|
+
* checkpoints are identity-checked, version-checked, decoded, and resumed
|
|
295
|
+
* without rerunning initial entry behavior. Final checkpoints accept later
|
|
296
|
+
* requests as no-ops. The stable bridge identity is `machine.id` when present,
|
|
297
|
+
* otherwise the Cluster entity type.
|
|
298
|
+
*
|
|
299
|
+
* **Gotchas**
|
|
300
|
+
*
|
|
301
|
+
* Invoked processes, spawned children, action-time `runtime.raise`, timers,
|
|
302
|
+
* subscriptions, and other process-local state are not durable and are
|
|
303
|
+
* rejected. Planning-time raised events remain part of the current macrostep.
|
|
304
|
+
* Arbitrary action effects may run again after a crash before checkpoint
|
|
305
|
+
* commit, so the bridge does not provide exactly-once external effects.
|
|
306
|
+
*
|
|
307
|
+
* **Example**
|
|
308
|
+
*
|
|
309
|
+
* ```ts
|
|
310
|
+
* import { Schema } from "effect"
|
|
311
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
312
|
+
* import { ClusterMachine } from "@typeonce/effect-machine/cluster"
|
|
313
|
+
*
|
|
314
|
+
* class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
|
|
315
|
+
* const States = Machine.defineStates({ Idle })
|
|
316
|
+
* const machine = Machine.make({
|
|
317
|
+
* states: States.states,
|
|
318
|
+
* events: [],
|
|
319
|
+
* initial: () => States.initial.Idle.from()
|
|
320
|
+
* }).handle({ Idle: {} })
|
|
321
|
+
*
|
|
322
|
+
* const adapter = ClusterMachine.make("IdleMachine", machine, { version: "1" })
|
|
323
|
+
* ```
|
|
324
|
+
*
|
|
325
|
+
* @category constructors
|
|
326
|
+
* @since 0.4.0
|
|
327
|
+
*/
|
|
328
|
+
export const make: <
|
|
329
|
+
const Type extends string,
|
|
330
|
+
States extends Machine.Machine.StateSchemas,
|
|
331
|
+
Events extends ReadonlyArray<Machine.Machine.TaggedSchema>,
|
|
332
|
+
Input extends Schema.Top,
|
|
333
|
+
UnhandledStates extends Machine.Machine.StateIdentifier<States>,
|
|
334
|
+
E,
|
|
335
|
+
R,
|
|
336
|
+
InitialE,
|
|
337
|
+
InitialR,
|
|
338
|
+
FinalStates extends Machine.Machine.StateIdentifier<States>,
|
|
339
|
+
Output,
|
|
340
|
+
Emits extends ReadonlyArray<Machine.Machine.TaggedSchema>,
|
|
341
|
+
OutputStates extends Machine.Machine.StateIdentifier<States>,
|
|
342
|
+
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events
|
|
343
|
+
>(
|
|
344
|
+
type: Type,
|
|
345
|
+
machine:
|
|
346
|
+
& Machine.Machine<
|
|
347
|
+
States,
|
|
348
|
+
Events,
|
|
349
|
+
Input,
|
|
350
|
+
UnhandledStates,
|
|
351
|
+
E,
|
|
352
|
+
R,
|
|
353
|
+
InitialE,
|
|
354
|
+
InitialR,
|
|
355
|
+
FinalStates,
|
|
356
|
+
Output,
|
|
357
|
+
Emits,
|
|
358
|
+
OutputStates,
|
|
359
|
+
InputEvents
|
|
360
|
+
>
|
|
361
|
+
& EnsureExecutable<States, UnhandledStates, OutputStates>,
|
|
362
|
+
options: {
|
|
363
|
+
readonly version: string
|
|
364
|
+
},
|
|
365
|
+
...input: [...Machine.Machine.InputArgs<Input>]
|
|
366
|
+
) => ClusterMachine<
|
|
367
|
+
Type,
|
|
368
|
+
Machine.Machine<
|
|
369
|
+
States,
|
|
370
|
+
Events,
|
|
371
|
+
Input,
|
|
372
|
+
UnhandledStates,
|
|
373
|
+
E,
|
|
374
|
+
R,
|
|
375
|
+
InitialE,
|
|
376
|
+
InitialR,
|
|
377
|
+
FinalStates,
|
|
378
|
+
Output,
|
|
379
|
+
Emits,
|
|
380
|
+
OutputStates,
|
|
381
|
+
InputEvents
|
|
382
|
+
>,
|
|
383
|
+
| ExcludeCompatibleRuntime<
|
|
384
|
+
Machine.ExecutionServices<R | InitialR>,
|
|
385
|
+
Machine.Machine.EventOf<Events>,
|
|
386
|
+
Machine.Machine.EmitOf<Emits>
|
|
387
|
+
>
|
|
388
|
+
| Machine.Machine.SnapshotDecodingServices<States>
|
|
389
|
+
| Machine.Machine.SnapshotEncodingServices<States>
|
|
390
|
+
> = internal.make
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as ClusterMachine from "./ClusterMachine.js"
|