@typeonce/effect-machine 0.8.0 → 0.9.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.
Files changed (57) hide show
  1. package/README.md +64 -24
  2. package/dist/Machine.d.ts +386 -304
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +66 -171
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/activities.d.ts +5 -12
  7. package/dist/internal/machine/activities.d.ts.map +1 -1
  8. package/dist/internal/machine/activities.js +47 -17
  9. package/dist/internal/machine/activities.js.map +1 -1
  10. package/dist/internal/machine/atom.d.ts +2 -2
  11. package/dist/internal/machine/atom.d.ts.map +1 -1
  12. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  13. package/dist/internal/machine/executionPlan.js +25 -0
  14. package/dist/internal/machine/executionPlan.js.map +1 -1
  15. package/dist/internal/machine/invocation.d.ts +10 -2
  16. package/dist/internal/machine/invocation.d.ts.map +1 -1
  17. package/dist/internal/machine/invocation.js +93 -34
  18. package/dist/internal/machine/invocation.js.map +1 -1
  19. package/dist/internal/machine/invocationEvent.d.ts +39 -0
  20. package/dist/internal/machine/invocationEvent.d.ts.map +1 -0
  21. package/dist/internal/machine/invocationEvent.js +43 -0
  22. package/dist/internal/machine/invocationEvent.js.map +1 -0
  23. package/dist/internal/machine/machine.d.ts +6 -50
  24. package/dist/internal/machine/machine.d.ts.map +1 -1
  25. package/dist/internal/machine/machine.js +8 -65
  26. package/dist/internal/machine/machine.js.map +1 -1
  27. package/dist/internal/machine/planner.d.ts +2 -1
  28. package/dist/internal/machine/planner.d.ts.map +1 -1
  29. package/dist/internal/machine/planner.js +45 -2
  30. package/dist/internal/machine/planner.js.map +1 -1
  31. package/dist/internal/machine/protocol.d.ts +9 -0
  32. package/dist/internal/machine/protocol.d.ts.map +1 -1
  33. package/dist/internal/machine/protocol.js +114 -0
  34. package/dist/internal/machine/protocol.js.map +1 -1
  35. package/dist/internal/machine/symbols.d.ts +2 -0
  36. package/dist/internal/machine/symbols.d.ts.map +1 -1
  37. package/dist/internal/machine/symbols.js +2 -0
  38. package/dist/internal/machine/symbols.js.map +1 -1
  39. package/dist/internal/machine/topology.d.ts.map +1 -1
  40. package/dist/internal/machine/topology.js +23 -0
  41. package/dist/internal/machine/topology.js.map +1 -1
  42. package/dist/unstable/reactivity/AtomMachine.d.ts +3 -3
  43. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  44. package/docs/agent-guide.md +118 -93
  45. package/package.json +2 -2
  46. package/src/Machine.ts +1040 -651
  47. package/src/internal/machine/activities.ts +48 -33
  48. package/src/internal/machine/atom.ts +3 -3
  49. package/src/internal/machine/executionPlan.ts +29 -0
  50. package/src/internal/machine/invocation.ts +179 -48
  51. package/src/internal/machine/invocationEvent.ts +72 -0
  52. package/src/internal/machine/machine.ts +20 -346
  53. package/src/internal/machine/planner.ts +61 -10
  54. package/src/internal/machine/protocol.ts +149 -0
  55. package/src/internal/machine/symbols.ts +3 -0
  56. package/src/internal/machine/topology.ts +23 -0
  57. package/src/unstable/reactivity/AtomMachine.ts +3 -3
@@ -4,8 +4,7 @@
4
4
  * @since 0.4.0
5
5
  */
6
6
 
7
- /** @internal */
8
- export const ActivityMetadataTypeId: unique symbol = Symbol.for("effect/Machine/ActivityMetadata")
7
+ import * as Duration from "effect/Duration"
9
8
 
10
9
  /** @internal */
