@typeonce/effect-machine 0.1.0 → 0.3.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/NOTICE +0 -2
- package/README.md +464 -53
- package/dist/AtomMachine.d.ts +189 -10
- package/dist/AtomMachine.d.ts.map +1 -1
- package/dist/AtomMachine.js +145 -18
- package/dist/AtomMachine.js.map +1 -1
- package/dist/ClusterMachine.d.ts +17 -7
- package/dist/ClusterMachine.d.ts.map +1 -1
- package/dist/ClusterMachine.js +2 -2
- package/dist/ClusterMachine.js.map +1 -1
- package/dist/Machine.d.ts +949 -202
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +245 -82
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machineErrors.d.ts +2 -2
- package/dist/internal/machineErrors.d.ts.map +1 -1
- package/dist/internal/machineModel.d.ts +52 -4
- package/dist/internal/machineModel.d.ts.map +1 -1
- package/dist/internal/machineModel.js +576 -29
- package/dist/internal/machineModel.js.map +1 -1
- package/dist/internal/machinePlanner.d.ts +15 -5
- package/dist/internal/machinePlanner.d.ts.map +1 -1
- package/dist/internal/machinePlanner.js +260 -57
- package/dist/internal/machinePlanner.js.map +1 -1
- package/dist/internal/machineProcess.d.ts +2 -2
- package/dist/internal/machineProcess.d.ts.map +1 -1
- package/dist/internal/machineProcess.js +22 -11
- package/dist/internal/machineProcess.js.map +1 -1
- package/dist/internal/machineRuntime.d.ts +11 -5
- package/dist/internal/machineRuntime.d.ts.map +1 -1
- package/dist/internal/machineRuntime.js +24 -13
- package/dist/internal/machineRuntime.js.map +1 -1
- package/docs/agent-guide.md +680 -0
- package/package.json +20 -22
package/dist/Machine.js
CHANGED
|
@@ -104,6 +104,7 @@ const ActionRequirementTypeId = "~effect/Machine/ActionRequirement";
|
|
|
104
104
|
const RuntimeCompatibilityErrorTypeId = "~effect/Machine/RuntimeCompatibilityError";
|
|
105
105
|
const InvokeTypeId = Symbol.for("effect/Machine/Invoke");
|
|
106
106
|
const SnapshotBuilderStateTypeId = Symbol("effect/Machine/SnapshotBuilderState");
|
|
107
|
+
const SnapshotBuilderConstructionTypeId = Symbol("effect/Machine/SnapshotBuilderConstruction");
|
|
107
108
|
const ChildAddressTypeId = "~effect/Machine/ChildAddress";
|
|
108
109
|
const ChildAddressCompatibilityErrorTypeId = "~effect/Machine/ChildAddressCompatibilityError";
|
|
109
110
|
const ChildMachineTypeId = "~effect/Machine/ChildMachine";
|
|
@@ -121,6 +122,7 @@ const cloneWithHandlers = (self, handlers) => {
|
|
|
121
122
|
const machine = Object.create(Proto);
|
|
122
123
|
machine.states = self.states;
|
|
123
124
|
machine.events = self.events;
|
|
125
|
+
machine.internalEvents = self.internalEvents;
|
|
124
126
|
machine.emits = self.emits;
|
|
125
127
|
machine.input = self.input;
|
|
126
128
|
machine.id = self.id;
|
|
@@ -129,6 +131,7 @@ const cloneWithHandlers = (self, handlers) => {
|
|
|
129
131
|
machine.makeTargetBuilder = self.makeTargetBuilder;
|
|
130
132
|
machine.handlers = handlers;
|
|
131
133
|
machine.handle = makeHandle(machine);
|
|
134
|
+
Model.copyProtocol(self, machine);
|
|
132
135
|
return machine;
|
|
133
136
|
};
|
|
134
137
|
const flattenHandlers = (handlers, states, prefix, config) => {
|
|
@@ -174,10 +177,27 @@ export const isMachine = (u) => hasProperty(u, TypeId);
|
|
|
174
177
|
* @since 4.0.0
|
|
175
178
|
*/
|
|
176
179
|
export const isFinal = (machine, state) => internalPlanner.isFinal(machine, state);
|
|
180
|
+
const withFrom = (method, kind) => {
|
|
181
|
+
Object.defineProperty(method, "from", {
|
|
182
|
+
value: (...args) => {
|
|
183
|
+
const omitted = args.length === 0 || (kind === "nested" && args.length === 1 && typeof args[0] === "function");
|
|
184
|
+
const input = omitted ? {} : args[0];
|
|
185
|
+
const rest = omitted ? args : args.slice(1);
|
|
186
|
+
return Model.markStateConstruction(method(Model.makeStateInput(input), ...rest));
|
|
187
|
+
},
|
|
188
|
+
enumerable: false
|
|
189
|
+
});
|
|
190
|
+
return method;
|
|
191
|
+
};
|
|
177
192
|
const makeSnapshotBuilder = (states, options) => {
|
|
178
193
|
const builder = {};
|
|
179
194
|
for (const key of Object.keys(states)) {
|
|
180
|
-
|
|
195
|
+
if (states[key].type === "history") {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const path = options.prefix === "" ? key : `${options.prefix}.${key}`;
|
|
199
|
+
const node = Model.getStateNodeDefinition(path, states[key]);
|
|
200
|
+
builder[key] = withFrom((value, selector) => makeSnapshotForNode(states[key], key, value, selector, options), node.states === undefined ? "leaf" : "nested");
|
|
181
201
|
}
|
|
182
202
|
return builder;
|
|
183
203
|
};
|
|
@@ -187,18 +207,28 @@ const makeParallelSnapshotBuilder = (states, options, regions) => {
|
|
|
187
207
|
value: regions,
|
|
188
208
|
enumerable: false
|
|
189
209
|
});
|
|
210
|
+
Object.defineProperty(builder, SnapshotBuilderConstructionTypeId, {
|
|
211
|
+
value: Model.isStateConstruction(builder),
|
|
212
|
+
enumerable: false
|
|
213
|
+
});
|
|
190
214
|
for (const key of Object.keys(states)) {
|
|
215
|
+
if (states[key].type === "history") {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
191
218
|
if (hasProperty(regions, key)) {
|
|
192
219
|
continue;
|
|
193
220
|
}
|
|
194
|
-
|
|
221
|
+
const path = options.prefix === "" ? key : `${options.prefix}.${key}`;
|
|
222
|
+
const node = Model.getStateNodeDefinition(path, states[key]);
|
|
223
|
+
builder[key] = withFrom((value, selector) => {
|
|
195
224
|
const nextRegions = {};
|
|
196
225
|
for (const regionKey of Object.keys(regions)) {
|
|
197
226
|
nextRegions[regionKey] = regions[regionKey];
|
|
198
227
|
}
|
|
199
228
|
nextRegions[key] = makeSnapshotForNode(states[key], key, value, selector, options);
|
|
200
|
-
|
|
201
|
-
|
|
229
|
+
const next = makeParallelSnapshotBuilder(states, options, nextRegions);
|
|
230
|
+
return Model.isStateConstruction(builder) ? Model.markStateConstruction(next) : next;
|
|
231
|
+
}, node.states === undefined ? "leaf" : "nested");
|
|
202
232
|
}
|
|
203
233
|
return builder;
|
|
204
234
|
};
|
|
@@ -208,6 +238,9 @@ const getParallelSnapshotBuilderRegions = (path, states, builder) => {
|
|
|
208
238
|
}
|
|
209
239
|
const regions = builder[SnapshotBuilderStateTypeId];
|
|
210
240
|
for (const key of Object.keys(states)) {
|
|
241
|
+
if (states[key].type === "history") {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
211
244
|
if (!hasProperty(regions, key)) {
|
|
212
245
|
throw new Error(`Machine expected parallel state "${path}" builder callback to provide region "${key}"`);
|
|
213
246
|
}
|
|
@@ -229,14 +262,16 @@ const makeSnapshotForNode = (definition, key, value, selector, options) => {
|
|
|
229
262
|
}
|
|
230
263
|
if (node.type === "parallel") {
|
|
231
264
|
const builder = makeParallelSnapshotBuilder(node.states, { ...options, prefix: path }, {});
|
|
232
|
-
|
|
233
|
-
|
|
265
|
+
const selected = selector(builder);
|
|
266
|
+
snapshot.states = getParallelSnapshotBuilderRegions(path, node.states, selected);
|
|
267
|
+
return Model.isStateConstruction(selected) ? Model.markStateConstruction(snapshot) : snapshot;
|
|
234
268
|
}
|
|
235
269
|
const childStates = options.mode === "initial" && node.initial !== undefined
|
|
236
270
|
? { [node.initial]: node.states[node.initial] }
|
|
237
271
|
: node.states;
|
|
238
|
-
|
|
239
|
-
|
|
272
|
+
const selected = selector(makeSnapshotBuilder(childStates, { ...options, prefix: path }));
|
|
273
|
+
snapshot.state = selected;
|
|
274
|
+
return Model.isStateConstruction(selected) ? Model.markStateConstruction(snapshot) : snapshot;
|
|
240
275
|
};
|
|
241
276
|
const getTargetBuilderNode = (stateNodes, path) => {
|
|
242
277
|
const node = stateNodes.byPath.get(path);
|
|
@@ -263,6 +298,31 @@ const hasTargetValues = (values) => values !== undefined && Object.keys(values).
|
|
|
263
298
|
const makeTargetWithValues = (path, value, values) => hasTargetValues(values)
|
|
264
299
|
? Model.makeTarget(path, value, { values: values })
|
|
265
300
|
: Model.makeTarget(path, value);
|
|
301
|
+
const getTargetBuilderDefinition = (states, targetPath) => {
|
|
302
|
+
let children = states;
|
|
303
|
+
let path = "";
|
|
304
|
+
let definition;
|
|
305
|
+
for (const key of targetPath.split(".")) {
|
|
306
|
+
if (!hasProperty(children, key)) {
|
|
307
|
+
throw new Error(`Machine expected state path "${targetPath}" to exist`);
|
|
308
|
+
}
|
|
309
|
+
definition = children[key];
|
|
310
|
+
path = path === "" ? key : `${path}.${key}`;
|
|
311
|
+
const node = Model.getStateNodeDefinition(path, definition);
|
|
312
|
+
children = node.states ?? {};
|
|
313
|
+
}
|
|
314
|
+
return definition;
|
|
315
|
+
};
|
|
316
|
+
const makeParallelTarget = (states, node, value, selector, values) => {
|
|
317
|
+
if (selector === undefined) {
|
|
318
|
+
throw new Error(`Machine expected parallel target "${node.path}" builder to provide every active region`);
|
|
319
|
+
}
|
|
320
|
+
const snapshot = makeSnapshotForNode(getTargetBuilderDefinition(states, node.path), node.key, value, selector, { mode: "full", prefix: node.parent ?? "" });
|
|
321
|
+
return Model.makeTarget(node.path, value, {
|
|
322
|
+
snapshot: snapshot,
|
|
323
|
+
values: values
|
|
324
|
+
});
|
|
325
|
+
};
|
|
266
326
|
const extendTargetValues = (values, path, value) => {
|
|
267
327
|
const next = {};
|
|
268
328
|
if (values !== undefined) {
|
|
@@ -273,100 +333,122 @@ const extendTargetValues = (values, path, value) => {
|
|
|
273
333
|
next[path] = value;
|
|
274
334
|
return next;
|
|
275
335
|
};
|
|
276
|
-
const makeLocalTargetChildBuilder = (stateNodes, parentPath, values) => {
|
|
336
|
+
const makeLocalTargetChildBuilder = (states, stateNodes, parentPath, values, source) => {
|
|
277
337
|
const parent = getTargetBuilderNode(stateNodes, parentPath);
|
|
278
338
|
const builder = {};
|
|
279
339
|
for (const childPath of parent.children) {
|
|
280
340
|
const child = getTargetBuilderNode(stateNodes, childPath);
|
|
281
|
-
builder[child.key] = (value, selector) => {
|
|
341
|
+
builder[child.key] = withFrom((value, selector) => {
|
|
282
342
|
if (child.type === "atomic" || child.type === "final") {
|
|
283
343
|
return makeTargetWithValues(child.path, value, values);
|
|
284
344
|
}
|
|
345
|
+
if (child.type === "parallel") {
|
|
346
|
+
if (source !== child.path && !source.startsWith(`${child.path}.`)) {
|
|
347
|
+
return makeParallelTarget(states, child, value, selector, values);
|
|
348
|
+
}
|
|
349
|
+
if (selector === undefined) {
|
|
350
|
+
throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`);
|
|
351
|
+
}
|
|
352
|
+
return selector(makeLocalTargetChildBuilder(states, stateNodes, child.path, extendTargetValues(values, child.path, value), source));
|
|
353
|
+
}
|
|
285
354
|
if (selector === undefined) {
|
|
286
355
|
throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`);
|
|
287
356
|
}
|
|
288
|
-
return selector(makeLocalTargetChildBuilder(stateNodes, child.path, extendTargetValues(values, child.path, value)));
|
|
289
|
-
};
|
|
357
|
+
return selector(makeLocalTargetChildBuilder(states, stateNodes, child.path, extendTargetValues(values, child.path, value), source));
|
|
358
|
+
}, child.type === "atomic" || child.type === "final" ? "leaf" : "nested");
|
|
290
359
|
}
|
|
291
360
|
return builder;
|
|
292
361
|
};
|
|
293
|
-
const makeLocalTargetBuilder = (stateNodes, source) => {
|
|
362
|
+
const makeLocalTargetBuilder = (states, stateNodes, source) => {
|
|
294
363
|
const scope = getLocalTargetScope(stateNodes, source);
|
|
295
364
|
if (scope === undefined) {
|
|
296
365
|
return {};
|
|
297
366
|
}
|
|
298
|
-
const builder = makeLocalTargetChildBuilder(stateNodes, scope, undefined);
|
|
299
|
-
builder.with = (value, selector) => {
|
|
367
|
+
const builder = makeLocalTargetChildBuilder(states, stateNodes, scope, undefined, source);
|
|
368
|
+
builder.with = withFrom((value, selector) => {
|
|
300
369
|
if (selector === undefined) {
|
|
301
370
|
throw new Error(`Machine expected target "${scope}" builder to provide an active child state`);
|
|
302
371
|
}
|
|
303
|
-
return selector(makeLocalTargetChildBuilder(stateNodes, scope, { [scope]: value }));
|
|
304
|
-
};
|
|
372
|
+
return selector(makeLocalTargetChildBuilder(states, stateNodes, scope, { [scope]: value }, source));
|
|
373
|
+
}, "nested");
|
|
305
374
|
return builder;
|
|
306
375
|
};
|
|
307
|
-
const addBranchTargetChildren = (builder, stateNodes, parentPath, values) => {
|
|
376
|
+
const addBranchTargetChildren = (builder, states, stateNodes, parentPath, values, source) => {
|
|
308
377
|
const parent = getTargetBuilderNode(stateNodes, parentPath);
|
|
309
378
|
for (const childPath of parent.children) {
|
|
310
379
|
const child = getTargetBuilderNode(stateNodes, childPath);
|
|
311
|
-
builder[child.key] = makeBranchTargetNodeBuilder(stateNodes, child.path, values);
|
|
380
|
+
builder[child.key] = makeBranchTargetNodeBuilder(states, stateNodes, child.path, values, source);
|
|
312
381
|
}
|
|
313
382
|
};
|
|
314
|
-
const makeBranchTargetNodeBuilder = (stateNodes, path, values) => {
|
|
383
|
+
const makeBranchTargetNodeBuilder = (states, stateNodes, path, values, source) => {
|
|
315
384
|
const node = getTargetBuilderNode(stateNodes, path);
|
|
316
385
|
if (node.type === "atomic" || node.type === "final") {
|
|
317
|
-
return (value) => makeTargetWithValues(node.path, value, values);
|
|
386
|
+
return withFrom((value) => makeTargetWithValues(node.path, value, values), "leaf");
|
|
318
387
|
}
|
|
319
|
-
const builder = ((value, selector) => {
|
|
388
|
+
const builder = withFrom((value, selector) => {
|
|
389
|
+
if (node.type === "parallel") {
|
|
390
|
+
if (source !== node.path && !source.startsWith(`${node.path}.`)) {
|
|
391
|
+
return makeParallelTarget(states, node, value, selector, values);
|
|
392
|
+
}
|
|
393
|
+
if (selector === undefined) {
|
|
394
|
+
throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`);
|
|
395
|
+
}
|
|
396
|
+
const nextBuilder = {};
|
|
397
|
+
addBranchTargetChildren(nextBuilder, states, stateNodes, node.path, extendTargetValues(values, node.path, value), source);
|
|
398
|
+
return selector(nextBuilder);
|
|
399
|
+
}
|
|
320
400
|
if (selector === undefined) {
|
|
321
401
|
throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`);
|
|
322
402
|
}
|
|
323
403
|
const nextBuilder = {};
|
|
324
|
-
addBranchTargetChildren(nextBuilder, stateNodes, node.path, extendTargetValues(values, node.path, value));
|
|
404
|
+
addBranchTargetChildren(nextBuilder, states, stateNodes, node.path, extendTargetValues(values, node.path, value), source);
|
|
325
405
|
return selector(nextBuilder);
|
|
326
|
-
});
|
|
327
|
-
|
|
406
|
+
}, "nested");
|
|
407
|
+
if (node.type !== "parallel" || source === node.path || source.startsWith(`${node.path}.`)) {
|
|
408
|
+
addBranchTargetChildren(builder, states, stateNodes, node.path, values, source);
|
|
409
|
+
}
|
|
328
410
|
return builder;
|
|
329
411
|
};
|
|
330
|
-
const makeBranchTargetBuilder = (stateNodes, source) => {
|
|
412
|
+
const makeBranchTargetBuilder = (states, stateNodes, source) => {
|
|
331
413
|
const rootPath = source.split(".")[0];
|
|
332
414
|
const root = getTargetBuilderNode(stateNodes, rootPath);
|
|
333
415
|
return {
|
|
334
|
-
[root.key]: makeBranchTargetNodeBuilder(stateNodes, root.path, undefined)
|
|
416
|
+
[root.key]: makeBranchTargetNodeBuilder(states, stateNodes, root.path, undefined, source)
|
|
335
417
|
};
|
|
336
418
|
};
|
|
419
|
+
const makeHistoryTargetBuilder = (states, prefix) => {
|
|
420
|
+
const builder = {};
|
|
421
|
+
for (const key of Object.keys(states)) {
|
|
422
|
+
const path = prefix === "" ? key : `${prefix}.${key}`;
|
|
423
|
+
const definition = states[key];
|
|
424
|
+
if (definition.type === "history") {
|
|
425
|
+
const parent = getParentPathRuntime(path);
|
|
426
|
+
builder[key] = () => Model.makeHistoryTarget(path, parent);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (typeof definition === "object" && definition !== null && hasProperty(definition, "states")) {
|
|
430
|
+
builder[key] = makeHistoryTargetBuilder(definition.states, path);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return builder;
|
|
434
|
+
};
|
|
435
|
+
const getParentPathRuntime = (path) => {
|
|
436
|
+
const separator = path.lastIndexOf(".");
|
|
437
|
+
if (separator < 0) {
|
|
438
|
+
throw new Error(`Machine expected history state "${path}" to have an active parent`);
|
|
439
|
+
}
|
|
440
|
+
return path.slice(0, separator);
|
|
441
|
+
};
|
|
337
442
|
const makeTargetBuilder = (states, stateNodes) => {
|
|
338
443
|
const full = makeSnapshotBuilder(states, { mode: "full", prefix: "" });
|
|
444
|
+
const history = makeHistoryTargetBuilder(states, "");
|
|
339
445
|
return (source) => ({
|
|
340
|
-
local: makeLocalTargetBuilder(stateNodes, source),
|
|
341
|
-
branch: makeBranchTargetBuilder(stateNodes, source),
|
|
342
|
-
full
|
|
446
|
+
local: makeLocalTargetBuilder(states, stateNodes, source),
|
|
447
|
+
branch: makeBranchTargetBuilder(states, stateNodes, source),
|
|
448
|
+
full,
|
|
449
|
+
history
|
|
343
450
|
});
|
|
344
451
|
};
|
|
345
|
-
const getSnapshotByPath = (snapshot, path, parents) => {
|
|
346
|
-
if (snapshot.path === path) {
|
|
347
|
-
return Option.some(snapshot);
|
|
348
|
-
}
|
|
349
|
-
if (!path.startsWith(`${snapshot.path}.`)) {
|
|
350
|
-
return Option.none();
|
|
351
|
-
}
|
|
352
|
-
if (parents !== undefined) {
|
|
353
|
-
parents[snapshot.path] = snapshot.value;
|
|
354
|
-
}
|
|
355
|
-
if (hasProperty(snapshot, "state") && Model.isSnapshot(snapshot.state)) {
|
|
356
|
-
return getSnapshotByPath(snapshot.state, path, parents);
|
|
357
|
-
}
|
|
358
|
-
if (hasProperty(snapshot, "states") && typeof snapshot.states === "object" && snapshot.states !== null) {
|
|
359
|
-
for (const child of Object.values(snapshot.states)) {
|
|
360
|
-
if (Model.isSnapshot(child)) {
|
|
361
|
-
const result = getSnapshotByPath(child, path, parents);
|
|
362
|
-
if (Option.isSome(result)) {
|
|
363
|
-
return result;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return Option.none();
|
|
369
|
-
};
|
|
370
452
|
/**
|
|
371
453
|
* Defines a state tree while preserving literal state paths.
|
|
372
454
|
*
|
|
@@ -385,7 +467,7 @@ const getSnapshotByPath = (snapshot, path, parents) => {
|
|
|
385
467
|
*
|
|
386
468
|
* ```ts
|
|
387
469
|
* import { Schema } from "effect"
|
|
388
|
-
* import { Machine } from "effect
|
|
470
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
389
471
|
*
|
|
390
472
|
* class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
|
|
391
473
|
*
|
|
@@ -394,24 +476,24 @@ const getSnapshotByPath = (snapshot, path, parents) => {
|
|
|
394
476
|
* Machine.make({
|
|
395
477
|
* states: States.states,
|
|
396
478
|
* events: [],
|
|
397
|
-
* initial: () => States.initial.idle(
|
|
479
|
+
* initial: () => States.initial.idle.from()
|
|
398
480
|
* })
|
|
399
481
|
* ```
|
|
400
482
|
*
|
|
401
483
|
* @category constructors
|
|
402
484
|
* @since 4.0.0
|
|
403
485
|
*/
|
|
404
|
-
export const defineStates = (states
|
|
486
|
+
export const defineStates = ((states) => ({
|
|
405
487
|
states: states,
|
|
406
488
|
initial: makeSnapshotBuilder(states, { mode: "initial", prefix: "" }),
|
|
407
|
-
get: ((snapshot, path) => getSnapshotByPath(snapshot, path).pipe(Option.map((snapshot) => snapshot.value))),
|
|
489
|
+
get: ((snapshot, path) => Model.getSnapshotByPath(snapshot, path).pipe(Option.map((snapshot) => snapshot.value))),
|
|
408
490
|
getWithParents: ((snapshot, path) => {
|
|
409
491
|
const parents = {};
|
|
410
|
-
return getSnapshotByPath(snapshot, path, parents).pipe(Option.map((snapshot) => ({ value: snapshot.value, parents })));
|
|
492
|
+
return Model.getSnapshotByPath(snapshot, path, parents).pipe(Option.map((snapshot) => ({ value: snapshot.value, parents })));
|
|
411
493
|
}),
|
|
412
|
-
getSnapshot: getSnapshotByPath,
|
|
413
|
-
matches: (snapshot, path) => Option.isSome(getSnapshotByPath(snapshot, path))
|
|
414
|
-
});
|
|
494
|
+
getSnapshot: Model.getSnapshotByPath,
|
|
495
|
+
matches: (snapshot, path) => Option.isSome(Model.getSnapshotByPath(snapshot, path))
|
|
496
|
+
}));
|
|
415
497
|
/**
|
|
416
498
|
* Creates a schema-first machine definition.
|
|
417
499
|
*
|
|
@@ -423,11 +505,16 @@ export const defineStates = (states, ..._validation) => ({
|
|
|
423
505
|
* `defineStates` or is passed inline. Call `handle` on the returned definition
|
|
424
506
|
* to implement state behavior with ordinary TypeScript control flow.
|
|
425
507
|
*
|
|
508
|
+
* Schemas in `events` define the public input protocol. Schemas in
|
|
509
|
+
* `internalEvents` are added to the complete handler protocol for invoke
|
|
510
|
+
* results, child emissions, and other machine-local deliveries. Their tags
|
|
511
|
+
* must be disjoint.
|
|
512
|
+
*
|
|
426
513
|
* **Example** (Typed counter machine)
|
|
427
514
|
*
|
|
428
515
|
* ```ts
|
|
429
516
|
* import { Schema } from "effect"
|
|
430
|
-
* import { Machine } from "effect
|
|
517
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
431
518
|
*
|
|
432
519
|
* class Count extends Schema.TaggedClass<Count>("Count")("Count", {
|
|
433
520
|
* value: Schema.Number
|
|
@@ -457,10 +544,11 @@ export const defineStates = (states, ..._validation) => ({
|
|
|
457
544
|
* @category constructors
|
|
458
545
|
* @since 4.0.0
|
|
459
546
|
*/
|
|
460
|
-
export const make = (config
|
|
547
|
+
export const make = ((config) => {
|
|
461
548
|
const self = Object.create(Proto);
|
|
462
549
|
self.states = config.states;
|
|
463
550
|
self.events = config.events;
|
|
551
|
+
self.internalEvents = config.internalEvents ?? [];
|
|
464
552
|
self.emits = config.emits ?? [];
|
|
465
553
|
self.input = config.input;
|
|
466
554
|
self.id = config.id;
|
|
@@ -469,8 +557,9 @@ export const make = (config, ..._validation) => {
|
|
|
469
557
|
self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes);
|
|
470
558
|
self.handlers = Object.create(null);
|
|
471
559
|
self.handle = makeHandle(self);
|
|
560
|
+
Model.setProtocol(self);
|
|
472
561
|
return self;
|
|
473
|
-
};
|
|
562
|
+
});
|
|
474
563
|
/**
|
|
475
564
|
* Encodes a decoded machine snapshot into a normalized data representation.
|
|
476
565
|
*
|
|
@@ -538,6 +627,8 @@ export const decodeSnapshot = Model.decodeSnapshot;
|
|
|
538
627
|
* Invoked child processes run while their owning state is active and are
|
|
539
628
|
* stopped before the state exits. An unrecovered child failure fails the owning
|
|
540
629
|
* machine; recover inside the child Effect when failure should become an event.
|
|
630
|
+
* The `id` is a state-local lifecycle key, not a communication address. To
|
|
631
|
+
* send events to the invocation, pass a typed `childAddress` as `address`.
|
|
541
632
|
* The `src` callback is intentionally independent from its parent state. When
|
|
542
633
|
* construction depends on the typed state, lifecycle event, or runtime, use
|
|
543
634
|
* the state config factory form `invoke: (context) => Machine.invoke(...)` and
|
|
@@ -547,7 +638,7 @@ export const decodeSnapshot = Model.decodeSnapshot;
|
|
|
547
638
|
*
|
|
548
639
|
* ```ts
|
|
549
640
|
* import { Effect, Schema } from "effect"
|
|
550
|
-
* import { Machine } from "effect
|
|
641
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
551
642
|
*
|
|
552
643
|
* class Loaded extends Schema.TaggedClass<Loaded>("Loaded")("Loaded", {
|
|
553
644
|
* value: Schema.String
|
|
@@ -565,6 +656,64 @@ export const decodeSnapshot = Model.decodeSnapshot;
|
|
|
565
656
|
* @since 4.0.0
|
|
566
657
|
*/
|
|
567
658
|
export const invoke = (config) => ({ ...config, [InvokeTypeId]: undefined });
|
|
659
|
+
/**
|
|
660
|
+
* Invokes one Effect and maps its typed outcome into machine-local events.
|
|
661
|
+
*
|
|
662
|
+
* **Details**
|
|
663
|
+
*
|
|
664
|
+
* This is the high-level one-shot counterpart to `invoke`. Success and typed
|
|
665
|
+
* failure values are mapped independently, so callers do not need to recover
|
|
666
|
+
* an Effect into a common event union by hand. Defects and interruption remain
|
|
667
|
+
* failures of the owning machine.
|
|
668
|
+
*
|
|
669
|
+
* Declare mapped outcomes in `internalEvents` unless they are also legitimate
|
|
670
|
+
* public commands.
|
|
671
|
+
*
|
|
672
|
+
* @see {@link invoke} for arbitrary child process logic.
|
|
673
|
+
* @see {@link after} for a state-scoped delayed event.
|
|
674
|
+
* @category constructors
|
|
675
|
+
* @since 4.0.0
|
|
676
|
+
*/
|
|
677
|
+
export const invokeEffect = (config) => ((config) => invoke({
|
|
678
|
+
id: config.id,
|
|
679
|
+
src: () => effect(config.onFailure === undefined
|
|
680
|
+
? Effect.map(config.effect, config.onSuccess)
|
|
681
|
+
: Effect.matchEffect(config.effect, {
|
|
682
|
+
onFailure: (error) => Effect.succeed(config.onFailure(error)),
|
|
683
|
+
onSuccess: (value) => Effect.succeed(config.onSuccess(value))
|
|
684
|
+
}))
|
|
685
|
+
}))(config);
|
|
686
|
+
/**
|
|
687
|
+
* Creates a cancellable state-scoped delayed event.
|
|
688
|
+
*
|
|
689
|
+
* The timer starts when its owning state is entered and is interrupted when
|
|
690
|
+
* that state exits. The delayed value should normally be declared in
|
|
691
|
+
* `internalEvents`.
|
|
692
|
+
*
|
|
693
|
+
* @category constructors
|
|
694
|
+
* @since 4.0.0
|
|
695
|
+
*/
|
|
696
|
+
export const after = (duration, event, options) => invoke({
|
|
697
|
+
id: options?.id ?? `Machine.after:${String(event._tag)}`,
|
|
698
|
+
src: () => effect(Effect.as(Effect.sleep(duration), event))
|
|
699
|
+
});
|
|
700
|
+
/**
|
|
701
|
+
* Constructs another tagged case from compatible source fields.
|
|
702
|
+
*
|
|
703
|
+
* The target must be one tagged struct or tagged class whose discriminator is
|
|
704
|
+
* supplied by its constructor. Union wrappers are rejected because their
|
|
705
|
+
* `make` operation cannot select a member after the source discriminator has
|
|
706
|
+
* been discarded. Missing or incompatible required target fields must be
|
|
707
|
+
* supplied by the patch, and the target schema's normal `make` validation
|
|
708
|
+
* remains authoritative at runtime.
|
|
709
|
+
*
|
|
710
|
+
* @category constructors
|
|
711
|
+
* @since 4.0.0
|
|
712
|
+
*/
|
|
713
|
+
export const retag = (target, source, ...args) => {
|
|
714
|
+
const { _tag: _, ...fields } = source;
|
|
715
|
+
return target.make({ ...fields, ...(args[0] ?? {}) });
|
|
716
|
+
};
|
|
568
717
|
/**
|
|
569
718
|
* Creates an invoked child process from a complete statechart machine.
|
|
570
719
|
*
|
|
@@ -594,7 +743,8 @@ export const invokeMachine = ((config) => {
|
|
|
594
743
|
const machine = config.child.machine;
|
|
595
744
|
return {
|
|
596
745
|
id: config.child.id,
|
|
597
|
-
|
|
746
|
+
address: config.child.id,
|
|
747
|
+
descriptor: config.child,
|
|
598
748
|
src: () => machine.input === undefined
|
|
599
749
|
? internalProcess.toProcessLogic(machine)
|
|
600
750
|
: internalProcess.toProcessLogic(machine, config.input),
|
|
@@ -664,13 +814,14 @@ export const plan = internalPlanner.plan;
|
|
|
664
814
|
* The action's error and service requirements are retained in the machine
|
|
665
815
|
* type without becoming requirements of `plan` or `planInitial`. The managed
|
|
666
816
|
* runtime executes staged actions sequentially before publishing the planned
|
|
667
|
-
* state.
|
|
817
|
+
* state. Pass a second argument when the planning Effect should return that
|
|
818
|
+
* value after staging.
|
|
668
819
|
*
|
|
669
820
|
* **Example** (Typed staged action)
|
|
670
821
|
*
|
|
671
822
|
* ```ts
|
|
672
823
|
* import { Context, Effect } from "effect"
|
|
673
|
-
* import { Machine } from "effect
|
|
824
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
674
825
|
*
|
|
675
826
|
* class Audit extends Context.Service<Audit, {
|
|
676
827
|
* readonly write: Effect.Effect<void, "AuditError">
|
|
@@ -685,7 +836,10 @@ export const plan = internalPlanner.plan;
|
|
|
685
836
|
* @category combinators
|
|
686
837
|
* @since 4.0.0
|
|
687
838
|
*/
|
|
688
|
-
export const action = (effect) =>
|
|
839
|
+
export const action = ((effect, ...next) => {
|
|
840
|
+
const staged = internalPlanner.action(effect);
|
|
841
|
+
return next.length === 0 ? staged : Effect.as(staged, next[0]);
|
|
842
|
+
});
|
|
689
843
|
const processLocal = (operation) => Effect.die(new ProcessLocalErrorValue({ operation }));
|
|
690
844
|
const standaloneProcessRuntime = internalRuntime.MachineRuntime.of({
|
|
691
845
|
self: {
|
|
@@ -756,7 +910,7 @@ export const runtime = () => internalPlanner.runtime();
|
|
|
756
910
|
*
|
|
757
911
|
* ```ts
|
|
758
912
|
* import { Effect, Schema } from "effect"
|
|
759
|
-
* import { Machine } from "effect
|
|
913
|
+
* import { Machine } from "@typeonce/effect-machine"
|
|
760
914
|
*
|
|
761
915
|
* class LoadFailed extends Schema.TaggedClass<LoadFailed>("LoadFailed")("LoadFailed", {
|
|
762
916
|
* reason: Schema.String
|
|
@@ -835,20 +989,29 @@ export const transition = (initial, transition) => logic({
|
|
|
835
989
|
run: ({ receive, updateState }) => receive.pipe(Effect.flatMap((event) => updateState((state) => transition(state, event))), Effect.forever)
|
|
836
990
|
});
|
|
837
991
|
/**
|
|
838
|
-
* Creates a typed
|
|
992
|
+
* Creates a typed descriptor for a complete child machine.
|
|
839
993
|
*
|
|
840
|
-
*
|
|
994
|
+
* Descriptors identify a child by id and machine identity, so independently
|
|
995
|
+
* constructed descriptors for the same pair address the same invoked child.
|
|
996
|
+
*
|
|
997
|
+
* @category constructors
|
|
998
|
+
* @since 4.0.0
|
|
999
|
+
*/
|
|
1000
|
+
export const child = (id, machine) => ({
|
|
1001
|
+
[ChildMachineTypeId]: ChildMachineTypeId,
|
|
1002
|
+
id,
|
|
1003
|
+
machine
|
|
1004
|
+
});
|
|
1005
|
+
/**
|
|
1006
|
+
* Creates a typed parent-local address for lower-level child process logic.
|
|
841
1007
|
*
|
|
842
|
-
*
|
|
843
|
-
* `
|
|
844
|
-
* creates an event-only address for lower-level process logic.
|
|
1008
|
+
* The default event protocol is `never`; provide an event type before using
|
|
1009
|
+
* the address with `spawn`, `invoke`, or `sendTo`.
|
|
845
1010
|
*
|
|
846
1011
|
* @category constructors
|
|
847
1012
|
* @since 4.0.0
|
|
848
1013
|
*/
|
|
849
|
-
export const
|
|
850
|
-
? id
|
|
851
|
-
: { [ChildMachineTypeId]: ChildMachineTypeId, id, machine });
|
|
1014
|
+
export const childAddress = (id) => id;
|
|
852
1015
|
/**
|
|
853
1016
|
* Spawns a child process owned by the currently running machine.
|
|
854
1017
|
*
|
|
@@ -876,14 +1039,14 @@ export const spawn = ((logic, options) => Effect.flatMap(internalRuntime.Machine
|
|
|
876
1039
|
* @category runtime
|
|
877
1040
|
* @since 4.0.0
|
|
878
1041
|
*/
|
|
879
|
-
export const sendTo = ((child, event) => Effect.flatMap(internalRuntime.MachineRuntime, (runtime) => runtime.sendTo(
|
|
1042
|
+
export const sendTo = ((child, event) => Effect.flatMap(internalRuntime.MachineRuntime, (runtime) => runtime.sendTo(child, event)));
|
|
880
1043
|
/**
|
|
881
1044
|
* Stops a named child process of the running machine.
|
|
882
1045
|
*
|
|
883
1046
|
* @category runtime
|
|
884
1047
|
* @since 4.0.0
|
|
885
1048
|
*/
|
|
886
|
-
export const stopChild = ((child) => Effect.flatMap(internalRuntime.MachineRuntime, (runtime) => runtime.stopChild(
|
|
1049
|
+
export const stopChild = ((child) => Effect.flatMap(internalRuntime.MachineRuntime, (runtime) => runtime.stopChild(child)));
|
|
887
1050
|
/**
|
|
888
1051
|
* Returns a stream of terminal lifecycle outcomes for a running machine.
|
|
889
1052
|
*
|