effect-machine 0.3.1 → 0.4.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 +24 -0
- package/dist/_virtual/_rolldown/runtime.js +18 -0
- package/dist/actor.d.ts +256 -0
- package/dist/actor.js +402 -0
- package/dist/cluster/entity-machine.d.ts +90 -0
- package/dist/cluster/entity-machine.js +80 -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 +160 -0
- package/dist/internal/transition.js +238 -0
- package/dist/internal/utils.d.ts +60 -0
- package/dist/internal/utils.js +46 -0
- package/dist/machine.d.ts +278 -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 +358 -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 +130 -0
- package/dist/slot.js +99 -0
- package/dist/testing.d.ts +142 -0
- package/dist/testing.js +138 -0
- package/package.json +28 -14
- 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,294 +0,0 @@
|
|
|
1
|
-
import { Effect, Layer, Option, Ref, Schema } from "effect";
|
|
2
|
-
|
|
3
|
-
import type { ActorMetadata, PersistedEvent, PersistenceAdapter, Snapshot } from "../adapter.js";
|
|
4
|
-
import { PersistenceAdapterTag, PersistenceError, VersionConflictError } from "../adapter.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* In-memory storage for a single actor
|
|
8
|
-
*/
|
|
9
|
-
interface ActorStorage {
|
|
10
|
-
snapshot: Option.Option<{
|
|
11
|
-
readonly data: unknown;
|
|
12
|
-
readonly version: number;
|
|
13
|
-
readonly timestamp: number;
|
|
14
|
-
}>;
|
|
15
|
-
events: Array<{ readonly data: unknown; readonly version: number; readonly timestamp: number }>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Create an in-memory persistence adapter.
|
|
20
|
-
* Useful for testing and development.
|
|
21
|
-
*/
|
|
22
|
-
const make = Effect.gen(function* () {
|
|
23
|
-
const storage = yield* Ref.make(new Map<string, ActorStorage>());
|
|
24
|
-
const registry = yield* Ref.make(new Map<string, ActorMetadata>());
|
|
25
|
-
|
|
26
|
-
const getOrCreateStorage = Effect.fn("effect-machine.persistence.inMemory.getOrCreateStorage")(
|
|
27
|
-
function* (id: string) {
|
|
28
|
-
return yield* Ref.modify(storage, (map) => {
|
|
29
|
-
const existing = map.get(id);
|
|
30
|
-
if (existing !== undefined) {
|
|
31
|
-
return [existing, map];
|
|
32
|
-
}
|
|
33
|
-
const newStorage: ActorStorage = {
|
|
34
|
-
snapshot: Option.none(),
|
|
35
|
-
events: [],
|
|
36
|
-
};
|
|
37
|
-
const newMap = new Map(map);
|
|
38
|
-
newMap.set(id, newStorage);
|
|
39
|
-
return [newStorage, newMap];
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const updateStorage = Effect.fn("effect-machine.persistence.inMemory.updateStorage")(function* (
|
|
45
|
-
id: string,
|
|
46
|
-
update: (storage: ActorStorage) => ActorStorage,
|
|
47
|
-
) {
|
|
48
|
-
yield* Ref.update(storage, (map) => {
|
|
49
|
-
const existing = map.get(id);
|
|
50
|
-
if (existing === undefined) {
|
|
51
|
-
return map;
|
|
52
|
-
}
|
|
53
|
-
const newMap = new Map(map);
|
|
54
|
-
newMap.set(id, update(existing));
|
|
55
|
-
return newMap;
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const saveSnapshot = Effect.fn("effect-machine.persistence.inMemory.saveSnapshot")(function* <
|
|
60
|
-
S,
|
|
61
|
-
SI,
|
|
62
|
-
>(id: string, snapshot: Snapshot<S>, schema: Schema.Schema<S, SI, never>) {
|
|
63
|
-
const actorStorage = yield* getOrCreateStorage(id);
|
|
64
|
-
|
|
65
|
-
// Optimistic locking: check version
|
|
66
|
-
// Reject only if trying to save an older version (strict <)
|
|
67
|
-
// Same-version saves are idempotent (allow retries/multiple callers)
|
|
68
|
-
if (Option.isSome(actorStorage.snapshot)) {
|
|
69
|
-
const existingVersion = actorStorage.snapshot.value.version;
|
|
70
|
-
if (snapshot.version < existingVersion) {
|
|
71
|
-
return yield* new VersionConflictError({
|
|
72
|
-
actorId: id,
|
|
73
|
-
expectedVersion: existingVersion,
|
|
74
|
-
actualVersion: snapshot.version,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Encode state using schema
|
|
80
|
-
const encoded = yield* Schema.encode(schema)(snapshot.state).pipe(
|
|
81
|
-
Effect.mapError(
|
|
82
|
-
(cause) =>
|
|
83
|
-
new PersistenceError({
|
|
84
|
-
operation: "saveSnapshot",
|
|
85
|
-
actorId: id,
|
|
86
|
-
cause,
|
|
87
|
-
message: "Failed to encode state",
|
|
88
|
-
}),
|
|
89
|
-
),
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
yield* updateStorage(id, (s) => ({
|
|
93
|
-
...s,
|
|
94
|
-
snapshot: Option.some({
|
|
95
|
-
data: encoded,
|
|
96
|
-
version: snapshot.version,
|
|
97
|
-
timestamp: snapshot.timestamp,
|
|
98
|
-
}),
|
|
99
|
-
}));
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
const loadSnapshot = Effect.fn("effect-machine.persistence.inMemory.loadSnapshot")(function* <
|
|
103
|
-
S,
|
|
104
|
-
SI,
|
|
105
|
-
>(id: string, schema: Schema.Schema<S, SI, never>) {
|
|
106
|
-
const actorStorage = yield* getOrCreateStorage(id);
|
|
107
|
-
|
|
108
|
-
if (Option.isNone(actorStorage.snapshot)) {
|
|
109
|
-
return Option.none();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const stored = actorStorage.snapshot.value;
|
|
113
|
-
|
|
114
|
-
// Decode state using schema
|
|
115
|
-
const decoded = yield* Schema.decode(schema)(stored.data as SI).pipe(
|
|
116
|
-
Effect.mapError(
|
|
117
|
-
(cause) =>
|
|
118
|
-
new PersistenceError({
|
|
119
|
-
operation: "loadSnapshot",
|
|
120
|
-
actorId: id,
|
|
121
|
-
cause,
|
|
122
|
-
message: "Failed to decode state",
|
|
123
|
-
}),
|
|
124
|
-
),
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
return Option.some({
|
|
128
|
-
state: decoded,
|
|
129
|
-
version: stored.version,
|
|
130
|
-
timestamp: stored.timestamp,
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
const appendEvent = Effect.fn("effect-machine.persistence.inMemory.appendEvent")(function* <
|
|
135
|
-
E,
|
|
136
|
-
EI,
|
|
137
|
-
>(id: string, event: PersistedEvent<E>, schema: Schema.Schema<E, EI, never>) {
|
|
138
|
-
yield* getOrCreateStorage(id);
|
|
139
|
-
|
|
140
|
-
// Encode event using schema
|
|
141
|
-
const encoded = yield* Schema.encode(schema)(event.event).pipe(
|
|
142
|
-
Effect.mapError(
|
|
143
|
-
(cause) =>
|
|
144
|
-
new PersistenceError({
|
|
145
|
-
operation: "appendEvent",
|
|
146
|
-
actorId: id,
|
|
147
|
-
cause,
|
|
148
|
-
message: "Failed to encode event",
|
|
149
|
-
}),
|
|
150
|
-
),
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
yield* updateStorage(id, (s) => ({
|
|
154
|
-
...s,
|
|
155
|
-
events: [
|
|
156
|
-
...s.events,
|
|
157
|
-
{
|
|
158
|
-
data: encoded,
|
|
159
|
-
version: event.version,
|
|
160
|
-
timestamp: event.timestamp,
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
}));
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const loadEvents = Effect.fn("effect-machine.persistence.inMemory.loadEvents")(function* <E, EI>(
|
|
167
|
-
id: string,
|
|
168
|
-
schema: Schema.Schema<E, EI, never>,
|
|
169
|
-
afterVersion?: number,
|
|
170
|
-
) {
|
|
171
|
-
const actorStorage = yield* getOrCreateStorage(id);
|
|
172
|
-
|
|
173
|
-
// Single pass - skip filtered events inline instead of creating intermediate array
|
|
174
|
-
const decoded: PersistedEvent<E>[] = [];
|
|
175
|
-
for (const stored of actorStorage.events) {
|
|
176
|
-
if (afterVersion !== undefined && stored.version <= afterVersion) continue;
|
|
177
|
-
|
|
178
|
-
const event = yield* Schema.decode(schema)(stored.data as EI).pipe(
|
|
179
|
-
Effect.mapError(
|
|
180
|
-
(cause) =>
|
|
181
|
-
new PersistenceError({
|
|
182
|
-
operation: "loadEvents",
|
|
183
|
-
actorId: id,
|
|
184
|
-
cause,
|
|
185
|
-
message: "Failed to decode event",
|
|
186
|
-
}),
|
|
187
|
-
),
|
|
188
|
-
);
|
|
189
|
-
decoded.push({
|
|
190
|
-
event,
|
|
191
|
-
version: stored.version,
|
|
192
|
-
timestamp: stored.timestamp,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return decoded;
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const deleteActor = Effect.fn("effect-machine.persistence.inMemory.deleteActor")(function* (
|
|
200
|
-
id: string,
|
|
201
|
-
) {
|
|
202
|
-
yield* Ref.update(storage, (map) => {
|
|
203
|
-
const newMap = new Map(map);
|
|
204
|
-
newMap.delete(id);
|
|
205
|
-
return newMap;
|
|
206
|
-
});
|
|
207
|
-
// Also delete metadata
|
|
208
|
-
yield* Ref.update(registry, (map) => {
|
|
209
|
-
const newMap = new Map(map);
|
|
210
|
-
newMap.delete(id);
|
|
211
|
-
return newMap;
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
const listActors = Effect.fn("effect-machine.persistence.inMemory.listActors")(function* () {
|
|
216
|
-
const map = yield* Ref.get(registry);
|
|
217
|
-
return Array.from(map.values());
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
const saveMetadata = Effect.fn("effect-machine.persistence.inMemory.saveMetadata")(function* (
|
|
221
|
-
metadata: ActorMetadata,
|
|
222
|
-
) {
|
|
223
|
-
yield* Ref.update(registry, (map) => {
|
|
224
|
-
const newMap = new Map(map);
|
|
225
|
-
newMap.set(metadata.id, metadata);
|
|
226
|
-
return newMap;
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
const deleteMetadata = Effect.fn("effect-machine.persistence.inMemory.deleteMetadata")(function* (
|
|
231
|
-
id: string,
|
|
232
|
-
) {
|
|
233
|
-
yield* Ref.update(registry, (map) => {
|
|
234
|
-
const newMap = new Map(map);
|
|
235
|
-
newMap.delete(id);
|
|
236
|
-
return newMap;
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
const loadMetadata = Effect.fn("effect-machine.persistence.inMemory.loadMetadata")(function* (
|
|
241
|
-
id: string,
|
|
242
|
-
) {
|
|
243
|
-
const map = yield* Ref.get(registry);
|
|
244
|
-
const meta = map.get(id);
|
|
245
|
-
return meta !== undefined ? Option.some(meta) : Option.none();
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const adapter: PersistenceAdapter = {
|
|
249
|
-
saveSnapshot,
|
|
250
|
-
loadSnapshot,
|
|
251
|
-
appendEvent,
|
|
252
|
-
loadEvents,
|
|
253
|
-
deleteActor,
|
|
254
|
-
|
|
255
|
-
// Registry methods for actor discovery
|
|
256
|
-
listActors,
|
|
257
|
-
saveMetadata,
|
|
258
|
-
deleteMetadata,
|
|
259
|
-
loadMetadata,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
return adapter;
|
|
263
|
-
}).pipe(Effect.withSpan("effect-machine.persistence.inMemory.make"));
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Create an in-memory persistence adapter effect.
|
|
267
|
-
* Returns the adapter directly for custom layer composition.
|
|
268
|
-
*/
|
|
269
|
-
export const makeInMemoryPersistenceAdapter = make;
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* In-memory persistence adapter layer.
|
|
273
|
-
* Data is not persisted across process restarts.
|
|
274
|
-
*
|
|
275
|
-
* NOTE: Each `Effect.provide(InMemoryPersistenceAdapter)` creates a NEW adapter
|
|
276
|
-
* with empty storage. For tests that need persistent storage across multiple
|
|
277
|
-
* runPromise calls, use `makeInMemoryPersistenceAdapter` with a shared scope.
|
|
278
|
-
*
|
|
279
|
-
* @example
|
|
280
|
-
* ```ts
|
|
281
|
-
* const program = Effect.gen(function* () {
|
|
282
|
-
* const system = yield* ActorSystemService;
|
|
283
|
-
* const actor = yield* system.spawn("my-actor", persistentMachine);
|
|
284
|
-
* // ...
|
|
285
|
-
* }).pipe(
|
|
286
|
-
* Effect.provide(InMemoryPersistenceAdapter),
|
|
287
|
-
* Effect.provide(ActorSystemDefault),
|
|
288
|
-
* );
|
|
289
|
-
* ```
|
|
290
|
-
*/
|
|
291
|
-
export const InMemoryPersistenceAdapter: Layer.Layer<PersistenceAdapterTag> = Layer.effect(
|
|
292
|
-
PersistenceAdapterTag,
|
|
293
|
-
make,
|
|
294
|
-
);
|
package/src/persistence/index.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// Core types
|
|
2
|
-
export type {
|
|
3
|
-
ActorMetadata,
|
|
4
|
-
PersistedEvent,
|
|
5
|
-
PersistenceAdapter,
|
|
6
|
-
RestoreFailure,
|
|
7
|
-
RestoreResult,
|
|
8
|
-
Snapshot,
|
|
9
|
-
} from "./adapter.js";
|
|
10
|
-
export { PersistenceAdapterTag, PersistenceError, VersionConflictError } from "./adapter.js";
|
|
11
|
-
|
|
12
|
-
// Persistent machine
|
|
13
|
-
export type { PersistenceConfig, PersistentMachine } from "./persistent-machine.js";
|
|
14
|
-
export { isPersistentMachine, persist } from "./persistent-machine.js";
|
|
15
|
-
|
|
16
|
-
// Persistent actor
|
|
17
|
-
export type { PersistentActorRef } from "./persistent-actor.js";
|
|
18
|
-
export { createPersistentActor, restorePersistentActor } from "./persistent-actor.js";
|
|
19
|
-
|
|
20
|
-
// Adapters
|
|
21
|
-
export {
|
|
22
|
-
InMemoryPersistenceAdapter,
|
|
23
|
-
makeInMemoryPersistenceAdapter,
|
|
24
|
-
} from "./adapters/in-memory.js";
|