effect-machine 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_rolldown/runtime.js +18 -0
- package/dist/actor.d.ts +251 -0
- package/dist/actor.js +385 -0
- package/dist/cluster/entity-machine.d.ts +90 -0
- package/dist/cluster/entity-machine.js +74 -0
- package/dist/cluster/index.d.ts +3 -0
- package/dist/cluster/index.js +4 -0
- package/dist/cluster/to-entity.d.ts +64 -0
- package/dist/cluster/to-entity.js +53 -0
- package/dist/errors.d.ts +61 -0
- package/dist/errors.js +38 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/inspection.d.ts +125 -0
- package/dist/inspection.js +50 -0
- package/dist/internal/brands.d.ts +40 -0
- package/dist/internal/brands.js +0 -0
- package/dist/internal/inspection.d.ts +11 -0
- package/dist/internal/inspection.js +15 -0
- package/dist/internal/transition.d.ts +159 -0
- package/dist/internal/transition.js +235 -0
- package/dist/internal/utils.d.ts +52 -0
- package/dist/internal/utils.js +31 -0
- package/dist/machine.d.ts +271 -0
- package/dist/machine.js +317 -0
- package/{src/persistence/adapter.ts → dist/persistence/adapter.d.ts} +40 -72
- package/dist/persistence/adapter.js +27 -0
- package/dist/persistence/adapters/in-memory.d.ts +32 -0
- package/dist/persistence/adapters/in-memory.js +176 -0
- package/dist/persistence/index.d.ts +5 -0
- package/dist/persistence/index.js +6 -0
- package/dist/persistence/persistent-actor.d.ts +50 -0
- package/dist/persistence/persistent-actor.js +348 -0
- package/{src/persistence/persistent-machine.ts → dist/persistence/persistent-machine.d.ts} +28 -54
- package/dist/persistence/persistent-machine.js +24 -0
- package/dist/schema.d.ts +141 -0
- package/dist/schema.js +165 -0
- package/dist/slot.d.ts +128 -0
- package/dist/slot.js +99 -0
- package/dist/testing.d.ts +142 -0
- package/dist/testing.js +131 -0
- package/package.json +18 -7
- package/src/actor.ts +0 -1058
- package/src/cluster/entity-machine.ts +0 -201
- package/src/cluster/index.ts +0 -43
- package/src/cluster/to-entity.ts +0 -99
- package/src/errors.ts +0 -64
- package/src/index.ts +0 -105
- package/src/inspection.ts +0 -178
- package/src/internal/brands.ts +0 -51
- package/src/internal/inspection.ts +0 -18
- package/src/internal/transition.ts +0 -489
- package/src/internal/utils.ts +0 -80
- package/src/machine.ts +0 -836
- package/src/persistence/adapters/in-memory.ts +0 -294
- package/src/persistence/index.ts +0 -24
- package/src/persistence/persistent-actor.ts +0 -791
- package/src/schema.ts +0 -362
- package/src/slot.ts +0 -281
- package/src/testing.ts +0 -284
- package/tsconfig.json +0 -65
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EntityMachine adapter - wires a machine to a cluster Entity layer.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
import { Entity } from "@effect/cluster";
|
|
7
|
-
import type { Rpc } from "@effect/rpc";
|
|
8
|
-
import { Effect, type Layer, Queue, Ref, Scope } from "effect";
|
|
9
|
-
|
|
10
|
-
import type { Machine, MachineRef } from "../machine.js";
|
|
11
|
-
import { runSpawnEffects, processEventCore } from "../actor.js";
|
|
12
|
-
import type { ProcessEventHooks } from "../actor.js";
|
|
13
|
-
import type { GuardsDef, EffectsDef } from "../slot.js";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Options for EntityMachine.layer
|
|
17
|
-
*/
|
|
18
|
-
export interface EntityMachineOptions<S, E> {
|
|
19
|
-
/**
|
|
20
|
-
* Initialize state from entity ID.
|
|
21
|
-
* Called once when entity is first activated.
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```ts
|
|
25
|
-
* EntityMachine.layer(OrderEntity, orderMachine, {
|
|
26
|
-
* initializeState: (entityId) => OrderState.Pending({ orderId: entityId }),
|
|
27
|
-
* })
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
readonly initializeState?: (entityId: string) => S;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Optional hooks for inspection/tracing.
|
|
34
|
-
* Called at specific points during event processing.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```ts
|
|
38
|
-
* EntityMachine.layer(OrderEntity, orderMachine, {
|
|
39
|
-
* hooks: {
|
|
40
|
-
* onTransition: (from, to, event) =>
|
|
41
|
-
* Effect.log(`Transition: ${from._tag} -> ${to._tag}`),
|
|
42
|
-
* onSpawnEffect: (state) =>
|
|
43
|
-
* Effect.log(`Running spawn effects for ${state._tag}`),
|
|
44
|
-
* onError: ({ phase, state }) =>
|
|
45
|
-
* Effect.log(`Defect in ${phase} at ${state._tag}`),
|
|
46
|
-
* },
|
|
47
|
-
* })
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
|
-
readonly hooks?: ProcessEventHooks<S, E>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Process a single event through the machine using shared core.
|
|
55
|
-
* Returns the new state after processing.
|
|
56
|
-
*/
|
|
57
|
-
const processEvent = Effect.fn("effect-machine.cluster.processEvent")(function* <
|
|
58
|
-
S extends { readonly _tag: string },
|
|
59
|
-
E extends { readonly _tag: string },
|
|
60
|
-
R,
|
|
61
|
-
GD extends GuardsDef = Record<string, never>,
|
|
62
|
-
EFD extends EffectsDef = Record<string, never>,
|
|
63
|
-
>(
|
|
64
|
-
machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>,
|
|
65
|
-
stateRef: Ref.Ref<S>,
|
|
66
|
-
event: E,
|
|
67
|
-
self: MachineRef<E>,
|
|
68
|
-
stateScopeRef: { current: Scope.CloseableScope },
|
|
69
|
-
hooks?: ProcessEventHooks<S, E>,
|
|
70
|
-
) {
|
|
71
|
-
const currentState = yield* Ref.get(stateRef);
|
|
72
|
-
|
|
73
|
-
// Process event using shared core
|
|
74
|
-
const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, hooks);
|
|
75
|
-
|
|
76
|
-
// Update state ref if transition occurred
|
|
77
|
-
if (result.transitioned) {
|
|
78
|
-
yield* Ref.set(stateRef, result.newState);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return result.newState;
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Create an Entity layer that wires a machine to handle RPC calls.
|
|
86
|
-
*
|
|
87
|
-
* The layer:
|
|
88
|
-
* - Maintains state via Ref per entity instance
|
|
89
|
-
* - Resolves transitions using the indexed lookup
|
|
90
|
-
* - Evaluates guards in registration order
|
|
91
|
-
* - Runs lifecycle effects (onEnter/spawn)
|
|
92
|
-
* - Processes internal events from spawn effects
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```ts
|
|
96
|
-
* const OrderEntity = toEntity(orderMachine, {
|
|
97
|
-
* type: "Order",
|
|
98
|
-
* stateSchema: OrderState,
|
|
99
|
-
* eventSchema: OrderEvent,
|
|
100
|
-
* })
|
|
101
|
-
*
|
|
102
|
-
* const OrderEntityLayer = EntityMachine.layer(OrderEntity, orderMachine, {
|
|
103
|
-
* initializeState: (entityId) => OrderState.Pending({ orderId: entityId }),
|
|
104
|
-
* })
|
|
105
|
-
*
|
|
106
|
-
* // Use in cluster
|
|
107
|
-
* const program = Effect.gen(function* () {
|
|
108
|
-
* const client = yield* ShardingClient.client(OrderEntity)
|
|
109
|
-
* yield* client.Send("order-123", { event: OrderEvent.Ship({ trackingId: "abc" }) })
|
|
110
|
-
* })
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
export const EntityMachine = {
|
|
114
|
-
/**
|
|
115
|
-
* Create a layer that wires a machine to an Entity.
|
|
116
|
-
*
|
|
117
|
-
* @param entity - Entity created via toEntity()
|
|
118
|
-
* @param machine - Machine with all effects provided
|
|
119
|
-
* @param options - Optional configuration (state initializer, inspection hooks)
|
|
120
|
-
*/
|
|
121
|
-
layer: <
|
|
122
|
-
S extends { readonly _tag: string },
|
|
123
|
-
E extends { readonly _tag: string },
|
|
124
|
-
R,
|
|
125
|
-
GD extends GuardsDef,
|
|
126
|
-
EFD extends EffectsDef,
|
|
127
|
-
EntityType extends string,
|
|
128
|
-
Rpcs extends Rpc.Any,
|
|
129
|
-
>(
|
|
130
|
-
entity: Entity.Entity<EntityType, Rpcs>,
|
|
131
|
-
machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>,
|
|
132
|
-
options?: EntityMachineOptions<S, E>,
|
|
133
|
-
): Layer.Layer<never, never, R> => {
|
|
134
|
-
const layer = Effect.fn("effect-machine.cluster.layer")(function* () {
|
|
135
|
-
// Get entity ID from context if available
|
|
136
|
-
const entityId = yield* Effect.serviceOption(Entity.CurrentAddress).pipe(
|
|
137
|
-
Effect.map((opt) => (opt._tag === "Some" ? opt.value.entityId : "")),
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
// Initialize state - use provided initializer or machine's initial state
|
|
141
|
-
const initialState =
|
|
142
|
-
options?.initializeState !== undefined
|
|
143
|
-
? options.initializeState(entityId)
|
|
144
|
-
: machine.initial;
|
|
145
|
-
|
|
146
|
-
// Create self reference for sending events back to machine
|
|
147
|
-
const internalQueue = yield* Queue.unbounded<E>();
|
|
148
|
-
const self: MachineRef<E> = {
|
|
149
|
-
send: Effect.fn("effect-machine.cluster.self.send")(function* (event: E) {
|
|
150
|
-
yield* Queue.offer(internalQueue, event);
|
|
151
|
-
}),
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// Create state ref
|
|
155
|
-
const stateRef = yield* Ref.make<S>(initialState);
|
|
156
|
-
|
|
157
|
-
// Create state scope for spawn effects
|
|
158
|
-
const stateScopeRef: { current: Scope.CloseableScope } = {
|
|
159
|
-
current: yield* Scope.make(),
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// Use $init event for initial lifecycle
|
|
163
|
-
const initEvent = { _tag: "$init" } as E;
|
|
164
|
-
|
|
165
|
-
// Run initial spawn effects
|
|
166
|
-
yield* runSpawnEffects(
|
|
167
|
-
machine,
|
|
168
|
-
initialState,
|
|
169
|
-
initEvent,
|
|
170
|
-
self,
|
|
171
|
-
stateScopeRef.current,
|
|
172
|
-
options?.hooks?.onError,
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// Process internal events in background
|
|
176
|
-
const runInternalEvent = Effect.fn("effect-machine.cluster.internalEvent")(function* () {
|
|
177
|
-
const event = yield* Queue.take(internalQueue);
|
|
178
|
-
yield* processEvent(machine, stateRef, event, self, stateScopeRef, options?.hooks);
|
|
179
|
-
});
|
|
180
|
-
yield* Effect.forkScoped(Effect.forever(runInternalEvent()));
|
|
181
|
-
|
|
182
|
-
// Return handlers matching the Entity's RPC protocol
|
|
183
|
-
// The actual types are inferred from the entity definition
|
|
184
|
-
return entity.of({
|
|
185
|
-
Send: (envelope: { payload: { event: E } }) =>
|
|
186
|
-
processEvent(
|
|
187
|
-
machine,
|
|
188
|
-
stateRef,
|
|
189
|
-
envelope.payload.event,
|
|
190
|
-
self,
|
|
191
|
-
stateScopeRef,
|
|
192
|
-
options?.hooks,
|
|
193
|
-
),
|
|
194
|
-
|
|
195
|
-
GetState: () => Ref.get(stateRef),
|
|
196
|
-
// Entity.of expects handlers matching Rpcs type param - dynamic construction requires cast
|
|
197
|
-
} as unknown as Parameters<typeof entity.of>[0]);
|
|
198
|
-
});
|
|
199
|
-
return entity.toLayer(layer()) as unknown as Layer.Layer<never, never, R>;
|
|
200
|
-
},
|
|
201
|
-
};
|
package/src/cluster/index.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cluster integration for effect-machine.
|
|
3
|
-
*
|
|
4
|
-
* Provides bridges between effect-machine state machines and @effect/cluster:
|
|
5
|
-
* - `toEntity` - Generate Entity definition from machine
|
|
6
|
-
* - `EntityMachine` - Wire machine to cluster Entity layer
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { Machine, MachineSchema } from "effect-machine"
|
|
11
|
-
* import { toEntity, EntityMachine } from "effect-machine/cluster"
|
|
12
|
-
*
|
|
13
|
-
* // Schema-first definitions
|
|
14
|
-
* const OrderState = MachineSchema.State({
|
|
15
|
-
* Pending: { orderId: Schema.String },
|
|
16
|
-
* Shipped: { trackingId: Schema.String },
|
|
17
|
-
* })
|
|
18
|
-
*
|
|
19
|
-
* const OrderEvent = MachineSchema.Event({
|
|
20
|
-
* Ship: { trackingId: Schema.String },
|
|
21
|
-
* })
|
|
22
|
-
*
|
|
23
|
-
* // Define machine
|
|
24
|
-
* const orderMachine = Machine.make(OrderState.Pending({ orderId: "" })).pipe(
|
|
25
|
-
* Machine.on(OrderState.Pending, OrderEvent.Ship, ...)
|
|
26
|
-
* )
|
|
27
|
-
*
|
|
28
|
-
* // Generate Entity
|
|
29
|
-
* const OrderEntity = toEntity(orderMachine, {
|
|
30
|
-
* type: "Order",
|
|
31
|
-
* stateSchema: OrderState,
|
|
32
|
-
* eventSchema: OrderEvent,
|
|
33
|
-
* })
|
|
34
|
-
*
|
|
35
|
-
* // Create layer
|
|
36
|
-
* const OrderEntityLayer = EntityMachine.layer(OrderEntity, orderMachine)
|
|
37
|
-
* ```
|
|
38
|
-
*
|
|
39
|
-
* @module
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
export { toEntity, type ToEntityOptions } from "./to-entity.js";
|
|
43
|
-
export { EntityMachine, type EntityMachineOptions } from "./entity-machine.js";
|
package/src/cluster/to-entity.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate Entity definition from a machine.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
import { Entity } from "@effect/cluster";
|
|
7
|
-
import { Rpc } from "@effect/rpc";
|
|
8
|
-
import type { Schema } from "effect";
|
|
9
|
-
|
|
10
|
-
import type { Machine } from "../machine.js";
|
|
11
|
-
import { MissingSchemaError } from "../errors.js";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Options for toEntity.
|
|
15
|
-
*/
|
|
16
|
-
export interface ToEntityOptions {
|
|
17
|
-
/**
|
|
18
|
-
* Entity type name (e.g., "Order", "User")
|
|
19
|
-
*/
|
|
20
|
-
readonly type: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Default RPC protocol for entity machines.
|
|
25
|
-
*
|
|
26
|
-
* - `Send` - Send event to machine, returns new state
|
|
27
|
-
* - `GetState` - Get current state
|
|
28
|
-
*/
|
|
29
|
-
export type EntityRpcs<
|
|
30
|
-
StateSchema extends Schema.Schema.Any,
|
|
31
|
-
EventSchema extends Schema.Schema.Any,
|
|
32
|
-
> = readonly [
|
|
33
|
-
Rpc.Rpc<
|
|
34
|
-
"Send",
|
|
35
|
-
Schema.Struct<{ readonly event: EventSchema }>,
|
|
36
|
-
StateSchema,
|
|
37
|
-
typeof Schema.Never,
|
|
38
|
-
never
|
|
39
|
-
>,
|
|
40
|
-
Rpc.Rpc<"GetState", typeof Schema.Void, StateSchema, typeof Schema.Never, never>,
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Generate an Entity definition from a machine.
|
|
45
|
-
*
|
|
46
|
-
* Creates an Entity with a standard RPC protocol:
|
|
47
|
-
* - `Send(event)` - Process event through machine, returns new state
|
|
48
|
-
* - `GetState()` - Returns current state
|
|
49
|
-
*
|
|
50
|
-
* Schemas are read from the machine - must use `Machine.make({ state, event, initial })`.
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```ts
|
|
54
|
-
* const OrderState = State({
|
|
55
|
-
* Pending: { orderId: Schema.String },
|
|
56
|
-
* Shipped: { trackingId: Schema.String },
|
|
57
|
-
* })
|
|
58
|
-
*
|
|
59
|
-
* const OrderEvent = Event({
|
|
60
|
-
* Ship: { trackingId: Schema.String },
|
|
61
|
-
* })
|
|
62
|
-
*
|
|
63
|
-
* const orderMachine = Machine.make({
|
|
64
|
-
* state: OrderState,
|
|
65
|
-
* event: OrderEvent,
|
|
66
|
-
* initial: OrderState.Pending({ orderId: "" }),
|
|
67
|
-
* }).pipe(
|
|
68
|
-
* Machine.on(OrderState.Pending, OrderEvent.Ship, ...),
|
|
69
|
-
* )
|
|
70
|
-
*
|
|
71
|
-
* const OrderEntity = toEntity(orderMachine, { type: "Order" })
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
export const toEntity = <
|
|
75
|
-
S extends { readonly _tag: string },
|
|
76
|
-
E extends { readonly _tag: string },
|
|
77
|
-
R,
|
|
78
|
-
>(
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Schema fields need wide acceptance
|
|
80
|
-
machine: Machine<S, E, R, any, any, any, any>,
|
|
81
|
-
options: ToEntityOptions,
|
|
82
|
-
) => {
|
|
83
|
-
const stateSchema = machine.stateSchema;
|
|
84
|
-
const eventSchema = machine.eventSchema;
|
|
85
|
-
|
|
86
|
-
if (stateSchema === undefined || eventSchema === undefined) {
|
|
87
|
-
throw new MissingSchemaError({ operation: "toEntity" });
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return Entity.make(options.type, [
|
|
91
|
-
Rpc.make("Send", {
|
|
92
|
-
payload: { event: eventSchema },
|
|
93
|
-
success: stateSchema,
|
|
94
|
-
}),
|
|
95
|
-
Rpc.make("GetState", {
|
|
96
|
-
success: stateSchema,
|
|
97
|
-
}),
|
|
98
|
-
]);
|
|
99
|
-
};
|
package/src/errors.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typed error classes for effect-machine.
|
|
3
|
-
*
|
|
4
|
-
* All errors extend Schema.TaggedError for:
|
|
5
|
-
* - Type-safe catching via Effect.catchTag
|
|
6
|
-
* - Serialization support
|
|
7
|
-
* - Composable error handling
|
|
8
|
-
*
|
|
9
|
-
* @module
|
|
10
|
-
*/
|
|
11
|
-
import { Schema } from "effect";
|
|
12
|
-
|
|
13
|
-
/** Attempted to spawn/restore actor with ID already in use */
|
|
14
|
-
export class DuplicateActorError extends Schema.TaggedError<DuplicateActorError>()(
|
|
15
|
-
"DuplicateActorError",
|
|
16
|
-
{ actorId: Schema.String },
|
|
17
|
-
) {}
|
|
18
|
-
|
|
19
|
-
/** Machine has unprovided effect slots */
|
|
20
|
-
export class UnprovidedSlotsError extends Schema.TaggedError<UnprovidedSlotsError>()(
|
|
21
|
-
"UnprovidedSlotsError",
|
|
22
|
-
{ slots: Schema.Array(Schema.String) },
|
|
23
|
-
) {}
|
|
24
|
-
|
|
25
|
-
/** Operation requires schemas attached to machine */
|
|
26
|
-
export class MissingSchemaError extends Schema.TaggedError<MissingSchemaError>()(
|
|
27
|
-
"MissingSchemaError",
|
|
28
|
-
{ operation: Schema.String },
|
|
29
|
-
) {}
|
|
30
|
-
|
|
31
|
-
/** State/Event schema has no variants */
|
|
32
|
-
export class InvalidSchemaError extends Schema.TaggedError<InvalidSchemaError>()(
|
|
33
|
-
"InvalidSchemaError",
|
|
34
|
-
{},
|
|
35
|
-
) {}
|
|
36
|
-
|
|
37
|
-
/** $match called with missing handler for tag */
|
|
38
|
-
export class MissingMatchHandlerError extends Schema.TaggedError<MissingMatchHandlerError>()(
|
|
39
|
-
"MissingMatchHandlerError",
|
|
40
|
-
{ tag: Schema.String },
|
|
41
|
-
) {}
|
|
42
|
-
|
|
43
|
-
/** Slot handler not found at runtime (internal error) */
|
|
44
|
-
export class SlotProvisionError extends Schema.TaggedError<SlotProvisionError>()(
|
|
45
|
-
"SlotProvisionError",
|
|
46
|
-
{
|
|
47
|
-
slotName: Schema.String,
|
|
48
|
-
slotType: Schema.Literal("guard", "effect"),
|
|
49
|
-
},
|
|
50
|
-
) {}
|
|
51
|
-
|
|
52
|
-
/** Machine.build() validation failed - missing or extra handlers */
|
|
53
|
-
export class ProvisionValidationError extends Schema.TaggedError<ProvisionValidationError>()(
|
|
54
|
-
"ProvisionValidationError",
|
|
55
|
-
{
|
|
56
|
-
missing: Schema.Array(Schema.String),
|
|
57
|
-
extra: Schema.Array(Schema.String),
|
|
58
|
-
},
|
|
59
|
-
) {}
|
|
60
|
-
|
|
61
|
-
/** Assertion failed in testing utilities */
|
|
62
|
-
export class AssertionError extends Schema.TaggedError<AssertionError>()("AssertionError", {
|
|
63
|
-
message: Schema.String,
|
|
64
|
-
}) {}
|
package/src/index.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
// Machine namespace (Effect-style)
|
|
2
|
-
export * as Machine from "./machine.js";
|
|
3
|
-
|
|
4
|
-
// Slot module
|
|
5
|
-
export { Slot } from "./slot.js";
|
|
6
|
-
export type {
|
|
7
|
-
GuardsSchema,
|
|
8
|
-
EffectsSchema,
|
|
9
|
-
GuardsDef,
|
|
10
|
-
EffectsDef,
|
|
11
|
-
GuardSlots,
|
|
12
|
-
EffectSlots,
|
|
13
|
-
GuardSlot,
|
|
14
|
-
EffectSlot as SlotEffectSlot,
|
|
15
|
-
GuardHandlers,
|
|
16
|
-
EffectHandlers as SlotEffectHandlers,
|
|
17
|
-
MachineContext,
|
|
18
|
-
} from "./slot.js";
|
|
19
|
-
|
|
20
|
-
// Errors
|
|
21
|
-
export {
|
|
22
|
-
AssertionError,
|
|
23
|
-
DuplicateActorError,
|
|
24
|
-
InvalidSchemaError,
|
|
25
|
-
MissingMatchHandlerError,
|
|
26
|
-
MissingSchemaError,
|
|
27
|
-
ProvisionValidationError,
|
|
28
|
-
SlotProvisionError,
|
|
29
|
-
UnprovidedSlotsError,
|
|
30
|
-
} from "./errors.js";
|
|
31
|
-
|
|
32
|
-
// Schema-first State/Event definitions
|
|
33
|
-
export { State, Event } from "./schema.js";
|
|
34
|
-
export type { MachineStateSchema, MachineEventSchema } from "./schema.js";
|
|
35
|
-
|
|
36
|
-
// Core machine types (for advanced use)
|
|
37
|
-
export type {
|
|
38
|
-
Machine as MachineType,
|
|
39
|
-
BuiltMachine,
|
|
40
|
-
MachineRef,
|
|
41
|
-
MakeConfig,
|
|
42
|
-
Transition,
|
|
43
|
-
SpawnEffect,
|
|
44
|
-
BackgroundEffect,
|
|
45
|
-
PersistOptions,
|
|
46
|
-
HandlerContext,
|
|
47
|
-
StateHandlerContext,
|
|
48
|
-
ProvideHandlers,
|
|
49
|
-
} from "./machine.js";
|
|
50
|
-
|
|
51
|
-
// Actor types and system
|
|
52
|
-
export type { ActorRef, ActorSystem } from "./actor.js";
|
|
53
|
-
export { ActorSystem as ActorSystemService, Default as ActorSystemDefault } from "./actor.js";
|
|
54
|
-
|
|
55
|
-
// Testing utilities
|
|
56
|
-
export {
|
|
57
|
-
assertNeverReaches,
|
|
58
|
-
assertPath,
|
|
59
|
-
assertReaches,
|
|
60
|
-
createTestHarness,
|
|
61
|
-
simulate,
|
|
62
|
-
} from "./testing.js";
|
|
63
|
-
export type { SimulationResult, TestHarness, TestHarnessOptions } from "./testing.js";
|
|
64
|
-
|
|
65
|
-
// Inspection
|
|
66
|
-
export type {
|
|
67
|
-
AnyInspectionEvent,
|
|
68
|
-
EffectEvent,
|
|
69
|
-
ErrorEvent,
|
|
70
|
-
EventReceivedEvent,
|
|
71
|
-
InspectionEvent,
|
|
72
|
-
Inspector,
|
|
73
|
-
SpawnEvent,
|
|
74
|
-
StopEvent,
|
|
75
|
-
TransitionEvent,
|
|
76
|
-
} from "./inspection.js";
|
|
77
|
-
export {
|
|
78
|
-
collectingInspector,
|
|
79
|
-
consoleInspector,
|
|
80
|
-
Inspector as InspectorService,
|
|
81
|
-
makeInspector,
|
|
82
|
-
} from "./inspection.js";
|
|
83
|
-
|
|
84
|
-
// Persistence
|
|
85
|
-
export type {
|
|
86
|
-
ActorMetadata,
|
|
87
|
-
PersistedEvent,
|
|
88
|
-
PersistenceAdapter,
|
|
89
|
-
PersistenceConfig,
|
|
90
|
-
PersistentActorRef,
|
|
91
|
-
PersistentMachine,
|
|
92
|
-
RestoreFailure,
|
|
93
|
-
RestoreResult,
|
|
94
|
-
Snapshot,
|
|
95
|
-
} from "./persistence/index.js";
|
|
96
|
-
export {
|
|
97
|
-
createPersistentActor,
|
|
98
|
-
InMemoryPersistenceAdapter,
|
|
99
|
-
isPersistentMachine,
|
|
100
|
-
makeInMemoryPersistenceAdapter,
|
|
101
|
-
PersistenceAdapterTag,
|
|
102
|
-
PersistenceError,
|
|
103
|
-
restorePersistentActor,
|
|
104
|
-
VersionConflictError,
|
|
105
|
-
} from "./persistence/index.js";
|
package/src/inspection.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { Context, type Schema } from "effect";
|
|
2
|
-
|
|
3
|
-
// ============================================================================
|
|
4
|
-
// Type-level helpers
|
|
5
|
-
// ============================================================================
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Resolve a type param: if it's a Schema, extract `.Type`; otherwise use as-is.
|
|
9
|
-
*/
|
|
10
|
-
type ResolveType<T> = T extends Schema.Schema<infer A, infer _I, infer _R> ? A : T;
|
|
11
|
-
|
|
12
|
-
// ============================================================================
|
|
13
|
-
// Inspection Events
|
|
14
|
-
// ============================================================================
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Event emitted when an actor is spawned
|
|
18
|
-
*/
|
|
19
|
-
export interface SpawnEvent<S> {
|
|
20
|
-
readonly type: "@machine.spawn";
|
|
21
|
-
readonly actorId: string;
|
|
22
|
-
readonly initialState: S;
|
|
23
|
-
readonly timestamp: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Event emitted when an actor receives an event
|
|
28
|
-
*/
|
|
29
|
-
export interface EventReceivedEvent<S, E> {
|
|
30
|
-
readonly type: "@machine.event";
|
|
31
|
-
readonly actorId: string;
|
|
32
|
-
readonly state: S;
|
|
33
|
-
readonly event: E;
|
|
34
|
-
readonly timestamp: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Event emitted when a transition occurs
|
|
39
|
-
*/
|
|
40
|
-
export interface TransitionEvent<S, E> {
|
|
41
|
-
readonly type: "@machine.transition";
|
|
42
|
-
readonly actorId: string;
|
|
43
|
-
readonly fromState: S;
|
|
44
|
-
readonly toState: S;
|
|
45
|
-
readonly event: E;
|
|
46
|
-
readonly timestamp: number;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Event emitted when a spawn effect runs
|
|
51
|
-
*/
|
|
52
|
-
export interface EffectEvent<S> {
|
|
53
|
-
readonly type: "@machine.effect";
|
|
54
|
-
readonly actorId: string;
|
|
55
|
-
readonly effectType: "spawn";
|
|
56
|
-
readonly state: S;
|
|
57
|
-
readonly timestamp: number;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Event emitted when a transition handler or spawn effect fails with a defect
|
|
62
|
-
*/
|
|
63
|
-
export interface ErrorEvent<S, E> {
|
|
64
|
-
readonly type: "@machine.error";
|
|
65
|
-
readonly actorId: string;
|
|
66
|
-
readonly phase: "transition" | "spawn";
|
|
67
|
-
readonly state: S;
|
|
68
|
-
readonly event: E;
|
|
69
|
-
readonly error: string;
|
|
70
|
-
readonly timestamp: number;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Event emitted when an actor stops
|
|
75
|
-
*/
|
|
76
|
-
export interface StopEvent<S> {
|
|
77
|
-
readonly type: "@machine.stop";
|
|
78
|
-
readonly actorId: string;
|
|
79
|
-
readonly finalState: S;
|
|
80
|
-
readonly timestamp: number;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Union of all inspection events
|
|
85
|
-
*/
|
|
86
|
-
export type InspectionEvent<S, E> =
|
|
87
|
-
| SpawnEvent<S>
|
|
88
|
-
| EventReceivedEvent<S, E>
|
|
89
|
-
| TransitionEvent<S, E>
|
|
90
|
-
| EffectEvent<S>
|
|
91
|
-
| ErrorEvent<S, E>
|
|
92
|
-
| StopEvent<S>;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Convenience alias for untyped inspection events.
|
|
96
|
-
* Useful for general-purpose inspectors that don't need specific state/event types.
|
|
97
|
-
* State and event fields are typed as `{ readonly _tag: string }` so discriminated
|
|
98
|
-
* access to `_tag` works without casting.
|
|
99
|
-
*/
|
|
100
|
-
export type AnyInspectionEvent = InspectionEvent<
|
|
101
|
-
{ readonly _tag: string },
|
|
102
|
-
{ readonly _tag: string }
|
|
103
|
-
>;
|
|
104
|
-
|
|
105
|
-
// ============================================================================
|
|
106
|
-
// Inspector Service
|
|
107
|
-
// ============================================================================
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Inspector interface for observing machine behavior
|
|
111
|
-
*/
|
|
112
|
-
export interface Inspector<S, E> {
|
|
113
|
-
readonly onInspect: (event: InspectionEvent<S, E>) => void;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Inspector service tag - optional service for machine introspection
|
|
118
|
-
* Uses `any` types to allow variance flexibility when providing the service
|
|
119
|
-
*/
|
|
120
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
121
|
-
export const Inspector = Context.GenericTag<Inspector<any, any>>("@effect/machine/Inspector");
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Create an inspector from a callback function.
|
|
125
|
-
*
|
|
126
|
-
* Type params accept either raw tagged types or Schema constructors:
|
|
127
|
-
* - `makeInspector(cb)` — defaults to `AnyInspectionEvent`
|
|
128
|
-
* - `makeInspector<MyState, MyEvent>(cb)` — explicit tagged types
|
|
129
|
-
* - `makeInspector<typeof MyState, typeof MyEvent>(cb)` — schema constructors (auto-extracts `.Type`)
|
|
130
|
-
*/
|
|
131
|
-
export const makeInspector = <S = { readonly _tag: string }, E = { readonly _tag: string }>(
|
|
132
|
-
onInspect: (event: InspectionEvent<ResolveType<S>, ResolveType<E>>) => void,
|
|
133
|
-
): Inspector<ResolveType<S>, ResolveType<E>> => ({ onInspect });
|
|
134
|
-
|
|
135
|
-
// ============================================================================
|
|
136
|
-
// Built-in Inspectors
|
|
137
|
-
// ============================================================================
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Console inspector that logs events in a readable format
|
|
141
|
-
*/
|
|
142
|
-
export const consoleInspector = (): Inspector<
|
|
143
|
-
{ readonly _tag: string },
|
|
144
|
-
{ readonly _tag: string }
|
|
145
|
-
> =>
|
|
146
|
-
makeInspector((event) => {
|
|
147
|
-
const prefix = `[${event.actorId}]`;
|
|
148
|
-
switch (event.type) {
|
|
149
|
-
case "@machine.spawn":
|
|
150
|
-
console.log(prefix, "spawned →", event.initialState._tag);
|
|
151
|
-
break;
|
|
152
|
-
case "@machine.event":
|
|
153
|
-
console.log(prefix, "received", event.event._tag, "in", event.state._tag);
|
|
154
|
-
break;
|
|
155
|
-
case "@machine.transition":
|
|
156
|
-
console.log(prefix, event.fromState._tag, "→", event.toState._tag);
|
|
157
|
-
break;
|
|
158
|
-
case "@machine.effect":
|
|
159
|
-
console.log(prefix, event.effectType, "effect in", event.state._tag);
|
|
160
|
-
break;
|
|
161
|
-
case "@machine.error":
|
|
162
|
-
console.log(prefix, "error in", event.phase, event.state._tag, "-", event.error);
|
|
163
|
-
break;
|
|
164
|
-
case "@machine.stop":
|
|
165
|
-
console.log(prefix, "stopped in", event.finalState._tag);
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Collecting inspector that stores events in an array for testing
|
|
172
|
-
*/
|
|
173
|
-
export const collectingInspector = <
|
|
174
|
-
S extends { readonly _tag: string },
|
|
175
|
-
E extends { readonly _tag: string },
|
|
176
|
-
>(
|
|
177
|
-
events: InspectionEvent<S, E>[],
|
|
178
|
-
): Inspector<S, E> => ({ onInspect: (event) => events.push(event) });
|