effect-cf 0.0.4 → 0.2.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.
@@ -0,0 +1,129 @@
1
+ import { Effect, Schema as S, type Layer, type Scope } from "effect";
2
+
3
+ import type { WorkerEnvironment } from "./Environment";
4
+ import * as QueueEntrypoint from "./Queue";
5
+ import * as QueueBinding from "./QueueBinding";
6
+ import type { ExecutionContext, WorkerContext } from "./Worker";
7
+ import * as WorkerEntrypoint from "./Worker";
8
+ import type * as RpcDefinition from "./RpcDefinition";
9
+
10
+ export interface Definition<
11
+ Id extends string = string,
12
+ Message extends RpcDefinition.ServiceFreeSchema = RpcDefinition.ServiceFreeSchema,
13
+ > {
14
+ readonly id: Id;
15
+ readonly message: Message;
16
+ }
17
+
18
+ export namespace Definition {
19
+ export type Any = Definition<string, RpcDefinition.ServiceFreeSchema>;
20
+ }
21
+
22
+ declare const BindingServiceTypeId: unique symbol;
23
+
24
+ export interface BindingService<Id extends string, Self extends Definition.Any> {
25
+ readonly [BindingServiceTypeId]: {
26
+ readonly id: Id;
27
+ readonly definition: Self;
28
+ };
29
+ }
30
+
31
+ export type Handler<ROut, Self extends Definition.Any> = (
32
+ batch: QueueEntrypoint.QueueBatch<S.Schema.Type<Self["message"]>>,
33
+ ) => Effect.Effect<
34
+ void,
35
+ unknown,
36
+ ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment | Scope.Scope | ROut
37
+ >;
38
+
39
+ export interface Options<ROut, Self extends Definition.Any> extends Omit<
40
+ WorkerEntrypoint.WorkerOptions<ROut, Record<never, never>>,
41
+ "queue" | "rpc"
42
+ > {
43
+ readonly queue: Handler<ROut, Self>;
44
+ readonly rpc?: never;
45
+ }
46
+
47
+ const makeDefinition = <Id extends string, Message extends RpcDefinition.ServiceFreeSchema>(
48
+ id: Id,
49
+ definition: { readonly message: Message },
50
+ ) => {
51
+ type SelfDefinition = Definition<Id, Message>;
52
+ const queueDefinition: SelfDefinition = {
53
+ id,
54
+ message: definition.message,
55
+ };
56
+
57
+ return Object.assign(queueDefinition, {
58
+ make: <ROut, LayerError>(
59
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
60
+ options: Options<ROut, SelfDefinition>,
61
+ ) =>
62
+ WorkerEntrypoint.make(layer, {
63
+ ...options,
64
+ queue: wrapHandler(queueDefinition, options.queue),
65
+ }),
66
+ Binding:
67
+ <Self>() =>
68
+ <BindingId extends string>(
69
+ bindingId: BindingId,
70
+ binding: Omit<QueueBinding.QueueBindingDefinition<Message>, "message">,
71
+ ) =>
72
+ QueueBinding.Service<Self>()<BindingId, Message>(bindingId, {
73
+ ...binding,
74
+ message: definition.message,
75
+ }),
76
+ binding: <BindingId extends string>(
77
+ bindingId: BindingId,
78
+ binding: Omit<QueueBinding.QueueBindingDefinition<Message>, "message">,
79
+ ) =>
80
+ QueueBinding.Service<BindingService<BindingId, SelfDefinition>>()<BindingId, Message>(
81
+ bindingId,
82
+ {
83
+ ...binding,
84
+ message: definition.message,
85
+ },
86
+ ),
87
+ });
88
+ };
89
+
90
+ export const make = makeDefinition;
91
+
92
+ export const Tag =
93
+ <_Self>() =>
94
+ <Id extends string, Message extends RpcDefinition.ServiceFreeSchema>(
95
+ id: Id,
96
+ definition: { readonly message: Message },
97
+ ) => {
98
+ const queueDefinition = makeDefinition(id, definition);
99
+
100
+ abstract class QueueDefinitionClass {
101
+ static readonly id = queueDefinition.id;
102
+ static readonly message = queueDefinition.message;
103
+ static readonly make = queueDefinition.make;
104
+ static readonly Binding = queueDefinition.Binding;
105
+ static readonly binding = queueDefinition.binding;
106
+ }
107
+
108
+ return QueueDefinitionClass as (abstract new () => object) & typeof queueDefinition;
109
+ };
110
+
111
+ export const Queue = Tag;
112
+
113
+ const wrapHandler = <ROut, const Self extends Definition.Any>(
114
+ definition: Self,
115
+ handler: Handler<ROut, Self>,
116
+ ): QueueEntrypoint.QueueHandler<ROut> => {
117
+ const decodeBody = S.decodeUnknownEffect(definition.message);
118
+
119
+ return (batch) =>
120
+ Effect.gen(function* () {
121
+ const decoded = yield* QueueEntrypoint.decodeBatch(batch.raw, decodeBody);
122
+ yield* handler(decoded);
123
+ });
124
+ };
125
+
126
+ export const implement = <ROut, const Self extends Definition.Any>(
127
+ _definition: Self,
128
+ handler: Handler<ROut, Self>,
129
+ ): Handler<ROut, Self> => handler;
package/src/Worker.ts CHANGED
@@ -1,20 +1,13 @@
1
1
  import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
