effect-machine 0.15.2 → 0.17.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 +4 -0
- package/dist/actor.d.ts +19 -7
- package/dist/actor.js +26 -4
- package/dist/cluster/adapters/in-memory.d.ts +4 -4
- package/dist/cluster/adapters/in-memory.js +2 -2
- package/dist/cluster/entity-machine.d.ts +2 -2
- package/dist/cluster/index.d.ts +2 -2
- package/dist/cluster/persistence.d.ts +5 -4
- package/dist/cluster/persistence.js +2 -2
- package/dist/cluster/to-entity.d.ts +2 -2
- package/dist/errors.d.ts +12 -12
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/inspection.d.ts +12 -11
- package/dist/inspection.js +10 -24
- package/dist/internal/inspection.d.ts +2 -2
- package/dist/internal/runtime.d.ts +7 -7
- package/dist/internal/runtime.js +10 -15
- package/dist/internal/transition.d.ts +9 -9
- package/dist/internal/utils.d.ts +3 -3
- package/dist/internal/utils.js +1 -1
- package/dist/machine.d.ts +27 -7
- package/dist/machine.js +26 -4
- package/dist/schema.d.ts +24 -13
- package/dist/schema.js +8 -8
- package/dist/slot.d.ts +8 -6
- package/dist/slot.js +38 -3
- package/dist/testing.d.ts +7 -7
- package/package.json +22 -22
- package/v3/dist/actor.d.ts +12 -2
- package/v3/dist/actor.js +24 -2
- package/v3/dist/cluster/entity-machine.d.ts +1 -1
- package/v3/dist/cluster/entity-machine.js +4 -3
- package/v3/dist/cluster/to-entity.d.ts +8 -3
- package/v3/dist/index.d.ts +2 -2
- package/v3/dist/index.js +2 -2
- package/v3/dist/inspection.d.ts +2 -2
- package/v3/dist/inspection.js +8 -22
- package/v3/dist/internal/runtime.d.ts +5 -5
- package/v3/dist/internal/runtime.js +9 -14
- package/v3/dist/machine.d.ts +22 -2
- package/v3/dist/machine.js +26 -4
- package/v3/dist/schema.d.ts +25 -12
- package/v3/dist/schema.js +8 -7
- package/v3/dist/slot.d.ts +3 -2
- package/v3/dist/slot.js +37 -2
package/v3/dist/actor.js
CHANGED
|
@@ -19,6 +19,16 @@ import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, O
|
|
|
19
19
|
*/
|
|
20
20
|
const ActorSystem = Context.GenericTag("@effect/machine/ActorSystem");
|
|
21
21
|
/**
|
|
22
|
+
* Explicit scope for actor lifecycle management.
|
|
23
|
+
*
|
|
24
|
+
* When present in context, actors attach cleanup finalizers to this scope.
|
|
25
|
+
* This replaces ambient `Scope.Scope` detection which caused bugs where
|
|
26
|
+
* unrelated scopes would tear down actors unexpectedly.
|
|
27
|
+
*
|
|
28
|
+
* Provide via `Machine.scoped` or `Effect.provideService(ActorScope, scope)`.
|
|
29
|
+
*/
|
|
30
|
+
const ActorScope = Context.GenericTag("@effect/machine/ActorScope");
|
|
31
|
+
/**
|
|
22
32
|
* Notify all listeners of state change.
|
|
23
33
|
*/
|
|
24
34
|
const notifyListeners = (listeners, state) => {
|
|
@@ -40,11 +50,19 @@ const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, lis
|
|
|
40
50
|
});
|
|
41
51
|
const call = Effect.fn("effect-machine.actor.call")(function* (event) {
|
|
42
52
|
if (yield* Ref.get(stoppedRef)) {
|
|
53
|
+
yield* Effect.logWarning("effect-machine.actor.call.stopped").pipe(Effect.annotateLogs({
|
|
54
|
+
actorId: id,
|
|
55
|
+
eventTag: event._tag
|
|
56
|
+
}));
|
|
43
57
|
const currentState = yield* SubscriptionRef.get(stateRef);
|
|
44
58
|
return {
|
|
45
59
|
newState: currentState,
|
|
46
60
|
previousState: currentState,
|
|
47
61
|
transitioned: false,
|
|
62
|
+
hasReply: false,
|
|
63
|
+
deferReply: false,
|
|
64
|
+
reply: void 0,
|
|
65
|
+
postponed: false,
|
|
48
66
|
lifecycleRan: false,
|
|
49
67
|
isFinal: machine.finalStates.has(currentState._tag)
|
|
50
68
|
};
|
|
@@ -61,6 +79,10 @@ const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, lis
|
|
|
61
79
|
newState: currentState,
|
|
62
80
|
previousState: currentState,
|
|
63
81
|
transitioned: false,
|
|
82
|
+
hasReply: false,
|
|
83
|
+
deferReply: false,
|
|
84
|
+
reply: void 0,
|
|
85
|
+
postponed: false,
|
|
64
86
|
lifecycleRan: false,
|
|
65
87
|
isFinal: machine.finalStates.has(currentState._tag)
|
|
66
88
|
})))));
|
|
@@ -469,7 +491,7 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
|
469
491
|
id,
|
|
470
492
|
actor: actorRef
|
|
471
493
|
});
|
|
472
|
-
const maybeScope = yield* Effect.serviceOption(
|
|
494
|
+
const maybeScope = yield* Effect.serviceOption(ActorScope);
|
|
473
495
|
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, Effect.gen(function* () {
|
|
474
496
|
if (MutableHashMap.has(actorsMap, id)) {
|
|
475
497
|
yield* emitSystemEvent({
|
|
@@ -552,4 +574,4 @@ const makeSystem = make;
|
|
|
552
574
|
*/
|
|
553
575
|
const Default = Layer.scoped(ActorSystem, make());
|
|
554
576
|
//#endregion
|
|
555
|
-
export { ActorSystem, Default, buildActorRefCore, createActor, makeSystem, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
|
|
577
|
+
export { ActorScope, ActorSystem, Default, buildActorRefCore, createActor, makeSystem, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
|
|
@@ -43,7 +43,7 @@ interface EntityMachineOptions<S, E> {
|
|
|
43
43
|
* Retry policy for defects (schedule for restarting after defect).
|
|
44
44
|
* Forwarded to Entity.toLayer.
|
|
45
45
|
*/
|
|
46
|
-
readonly defectRetryPolicy?: Schedule.Schedule<any
|
|
46
|
+
readonly defectRetryPolicy?: Schedule.Schedule<any>;
|
|
47
47
|
/**
|
|
48
48
|
* Persistence configuration. When set, requires PersistenceAdapter in R.
|
|
49
49
|
*/
|
|
@@ -3,7 +3,7 @@ import { replay } from "../machine.js";
|
|
|
3
3
|
import { createRuntime } from "../internal/runtime.js";
|
|
4
4
|
import { ActorSystem } from "../actor.js";
|
|
5
5
|
import { PersistenceAdapter } from "./persistence.js";
|
|
6
|
-
import { Effect, Option, Ref } from "effect";
|
|
6
|
+
import { Clock, Effect, Option, Ref } from "effect";
|
|
7
7
|
import { Entity } from "@effect/cluster";
|
|
8
8
|
//#region src/cluster/entity-machine.ts
|
|
9
9
|
/**
|
|
@@ -56,10 +56,11 @@ const EntityMachine = { layer: (entity, machine, options) => {
|
|
|
56
56
|
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
57
57
|
const state = yield* runtime.getState;
|
|
58
58
|
const version = yield* Ref.get(versionRef);
|
|
59
|
+
const now = yield* Clock.currentTimeMillis;
|
|
59
60
|
yield* pAdapter.saveSnapshot(key, {
|
|
60
61
|
state,
|
|
61
62
|
version,
|
|
62
|
-
timestamp:
|
|
63
|
+
timestamp: now
|
|
63
64
|
});
|
|
64
65
|
}).pipe(Effect.catchAll(() => Effect.void)));
|
|
65
66
|
}
|
|
@@ -153,7 +154,7 @@ const persistEvent = (adapter, key, versionRef, event) => Effect.gen(function* (
|
|
|
153
154
|
const persisted = {
|
|
154
155
|
event,
|
|
155
156
|
version: newVersion,
|
|
156
|
-
timestamp:
|
|
157
|
+
timestamp: yield* Clock.currentTimeMillis
|
|
157
158
|
};
|
|
158
159
|
yield* adapter.appendEvents(key, [persisted], expectedVersion);
|
|
159
160
|
yield* Ref.set(versionRef, newVersion);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Machine } from "../machine.js";
|
|
2
2
|
import { Schema } from "effect";
|
|
3
|
+
import { Entity } from "@effect/cluster";
|
|
3
4
|
import { Rpc } from "@effect/rpc";
|
|
4
5
|
|
|
5
6
|
//#region src/cluster/to-entity.d.ts
|
|
@@ -21,9 +22,9 @@ interface ToEntityOptions {
|
|
|
21
22
|
*/
|
|
22
23
|
type EntityRpcs<StateSchema extends Schema.Schema.Any, EventSchema extends Schema.Schema.Any> = readonly [Rpc.Rpc<"Send", Schema.Struct<{
|
|
23
24
|
readonly event: EventSchema;
|
|
24
|
-
}>, StateSchema, typeof Schema.Never
|
|
25
|
+
}>, StateSchema, typeof Schema.Never>, Rpc.Rpc<"Ask", Schema.Struct<{
|
|
25
26
|
readonly event: EventSchema;
|
|
26
|
-
}>, typeof Schema.Unknown, typeof Schema.Never
|
|
27
|
+
}>, typeof Schema.Unknown, typeof Schema.Never>, Rpc.Rpc<"GetState", typeof Schema.Void, StateSchema, typeof Schema.Never>];
|
|
27
28
|
/**
|
|
28
29
|
* Generate an Entity definition from a machine.
|
|
29
30
|
*
|
|
@@ -59,6 +60,10 @@ declare const toEntity: <S extends {
|
|
|
59
60
|
readonly _tag: string;
|
|
60
61
|
}, E extends {
|
|
61
62
|
readonly _tag: string;
|
|
62
|
-
}, R>(machine: Machine<S, E, R, any, any, any>, options: ToEntityOptions) =>
|
|
63
|
+
}, R>(machine: Machine<S, E, R, any, any, any>, options: ToEntityOptions) => Entity.Entity<string, Rpc.Rpc<"Send", Schema.Struct<{
|
|
64
|
+
event: Schema.Schema<E, E, never>;
|
|
65
|
+
}>, Schema.Schema<S, S, never>, typeof Schema.Never, never> | Rpc.Rpc<"Ask", Schema.Struct<{
|
|
66
|
+
event: Schema.Schema<E, E, never>;
|
|
67
|
+
}>, typeof Schema.Unknown, typeof Schema.Never, never> | Rpc.Rpc<"GetState", typeof Schema.Void, Schema.Schema<S, S, never>, typeof Schema.Never, never>>;
|
|
63
68
|
//#endregion
|
|
64
69
|
export { EntityRpcs, ToEntityOptions, toEntity };
|
package/v3/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { HasSlotKeys, MachineContext, ProvideSlots, Slot, SlotCall, SlotCalls, S
|
|
|
5
5
|
import { ActorExit, CellPhase, DefectPhase, Supervision } from "./supervision.js";
|
|
6
6
|
import { ProcessEventResult } from "./internal/transition.js";
|
|
7
7
|
import { BackgroundEffect, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, SpawnEffect, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, machine_d_exports } from "./machine.js";
|
|
8
|
-
import { ActorRef, ActorRefSync, ActorSystem, Default, SystemEvent, SystemEventListener, TransitionInfo } from "./actor.js";
|
|
8
|
+
import { ActorRef, ActorRefSync, ActorScope, ActorSystem, Default, SystemEvent, SystemEventListener, TransitionInfo } from "./actor.js";
|
|
9
9
|
import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
10
10
|
import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
11
|
-
export { ActorExit, type ActorRef, type ActorRefSync, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type CellPhase, type DefectPhase, type DeferReplyResult, DuplicateActorError, type Durability, type DurabilityCommit, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type HandlerContext, type HasSlotKeys, type InspectionEvent, type Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, type Lifecycle, machine_d_exports as Machine, type MachineContext, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, type ProcessEventResult, type ProvideSlots, ProvisionValidationError, type Recovery, type RecoveryContext, type ReplyFields, type ReplyResult, type SimulationResult, Slot, type SlotCall, type SlotCalls, SlotCodecError, type SlotFnDef, type SlotHandler, type SlotInvocation, SlotProvisionError, type SlotRequest, type SlotResult, type SlotsDef, type SlotsSchema, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, Supervision, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TimeoutConfig, type TracingInspectorOptions, type Transition, type TransitionEvent, type TransitionInfo, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
|
11
|
+
export { ActorExit, type ActorRef, type ActorRefSync, ActorScope, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type CellPhase, type DefectPhase, type DeferReplyResult, DuplicateActorError, type Durability, type DurabilityCommit, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type HandlerContext, type HasSlotKeys, type InspectionEvent, type Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, type Lifecycle, machine_d_exports as Machine, type MachineContext, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, type ProcessEventResult, type ProvideSlots, ProvisionValidationError, type Recovery, type RecoveryContext, type ReplyFields, type ReplyResult, type SimulationResult, Slot, type SlotCall, type SlotCalls, SlotCodecError, type SlotFnDef, type SlotHandler, type SlotInvocation, SlotProvisionError, type SlotRequest, type SlotResult, type SlotsDef, type SlotsSchema, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, Supervision, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TimeoutConfig, type TracingInspectorOptions, type Transition, type TransitionEvent, type TransitionInfo, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
package/v3/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Inspector, collectingInspector, combineInspectors, consoleInspector, ma
|
|
|
3
3
|
import { Slot } from "./slot.js";
|
|
4
4
|
import { machine_exports } from "./machine.js";
|
|
5
5
|
import { ActorExit, Supervision } from "./supervision.js";
|
|
6
|
-
import { ActorSystem, Default } from "./actor.js";
|
|
6
|
+
import { ActorScope, ActorSystem, Default } from "./actor.js";
|
|
7
7
|
import { Event, State } from "./schema.js";
|
|
8
8
|
import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
9
|
-
export { ActorExit, ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, Slot, SlotCodecError, SlotProvisionError, State, Supervision, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
|
9
|
+
export { ActorExit, ActorScope, ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, Slot, SlotCodecError, SlotProvisionError, State, Supervision, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
package/v3/dist/inspection.d.ts
CHANGED
|
@@ -93,7 +93,7 @@ type AnyInspectionEvent = InspectionEvent<{
|
|
|
93
93
|
/**
|
|
94
94
|
* Inspector interface for observing machine behavior
|
|
95
95
|
*/
|
|
96
|
-
type InspectorHandler<S, E> = (event: InspectionEvent<S, E>) => void | Effect.Effect<void
|
|
96
|
+
type InspectorHandler<S, E> = (event: InspectionEvent<S, E>) => void | Effect.Effect<void>;
|
|
97
97
|
interface Inspector<S, E> {
|
|
98
98
|
readonly onInspect: InspectorHandler<S, E>;
|
|
99
99
|
}
|
|
@@ -119,7 +119,7 @@ declare const makeInspectorEffect: <S = {
|
|
|
119
119
|
readonly _tag: string;
|
|
120
120
|
}, E = {
|
|
121
121
|
readonly _tag: string;
|
|
122
|
-
}>(onInspect: (event: InspectionEvent<ResolveType<S>, ResolveType<E>>) => Effect.Effect<void
|
|
122
|
+
}>(onInspect: (event: InspectionEvent<ResolveType<S>, ResolveType<E>>) => Effect.Effect<void>) => Inspector<ResolveType<S>, ResolveType<E>>;
|
|
123
123
|
declare const combineInspectors: <S, E>(...inspectors: ReadonlyArray<Inspector<S, E>>) => Inspector<S, E>;
|
|
124
124
|
interface TracingInspectorOptions<S, E> {
|
|
125
125
|
readonly spanName?: string | ((event: InspectionEvent<S, E>) => string);
|
package/v3/dist/inspection.js
CHANGED
|
@@ -106,30 +106,16 @@ const tracingInspector = (options) => ({ onInspect: (event) => {
|
|
|
106
106
|
/**
|
|
107
107
|
* Console inspector that logs events in a readable format
|
|
108
108
|
*/
|
|
109
|
-
const consoleInspector = () =>
|
|
109
|
+
const consoleInspector = () => makeInspectorEffect((event) => {
|
|
110
110
|
const prefix = `[${event.actorId}]`;
|
|
111
111
|
switch (event.type) {
|
|
112
|
-
case "@machine.spawn":
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
case "@machine.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
case "@machine.
|
|
119
|
-
console.log(prefix, event.fromState._tag, "→", event.toState._tag);
|
|
120
|
-
break;
|
|
121
|
-
case "@machine.effect":
|
|
122
|
-
console.log(prefix, event.effectType, "effect in", event.state._tag);
|
|
123
|
-
break;
|
|
124
|
-
case "@machine.task":
|
|
125
|
-
console.log(prefix, "task", event.phase, event.taskName ?? "<unnamed>", "in", event.state._tag);
|
|
126
|
-
break;
|
|
127
|
-
case "@machine.error":
|
|
128
|
-
console.log(prefix, "error in", event.phase, event.state._tag, "-", event.error);
|
|
129
|
-
break;
|
|
130
|
-
case "@machine.stop":
|
|
131
|
-
console.log(prefix, "stopped in", event.finalState._tag);
|
|
132
|
-
break;
|
|
112
|
+
case "@machine.spawn": return Effect.log(`${prefix} spawned -> ${event.initialState._tag}`);
|
|
113
|
+
case "@machine.event": return Effect.log(`${prefix} received ${event.event._tag} in ${event.state._tag}`);
|
|
114
|
+
case "@machine.transition": return Effect.log(`${prefix} ${event.fromState._tag} -> ${event.toState._tag}`);
|
|
115
|
+
case "@machine.effect": return Effect.log(`${prefix} ${event.effectType} effect in ${event.state._tag}`);
|
|
116
|
+
case "@machine.task": return Effect.log(`${prefix} task ${event.phase} ${event.taskName ?? "<unnamed>"} in ${event.state._tag}`);
|
|
117
|
+
case "@machine.error": return Effect.log(`${prefix} error in ${event.phase} ${event.state._tag} - ${String(event.error)}`);
|
|
118
|
+
case "@machine.stop": return Effect.log(`${prefix} stopped in ${event.finalState._tag}`);
|
|
133
119
|
}
|
|
134
120
|
});
|
|
135
121
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { NoReplyError } from "../errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { SlotsDef } from "../slot.js";
|
|
3
3
|
import { ActorExit } from "../supervision.js";
|
|
4
4
|
import { ProcessEventHooks, ProcessEventResult } from "./transition.js";
|
|
5
|
-
import { Machine
|
|
5
|
+
import { Machine } from "../machine.js";
|
|
6
6
|
import { ActorSystem } from "../actor.js";
|
|
7
7
|
import { Deferred, Effect, Queue, Ref, Scope, SubscriptionRef } from "effect";
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ type RuntimeQueuedEvent<E> = {
|
|
|
27
27
|
readonly reply: Deferred.Deferred<unknown, NoReplyError>;
|
|
28
28
|
} | {
|
|
29
29
|
readonly _tag: "drain";
|
|
30
|
-
readonly done: Deferred.Deferred<void
|
|
30
|
+
readonly done: Deferred.Deferred<void>;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
33
|
* Resources owned by the actor cell (stable across generations).
|
|
@@ -69,7 +69,7 @@ interface RuntimeHandle<S, E> {
|
|
|
69
69
|
* Exit deferred — set exactly once with the exit reason when the runtime stops.
|
|
70
70
|
* Final state → ActorExit.Final, explicit stop → ActorExit.Stopped, defect → ActorExit.Defect.
|
|
71
71
|
*/
|
|
72
|
-
readonly exitDeferred: Deferred.Deferred<ActorExit<S
|
|
72
|
+
readonly exitDeferred: Deferred.Deferred<ActorExit<S>>;
|
|
73
73
|
/**
|
|
74
74
|
* Actor scope — owns background fibers for this generation.
|
|
75
75
|
* Closing this scope interrupts all background fibers.
|
|
@@ -145,7 +145,7 @@ declare const createRuntime: <S extends {
|
|
|
145
145
|
readonly _tag: string;
|
|
146
146
|
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, system: ActorSystem, config: RuntimeConfig<S, E>) => Effect.Effect<{
|
|
147
147
|
stop: Effect.Effect<void, never, never>;
|
|
148
|
-
start: Effect.Effect<void, unknown,
|
|
148
|
+
start: Effect.Effect<void, unknown, unknown>;
|
|
149
149
|
send: (event: E) => Effect.Effect<void>;
|
|
150
150
|
sendWait: (event: E) => Effect.Effect<void, unknown>;
|
|
151
151
|
ask: (event: E) => Effect.Effect<unknown, NoReplyError>;
|
|
@@ -105,15 +105,13 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
|
|
|
105
105
|
}
|
|
106
106
|
if (lifecycle?.onInitialSpawnEffects !== void 0) yield* lifecycle.onInitialSpawnEffects(machine.initial);
|
|
107
107
|
const initialSpawnDefectSignal = (cause) => Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "initial-spawn")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Effect.suspend(() => loopFiberRef.current !== void 0 ? Fiber.interrupt(loopFiberRef.current) : Effect.void)), Effect.asVoid);
|
|
108
|
-
yield* runSpawnEffects(machine, machine.initial, initEvent, self, stateScopeRef.current, system, actorId, hooks?.onError, initialSpawnDefectSignal).pipe(Effect.catchAllCause((cause) => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
116
|
-
}));
|
|
108
|
+
yield* runSpawnEffects(machine, machine.initial, initEvent, self, stateScopeRef.current, system, actorId, hooks?.onError, initialSpawnDefectSignal).pipe(Effect.catchAllCause((cause) => Effect.gen(function* () {
|
|
109
|
+
yield* Ref.set(stoppedRef, true);
|
|
110
|
+
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
111
|
+
yield* Scope.close(actorScope, Exit.void);
|
|
112
|
+
yield* Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "initial-spawn"));
|
|
113
|
+
return yield* Effect.failCause(cause);
|
|
114
|
+
})));
|
|
117
115
|
if (machine.finalStates.has(machine.initial._tag)) {
|
|
118
116
|
if (lifecycle?.onFinal !== void 0) yield* lifecycle.onFinal(machine.initial);
|
|
119
117
|
yield* Ref.set(stoppedRef, true);
|
|
@@ -262,13 +260,10 @@ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function*
|
|
|
262
260
|
if (result.hasReply) {
|
|
263
261
|
const replySchema = machine._replySchemas?.get(event._tag);
|
|
264
262
|
if (replySchema !== void 0) {
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
decoded = Schema.decodeUnknownSync(replySchema)(result.reply);
|
|
268
|
-
} catch (decodeError) {
|
|
263
|
+
const decoded = yield* Schema.decodeUnknown(replySchema)(result.reply).pipe(Effect.catchAll((decodeError) => Effect.gen(function* () {
|
|
269
264
|
yield* Deferred.die(queued.reply, decodeError);
|
|
270
265
|
return yield* Effect.die(decodeError);
|
|
271
|
-
}
|
|
266
|
+
})));
|
|
272
267
|
yield* Deferred.succeed(queued.reply, decoded);
|
|
273
268
|
} else yield* Deferred.succeed(queued.reply, result.reply);
|
|
274
269
|
} else if (result.deferReply && deferredReplyRef !== void 0) deferredReplyRef.current = queued.reply;
|
package/v3/dist/machine.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { Cause, Context, Duration, Effect, Option, Schema, Scope } from "effect"
|
|
|
10
10
|
|
|
11
11
|
//#region src/machine.d.ts
|
|
12
12
|
declare namespace machine_d_exports {
|
|
13
|
-
export { BackgroundEffect, DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, make, materializeMachine, replay, reply, spawn };
|
|
13
|
+
export { BackgroundEffect, DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, make, materializeMachine, replay, reply, scoped, spawn };
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Self reference for sending events back to the machine
|
|
@@ -362,6 +362,26 @@ declare const spawn: <S extends {
|
|
|
362
362
|
supervision?: Supervision.Policy;
|
|
363
363
|
lifecycle?: Lifecycle<S, E>;
|
|
364
364
|
}) => Effect.Effect<ActorRef<S, E>, never, R>;
|
|
365
|
+
/**
|
|
366
|
+
* Wrap an effect to provide an `ActorScope` from the current `Scope`.
|
|
367
|
+
*
|
|
368
|
+
* Actors spawned inside will attach cleanup finalizers to this scope,
|
|
369
|
+
* so they are automatically stopped when the scope closes.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* yield* Effect.scoped(
|
|
374
|
+
* Machine.scoped(
|
|
375
|
+
* Effect.gen(function* () {
|
|
376
|
+
* const actor = yield* Machine.spawn(machine);
|
|
377
|
+
* yield* actor.start;
|
|
378
|
+
* // actor auto-stopped when scope closes
|
|
379
|
+
* }),
|
|
380
|
+
* ),
|
|
381
|
+
* );
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
declare const scoped: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R | Scope.Scope>;
|
|
365
385
|
declare const replay: {
|
|
366
386
|
<S extends {
|
|
367
387
|
readonly _tag: string;
|
|
@@ -375,4 +395,4 @@ declare const replay: {
|
|
|
375
395
|
declare const reply: <State, Reply>(state: State, reply: Reply) => ReplyResult<State, Reply>;
|
|
376
396
|
declare const deferReply: <State>(state: State) => DeferReplyResult<State>;
|
|
377
397
|
//#endregion
|
|
378
|
-
export { BackgroundEffect, type DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, type ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, machine_d_exports, make, materializeMachine, replay, reply, spawn };
|
|
398
|
+
export { BackgroundEffect, type DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, type ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, machine_d_exports, make, materializeMachine, replay, reply, scoped, spawn };
|
package/v3/dist/machine.js
CHANGED
|
@@ -5,7 +5,7 @@ import { findTransitions, invalidateIndex, resolveTransition, runTransitionHandl
|
|
|
5
5
|
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
6
6
|
import { Inspector } from "./inspection.js";
|
|
7
7
|
import { MachineContextTag } from "./slot.js";
|
|
8
|
-
import { createActor } from "./actor.js";
|
|
8
|
+
import { ActorScope, createActor } from "./actor.js";
|
|
9
9
|
import { Cause, Effect, Exit, Option, Random, Schema, Scope } from "effect";
|
|
10
10
|
//#region src/machine.ts
|
|
11
11
|
var machine_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -16,6 +16,7 @@ var machine_exports = /* @__PURE__ */ __exportAll({
|
|
|
16
16
|
materializeMachine: () => materializeMachine,
|
|
17
17
|
replay: () => replay,
|
|
18
18
|
reply: () => reply,
|
|
19
|
+
scoped: () => scoped,
|
|
19
20
|
spawn: () => spawn
|
|
20
21
|
});
|
|
21
22
|
const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOption(Inspector), (inspector) => Option.isNone(inspector) ? Effect.void : emitWithTimestamp(inspector.value, (timestamp) => ({
|
|
@@ -177,7 +178,8 @@ var Machine = class Machine {
|
|
|
177
178
|
this._slots = this._slotsSchema !== void 0 ? this._slotsSchema._createSlots(resolve) : {};
|
|
178
179
|
}
|
|
179
180
|
from(stateOrStates, build) {
|
|
180
|
-
|
|
181
|
+
const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
|
|
182
|
+
build(new TransitionScope(this, states));
|
|
181
183
|
return this;
|
|
182
184
|
}
|
|
183
185
|
/** @internal */
|
|
@@ -411,10 +413,30 @@ const spawn = Effect.fn("effect-machine.spawn")(function* (machine, idOrOptions)
|
|
|
411
413
|
supervision: opts?.supervision,
|
|
412
414
|
lifecycle: opts?.lifecycle
|
|
413
415
|
});
|
|
414
|
-
const maybeScope = yield* Effect.serviceOption(
|
|
416
|
+
const maybeScope = yield* Effect.serviceOption(ActorScope);
|
|
415
417
|
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
|
|
416
418
|
return actor;
|
|
417
419
|
});
|
|
420
|
+
/**
|
|
421
|
+
* Wrap an effect to provide an `ActorScope` from the current `Scope`.
|
|
422
|
+
*
|
|
423
|
+
* Actors spawned inside will attach cleanup finalizers to this scope,
|
|
424
|
+
* so they are automatically stopped when the scope closes.
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* yield* Effect.scoped(
|
|
429
|
+
* Machine.scoped(
|
|
430
|
+
* Effect.gen(function* () {
|
|
431
|
+
* const actor = yield* Machine.spawn(machine);
|
|
432
|
+
* yield* actor.start;
|
|
433
|
+
* // actor auto-stopped when scope closes
|
|
434
|
+
* }),
|
|
435
|
+
* ),
|
|
436
|
+
* );
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
const scoped = (effect) => Effect.flatMap(Scope.Scope, (scope) => Effect.provideService(effect, ActorScope, scope));
|
|
418
440
|
const replay = Effect.fn("effect-machine.replay")(function* (input, events, options) {
|
|
419
441
|
const machine = materializeMachine(input, options?.slots);
|
|
420
442
|
let state = options?.from ?? machine.initial;
|
|
@@ -462,4 +484,4 @@ const replay = Effect.fn("effect-machine.replay")(function* (input, events, opti
|
|
|
462
484
|
const reply = makeReply;
|
|
463
485
|
const deferReply = makeDeferReply;
|
|
464
486
|
//#endregion
|
|
465
|
-
export { Machine, deferReply, findTransitions, machine_exports, make, materializeMachine, replay, reply, spawn };
|
|
487
|
+
export { Machine, deferReply, findTransitions, machine_exports, make, materializeMachine, replay, reply, scoped, spawn };
|
package/v3/dist/schema.d.ts
CHANGED
|
@@ -49,16 +49,23 @@ type VariantReplyBrand<Fields extends Schema.Struct.Fields> = Fields extends {
|
|
|
49
49
|
* Empty structs: plain values with `_tag`: `State.Idle`
|
|
50
50
|
* Non-empty structs require args: `State.Loading({ url })`
|
|
51
51
|
*
|
|
52
|
-
* Each variant also has a `
|
|
52
|
+
* Each variant also has a `with` method for constructing from a source object,
|
|
53
|
+
* copying matching fields and overriding with a partial.
|
|
53
54
|
* The source type uses `object` to accept branded state types without index signature issues.
|
|
54
55
|
* Reply-bearing variants carry ReplyTypeBrand<R> for ask() type inference.
|
|
55
56
|
*/
|
|
56
57
|
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 & VariantReplyBrand<D[K]> & {
|
|
57
|
-
readonly
|
|
58
|
+
readonly with: (source: object) => TaggedStructType<K, D[K]> & Brand;
|
|
58
59
|
} : ((args: Schema.Struct.Type<PayloadFields<D[K]>>) => TaggedStructType<K, D[K]> & Brand & VariantReplyBrand<D[K]>) & {
|
|
59
|
-
readonly
|
|
60
|
+
readonly with: (source: object, partial?: Partial<Schema.Struct.Type<PayloadFields<D[K]>>>) => TaggedStructType<K, D[K]> & Brand;
|
|
60
61
|
readonly _tag: K;
|
|
61
62
|
} };
|
|
63
|
+
/**
|
|
64
|
+
* Keys present in ALL variants (intersection of field names).
|
|
65
|
+
* Used by union-level `with` to accept only fields safe to update
|
|
66
|
+
* regardless of which variant the source is.
|
|
67
|
+
*/
|
|
68
|
+
type SharedKeys<D extends Record<string, Schema.Struct.Fields>> = keyof D[keyof D & string] & string;
|
|
62
69
|
/**
|
|
63
70
|
* Pattern matching cases type
|
|
64
71
|
*/
|
|
@@ -87,19 +94,23 @@ interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Bran
|
|
|
87
94
|
<R>(value: VariantsUnion<D> & Brand, cases: MatchCases<D, R>): R;
|
|
88
95
|
};
|
|
89
96
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
97
|
+
* Copy fields from `source` into the same variant, overriding with `partial`.
|
|
98
|
+
* Preserves the specific variant subtype in the return.
|
|
92
99
|
*
|
|
93
|
-
*
|
|
100
|
+
* The partial accepts fields common to all variants, so it works safely
|
|
101
|
+
* when `S` is a generic type parameter (e.g., `<S extends MyState>`).
|
|
94
102
|
*
|
|
95
103
|
* @example
|
|
96
104
|
* ```ts
|
|
97
|
-
* //
|
|
98
|
-
* const
|
|
99
|
-
*
|
|
105
|
+
* // Per-variant field update
|
|
106
|
+
* const next = MyState.Streaming.with(state, { draft: newDraft })
|
|
107
|
+
*
|
|
108
|
+
* // Cross-variant shared field — works with generic state
|
|
109
|
+
* const updateQueue = <S extends MyState>(state: S, queue: Queue): S =>
|
|
110
|
+
* MyState.with(state, { queue })
|
|
100
111
|
* ```
|
|
101
112
|
*/
|
|
102
|
-
readonly
|
|
113
|
+
readonly with: <S extends VariantsUnion<D> & Brand>(source: S, partial?: Partial<Record<SharedKeys<D>, unknown>>) => S;
|
|
103
114
|
/**
|
|
104
115
|
* Reply schemas per variant tag. Only populated for event schemas
|
|
105
116
|
* with variants defined via `Event.reply()`.
|
|
@@ -116,14 +127,16 @@ interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Bran
|
|
|
116
127
|
* The D type parameter captures the definition, creating a unique brand
|
|
117
128
|
* per distinct schema definition shape.
|
|
118
129
|
*/
|
|
119
|
-
type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<VariantsUnion<D> & FullStateBrand<D>, unknown
|
|
130
|
+
type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<VariantsUnion<D> & FullStateBrand<D>, unknown> & MachineSchemaBase<D, FullStateBrand<D>> & VariantConstructors<D, FullStateBrand<D>> & {
|
|
131
|
+
/** Schema for persistence, config, and registration. */readonly schema: Schema.Schema<VariantsUnion<D> & FullStateBrand<D>>;
|
|
132
|
+
};
|
|
120
133
|
/**
|
|
121
134
|
* Schema-first event definition (same structure as state, different brand)
|
|
122
135
|
*
|
|
123
136
|
* The D type parameter captures the definition, creating a unique brand
|
|
124
137
|
* per distinct schema definition shape.
|
|
125
138
|
*/
|
|
126
|
-
type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<VariantsUnion<D> & FullEventBrand<D>, unknown
|
|
139
|
+
type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<VariantsUnion<D> & FullEventBrand<D>, unknown> & MachineSchemaBase<D, FullEventBrand<D>> & VariantConstructors<D, FullEventBrand<D>>;
|
|
127
140
|
/**
|
|
128
141
|
* Create a schema-first State definition.
|
|
129
142
|
*
|
package/v3/dist/schema.js
CHANGED
|
@@ -62,7 +62,7 @@ const buildMachineSchema = (definition) => {
|
|
|
62
62
|
_tag: tag
|
|
63
63
|
});
|
|
64
64
|
constructor._tag = tag;
|
|
65
|
-
constructor.
|
|
65
|
+
constructor.with = (source, partial) => {
|
|
66
66
|
const result = { _tag: tag };
|
|
67
67
|
for (const key of fieldNames) if (key in source) result[key] = source[key];
|
|
68
68
|
if (partial !== void 0) for (const [key, value] of Object.entries(partial)) {
|
|
@@ -75,7 +75,7 @@ const buildMachineSchema = (definition) => {
|
|
|
75
75
|
constructors[tag] = constructor;
|
|
76
76
|
} else constructors[tag] = {
|
|
77
77
|
_tag: tag,
|
|
78
|
-
|
|
78
|
+
with: () => ({ _tag: tag })
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
const variantArray = Object.values(variants);
|
|
@@ -112,20 +112,21 @@ const buildMachineSchema = (definition) => {
|
|
|
112
112
|
*/
|
|
113
113
|
const createMachineSchema = (definition) => {
|
|
114
114
|
const { schema, variants, constructors, _definition, replySchemas, $is, $match } = buildMachineSchema(definition);
|
|
115
|
-
const
|
|
115
|
+
const withFn = (source, partial) => {
|
|
116
116
|
const ctor = constructors[source._tag];
|
|
117
117
|
if (ctor === void 0) throw new MissingMatchHandlerError({ tag: source._tag });
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
120
|
-
return
|
|
118
|
+
const fn = ctor.with;
|
|
119
|
+
if (fn === void 0) throw new MissingMatchHandlerError({ tag: source._tag });
|
|
120
|
+
return fn(source, partial);
|
|
121
121
|
};
|
|
122
122
|
return Object.assign(Object.create(schema), {
|
|
123
123
|
variants,
|
|
124
124
|
_definition,
|
|
125
125
|
_replySchemas: replySchemas,
|
|
126
|
+
schema,
|
|
126
127
|
$is,
|
|
127
128
|
$match,
|
|
128
|
-
|
|
129
|
+
with: withFn,
|
|
129
130
|
...constructors
|
|
130
131
|
});
|
|
131
132
|
};
|
package/v3/dist/slot.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ interface SlotFnDef<F extends Fields = Fields, _Return = void> {
|
|
|
40
40
|
*/
|
|
41
41
|
declare const fn: {
|
|
42
42
|
<F extends Fields, S extends Schema.Schema.Any>(fields: F, returnSchema: S): SlotFnDef<F, Schema.Schema.Type<S>>;
|
|
43
|
-
<F extends Fields>(fields: F): SlotFnDef<F
|
|
43
|
+
<F extends Fields>(fields: F): SlotFnDef<F>;
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
46
46
|
* Record of slot definitions. Keys are slot names, values are SlotFnDef.
|
|
@@ -149,9 +149,10 @@ declare const define: <D extends SlotsDef>(definitions: D) => SlotsSchema<D>;
|
|
|
149
149
|
declare const Slot: {
|
|
150
150
|
readonly fn: {
|
|
151
151
|
<F extends Fields, S extends Schema.Schema.Any>(fields: F, returnSchema: S): SlotFnDef<F, Schema.Schema.Type<S>>;
|
|
152
|
-
<F extends Fields>(fields: F): SlotFnDef<F
|
|
152
|
+
<F extends Fields>(fields: F): SlotFnDef<F>;
|
|
153
153
|
};
|
|
154
154
|
readonly define: <D extends SlotsDef>(definitions: D) => SlotsSchema<D>;
|
|
155
|
+
readonly of: <D extends SlotsDef>(slotsSchema: SlotsSchema<D>, provided: ProvideSlots<D>) => SlotCalls<D>;
|
|
155
156
|
};
|
|
156
157
|
//#endregion
|
|
157
158
|
export { HasSlotKeys, MachineContext, MachineContextTag, ProvideSlots, Slot, SlotCall, SlotCalls, SlotFnDef, SlotHandler, SlotInvocation, SlotRequest, SlotResult, SlotsDef, SlotsSchema, define, fn };
|
package/v3/dist/slot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context, Schema } from "effect";
|
|
1
|
+
import { Context, Effect, Schema } from "effect";
|
|
2
2
|
//#region src/slot.ts
|
|
3
3
|
/**
|
|
4
4
|
* Slot module — unified, schema-based parameterized slots.
|
|
@@ -122,9 +122,44 @@ const define = (definitions) => {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
};
|
|
125
|
+
/**
|
|
126
|
+
* Convert raw slot handler implementations into the callable `SlotCalls` form.
|
|
127
|
+
*
|
|
128
|
+
* Handlers that return plain values are wrapped in `Effect.succeed`.
|
|
129
|
+
* Handlers that return Effects are called directly inside `Effect.suspend`.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const provided = yield* myExtension.slots(ctx)
|
|
134
|
+
* const slots = Slot.of(slotsSchema, provided)
|
|
135
|
+
* // slots.mySlot({ param: 1 }) returns Effect<ReturnType>
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
const of = (slotsSchema, provided) => {
|
|
139
|
+
const slots = {};
|
|
140
|
+
for (const name of Object.keys(slotsSchema.definitions)) {
|
|
141
|
+
const handler = provided[name];
|
|
142
|
+
if (handler === void 0) continue;
|
|
143
|
+
const call = (params) => Effect.suspend(() => {
|
|
144
|
+
const result = handler(params);
|
|
145
|
+
return Effect.isEffect(result) ? result : Effect.succeed(result);
|
|
146
|
+
});
|
|
147
|
+
Object.defineProperty(call, "_tag", {
|
|
148
|
+
value: "Slot",
|
|
149
|
+
enumerable: true
|
|
150
|
+
});
|
|
151
|
+
Object.defineProperty(call, "name", {
|
|
152
|
+
value: name,
|
|
153
|
+
enumerable: true
|
|
154
|
+
});
|
|
155
|
+
slots[name] = call;
|
|
156
|
+
}
|
|
157
|
+
return slots;
|
|
158
|
+
};
|
|
125
159
|
const Slot = {
|
|
126
160
|
fn,
|
|
127
|
-
define
|
|
161
|
+
define,
|
|
162
|
+
of
|
|
128
163
|
};
|
|
129
164
|
//#endregion
|
|
130
165
|
export { MachineContextTag, Slot, define, fn };
|