effect-cf 0.12.0 → 0.13.1
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 +15 -0
- package/dist/index.d.mts +1085 -1085
- package/dist/index.mjs +1489 -1519
- package/package.json +1 -1
- package/src/CloudflareOtlp.ts +0 -112
- package/src/DurableObject.ts +64 -21
- package/src/DurableObjectDefinition.ts +19 -9
- package/src/Worker.ts +90 -29
- package/src/WorkerDefinition.ts +16 -8
- package/src/internal/Clock.ts +28 -0
package/package.json
CHANGED
package/src/CloudflareOtlp.ts
CHANGED
|
@@ -10,10 +10,8 @@ import {
|
|
|
10
10
|
OtlpTracer,
|
|
11
11
|
} from "effect/unstable/observability";
|
|
12
12
|
|
|
13
|
-
import * as DurableObject from "./DurableObject";
|
|
14
13
|
import { DurableObjectState } from "./DurableObjectState";
|
|
15
14
|
import { WorkerConfig, WorkerEnvironment } from "./Environment";
|
|
16
|
-
import * as Worker from "./Worker";
|
|
17
15
|
|
|
18
16
|
/** Telemetry signal groups supported by the Cloudflare OTLP layers. */
|
|
19
17
|
export type Signal = "logs" | "traces" | "metrics";
|
|
@@ -516,113 +514,3 @@ export const durableObjectLayer = (options: LayerOptions & DurableObjectResource
|
|
|
516
514
|
}),
|
|
517
515
|
),
|
|
518
516
|
);
|
|
519
|
-
|
|
520
|
-
const provideRpcLayer = <
|
|
521
|
-
Rpc extends Record<string, (...args: Array<any>) => Effect.Effect<any, any, any>>,
|
|
522
|
-
RLayer,
|
|
523
|
-
>(
|
|
524
|
-
rpc: Rpc | undefined,
|
|
525
|
-
layer: Layer.Layer<never, never, RLayer>,
|
|
526
|
-
): Rpc | undefined => {
|
|
527
|
-
if (rpc === undefined) {
|
|
528
|
-
return undefined;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
return new Proxy(rpc, {
|
|
532
|
-
get(target, property, receiver) {
|
|
533
|
-
const handler = Reflect.get(target, property, receiver);
|
|
534
|
-
if (typeof handler !== "function") {
|
|
535
|
-
return handler;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
return (...args: Array<any>) =>
|
|
539
|
-
Effect.suspend(() => Reflect.apply(handler, receiver, args)).pipe(Effect.provide(layer));
|
|
540
|
-
},
|
|
541
|
-
});
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Instrument Worker lifecycle handlers with event-scoped OTLP telemetry.
|
|
546
|
-
*
|
|
547
|
-
* Each defined handler is provided with {@link workerLayer}. Because Worker
|
|
548
|
-
* handlers run inside `Effect.scoped`, the exporter scope closes and flushes at
|
|
549
|
-
* the end of each Cloudflare event.
|
|
550
|
-
*/
|
|
551
|
-
export const instrumentWorkerOptions =
|
|
552
|
-
(options?: LayerOptions & WorkerResourceOptions) =>
|
|
553
|
-
<ROut, Rpc extends Worker.WorkerRpc<ROut>>(
|
|
554
|
-
handlers: Worker.WorkerOptions<ROut, Rpc>,
|
|
555
|
-
): Worker.WorkerOptions<ROut | WorkerEnvironment | CloudflareOtlpSettings, Rpc> => {
|
|
556
|
-
const telemetryLayer = workerLayer(options);
|
|
557
|
-
const queue = handlers.queue;
|
|
558
|
-
|
|
559
|
-
return {
|
|
560
|
-
...handlers,
|
|
561
|
-
fetch:
|
|
562
|
-
handlers.fetch === undefined
|
|
563
|
-
? undefined
|
|
564
|
-
: handlers.fetch.pipe(Effect.provide(telemetryLayer)),
|
|
565
|
-
queue:
|
|
566
|
-
queue === undefined
|
|
567
|
-
? undefined
|
|
568
|
-
: (batch) => queue(batch).pipe(Effect.provide(telemetryLayer)),
|
|
569
|
-
rpc: provideRpcLayer(handlers.rpc, telemetryLayer),
|
|
570
|
-
};
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Instrument Durable Object lifecycle handlers with event-scoped OTLP telemetry.
|
|
575
|
-
*
|
|
576
|
-
* Each defined handler is provided with {@link durableObjectLayer}. Because
|
|
577
|
-
* Durable Object handlers run inside `Effect.scoped`, the exporter scope closes
|
|
578
|
-
* and flushes at the end of each Cloudflare event.
|
|
579
|
-
*/
|
|
580
|
-
export const instrumentDurableObjectOptions =
|
|
581
|
-
(options?: LayerOptions & DurableObjectResourceOptions) =>
|
|
582
|
-
<ROut, Rpc extends DurableObject.DurableObjectRpc<ROut>>(
|
|
583
|
-
handlers: DurableObject.DurableObjectOptions<ROut, Rpc>,
|
|
584
|
-
): DurableObject.DurableObjectOptions<
|
|
585
|
-
ROut | WorkerEnvironment | DurableObjectState | CloudflareOtlpSettings,
|
|
586
|
-
Rpc
|
|
587
|
-
> => {
|
|
588
|
-
const telemetryLayer = durableObjectLayer(options);
|
|
589
|
-
const alarm = handlers.alarm;
|
|
590
|
-
const webSocketMessage = handlers.webSocketMessage;
|
|
591
|
-
const webSocketClose = handlers.webSocketClose;
|
|
592
|
-
const webSocketError = handlers.webSocketError;
|
|
593
|
-
|
|
594
|
-
return {
|
|
595
|
-
...handlers,
|
|
596
|
-
initialize:
|
|
597
|
-
handlers.initialize === undefined
|
|
598
|
-
? undefined
|
|
599
|
-
: handlers.initialize.pipe(Effect.provide(telemetryLayer)),
|
|
600
|
-
fetch:
|
|
601
|
-
handlers.fetch === undefined
|
|
602
|
-
? undefined
|
|
603
|
-
: handlers.fetch.pipe(Effect.provide(telemetryLayer)),
|
|
604
|
-
alarms:
|
|
605
|
-
handlers.alarms === undefined
|
|
606
|
-
? undefined
|
|
607
|
-
: handlers.alarms.pipe(Effect.provide(telemetryLayer)),
|
|
608
|
-
alarm:
|
|
609
|
-
alarm === undefined
|
|
610
|
-
? undefined
|
|
611
|
-
: (alarmInfo) => alarm(alarmInfo).pipe(Effect.provide(telemetryLayer)),
|
|
612
|
-
webSocketMessage:
|
|
613
|
-
webSocketMessage === undefined
|
|
614
|
-
? undefined
|
|
615
|
-
: (socket, message) =>
|
|
616
|
-
webSocketMessage(socket, message).pipe(Effect.provide(telemetryLayer)),
|
|
617
|
-
webSocketClose:
|
|
618
|
-
webSocketClose === undefined
|
|
619
|
-
? undefined
|
|
620
|
-
: (socket, code, reason, wasClean) =>
|
|
621
|
-
webSocketClose(socket, code, reason, wasClean).pipe(Effect.provide(telemetryLayer)),
|
|
622
|
-
webSocketError:
|
|
623
|
-
webSocketError === undefined
|
|
624
|
-
? undefined
|
|
625
|
-
: (socket, error) => webSocketError(socket, error).pipe(Effect.provide(telemetryLayer)),
|
|
626
|
-
rpc: provideRpcLayer(handlers.rpc, telemetryLayer),
|
|
627
|
-
};
|
|
628
|
-
};
|
package/src/DurableObject.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type * as Binding from "./Binding";
|
|
|
10
10
|
import * as DurableObjectDefinition from "./DurableObjectDefinition";
|
|
11
11
|
import type * as DurableObjectNamespace from "./DurableObjectNamespace";
|
|
12
12
|
import type * as Rpc from "./Rpc";
|
|
13
|
+
import * as CloudflareClock from "./internal/Clock";
|
|
13
14
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
14
15
|
|
|
15
16
|
const reservedMethodNames = new Set<string>([
|
|
@@ -27,6 +28,9 @@ type RuntimeContext<ROut> = DurableObjectState | WorkerEnvironment | ROut;
|
|
|
27
28
|
type HandlerContext<ROut> = RuntimeContext<ROut> | Scope.Scope;
|
|
28
29
|
|
|
29
30
|
type FetchContext<ROut> = HandlerContext<ROut> | NativeRequest;
|
|
31
|
+
type RunOptions = {
|
|
32
|
+
readonly eventLayer?: boolean;
|
|
33
|
+
};
|
|
30
34
|
|
|
31
35
|
const RunSymbol = Symbol.for("effect-cf/DurableObject/run");
|
|
32
36
|
|
|
@@ -72,7 +76,24 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
72
76
|
/**
|
|
73
77
|
* Options for creating a Durable Object class backed by Effect handlers.
|
|
74
78
|
*/
|
|
75
|
-
export interface DurableObjectOptions<
|
|
79
|
+
export interface DurableObjectOptions<
|
|
80
|
+
RRuntime,
|
|
81
|
+
REvent = never,
|
|
82
|
+
EventLayerError = never,
|
|
83
|
+
Rpc extends DurableObjectRpc<RRuntime | REvent> = Record<never, never>,
|
|
84
|
+
> {
|
|
85
|
+
/**
|
|
86
|
+
* Layer provided around each Cloudflare event handled by this Durable Object.
|
|
87
|
+
*
|
|
88
|
+
* The layer is built inside the event's Effect scope and finalized when the
|
|
89
|
+
* event effect completes. It is not applied to `initialize`, which is an
|
|
90
|
+
* instance-load lifecycle hook rather than a platform event.
|
|
91
|
+
*/
|
|
92
|
+
readonly eventLayer?: Layer.Layer<
|
|
93
|
+
REvent,
|
|
94
|
+
EventLayerError,
|
|
95
|
+
DurableObjectState | WorkerEnvironment | RRuntime
|
|
96
|
+
>;
|
|
76
97
|
/**
|
|
77
98
|
* Effect run when Cloudflare loads this Durable Object instance into memory.
|
|
78
99
|
*
|
|
@@ -81,11 +102,11 @@ export interface DurableObjectOptions<ROut, Rpc extends DurableObjectRpc<ROut>>
|
|
|
81
102
|
* the same Durable Object id again after eviction or restart; use Durable
|
|
82
103
|
* Object storage if work must happen only once per id.
|
|
83
104
|
*/
|
|
84
|
-
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<
|
|
105
|
+
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<RRuntime>>;
|
|
85
106
|
/** Optional RPC methods exposed as Durable Object instance methods. */
|
|
86
107
|
readonly rpc?: Rpc;
|
|
87
108
|
/** Optional fetch handler for HTTP/WebSocket requests. */
|
|
88
|
-
readonly fetch?: Effect.Effect<Response, unknown, FetchContext<
|
|
109
|
+
readonly fetch?: Effect.Effect<Response, unknown, FetchContext<RRuntime | REvent>>;
|
|
89
110
|
/**
|
|
90
111
|
* Optional logical alarm processing effect.
|
|
91
112
|
*
|
|
@@ -93,24 +114,24 @@ export interface DurableObjectOptions<ROut, Rpc extends DurableObjectRpc<ROut>>
|
|
|
93
114
|
* `DurableObjectAlarm.processDue(...)` so the reusable scheduler stays inside
|
|
94
115
|
* the Durable Object's single managed runtime boundary.
|
|
95
116
|
*/
|
|
96
|
-
readonly alarms?: Effect.Effect<unknown, unknown, HandlerContext<
|
|
117
|
+
readonly alarms?: Effect.Effect<unknown, unknown, HandlerContext<RRuntime | REvent>>;
|
|
97
118
|
readonly alarm?: (
|
|
98
119
|
alarmInfo?: globalThis.AlarmInvocationInfo,
|
|
99
|
-
) => Effect.Effect<void, unknown, HandlerContext<
|
|
120
|
+
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
|
|
100
121
|
readonly webSocketMessage?: (
|
|
101
122
|
socket: DurableWebSocket,
|
|
102
123
|
message: string | ArrayBuffer,
|
|
103
|
-
) => Effect.Effect<void, unknown, HandlerContext<
|
|
124
|
+
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
|
|
104
125
|
readonly webSocketClose?: (
|
|
105
126
|
socket: DurableWebSocket,
|
|
106
127
|
code: number,
|
|
107
128
|
reason: string,
|
|
108
129
|
wasClean: boolean,
|
|
109
|
-
) => Effect.Effect<void, unknown, HandlerContext<
|
|
130
|
+
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
|
|
110
131
|
readonly webSocketError?: (
|
|
111
132
|
socket: DurableWebSocket,
|
|
112
133
|
error: unknown,
|
|
113
|
-
) => Effect.Effect<void, unknown, HandlerContext<
|
|
134
|
+
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
|
|
114
135
|
}
|
|
115
136
|
|
|
116
137
|
/**
|
|
@@ -127,11 +148,13 @@ export type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (
|
|
|
127
148
|
export const make = <
|
|
128
149
|
ROut,
|
|
129
150
|
LayerError,
|
|
130
|
-
|
|
151
|
+
REvent = never,
|
|
152
|
+
EventLayerError = never,
|
|
153
|
+
const Rpc extends DurableObjectRpc<ROut | REvent> = Record<never, never>,
|
|
131
154
|
>(
|
|
132
155
|
layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
|
|
133
|
-
options: DurableObjectOptions<ROut, Rpc> = {},
|
|
134
|
-
): DurableObjectClass<Rpc, ROut> => {
|
|
156
|
+
options: DurableObjectOptions<ROut, REvent, EventLayerError, Rpc> = {},
|
|
157
|
+
): DurableObjectClass<Rpc, ROut | REvent> => {
|
|
135
158
|
class EffectDurableObject extends CloudflareDurableObject<WorkerEnv> {
|
|
136
159
|
readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
|
|
137
160
|
|
|
@@ -139,6 +162,7 @@ export const make = <
|
|
|
139
162
|
super(state, env);
|
|
140
163
|
|
|
141
164
|
const services = Layer.mergeAll(
|
|
165
|
+
CloudflareClock.layer,
|
|
142
166
|
Layer.succeed(DurableObjectState, fromDurableObjectState(state)),
|
|
143
167
|
Layer.succeed(WorkerEnvironment, env),
|
|
144
168
|
);
|
|
@@ -149,12 +173,24 @@ export const make = <
|
|
|
149
173
|
|
|
150
174
|
const initialize = options.initialize;
|
|
151
175
|
if (initialize !== undefined) {
|
|
152
|
-
state.waitUntil(this[RunSymbol](initialize));
|
|
176
|
+
state.waitUntil(this[RunSymbol](initialize, { eventLayer: false }));
|
|
153
177
|
}
|
|
154
178
|
}
|
|
155
179
|
|
|
156
|
-
[RunSymbol]<A, E>(
|
|
157
|
-
|
|
180
|
+
[RunSymbol]<A, E>(
|
|
181
|
+
effect: Effect.Effect<A, E, HandlerContext<ROut | REvent>>,
|
|
182
|
+
runOptions: RunOptions = {},
|
|
183
|
+
): Promise<A> {
|
|
184
|
+
const effectWithEventLayer =
|
|
185
|
+
runOptions.eventLayer === false || options.eventLayer === undefined
|
|
186
|
+
? effect
|
|
187
|
+
: effect.pipe(Effect.provide(options.eventLayer, { local: true }));
|
|
188
|
+
|
|
189
|
+
return this.runtime.runPromise(
|
|
190
|
+
Effect.scoped(
|
|
191
|
+
effectWithEventLayer as Effect.Effect<A, E | EventLayerError, HandlerContext<ROut>>,
|
|
192
|
+
),
|
|
193
|
+
);
|
|
158
194
|
}
|
|
159
195
|
|
|
160
196
|
fetch(request: Request): Promise<Response> {
|
|
@@ -223,7 +259,9 @@ export const make = <
|
|
|
223
259
|
(self, effect) => self[RunSymbol](effect),
|
|
224
260
|
);
|
|
225
261
|
|
|
226
|
-
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut>>(
|
|
262
|
+
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent>>(
|
|
263
|
+
EffectDurableObject,
|
|
264
|
+
);
|
|
227
265
|
};
|
|
228
266
|
|
|
229
267
|
export type ServiceFreeSchema = S.Codec<any, any, never, never>;
|
|
@@ -285,11 +323,16 @@ export type Handlers<ROut, Self extends Definition.Any> = {
|
|
|
285
323
|
) => DurableObjectHandler<ROut, Method.Success<Self["methods"][Key]>>;
|
|
286
324
|
};
|
|
287
325
|
|
|
288
|
-
export interface Options<
|
|
289
|
-
|
|
326
|
+
export interface Options<
|
|
327
|
+
ROut,
|
|
328
|
+
Self extends Definition.Any,
|
|
329
|
+
REvent = never,
|
|
330
|
+
EventLayerError = never,
|
|
331
|
+
> extends Omit<
|
|
332
|
+
DurableObjectOptions<ROut, REvent, EventLayerError, Handlers<ROut | REvent, Self>>,
|
|
290
333
|
"rpc"
|
|
291
334
|
> {
|
|
292
|
-
readonly rpc: Handlers<ROut, Self>;
|
|
335
|
+
readonly rpc: Handlers<ROut | REvent, Self>;
|
|
293
336
|
}
|
|
294
337
|
|
|
295
338
|
export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Context.ServiceClass<
|
|
@@ -307,10 +350,10 @@ export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Co
|
|
|
307
350
|
> & {
|
|
308
351
|
readonly id: Id;
|
|
309
352
|
readonly methods: MethodsShape;
|
|
310
|
-
readonly make: <ROut, LayerError>(
|
|
353
|
+
readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
311
354
|
layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
|
|
312
|
-
options: Options<ROut, Definition<Id, MethodsShape
|
|
313
|
-
) => DurableObjectClass<Handlers<ROut, Definition<Id, MethodsShape>>, ROut>;
|
|
355
|
+
options: Options<ROut, Definition<Id, MethodsShape>, REvent, EventLayerError>,
|
|
356
|
+
) => DurableObjectClass<Handlers<ROut | REvent, Definition<Id, MethodsShape>>, ROut | REvent>;
|
|
314
357
|
readonly layer: (
|
|
315
358
|
options: LayerOptions,
|
|
316
359
|
) => Layer.Layer<
|
|
@@ -115,11 +115,21 @@ type BoundaryHandlers<ROut, Self extends Definition.Any> = {
|
|
|
115
115
|
/**
|
|
116
116
|
* Durable Object constructor options for a specific RPC definition.
|
|
117
117
|
*/
|
|
118
|
-
export interface Options<
|
|
119
|
-
|
|
118
|
+
export interface Options<
|
|
119
|
+
ROut,
|
|
120
|
+
Self extends Definition.Any,
|
|
121
|
+
REvent = never,
|
|
122
|
+
EventLayerError = never,
|
|
123
|
+
> extends Omit<
|
|
124
|
+
DurableObjectEntrypoint.DurableObjectOptions<
|
|
125
|
+
ROut,
|
|
126
|
+
REvent,
|
|
127
|
+
EventLayerError,
|
|
128
|
+
Handlers<ROut | REvent, Self>
|
|
129
|
+
>,
|
|
120
130
|
"rpc"
|
|
121
131
|
> {
|
|
122
|
-
readonly rpc: Handlers<ROut, Self>;
|
|
132
|
+
readonly rpc: Handlers<ROut | REvent, Self>;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
export type LayerOptions = {
|
|
@@ -141,12 +151,12 @@ export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Co
|
|
|
141
151
|
> & {
|
|
142
152
|
readonly id: Id;
|
|
143
153
|
readonly methods: MethodsShape;
|
|
144
|
-
readonly make: <ROut, LayerError>(
|
|
154
|
+
readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
145
155
|
layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
|
|
146
|
-
options: Options<ROut, Definition<Id, MethodsShape
|
|
156
|
+
options: Options<ROut, Definition<Id, MethodsShape>, REvent, EventLayerError>,
|
|
147
157
|
) => DurableObjectEntrypoint.DurableObjectClass<
|
|
148
|
-
Handlers<ROut, Definition<Id, MethodsShape>>,
|
|
149
|
-
ROut
|
|
158
|
+
Handlers<ROut | REvent, Definition<Id, MethodsShape>>,
|
|
159
|
+
ROut | REvent
|
|
150
160
|
>;
|
|
151
161
|
readonly layer: (
|
|
152
162
|
options: LayerOptions,
|
|
@@ -195,9 +205,9 @@ const makeDefinition = <Id extends string, const MethodsShape extends Methods>(
|
|
|
195
205
|
const definition: SelfDefinition = RpcDefinition.make(id, methods);
|
|
196
206
|
|
|
197
207
|
return Object.assign(definition, {
|
|
198
|
-
make: <ROut, LayerError>(
|
|
208
|
+
make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
199
209
|
layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
|
|
200
|
-
options: Options<ROut, SelfDefinition>,
|
|
210
|
+
options: Options<ROut, SelfDefinition, REvent, EventLayerError>,
|
|
201
211
|
) =>
|
|
202
212
|
DurableObjectEntrypoint.make(layer, {
|
|
203
213
|
...options,
|
package/src/Worker.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type * as Rpc from "./Rpc";
|
|
|
10
10
|
import type * as RpcDefinition from "./RpcDefinition";
|
|
11
11
|
import type * as ServiceBinding from "./ServiceBinding";
|
|
12
12
|
import * as WorkerDefinition from "./WorkerDefinition";
|
|
13
|
+
import * as CloudflareClock from "./internal/Clock";
|
|
13
14
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
14
15
|
import { fromExecutionContext, type RunWaitUntilEffect } from "./internal/WorkerContext";
|
|
15
16
|
|
|
@@ -88,6 +89,9 @@ type WorkerFetchContext<ROut> =
|
|
|
88
89
|
type WorkerRpcContext<ROut> = WorkerBaseContext<ROut> | Scope.Scope;
|
|
89
90
|
|
|
90
91
|
type RuntimeContext<ROut> = WorkerBaseContext<ROut>;
|
|
92
|
+
type RunOptions = {
|
|
93
|
+
readonly eventLayer?: boolean;
|
|
94
|
+
};
|
|
91
95
|
|
|
92
96
|
const RunSymbol = Symbol.for("effect-cf/Worker/run");
|
|
93
97
|
|
|
@@ -127,13 +131,33 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
127
131
|
: never;
|
|
128
132
|
};
|
|
129
133
|
|
|
130
|
-
export interface WorkerOptions<
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
export interface WorkerOptions<
|
|
135
|
+
RRuntime,
|
|
136
|
+
REvent = never,
|
|
137
|
+
EventLayerError = never,
|
|
138
|
+
Rpc extends WorkerRpc<RRuntime | REvent> = Record<never, never>,
|
|
139
|
+
> {
|
|
140
|
+
/**
|
|
141
|
+
* Layer provided around each Cloudflare event handled by this Worker.
|
|
142
|
+
*
|
|
143
|
+
* The layer is built inside the event's Effect scope and finalized when the
|
|
144
|
+
* event effect completes. Use this for event-scoped resources such as OTLP
|
|
145
|
+
* trace/log exporters that should flush at event completion.
|
|
146
|
+
*/
|
|
147
|
+
readonly eventLayer?: Layer.Layer<REvent, EventLayerError, WorkerBaseContext<RRuntime>>;
|
|
148
|
+
readonly fetch?: Effect.Effect<
|
|
149
|
+
WorkerFetchSuccess,
|
|
150
|
+
unknown,
|
|
151
|
+
WorkerFetchContext<RRuntime | REvent>
|
|
152
|
+
>;
|
|
153
|
+
readonly queue?: QueueHandler<RRuntime | REvent>;
|
|
133
154
|
readonly rpc?: Rpc;
|
|
134
155
|
}
|
|
135
156
|
|
|
136
|
-
export type FetchWorkerOptions<
|
|
157
|
+
export type FetchWorkerOptions<RRuntime, REvent = never, EventLayerError = never> = Omit<
|
|
158
|
+
WorkerOptions<RRuntime, REvent, EventLayerError, Record<never, never>>,
|
|
159
|
+
"rpc"
|
|
160
|
+
> & {
|
|
137
161
|
readonly rpc?: never;
|
|
138
162
|
};
|
|
139
163
|
|
|
@@ -173,28 +197,40 @@ const renderFetchSuccess = <E, R>(
|
|
|
173
197
|
),
|
|
174
198
|
);
|
|
175
199
|
|
|
176
|
-
const isWorkerOptions = <ROut, Rpc extends WorkerRpc<ROut>>(
|
|
177
|
-
options: WorkerOptions<ROut, Rpc> | WorkerHandler<ROut>,
|
|
178
|
-
): options is WorkerOptions<ROut, Rpc> =>
|
|
200
|
+
const isWorkerOptions = <ROut, REvent, EventLayerError, Rpc extends WorkerRpc<ROut | REvent>>(
|
|
201
|
+
options: WorkerOptions<ROut, REvent, EventLayerError, Rpc> | WorkerHandler<ROut>,
|
|
202
|
+
): options is WorkerOptions<ROut, REvent, EventLayerError, Rpc> =>
|
|
179
203
|
typeof options === "object" &&
|
|
180
204
|
options !== null &&
|
|
181
|
-
("fetch" in options || "queue" in options || "rpc" in options);
|
|
205
|
+
("eventLayer" in options || "fetch" in options || "queue" in options || "rpc" in options);
|
|
182
206
|
|
|
183
207
|
export function make<ROut, LayerError>(
|
|
184
208
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
185
209
|
fetch: WorkerHandler<ROut>,
|
|
186
210
|
): WorkerClass<Record<never, never>, ROut>;
|
|
187
|
-
export function make<
|
|
211
|
+
export function make<
|
|
212
|
+
ROut,
|
|
213
|
+
LayerError,
|
|
214
|
+
REvent = never,
|
|
215
|
+
EventLayerError = never,
|
|
216
|
+
const Rpc extends WorkerRpc<ROut | REvent> = Record<never, never>,
|
|
217
|
+
>(
|
|
188
218
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
189
|
-
options: WorkerOptions<ROut, Rpc>,
|
|
190
|
-
): WorkerClass<Rpc, ROut>;
|
|
191
|
-
export function make<
|
|
219
|
+
options: WorkerOptions<ROut, REvent, EventLayerError, Rpc>,
|
|
220
|
+
): WorkerClass<Rpc, ROut | REvent>;
|
|
221
|
+
export function make<
|
|
222
|
+
ROut,
|
|
223
|
+
LayerError,
|
|
224
|
+
REvent = never,
|
|
225
|
+
EventLayerError = never,
|
|
226
|
+
const Rpc extends WorkerRpc<ROut | REvent> = Record<never, never>,
|
|
227
|
+
>(
|
|
192
228
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
193
|
-
optionsOrFetch: WorkerOptions<ROut, Rpc> | WorkerHandler<ROut>,
|
|
194
|
-
): WorkerClass<Rpc, ROut> {
|
|
229
|
+
optionsOrFetch: WorkerOptions<ROut, REvent, EventLayerError, Rpc> | WorkerHandler<ROut>,
|
|
230
|
+
): WorkerClass<Rpc, ROut | REvent> {
|
|
195
231
|
const options = isWorkerOptions(optionsOrFetch)
|
|
196
232
|
? optionsOrFetch
|
|
197
|
-
: ({ fetch: optionsOrFetch } as WorkerOptions<ROut, Rpc>);
|
|
233
|
+
: ({ fetch: optionsOrFetch } as WorkerOptions<ROut, REvent, EventLayerError, Rpc>);
|
|
198
234
|
|
|
199
235
|
class EffectWorker extends CloudflareWorkerEntrypoint<WorkerEnv> {
|
|
200
236
|
readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
|
|
@@ -206,6 +242,7 @@ export function make<ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
|
|
|
206
242
|
Promise.reject(new Error("WorkerContext runtime is not initialized"));
|
|
207
243
|
|
|
208
244
|
const services = Layer.mergeAll(
|
|
245
|
+
CloudflareClock.layer,
|
|
209
246
|
Layer.succeed(ExecutionContext, ctx),
|
|
210
247
|
ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))),
|
|
211
248
|
Layer.succeed(
|
|
@@ -226,8 +263,24 @@ export function make<ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
|
|
|
226
263
|
this.runtime.runPromiseExit(effect as Effect.Effect<A, E, RuntimeContext<ROut>>);
|
|
227
264
|
}
|
|
228
265
|
|
|
229
|
-
[RunSymbol]<A, E>(
|
|
230
|
-
|
|
266
|
+
[RunSymbol]<A, E>(
|
|
267
|
+
effect: Effect.Effect<A, E, RuntimeContext<ROut> | REvent | Scope.Scope>,
|
|
268
|
+
runOptions: RunOptions = {},
|
|
269
|
+
): Promise<A> {
|
|
270
|
+
const effectWithEventLayer =
|
|
271
|
+
runOptions.eventLayer === false || options.eventLayer === undefined
|
|
272
|
+
? effect
|
|
273
|
+
: effect.pipe(Effect.provide(options.eventLayer, { local: true }));
|
|
274
|
+
|
|
275
|
+
return this.runtime.runPromise(
|
|
276
|
+
Effect.scoped(
|
|
277
|
+
effectWithEventLayer as Effect.Effect<
|
|
278
|
+
A,
|
|
279
|
+
E | EventLayerError,
|
|
280
|
+
RuntimeContext<ROut> | Scope.Scope
|
|
281
|
+
>,
|
|
282
|
+
),
|
|
283
|
+
);
|
|
231
284
|
}
|
|
232
285
|
|
|
233
286
|
fetch(request: Request): Promise<Response> {
|
|
@@ -246,7 +299,7 @@ export function make<ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
|
|
|
246
299
|
renderFetchSuccess(fetchHandler).pipe(Effect.provide(requestServices)) as Effect.Effect<
|
|
247
300
|
Response,
|
|
248
301
|
unknown,
|
|
249
|
-
RuntimeContext<ROut> | Scope.Scope
|
|
302
|
+
RuntimeContext<ROut> | REvent | Scope.Scope
|
|
250
303
|
>,
|
|
251
304
|
);
|
|
252
305
|
}
|
|
@@ -272,12 +325,18 @@ export function make<ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
|
|
|
272
325
|
(self, effect) => self[RunSymbol](effect),
|
|
273
326
|
);
|
|
274
327
|
|
|
275
|
-
return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut>>(EffectWorker);
|
|
328
|
+
return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut | REvent>>(EffectWorker);
|
|
276
329
|
}
|
|
277
330
|
|
|
278
|
-
export const makeFetchHandler = <
|
|
331
|
+
export const makeFetchHandler = <
|
|
332
|
+
ROut,
|
|
333
|
+
LayerError,
|
|
334
|
+
REvent = never,
|
|
335
|
+
EventLayerError = never,
|
|
336
|
+
Env extends WorkerEnv = WorkerEnv,
|
|
337
|
+
>(
|
|
279
338
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
280
|
-
options: FetchWorkerOptions<ROut>,
|
|
339
|
+
options: FetchWorkerOptions<ROut, REvent, EventLayerError>,
|
|
281
340
|
): FetchHandler<Env> => {
|
|
282
341
|
const WorkerClass = make(layer, options);
|
|
283
342
|
|
|
@@ -341,11 +400,13 @@ export type Handlers<ROut, Self extends Definition.Any> = {
|
|
|
341
400
|
) => WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
|
|
342
401
|
};
|
|
343
402
|
|
|
344
|
-
export interface Options<
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
403
|
+
export interface Options<
|
|
404
|
+
ROut,
|
|
405
|
+
Self extends Definition.Any,
|
|
406
|
+
REvent = never,
|
|
407
|
+
EventLayerError = never,
|
|
408
|
+
> extends Omit<WorkerOptions<ROut, REvent, EventLayerError, Handlers<ROut | REvent, Self>>, "rpc"> {
|
|
409
|
+
readonly rpc: Handlers<ROut | REvent, Self>;
|
|
349
410
|
}
|
|
350
411
|
|
|
351
412
|
export type LayerOptions = WorkerDefinition.LayerOptions;
|
|
@@ -365,10 +426,10 @@ export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Co
|
|
|
365
426
|
> & {
|
|
366
427
|
readonly id: Id;
|
|
367
428
|
readonly methods: MethodsShape;
|
|
368
|
-
readonly make: <ROut, LayerError>(
|
|
429
|
+
readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
369
430
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
370
|
-
options: Options<ROut, Definition<Id, MethodsShape
|
|
371
|
-
) => WorkerClass<Handlers<ROut, Definition<Id, MethodsShape>>, ROut>;
|
|
431
|
+
options: Options<ROut, Definition<Id, MethodsShape>, REvent, EventLayerError>,
|
|
432
|
+
) => WorkerClass<Handlers<ROut | REvent, Definition<Id, MethodsShape>>, ROut | REvent>;
|
|
372
433
|
readonly layer: (
|
|
373
434
|
options: LayerOptions,
|
|
374
435
|
) => Layer.Layer<
|
package/src/WorkerDefinition.ts
CHANGED
|
@@ -118,11 +118,16 @@ type BoundaryHandlers<ROut, Self extends Definition.Any> = {
|
|
|
118
118
|
/**
|
|
119
119
|
* Worker constructor options for a specific RPC definition.
|
|
120
120
|
*/
|
|
121
|
-
export interface Options<
|
|
122
|
-
|
|
121
|
+
export interface Options<
|
|
122
|
+
ROut,
|
|
123
|
+
Self extends Definition.Any,
|
|
124
|
+
REvent = never,
|
|
125
|
+
EventLayerError = never,
|
|
126
|
+
> extends Omit<
|
|
127
|
+
WorkerEntrypoint.WorkerOptions<ROut, REvent, EventLayerError, Handlers<ROut | REvent, Self>>,
|
|
123
128
|
"rpc"
|
|
124
129
|
> {
|
|
125
|
-
readonly rpc: Handlers<ROut, Self>;
|
|
130
|
+
readonly rpc: Handlers<ROut | REvent, Self>;
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
export type LayerOptions = {
|
|
@@ -144,14 +149,17 @@ export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Co
|
|
|
144
149
|
> & {
|
|
145
150
|
readonly id: Id;
|
|
146
151
|
readonly methods: MethodsShape;
|
|
147
|
-
readonly make: <ROut, LayerError>(
|
|
152
|
+
readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
148
153
|
layer: Layer.Layer<
|
|
149
154
|
ROut,
|
|
150
155
|
LayerError,
|
|
151
156
|
WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
|
|
152
157
|
>,
|
|
153
|
-
options: Options<ROut, Definition<Id, MethodsShape
|
|
154
|
-
) => WorkerEntrypoint.WorkerClass<
|
|
158
|
+
options: Options<ROut, Definition<Id, MethodsShape>, REvent, EventLayerError>,
|
|
159
|
+
) => WorkerEntrypoint.WorkerClass<
|
|
160
|
+
Handlers<ROut | REvent, Definition<Id, MethodsShape>>,
|
|
161
|
+
ROut | REvent
|
|
162
|
+
>;
|
|
155
163
|
readonly layer: (
|
|
156
164
|
options: LayerOptions,
|
|
157
165
|
) => Layer.Layer<
|
|
@@ -199,13 +207,13 @@ const makeDefinition = <Id extends string, const MethodsShape extends Methods>(
|
|
|
199
207
|
const definition: SelfDefinition = RpcDefinition.make(id, methods);
|
|
200
208
|
|
|
201
209
|
return Object.assign(definition, {
|
|
202
|
-
make: <ROut, LayerError>(
|
|
210
|
+
make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
203
211
|
layer: Layer.Layer<
|
|
204
212
|
ROut,
|
|
205
213
|
LayerError,
|
|
206
214
|
WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
|
|
207
215
|
>,
|
|
208
|
-
options: Options<ROut, SelfDefinition>,
|
|
216
|
+
options: Options<ROut, SelfDefinition, REvent, EventLayerError>,
|
|
209
217
|
) =>
|
|
210
218
|
WorkerEntrypoint.make(layer, {
|
|
211
219
|
...options,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Clock, Duration, Effect, Layer } from "effect";
|
|
2
|
+
|
|
3
|
+
const maxTimerMillis = 2 ** 31 - 1;
|
|
4
|
+
const nanosPerMilli = BigInt(1_000_000);
|
|
5
|
+
|
|
6
|
+
export const clock: Clock.Clock = {
|
|
7
|
+
currentTimeMillisUnsafe: () => Date.now(),
|
|
8
|
+
currentTimeMillis: Effect.sync(() => clock.currentTimeMillisUnsafe()),
|
|
9
|
+
currentTimeNanosUnsafe: () => BigInt(Date.now()) * nanosPerMilli,
|
|
10
|
+
currentTimeNanos: Effect.sync(() => clock.currentTimeNanosUnsafe()),
|
|
11
|
+
sleep: (duration) => {
|
|
12
|
+
const millis = Duration.toMillis(duration);
|
|
13
|
+
if (millis <= 0) {
|
|
14
|
+
return Effect.yieldNow;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return Effect.callback<void>((resume) => {
|
|
18
|
+
if (millis > maxTimerMillis) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const handle = setTimeout(() => resume(Effect.void), millis);
|
|
23
|
+
return Effect.sync(() => clearTimeout(handle));
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const layer = Layer.effect(Clock.Clock, Effect.succeed(clock));
|