2
- import {
3
- Cause,
4
- ConfigProvider,
5
- Context,
6
- Effect,
7
- Exit,
8
- Layer,
9
- ManagedRuntime,
10
- type Scope,
11
- } from "effect";
2
+ import { Cause, ConfigProvider, Context, Effect, Layer, ManagedRuntime, type Scope } from "effect";
12
3
  import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
13
4
 
14
5
  import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
6
+ import { fromMessage, fromMessageBatch, type QueueHandler } from "./Queue";
15
7
  import type * as RpcDefinition from "./RpcDefinition";
16
8
  import * as WorkerDefinition from "./WorkerDefinition";
17
9
  import * as Entrypoint from "./internal/Entrypoint";
10
+ import { fromExecutionContext, type RunWaitUntilEffect } from "./internal/WorkerContext";
18
11
 
19
12
  export class ExecutionContext extends Context.Service<
20
13
  ExecutionContext,
@@ -22,6 +15,7 @@ export class ExecutionContext extends Context.Service<
22
15
  >()("effect-cf/ExecutionContext") {}
23
16
 
24
17
  export interface WorkerContextWaitUntilOptions<E, R> {
18
+ readonly mode?: "observe" | "propagate";
25
19
  readonly onFailure?: (cause: Cause.Cause<E>) => Effect.Effect<void, never, R>;
26
20
  }
27
21
 
@@ -31,6 +25,10 @@ export interface WorkerContextService {
31
25
  effect: Effect.Effect<A, E, R>,
32
26
  options?: WorkerContextWaitUntilOptions<E, R2>,
33
27
  ): Effect.Effect<void, never, R | R2>;
28
+ waitUntilPropagating<A, E, R, R2 = never>(
29
+ effect: Effect.Effect<A, E, R>,
30
+ options?: Omit<WorkerContextWaitUntilOptions<E, R2>, "mode">,
31
+ ): Effect.Effect<void, never, R | R2>;
34
32
  readonly passThroughOnException: Effect.Effect<void>;
35
33
  }
36
34
 
@@ -38,60 +36,6 @@ export class WorkerContext extends Context.Service<WorkerContext, WorkerContextS
38
36
  "effect-cf/WorkerContext",
39
37
  ) {}
40
38
 
