effect-cf 0.20.1 → 0.23.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/CHANGELOG.md +52 -0
- package/README.md +27 -3
- package/dist/{Hyperdrive-RTjgirMV.d.mts → Hyperdrive-COyg7R26.d.mts} +40 -13
- package/dist/HyperdrivePg.d.mts +13 -5
- package/dist/HyperdrivePg.mjs +12 -7
- package/dist/index.d.mts +1113 -922
- package/dist/index.mjs +980 -604
- package/package.json +10 -5
- package/src/AiGateway.ts +82 -47
- package/src/AnalyticsEngine.ts +74 -58
- package/src/Binding.ts +66 -13
- package/src/BrowserRendering.ts +30 -7
- package/src/Cache.ts +32 -8
- package/src/CloudflareOtlp.ts +32 -9
- package/src/ContainerNamespace.ts +251 -80
- package/src/D1.ts +13 -3
- package/src/DurableObject.ts +27 -164
- package/src/DurableObjectAlarm.ts +30 -9
- package/src/DurableObjectDefinition.ts +58 -54
- package/src/DurableObjectNamespace.ts +99 -96
- package/src/DurableObjectRpcWebSocket.ts +14 -6
- package/src/DurableObjectSqlite.ts +6 -3
- package/src/DurableObjectState.ts +63 -39
- package/src/DurableObjectStorage.ts +7 -1
- package/src/DurableObjectWebSocket.ts +51 -41
- package/src/Email.ts +40 -13
- package/src/Environment.ts +26 -6
- package/src/Hyperdrive.ts +2 -2
- package/src/HyperdrivePg.ts +25 -13
- package/src/Images.ts +77 -82
- package/src/Kv.ts +37 -27
- package/src/Queue.ts +6 -1
- package/src/QueueBinding.ts +18 -21
- package/src/QueueDefinition.ts +15 -14
- package/src/R2.ts +107 -29
- package/src/Rpc.ts +3 -1
- package/src/RpcDefinition.ts +177 -133
- package/src/ServiceBinding.ts +112 -108
- package/src/Vectorize.ts +59 -14
- package/src/Worker.ts +172 -192
- package/src/WorkerDefinition.ts +5 -10
- package/src/WorkersAi.ts +27 -17
- package/src/Workflow.ts +54 -49
- package/src/WorkflowBinding.ts +19 -11
- package/src/WorkflowDefinition.ts +24 -27
- package/src/index.ts +4 -3
- package/src/internal/Entrypoint.ts +3 -1
- package/src/internal/ErrorMessage.ts +24 -0
- package/src/internal/RpcInvocation.ts +11 -8
- package/src/internal/Runtime.ts +52 -0
- package/src/internal/WorkerContext.ts +39 -17
- package/src/Entry.ts +0 -259
- package/src/internal/Clock.ts +0 -37
package/src/Workflow.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect wrapper for Cloudflare Workflows entrypoints and bindings; distinct
|
|
3
|
+
* from Effect's runtime-agnostic `effect/unstable/workflow` cluster module.
|
|
4
|
+
*/
|
|
1
5
|
import {
|
|
2
6
|
WorkflowEntrypoint as CloudflareWorkflowEntrypoint,
|
|
3
7
|
type WorkflowEvent as CloudflareWorkflowEvent,
|
|
@@ -8,13 +12,15 @@ import {
|
|
|
8
12
|
type WorkflowStepEvent,
|
|
9
13
|
type WorkflowTimeoutDuration,
|
|
10
14
|
} from "cloudflare:workers";
|
|
11
|
-
import {
|
|
15
|
+
import { Context, Data, Effect, Layer, type ManagedRuntime, type Scope } from "effect";
|
|
12
16
|
|
|
13
|
-
import {
|
|
17
|
+
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
14
18
|
import { ExecutionContext, WorkerContext } from "./Worker";
|
|
15
19
|
import * as WorkflowDefinition from "./WorkflowDefinition";
|
|
16
20
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
17
|
-
import
|
|
21
|
+
import * as ErrorMessage from "./internal/ErrorMessage";
|
|
22
|
+
import * as Runtime from "./internal/Runtime";
|
|
23
|
+
import { fromExecutionContext } from "./internal/WorkerContext";
|
|
18
24
|
|
|
19
25
|
export interface WorkflowEventService<Payload = unknown> {
|
|
20
26
|
readonly raw: CloudflareWorkflowEvent<unknown>;
|
|
@@ -30,22 +36,41 @@ export class WorkflowEvent extends Context.Service<WorkflowEvent, WorkflowEventS
|
|
|
30
36
|
|
|
31
37
|
type RunWorkflowStepEffect = <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>;
|
|
32
38
|
|
|
39
|
+
/** Failure raised when a Cloudflare Workflow step operation rejects. */
|
|
40
|
+
export class WorkflowStepError extends Data.TaggedError("WorkflowStepError")<{
|
|
41
|
+
readonly step?: string;
|
|
42
|
+
readonly operation: string;
|
|
43
|
+
readonly cause: unknown;
|
|
44
|
+
}> {
|
|
45
|
+
override get message(): string {
|
|
46
|
+
const step = this.step === undefined ? "" : ` in step "${this.step}"`;
|
|
47
|
+
|
|
48
|
+
return `Workflow ${this.operation} failed${step}: ${ErrorMessage.causeMessage(this.cause)}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
33
52
|
export interface WorkflowStepService {
|
|
34
53
|
readonly raw: CloudflareWorkflowStep;
|
|
35
54
|
do<A, E, R>(
|
|
36
55
|
name: string,
|
|
37
56
|
effect: Effect.Effect<A, E, R>,
|
|
38
57
|
config?: WorkflowStepConfig,
|
|
39
|
-
): Effect.Effect<A,
|
|
40
|
-
readonly sleep: (
|
|
41
|
-
|
|
58
|
+
): Effect.Effect<A, WorkflowStepError, Exclude<R, WorkflowStepContext>>;
|
|
59
|
+
readonly sleep: (
|
|
60
|
+
name: string,
|
|
61
|
+
duration: WorkflowSleepDuration,
|
|
62
|
+
) => Effect.Effect<void, WorkflowStepError>;
|
|
63
|
+
readonly sleepUntil: (
|
|
64
|
+
name: string,
|
|
65
|
+
timestamp: Date | number,
|
|
66
|
+
) => Effect.Effect<void, WorkflowStepError>;
|
|
42
67
|
readonly waitForEvent: <Payload>(
|
|
43
68
|
name: string,
|
|
44
69
|
options: {
|
|
45
70
|
readonly type: string;
|
|
46
71
|
readonly timeout?: WorkflowTimeoutDuration | number;
|
|
47
72
|
},
|
|
48
|
-
) => Effect.Effect<WorkflowStepEvent<Payload>,
|
|
73
|
+
) => Effect.Effect<WorkflowStepEvent<Payload>, WorkflowStepError>;
|
|
49
74
|
}
|
|
50
75
|
|
|
51
76
|
export class WorkflowStep extends Context.Service<WorkflowStep, WorkflowStepService>()(
|
|
@@ -104,19 +129,19 @@ const fromWorkflowStep = (
|
|
|
104
129
|
|
|
105
130
|
return config === undefined ? rawStep.do(name, run) : rawStep.do(name, config, run);
|
|
106
131
|
},
|
|
107
|
-
catch: (cause) => cause,
|
|
132
|
+
catch: (cause) => new WorkflowStepError({ step: name, operation: "do", cause }),
|
|
108
133
|
}),
|
|
109
134
|
),
|
|
110
|
-
) as Effect.Effect<A,
|
|
135
|
+
) as Effect.Effect<A, WorkflowStepError, Exclude<R, WorkflowStepContext>>,
|
|
111
136
|
sleep: (name, duration) =>
|
|
112
137
|
Effect.tryPromise({
|
|
113
138
|
try: () => step.sleep(name, duration),
|
|
114
|
-
catch: (cause) => cause,
|
|
139
|
+
catch: (cause) => new WorkflowStepError({ step: name, operation: "sleep", cause }),
|
|
115
140
|
}),
|
|
116
141
|
sleepUntil: (name, timestamp) =>
|
|
117
142
|
Effect.tryPromise({
|
|
118
143
|
try: () => step.sleepUntil(name, timestamp),
|
|
119
|
-
catch: (cause) => cause,
|
|
144
|
+
catch: (cause) => new WorkflowStepError({ step: name, operation: "sleepUntil", cause }),
|
|
120
145
|
}),
|
|
121
146
|
waitForEvent: <Payload>(
|
|
122
147
|
name: string,
|
|
@@ -127,7 +152,7 @@ const fromWorkflowStep = (
|
|
|
127
152
|
) =>
|
|
128
153
|
Effect.tryPromise({
|
|
129
154
|
try: () => step.waitForEvent(name, options) as Promise<WorkflowStepEvent<Payload>>,
|
|
130
|
-
catch: (cause) => cause,
|
|
155
|
+
catch: (cause) => new WorkflowStepError({ step: name, operation: "waitForEvent", cause }),
|
|
131
156
|
}),
|
|
132
157
|
});
|
|
133
158
|
|
|
@@ -167,28 +192,18 @@ export const make = <ROut, LayerError, Payload = unknown, Result = unknown>(
|
|
|
167
192
|
constructor(ctx: globalThis.ExecutionContext, env: WorkerEnv) {
|
|
168
193
|
super(ctx, env);
|
|
169
194
|
|
|
170
|
-
|
|
171
|
-
Promise.reject(new Error("WorkerContext runtime is not initialized"));
|
|
172
|
-
|
|
173
|
-
const services = Layer.mergeAll(
|
|
174
|
-
Layer.succeed(ExecutionContext, ctx),
|
|
175
|
-
ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))),
|
|
176
|
-
Layer.succeed(
|
|
177
|
-
WorkerContext,
|
|
178
|
-
fromExecutionContext(ctx, (effect) => runWaitUntilEffect(effect)),
|
|
179
|
-
),
|
|
180
|
-
Layer.succeed(WorkerEnvironment, env),
|
|
181
|
-
) as Layer.Layer<ExecutionContext | WorkerContext | WorkerEnvironment, never, never>;
|
|
182
|
-
|
|
183
|
-
const runtimeLayer = Entrypoint.provideEntrypointServices<
|
|
195
|
+
this.runtime = Runtime.makeEntrypointRuntime<
|
|
184
196
|
ROut,
|
|
185
197
|
LayerError,
|
|
186
|
-
ExecutionContext | WorkerContext
|
|
187
|
-
>(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
ExecutionContext | WorkerContext
|
|
199
|
+
>(
|
|
200
|
+
layer,
|
|
201
|
+
env,
|
|
202
|
+
Layer.mergeAll(
|
|
203
|
+
Layer.succeed(ExecutionContext, ctx),
|
|
204
|
+
Layer.succeed(WorkerContext, fromExecutionContext(ctx)),
|
|
205
|
+
),
|
|
206
|
+
);
|
|
192
207
|
}
|
|
193
208
|
|
|
194
209
|
run(
|
|
@@ -199,21 +214,11 @@ export const make = <ROut, LayerError, Payload = unknown, Result = unknown>(
|
|
|
199
214
|
Layer.succeed(WorkflowEvent, fromWorkflowEvent(event)),
|
|
200
215
|
Layer.succeed(
|
|
201
216
|
WorkflowStep,
|
|
202
|
-
fromWorkflowStep(
|
|
203
|
-
step,
|
|
204
|
-
(effect) =>
|
|
205
|
-
this.runtime.runPromise(
|
|
206
|
-
effect as Effect.Effect<unknown, unknown, RuntimeContext<ROut>>,
|
|
207
|
-
) as never,
|
|
208
|
-
),
|
|
217
|
+
fromWorkflowStep(step, (effect) => this.runtime.runPromise(effect)),
|
|
209
218
|
),
|
|
210
219
|
);
|
|
211
220
|
|
|
212
|
-
return this.runtime.
|
|
213
|
-
Effect.scoped(
|
|
214
|
-
options.run(event.payload).pipe(Effect.provide(workflowServices)),
|
|
215
|
-
) as Effect.Effect<Result, unknown, RuntimeContext<ROut>>,
|
|
216
|
-
);
|
|
221
|
+
return Runtime.runEventPromise(this.runtime, options.run(event.payload), workflowServices);
|
|
217
222
|
}
|
|
218
223
|
}
|
|
219
224
|
|
|
@@ -224,21 +229,21 @@ export const step = <A, E, R>(
|
|
|
224
229
|
name: string,
|
|
225
230
|
effect: Effect.Effect<A, E, R>,
|
|
226
231
|
config?: WorkflowStepConfig,
|
|
227
|
-
): Effect.Effect<A,
|
|
232
|
+
): Effect.Effect<A, WorkflowStepError, WorkflowStep | Exclude<R, WorkflowStepContext>> =>
|
|
228
233
|
Effect.flatMap(WorkflowStep, (workflowStep) =>
|
|
229
234
|
workflowStep.do(name, effect, config),
|
|
230
|
-
) as Effect.Effect<A,
|
|
235
|
+
) as Effect.Effect<A, WorkflowStepError, WorkflowStep | Exclude<R, WorkflowStepContext>>;
|
|
231
236
|
|
|
232
237
|
export const sleep = (
|
|
233
238
|
name: string,
|
|
234
239
|
duration: WorkflowSleepDuration,
|
|
235
|
-
): Effect.Effect<void,
|
|
240
|
+
): Effect.Effect<void, WorkflowStepError, WorkflowStep> =>
|
|
236
241
|
Effect.flatMap(WorkflowStep, (workflowStep) => workflowStep.sleep(name, duration));
|
|
237
242
|
|
|
238
243
|
export const sleepUntil = (
|
|
239
244
|
name: string,
|
|
240
245
|
timestamp: Date | number,
|
|
241
|
-
): Effect.Effect<void,
|
|
246
|
+
): Effect.Effect<void, WorkflowStepError, WorkflowStep> =>
|
|
242
247
|
Effect.flatMap(WorkflowStep, (workflowStep) => workflowStep.sleepUntil(name, timestamp));
|
|
243
248
|
|
|
244
249
|
export const waitForEvent = <Payload>(
|
|
@@ -247,7 +252,7 @@ export const waitForEvent = <Payload>(
|
|
|
247
252
|
readonly type: string;
|
|
248
253
|
readonly timeout?: WorkflowTimeoutDuration | number;
|
|
249
254
|
},
|
|
250
|
-
): Effect.Effect<WorkflowStepEvent<Payload>,
|
|
255
|
+
): Effect.Effect<WorkflowStepEvent<Payload>, WorkflowStepError, WorkflowStep> =>
|
|
251
256
|
Effect.flatMap(WorkflowStep, (workflowStep) => workflowStep.waitForEvent<Payload>(name, options));
|
|
252
257
|
|
|
253
258
|
export type Definition<
|
package/src/WorkflowBinding.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { type Context, Data, Effect, Option, Schema as S } from "effect";
|
|
|
8
8
|
|
|
9
9
|
import * as Binding from "./Binding";
|
|
10
10
|
import type * as RpcDefinition from "./RpcDefinition";
|
|
11
|
+
import * as ErrorMessage from "./internal/ErrorMessage";
|
|
11
12
|
|
|
12
13
|
const expectedWorkflow = "Workflow binding with create(), createBatch(), and get()";
|
|
13
14
|
|
|
@@ -92,20 +93,28 @@ export interface WorkflowBindingClient<
|
|
|
92
93
|
readonly get: (
|
|
93
94
|
instanceId: string,
|
|
94
95
|
) => Effect.Effect<WorkflowInstance<S.Schema.Type<Result>>, WorkflowOperationError>;
|
|
95
|
-
readonly
|
|
96
|
+
readonly rawUnsafe: Effect.Effect<CloudflareWorkflow<S.Codec.Encoded<Payload>>>;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
export class WorkflowOperationError extends Data.TaggedError("WorkflowOperationError")<{
|
|
99
100
|
readonly binding: string;
|
|
100
101
|
readonly operation: string;
|
|
101
102
|
readonly cause: unknown;
|
|
102
|
-
}> {
|
|
103
|
+
}> {
|
|
104
|
+
override get message(): string {
|
|
105
|
+
return `Workflow ${this.operation} failed for binding "${this.binding}": ${ErrorMessage.causeMessage(this.cause)}`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
103
108
|
|
|
104
109
|
export class WorkflowResultDecodeError extends Data.TaggedError("WorkflowResultDecodeError")<{
|
|
105
110
|
readonly binding: string;
|
|
106
111
|
readonly instanceId: string;
|
|
107
112
|
readonly cause: unknown;
|
|
108
|
-
}> {
|
|
113
|
+
}> {
|
|
114
|
+
override get message(): string {
|
|
115
|
+
return `Workflow result decode failed for binding "${this.binding}" instance "${this.instanceId}": ${ErrorMessage.causeMessage(this.cause)}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
109
118
|
|
|
110
119
|
const workflowError = (binding: string, operation: string, cause: unknown) =>
|
|
111
120
|
new WorkflowOperationError({ binding, operation, cause });
|
|
@@ -200,10 +209,9 @@ export const makeClient = <
|
|
|
200
209
|
};
|
|
201
210
|
|
|
202
211
|
return (workflow) => ({
|
|
203
|
-
create: Effect.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
) {
|
|
212
|
+
create: Effect.fn("WorkflowBinding.create", {
|
|
213
|
+
attributes: { binding: definition.binding, operation: "create" },
|
|
214
|
+
})(function* (payload: PayloadValue, options?: WorkflowInstanceCreateOptions<EncodedPayload>) {
|
|
207
215
|
const encoded = yield* encodePayload(payload);
|
|
208
216
|
const raw = yield* tryWorkflowPromise(definition.binding, "create", () =>
|
|
209
217
|
workflow.create({ ...options, params: encoded }),
|
|
@@ -211,9 +219,9 @@ export const makeClient = <
|
|
|
211
219
|
|
|
212
220
|
return wrapInstance(raw);
|
|
213
221
|
}),
|
|
214
|
-
createBatch: Effect.
|
|
215
|
-
|
|
216
|
-
) {
|
|
222
|
+
createBatch: Effect.fn("WorkflowBinding.createBatch", {
|
|
223
|
+
attributes: { binding: definition.binding, operation: "createBatch" },
|
|
224
|
+
})(function* (batch: WorkflowInstanceCreateBatchOptions<PayloadValue, EncodedPayload>) {
|
|
217
225
|
const encodedBatch: Array<CloudflareWorkflowInstanceCreateOptions<EncodedPayload>> = [];
|
|
218
226
|
|
|
219
227
|
for (const item of batch) {
|
|
@@ -235,7 +243,7 @@ export const makeClient = <
|
|
|
235
243
|
tryWorkflowPromise(definition.binding, "get", () => workflow.get(instanceId)).pipe(
|
|
236
244
|
Effect.map(wrapInstance),
|
|
237
245
|
),
|
|
238
|
-
|
|
246
|
+
rawUnsafe: Effect.succeed(workflow),
|
|
239
247
|
});
|
|
240
248
|
};
|
|
241
249
|
|
|
@@ -46,7 +46,11 @@ export interface TagClass<
|
|
|
46
46
|
Id extends string,
|
|
47
47
|
Payload extends RpcDefinition.ServiceFreeSchema,
|
|
48
48
|
Result extends RpcDefinition.ServiceFreeSchema,
|
|
49
|
-
> extends Context.ServiceClass<
|
|
49
|
+
> extends Context.ServiceClass<
|
|
50
|
+
Self,
|
|
51
|
+
`effect-cf/Workflow/${Id}`,
|
|
52
|
+
WorkflowBinding.WorkflowBindingClient<Payload, Result>
|
|
53
|
+
> {
|
|
50
54
|
readonly id: Id;
|
|
51
55
|
readonly payload: Payload;
|
|
52
56
|
readonly result: Result;
|
|
@@ -86,11 +90,7 @@ export interface TagClass<
|
|
|
86
90
|
WorkflowBinding.WorkflowOperationError,
|
|
87
91
|
Self
|
|
88
92
|
>;
|
|
89
|
-
readonly
|
|
90
|
-
globalThis.Workflow<S.Codec.Encoded<Payload>>,
|
|
91
|
-
never,
|
|
92
|
-
Self
|
|
93
|
-
>;
|
|
93
|
+
readonly rawUnsafe: Effect.Effect<globalThis.Workflow<S.Codec.Encoded<Payload>>, never, Self>;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const makeDefinition = <
|
|
@@ -148,7 +148,9 @@ export const Tag =
|
|
|
148
148
|
},
|
|
149
149
|
) => {
|
|
150
150
|
const workflowDefinition = makeDefinition(id, definition);
|
|
151
|
-
const tag = Context.Service<Self, WorkflowBinding.WorkflowBindingClient<Payload, Result>>()(
|
|
151
|
+
const tag = Context.Service<Self, WorkflowBinding.WorkflowBindingClient<Payload, Result>>()(
|
|
152
|
+
`effect-cf/Workflow/${id}` as const,
|
|
153
|
+
);
|
|
152
154
|
|
|
153
155
|
const layer = (binding: LayerOptions) =>
|
|
154
156
|
WorkflowBinding.layer(tag, {
|
|
@@ -183,11 +185,7 @@ export const Tag =
|
|
|
183
185
|
return yield* workflow.get(instanceId);
|
|
184
186
|
});
|
|
185
187
|
|
|
186
|
-
const
|
|
187
|
-
const workflow = yield* tag;
|
|
188
|
-
|
|
189
|
-
return yield* workflow.unsafeRaw;
|
|
190
|
-
});
|
|
188
|
+
const rawUnsafe = Effect.flatMap(tag, (workflow) => workflow.rawUnsafe);
|
|
191
189
|
|
|
192
190
|
return Object.assign(tag, {
|
|
193
191
|
id: workflowDefinition.id,
|
|
@@ -198,7 +196,7 @@ export const Tag =
|
|
|
198
196
|
create,
|
|
199
197
|
createBatch,
|
|
200
198
|
get,
|
|
201
|
-
|
|
199
|
+
rawUnsafe,
|
|
202
200
|
}) as TagClass<Self, Id, Payload, Result>;
|
|
203
201
|
};
|
|
204
202
|
|
|
@@ -215,20 +213,19 @@ const wrapHandler = <ROut, const Self extends Definition.Any>(
|
|
|
215
213
|
const decodePayload = S.decodeUnknownEffect(definition.payload);
|
|
216
214
|
const encodeResult = S.encodeEffect(definition.result);
|
|
217
215
|
|
|
218
|
-
return (payload)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
});
|
|
216
|
+
return Effect.fnUntraced(function* (payload: S.Codec.Encoded<Self["payload"]>) {
|
|
217
|
+
const decodedPayload = yield* decodePayload(payload);
|
|
218
|
+
const event = yield* WorkflowEntrypoint.WorkflowEvent;
|
|
219
|
+
const decodedEvent = {
|
|
220
|
+
...event,
|
|
221
|
+
payload: decodedPayload,
|
|
222
|
+
} as WorkflowEntrypoint.WorkflowEventService<S.Schema.Type<Self["payload"]>>;
|
|
223
|
+
const result = yield* handler(decodedPayload as S.Schema.Type<Self["payload"]>).pipe(
|
|
224
|
+
Effect.provideService(WorkflowEntrypoint.WorkflowEvent, decodedEvent),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
return yield* encodeResult(result as S.Schema.Type<Self["result"]>);
|
|
228
|
+
});
|
|
232
229
|
};
|
|
233
230
|
|
|
234
231
|
export const implement = <ROut, const Self extends Definition.Any>(
|
package/src/index.ts
CHANGED
|
@@ -13,9 +13,10 @@ export * as DurableObjectNamespace from "./DurableObjectNamespace";
|
|
|
13
13
|
export * as DurableObjectRpcWebSocket from "./DurableObjectRpcWebSocket";
|
|
14
14
|
export * as DurableObjectSqlite from "./DurableObjectSqlite";
|
|
15
15
|
export * as DurableObjectState from "./DurableObjectState";
|
|
16
|
-
export * as DurableObjectWebSocket from "./DurableObjectWebSocket";
|
|
17
16
|
export * as DurableObjectStorage from "./DurableObjectStorage";
|
|
17
|
+
export * as DurableObjectWebSocket from "./DurableObjectWebSocket";
|
|
18
18
|
export * as Email from "./Email";
|
|
19
|
+
export * as Environment from "./Environment";
|
|
19
20
|
export * as Hyperdrive from "./Hyperdrive";
|
|
20
21
|
export * as Images from "./Images";
|
|
21
22
|
export * as Kv from "./Kv";
|
|
@@ -26,11 +27,11 @@ export * as R2 from "./R2";
|
|
|
26
27
|
export * as Rpc from "./Rpc";
|
|
27
28
|
export * as RpcDefinition from "./RpcDefinition";
|
|
28
29
|
export * as ServiceBinding from "./ServiceBinding";
|
|
30
|
+
export * as Vectorize from "./Vectorize";
|
|
29
31
|
export * as Worker from "./Worker";
|
|
30
32
|
export * as WorkerDefinition from "./WorkerDefinition";
|
|
33
|
+
export * as WorkersAi from "./WorkersAi";
|
|
31
34
|
export * as Workflow from "./Workflow";
|
|
32
35
|
export * as WorkflowBinding from "./WorkflowBinding";
|
|
33
36
|
export * as WorkflowDefinition from "./WorkflowDefinition";
|
|
34
|
-
export * as Vectorize from "./Vectorize";
|
|
35
|
-
export * as WorkersAi from "./WorkersAi";
|
|
36
37
|
export { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
@@ -30,7 +30,9 @@ export const defineEntrypointRpcMethods = <Self>(
|
|
|
30
30
|
value(this: Self, ...args: AnyArgs) {
|
|
31
31
|
return run(
|
|
32
32
|
this,
|
|
33
|
-
Effect.suspend(() => method(...args))
|
|
33
|
+
Effect.suspend(() => method(...args)).pipe(
|
|
34
|
+
Effect.mapError(RpcDefinition.encodeWireError),
|
|
35
|
+
),
|
|
34
36
|
);
|
|
35
37
|
},
|
|
36
38
|
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders the most useful human-readable description of an unknown `cause`
|
|
3
|
+
* for use in tagged error `message` getters.
|
|
4
|
+
*/
|
|
5
|
+
export const causeMessage = (cause: unknown): string => {
|
|
6
|
+
if (typeof cause === "object" && cause !== null) {
|
|
7
|
+
const message = Reflect.get(cause, "message");
|
|
8
|
+
|
|
9
|
+
if (typeof message === "string" && message.length > 0) {
|
|
10
|
+
return message;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
return String(cause);
|
|
16
|
+
} catch {
|
|
17
|
+
return "unknown cause";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Renders a violation list as a single `path: message; ...` line. */
|
|
22
|
+
export const violationsMessage = (
|
|
23
|
+
violations: ReadonlyArray<{ readonly path: string; readonly message: string }>,
|
|
24
|
+
): string => violations.map((violation) => `${violation.path}: ${violation.message}`).join("; ");
|
|
@@ -58,17 +58,20 @@ export const lookupRpcMethod = <Api, Method extends AsyncMethodKey<Api>, Error>(
|
|
|
58
58
|
catch: makeError,
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
export const invokeRpcMethod =
|
|
61
|
+
export const invokeRpcMethod = Effect.fnUntraced(function* <
|
|
62
|
+
Api,
|
|
63
|
+
Method extends AsyncMethodKey<Api>,
|
|
64
|
+
Error,
|
|
65
|
+
>(
|
|
62
66
|
target: unknown,
|
|
63
67
|
method: Method,
|
|
64
68
|
args: AsyncMethodArgs<Api, Method>,
|
|
65
69
|
makeError: (cause: unknown) => Error,
|
|
66
|
-
): Effect.
|
|
67
|
-
|
|
68
|
-
const fn = yield* lookupRpcMethod<Api, Method, Error>(target, method, makeError);
|
|
70
|
+
): Effect.fn.Return<AsyncMethodCloudflareReturn<Api, Method>, Error> {
|
|
71
|
+
const fn = yield* lookupRpcMethod<Api, Method, Error>(target, method, makeError);
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
73
|
+
return yield* Effect.try({
|
|
74
|
+
try: () => fn(...args),
|
|
75
|
+
catch: makeError,
|
|
74
76
|
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ConfigProvider, Effect, Layer, ManagedRuntime, type Scope } from "effect";
|
|
2
|
+
|
|
3
|
+
import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "../Environment";
|
|
4
|
+
import { provideEntrypointServices } from "./Entrypoint";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Assembles the shared `ManagedRuntime` used by the Cloudflare entrypoint
|
|
8
|
+
* classes (Worker, Durable Object, Workflow).
|
|
9
|
+
*
|
|
10
|
+
* The entrypoint-specific services are merged with the env-backed
|
|
11
|
+
* `ConfigProvider` and `WorkerEnvironment`, then provided to the user-supplied
|
|
12
|
+
* layer so the resulting runtime satisfies both the user services and the
|
|
13
|
+
* platform services.
|
|
14
|
+
*/
|
|
15
|
+
export const makeEntrypointRuntime = <ROut, LayerError, Services>(
|
|
16
|
+
layer: Layer.Layer<ROut, LayerError, Services | WorkerEnvironment>,
|
|
17
|
+
env: WorkerEnv,
|
|
18
|
+
services: Layer.Layer<Services>,
|
|
19
|
+
): ManagedRuntime.ManagedRuntime<ROut | Services | WorkerEnvironment, LayerError> => {
|
|
20
|
+
const entrypointServices = Layer.mergeAll(
|
|
21
|
+
services,
|
|
22
|
+
ConfigProvider.layer(WorkerConfig.providerFromEnv(env)),
|
|
23
|
+
Layer.succeed(WorkerEnvironment, env),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return ManagedRuntime.make(provideEntrypointServices(layer, entrypointServices));
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Runs a Cloudflare event effect on an entrypoint runtime.
|
|
31
|
+
*
|
|
32
|
+
* When an event layer is given it is built inside the event's Effect scope
|
|
33
|
+
* (finalized when the event effect completes). The casts widen away the
|
|
34
|
+
* `REvent` requirement satisfied by the event layer; TypeScript cannot reduce
|
|
35
|
+
* `Exclude<...>` over the generic union.
|
|
36
|
+
*/
|
|
37
|
+
export const runEventPromise = <A, E, R, REvent, EventLayerError, LayerError>(
|
|
38
|
+
runtime: ManagedRuntime.ManagedRuntime<R, LayerError>,
|
|
39
|
+
effect: Effect.Effect<A, E, R | REvent | Scope.Scope>,
|
|
40
|
+
eventLayer: Layer.Layer<REvent, EventLayerError, R> | undefined,
|
|
41
|
+
): Promise<A> => {
|
|
42
|
+
const withEventLayer =
|
|
43
|
+
eventLayer === undefined
|
|
44
|
+
? (effect as Effect.Effect<A, E, R | Scope.Scope>)
|
|
45
|
+
: (effect.pipe(Effect.provide(eventLayer, { local: true })) as Effect.Effect<
|
|
46
|
+
A,
|
|
47
|
+
E | EventLayerError,
|
|
48
|
+
R | Scope.Scope
|
|
49
|
+
>);
|
|
50
|
+
|
|
51
|
+
return runtime.runPromise(Effect.scoped(withEventLayer));
|
|
52
|
+
};
|
|
@@ -6,30 +6,41 @@ export type RunWaitUntilEffect = <A, E>(
|
|
|
6
6
|
effect: Effect.Effect<A, E, never>,
|
|
7
7
|
) => Promise<Exit.Exit<A, unknown>>;
|
|
8
8
|
|
|
9
|
-
const causeError = <E>(cause: Cause.Cause<E>) =>
|
|
9
|
+
const causeError = <E>(cause: Cause.Cause<E>) => {
|
|
10
|
+
const squashed = Cause.squash(cause);
|
|
11
|
+
|
|
12
|
+
return squashed instanceof Error ? squashed : new Error(Cause.pretty(cause), { cause: squashed });
|
|
13
|
+
};
|
|
10
14
|
|
|
11
15
|
const failureHandler = <E, R>(
|
|
16
|
+
label: string,
|
|
12
17
|
cause: Cause.Cause<E>,
|
|
13
18
|
options: WorkerContextWaitUntilOptions<E, R> | undefined,
|
|
14
19
|
) =>
|
|
15
|
-
(
|
|
16
|
-
options?.onFailure?.(cause) ??
|
|
17
|
-
Effect.logError("WorkerContext.waitUntil failed", Cause.pretty(cause))
|
|
18
|
-
).pipe(
|
|
20
|
+
(options?.onFailure?.(cause) ?? Effect.logError(`${label} failed`, Cause.pretty(cause))).pipe(
|
|
19
21
|
Effect.catchCause((handlerCause) =>
|
|
20
22
|
Effect.logError(
|
|
21
|
-
|
|
23
|
+
`${label} failure handler failed`,
|
|
22
24
|
Cause.pretty(cause),
|
|
23
25
|
Cause.pretty(handlerCause),
|
|
24
26
|
),
|
|
25
27
|
),
|
|
26
28
|
);
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Builds a `waitUntil` scheduler for any Cloudflare lifecycle that accepts
|
|
32
|
+
* background promises (`ExecutionContext`, `DurableObjectState`).
|
|
33
|
+
*
|
|
34
|
+
* Background effects capture the calling fiber's context via `Effect.context`
|
|
35
|
+
* before being registered, so the default module-level `Effect.runPromiseExit`
|
|
36
|
+
* runner is sufficient; entrypoints may still pass a runtime-bound runner.
|
|
37
|
+
*/
|
|
38
|
+
export const makeWaitUntilScheduler = (
|
|
39
|
+
label: string,
|
|
40
|
+
register: (promise: Promise<unknown>) => void,
|
|
41
|
+
runPromiseExit: RunWaitUntilEffect = (effect) => Effect.runPromiseExit(effect),
|
|
42
|
+
) => {
|
|
43
|
+
return <A, E, R, R2 = never>(
|
|
33
44
|
effect: Effect.Effect<A, E, R>,
|
|
34
45
|
options: WorkerContextWaitUntilOptions<E, R2> | undefined,
|
|
35
46
|
mode: "observe" | "propagate",
|
|
@@ -39,17 +50,14 @@ export const fromExecutionContext = (
|
|
|
39
50
|
Effect.sync(() => {
|
|
40
51
|
const runHandler = (cause: Cause.Cause<E>) =>
|
|
41
52
|
runPromiseExit(
|
|
42
|
-
Effect.scoped(Effect.provideContext(failureHandler(cause, options), context)),
|
|
53
|
+
Effect.scoped(Effect.provideContext(failureHandler(label, cause, options), context)),
|
|
43
54
|
).then((exit) => {
|
|
44
55
|
if (Exit.isFailure(exit)) {
|
|
45
|
-
console.error(
|
|
46
|
-
"WorkerContext.waitUntil failure handler failed",
|
|
47
|
-
Cause.pretty(exit.cause),
|
|
48
|
-
);
|
|
56
|
+
console.error(`${label} failure handler failed`, Cause.pretty(exit.cause));
|
|
49
57
|
}
|
|
50
58
|
});
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
register(
|
|
53
61
|
runPromiseExit(Effect.scoped(Effect.provideContext(effect, context))).then(
|
|
54
62
|
async (exit) => {
|
|
55
63
|
if (Exit.isSuccess(exit)) {
|
|
@@ -67,6 +75,20 @@ export const fromExecutionContext = (
|
|
|
67
75
|
}),
|
|
68
76
|
),
|
|
69
77
|
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Builds the `WorkerContext` service from a native `ExecutionContext`.
|
|
82
|
+
*/
|
|
83
|
+
export const fromExecutionContext = (
|
|
84
|
+
ctx: globalThis.ExecutionContext,
|
|
85
|
+
runPromiseExit: RunWaitUntilEffect = (effect) => Effect.runPromiseExit(effect),
|
|
86
|
+
): WorkerContextService => {
|
|
87
|
+
const schedule = makeWaitUntilScheduler(
|
|
88
|
+
"WorkerContext.waitUntil",
|
|
89
|
+
(promise) => ctx.waitUntil(promise),
|
|
90
|
+
runPromiseExit,
|
|
91
|
+
);
|
|
70
92
|
|
|
71
93
|
return {
|
|
72
94
|
raw: ctx,
|