effect-cf 0.33.0 → 0.35.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 +22 -0
- package/README.md +31 -1079
- package/dist/{Artifacts-DnHQA-Ax.d.mts → Artifacts-B3r3RuTB.d.mts} +1 -48
- package/dist/{Artifacts-CSIk1Oml.mjs → Artifacts-CBxWGeZB.mjs} +2 -7
- package/dist/{Binding-pfilNJmP.mjs → Binding-C82jRhdr.mjs} +1 -2
- package/dist/{Binding-xJJJPmiD.d.mts → Binding-DKcLrQCM.d.mts} +0 -45
- package/dist/ComputerArtifacts.d.mts +1 -3
- package/dist/ComputerArtifacts.mjs +1 -2
- package/dist/ComputerWorkspace.d.mts +11 -12
- package/dist/ComputerWorkspace.mjs +31 -10
- package/dist/ComputerWorkspaceHost.d.mts +1 -9
- package/dist/{ContainerNamespace-CFjiLw8b.d.mts → ContainerNamespace-DbLaeFhN.d.mts} +1 -14
- package/dist/{DurableObjectState-CVLLse69.mjs → DurableObjectState-DcqElJ4e.mjs} +82 -89
- package/dist/{DurableObjectState-BpRjz6-q.d.mts → DurableObjectState-DpYdNZgT.d.mts} +102 -266
- package/dist/{ErrorMessage-BM0K-Nik.mjs → ErrorMessage-Byx2OgNd.mjs} +0 -1
- package/dist/{Hyperdrive-z-9fFz15.d.mts → Hyperdrive-HrwFlZ6x.d.mts} +1 -4
- package/dist/HyperdrivePg.d.mts +2 -11
- package/dist/HyperdrivePg.mjs +0 -8
- package/dist/Sandbox.d.mts +2 -32
- package/dist/Sandbox.mjs +2 -11
- package/dist/Vitest.d.mts +3 -31
- package/dist/Vitest.mjs +8 -26
- package/dist/{WorkflowBinding-lBR4_-op.d.mts → WorkflowBinding-Cn3AuUyP.d.mts} +2 -5
- package/dist/index.d.mts +42 -480
- package/dist/index.mjs +267 -380
- package/package.json +1 -1
- package/src/AiGateway.ts +0 -6
- package/src/AnalyticsEngine.ts +8 -10
- package/src/Artifacts.ts +0 -47
- package/src/Binding.ts +0 -8
- package/src/BrowserRendering.ts +0 -4
- package/src/Cache.ts +0 -19
- package/src/CloudflareOtlp.ts +6 -39
- package/src/ComputerArtifacts.ts +0 -2
- package/src/ComputerWorkspace.ts +53 -35
- package/src/ComputerWorkspaceHost.ts +0 -8
- package/src/ContainerNamespace.ts +0 -13
- package/src/D1.ts +0 -22
- package/src/DurableObject.ts +51 -32
- package/src/DurableObjectAlarm.ts +6 -210
- package/src/DurableObjectDefinition.ts +8 -20
- package/src/DurableObjectNamespace.ts +88 -115
- package/src/DurableObjectRpcWebSocket.ts +5 -46
- package/src/DurableObjectSqlite.ts +0 -12
- package/src/DurableObjectState.ts +18 -38
- package/src/DurableObjectStorage.ts +55 -70
- package/src/DurableObjectWebSocket.ts +23 -72
- package/src/Email.ts +0 -12
- package/src/Environment.ts +0 -37
- package/src/Hyperdrive.ts +0 -3
- package/src/HyperdrivePg.ts +0 -9
- package/src/Images.ts +0 -5
- package/src/Kv.ts +0 -15
- package/src/QueueBinding.ts +0 -2
- package/src/R2.ts +0 -5
- package/src/RpcDefinition.ts +5 -17
- package/src/RpcTracing.ts +133 -0
- package/src/Sandbox.ts +0 -30
- package/src/ServiceBinding.ts +83 -128
- package/src/Vectorize.ts +0 -5
- package/src/Vitest.ts +18 -43
- package/src/WebTransport.ts +0 -15
- package/src/Worker.ts +140 -108
- package/src/WorkerDefinition.ts +5 -14
- package/src/WorkersAi.ts +21 -18
- package/src/Workflow.ts +45 -54
- package/src/WorkflowBinding.ts +0 -3
- package/src/index.ts +1 -0
- package/src/internal/Entrypoint.ts +23 -4
- package/src/internal/ErrorMessage.ts +0 -1
- package/src/internal/NativeCallback.ts +55 -0
- package/src/internal/RpcInvocation.ts +18 -1
- package/src/internal/Runtime.ts +12 -7
- package/src/internal/WorkerContext.ts +5 -14
package/src/Worker.ts
CHANGED
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
type Cause,
|
|
4
|
+
Context,
|
|
5
|
+
Effect,
|
|
6
|
+
Layer,
|
|
7
|
+
type ManagedRuntime,
|
|
8
|
+
References,
|
|
9
|
+
type Scope,
|
|
10
|
+
Tracer,
|
|
11
|
+
} from "effect";
|
|
12
|
+
import {
|
|
13
|
+
HttpEffect,
|
|
14
|
+
HttpMiddleware,
|
|
15
|
+
HttpServerRequest,
|
|
16
|
+
HttpServerResponse,
|
|
17
|
+
} from "effect/unstable/http";
|
|
4
18
|
|
|
5
19
|
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
6
20
|
import { fromMessage, fromMessageBatch, type QueueHandler } from "./Queue";
|
|
7
|
-
import
|
|
21
|
+
import * as RpcDefinition from "./RpcDefinition";
|
|
22
|
+
import type { ReceiverOptions, RpcInvocationInfo } from "./RpcTracing";
|
|
8
23
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
9
24
|
import * as Runtime from "./internal/Runtime";
|
|
10
25
|
import * as Telemetry from "./internal/Telemetry";
|
|
11
26
|
import { fromExecutionContext } from "./internal/WorkerContext";
|
|
12
27
|
|
|
13
|
-
/**
|
|
14
|
-
* Access to Cloudflare's native `ExecutionContext`.
|
|
15
|
-
*/
|
|
16
28
|
export class ExecutionContext extends Context.Service<
|
|
17
29
|
ExecutionContext,
|
|
18
30
|
globalThis.ExecutionContext
|
|
19
31
|
>()("effect-cf/ExecutionContext") {}
|
|
20
32
|
|
|
21
|
-
/**
|
|
22
|
-
* Options for {@link WorkerContextService.waitUntil}.
|
|
23
|
-
*/
|
|
24
33
|
export interface WorkerContextWaitUntilOptions<E, R> {
|
|
25
34
|
/**
|
|
26
35
|
* Failure mode for the background effect: `"observe"` (default) logs or
|
|
@@ -28,13 +37,9 @@ export interface WorkerContextWaitUntilOptions<E, R> {
|
|
|
28
37
|
* native `waitUntil` promise.
|
|
29
38
|
*/
|
|
30
39
|
readonly mode?: "observe" | "propagate";
|
|
31
|
-
/** Custom failure handler for the background effect. */
|
|
32
40
|
readonly onFailure?: (cause: Cause.Cause<E>) => Effect.Effect<void, never, R>;
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
/**
|
|
36
|
-
* Effect wrapper around `ExecutionContext` background APIs.
|
|
37
|
-
*/
|
|
38
43
|
export interface WorkerContextService {
|
|
39
44
|
readonly raw: globalThis.ExecutionContext;
|
|
40
45
|
waitUntil<A, E, R, R2 = never>(
|
|
@@ -46,36 +51,17 @@ export interface WorkerContextService {
|
|
|
46
51
|
options?: Omit<WorkerContextWaitUntilOptions<E, R2>, "mode">,
|
|
47
52
|
): Effect.Effect<void, never, R | R2>;
|
|
48
53
|
readonly passThroughOnException: Effect.Effect<void>;
|
|
49
|
-
/** Abort the current stateless Worker invocation (`ExecutionContext.abort`). */
|
|
50
54
|
abort(reason?: string): Effect.Effect<void>;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
/**
|
|
54
|
-
* Service used inside handlers to schedule background work via `waitUntil`.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* const handler = Effect.gen(function* () {
|
|
59
|
-
* const ctx = yield* Worker.WorkerContext;
|
|
60
|
-
* yield* ctx.waitUntil(Effect.log("flush analytics"));
|
|
61
|
-
* return new Response("ok");
|
|
62
|
-
* });
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
57
|
export class WorkerContext extends Context.Service<WorkerContext, WorkerContextService>()(
|
|
66
58
|
"effect-cf/WorkerContext",
|
|
67
59
|
) {}
|
|
68
60
|
|
|
69
|
-
/**
|
|
70
|
-
* Access to the incoming `Request` currently handled by a worker or Durable Object fetch.
|
|
71
|
-
*/
|
|
72
61
|
export class NativeRequest extends Context.Service<NativeRequest, Request>()(
|
|
73
62
|
"effect-cf/NativeRequest",
|
|
74
63
|
) {}
|
|
75
64
|
|
|
76
|
-
/**
|
|
77
|
-
* Returns `true` when the request is a websocket upgrade request.
|
|
78
|
-
*/
|
|
79
65
|
export const isWebSocketUpgrade = (request: Request): boolean =>
|
|
80
66
|
request.headers.get("Upgrade")?.toLowerCase() === "websocket";
|
|
81
67
|
|
|
@@ -95,20 +81,13 @@ export type ReservedMethodName =
|
|
|
95
81
|
| "webSocketError";
|
|
96
82
|
|
|
97
83
|
const reservedMethodNames = new Set<string>([
|
|
98
|
-
|
|
99
|
-
"dup",
|
|
100
|
-
"fetch",
|
|
101
|
-
"connect",
|
|
84
|
+
...RpcDefinition.reservedMethodNames,
|
|
102
85
|
"queue",
|
|
103
86
|
"scheduled",
|
|
104
87
|
"tail",
|
|
105
88
|
"tailStream",
|
|
106
89
|
"test",
|
|
107
90
|
"trace",
|
|
108
|
-
"alarm",
|
|
109
|
-
"webSocketMessage",
|
|
110
|
-
"webSocketClose",
|
|
111
|
-
"webSocketError",
|
|
112
91
|
]);
|
|
113
92
|
|
|
114
93
|
type WorkerBaseContext<ROut> = ExecutionContext | WorkerContext | WorkerEnvironment | ROut;
|
|
@@ -120,14 +99,16 @@ type WorkerFetchContext<ROut> =
|
|
|
120
99
|
type WorkerRpcContext<ROut> = WorkerBaseContext<ROut> | Scope.Scope;
|
|
121
100
|
|
|
122
101
|
type RuntimeContext<ROut> = WorkerBaseContext<ROut>;
|
|
123
|
-
|
|
102
|
+
/** Override to instrument the whole event, then call super[RunSymbol]. */
|
|
103
|
+
export const RunSymbol = Symbol.for("effect-cf/Worker/run");
|
|
124
104
|
const FetchSymbol = Symbol.for("effect-cf/Worker/fetch");
|
|
125
105
|
|
|
126
|
-
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
106
|
+
/** Metadata available before an event effect or its event layer starts. */
|
|
107
|
+
export interface RunOptions {
|
|
108
|
+
readonly event?: "fetch" | "rpc" | "queue";
|
|
109
|
+
readonly rpc?: RpcInvocationInfo;
|
|
110
|
+
}
|
|
111
|
+
|
|
131
112
|
export type WorkerFetchSuccess = Response | HttpServerResponse.HttpServerResponse;
|
|
132
113
|
|
|
133
114
|
/**
|
|
@@ -173,9 +154,6 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
173
154
|
: never;
|
|
174
155
|
};
|
|
175
156
|
|
|
176
|
-
/**
|
|
177
|
-
* Options for creating a worker class with Effect handlers.
|
|
178
|
-
*/
|
|
179
157
|
export interface WorkerOptions<
|
|
180
158
|
RRuntime,
|
|
181
159
|
REvent = never,
|
|
@@ -185,19 +163,19 @@ export interface WorkerOptions<
|
|
|
185
163
|
/**
|
|
186
164
|
* Layer provided around each Cloudflare event handled by this Worker.
|
|
187
165
|
*
|
|
188
|
-
* The layer is built inside the event's Effect scope
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
166
|
+
* The layer is built inside the event's Effect scope. For fetch handlers that
|
|
167
|
+
* return an Effect `HttpServerResponse` stream, its scope remains open until
|
|
168
|
+
* the response body finishes or is canceled. Native `Response` values pass
|
|
169
|
+
* through unchanged, so their body streams do not carry this scoped-lifetime
|
|
170
|
+
* guarantee. Fetch, queue, and RPC handlers schedule a best-effort telemetry
|
|
171
|
+
* flush capped at two seconds.
|
|
192
172
|
*/
|
|
193
173
|
readonly eventLayer?: Layer.Layer<REvent, EventLayerError, WorkerBaseContext<RRuntime>>;
|
|
194
|
-
/** Main request handler. */
|
|
195
174
|
readonly fetch?: Effect.Effect<
|
|
196
175
|
WorkerFetchSuccess,
|
|
197
176
|
unknown,
|
|
198
177
|
WorkerFetchContext<RRuntime | REvent>
|
|
199
178
|
>;
|
|
200
|
-
/** Queue consumer handler invoked per message batch. */
|
|
201
179
|
readonly queue?: QueueHandler<RRuntime | REvent>;
|
|
202
180
|
/**
|
|
203
181
|
* Optional RPC methods exposed as class instance methods.
|
|
@@ -207,6 +185,8 @@ export interface WorkerOptions<
|
|
|
207
185
|
* flush silently settles within two seconds even if the exporter does not.
|
|
208
186
|
*/
|
|
209
187
|
readonly rpc?: Rpc;
|
|
188
|
+
/** Accept live native trace metadata. The client must also enable rpcTracing. */
|
|
189
|
+
readonly rpcTracing?: ReceiverOptions;
|
|
210
190
|
}
|
|
211
191
|
|
|
212
192
|
type WorkerOptionsWithEventLayer<
|
|
@@ -241,6 +221,10 @@ export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
|
|
|
241
221
|
) => CloudflareWorkerEntrypoint<WorkerEnv> & {
|
|
242
222
|
fetch(request: Request): Promise<Response>;
|
|
243
223
|
queue(batch: globalThis.MessageBatch): Promise<void>;
|
|
224
|
+
[RunSymbol]<A, E>(
|
|
225
|
+
effect: Effect.Effect<A, E, WorkerRpcContext<ROut>>,
|
|
226
|
+
options?: RunOptions,
|
|
227
|
+
): Promise<A>;
|
|
244
228
|
[FetchSymbol](request: Request, requestContext: globalThis.ExecutionContext): Promise<Response>;
|
|
245
229
|
} & WorkerRpcContract<Rpc, ROut>;
|
|
246
230
|
|
|
@@ -255,9 +239,14 @@ export interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
|
|
|
255
239
|
) => Promise<Response>;
|
|
256
240
|
}
|
|
257
241
|
|
|
258
|
-
const renderFetchSuccess = <E, R>(
|
|
242
|
+
const renderFetchSuccess = <E, R, REvent, EventLayerError, EventLayerRequirements>(
|
|
259
243
|
effect: Effect.Effect<WorkerFetchSuccess, E, R>,
|
|
260
|
-
|
|
244
|
+
eventLayer: Layer.Layer<REvent, EventLayerError, EventLayerRequirements> | undefined,
|
|
245
|
+
): Effect.Effect<
|
|
246
|
+
Response,
|
|
247
|
+
E | EventLayerError,
|
|
248
|
+
Exclude<R | EventLayerRequirements, Scope.Scope> | HttpServerRequest.HttpServerRequest
|
|
249
|
+
> =>
|
|
261
250
|
Effect.suspend(() => {
|
|
262
251
|
// Native `Response` values bypass all HTTP response processing: WebSocket
|
|
263
252
|
// upgrade responses and app-level `HttpEffect.toHandled` results pass
|
|
@@ -266,26 +255,64 @@ const renderFetchSuccess = <E, R>(
|
|
|
266
255
|
let nativeResponse: Response | undefined;
|
|
267
256
|
let webResponse: Response | undefined;
|
|
268
257
|
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
nativeResponse = response;
|
|
258
|
+
const httpApp = Effect.flatMap(effect, (response) => {
|
|
259
|
+
if (response instanceof Response) {
|
|
260
|
+
nativeResponse = response;
|
|
273
261
|
|
|
274
|
-
|
|
275
|
-
|
|
262
|
+
return Effect.succeed(HttpServerResponse.empty());
|
|
263
|
+
}
|
|
276
264
|
|
|
277
|
-
|
|
265
|
+
return Effect.succeed(response);
|
|
266
|
+
});
|
|
267
|
+
const handleResponse = (
|
|
268
|
+
request: HttpServerRequest.HttpServerRequest,
|
|
269
|
+
response: HttpServerResponse.HttpServerResponse,
|
|
270
|
+
) =>
|
|
271
|
+
nativeResponse !== undefined
|
|
272
|
+
? Effect.void
|
|
273
|
+
: Effect.map(Effect.context<never>(), (context) => {
|
|
274
|
+
webResponse = HttpServerResponse.toWeb(HttpEffect.scopeTransferToStream(response), {
|
|
275
|
+
withoutBody: request.method === "HEAD",
|
|
276
|
+
context,
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
const telemetryMiddleware = HttpMiddleware.make((self) =>
|
|
280
|
+
Effect.gen(function* () {
|
|
281
|
+
yield* Effect.addFinalizer(() => scheduleTelemetryFlush);
|
|
282
|
+
|
|
283
|
+
return yield* self;
|
|
278
284
|
}),
|
|
279
|
-
(request, response) =>
|
|
280
|
-
nativeResponse !== undefined
|
|
281
|
-
? Effect.void
|
|
282
|
-
: Effect.map(Effect.context<never>(), (context) => {
|
|
283
|
-
webResponse = HttpServerResponse.toWeb(HttpEffect.scopeTransferToStream(response), {
|
|
284
|
-
withoutBody: request.method === "HEAD",
|
|
285
|
-
context,
|
|
286
|
-
});
|
|
287
|
-
}),
|
|
288
285
|
);
|
|
286
|
+
const handled =
|
|
287
|
+
eventLayer === undefined
|
|
288
|
+
? HttpEffect.toHandled(httpApp, handleResponse, telemetryMiddleware)
|
|
289
|
+
: Effect.flatMap(References.TracerEnabled, (tracerEnabled) =>
|
|
290
|
+
HttpEffect.toHandled(
|
|
291
|
+
httpApp,
|
|
292
|
+
handleResponse,
|
|
293
|
+
HttpMiddleware.make((self) =>
|
|
294
|
+
Effect.provideService(
|
|
295
|
+
Effect.gen(function* () {
|
|
296
|
+
const context = yield* Layer.build(Layer.fresh(eventLayer));
|
|
297
|
+
|
|
298
|
+
yield* Effect.addFinalizer(() =>
|
|
299
|
+
Effect.provideContext(scheduleTelemetryFlush, context),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
return yield* HttpMiddleware.tracer(self).pipe(
|
|
303
|
+
// The tracer schedules span completion. Let it publish on
|
|
304
|
+
// every exit with event references still installed, before
|
|
305
|
+
// the exporter scope finalizes.
|
|
306
|
+
Effect.onExit(() => Effect.yieldNow),
|
|
307
|
+
Effect.provideContext(context),
|
|
308
|
+
);
|
|
309
|
+
}),
|
|
310
|
+
References.TracerEnabled,
|
|
311
|
+
tracerEnabled,
|
|
312
|
+
),
|
|
313
|
+
),
|
|
314
|
+
).pipe(Effect.withTracerEnabled(false)),
|
|
315
|
+
);
|
|
289
316
|
|
|
290
317
|
// `toHandled` re-fails with the original cause after rendering the error
|
|
291
318
|
// response, so both branches return the response captured above.
|
|
@@ -299,13 +326,13 @@ const isWorkerOptions = <ROut, REvent, EventLayerError, Rpc extends WorkerRpc<RO
|
|
|
299
326
|
): options is WorkerOptions<ROut, REvent, EventLayerError, Rpc> => !Effect.isEffect(options);
|
|
300
327
|
|
|
301
328
|
/**
|
|
302
|
-
* Drains buffered OTLP telemetry
|
|
303
|
-
* response
|
|
329
|
+
* Drains buffered OTLP telemetry when the fetch Effect scope closes. Effect
|
|
330
|
+
* response streams transfer that scope to the body and schedule the flush when
|
|
331
|
+
* the body finishes or is canceled.
|
|
304
332
|
*
|
|
305
|
-
* The flush is handed to `ctx.waitUntil`
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* best-effort and silently settles within two seconds.
|
|
333
|
+
* The flush is handed to `ctx.waitUntil` instead of being awaited by the
|
|
334
|
+
* handler. It is a no-op when the context does not contain an
|
|
335
|
+
* `OtlpExporter.Flusher`, and silently settles within two seconds.
|
|
309
336
|
*/
|
|
310
337
|
const scheduleTelemetryFlush: Effect.Effect<void, never, WorkerContext> =
|
|
311
338
|
Telemetry.scheduleTelemetryFlush((flush) =>
|
|
@@ -313,19 +340,10 @@ const scheduleTelemetryFlush: Effect.Effect<void, never, WorkerContext> =
|
|
|
313
340
|
);
|
|
314
341
|
|
|
315
342
|
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* for
|
|
320
|
-
* isolate is evicted; Cloudflare provides no shutdown hook. Use `eventLayer`
|
|
321
|
-
* for resources that must be finalized per event.
|
|
322
|
-
*
|
|
323
|
-
* @example
|
|
324
|
-
* ```ts
|
|
325
|
-
* export default Worker.make(Layer.empty, {
|
|
326
|
-
* fetch: Effect.succeed(new Response("ok")),
|
|
327
|
-
* });
|
|
328
|
-
* ```
|
|
343
|
+
* Cloudflare creates a new `WorkerEntrypoint` instance for each native
|
|
344
|
+
* invocation, so this base layer is not shared across invocations. Its managed
|
|
345
|
+
* runtime has no deterministic disposal point in this adapter; use
|
|
346
|
+
* `eventLayer` for resources whose finalizers must run with an event.
|
|
329
347
|
*/
|
|
330
348
|
export function make<ROut, LayerError>(
|
|
331
349
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
@@ -387,15 +405,22 @@ export function make<
|
|
|
387
405
|
|
|
388
406
|
[RunSymbol]<A, E>(
|
|
389
407
|
effect: Effect.Effect<A, E, RuntimeContext<ROut> | REvent | Scope.Scope>,
|
|
408
|
+
runOptions: RunOptions = {},
|
|
390
409
|
): Promise<A> {
|
|
391
|
-
|
|
410
|
+
// Fetch builds its event layer in the HTTP scope transferred to the body.
|
|
411
|
+
const eventLayer = runOptions.event === "fetch" ? undefined : options.eventLayer;
|
|
412
|
+
const parent = runOptions.rpc?.parent;
|
|
413
|
+
const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
|
|
392
414
|
|
|
393
415
|
if (eventLayer === undefined) {
|
|
394
|
-
// SAFETY:
|
|
416
|
+
// SAFETY: fetch provides REvent inside its HTTP middleware; otherwise the
|
|
417
|
+
// public make overloads permit an absent event layer only when REvent is never.
|
|
395
418
|
return Runtime.runEventPromise(
|
|
396
419
|
this.runtime,
|
|
397
420
|
// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
|
|
398
421
|
effect as Effect.Effect<A, E, RuntimeContext<ROut> | Scope.Scope>,
|
|
422
|
+
undefined,
|
|
423
|
+
parentSpan,
|
|
399
424
|
);
|
|
400
425
|
}
|
|
401
426
|
|
|
@@ -406,7 +431,7 @@ export function make<
|
|
|
406
431
|
REvent,
|
|
407
432
|
EventLayerError,
|
|
408
433
|
LayerError
|
|
409
|
-
>(this.runtime, effect, eventLayer);
|
|
434
|
+
>(this.runtime, effect, eventLayer, parentSpan);
|
|
410
435
|
}
|
|
411
436
|
|
|
412
437
|
fetch(request: Request): Promise<Response> {
|
|
@@ -431,12 +456,12 @@ export function make<
|
|
|
431
456
|
Layer.succeed(WorkerContext, fromExecutionContext(ctx)),
|
|
432
457
|
);
|
|
433
458
|
|
|
434
|
-
//
|
|
459
|
+
// The request services wrap eventLayer construction so cached object handlers
|
|
460
|
+
// never build an event layer against a previous request's ExecutionContext.
|
|
461
|
+
// HttpEffect owns the inner scope and transfers it to streaming response bodies.
|
|
435
462
|
return this[RunSymbol](
|
|
436
|
-
renderFetchSuccess(fetchHandler).pipe(
|
|
437
|
-
|
|
438
|
-
Effect.provide(requestServices),
|
|
439
|
-
) as Effect.Effect<Response, unknown, RuntimeContext<ROut> | REvent | Scope.Scope>,
|
|
463
|
+
renderFetchSuccess(fetchHandler, options.eventLayer).pipe(Effect.provide(requestServices)),
|
|
464
|
+
{ event: "fetch" },
|
|
440
465
|
);
|
|
441
466
|
}
|
|
442
467
|
|
|
@@ -449,7 +474,12 @@ export function make<
|
|
|
449
474
|
|
|
450
475
|
const messages = batch.messages.map((message) => fromMessage(message, message.body));
|
|
451
476
|
|
|
452
|
-
return this[RunSymbol](
|
|
477
|
+
return this[RunSymbol](
|
|
478
|
+
queueHandler(fromMessageBatch(batch, messages)).pipe(
|
|
479
|
+
Effect.onExit(() => scheduleTelemetryFlush),
|
|
480
|
+
),
|
|
481
|
+
{ event: "queue" },
|
|
482
|
+
);
|
|
453
483
|
}
|
|
454
484
|
}
|
|
455
485
|
|
|
@@ -458,8 +488,9 @@ export function make<
|
|
|
458
488
|
EffectWorker.prototype,
|
|
459
489
|
options.rpc,
|
|
460
490
|
reservedMethodNames,
|
|
461
|
-
(self, effect) => self[RunSymbol](effect),
|
|
491
|
+
(self, effect, rpc) => self[RunSymbol](effect, { event: "rpc", rpc }),
|
|
462
492
|
() => scheduleTelemetryFlush,
|
|
493
|
+
options.rpcTracing,
|
|
463
494
|
);
|
|
464
495
|
|
|
465
496
|
return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut | REvent>>(EffectWorker);
|
|
@@ -473,12 +504,13 @@ interface FetchCapableInstance {
|
|
|
473
504
|
* Creates an `ExportedHandler`-compatible `{ fetch }` object backed by
|
|
474
505
|
* {@link make}.
|
|
475
506
|
*
|
|
476
|
-
* The Effect runtime is cached per `env` identity, so the
|
|
477
|
-
* once
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
*
|
|
507
|
+
* The Effect runtime is cached per `env` identity, so the base layer builds
|
|
508
|
+
* once for that cached handler. Request handlers and `eventLayer` receive fresh
|
|
509
|
+
* `ExecutionContext` and `WorkerContext` services. A service constructed by the
|
|
510
|
+
* cached base layer from either context instead captures the first request's
|
|
511
|
+
* value, so construct invocation-bound services in `eventLayer`. Cloudflare
|
|
512
|
+
* provides no isolate shutdown hook, so cached base-layer finalizers are not
|
|
513
|
+
* guaranteed to run.
|
|
482
514
|
*/
|
|
483
515
|
export function makeFetchHandler<
|
|
484
516
|
ROut,
|
package/src/WorkerDefinition.ts
CHANGED
|
@@ -6,6 +6,7 @@ import * as WorkerEntrypoint from "./Worker";
|
|
|
6
6
|
import type { WorkerRpcHandler } from "./Worker";
|
|
7
7
|
import type * as Rpc from "./Rpc";
|
|
8
8
|
import * as RpcDefinition from "./RpcDefinition";
|
|
9
|
+
import { recordDecodedArgs } from "./internal/RpcInvocation";
|
|
9
10
|
import * as ServiceBinding from "./ServiceBinding";
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -37,7 +38,6 @@ export namespace Method {
|
|
|
37
38
|
? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
|
|
38
39
|
: Array<S.Schema.Type<Args[number]>>;
|
|
39
40
|
|
|
40
|
-
/** Method schemas cross the wire through their canonical JSON codec. */
|
|
41
41
|
type EncodedArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = {
|
|
42
42
|
[Index in keyof Args]: S.Json;
|
|
43
43
|
};
|
|
@@ -153,6 +153,7 @@ export type Options<
|
|
|
153
153
|
|
|
154
154
|
export type LayerOptions = {
|
|
155
155
|
readonly binding: string;
|
|
156
|
+
readonly rpcTracing?: boolean;
|
|
156
157
|
};
|
|
157
158
|
|
|
158
159
|
export type TagClass<
|
|
@@ -236,19 +237,6 @@ export const method: {
|
|
|
236
237
|
}): Method<Args, Success>;
|
|
237
238
|
} = RpcDefinition.method;
|
|
238
239
|
|
|
239
|
-
/**
|
|
240
|
-
* Creates a typed worker RPC definition plus helpers for implementation and bindings.
|
|
241
|
-
*
|
|
242
|
-
* @example
|
|
243
|
-
* ```ts
|
|
244
|
-
* const CounterWorker = WorkerDefinition.make("CounterWorker", {
|
|
245
|
-
* increment: WorkerDefinition.method({
|
|
246
|
-
* args: [Schema.Number],
|
|
247
|
-
* success: Schema.Number,
|
|
248
|
-
* }),
|
|
249
|
-
* });
|
|
250
|
-
* ```
|
|
251
|
-
*/
|
|
252
240
|
const makeDefinition = <Id extends string, const MethodDefinitions extends Methods>(
|
|
253
241
|
id: Id,
|
|
254
242
|
methods: MethodDefinitions & NoReservedMethods<MethodDefinitions>,
|
|
@@ -414,6 +402,9 @@ const wrapHandlers = <ROut, const Self extends Definition.Any>(
|
|
|
414
402
|
wrapped[key] = (...args: Array<unknown>) =>
|
|
415
403
|
Effect.gen(function* () {
|
|
416
404
|
const decodedArgs = yield* RpcDefinition.decodeArgs(definition, key, args);
|
|
405
|
+
|
|
406
|
+
yield* recordDecodedArgs(decodedArgs);
|
|
407
|
+
|
|
417
408
|
const value = yield* handler(...decodedArgs);
|
|
418
409
|
|
|
419
410
|
return yield* RpcDefinition.encodeSuccess(definition, key, value);
|
package/src/WorkersAi.ts
CHANGED
|
@@ -18,7 +18,6 @@ import * as ErrorMessage from "./internal/ErrorMessage";
|
|
|
18
18
|
|
|
19
19
|
const expectedWorkersAiBinding = "Workers AI binding with run(), gateway(), and models()";
|
|
20
20
|
|
|
21
|
-
/** Error raised when a Workers AI operation fails. */
|
|
22
21
|
export class WorkersAiOperationError extends Data.TaggedError("WorkersAiOperationError")<{
|
|
23
22
|
readonly binding: string;
|
|
24
23
|
readonly operation: string;
|
|
@@ -29,9 +28,7 @@ export class WorkersAiOperationError extends Data.TaggedError("WorkersAiOperatio
|
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
/** Typed Workers AI binding definition. */
|
|
33
31
|
export interface WorkersAiDefinition {
|
|
34
|
-
/** Binding name as configured in `wrangler.jsonc`. */
|
|
35
32
|
readonly binding: string;
|
|
36
33
|
}
|
|
37
34
|
|
|
@@ -117,7 +114,6 @@ export interface WorkersAiClient<ModelList extends AiModelListType = AiModels> {
|
|
|
117
114
|
|
|
118
115
|
declare const WorkersAiServiceTypeId: unique symbol;
|
|
119
116
|
|
|
120
|
-
/** Nominal service marker for Workers AI services created with {@link make}. */
|
|
121
117
|
export interface WorkersAiService<Id extends string> {
|
|
122
118
|
readonly [WorkersAiServiceTypeId]: {
|
|
123
119
|
readonly id: Id;
|
|
@@ -190,7 +186,7 @@ export const embeddingResponse = (value: {
|
|
|
190
186
|
[embeddingDimensionsKey]: value[embeddingDimensionsKey] ?? [],
|
|
191
187
|
});
|
|
192
188
|
|
|
193
|
-
const decodeEmbeddingResponse = S.
|
|
189
|
+
const decodeEmbeddingResponse = S.decodeUnknownEffect(
|
|
194
190
|
S.Struct({
|
|
195
191
|
data: S.optional(S.Array(S.Array(S.Number))),
|
|
196
192
|
[embeddingDimensionsKey]: S.optional(S.Array(S.Number)),
|
|
@@ -216,21 +212,28 @@ export const makeClient =
|
|
|
216
212
|
() => ai.aiGatewayLogId,
|
|
217
213
|
),
|
|
218
214
|
run,
|
|
219
|
-
runEmbedding: Effect.fn("WorkersAi.runEmbedding")(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
runEmbedding: Effect.fn("WorkersAi.runEmbedding")(function* (
|
|
216
|
+
model: string,
|
|
217
|
+
input: WorkersAiEmbeddingInput,
|
|
218
|
+
options?: WorkersAiOptions,
|
|
219
|
+
) {
|
|
220
|
+
// SAFETY: every WorkersAiEmbeddingInput variant is a string-keyed Workers AI payload.
|
|
221
|
+
const modelInput = input as Parameters<WorkersAiBinding["run"]>[1];
|
|
223
222
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
223
|
+
const response = yield* run(model, modelInput, options);
|
|
224
|
+
const decoded = yield* decodeEmbeddingResponse(response).pipe(
|
|
225
|
+
Effect.mapError(
|
|
226
|
+
(cause) =>
|
|
227
|
+
new WorkersAiOperationError({
|
|
228
|
+
binding: definition.binding,
|
|
229
|
+
operation: "runEmbedding",
|
|
230
|
+
cause,
|
|
229
231
|
}),
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
return embeddingResponse(decoded);
|
|
236
|
+
}),
|
|
234
237
|
models: Effect.fn("WorkersAi.models")((params?: WorkersAiModelsSearchParams) =>
|
|
235
238
|
tryWorkersAiPromise(definition.binding, "models", () => ai.models(params)),
|
|
236
239
|
),
|