41
- type RunWaitUntilEffect = <A, E>(
42
- effect: Effect.Effect<A, E, never>,
43
- ) => Promise<Exit.Exit<A, unknown>>;
44
-
45
- const fromExecutionContext = (
46
- ctx: globalThis.ExecutionContext,
47
- runPromiseExit: RunWaitUntilEffect,
48
- ): WorkerContextService => ({
49
- raw: ctx,
50
- waitUntil: <A, E, R, R2 = never>(
51
- effect: Effect.Effect<A, E, R>,
52
- options?: WorkerContextWaitUntilOptions<E, R2>,
53
- ) =>
54
- Effect.context<R | R2>().pipe(
55
- Effect.flatMap((context) =>
56
- Effect.sync(() => {
57
- const observed = Effect.exit(effect).pipe(
58
- Effect.flatMap((exit) => {
59
- if (Exit.isSuccess(exit)) {
60
- return Effect.void;
61
- }
62
-
63
- const handleFailure =
64
- options?.onFailure?.(exit.cause) ??
65
- Effect.logError("WorkerContext.waitUntil failed", Cause.pretty(exit.cause));
66
-
67
- return handleFailure.pipe(
68
- Effect.catchCause((handlerCause) =>
69
- Effect.logError(
70
- "WorkerContext.waitUntil failure handler failed",
71
- Cause.pretty(exit.cause),
72
- Cause.pretty(handlerCause),
73
- ),
74
- ),
75
- );
76
- }),
77
- );
78
-
79
- ctx.waitUntil(
80
- runPromiseExit(Effect.scoped(Effect.provideContext(observed, context))).then((exit) => {
81
- if (Exit.isFailure(exit)) {
82
- console.error(
83
- "WorkerContext.waitUntil failure handler failed",
84
- Cause.pretty(exit.cause),
85
- );
86
- }
87
- }),
88
- );
89
- }),
90
- ),
91
- ),
92
- passThroughOnException: Effect.sync(() => ctx.passThroughOnException()),
93
- });
94
-
95
39
  export class NativeRequest extends Context.Service<NativeRequest, Request>()(
96
40
  "effect-cf/NativeRequest",
97
41
  ) {}
@@ -103,6 +47,12 @@ export type ReservedMethodName =
103
47
  | RpcDefinition.ReservedMethodName
104
48
  | "fetch"
105
49
  | "connect"
50
+ | "queue"
51
+ | "scheduled"
52
+ | "tail"
53
+ | "tailStream"
54
+ | "test"
55
+ | "trace"
106
56
  | "alarm"
107
57
  | "webSocketMessage"
108
58
  | "webSocketClose"
