@effect/workflow 0.1.3 → 0.1.5
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/dist/cjs/Activity.js +17 -8
- package/dist/cjs/Activity.js.map +1 -1
- package/dist/cjs/DurableDeferred.js +40 -2
- package/dist/cjs/DurableDeferred.js.map +1 -1
- package/dist/cjs/Workflow.js +31 -21
- package/dist/cjs/Workflow.js.map +1 -1
- package/dist/cjs/WorkflowEngine.js +14 -1
- package/dist/cjs/WorkflowEngine.js.map +1 -1
- package/dist/dts/Activity.d.ts +13 -6
- package/dist/dts/Activity.d.ts.map +1 -1
- package/dist/dts/DurableDeferred.d.ts +28 -1
- package/dist/dts/DurableDeferred.d.ts.map +1 -1
- package/dist/dts/Workflow.d.ts +6 -1
- package/dist/dts/Workflow.d.ts.map +1 -1
- package/dist/dts/WorkflowEngine.d.ts +6 -5
- package/dist/dts/WorkflowEngine.d.ts.map +1 -1
- package/dist/esm/Activity.js +15 -6
- package/dist/esm/Activity.js.map +1 -1
- package/dist/esm/DurableDeferred.js +38 -1
- package/dist/esm/DurableDeferred.js.map +1 -1
- package/dist/esm/Workflow.js +28 -19
- package/dist/esm/Workflow.js.map +1 -1
- package/dist/esm/WorkflowEngine.js +14 -1
- package/dist/esm/WorkflowEngine.js.map +1 -1
- package/package.json +4 -4
- package/src/Activity.ts +47 -13
- package/src/DurableDeferred.ts +103 -2
- package/src/Workflow.ts +51 -22
- package/src/WorkflowEngine.ts +19 -10
package/src/DurableDeferred.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
+
import type { NonEmptyReadonlyArray } from "effect/Array"
|
|
4
5
|
import type * as Brand from "effect/Brand"
|
|
5
6
|
import type * as Cause from "effect/Cause"
|
|
6
7
|
import * as Context from "effect/Context"
|
|
@@ -10,7 +11,7 @@ import * as Exit from "effect/Exit"
|
|
|
10
11
|
import { dual } from "effect/Function"
|
|
11
12
|
import * as Option from "effect/Option"
|
|
12
13
|
import * as Schema from "effect/Schema"
|
|
13
|
-
import
|
|
14
|
+
import * as Workflow from "./Workflow.js"
|
|
14
15
|
import type { WorkflowEngine, WorkflowInstance } from "./WorkflowEngine.js"
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -94,7 +95,7 @@ const await_: <Success extends Schema.Schema.Any, Error extends Schema.Schema.Al
|
|
|
94
95
|
>(self: DurableDeferred<Success, Error>) {
|
|
95
96
|
const engine = yield* EngineTag
|
|
96
97
|
const instance = yield* InstanceTag
|
|
97
|
-
const oexit = yield* engine.deferredResult(self)
|
|
98
|
+
const oexit = yield* Workflow.wrapActivityResult(engine.deferredResult(self), Option.isNone)
|
|
98
99
|
if (Option.isNone(oexit)) {
|
|
99
100
|
instance.suspended = true
|
|
100
101
|
return yield* Effect.interrupt
|
|
@@ -112,6 +113,106 @@ export {
|
|
|
112
113
|
await_ as await
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
/**
|
|
117
|
+
* @since 1.0.0
|
|
118
|
+
* @category Combinators
|
|
119
|
+
*/
|
|
120
|
+
export const into: {
|
|
121
|
+
/**
|
|
122
|
+
* @since 1.0.0
|
|
123
|
+
* @category Combinators
|
|
124
|
+
*/
|
|
125
|
+
<Success extends Schema.Schema.Any, Error extends Schema.Schema.All>(self: DurableDeferred<Success, Error>): <R>(effect: Effect.Effect<Success["Type"], Error["Type"], R>) => Effect.Effect<
|
|
126
|
+
Success["Type"],
|
|
127
|
+
Error["Type"],
|
|
128
|
+
R | WorkflowEngine | WorkflowInstance | Success["Context"] | Error["Context"]
|
|
129
|
+
>
|
|
130
|
+
/**
|
|
131
|
+
* @since 1.0.0
|
|
132
|
+
* @category Combinators
|
|
133
|
+
*/
|
|
134
|
+
<Success extends Schema.Schema.Any, Error extends Schema.Schema.All, R>(
|
|
135
|
+
effect: Effect.Effect<Success["Type"], Error["Type"], R>,
|
|
136
|
+
self: DurableDeferred<Success, Error>
|
|
137
|
+
): Effect.Effect<
|
|
138
|
+
Success["Type"],
|
|
139
|
+
Error["Type"],
|
|
140
|
+
R | WorkflowEngine | WorkflowInstance | Success["Context"] | Error["Context"]
|
|
141
|
+
>
|
|
142
|
+
} = dual(2, <Success extends Schema.Schema.Any, Error extends Schema.Schema.All, R>(
|
|
143
|
+
effect: Effect.Effect<Success["Type"], Error["Type"], R>,
|
|
144
|
+
self: DurableDeferred<Success, Error>
|
|
145
|
+
): Effect.Effect<
|
|
146
|
+
Success["Type"],
|
|
147
|
+
Error["Type"],
|
|
148
|
+
R | WorkflowEngine | WorkflowInstance | Success["Context"] | Error["Context"]
|
|
149
|
+
> =>
|
|
150
|
+
Effect.contextWithEffect((context: Context.Context<WorkflowEngine | WorkflowInstance>) => {
|
|
151
|
+
const engine = Context.get(context, EngineTag)
|
|
152
|
+
const instance = Context.get(context, InstanceTag)
|
|
153
|
+
return Effect.onExit(
|
|
154
|
+
effect,
|
|
155
|
+
Effect.fnUntraced(function*(exit) {
|
|
156
|
+
if (instance.suspended) return
|
|
157
|
+
const encodedExit = yield* Effect.orDie(Schema.encode(self.exitSchema)(exit))
|
|
158
|
+
yield* engine.deferredDone({
|
|
159
|
+
workflowName: instance.workflow.name,
|
|
160
|
+
executionId: instance.executionId,
|
|
161
|
+
deferred: self,
|
|
162
|
+
exit: encodedExit as any
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
)
|
|
166
|
+
}))
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @since 1.0.0
|
|
170
|
+
* @category Racing
|
|
171
|
+
*/
|
|
172
|
+
export const raceAll = <
|
|
173
|
+
const Effects extends NonEmptyReadonlyArray<Effect.Effect<any, any, any>>,
|
|
174
|
+
SI,
|
|
175
|
+
SR,
|
|
176
|
+
EI,
|
|
177
|
+
ER
|
|
178
|
+
>(options: {
|
|
179
|
+
name: string
|
|
180
|
+
success: Schema.Schema<
|
|
181
|
+
Effects[number] extends Effect.Effect<infer S, infer _E, infer _R> ? S : never,
|
|
182
|
+
SI,
|
|
183
|
+
SR
|
|
184
|
+
>
|
|
185
|
+
error: Schema.Schema<
|
|
186
|
+
Effects[number] extends Effect.Effect<infer _S, infer E, infer _R> ? E : never,
|
|
187
|
+
EI,
|
|
188
|
+
ER
|
|
189
|
+
>
|
|
190
|
+
effects: Effects
|
|
191
|
+
}): Effect.Effect<
|
|
192
|
+
(Effects[number] extends Effect.Effect<infer _A, infer _E, infer _R> ? _A : never),
|
|
193
|
+
(Effects[number] extends Effect.Effect<infer _A, infer _E, infer _R> ? _E : never),
|
|
194
|
+
| (Effects[number] extends Effect.Effect<infer _A, infer _R, infer R> ? R : never)
|
|
195
|
+
| SR
|
|
196
|
+
| ER
|
|
197
|
+
| WorkflowEngine
|
|
198
|
+
| WorkflowInstance
|
|
199
|
+
> => {
|
|
200
|
+
const deferred = make<any, any>(`raceAll/${options.name}`, {
|
|
201
|
+
success: options.success,
|
|
202
|
+
error: options.error
|
|
203
|
+
})
|
|
204
|
+
return Effect.gen(function*() {
|
|
205
|
+
const engine = yield* EngineTag
|
|
206
|
+
const oexit = yield* Workflow.wrapActivityResult(engine.deferredResult(deferred), Option.isNone)
|
|
207
|
+
if (Option.isSome(oexit)) {
|
|
208
|
+
return yield* (Effect.flatten(Effect.orDie(
|
|
209
|
+
Schema.decodeUnknown(deferred.exitSchema)(oexit.value)
|
|
210
|
+
)) as Effect.Effect<any, any, any>)
|
|
211
|
+
}
|
|
212
|
+
return yield* into(Effect.raceAll(options.effects), deferred)
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
115
216
|
/**
|
|
116
217
|
* @since 1.0.0
|
|
117
218
|
* @category Token
|
package/src/Workflow.ts
CHANGED
|
@@ -291,7 +291,6 @@ export const make = <
|
|
|
291
291
|
if (result._tag === "Complete") {
|
|
292
292
|
return yield* result.exit as Exit.Exit<Success["Type"], Error["Type"]>
|
|
293
293
|
}
|
|
294
|
-
// @effect-diagnostics effect/floatingEffect:off
|
|
295
294
|
sleep ??= (yield* Schedule.driver(suspendedRetrySchedule)).next(void 0).pipe(
|
|
296
295
|
Effect.catchAll(() => Effect.dieMessage(`${options.name}.execute: suspendedRetrySchedule exhausted`))
|
|
297
296
|
)
|
|
@@ -480,18 +479,47 @@ export const Result = <Success extends Schema.Schema.Any, Error extends Schema.S
|
|
|
480
479
|
*/
|
|
481
480
|
export const intoResult = <A, E, R>(
|
|
482
481
|
effect: Effect.Effect<A, E, R>
|
|
483
|
-
): Effect.Effect<Result<A, E>, never, R | WorkflowInstance> =>
|
|
484
|
-
Effect.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
482
|
+
): Effect.Effect<Result<A, E>, never, Exclude<R, Scope.Scope> | WorkflowInstance> =>
|
|
483
|
+
Effect.contextWithEffect((context: Context.Context<WorkflowInstance>) => {
|
|
484
|
+
const instance = Context.get(context, InstanceTag)
|
|
485
|
+
return Effect.uninterruptibleMask((restore) =>
|
|
486
|
+
restore(effect).pipe(
|
|
487
|
+
Effect.scoped,
|
|
488
|
+
Effect.matchCause({
|
|
489
|
+
onSuccess: (value) => new Complete({ exit: Exit.succeed(value) }),
|
|
490
|
+
onFailure: (cause) => instance.suspended ? new Suspended() : new Complete({ exit: Exit.failCause(cause) })
|
|
491
|
+
})
|
|
492
|
+
)
|
|
493
493
|
)
|
|
494
|
-
)
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* @since 1.0.0
|
|
498
|
+
* @category Result
|
|
499
|
+
*/
|
|
500
|
+
export const wrapActivityResult = <A, E, R>(
|
|
501
|
+
effect: Effect.Effect<A, E, R>,
|
|
502
|
+
isSuspend: (value: A) => boolean
|
|
503
|
+
): Effect.Effect<A, E, R | WorkflowInstance> =>
|
|
504
|
+
Effect.contextWithEffect((context: Context.Context<WorkflowInstance>) => {
|
|
505
|
+
const instance = Context.get(context, InstanceTag)
|
|
506
|
+
const state = instance.activityState
|
|
507
|
+
if (instance.suspended) {
|
|
508
|
+
return state.count > 0 ?
|
|
509
|
+
state.latch.await.pipe(
|
|
510
|
+
Effect.andThen(Effect.yieldNow()),
|
|
511
|
+
Effect.andThen(Effect.interrupt)
|
|
512
|
+
) :
|
|
513
|
+
Effect.interrupt
|
|
514
|
+
}
|
|
515
|
+
if (state.count === 0) state.latch.unsafeClose()
|
|
516
|
+
state.count++
|
|
517
|
+
return Effect.onExit(effect, (exit) => {
|
|
518
|
+
state.count--
|
|
519
|
+
const isSuspended = Exit.isSuccess(exit) && isSuspend(exit.value)
|
|
520
|
+
return state.count === 0 ? state.latch.open : isSuspended ? state.latch.await : Effect.void
|
|
521
|
+
})
|
|
522
|
+
})
|
|
495
523
|
|
|
496
524
|
/**
|
|
497
525
|
* Add compensation logic to an effect inside a Workflow. The compensation finalizer will be
|
|
@@ -541,14 +569,15 @@ export const withCompensation: {
|
|
|
541
569
|
compensation: (value: A, cause: Cause.Cause<unknown>) => Effect.Effect<void, never, R2>
|
|
542
570
|
): Effect.Effect<A, E, R | R2 | WorkflowInstance | Scope.Scope> =>
|
|
543
571
|
Effect.uninterruptibleMask((restore) =>
|
|
544
|
-
Effect.
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
Effect.
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
572
|
+
Effect.tap(
|
|
573
|
+
restore(effect),
|
|
574
|
+
(value) =>
|
|
575
|
+
Effect.contextWithEffect((context: Context.Context<WorkflowInstance>) =>
|
|
576
|
+
Effect.addFinalizer((exit) =>
|
|
577
|
+
Exit.isSuccess(exit) || Context.get(context, InstanceTag).suspended
|
|
578
|
+
? Effect.void
|
|
579
|
+
: compensation(value, exit.cause)
|
|
580
|
+
)
|
|
581
|
+
)
|
|
582
|
+
)
|
|
554
583
|
))
|
package/src/WorkflowEngine.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
import * as Context from "effect/Context"
|
|
5
|
-
import
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
6
|
import type * as Option from "effect/Option"
|
|
7
7
|
import type * as Schema from "effect/Schema"
|
|
8
8
|
import type * as Activity from "./Activity.js"
|
|
@@ -48,14 +48,6 @@ export class WorkflowEngine extends Context.Tag("@effect/workflow/WorkflowEngine
|
|
|
48
48
|
executionId: string
|
|
49
49
|
) => Effect.Effect<void>
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
* Resume a suspended workflow.
|
|
53
|
-
*/
|
|
54
|
-
readonly resume: (
|
|
55
|
-
workflowName: string,
|
|
56
|
-
executionId: string
|
|
57
|
-
) => Effect.Effect<void>
|
|
58
|
-
|
|
59
51
|
/**
|
|
60
52
|
* Execute an activity from a workflow.
|
|
61
53
|
*/
|
|
@@ -118,5 +110,22 @@ export class WorkflowInstance extends Context.Tag("@effect/workflow/WorkflowEngi
|
|
|
118
110
|
* Whether the workflow has requested to be suspended.
|
|
119
111
|
*/
|
|
120
112
|
suspended: boolean
|
|
113
|
+
|
|
114
|
+
readonly activityState: {
|
|
115
|
+
count: number
|
|
116
|
+
readonly latch: Effect.Latch
|
|
117
|
+
}
|
|
121
118
|
}
|
|
122
|
-
>() {
|
|
119
|
+
>() {
|
|
120
|
+
static initial(workflow: Workflow.Any, executionId: string): WorkflowInstance["Type"] {
|
|
121
|
+
return WorkflowInstance.of({
|
|
122
|
+
executionId,
|
|
123
|
+
workflow,
|
|
124
|
+
suspended: false,
|
|
125
|
+
activityState: {
|
|
126
|
+
count: 0,
|
|
127
|
+
latch: Effect.unsafeMakeLatch()
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
}
|