effect-cf 0.20.1 → 0.22.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 +44 -0
- package/README.md +2 -2
- 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 +708 -549
- package/dist/index.mjs +924 -587
- 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 +87 -55
- 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 +6 -9
- 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 +14 -2
- package/src/Entry.ts +0 -259
- package/src/internal/Clock.ts +0 -37
package/src/Worker.ts
CHANGED
|
@@ -1,37 +1,48 @@
|
|
|
1
1
|
import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
|
|
2
2
|
import {
|
|
3
3
|
type Cause,
|
|
4
|
-
ConfigProvider,
|
|
5
4
|
Context,
|
|
6
5
|
Effect,
|
|
7
6
|
Layer,
|
|
8
|
-
ManagedRuntime,
|
|
9
|
-
|
|
7
|
+
type ManagedRuntime,
|
|
8
|
+
Option,
|
|
10
9
|
type Scope,
|
|
11
10
|
} from "effect";
|
|
12
11
|
import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
12
|
+
import { OtlpExporter } from "effect/unstable/observability";
|
|
13
13
|
|
|
14
|
-
import type
|
|
15
|
-
import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
14
|
+
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
16
15
|
import { fromMessage, fromMessageBatch, type QueueHandler } from "./Queue";
|
|
17
|
-
import type * as Rpc from "./Rpc";
|
|
18
16
|
import type * as RpcDefinition from "./RpcDefinition";
|
|
19
|
-
import type * as ServiceBinding from "./ServiceBinding";
|
|
20
|
-
import * as WorkerDefinition from "./WorkerDefinition";
|
|
21
|
-
import * as CloudflareClock from "./internal/Clock";
|
|
22
17
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
23
|
-
import
|
|
18
|
+
import * as Runtime from "./internal/Runtime";
|
|
19
|
+
import { fromExecutionContext } from "./internal/WorkerContext";
|
|
24
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Access to Cloudflare's native `ExecutionContext`.
|
|
23
|
+
*/
|
|
25
24
|
export class ExecutionContext extends Context.Service<
|
|
26
25
|
ExecutionContext,
|
|
27
26
|
globalThis.ExecutionContext
|
|
28
27
|
>()("effect-cf/ExecutionContext") {}
|
|
29
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Options for {@link WorkerContextService.waitUntil}.
|
|
31
|
+
*/
|
|
30
32
|
export interface WorkerContextWaitUntilOptions<E, R> {
|
|
33
|
+
/**
|
|
34
|
+
* Failure mode for the background effect: `"observe"` (default) logs or
|
|
35
|
+
* routes the failure to `onFailure`, while `"propagate"` also rejects the
|
|
36
|
+
* native `waitUntil` promise.
|
|
37
|
+
*/
|
|
31
38
|
readonly mode?: "observe" | "propagate";
|
|
39
|
+
/** Custom failure handler for the background effect. */
|
|
32
40
|
readonly onFailure?: (cause: Cause.Cause<E>) => Effect.Effect<void, never, R>;
|
|
33
41
|
}
|
|
34
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Effect wrapper around `ExecutionContext` background APIs.
|
|
45
|
+
*/
|
|
35
46
|
export interface WorkerContextService {
|
|
36
47
|
readonly raw: globalThis.ExecutionContext;
|
|
37
48
|
waitUntil<A, E, R, R2 = never>(
|
|
@@ -45,14 +56,32 @@ export interface WorkerContextService {
|
|
|
45
56
|
readonly passThroughOnException: Effect.Effect<void>;
|
|
46
57
|
}
|
|
47
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Service used inside handlers to schedule background work via `waitUntil`.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const handler = Effect.gen(function* () {
|
|
65
|
+
* const ctx = yield* Worker.WorkerContext;
|
|
66
|
+
* yield* ctx.waitUntil(Effect.log("flush analytics"));
|
|
67
|
+
* return new Response("ok");
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
48
71
|
export class WorkerContext extends Context.Service<WorkerContext, WorkerContextService>()(
|
|
49
72
|
"effect-cf/WorkerContext",
|
|
50
73
|
) {}
|
|
51
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Access to the incoming `Request` currently handled by a worker or Durable Object fetch.
|
|
77
|
+
*/
|
|
52
78
|
export class NativeRequest extends Context.Service<NativeRequest, Request>()(
|
|
53
79
|
"effect-cf/NativeRequest",
|
|
54
80
|
) {}
|
|
55
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Returns `true` when the request is a websocket upgrade request.
|
|
84
|
+
*/
|
|
56
85
|
export const isWebSocketUpgrade = (request: Request): boolean =>
|
|
57
86
|
request.headers.get("Upgrade")?.toLowerCase() === "websocket";
|
|
58
87
|
|
|
@@ -102,17 +131,32 @@ type RunOptions = {
|
|
|
102
131
|
};
|
|
103
132
|
|
|
104
133
|
const RunSymbol = Symbol.for("effect-cf/Worker/run");
|
|
134
|
+
const FetchSymbol = Symbol.for("effect-cf/Worker/fetch");
|
|
105
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Successful result of a worker fetch handler: either a native `Response`
|
|
138
|
+
* (returned to Cloudflare untouched) or an Effect `HttpServerResponse`
|
|
139
|
+
* (rendered through the Effect HTTP pipeline).
|
|
140
|
+
*/
|
|
106
141
|
export type WorkerFetchSuccess = Response | HttpServerResponse.HttpServerResponse;
|
|
107
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Effect type for `fetch` handlers executed by {@link make}.
|
|
145
|
+
*/
|
|
108
146
|
export type WorkerHandler<ROut, A = WorkerFetchSuccess> = Effect.Effect<
|
|
109
147
|
A,
|
|
110
148
|
unknown,
|
|
111
149
|
WorkerFetchContext<ROut>
|
|
112
150
|
>;
|
|
113
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Effect type for worker RPC handlers.
|
|
154
|
+
*/
|
|
114
155
|
export type WorkerRpcHandler<ROut, A = unknown> = Effect.Effect<A, unknown, WorkerRpcContext<ROut>>;
|
|
115
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Shape of worker RPC handlers passed to {@link make}.
|
|
159
|
+
*/
|
|
116
160
|
export type WorkerRpc<ROut> = Record<string, (...args: Array<any>) => WorkerRpcHandler<ROut>>;
|
|
117
161
|
|
|
118
162
|
export type WorkerRpcShape<Rpc extends WorkerRpc<ROut>, ROut> = {
|
|
@@ -139,6 +183,9 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
139
183
|
: never;
|
|
140
184
|
};
|
|
141
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Options for creating a worker class with Effect handlers.
|
|
188
|
+
*/
|
|
142
189
|
export interface WorkerOptions<
|
|
143
190
|
RRuntime,
|
|
144
191
|
REvent = never,
|
|
@@ -153,12 +200,15 @@ export interface WorkerOptions<
|
|
|
153
200
|
* trace/log exporters that should flush at event completion.
|
|
154
201
|
*/
|
|
155
202
|
readonly eventLayer?: Layer.Layer<REvent, EventLayerError, WorkerBaseContext<RRuntime>>;
|
|
203
|
+
/** Main request handler. */
|
|
156
204
|
readonly fetch?: Effect.Effect<
|
|
157
205
|
WorkerFetchSuccess,
|
|
158
206
|
unknown,
|
|
159
207
|
WorkerFetchContext<RRuntime | REvent>
|
|
160
208
|
>;
|
|
209
|
+
/** Queue consumer handler invoked per message batch. */
|
|
161
210
|
readonly queue?: QueueHandler<RRuntime | REvent>;
|
|
211
|
+
/** Optional RPC methods exposed as class instance methods. */
|
|
162
212
|
readonly rpc?: Rpc;
|
|
163
213
|
}
|
|
164
214
|
|
|
@@ -169,6 +219,9 @@ export type FetchWorkerOptions<RRuntime, REvent = never, EventLayerError = never
|
|
|
169
219
|
readonly rpc?: never;
|
|
170
220
|
};
|
|
171
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Cloudflare `WorkerEntrypoint` constructor produced by {@link make}.
|
|
224
|
+
*/
|
|
172
225
|
export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
|
|
173
226
|
ctx: globalThis.ExecutionContext,
|
|
174
227
|
env: WorkerEnv,
|
|
@@ -177,6 +230,9 @@ export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
|
|
|
177
230
|
queue(batch: globalThis.MessageBatch): Promise<void>;
|
|
178
231
|
} & WorkerRpcShape<Rpc, ROut>;
|
|
179
232
|
|
|
233
|
+
/**
|
|
234
|
+
* `ExportedHandler`-compatible fetch object produced by {@link makeFetchHandler}.
|
|
235
|
+
*/
|
|
180
236
|
export interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
|
|
181
237
|
readonly fetch: (
|
|
182
238
|
request: Request,
|
|
@@ -185,22 +241,14 @@ export interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
|
|
|
185
241
|
) => Promise<Response>;
|
|
186
242
|
}
|
|
187
243
|
|
|
188
|
-
export const renderHttpResponse = <A extends HttpServerResponse.HttpServerResponse, E, R>(
|
|
189
|
-
effect: Effect.Effect<A, E, R>,
|
|
190
|
-
): Effect.Effect<Response, E, R> =>
|
|
191
|
-
Effect.flatMap(effect, (response) =>
|
|
192
|
-
Effect.map(Effect.context<never>(), (context) =>
|
|
193
|
-
HttpServerResponse.toWeb(response, { context }),
|
|
194
|
-
),
|
|
195
|
-
);
|
|
196
|
-
|
|
197
244
|
const renderFetchSuccess = <E, R>(
|
|
198
245
|
effect: Effect.Effect<WorkerFetchSuccess, E, R>,
|
|
199
246
|
): Effect.Effect<Response, E, Exclude<R, Scope.Scope> | HttpServerRequest.HttpServerRequest> =>
|
|
200
247
|
Effect.suspend(() => {
|
|
201
|
-
// Native `Response` values bypass all HTTP response processing
|
|
202
|
-
//
|
|
203
|
-
//
|
|
248
|
+
// Native `Response` values bypass all HTTP response processing: WebSocket
|
|
249
|
+
// upgrade responses and app-level `HttpEffect.toHandled` results pass
|
|
250
|
+
// through untouched. Pre-response handlers still run against a placeholder
|
|
251
|
+
// response, but their output is discarded for native `Response` results.
|
|
204
252
|
let nativeResponse: Response | undefined;
|
|
205
253
|
let webResponse: Response | undefined;
|
|
206
254
|
|
|
@@ -234,11 +282,40 @@ const renderFetchSuccess = <E, R>(
|
|
|
234
282
|
|
|
235
283
|
const isWorkerOptions = <ROut, REvent, EventLayerError, Rpc extends WorkerRpc<ROut | REvent>>(
|
|
236
284
|
options: WorkerOptions<ROut, REvent, EventLayerError, Rpc> | WorkerHandler<ROut>,
|
|
237
|
-
): options is WorkerOptions<ROut, REvent, EventLayerError, Rpc> =>
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
285
|
+
): options is WorkerOptions<ROut, REvent, EventLayerError, Rpc> => !Effect.isEffect(options);
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Drains buffered OTLP telemetry once the fetch handler has produced its
|
|
289
|
+
* response.
|
|
290
|
+
*
|
|
291
|
+
* The flush is handed to `ctx.waitUntil` so it never delays the response
|
|
292
|
+
* (including streaming bodies still being consumed). It is a no-op when the
|
|
293
|
+
* context does not contain an `OtlpExporter.Flusher`.
|
|
294
|
+
*/
|
|
295
|
+
const scheduleTelemetryFlush: Effect.Effect<void, never, WorkerContext> = Effect.flatMap(
|
|
296
|
+
Effect.serviceOption(OtlpExporter.Flusher),
|
|
297
|
+
Option.match({
|
|
298
|
+
onNone: () => Effect.void,
|
|
299
|
+
onSome: (flusher) =>
|
|
300
|
+
Effect.flatMap(WorkerContext, (workerContext) => workerContext.waitUntil(flusher.flush)),
|
|
301
|
+
}),
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Creates a Cloudflare worker class backed by a single managed Effect runtime.
|
|
306
|
+
*
|
|
307
|
+
* The runtime layer is built when Cloudflare constructs the class and reused
|
|
308
|
+
* for every event the instance handles. Layer finalizers do not run when the
|
|
309
|
+
* isolate is evicted; Cloudflare provides no shutdown hook. Use `eventLayer`
|
|
310
|
+
* for resources that must be finalized per event.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* export default Worker.make(Layer.empty, {
|
|
315
|
+
* fetch: Effect.succeed(new Response("ok")),
|
|
316
|
+
* });
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
242
319
|
export function make<ROut, LayerError>(
|
|
243
320
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
244
321
|
fetch: WorkerHandler<ROut>,
|
|
@@ -273,69 +350,58 @@ export function make<
|
|
|
273
350
|
constructor(ctx: globalThis.ExecutionContext, env: WorkerEnv) {
|
|
274
351
|
super(ctx, env);
|
|
275
352
|
|
|
276
|
-
|
|
277
|
-
Promise.reject(new Error("WorkerContext runtime is not initialized"));
|
|
278
|
-
|
|
279
|
-
const services = Layer.mergeAll(
|
|
280
|
-
CloudflareClock.layer,
|
|
281
|
-
Layer.succeed(ExecutionContext, ctx),
|
|
282
|
-
ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))),
|
|
283
|
-
Layer.succeed(
|
|
284
|
-
WorkerContext,
|
|
285
|
-
fromExecutionContext(ctx, (effect) => runWaitUntilEffect(effect)),
|
|
286
|
-
),
|
|
287
|
-
Layer.succeed(WorkerEnvironment, env),
|
|
288
|
-
) as Layer.Layer<ExecutionContext | WorkerContext | WorkerEnvironment, never, never>;
|
|
289
|
-
|
|
290
|
-
const runtimeLayer = Entrypoint.provideEntrypointServices<
|
|
353
|
+
this.runtime = Runtime.makeEntrypointRuntime<
|
|
291
354
|
ROut,
|
|
292
355
|
LayerError,
|
|
293
|
-
ExecutionContext | WorkerContext
|
|
294
|
-
>(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
356
|
+
ExecutionContext | WorkerContext
|
|
357
|
+
>(
|
|
358
|
+
layer,
|
|
359
|
+
env,
|
|
360
|
+
Layer.mergeAll(
|
|
361
|
+
Layer.succeed(ExecutionContext, ctx),
|
|
362
|
+
Layer.succeed(WorkerContext, fromExecutionContext(ctx)),
|
|
363
|
+
),
|
|
364
|
+
);
|
|
299
365
|
}
|
|
300
366
|
|
|
301
367
|
[RunSymbol]<A, E>(
|
|
302
368
|
effect: Effect.Effect<A, E, RuntimeContext<ROut> | REvent | Scope.Scope>,
|
|
303
369
|
runOptions: RunOptions = {},
|
|
304
370
|
): Promise<A> {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return this.runtime.runPromise(
|
|
311
|
-
Effect.scoped(
|
|
312
|
-
effectWithEventLayer as Effect.Effect<
|
|
313
|
-
A,
|
|
314
|
-
E | EventLayerError,
|
|
315
|
-
RuntimeContext<ROut> | Scope.Scope
|
|
316
|
-
>,
|
|
317
|
-
),
|
|
371
|
+
return Runtime.runEventPromise(
|
|
372
|
+
this.runtime,
|
|
373
|
+
effect,
|
|
374
|
+
runOptions.eventLayer === false ? undefined : options.eventLayer,
|
|
318
375
|
);
|
|
319
376
|
}
|
|
320
377
|
|
|
321
378
|
fetch(request: Request): Promise<Response> {
|
|
379
|
+
return this[FetchSymbol](request);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
[FetchSymbol](
|
|
383
|
+
request: Request,
|
|
384
|
+
requestContext?: globalThis.ExecutionContext,
|
|
385
|
+
): Promise<Response> {
|
|
322
386
|
const fetchHandler = options.fetch;
|
|
323
387
|
|
|
324
388
|
if (fetchHandler === undefined) {
|
|
325
389
|
return Promise.resolve(new Response("Not Found", { status: 404 }));
|
|
326
390
|
}
|
|
327
391
|
|
|
392
|
+
const ctx = requestContext ?? this.ctx;
|
|
328
393
|
const requestServices = Layer.mergeAll(
|
|
329
394
|
Layer.succeed(NativeRequest, request),
|
|
330
395
|
Layer.succeed(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromWeb(request)),
|
|
396
|
+
Layer.succeed(ExecutionContext, ctx),
|
|
397
|
+
Layer.succeed(WorkerContext, fromExecutionContext(ctx)),
|
|
331
398
|
);
|
|
332
399
|
|
|
333
400
|
return this[RunSymbol](
|
|
334
|
-
renderFetchSuccess(fetchHandler).pipe(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
>,
|
|
401
|
+
renderFetchSuccess(fetchHandler).pipe(
|
|
402
|
+
Effect.tap(() => scheduleTelemetryFlush),
|
|
403
|
+
Effect.provide(requestServices),
|
|
404
|
+
) as Effect.Effect<Response, unknown, RuntimeContext<ROut> | REvent | Scope.Scope>,
|
|
339
405
|
);
|
|
340
406
|
}
|
|
341
407
|
|
|
@@ -363,6 +429,21 @@ export function make<
|
|
|
363
429
|
return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut | REvent>>(EffectWorker);
|
|
364
430
|
}
|
|
365
431
|
|
|
432
|
+
interface FetchCapableInstance {
|
|
433
|
+
[FetchSymbol](request: Request, requestContext: globalThis.ExecutionContext): Promise<Response>;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Creates an `ExportedHandler`-compatible `{ fetch }` object backed by
|
|
438
|
+
* {@link make}.
|
|
439
|
+
*
|
|
440
|
+
* The Effect runtime is cached per `env` identity, so the user layer builds
|
|
441
|
+
* once per isolate instead of once per request. `ExecutionContext`-backed
|
|
442
|
+
* services (`Worker.ExecutionContext`, `Worker.WorkerContext`) are still
|
|
443
|
+
* provided fresh for every request. Layer finalizers do not run when the
|
|
444
|
+
* isolate is evicted; Cloudflare provides no shutdown hook. Use `eventLayer`
|
|
445
|
+
* for resources that must be finalized per event.
|
|
446
|
+
*/
|
|
366
447
|
export const makeFetchHandler = <
|
|
367
448
|
ROut,
|
|
368
449
|
LayerError,
|
|
@@ -374,136 +455,35 @@ export const makeFetchHandler = <
|
|
|
374
455
|
options: FetchWorkerOptions<ROut, REvent, EventLayerError>,
|
|
375
456
|
): FetchHandler<Env> => {
|
|
376
457
|
const WorkerClass = make(layer, options);
|
|
458
|
+
const instances = new WeakMap<WorkerEnv, FetchCapableInstance>();
|
|
377
459
|
|
|
378
460
|
return {
|
|
379
|
-
fetch: (request, env, ctx) =>
|
|
380
|
-
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
export type ServiceFreeSchema = S.Codec<any, any, never, never>;
|
|
384
|
-
|
|
385
|
-
export interface Method<
|
|
386
|
-
Args extends ReadonlyArray<ServiceFreeSchema> = ReadonlyArray<ServiceFreeSchema>,
|
|
387
|
-
Success extends ServiceFreeSchema = ServiceFreeSchema,
|
|
388
|
-
> {
|
|
389
|
-
readonly args: Args;
|
|
390
|
-
readonly success: Success;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
export namespace Method {
|
|
394
|
-
export type Any = Method<ReadonlyArray<ServiceFreeSchema>, ServiceFreeSchema>;
|
|
461
|
+
fetch: (request, env, ctx) => {
|
|
462
|
+
let instance = instances.get(env);
|
|
395
463
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
|
|
401
|
-
]
|
|
402
|
-
? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
|
|
403
|
-
: Array<S.Schema.Type<Args[number]>>;
|
|
404
|
-
|
|
405
|
-
export type Args<Self extends Any> = ArgsFromSchemas<Self["args"]>;
|
|
406
|
-
|
|
407
|
-
export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
export type Methods = Record<string, Method.Any>;
|
|
411
|
-
|
|
412
|
-
export type NoReservedMethods<MethodsShape extends Methods> =
|
|
413
|
-
Extract<keyof MethodsShape, ReservedMethodName> extends never ? MethodsShape : never;
|
|
414
|
-
|
|
415
|
-
export interface Definition<Id extends string = string, MethodsShape extends Methods = Methods> {
|
|
416
|
-
readonly id: Id;
|
|
417
|
-
readonly methods: MethodsShape;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
export namespace Definition {
|
|
421
|
-
export type Any = Definition<string, Methods>;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
export type ServerApi<Self extends Definition.Any> = {
|
|
425
|
-
readonly [Key in keyof Self["methods"]]: (
|
|
426
|
-
...args: Method.Args<Self["methods"][Key]>
|
|
427
|
-
) => Promise<Method.Success<Self["methods"][Key]>>;
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
export type Api<Self extends Definition.Any> = Rpc.Provider<ServerApi<Self>, ReservedMethodName>;
|
|
431
|
-
|
|
432
|
-
export type Handlers<ROut, Self extends Definition.Any> = {
|
|
433
|
-
readonly [Key in keyof Self["methods"]]: (
|
|
434
|
-
...args: Method.Args<Self["methods"][Key]>
|
|
435
|
-
) => WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
export interface Options<
|
|
439
|
-
ROut,
|
|
440
|
-
Self extends Definition.Any,
|
|
441
|
-
REvent = never,
|
|
442
|
-
EventLayerError = never,
|
|
443
|
-
> extends Omit<WorkerOptions<ROut, REvent, EventLayerError, Handlers<ROut | REvent, Self>>, "rpc"> {
|
|
444
|
-
readonly rpc: Handlers<ROut | REvent, Self>;
|
|
445
|
-
}
|
|
464
|
+
if (instance === undefined) {
|
|
465
|
+
instance = new WorkerClass(ctx, env) as unknown as FetchCapableInstance;
|
|
466
|
+
instances.set(env, instance);
|
|
467
|
+
}
|
|
446
468
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Context.ServiceClass<
|
|
450
|
-
Self,
|
|
451
|
-
Id,
|
|
452
|
-
ServiceBinding.ServiceBindingEffectClient<
|
|
453
|
-
Api<Definition<Id, MethodsShape>>,
|
|
454
|
-
Definition<Id, MethodsShape>
|
|
455
|
-
>
|
|
456
|
-
> &
|
|
457
|
-
ServiceBinding.ServiceBindingStaticClient<
|
|
458
|
-
Self,
|
|
459
|
-
Api<Definition<Id, MethodsShape>>,
|
|
460
|
-
Definition<Id, MethodsShape>
|
|
461
|
-
> & {
|
|
462
|
-
readonly id: Id;
|
|
463
|
-
readonly methods: MethodsShape;
|
|
464
|
-
readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
465
|
-
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
466
|
-
options: Options<ROut, Definition<Id, MethodsShape>, REvent, EventLayerError>,
|
|
467
|
-
) => WorkerClass<Handlers<ROut | REvent, Definition<Id, MethodsShape>>, ROut | REvent>;
|
|
468
|
-
readonly layer: (
|
|
469
|
-
options: LayerOptions,
|
|
470
|
-
) => Layer.Layer<
|
|
471
|
-
Self,
|
|
472
|
-
Binding.BindingNotFoundError | Binding.BindingValidationError,
|
|
473
|
-
WorkerEnvironment
|
|
474
|
-
>;
|
|
469
|
+
return instance[FetchSymbol](request, ctx);
|
|
470
|
+
},
|
|
475
471
|
};
|
|
476
|
-
|
|
477
|
-
export type TagFactory = <Self>() => <Id extends string, const MethodsShape extends Methods>(
|
|
478
|
-
id: Id,
|
|
479
|
-
methods: MethodsShape & NoReservedMethods<MethodsShape>,
|
|
480
|
-
) => TagClass<Self, Id, MethodsShape>;
|
|
481
|
-
|
|
482
|
-
export const Tag = WorkerDefinition.Tag as unknown as TagFactory;
|
|
483
|
-
|
|
484
|
-
export const method = WorkerDefinition.method as {
|
|
485
|
-
<Success extends ServiceFreeSchema>(definition: {
|
|
486
|
-
readonly success: Success;
|
|
487
|
-
}): Method<readonly [], Success>;
|
|
488
|
-
<
|
|
489
|
-
const Args extends ReadonlyArray<ServiceFreeSchema>,
|
|
490
|
-
Success extends ServiceFreeSchema,
|
|
491
|
-
>(definition: {
|
|
492
|
-
readonly args: Args;
|
|
493
|
-
readonly success: Success;
|
|
494
|
-
}): Method<Args, Success>;
|
|
495
472
|
};
|
|
496
473
|
|
|
497
|
-
export
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
474
|
+
export type {
|
|
475
|
+
Api,
|
|
476
|
+
Definition,
|
|
477
|
+
HandlerEffect,
|
|
478
|
+
Handlers,
|
|
479
|
+
LayerOptions,
|
|
480
|
+
Method,
|
|
481
|
+
Methods,
|
|
482
|
+
NoReservedMethods,
|
|
483
|
+
Options,
|
|
484
|
+
ServerApi,
|
|
485
|
+
ServiceFreeSchema,
|
|
486
|
+
TagClass,
|
|
487
|
+
} from "./WorkerDefinition";
|
|
488
|
+
|
|
489
|
+
export { implement, method, Tag } from "./WorkerDefinition";
|
package/src/WorkerDefinition.ts
CHANGED
|
@@ -37,15 +37,10 @@ export namespace Method {
|
|
|
37
37
|
? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
|
|
38
38
|
: Array<S.Schema.Type<Args[number]>>;
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
infer Head extends ServiceFreeSchema,
|
|
45
|
-
...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
|
|
46
|
-
]
|
|
47
|
-
? [S.Codec.Encoded<Head>, ...EncodedArgsFromSchemas<Tail>]
|
|
48
|
-
: Array<S.Codec.Encoded<Args[number]>>;
|
|
40
|
+
/** Method schemas cross the wire through their canonical JSON codec. */
|
|
41
|
+
type EncodedArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = {
|
|
42
|
+
[Index in keyof Args]: S.Json;
|
|
43
|
+
};
|
|
49
44
|
|
|
50
45
|
export type Args<Self extends Any> = ArgsFromSchemas<Self["args"]>;
|
|
51
46
|
|
|
@@ -53,7 +48,7 @@ export namespace Method {
|
|
|
53
48
|
|
|
54
49
|
export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
|
|
55
50
|
|
|
56
|
-
export type EncodedSuccess<Self extends Any> = S.
|
|
51
|
+
export type EncodedSuccess<Self extends Any> = S.Json;
|
|
57
52
|
}
|
|
58
53
|
|
|
59
54
|
export type Methods = Record<string, Method.Any>;
|
package/src/WorkersAi.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
|
|
15
15
|
import * as Binding from "./Binding";
|
|
16
16
|
import type { WorkerEnvironment } from "./Environment";
|
|
17
|
+
import * as ErrorMessage from "./internal/ErrorMessage";
|
|
17
18
|
|
|
18
19
|
const expectedWorkersAiBinding = "Workers AI binding with run(), gateway(), and models()";
|
|
19
20
|
|
|
@@ -22,7 +23,11 @@ export class WorkersAiOperationError extends Data.TaggedError("WorkersAiOperatio
|
|
|
22
23
|
readonly binding: string;
|
|
23
24
|
readonly operation: string;
|
|
24
25
|
readonly cause: unknown;
|
|
25
|
-
}> {
|
|
26
|
+
}> {
|
|
27
|
+
override get message(): string {
|
|
28
|
+
return `Workers AI ${this.operation} failed for binding "${this.binding}": ${ErrorMessage.causeMessage(this.cause)}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
26
31
|
|
|
27
32
|
/** Typed Workers AI binding definition. */
|
|
28
33
|
export interface WorkersAiDefinition {
|
|
@@ -101,7 +106,7 @@ export interface WorkersAiClient<ModelList extends AiModelListType = AiModels> {
|
|
|
101
106
|
readonly gateway: (
|
|
102
107
|
gatewayId: string,
|
|
103
108
|
) => Effect.Effect<CloudflareAiGateway, WorkersAiOperationError>;
|
|
104
|
-
readonly
|
|
109
|
+
readonly rawUnsafe: Effect.Effect<WorkersAiBinding<ModelList>>;
|
|
105
110
|
readonly definition: WorkersAiDefinition;
|
|
106
111
|
}
|
|
107
112
|
|
|
@@ -177,10 +182,12 @@ export const embeddingResponse = (value: {
|
|
|
177
182
|
export const makeClient =
|
|
178
183
|
<ModelList extends AiModelListType = AiModels>(definition: WorkersAiDefinition) =>
|
|
179
184
|
(ai: WorkersAiBinding<ModelList>): WorkersAiClient<ModelList> => {
|
|
180
|
-
const run = ((
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
const run = Effect.fn("WorkersAi.run")(
|
|
186
|
+
(model: string, input: Record<string, unknown>, options?: WorkersAiOptions) =>
|
|
187
|
+
tryWorkersAiPromise(definition.binding, "run", () =>
|
|
188
|
+
ai.run(model as string & {}, input, options),
|
|
189
|
+
),
|
|
190
|
+
) as WorkersAiClient<ModelList>["run"];
|
|
184
191
|
|
|
185
192
|
return {
|
|
186
193
|
definition,
|
|
@@ -190,22 +197,25 @@ export const makeClient =
|
|
|
190
197
|
() => ai.aiGatewayLogId,
|
|
191
198
|
),
|
|
192
199
|
run,
|
|
193
|
-
runEmbedding: (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
200
|
+
runEmbedding: Effect.fn("WorkersAi.runEmbedding")(
|
|
201
|
+
(model: string, input: WorkersAiEmbeddingInput, options?: WorkersAiOptions) =>
|
|
202
|
+
run(model, input as Record<string, unknown>, options).pipe(
|
|
203
|
+
Effect.map((response) =>
|
|
204
|
+
embeddingResponse(
|
|
205
|
+
response as {
|
|
206
|
+
readonly data?: ReadonlyArray<ReadonlyArray<number>>;
|
|
207
|
+
readonly shape?: ReadonlyArray<number>;
|
|
208
|
+
},
|
|
209
|
+
),
|
|
201
210
|
),
|
|
202
211
|
),
|
|
203
|
-
|
|
204
|
-
models: (params) =>
|
|
212
|
+
),
|
|
213
|
+
models: Effect.fn("WorkersAi.models")((params?: WorkersAiModelsSearchParams) =>
|
|
205
214
|
tryWorkersAiPromise(definition.binding, "models", () => ai.models(params)),
|
|
215
|
+
),
|
|
206
216
|
gateway: (gatewayId) =>
|
|
207
217
|
tryWorkersAiSync(definition.binding, "gateway", () => ai.gateway(gatewayId)),
|
|
208
|
-
|
|
218
|
+
rawUnsafe: Effect.succeed(ai),
|
|
209
219
|
};
|
|
210
220
|
};
|
|
211
221
|
|