@@ -113,6 +63,12 @@ const reservedMethodNames = new Set<string>([
113
63
  "dup",
114
64
  "fetch",
115
65
  "connect",
66
+ "queue",
67
+ "scheduled",
68
+ "tail",
69
+ "tailStream",
70
+ "test",
71
+ "trace",
116
72
  "alarm",
117
73
  "webSocketMessage",
118
74
  "webSocketClose",
@@ -169,6 +125,7 @@ export type RpcHandlers<ROut, Api> = {
169
125
 
170
126
  export interface WorkerOptions<ROut, Rpc extends WorkerRpc<ROut>> {
171
127
  readonly fetch?: Effect.Effect<WorkerFetchSuccess, unknown, WorkerFetchContext<ROut>>;
128
+ readonly queue?: QueueHandler<ROut>;
172
129
  readonly rpc?: Rpc;
173
130
  }
174
131
 
@@ -181,6 +138,7 @@ export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
181
138
  env: WorkerEnv,
182
139
  ) => CloudflareWorkerEntrypoint<WorkerEnv> & {
183
140
  fetch(request: Request): Promise<Response>;
141
+ queue(batch: globalThis.MessageBatch): Promise<void>;
184
142
  } & WorkerRpcShape<Rpc, ROut>;
185
143
 
186
144
  export interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
@@ -211,10 +169,29 @@ const renderFetchSuccess = <E, R>(
211
169
  ),
212
170
  );
213
171
 
214
- export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Record<never, never>>(
172
+ const isWorkerOptions = <ROut, Rpc extends WorkerRpc<ROut>>(
173
+ options: WorkerOptions<ROut, Rpc> | WorkerHandler<ROut>,
174
+ ): options is WorkerOptions<ROut, Rpc> =>
175
+ typeof options === "object" &&
176
+ options !== null &&
177
+ ("fetch" in options || "queue" in options || "rpc" in options);
178
+
179
+ export function make<ROut, LayerError>(
180
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
181
+ fetch: WorkerHandler<ROut>,
182
+ ): WorkerClass<Record<never, never>, ROut>;
183
+ export function make<ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Record<never, never>>(
215
184
  layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
216
185
  options: WorkerOptions<ROut, Rpc>,
217
- ): WorkerClass<Rpc, ROut> => {
186
+ ): WorkerClass<Rpc, ROut>;
187
+ export function make<ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Record<never, never>>(
188
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
189
+ optionsOrFetch: WorkerOptions<ROut, Rpc> | WorkerHandler<ROut>,
190
+ ): WorkerClass<Rpc, ROut> {
191
+ const options = isWorkerOptions(optionsOrFetch)
192
+ ? optionsOrFetch
193
+ : ({ fetch: optionsOrFetch } as WorkerOptions<ROut, Rpc>);
194
+
218
195
  class EffectWorker extends CloudflareWorkerEntrypoint<WorkerEnv> {
219
196
  readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
220
197
 
@@ -222,7 +199,7 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
222
199
  super(ctx, env);
223
200
 
224
201
  let runWaitUntilEffect: RunWaitUntilEffect = () =>
225
- Promise.resolve(Exit.die(new Error("WorkerContext runtime is not initialized")));
202
+ Promise.reject(new Error("WorkerContext runtime is not initialized"));
226
203
 
227
204
  const services = Layer.mergeAll(
228
205
  Layer.succeed(ExecutionContext, ctx),
@@ -269,6 +246,18 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
269
246
  >,
270
247
  );
271
248
  }
249
+
250
+ queue(batch: globalThis.MessageBatch): Promise<void> {
251
+ const queueHandler = options.queue;
252
+
253
+ if (queueHandler === undefined) {
254
+ return Promise.resolve();
255
+ }
256
+
257
+ const messages = batch.messages.map((message) => fromMessage(message, message.body));
258
+
259
+ return this[RunSymbol](queueHandler(fromMessageBatch(batch, messages)));
260
+ }
272
261
  }
273
262
 
274
263
  Entrypoint.defineEntrypointRpcMethods<EffectWorker>(
@@ -280,7 +269,7 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
280
269
  );
281
270
 
282
271
  return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut>>(EffectWorker);
283
- };
272
+ }
284
273
 