11
10
  export type StaticActivityMetadata =
@@ -21,8 +20,7 @@ export type StaticActivityMetadata =
21
20
  }
22
21
  | {
23
22
  readonly type: "timer"
24
- readonly duration: string
25
- readonly event: string
23
+ readonly duration: string | "dynamic"
26
24
  }
27
25
  | {
28
26
  readonly type: "machine"
@@ -33,20 +31,10 @@ export type StaticActivityMetadata =
33
31
  }
34
32
 
35
33
  /** @internal */
36
- export type ActivityDefinition<Source extends string = string> =
37
- | ({
38
- readonly source: Source
39
- readonly id: string
40
- } & StaticActivityMetadata)
41
- | {
42
- readonly source: Source
43
- readonly type: "dynamic"
44
- }
45
-
46
- interface ActivityDescriptor {
34
+ export type ActivityDefinition<Source extends string = string> = {
35
+ readonly source: Source
47
36
  readonly id: string
48
- readonly [ActivityMetadataTypeId]: StaticActivityMetadata
49
- }
37
+ } & StaticActivityMetadata
50
38
 
51
39
  interface InspectableMachine {
52
40
  readonly handlers: unknown
@@ -62,31 +50,60 @@ const isObject = (value: unknown): value is object =>
62
50
 
63
51
  const getProperty = (value: unknown, key: PropertyKey): unknown => isObject(value) ? Reflect.get(value, key) : undefined
64
52
 
65
- const hasActivityMetadata = (value: unknown): value is ActivityDescriptor =>
66
- isObject(value) && typeof getProperty(value, "id") === "string" &&
67
- isObject(getProperty(value, ActivityMetadataTypeId))
68
-
69
53
  const appendStaticDefinition = (
70
54
  definitions: Array<ActivityDefinition>,
71
55
  source: string,
72
56
  descriptor: unknown
73
57
  ): void => {
74
- if (!hasActivityMetadata(descriptor)) {
58
+ if (!isObject(descriptor)) return
59
+ const child = getProperty(descriptor, "child")
60
+ if (isObject(child)) {
61
+ const id = getProperty(child, "id")
62
+ if (typeof id !== "string") return
63
+ const machine = getProperty(child, "machine")
64
+ const machineId = getProperty(machine, "id")
65
+ definitions.push({
66
+ source,
67
+ id,
68
+ type: "machine",
69
+ child: { id, machineId: typeof machineId === "string" ? machineId : null }
70
+ })
75
71
  return
76
72
  }
77
- definitions.push({
78
- source,
79
- id: descriptor.id,
80
- ...descriptor[ActivityMetadataTypeId]
81
- })
73
+ const id = getProperty(descriptor, "id")
74
+ if (typeof id !== "string") return
75
+ if (Reflect.has(descriptor, "effect")) {
76
+ definitions.push({
77
+ source,
78
+ id,
79
+ type: "effect",
80
+ outcomes: {
81
+ success: "dynamic",
82
+ failure: Reflect.has(descriptor, "onFailure") ? "dynamic" : "none"
83
+ }
84
+ })
85
+ return
86
+ }
87
+ if (Reflect.has(descriptor, "after")) {
88
+ const after = getProperty(descriptor, "after")
89
+ definitions.push({
90
+ source,
91
+ id,
92
+ type: "timer",
93
+ duration: typeof after === "function" ? "dynamic" : Duration.format(Duration.fromInputUnsafe(after as any))
94
+ })
95
+ return
96
+ }
97
+ if (Reflect.has(descriptor, "logic")) {
98
+ definitions.push({ source, id, type: "process" })
99
+ }
82
100
  }
83
101
 
84
102
  /**
85
103
  * Collects state-owned activity descriptions without executing user code.
86
104
  *
87
- * Function-valued invoke definitions depend on an entry context and are
88
- * therefore represented as dynamic. Static descriptors are returned in state
89
- * definition order and descriptor array order.
105
+ * Source factories are inspected without execution. Static descriptors are
106
+ * returned in state definition order and descriptor array order.
90
107
  *
91
108
  * @internal
92
109
  */
@@ -94,9 +111,7 @@ export const activityDefinitions = (machine: InspectableMachine): ReadonlyArray<
94
111
  const definitions: Array<ActivityDefinition> = []
95
112
  for (const node of machine.stateNodes.byPath.values()) {
96
113
  const invoke = getProperty(getProperty(machine.handlers, node.path), "invoke")
97
- if (typeof invoke === "function") {
98
- definitions.push({ source: node.path, type: "dynamic" })
99
- } else if (Array.isArray(invoke)) {
114
+ if (Array.isArray(invoke)) {
100
115
  for (const descriptor of invoke) {
101
116
  appendStaticDefinition(definitions, node.path, descriptor)
102
117
  }
@@ -120,7 +120,7 @@ const startMachineAtomEffect = <
120
120
  ): Effect.Effect<
121
121
  Machine.MachineRef<
122
122
  Machine.Machine.Snapshot<States>,
123
- Machine.Machine.EventOf<InputEvents>,
123
+ Machine.Machine.EventInputOf<InputEvents>,
124
124
  MachineRuntimeError<E, R>,
125
125
  Output
126
126
  >,
@@ -585,7 +585,7 @@ type EnsureMachineExecutable<M extends Machine.Machine.Any> = IsAny<Machine.Mach
585
585
 
586
586
  type ResumedMachineAtomOf<M extends Machine.Machine.Any, RuntimeError> = MachineAtom<
587
587
  Machine.Machine.Snapshot<Machine.Machine.States<M>>,
588
- Machine.Machine.InputEvent<M>,
588
+ Machine.Machine.EventInput<Machine.Machine.InputEvent<M>>,
589
589
  MachineRuntimeError<Machine.Machine.Error<M>, Machine.Machine.Services<M>>,
590
590
  Machine.Machine.Output<M>,
591
591
  Machine.MachineSchemaDecodeError | RuntimeError
@@ -635,7 +635,7 @@ export const make: {
635
635
  ...args: [...Machine.Machine.InputArgs<Input>]
636
636
  ): MachineAtom<
637
637
  Machine.Machine.Snapshot<States>,
638
- Machine.Machine.EventOf<InputEvents>,
638
+ Machine.Machine.EventInputOf<InputEvents>,
639
639
  MachineRuntimeError<E, R>,
640
640
  Output,
641
641
  MachineStartError<InitialE, E, InitialR, R>
@@ -21,6 +21,7 @@ import {
21
21
  validateInitialConfiguration
22
22
  } from "./configuration.js"
23
23
  import { InfiniteTransitionError } from "./errors.js"
24
+ import * as InvocationEvent from "./invocationEvent.js"
24
25
  import {
25
26
  broadenTransitionBoundary,
26
27
  type EvaluatedTransition,
@@ -775,6 +776,34 @@ const planIndexedState = (
775
776
  input: unknown,
776
777
  retainMicrosteps: boolean
777
778
  ): ExecutionMacrostep<OwnedIndexedState> => {
779
+ if (InvocationEvent.isInvocationEvent(input)) {
780
+ // Invocation lifecycle transitions retain the generic planner as their
781
+ // semantic reference. Ordinary machine events and initialization stay on
782
+ // the indexed representation; only the private lifecycle macrostep crosses
783
+ // the representation boundary.
784
+ const planned = planConfiguration(
785
+ machine as any,
786
+ activeConfigurationFromIndexedState(descriptor, configuration),
787
+ input
788
+ )
789
+ return {
790
+ next: ownedIndexedStateFromActive(descriptor, planned.next),
791
+ commands: planned.commands,
792
+ emittedEvents: planned.emittedEvents,
793
+ microsteps: planned.microsteps.map((step) => ({
794
+ next: ownedIndexedStateFromActive(descriptor, step.next),
795
+ event: step.event,
796
+ commands: step.commands,
797
+ raisedEvents: step.raisedEvents,
798
+ emittedEvents: step.emittedEvents,
799
+ exitPaths: step.exitPaths,
800
+ entryPaths: step.entryPaths,
801
+ changed: step.changed
802
+ })),
803
+ done: planned.done,
804
+ output: planned.output
805
+ }
806
+ }
778
807
  const decoded = decodeEventSync(machine, input)
779
808
  if (descriptor.flat) {
780
809
  return planIndexedFlatState(machine, descriptor, configuration, decoded, retainMicrosteps)
@@ -4,28 +4,26 @@
4
4
  * @since 0.4.0
5
5
  */
6
6
 
7
+ import * as Cause from "effect/Cause"
7
8
  import * as Effect from "effect/Effect"
8
- import type { Machine } from "../../Machine.js"
9
+ import type { ChildMachine, Logic, Machine } from "../../Machine.js"
9
10
  import * as Configuration from "./configuration.js"
11
+ import { InfiniteTransitionError, MachineSchemaDecodeError, StoppedError } from "./errors.js"
12
+ import * as InvocationEvent from "./invocationEvent.js"
10
13
  import * as Planner from "./planner.js"
11
14
  import type * as Runtime from "./runtime.js"
15
+ import { ChildMachineLogicTypeId } from "./symbols.js"
12
16
 
13
17
  /** @internal */
14
- export type AnyConfig = Machine.InvokeConfig<
15
- any,
16
- any,
17
- any,
18
- any,
19
- any,
20
- any,
21
- any,
22
- any,
23
- any,
24
- any,
25
- any,
26
- any,
27
- any
28
- >
18
+ export interface AnyConfig {
19
+ readonly id: string
20
+ readonly address?: string
21
+ readonly descriptor?: ChildMachine.Any
22
+ readonly src: () => Runtime.ProcessLogic<any, any, any, any, any, any>
23
+ readonly onDone?: unknown
24
+ readonly onFailure?: unknown
25
+ readonly onSnapshot?: unknown
26
+ }
29
27
 
30
28
  /** @internal */
31
29
  export const makeKey = (path: string, id: string): string => `${path.length}:${path}${id}`
@@ -33,16 +31,79 @@ export const makeKey = (path: string, id: string): string => `${path.length}:${p
33
31
  /** @internal */
34
32
  export const makeChildId = (path: string, id: string): string => `Machine.invoke:${makeKey(path, id)}`
35
33
 
36
- const resolve = (
37
- config: Machine.AnyStateConfig | undefined,
34
+ const oneShot = (effect: Effect.Effect<any, any, any>): Logic<void, never, any, any, any> => ({
35
+ initial: () => Effect.void,
36
+ run: () => effect
37
+ })
38
+
39
+ const resolveValue = (value: unknown, context: Machine.InvokeContext<any, any, any, any>): unknown =>
40
+ typeof value === "function" ? value(context) : value
41
+
42
+ const resolveOne = (
43
+ raw: Record<PropertyKey, any>,
38
44
  context: Machine.InvokeContext<any, any, any, any>
39
- ): ReadonlyArray<AnyConfig> => {
40
- const definition = config?.invoke
41
- const invokes = typeof definition === "function" ? definition(context) : definition
42
- if (invokes === undefined) return []
43
- return Array.isArray(invokes) ? invokes as ReadonlyArray<AnyConfig> : [invokes as AnyConfig]
45
+ ): AnyConfig => {
46
+ if ("effect" in raw) {
47
+ return {
48
+ id: String(raw.id),
49
+ src: () =>
50
+ oneShot(resolveValue(raw.effect, context) as Effect.Effect<any, any, any>) as unknown as Runtime.ProcessLogic<
51
+ any,
52
+ any,
53
+ any,
54
+ any,
55
+ any,
56
+ any
57
+ >,
58
+ onDone: raw.onDone,
59
+ onFailure: raw.onFailure,
60
+ onSnapshot: raw.onSnapshot
61
+ }
62
+ }
63
+ if ("after" in raw) {
64
+ return {
65
+ id: String(raw.id),
66
+ src: () =>
67
+ oneShot(Effect.sleep(resolveValue(raw.after, context) as any)) as unknown as Runtime.ProcessLogic<
68
+ any,
69
+ any,
70
+ any,
71
+ any,
72
+ any,
73
+ any
74
+ >,
75
+ onDone: raw.onDone
76
+ }
77
+ }
78
+ if ("logic" in raw) {
79
+ return {
80
+ id: String(raw.id),
81
+ address: String(raw.address),
82
+ src: () => resolveValue(raw.logic, context) as Runtime.ProcessLogic<any, any, any, any, any, any>,
83
+ onDone: raw.onDone,
84
+ onFailure: raw.onFailure,
85
+ onSnapshot: raw.onSnapshot
86
+ }
87
+ }
88
+ if ("child" in raw) {
89
+ const descriptor = raw.child as ChildMachine.Any
90
+ return {
91
+ id: descriptor.id,
92
+ address: descriptor.id,
93
+ descriptor,
94
+ src: () =>
95
+ descriptor[ChildMachineLogicTypeId](
96
+ "input" in raw ? resolveValue(raw.input, context) : undefined
97
+ ) as Runtime.ProcessLogic<any, any, any, any, any, any>,
98
+ onDone: raw.onDone,
99
+ onFailure: raw.onFailure,
100
+ onSnapshot: raw.onSnapshot
101
+ }
102
+ }
103
+ throw new Error("Machine invoke must define exactly one of effect, after, logic, or child")
44
104
  }
45
105
 
106
+ /** @internal */
46
107
  const runSequentialDiscard = <E, R>(
47
108
  effects: ReadonlyArray<Effect.Effect<void, E, R>>
48
109
  ): Effect.Effect<void, E, R> =>
@@ -52,45 +113,110 @@ const runSequentialDiscard = <E, R>(
52
113
  ? effects[0]!
53
114
  : Effect.all(effects, { discard: true })
54
115
 
55
- const start = (
116
+ const sendLifecycle = (
117
+ scope: Runtime.ProcessScope<any>,
118
+ event: InvocationEvent.InvocationEvent
119
+ ): Effect.Effect<void> => scope.self.send(event).pipe(Effect.catchTag("StoppedError", () => Effect.void))
120
+
121
+ const isFrameworkFailure = (error: unknown): boolean =>
122
+ error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError || error instanceof StoppedError
123
+
124
+ const startResolved = (
56
125
  scope: Runtime.ProcessScope<any>,
57
126
  ownedChildren: Runtime.OwnedChildRuntime,
58
127
  path: string,
59
- config: AnyConfig
128
+ invokeId: string,
129
+ childId: string,
130
+ descriptor: ChildMachine.Any | undefined,
131
+ src: () => Runtime.ProcessLogic<any, any, any, any, any, any>,
132
+ onDone: unknown,
133
+ onFailure: unknown,
134
+ onSnapshot: unknown
60
135
  ): Effect.Effect<void, any, any> =>
61
136
  Effect.suspend(() => {
62
- const invokeId = String(config.id)
63
137
  const key = makeKey(path, invokeId)
64
- const childId = config.address === undefined ? makeChildId(path, invokeId) : String(config.address)
65
- return ownedChildren.spawn(config.src as () => Runtime.ProcessLogic<any, any, any, any, any, any>, {
138
+ return ownedChildren.spawn(src, {
66
139
  key,
67
140
  path,
68
141
  id: childId,
69
142
  duplicateId: invokeId,
70
- ...(config.descriptor === undefined ? undefined : { descriptor: config.descriptor }),
143
+ ...(descriptor === undefined ? undefined : { descriptor }),
71
144
  sendParent: (isCurrent, event) => isCurrent() ? scope.self.send(event) : Effect.void,
72
145
  onOutcome: (isCurrent, outcome) => {
73
146
  if (outcome._tag === "Stopped" || !isCurrent()) return Effect.void
74
- if (outcome._tag !== "Done") return scope.failCause(outcome.cause)
75
- const mappedEvent = config.onDone === undefined
76
- ? outcome.output
77
- : config.onDone({ id: config.id, output: outcome.output })
78
- return mappedEvent === undefined
79
- ? Effect.void
80
- : scope.self.send(mappedEvent).pipe(Effect.catchTag("StoppedError", () => Effect.void))
81
- },
82
- ...(config.snapshot === undefined ? undefined : {
83
- onSnapshot: (isCurrent: () => boolean, snapshot: any) => {
84
- if (!isCurrent()) return Effect.void
85
- const mappedEvent = config.snapshot!({ id: config.id, snapshot })
86
- return mappedEvent === undefined
87
- ? Effect.void
88
- : scope.self.send(mappedEvent).pipe(Effect.catchTag("StoppedError", () => Effect.void))
147
+ if (outcome._tag === "Done") {
148
+ if (onDone === undefined) {
149
+ return scope.failCause(Cause.die(
150
+ new Error(
151
+ `Invocation "${invokeId}" completed without the required onDone handler`
152
+ )
153
+ ))
154
+ }
155
+ return sendLifecycle(scope, InvocationEvent.done(path, invokeId, outcome.output))
89
156
  }
157
+ if (
158
+ outcome._tag === "Failure" &&
159
+ onFailure !== undefined &&
160
+ (descriptor === undefined || !isFrameworkFailure(outcome.error))
161
+ ) {
162
+ return sendLifecycle(scope, InvocationEvent.failure(path, invokeId, outcome.error))
163
+ }
164
+ return scope.failCause(outcome.cause)
165
+ },
166
+ ...(onSnapshot === undefined ? undefined : {
167
+ onSnapshot: (isCurrent: () => boolean, snapshot: any) =>
168
+ isCurrent()
169
+ ? sendLifecycle(scope, InvocationEvent.snapshot(path, invokeId, snapshot))
170
+ : Effect.void
90
171
  })
91
172
  })
92
173
  })
93
174
 
175
+ const start = (
176
+ scope: Runtime.ProcessScope<any>,
177
+ ownedChildren: Runtime.OwnedChildRuntime,
178
+ path: string,
179
+ config: AnyConfig
180
+ ): Effect.Effect<void, any, any> => {
181
+ const invokeId = String(config.id)
182
+ return startResolved(
183
+ scope,
184
+ ownedChildren,
185
+ path,
186
+ invokeId,
187
+ config.address === undefined ? makeChildId(path, invokeId) : String(config.address),
188
+ config.descriptor,
189
+ config.src,
190
+ config.onDone,
191
+ config.onFailure,
192
+ config.onSnapshot
193
+ )
194
+ }
195
+
196
+ const startStaticChild = (
197
+ scope: Runtime.ProcessScope<any>,
198
+ ownedChildren: Runtime.OwnedChildRuntime,
199
+ path: string,
200
+ raw: Record<PropertyKey, any>
201
+ ): Effect.Effect<void, any, any> => {
202
+ // A zero-input child has no entry-context dependency. Reuse the descriptor's
203
+ // source function so each running parent does not retain a resolved config
204
+ // object and an otherwise redundant source closure.
205
+ const descriptor = raw.child as ChildMachine.Any
206
+ return startResolved(
207
+ scope,
208
+ ownedChildren,
209
+ path,
210
+ descriptor.id,
211
+ descriptor.id,
212
+ descriptor,
213
+ descriptor[ChildMachineLogicTypeId],
214
+ raw.onDone,
215
+ raw.onFailure,
216
+ raw.onSnapshot
217
+ )
218
+ }
219
+
94
220
  /**
95
221
  * Starts every invocation owned by active entry paths in deterministic entry
96
222
  * order. `undefined` keeps the compiled drain free of empty Effect nodes.
@@ -107,13 +233,18 @@ export const startAll = (
107
233
  ): Effect.Effect<void, any, any> | undefined => {
108
234
  const effects = Planner.sortEntryPaths(machine, paths)
109
235
  .filter((path) => configuration.active.has(path))
110
- .flatMap((path) =>
111
- resolve(Configuration.getStateConfigByPath(machine, path), {
236
+ .flatMap((path) => {
237
+ const context = {
112
238
  state: configuration.values.get(path),
113
239
  parent: Configuration.getParentValue(machine, configuration, path),
114
240
  parents: Configuration.getParentValues(machine, configuration, path),
115
241
  event
116
- }).map((config) => start(scope, ownedChildren, path, config))
117
- )
242
+ }
243
+ return InvocationEvent.definitions(Configuration.getStateConfigByPath(machine, path)?.invoke).map((definition) =>
244
+ "child" in definition && !("input" in definition)
245
+ ? startStaticChild(scope, ownedChildren, path, definition)
246
+ : start(scope, ownedChildren, path, resolveOne(definition, context))
247
+ )
248
+ })
118
249
  return effects.length === 0 ? undefined : runSequentialDiscard(effects)
119
250
  }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Private mailbox messages used to route invocation lifecycle changes through
3
+ * the owning machine planner.
4
+ *
5
+ * @since 0.9.0
6
+ */
7
+
8
+ /** @internal */
9
+ export const InvocationEventTypeId: unique symbol = Symbol("effect/Machine/InvocationEvent")
10
+
11
+ /** @internal */
12
+ export type InvocationEvent =
13
+ | {
14
+ readonly [InvocationEventTypeId]: true
15
+ readonly path: string
16
+ readonly id: string
17
+ readonly type: "done"
18
+ readonly output: unknown
19
+ }
20
+ | {
21
+ readonly [InvocationEventTypeId]: true
22
+ readonly path: string
23
+ readonly id: string
24
+ readonly type: "failure"
25
+ readonly error: unknown
26
+ }
27
+ | {
28
+ readonly [InvocationEventTypeId]: true
29
+ readonly path: string
30
+ readonly id: string
31
+ readonly type: "snapshot"
32
+ readonly snapshot: unknown
33
+ }
34
+
35
+ /** @internal */
36
+ export const done = (path: string, id: string, output: unknown): InvocationEvent => ({
37
+ [InvocationEventTypeId]: true,
38
+ path,
39
+ id,
40
+ type: "done",
41
+ output
42
+ })
43
+
44
+ /** @internal */
45
+ export const failure = (path: string, id: string, error: unknown): InvocationEvent => ({
46
+ [InvocationEventTypeId]: true,
47
+ path,
48
+ id,
49
+ type: "failure",
50
+ error
51
+ })
52
+
53
+ /** @internal */
54
+ export const snapshot = (path: string, id: string, value: unknown): InvocationEvent => ({
55
+ [InvocationEventTypeId]: true,
56
+ path,
57
+ id,
58
+ type: "snapshot",
59
+ snapshot: value
60
+ })
61
+
62
+ /** @internal */
63
+ export const isInvocationEvent = (value: unknown): value is InvocationEvent =>
64
+ typeof value === "object" && value !== null && InvocationEventTypeId in value
65
+
66
+ /** @internal */
67
+ export const definitions = (invoke: unknown): ReadonlyArray<Record<PropertyKey, any>> => {
68
+ if (invoke === undefined) return []
69
+ return Array.isArray(invoke)
70
+ ? invoke as ReadonlyArray<Record<PropertyKey, any>>
71
+ : [invoke as Record<PropertyKey, any>]
72
+ }