effect-machine 0.4.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/actor.d.ts +40 -4
- package/dist/actor.js +89 -34
- package/dist/cluster/entity-machine.d.ts +2 -2
- package/dist/cluster/entity-machine.js +1 -1
- package/dist/cluster/to-entity.d.ts +5 -5
- package/dist/cluster/to-entity.js +2 -2
- package/dist/errors.d.ts +25 -40
- package/dist/errors.js +10 -10
- package/dist/index.d.ts +2 -2
- package/dist/inspection.d.ts +3 -3
- package/dist/inspection.js +2 -2
- package/dist/internal/brands.d.ts +3 -6
- package/dist/internal/inspection.js +5 -1
- package/dist/internal/transition.d.ts +2 -2
- package/dist/internal/transition.js +6 -6
- package/dist/internal/utils.js +11 -2
- package/dist/machine.d.ts +5 -5
- package/dist/machine.js +9 -5
- package/dist/persistence/adapter.d.ts +18 -21
- package/dist/persistence/adapter.js +4 -4
- package/dist/persistence/adapters/in-memory.js +4 -4
- package/dist/persistence/persistent-actor.js +23 -14
- package/dist/persistence/persistent-machine.d.ts +3 -3
- package/dist/schema.d.ts +4 -4
- package/dist/schema.js +2 -2
- package/dist/slot.d.ts +3 -3
- package/dist/slot.js +2 -2
- package/dist-v3/_virtual/_rolldown/runtime.js +18 -0
- package/dist-v3/actor.d.ts +291 -0
- package/dist-v3/actor.js +459 -0
- package/dist-v3/cluster/entity-machine.d.ts +90 -0
- package/dist-v3/cluster/entity-machine.js +80 -0
- package/dist-v3/cluster/index.d.ts +3 -0
- package/dist-v3/cluster/index.js +4 -0
- package/dist-v3/cluster/to-entity.d.ts +61 -0
- package/dist-v3/cluster/to-entity.js +53 -0
- package/dist-v3/errors.d.ts +27 -0
- package/dist-v3/errors.js +38 -0
- package/dist-v3/index.d.ts +13 -0
- package/dist-v3/index.js +14 -0
- package/dist-v3/inspection.d.ts +125 -0
- package/dist-v3/inspection.js +50 -0
- package/dist-v3/internal/brands.d.ts +40 -0
- package/dist-v3/internal/brands.js +0 -0
- package/dist-v3/internal/inspection.d.ts +11 -0
- package/dist-v3/internal/inspection.js +15 -0
- package/dist-v3/internal/transition.d.ts +160 -0
- package/dist-v3/internal/transition.js +238 -0
- package/dist-v3/internal/utils.d.ts +60 -0
- package/dist-v3/internal/utils.js +51 -0
- package/dist-v3/machine.d.ts +278 -0
- package/dist-v3/machine.js +317 -0
- package/dist-v3/persistence/adapter.d.ts +125 -0
- package/dist-v3/persistence/adapter.js +27 -0
- package/dist-v3/persistence/adapters/in-memory.d.ts +32 -0
- package/dist-v3/persistence/adapters/in-memory.js +176 -0
- package/dist-v3/persistence/index.d.ts +5 -0
- package/dist-v3/persistence/index.js +6 -0
- package/dist-v3/persistence/persistent-actor.d.ts +49 -0
- package/dist-v3/persistence/persistent-actor.js +367 -0
- package/dist-v3/persistence/persistent-machine.d.ts +105 -0
- package/dist-v3/persistence/persistent-machine.js +24 -0
- package/dist-v3/schema.d.ts +141 -0
- package/dist-v3/schema.js +165 -0
- package/dist-v3/slot.d.ts +130 -0
- package/dist-v3/slot.js +99 -0
- package/dist-v3/testing.d.ts +136 -0
- package/dist-v3/testing.js +138 -0
- package/package.json +29 -21
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { Brand } from "effect";
|
|
2
2
|
|
|
3
3
|
//#region src/internal/brands.d.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type StateTypeId = typeof StateTypeId;
|
|
7
|
-
type EventTypeId = typeof EventTypeId;
|
|
4
|
+
type StateTypeId = "effect-machine/StateTypeId";
|
|
5
|
+
type EventTypeId = "effect-machine/EventTypeId";
|
|
8
6
|
interface StateBrand extends Brand.Brand<StateTypeId> {}
|
|
9
7
|
interface EventBrand extends Brand.Brand<EventTypeId> {}
|
|
10
8
|
type BrandedState = {
|
|
@@ -13,8 +11,7 @@ type BrandedState = {
|
|
|
13
11
|
type BrandedEvent = {
|
|
14
12
|
readonly _tag: string;
|
|
15
13
|
} & EventBrand;
|
|
16
|
-
|
|
17
|
-
type SchemaIdTypeId = typeof SchemaIdTypeId;
|
|
14
|
+
type SchemaIdTypeId = "effect-machine/SchemaIdTypeId";
|
|
18
15
|
/**
|
|
19
16
|
* Brand that captures the schema definition type D.
|
|
20
17
|
* Two schemas with identical definition shapes will have compatible brands.
|
|
@@ -8,7 +8,11 @@ import { Clock, Effect } from "effect";
|
|
|
8
8
|
const emitWithTimestamp = Effect.fn("effect-machine.emitWithTimestamp")(function* (inspector, makeEvent) {
|
|
9
9
|
if (inspector === void 0) return;
|
|
10
10
|
const timestamp = yield* Clock.currentTimeMillis;
|
|
11
|
-
yield* Effect.
|
|
11
|
+
yield* Effect.sync(() => {
|
|
12
|
+
try {
|
|
13
|
+
inspector.onInspect(makeEvent(timestamp));
|
|
14
|
+
} catch {}
|
|
15
|
+
});
|
|
12
16
|
});
|
|
13
17
|
|
|
14
18
|
//#endregion
|
|
@@ -102,7 +102,7 @@ declare const processEventCore: <S extends {
|
|
|
102
102
|
}, E extends {
|
|
103
103
|
readonly _tag: string;
|
|
104
104
|
}, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, currentState: S, event: E, self: MachineRef<E>, stateScopeRef: {
|
|
105
|
-
current: Scope.
|
|
105
|
+
current: Scope.Closeable;
|
|
106
106
|
}, system: ActorSystem, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
|
|
107
107
|
newState: S;
|
|
108
108
|
previousState: S;
|
|
@@ -119,7 +119,7 @@ declare const runSpawnEffects: <S extends {
|
|
|
119
119
|
readonly _tag: string;
|
|
120
120
|
}, E extends {
|
|
121
121
|
readonly _tag: string;
|
|
122
|
-
}, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.
|
|
122
|
+
}, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.Closeable, system: ActorSystem, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
|
|
123
123
|
/**
|
|
124
124
|
* Resolve which transition should fire for a given state and event.
|
|
125
125
|
* Uses indexed O(1) lookup. First matching transition wins.
|
|
@@ -77,8 +77,8 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
|
|
|
77
77
|
* @internal
|
|
78
78
|
*/
|
|
79
79
|
const processEventCore = Effect.fn("effect-machine.processEventCore")(function* (machine, currentState, event, self, stateScopeRef, system, hooks) {
|
|
80
|
-
const result = yield* executeTransition(machine, currentState, event, self, system).pipe(Effect.
|
|
81
|
-
if (Cause.
|
|
80
|
+
const result = yield* executeTransition(machine, currentState, event, self, system).pipe(Effect.catchCause((cause) => {
|
|
81
|
+
if (Cause.hasInterruptsOnly(cause)) return Effect.interrupt;
|
|
82
82
|
const onError = hooks?.onError;
|
|
83
83
|
if (onError === void 0) return Effect.failCause(cause).pipe(Effect.orDie);
|
|
84
84
|
return onError({
|
|
@@ -86,7 +86,7 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
|
|
|
86
86
|
state: currentState,
|
|
87
87
|
event,
|
|
88
88
|
cause
|
|
89
|
-
}).pipe(Effect.
|
|
89
|
+
}).pipe(Effect.andThen(Effect.failCause(cause).pipe(Effect.orDie)));
|
|
90
90
|
}));
|
|
91
91
|
if (!result.transitioned) return {
|
|
92
92
|
newState: currentState,
|
|
@@ -134,15 +134,15 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
|
|
|
134
134
|
self,
|
|
135
135
|
effects: effectSlots,
|
|
136
136
|
system
|
|
137
|
-
}).pipe(Effect.provideService(machine.Context, ctx), Effect.
|
|
138
|
-
if (Cause.
|
|
137
|
+
}).pipe(Effect.provideService(machine.Context, ctx), Effect.catchCause((cause) => {
|
|
138
|
+
if (Cause.hasInterruptsOnly(cause)) return Effect.interrupt;
|
|
139
139
|
if (reportError === void 0) return Effect.failCause(cause).pipe(Effect.orDie);
|
|
140
140
|
return reportError({
|
|
141
141
|
phase: "spawn",
|
|
142
142
|
state,
|
|
143
143
|
event,
|
|
144
144
|
cause
|
|
145
|
-
}).pipe(Effect.
|
|
145
|
+
}).pipe(Effect.andThen(Effect.failCause(cause).pipe(Effect.orDie)));
|
|
146
146
|
}));
|
|
147
147
|
yield* Effect.forkScoped(effect).pipe(Effect.provideService(Scope.Scope, stateScope));
|
|
148
148
|
}
|
package/dist/internal/utils.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Effect, Stream } from "effect";
|
|
2
2
|
|
|
3
3
|
//#region src/internal/utils.ts
|
|
4
4
|
/**
|
|
5
|
+
* Internal utilities for effect-machine.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
5
9
|
* Internal event tags used for lifecycle effect contexts.
|
|
6
10
|
* Prefixed with $ to distinguish from user events.
|
|
7
11
|
* @internal
|
|
@@ -25,7 +29,7 @@ const getTag = (constructorOrValue) => {
|
|
|
25
29
|
}
|
|
26
30
|
};
|
|
27
31
|
/** Check if a value is an Effect */
|
|
28
|
-
const isEffect =
|
|
32
|
+
const isEffect = Effect.isEffect;
|
|
29
33
|
/**
|
|
30
34
|
* Stub ActorSystem that dies on any method call.
|
|
31
35
|
* Used in contexts where spawning/system access isn't supported
|
|
@@ -37,6 +41,11 @@ const stubSystem = {
|
|
|
37
41
|
restore: () => Effect.die("restore not supported in stub system"),
|
|
38
42
|
get: () => Effect.die("get not supported in stub system"),
|
|
39
43
|
stop: () => Effect.die("stop not supported in stub system"),
|
|
44
|
+
events: Stream.empty,
|
|
45
|
+
get actors() {
|
|
46
|
+
return /* @__PURE__ */ new Map();
|
|
47
|
+
},
|
|
48
|
+
subscribe: () => () => {},
|
|
40
49
|
listPersisted: () => Effect.die("listPersisted not supported in stub system"),
|
|
41
50
|
restoreMany: () => Effect.die("restoreMany not supported in stub system"),
|
|
42
51
|
restoreAll: () => Effect.die("restoreAll not supported in stub system")
|
package/dist/machine.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { DuplicateActorError } from "./errors.js";
|
|
|
7
7
|
import { findTransitions } from "./internal/transition.js";
|
|
8
8
|
import "./persistence/index.js";
|
|
9
9
|
import { ActorRef, ActorSystem } from "./actor.js";
|
|
10
|
-
import { Cause,
|
|
10
|
+
import { Cause, Effect, Schedule, Schema, Scope, ServiceMap } from "effect";
|
|
11
11
|
|
|
12
12
|
//#region src/machine.d.ts
|
|
13
13
|
declare namespace machine_d_exports {
|
|
@@ -159,13 +159,13 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
159
159
|
guards: GuardSlots<GD>;
|
|
160
160
|
effects: EffectSlots<EFD>;
|
|
161
161
|
};
|
|
162
|
-
readonly stateSchema?: Schema.Schema<State
|
|
163
|
-
readonly eventSchema?: Schema.Schema<Event
|
|
162
|
+
readonly stateSchema?: Schema.Schema<State>;
|
|
163
|
+
readonly eventSchema?: Schema.Schema<Event>;
|
|
164
164
|
/**
|
|
165
165
|
* Context tag for accessing machine state/event/self in slot handlers.
|
|
166
166
|
* Uses shared module-level tag for all machines.
|
|
167
167
|
*/
|
|
168
|
-
readonly Context:
|
|
168
|
+
readonly Context: ServiceMap.Service<MachineContext<State, Event, MachineRef<Event>>, MachineContext<State, Event, MachineRef<Event>>>;
|
|
169
169
|
get transitions(): ReadonlyArray<Transition<State, Event, GD, EFD, R>>;
|
|
170
170
|
get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
|
|
171
171
|
get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
|
|
@@ -173,7 +173,7 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
173
173
|
get guardsSchema(): GuardsSchema<GD> | undefined;
|
|
174
174
|
get effectsSchema(): EffectsSchema<EFD> | undefined;
|
|
175
175
|
/** @internal */
|
|
176
|
-
constructor(initial: State, stateSchema?: Schema.Schema<State
|
|
176
|
+
constructor(initial: State, stateSchema?: Schema.Schema<State>, eventSchema?: Schema.Schema<Event>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
|
|
177
177
|
/** Register transition for a single state */
|
|
178
178
|
on<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, GD, EFD, never>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
179
179
|
/** Register transition for multiple states (handler receives union of state types) */
|
package/dist/machine.js
CHANGED
|
@@ -98,7 +98,9 @@ var Machine = class Machine {
|
|
|
98
98
|
this.stateSchema = stateSchema;
|
|
99
99
|
this.eventSchema = eventSchema;
|
|
100
100
|
this._slots = {
|
|
101
|
-
guards: this._guardsSchema !== void 0 ? this._guardsSchema._createSlots((name, params) => Effect.flatMap(Effect.
|
|
101
|
+
guards: this._guardsSchema !== void 0 ? this._guardsSchema._createSlots((name, params) => Effect.flatMap(Effect.serviceOption(this.Context), (maybeCtx) => {
|
|
102
|
+
if (Option.isNone(maybeCtx)) return Effect.die("MachineContext not available");
|
|
103
|
+
const ctx = maybeCtx.value;
|
|
102
104
|
const handler = this._guardHandlers.get(name);
|
|
103
105
|
if (handler === void 0) return Effect.die(new SlotProvisionError({
|
|
104
106
|
slotName: name,
|
|
@@ -107,7 +109,9 @@ var Machine = class Machine {
|
|
|
107
109
|
const result = handler(params, ctx);
|
|
108
110
|
return typeof result === "boolean" ? Effect.succeed(result) : result;
|
|
109
111
|
})) : {},
|
|
110
|
-
effects: this._effectsSchema !== void 0 ? this._effectsSchema._createSlots((name, params) => Effect.flatMap(Effect.
|
|
112
|
+
effects: this._effectsSchema !== void 0 ? this._effectsSchema._createSlots((name, params) => Effect.flatMap(Effect.serviceOption(this.Context), (maybeCtx) => {
|
|
113
|
+
if (Option.isNone(maybeCtx)) return Effect.die("MachineContext not available");
|
|
114
|
+
const ctx = maybeCtx.value;
|
|
111
115
|
const handler = this._effectHandlers.get(name);
|
|
112
116
|
if (handler === void 0) return Effect.die(new SlotProvisionError({
|
|
113
117
|
slotName: name,
|
|
@@ -194,14 +198,14 @@ var Machine = class Machine {
|
|
|
194
198
|
const exit = yield* Effect.exit(run(ctx));
|
|
195
199
|
if (Exit.isSuccess(exit)) {
|
|
196
200
|
yield* ctx.self.send(options.onSuccess(exit.value, ctx));
|
|
197
|
-
yield* Effect.yieldNow
|
|
201
|
+
yield* Effect.yieldNow;
|
|
198
202
|
return;
|
|
199
203
|
}
|
|
200
204
|
const cause = exit.cause;
|
|
201
|
-
if (Cause.
|
|
205
|
+
if (Cause.hasInterruptsOnly(cause)) return;
|
|
202
206
|
if (options.onFailure !== void 0) {
|
|
203
207
|
yield* ctx.self.send(options.onFailure(cause, ctx));
|
|
204
|
-
yield* Effect.yieldNow
|
|
208
|
+
yield* Effect.yieldNow;
|
|
205
209
|
return;
|
|
206
210
|
}
|
|
207
211
|
return yield* Effect.failCause(cause).pipe(Effect.orDie);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DuplicateActorError } from "../errors.js";
|
|
2
2
|
import { PersistentActorRef } from "./persistent-actor.js";
|
|
3
|
-
import {
|
|
3
|
+
import { Effect, Option, Schema, ServiceMap } from "effect";
|
|
4
|
+
import * as effect_Cause0 from "effect/Cause";
|
|
4
5
|
|
|
5
6
|
//#region src/persistence/adapter.d.ts
|
|
6
7
|
/**
|
|
@@ -64,20 +65,20 @@ interface PersistenceAdapter {
|
|
|
64
65
|
* Save a snapshot of actor state.
|
|
65
66
|
* Implementations should use optimistic locking — fail if version mismatch.
|
|
66
67
|
*/
|
|
67
|
-
readonly saveSnapshot: <S
|
|
68
|
+
readonly saveSnapshot: <S>(id: string, snapshot: Snapshot<S>, schema: Schema.Codec<S, unknown, never, never>) => Effect.Effect<void, PersistenceError | VersionConflictError>;
|
|
68
69
|
/**
|
|
69
70
|
* Load the latest snapshot for an actor.
|
|
70
71
|
* Returns None if no snapshot exists.
|
|
71
72
|
*/
|
|
72
|
-
readonly loadSnapshot: <S
|
|
73
|
+
readonly loadSnapshot: <S>(id: string, schema: Schema.Codec<S, unknown, never, never>) => Effect.Effect<Option.Option<Snapshot<S>>, PersistenceError>;
|
|
73
74
|
/**
|
|
74
75
|
* Append an event to the actor's event journal.
|
|
75
76
|
*/
|
|
76
|
-
readonly appendEvent: <E
|
|
77
|
+
readonly appendEvent: <E>(id: string, event: PersistedEvent<E>, schema: Schema.Codec<E, unknown, never, never>) => Effect.Effect<void, PersistenceError>;
|
|
77
78
|
/**
|
|
78
79
|
* Load events from the journal, optionally after a specific version.
|
|
79
80
|
*/
|
|
80
|
-
readonly loadEvents: <E
|
|
81
|
+
readonly loadEvents: <E>(id: string, schema: Schema.Codec<E, unknown, never, never>, afterVersion?: number) => Effect.Effect<ReadonlyArray<PersistedEvent<E>>, PersistenceError>;
|
|
81
82
|
/**
|
|
82
83
|
* Delete all persisted data for an actor (snapshot + events).
|
|
83
84
|
*/
|
|
@@ -106,30 +107,26 @@ interface PersistenceAdapter {
|
|
|
106
107
|
*/
|
|
107
108
|
readonly loadMetadata?: (id: string) => Effect.Effect<Option.Option<ActorMetadata>, PersistenceError>;
|
|
108
109
|
}
|
|
109
|
-
declare const PersistenceError_base: Schema.
|
|
110
|
-
readonly
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
message: Schema.optional<typeof Schema.String>;
|
|
116
|
-
}>;
|
|
110
|
+
declare const PersistenceError_base: Schema.ErrorClass<PersistenceError, Schema.TaggedStruct<"PersistenceError", {
|
|
111
|
+
readonly operation: Schema.String;
|
|
112
|
+
readonly actorId: Schema.String;
|
|
113
|
+
readonly cause: Schema.optional<Schema.Unknown>;
|
|
114
|
+
readonly message: Schema.optional<Schema.String>;
|
|
115
|
+
}>, effect_Cause0.YieldableError>;
|
|
117
116
|
/**
|
|
118
117
|
* Error type for persistence operations
|
|
119
118
|
*/
|
|
120
119
|
declare class PersistenceError extends PersistenceError_base {}
|
|
121
|
-
declare const VersionConflictError_base: Schema.
|
|
122
|
-
readonly
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
actualVersion: typeof Schema.Number;
|
|
127
|
-
}>;
|
|
120
|
+
declare const VersionConflictError_base: Schema.ErrorClass<VersionConflictError, Schema.TaggedStruct<"VersionConflictError", {
|
|
121
|
+
readonly actorId: Schema.String;
|
|
122
|
+
readonly expectedVersion: Schema.Number;
|
|
123
|
+
readonly actualVersion: Schema.Number;
|
|
124
|
+
}>, effect_Cause0.YieldableError>;
|
|
128
125
|
/**
|
|
129
126
|
* Version conflict error — snapshot version doesn't match expected
|
|
130
127
|
*/
|
|
131
128
|
declare class VersionConflictError extends VersionConflictError_base {}
|
|
132
|
-
declare const PersistenceAdapterTag_base:
|
|
129
|
+
declare const PersistenceAdapterTag_base: ServiceMap.ServiceClass<PersistenceAdapterTag, "effect-machine/src/persistence/adapter/PersistenceAdapterTag", PersistenceAdapter>;
|
|
133
130
|
/**
|
|
134
131
|
* PersistenceAdapter service tag
|
|
135
132
|
*/
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Schema, ServiceMap } from "effect";
|
|
2
2
|
|
|
3
3
|
//#region src/persistence/adapter.ts
|
|
4
4
|
/**
|
|
5
5
|
* Error type for persistence operations
|
|
6
6
|
*/
|
|
7
|
-
var PersistenceError = class extends Schema.
|
|
7
|
+
var PersistenceError = class extends Schema.TaggedErrorClass()("PersistenceError", {
|
|
8
8
|
operation: Schema.String,
|
|
9
9
|
actorId: Schema.String,
|
|
10
10
|
cause: Schema.optional(Schema.Unknown),
|
|
@@ -13,7 +13,7 @@ var PersistenceError = class extends Schema.TaggedError()("PersistenceError", {
|
|
|
13
13
|
/**
|
|
14
14
|
* Version conflict error — snapshot version doesn't match expected
|
|
15
15
|
*/
|
|
16
|
-
var VersionConflictError = class extends Schema.
|
|
16
|
+
var VersionConflictError = class extends Schema.TaggedErrorClass()("VersionConflictError", {
|
|
17
17
|
actorId: Schema.String,
|
|
18
18
|
expectedVersion: Schema.Number,
|
|
19
19
|
actualVersion: Schema.Number
|
|
@@ -21,7 +21,7 @@ var VersionConflictError = class extends Schema.TaggedError()("VersionConflictEr
|
|
|
21
21
|
/**
|
|
22
22
|
* PersistenceAdapter service tag
|
|
23
23
|
*/
|
|
24
|
-
var PersistenceAdapterTag = class extends
|
|
24
|
+
var PersistenceAdapterTag = class extends ServiceMap.Service()("effect-machine/src/persistence/adapter/PersistenceAdapterTag") {};
|
|
25
25
|
|
|
26
26
|
//#endregion
|
|
27
27
|
export { PersistenceAdapterTag, PersistenceError, VersionConflictError };
|
|
@@ -42,7 +42,7 @@ const make = Effect.gen(function* () {
|
|
|
42
42
|
actualVersion: snapshot.version
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
const encoded = yield* Schema.
|
|
45
|
+
const encoded = yield* Schema.encodeEffect(schema)(snapshot.state).pipe(Effect.mapError((cause) => new PersistenceError({
|
|
46
46
|
operation: "saveSnapshot",
|
|
47
47
|
actorId: id,
|
|
48
48
|
cause,
|
|
@@ -61,7 +61,7 @@ const make = Effect.gen(function* () {
|
|
|
61
61
|
const actorStorage = yield* getOrCreateStorage(id);
|
|
62
62
|
if (Option.isNone(actorStorage.snapshot)) return Option.none();
|
|
63
63
|
const stored = actorStorage.snapshot.value;
|
|
64
|
-
const decoded = yield* Schema.
|
|
64
|
+
const decoded = yield* Schema.decodeEffect(schema)(stored.data).pipe(Effect.mapError((cause) => new PersistenceError({
|
|
65
65
|
operation: "loadSnapshot",
|
|
66
66
|
actorId: id,
|
|
67
67
|
cause,
|
|
@@ -75,7 +75,7 @@ const make = Effect.gen(function* () {
|
|
|
75
75
|
}),
|
|
76
76
|
appendEvent: Effect.fn("effect-machine.persistence.inMemory.appendEvent")(function* (id, event, schema) {
|
|
77
77
|
yield* getOrCreateStorage(id);
|
|
78
|
-
const encoded = yield* Schema.
|
|
78
|
+
const encoded = yield* Schema.encodeEffect(schema)(event.event).pipe(Effect.mapError((cause) => new PersistenceError({
|
|
79
79
|
operation: "appendEvent",
|
|
80
80
|
actorId: id,
|
|
81
81
|
cause,
|
|
@@ -95,7 +95,7 @@ const make = Effect.gen(function* () {
|
|
|
95
95
|
const decoded = [];
|
|
96
96
|
for (const stored of actorStorage.events) {
|
|
97
97
|
if (afterVersion !== void 0 && stored.version <= afterVersion) continue;
|
|
98
|
-
const event = yield* Schema.
|
|
98
|
+
const event = yield* Schema.decodeEffect(schema)(stored.data).pipe(Effect.mapError((cause) => new PersistenceError({
|
|
99
99
|
operation: "loadEvents",
|
|
100
100
|
actorId: id,
|
|
101
101
|
cause,
|
|
@@ -31,7 +31,7 @@ const replayEvents = Effect.fn("effect-machine.persistentActor.replayEvents")(fu
|
|
|
31
31
|
/**
|
|
32
32
|
* Build PersistentActorRef with all methods
|
|
33
33
|
*/
|
|
34
|
-
const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter, system) => {
|
|
34
|
+
const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter, system, childrenMap) => {
|
|
35
35
|
const { machine, persistence } = persistentMachine;
|
|
36
36
|
const typedMachine = machine;
|
|
37
37
|
const persist = Effect.gen(function* () {
|
|
@@ -71,7 +71,7 @@ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, ev
|
|
|
71
71
|
}
|
|
72
72
|
});
|
|
73
73
|
return {
|
|
74
|
-
...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop, system),
|
|
74
|
+
...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap),
|
|
75
75
|
persist,
|
|
76
76
|
version,
|
|
77
77
|
replayTo
|
|
@@ -92,12 +92,21 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
92
92
|
const inspector = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
|
|
93
93
|
const eventQueue = yield* Queue.unbounded();
|
|
94
94
|
const stoppedRef = yield* Ref.make(false);
|
|
95
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
95
96
|
const self = {
|
|
96
97
|
send: Effect.fn("effect-machine.persistentActor.self.send")(function* (event) {
|
|
97
98
|
if (yield* Ref.get(stoppedRef)) return;
|
|
98
99
|
yield* Queue.offer(eventQueue, event);
|
|
99
100
|
}),
|
|
100
|
-
spawn: (childId, childMachine) =>
|
|
101
|
+
spawn: (childId, childMachine) => Effect.gen(function* () {
|
|
102
|
+
const child = yield* system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system));
|
|
103
|
+
childrenMap.set(childId, child);
|
|
104
|
+
const maybeScope = yield* Effect.serviceOption(Scope.Scope);
|
|
105
|
+
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, Effect.sync(() => {
|
|
106
|
+
childrenMap.delete(childId);
|
|
107
|
+
}));
|
|
108
|
+
return child;
|
|
109
|
+
})
|
|
101
110
|
};
|
|
102
111
|
let resolvedInitial;
|
|
103
112
|
let initialVersion;
|
|
@@ -130,10 +139,10 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
130
139
|
}));
|
|
131
140
|
const snapshotEnabledRef = yield* Ref.make(true);
|
|
132
141
|
const persistenceQueue = yield* Queue.unbounded();
|
|
133
|
-
const persistenceFiber = yield* Effect.
|
|
142
|
+
const persistenceFiber = yield* Effect.forkDetach(persistenceWorker(persistenceQueue));
|
|
134
143
|
yield* Queue.offer(persistenceQueue, saveMetadata(id, resolvedInitial, initialVersion, createdAt, persistence, adapter));
|
|
135
144
|
const snapshotQueue = yield* Queue.unbounded();
|
|
136
|
-
const snapshotFiber = yield* Effect.
|
|
145
|
+
const snapshotFiber = yield* Effect.forkDetach(snapshotWorker(id, persistence, adapter, snapshotQueue, snapshotEnabledRef));
|
|
137
146
|
const backgroundFibers = [];
|
|
138
147
|
const initEvent = { _tag: INTERNAL_INIT_EVENT };
|
|
139
148
|
const initCtx = {
|
|
@@ -144,7 +153,7 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
144
153
|
};
|
|
145
154
|
const { effects: effectSlots } = typedMachine._slots;
|
|
146
155
|
for (const bg of typedMachine.backgroundEffects) {
|
|
147
|
-
const fiber = yield* Effect.
|
|
156
|
+
const fiber = yield* Effect.forkDetach(bg.handler({
|
|
148
157
|
state: resolvedInitial,
|
|
149
158
|
event: initEvent,
|
|
150
159
|
self,
|
|
@@ -167,9 +176,9 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
167
176
|
finalState: resolvedInitial,
|
|
168
177
|
timestamp
|
|
169
178
|
}));
|
|
170
|
-
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system);
|
|
179
|
+
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap);
|
|
171
180
|
}
|
|
172
|
-
const loopFiber = yield* Effect.
|
|
181
|
+
const loopFiber = yield* Effect.forkDetach(persistentEventLoop(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector, system));
|
|
173
182
|
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
|
|
174
183
|
const finalState = yield* SubscriptionRef.get(stateRef);
|
|
175
184
|
yield* emitWithTimestamp(inspector, (timestamp) => ({
|
|
@@ -184,7 +193,7 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
184
193
|
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
185
194
|
yield* Fiber.interrupt(snapshotFiber);
|
|
186
195
|
yield* Fiber.interrupt(persistenceFiber);
|
|
187
|
-
}).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system);
|
|
196
|
+
}).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap);
|
|
188
197
|
});
|
|
189
198
|
/**
|
|
190
199
|
* Main event loop for persistent actor
|
|
@@ -241,7 +250,7 @@ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop"
|
|
|
241
250
|
version: newVersion,
|
|
242
251
|
timestamp: yield* now
|
|
243
252
|
};
|
|
244
|
-
const journalTask = adapter.appendEvent(id, persistedEvent, persistence.eventSchema).pipe(Effect.
|
|
253
|
+
const journalTask = adapter.appendEvent(id, persistedEvent, persistence.eventSchema).pipe(Effect.catchEager((e) => Effect.logWarning(`Failed to journal event for actor ${id}`, e)), Effect.asVoid);
|
|
245
254
|
yield* Queue.offer(persistenceQueue, journalTask);
|
|
246
255
|
}
|
|
247
256
|
yield* Queue.offer(persistenceQueue, saveMetadata(id, result.newState, newVersion, createdAt, persistence, adapter));
|
|
@@ -297,11 +306,11 @@ const persistenceWorker = Effect.fn("effect-machine.persistentActor.persistenceW
|
|
|
297
306
|
* Snapshot scheduler worker (runs in background).
|
|
298
307
|
*/
|
|
299
308
|
const snapshotWorker = Effect.fn("effect-machine.persistentActor.snapshotWorker")(function* (id, persistence, adapter, queue, enabledRef) {
|
|
300
|
-
const
|
|
309
|
+
const step = yield* Schedule.toStep(persistence.snapshotSchedule);
|
|
301
310
|
while (true) {
|
|
302
311
|
const { state, version } = yield* Queue.take(queue);
|
|
303
312
|
if (!(yield* Ref.get(enabledRef))) continue;
|
|
304
|
-
if (!(yield*
|
|
313
|
+
if (!(yield* step(yield* Clock.currentTimeMillis, state).pipe(Effect.match({
|
|
305
314
|
onFailure: () => false,
|
|
306
315
|
onSuccess: () => true
|
|
307
316
|
})))) {
|
|
@@ -321,7 +330,7 @@ const saveSnapshot = Effect.fn("effect-machine.persistentActor.saveSnapshot")(fu
|
|
|
321
330
|
version,
|
|
322
331
|
timestamp: yield* now
|
|
323
332
|
};
|
|
324
|
-
yield* adapter.saveSnapshot(id, snapshot, persistence.stateSchema).pipe(Effect.
|
|
333
|
+
yield* adapter.saveSnapshot(id, snapshot, persistence.stateSchema).pipe(Effect.catchEager((e) => Effect.logWarning(`Failed to save snapshot for actor ${id}`, e)));
|
|
325
334
|
});
|
|
326
335
|
/**
|
|
327
336
|
* Save or update actor metadata if adapter supports registry.
|
|
@@ -338,7 +347,7 @@ const saveMetadata = Effect.fn("effect-machine.persistentActor.saveMetadata")(fu
|
|
|
338
347
|
lastActivityAt,
|
|
339
348
|
version,
|
|
340
349
|
stateTag: state._tag
|
|
341
|
-
}).pipe(Effect.
|
|
350
|
+
}).pipe(Effect.catchEager((e) => Effect.logWarning(`Failed to save metadata for actor ${id}`, e)));
|
|
342
351
|
});
|
|
343
352
|
/**
|
|
344
353
|
* Restore an actor from persistence.
|
|
@@ -16,7 +16,7 @@ type BrandedEvent = {
|
|
|
16
16
|
* Note: Schema types S and E should match the structural shape of the machine's
|
|
17
17
|
* state and event types (without brands). The schemas don't know about brands.
|
|
18
18
|
*/
|
|
19
|
-
interface PersistenceConfig<S, E
|
|
19
|
+
interface PersistenceConfig<S, E> {
|
|
20
20
|
/**
|
|
21
21
|
* Schedule controlling when snapshots are taken.
|
|
22
22
|
* Input is the new state after each transition.
|
|
@@ -36,12 +36,12 @@ interface PersistenceConfig<S, E, SSI = unknown, ESI = unknown> {
|
|
|
36
36
|
* Schema for serializing/deserializing state.
|
|
37
37
|
* Always present at runtime (resolved from config or machine).
|
|
38
38
|
*/
|
|
39
|
-
readonly stateSchema: Schema.
|
|
39
|
+
readonly stateSchema: Schema.Codec<S, unknown, never, never>;
|
|
40
40
|
/**
|
|
41
41
|
* Schema for serializing/deserializing events.
|
|
42
42
|
* Always present at runtime (resolved from config or machine).
|
|
43
43
|
*/
|
|
44
|
-
readonly eventSchema: Schema.
|
|
44
|
+
readonly eventSchema: Schema.Codec<E, unknown, never, never>;
|
|
45
45
|
/**
|
|
46
46
|
* User-provided identifier for the machine type.
|
|
47
47
|
* Used for filtering actors in restoreAll.
|
package/dist/schema.d.ts
CHANGED
|
@@ -36,8 +36,8 @@ type IsEmptyFields<Fields extends Schema.Struct.Fields> = keyof Fields extends n
|
|
|
36
36
|
*/
|
|
37
37
|
type VariantConstructors<D extends Record<string, Schema.Struct.Fields>, Brand> = { readonly [K in keyof D & string]: IsEmptyFields<D[K]> extends true ? TaggedStructType<K, D[K]> & Brand & {
|
|
38
38
|
readonly derive: (source: object) => TaggedStructType<K, D[K]> & Brand;
|
|
39
|
-
} : ((args: Schema.Struct.
|
|
40
|
-
readonly derive: (source: object, partial?: Partial<Schema.Struct.
|
|
39
|
+
} : ((args: Schema.Struct.Type<D[K]>) => TaggedStructType<K, D[K]> & Brand) & {
|
|
40
|
+
readonly derive: (source: object, partial?: Partial<Schema.Struct.Type<D[K]>>) => TaggedStructType<K, D[K]> & Brand;
|
|
41
41
|
readonly _tag: K;
|
|
42
42
|
} };
|
|
43
43
|
/**
|
|
@@ -78,14 +78,14 @@ interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Bran
|
|
|
78
78
|
* The D type parameter captures the definition, creating a unique brand
|
|
79
79
|
* per distinct schema definition shape.
|
|
80
80
|
*/
|
|
81
|
-
type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.
|
|
81
|
+
type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Codec<VariantsUnion<D> & FullStateBrand<D>, unknown, never, never> & MachineSchemaBase<D, FullStateBrand<D>> & VariantConstructors<D, FullStateBrand<D>>;
|
|
82
82
|
/**
|
|
83
83
|
* Schema-first event definition (same structure as state, different brand)
|
|
84
84
|
*
|
|
85
85
|
* The D type parameter captures the definition, creating a unique brand
|
|
86
86
|
* per distinct schema definition shape.
|
|
87
87
|
*/
|
|
88
|
-
type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.
|
|
88
|
+
type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Codec<VariantsUnion<D> & FullEventBrand<D>, unknown, never, never> & MachineSchemaBase<D, FullEventBrand<D>> & VariantConstructors<D, FullEventBrand<D>>;
|
|
89
89
|
/**
|
|
90
90
|
* Create a schema-first State definition.
|
|
91
91
|
*
|
package/dist/schema.js
CHANGED
|
@@ -69,8 +69,8 @@ const buildMachineSchema = (definition) => {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
const variantArray = Object.values(variants);
|
|
72
|
-
if (variantArray.length === 0) throw new InvalidSchemaError();
|
|
73
|
-
const unionSchema = variantArray.length === 1 ? variantArray[0] : Schema.Union(
|
|
72
|
+
if (variantArray.length === 0) throw new InvalidSchemaError({});
|
|
73
|
+
const unionSchema = variantArray.length === 1 ? variantArray[0] : Schema.Union(variantArray);
|
|
74
74
|
const $is = (tag) => (u) => typeof u === "object" && u !== null && "_tag" in u && u._tag === tag;
|
|
75
75
|
const $match = (valueOrCases, maybeCases) => {
|
|
76
76
|
if (maybeCases !== void 0) {
|
package/dist/slot.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ActorSystem } from "./actor.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Schema, ServiceMap } from "effect";
|
|
3
3
|
|
|
4
4
|
//#region src/slot.d.ts
|
|
5
5
|
/** Schema fields definition (like Schema.Struct.Fields) */
|
|
6
|
-
type Fields = Record<string, Schema.
|
|
6
|
+
type Fields = Record<string, Schema.Top>;
|
|
7
7
|
/** Extract the encoded type from schema fields (used for parameters) */
|
|
8
8
|
type FieldsToParams<F extends Fields> = keyof F extends never ? void : Schema.Schema.Type<Schema.Struct<F>>;
|
|
9
9
|
/**
|
|
@@ -53,7 +53,7 @@ interface MachineContext<State, Event, Self> {
|
|
|
53
53
|
* Single module-level tag instead of per-machine allocation.
|
|
54
54
|
* @internal
|
|
55
55
|
*/
|
|
56
|
-
declare const MachineContextTag:
|
|
56
|
+
declare const MachineContextTag: ServiceMap.Service<MachineContext<any, any, any>, MachineContext<any, any, any>>;
|
|
57
57
|
/**
|
|
58
58
|
* Guard handler implementation.
|
|
59
59
|
* Receives params and context, returns Effect<boolean>.
|
package/dist/slot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ServiceMap } from "effect";
|
|
2
2
|
|
|
3
3
|
//#region src/slot.ts
|
|
4
4
|
/**
|
|
@@ -41,7 +41,7 @@ import { Context } from "effect";
|
|
|
41
41
|
* Single module-level tag instead of per-machine allocation.
|
|
42
42
|
* @internal
|
|
43
43
|
*/
|
|
44
|
-
const MachineContextTag =
|
|
44
|
+
const MachineContextTag = ServiceMap.Service("@effect-machine/Context");
|
|
45
45
|
/**
|
|
46
46
|
* Generic slot schema factory. Used internally by Guards() and Effects().
|
|
47
47
|
* @internal
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (!no_symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { __exportAll };
|