285
274
  export const makeFetchHandler = <ROut, LayerError, Env extends WorkerEnv = WorkerEnv>(
286
275
  layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
@@ -28,6 +28,12 @@ const reservedMethodNames = new Set<string>([
28
28
  "dup",
29
29
  "fetch",
30
30
  "connect",
31
+ "queue",
32
+ "scheduled",
33
+ "tail",
34
+ "tailStream",
35
+ "test",
36
+ "trace",
31
37
  "alarm",
32
38
  "webSocketMessage",
33
39
  "webSocketClose",
@@ -0,0 +1,279 @@
1
+ import {
2
+ WorkflowEntrypoint as CloudflareWorkflowEntrypoint,
3
+ type WorkflowEvent as CloudflareWorkflowEvent,
4
+ type WorkflowSleepDuration,
5
+ type WorkflowStep as CloudflareWorkflowStep,
6
+ type WorkflowStepConfig,
7
+ type WorkflowStepContext as CloudflareWorkflowStepContext,
8
+ type WorkflowStepEvent,
9
+ type WorkflowTimeoutDuration,
10
+ } from "cloudflare:workers";
11
+ import { ConfigProvider, Context, Effect, Layer, ManagedRuntime, type Scope } from "effect";
12
+
13
+ import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
14
+ import { ExecutionContext, WorkerContext } from "./Worker";
15
+ import * as WorkflowDefinition from "./WorkflowDefinition";
16
+ import * as Entrypoint from "./internal/Entrypoint";
17
+ import { fromExecutionContext, type RunWaitUntilEffect } from "./internal/WorkerContext";
18
+
19
+ export interface WorkflowEventService<Payload = unknown> {
20
+ readonly raw: CloudflareWorkflowEvent<unknown>;
21
+ readonly payload: Payload;
22
+ readonly timestamp: Date;
23
+ readonly instanceId: string;
24
+ }
25
+
26
+ export class WorkflowEvent extends Context.Service<WorkflowEvent, WorkflowEventService>()(
27
+ "effect-cf/WorkflowEvent",
28
+ ) {}
29
+
30
+ type RunWorkflowStepEffect = <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>;
31
+
32
+ export interface WorkflowStepService {
33
+ readonly raw: CloudflareWorkflowStep;
34
+ do<A, E, R>(
35
+ name: string,
36
+ effect: Effect.Effect<A, E, R>,
37
+ config?: WorkflowStepConfig,
38
+ ): Effect.Effect<A, unknown, Exclude<R, WorkflowStepContext>>;
39
+ readonly sleep: (name: string, duration: WorkflowSleepDuration) => Effect.Effect<void, unknown>;
40
+ readonly sleepUntil: (name: string, timestamp: Date | number) => Effect.Effect<void, unknown>;
41
+ readonly waitForEvent: <Payload>(
42
+ name: string,
43
+ options: {
44
+ readonly type: string;
45
+ readonly timeout?: WorkflowTimeoutDuration | number;
46
+ },
47
+ ) => Effect.Effect<WorkflowStepEvent<Payload>, unknown>;
48
+ }
49
+
50
+ export class WorkflowStep extends Context.Service<WorkflowStep, WorkflowStepService>()(
51
+ "effect-cf/WorkflowStep",
52
+ ) {}
53
+
54
+ export class WorkflowStepContext extends Context.Service<
55
+ WorkflowStepContext,
56
+ CloudflareWorkflowStepContext
57
+ >()("effect-cf/WorkflowStepContext") {}
58
+
59
+ const fromWorkflowEvent = <Payload>(
60
+ event: CloudflareWorkflowEvent<Payload>,
61
+ ): WorkflowEventService<Payload> => ({
62
+ raw: event as CloudflareWorkflowEvent<unknown>,
63
+ payload: event.payload,
64
+ timestamp: event.timestamp,
65
+ instanceId: event.instanceId,
66
+ });
67
+
68
+ const fromWorkflowStep = (
69
+ step: CloudflareWorkflowStep,
70
+ runPromise: RunWorkflowStepEffect,
71
+ ): WorkflowStepService => ({
72
+ raw: step,
73
+ do: <A, E, R>(name: string, effect: Effect.Effect<A, E, R>, config?: WorkflowStepConfig) =>
74
+ Effect.context<Exclude<R, WorkflowStepContext>>().pipe(
75
+ Effect.flatMap((context) =>
76
+ Effect.tryPromise({
77
+ try: () => {
78
+ const run = (stepContext: CloudflareWorkflowStepContext) =>
79
+ runPromise(
80
+ Effect.scoped(
81
+ Effect.provideService(
82
+ Effect.provideContext(
83
+ effect as Effect.Effect<A, E, Exclude<R, WorkflowStepContext>>,
84
+ context,
85
+ ),
86
+ WorkflowStepContext,
87
+ stepContext,
88
+ ),
89
+ ),
90
+ );
91
+ const rawStep = step as {
92
+ do(
93
+ name: string,
94
+ callback: (context: CloudflareWorkflowStepContext) => Promise<A>,
95
+ ): Promise<A>;
96
+ do(
97
+ name: string,
98
+ config: WorkflowStepConfig,
99
+ callback: (context: CloudflareWorkflowStepContext) => Promise<A>,
100
+ ): Promise<A>;
101
+ };
102
+
103
+ return config === undefined ? rawStep.do(name, run) : rawStep.do(name, config, run);
104
+ },
105
+ catch: (cause) => cause,
106
+ }),
107
+ ),
108
+ ) as Effect.Effect<A, unknown, Exclude<R, WorkflowStepContext>>,
109
+ sleep: (name, duration) =>
110
+ Effect.tryPromise({
111
+ try: () => step.sleep(name, duration),
112
+ catch: (cause) => cause,
113
+ }),
114
+ sleepUntil: (name, timestamp) =>
115
+ Effect.tryPromise({
116
+ try: () => step.sleepUntil(name, timestamp),
117
+ catch: (cause) => cause,
118
+ }),
119
+ waitForEvent: <Payload>(
120
+ name: string,
121
+ options: {
122
+ readonly type: string;
123
+ readonly timeout?: WorkflowTimeoutDuration | number;
124
+ },
125
+ ) =>
126
+ Effect.tryPromise({
127
+ try: () => step.waitForEvent(name, options) as Promise<WorkflowStepEvent<Payload>>,
128
+ catch: (cause) => cause,
129
+ }),
130
+ });
131
+
132
+ type RuntimeContext<ROut> = ExecutionContext | WorkerContext | WorkerEnvironment | ROut;
133
+
134
+ export type WorkflowRunContext<ROut> =
135
+ | RuntimeContext<ROut>
136
+ | WorkflowEvent
137
+ | WorkflowStep
138
+ | Scope.Scope;
139
+
140
+ export type WorkflowHandler<ROut, Payload = unknown, Result = unknown> = (
141
+ payload: Payload,
142
+ ) => Effect.Effect<Result, unknown, WorkflowRunContext<ROut>>;
143
+
144
+ export interface WorkflowOptions<ROut, Payload = unknown, Result = unknown> {
145
+ readonly run: WorkflowHandler<ROut, Payload, Result>;
146
+ }
147
+
148
+ export type WorkflowClass<Payload, Result, _ROut> = new (
149
+ ctx: globalThis.ExecutionContext,
150
+ env: WorkerEnv,
151
+ ) => CloudflareWorkflowEntrypoint<WorkerEnv, Payload> & {
152
+ run(
153
+ event: Readonly<CloudflareWorkflowEvent<Payload>>,
154
+ step: CloudflareWorkflowStep,
155
+ ): Promise<Result>;
156
+ };
157
+
158
+ export const make = <ROut, LayerError, Payload = unknown, Result = unknown>(
159
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
160
+ options: WorkflowOptions<ROut, Payload, Result>,
161
+ ): WorkflowClass<Payload, Result, ROut> => {
162
+ class EffectWorkflow extends CloudflareWorkflowEntrypoint<WorkerEnv, Payload> {
163
+ readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
164
+
165
+ constructor(ctx: globalThis.ExecutionContext, env: WorkerEnv) {
166
+ super(ctx, env);
167
+
168
+ let runWaitUntilEffect: RunWaitUntilEffect = () =>
169
+ Promise.reject(new Error("WorkerContext runtime is not initialized"));
170
+
171
+ const services = Layer.mergeAll(
172
+ Layer.succeed(ExecutionContext, ctx),
173
+ ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))),
174
+ Layer.succeed(
175
+ WorkerContext,
176
+ fromExecutionContext(ctx, (effect) => runWaitUntilEffect(effect)),
177
+ ),
178
+ Layer.succeed(WorkerEnvironment, env),
179
+ ) as Layer.Layer<ExecutionContext | WorkerContext | WorkerEnvironment, never, never>;
180
+
181
+ const runtimeLayer = Entrypoint.provideEntrypointServices<
182
+ ROut,
183
+ LayerError,
184
+ ExecutionContext | WorkerContext | WorkerEnvironment
185
+ >(layer, services);
186
+
187
+ this.runtime = ManagedRuntime.make(runtimeLayer);
188
+ runWaitUntilEffect = <A, E>(effect: Effect.Effect<A, E, never>) =>
189
+ this.runtime.runPromiseExit(effect as Effect.Effect<A, E, RuntimeContext<ROut>>);
190
+ }
191
+
192
+ run(
193
+ event: Readonly<CloudflareWorkflowEvent<Payload>>,
194
+ step: CloudflareWorkflowStep,
195
+ ): Promise<Result> {
196
+ const workflowServices = Layer.mergeAll(
197
+ Layer.succeed(WorkflowEvent, fromWorkflowEvent(event)),
198
+ Layer.succeed(
199
+ WorkflowStep,
200
+ fromWorkflowStep(
201
+ step,
202
+ (effect) =>
203
+ this.runtime.runPromise(
204
+ effect as Effect.Effect<unknown, unknown, RuntimeContext<ROut>>,
205
+ ) as never,
206
+ ),
207
+ ),
208
+ );
209
+
210
+ return this.runtime.runPromise(
211
+ Effect.scoped(
212
+ options.run(event.payload).pipe(Effect.provide(workflowServices)),
213
+ ) as Effect.Effect<Result, unknown, RuntimeContext<ROut>>,
214
+ );
215
+ }
216
+ }
217
+
218
+ return Entrypoint.assumeEntrypointClass<WorkflowClass<Payload, Result, ROut>>(EffectWorkflow);
219
+ };
220
+
221
+ export const step = <A, E, R>(
222
+ name: string,
223
+ effect: Effect.Effect<A, E, R>,
224
+ config?: WorkflowStepConfig,
225
+ ): Effect.Effect<A, unknown, WorkflowStep | Exclude<R, WorkflowStepContext>> =>
226
+ Effect.flatMap(WorkflowStep.asEffect(), (workflowStep) =>
227
+ workflowStep.do(name, effect, config),
228
+ ) as Effect.Effect<A, unknown, WorkflowStep | Exclude<R, WorkflowStepContext>>;
229
+
230
+ export const sleep = (
231
+ name: string,
232
+ duration: WorkflowSleepDuration,
233
+ ): Effect.Effect<void, unknown, WorkflowStep> =>
234
+ Effect.flatMap(WorkflowStep.asEffect(), (workflowStep) => workflowStep.sleep(name, duration));
235
+
236
+ export const sleepUntil = (
237
+ name: string,
238
+ timestamp: Date | number,
239
+ ): Effect.Effect<void, unknown, WorkflowStep> =>
240
+ Effect.flatMap(WorkflowStep.asEffect(), (workflowStep) =>
241
+ workflowStep.sleepUntil(name, timestamp),
242
+ );
243
+
244
+ export const waitForEvent = <Payload>(
245
+ name: string,
246
+ options: {
247
+ readonly type: string;
248
+ readonly timeout?: WorkflowTimeoutDuration | number;
249
+ },
250
+ ): Effect.Effect<WorkflowStepEvent<Payload>, unknown, WorkflowStep> =>
251
+ Effect.flatMap(WorkflowStep.asEffect(), (workflowStep) =>
252
+ workflowStep.waitForEvent<Payload>(name, options),
253
+ );
254
+
255
+ export const Tag = WorkflowDefinition.Tag;
256
+
257
+ export const implement = WorkflowDefinition.implement;
258
+
259
+ export type Definition<
260
+ Id extends string = string,
261
+ Payload extends WorkflowDefinition.Definition.Any["payload"] =
262
+ WorkflowDefinition.Definition.Any["payload"],
263
+ Result extends WorkflowDefinition.Definition.Any["result"] =
264
+ WorkflowDefinition.Definition.Any["result"],
265
+ > = WorkflowDefinition.Definition<Id, Payload, Result>;
266
+
267
+ export namespace Definition {
268
+ export type Any = WorkflowDefinition.Definition.Any;
269
+ }
270
+
271
+ export type Handler<
272
+ ROut,
273
+ Self extends WorkflowDefinition.Definition.Any,
274
+ > = WorkflowDefinition.Handler<ROut, Self>;
275
+
276
+ export type Options<
277
+ ROut,
278
+ Self extends WorkflowDefinition.Definition.Any,
279
+ > = WorkflowDefinition.Options<ROut, Self>;