effect-cf 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -8,6 +8,12 @@ Cloudflare services should be described as `Context`, `Layer`, and `Effect` valu
8
8
 
9
9
  The low-level Worker entrypoint factory is exported as `Worker.make`. It returns a `WorkerEntrypoint` class with a `fetch` handler and optional typed RPC methods. Durable Object support starts with `DurableObject.make`, which routes Durable Object lifecycle methods and RPC handlers through one managed runtime for each Durable Object instance.
10
10
 
11
+ Worker fetch handlers can return either web `Response` values or Effect `HttpServerResponse` values. `Worker.make` renders `HttpServerResponse` back to a web `Response` at the Cloudflare boundary, so application handlers can stay in Effect HTTP primitives.
12
+
13
+ Request-scoped app services belong in the implementation layer that owns request semantics, such as `HttpRouter` middleware, RPC middleware, or explicit `Effect.provide(...)` inside a custom fetch effect. `Worker.make` stays focused on Cloudflare boundary services: `NativeRequest`, `HttpServerRequest`, `WorkerEnvironment`, `ExecutionContext`, and `WorkerContext`.
14
+
15
+ For modules that need an `ExportedHandler` object instead of a `WorkerEntrypoint` class, use `Worker.makeFetchHandler`. This helper is fetch-only and intentionally does not accept Worker RPC methods.
16
+
11
17
  RPC contracts can be described as classes with `Worker.Tag` and `DurableObject.Tag`. Contract classes expose `.make(...)` for runtime entrypoint creation and `.Binding(...)` / `.Namespace(...)` for typed bindings. Bindings infer call types from the same code-owned contract classes. Definition-backed calls encode/decode arguments and success values with their Effect schemas at Worker and Durable Object boundaries.
12
18
 
13
19
  Binding helpers expose two RPC paths:
@@ -31,6 +37,7 @@ Client APIs use Cloudflare-style provider types, so object results expose pipeli
31
37
  - `Rpc` - Cloudflare RPC type helpers, `RpcTarget` / `RpcStub` re-exports, and scoped disposal utilities
32
38
  - `ServiceBinding` - typed Worker-to-Worker service binding helper
33
39
  - `WorkerEnvironment` - request environment service
40
+ - `WorkerConfig` - Effect `Config` helpers backed by Cloudflare worker `env`, including `layerWith` for custom env-to-config mapping
34
41
 
35
42
  ## RPC Values
36
43
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Cause, Config, ConfigProvider, Context, Effect, Layer, Option, Schema, Scope } from "effect";
2
2
  import { DurableObject, RpcStub, RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
3
- import { HttpServerRequest } from "effect/unstable/http";
3
+ import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
4
4
  import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
5
5
  import * as RpcServer from "effect/unstable/rpc/RpcServer";
6
6
  import * as _$effect_Types0 from "effect/Types";
@@ -63,6 +63,13 @@ declare namespace WorkerConfig {
63
63
  const integer: <const Name extends Key>(name: Name) => Config.Config<number>;
64
64
  /** Read a scalar Cloudflare var or secret as a boolean. */
65
65
  const boolean: <const Name extends Key>(name: Name) => Config.Config<boolean>;
66
+ /** Build a `ConfigProvider` from a Cloudflare worker `env` object. */
67
+ const providerFromEnv: (env: WorkerEnv) => ConfigProvider.ConfigProvider;
68
+ /**
69
+ * Build a `ConfigProvider` from the current `WorkerEnvironment` with a custom
70
+ * conversion function.
71
+ */
72
+ const providerWith: (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) => Effect.Effect<ConfigProvider.ConfigProvider, never, WorkerEnvironment>;
66
73
  /** Build a `ConfigProvider` from the current `WorkerEnvironment`. */
67
74
  const provider: Effect.Effect<ConfigProvider.ConfigProvider, never, WorkerEnvironment>;
68
75
  /**
@@ -72,6 +79,8 @@ declare namespace WorkerConfig {
72
79
  const providerLayer: _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
73
80
  /** Alias for `providerLayer` for concise use in worker layers. */
74
81
  const layer: _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
82
+ /** Build a `ConfigProvider` layer with a custom `env` conversion function. */
83
+ const layerWith: (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) => _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
75
84
  }
76
85
  declare namespace Binding_d_exports {
77
86
  export { BindingNotFoundError, BindingValidationError, Service$3 as Service, TypeId$2 as TypeId };
@@ -985,7 +994,7 @@ declare const Service$1: <Self, Api extends object = never>() => <Id extends str
985
994
  readonly scopedCall: <Method extends ServiceMethodKey<ApiOrDefinition<Api, Definition>>>(method: Method, ...args: ServiceMethodArgs<ApiOrDefinition<Api, Definition>, Method>) => Effect.Effect<Awaited<Result<AsyncMethodSuccess<ApiOrDefinition<Api, Definition>, Method>>>, unknown, Self | _$effect_Scope0.Scope>;
986
995
  };
987
996
  declare namespace Worker_d_exports {
988
- export { Api$1 as Api, Definition$1 as Definition, ExecutionContext, HandlerEffect$1 as HandlerEffect, Handlers$1 as Handlers, NativeRequest, Options$1 as Options, ReservedMethodName, RpcHandlers$1 as RpcHandlers, ServerApi$2 as ServerApi, Tag$1 as Tag, WorkerClass, WorkerContext, WorkerContextService, WorkerContextWaitUntilOptions, WorkerHandler, WorkerOptions, WorkerRpc, WorkerRpcHandler, WorkerRpcShape, implement$1 as implement, isWebSocketUpgrade, make$2 as make, method$1 as method };
997
+ export { Api$1 as Api, Definition$1 as Definition, ExecutionContext, FetchHandler, FetchWorkerOptions, HandlerEffect$1 as HandlerEffect, Handlers$1 as Handlers, NativeRequest, Options$1 as Options, ReservedMethodName, RpcHandlers$1 as RpcHandlers, ServerApi$2 as ServerApi, Tag$1 as Tag, WorkerClass, WorkerContext, WorkerContextService, WorkerContextWaitUntilOptions, WorkerFetchSuccess, WorkerHandler, WorkerOptions, WorkerRpc, WorkerRpcHandler, WorkerRpcShape, implement$1 as implement, isWebSocketUpgrade, make$2 as make, makeFetchHandler, method$1 as method, renderHttpResponse };
989
998
  }
990
999
  declare const ExecutionContext_base: Context.ServiceClass<ExecutionContext, "effect-cf/ExecutionContext", globalThis.ExecutionContext<unknown>>;
991
1000
  declare class ExecutionContext extends ExecutionContext_base {}
@@ -1006,19 +1015,28 @@ type ReservedMethodName = ReservedMethodName$5 | "fetch" | "connect" | "alarm" |
1006
1015
  type WorkerBaseContext<ROut> = ExecutionContext | WorkerContext | WorkerEnvironment | ROut;
1007
1016
  type WorkerFetchContext<ROut> = WorkerBaseContext<ROut> | NativeRequest | HttpServerRequest.HttpServerRequest | Scope.Scope;
1008
1017
  type WorkerRpcContext<ROut> = WorkerBaseContext<ROut> | Scope.Scope;
1009
- type WorkerHandler<ROut, A = unknown> = Effect.Effect<A, unknown, WorkerFetchContext<ROut>>;
1018
+ type WorkerFetchSuccess = Response | HttpServerResponse.HttpServerResponse;
1019
+ type WorkerHandler<ROut, A = WorkerFetchSuccess> = Effect.Effect<A, unknown, WorkerFetchContext<ROut>>;
1010
1020
  type WorkerRpcHandler<ROut, A = unknown> = Effect.Effect<A, unknown, WorkerRpcContext<ROut>>;
1011
1021
  type WorkerRpc<ROut> = Record<string, (...args: Array<any>) => WorkerRpcHandler<ROut>>;
1012
1022
  type WorkerRpcShape<Rpc extends WorkerRpc<ROut>, ROut> = { readonly [Key in keyof Rpc]: Rpc[Key] extends ((...args: infer Args) => Effect.Effect<infer A, unknown, WorkerRpcContext<ROut>>) ? (...args: Args) => Promise<A> : never };
1013
1023
  type RpcHandlers$1<ROut, Api> = { readonly [Key in keyof Api as Key extends keyof WorkerEntrypoint ? never : Key extends string ? Key extends ReservedMethodName ? never : [Api[Key]] extends [never] ? never : Api[Key] extends ((...args: Array<any>) => Promise<unknown>) ? Key : never : never]: Api[Key] extends ((...args: infer Args) => Promise<infer A>) ? (...args: Args) => WorkerRpcHandler<ROut, A> : never };
1014
1024
  interface WorkerOptions<ROut, Rpc extends WorkerRpc<ROut>> {
1015
- readonly fetch?: Effect.Effect<Response, unknown, WorkerFetchContext<ROut>>;
1025
+ readonly fetch?: Effect.Effect<WorkerFetchSuccess, unknown, WorkerFetchContext<ROut>>;
1016
1026
  readonly rpc?: Rpc;
1017
1027
  }
1028
+ type FetchWorkerOptions<ROut> = Omit<WorkerOptions<ROut, Record<never, never>>, "rpc"> & {
1029
+ readonly rpc?: never;
1030
+ };
1018
1031
  type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (ctx: globalThis.ExecutionContext, env: WorkerEnv) => WorkerEntrypoint<WorkerEnv> & {
1019
1032
  fetch(request: Request): Promise<Response>;
1020
1033
  } & WorkerRpcShape<Rpc, ROut>;
1034
+ interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
1035
+ readonly fetch: (request: Request, env: Env, ctx: globalThis.ExecutionContext) => Promise<Response>;
1036
+ }
1037
+ declare const renderHttpResponse: <A extends HttpServerResponse.HttpServerResponse, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<Response, E, R>;
1021
1038
  declare const make$2: <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Record<never, never>>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: WorkerOptions<ROut, Rpc>) => WorkerClass<Rpc, ROut>;
1039
+ declare const makeFetchHandler: <ROut, LayerError, Env extends WorkerEnv = WorkerEnv>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: FetchWorkerOptions<ROut>) => FetchHandler<Env>;
1022
1040
  declare const Tag$1: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$2>) => (abstract new () => object) & (Definition$2<Id, MethodsShape> & {
1023
1041
  make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape>>) => WorkerClass<{ readonly [Key in keyof MethodsShape]: (...args: Array<unknown>) => WorkerRpcHandler<ROut, _$effect_Schema0.Codec.Encoded<MethodsShape[Key]["success"]>> }, ROut>;
1024
1042
  Binding: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<ServiceBindingDefinition<Definition$2<Id, MethodsShape>>, "definition">) => Context.ServiceClass<Self, BindingId, ServiceBindingClient<[Api$2<Definition$2<Id, MethodsShape>>] extends [never] ? Definition$2<Id, MethodsShape> extends infer T ? T extends Definition$2<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$3<T> : never : never : never : Api$2<Definition$2<Id, MethodsShape>>>> & {
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
2
2
  import { Cause, Config, ConfigProvider, Context, Data, Effect, Exit, Layer, ManagedRuntime, Option, Queue, Schema } from "effect";
3
3
  import { DurableObject, RpcStub, RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
4
- import { HttpServerRequest } from "effect/unstable/http";
4
+ import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
5
5
  import * as RpcMessage from "effect/unstable/rpc/RpcMessage";
6
6
  import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
7
7
  import * as RpcServer from "effect/unstable/rpc/RpcServer";
@@ -17,8 +17,11 @@ let WorkerConfig;
17
17
  _WorkerConfig.number = (name) => Config.number(name);
18
18
  _WorkerConfig.integer = (name) => Config.int(name);
19
19
  _WorkerConfig.boolean = (name) => Config.boolean(name);
20
- const provider = _WorkerConfig.provider = Effect.map(WorkerEnvironment.asEffect(), ConfigProvider.fromUnknown);
20
+ const providerFromEnv = _WorkerConfig.providerFromEnv = (env) => ConfigProvider.fromUnknown(env);
21
+ const providerWith = _WorkerConfig.providerWith = (makeProvider) => Effect.map(WorkerEnvironment.asEffect(), makeProvider);
22
+ const provider = _WorkerConfig.provider = providerWith(providerFromEnv);
21
23
  _WorkerConfig.layer = _WorkerConfig.providerLayer = ConfigProvider.layer(provider);
24
+ _WorkerConfig.layerWith = (makeProvider) => ConfigProvider.layer(providerWith(makeProvider));
22
25
  })(WorkerConfig || (WorkerConfig = {}));
23
26
  //#endregion
24
27
  //#region src/Binding.ts
@@ -430,7 +433,9 @@ var Worker_exports = /* @__PURE__ */ __exportAll({
430
433
  implement: () => implement$2,
431
434
  isWebSocketUpgrade: () => isWebSocketUpgrade,
432
435
  make: () => make$3,
433
- method: () => method$2
436
+ makeFetchHandler: () => makeFetchHandler,
437
+ method: () => method$2,
438
+ renderHttpResponse: () => renderHttpResponse
434
439
  });
435
440
  var ExecutionContext = class extends Context.Service()("effect-cf/ExecutionContext") {};
436
441
  var WorkerContext = class extends Context.Service()("effect-cf/WorkerContext") {};
@@ -460,13 +465,15 @@ const reservedMethodNames$2 = new Set([
460
465
  "webSocketError"
461
466
  ]);
462
467
  const RunSymbol$1 = Symbol.for("effect-cf/Worker/run");
468
+ const renderHttpResponse = (effect) => Effect.flatMap(effect, (response) => Effect.map(Effect.context(), (context) => HttpServerResponse.toWeb(response, { context })));
469
+ const renderFetchSuccess = (effect) => Effect.flatMap(effect, (response) => response instanceof Response ? Effect.succeed(response) : Effect.map(Effect.context(), (context) => HttpServerResponse.toWeb(response, { context })));
463
470
  const make$3 = (layer, options) => {
464
471
  class EffectWorker extends WorkerEntrypoint {
465
472
  runtime;
466
473
  constructor(ctx, env) {
467
474
  super(ctx, env);
468
475
  let runWaitUntilEffect = () => Promise.resolve(Exit.die(/* @__PURE__ */ new Error("WorkerContext runtime is not initialized")));
469
- const runtimeLayer = provideEntrypointServices(layer, Layer.mergeAll(Layer.succeed(ExecutionContext, ctx), Layer.succeed(WorkerContext, fromExecutionContext(ctx, (effect) => runWaitUntilEffect(effect))), Layer.succeed(WorkerEnvironment, env)));
476
+ const runtimeLayer = provideEntrypointServices(layer, Layer.mergeAll(Layer.succeed(ExecutionContext, ctx), ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))), Layer.succeed(WorkerContext, fromExecutionContext(ctx, (effect) => runWaitUntilEffect(effect))), Layer.succeed(WorkerEnvironment, env)));
470
477
  this.runtime = ManagedRuntime.make(runtimeLayer);
471
478
  runWaitUntilEffect = (effect) => this.runtime.runPromiseExit(effect);
472
479
  }
@@ -476,12 +483,17 @@ const make$3 = (layer, options) => {
476
483
  fetch(request) {
477
484
  const fetchHandler = options.fetch;
478
485
  if (fetchHandler === void 0) return Promise.resolve(new Response("Not Found", { status: 404 }));
479
- return this[RunSymbol$1](fetchHandler.pipe(Effect.provideService(NativeRequest, request), Effect.provideService(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromWeb(request))));
486
+ const requestServices = Layer.mergeAll(Layer.succeed(NativeRequest, request), Layer.succeed(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromWeb(request)));
487
+ return this[RunSymbol$1](renderFetchSuccess(fetchHandler).pipe(Effect.provide(requestServices)));
480
488
  }
481
489
  }
482
490
  defineEntrypointRpcMethods("Worker", EffectWorker.prototype, options.rpc, reservedMethodNames$2, (self, effect) => self[RunSymbol$1](effect));
483
491
  return assumeEntrypointClass(EffectWorker);
484
492
  };
493
+ const makeFetchHandler = (layer, options) => {
494
+ const WorkerClass = make$3(layer, options);
495
+ return { fetch: (request, env, ctx) => Promise.resolve(new WorkerClass(ctx, env).fetch(request)) };
496
+ };
485
497
  const Tag$2 = Tag$3;
486
498
  const method$2 = method$3;
487
499
  const implement$2 = implement$3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-cf",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -72,8 +72,18 @@ export namespace WorkerConfig {
72
72
  /** Read a scalar Cloudflare var or secret as a boolean. */
73
73
  export const boolean = <const Name extends Key>(name: Name) => Config.boolean(name);
74
74
 
75
+ /** Build a `ConfigProvider` from a Cloudflare worker `env` object. */
76
+ export const providerFromEnv = (env: WorkerEnv) => ConfigProvider.fromUnknown(env);
77
+
78
+ /**
79
+ * Build a `ConfigProvider` from the current `WorkerEnvironment` with a custom
80
+ * conversion function.
81
+ */
82
+ export const providerWith = (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) =>
83
+ Effect.map(WorkerEnvironment.asEffect(), makeProvider);
84
+
75
85
  /** Build a `ConfigProvider` from the current `WorkerEnvironment`. */
76
- export const provider = Effect.map(WorkerEnvironment.asEffect(), ConfigProvider.fromUnknown);
86
+ export const provider = providerWith(providerFromEnv);
77
87
 
78
88
  /**
79
89
  * Replace the active Effect `ConfigProvider` with one backed by the current
@@ -83,4 +93,8 @@ export namespace WorkerConfig {
83
93
 
84
94
  /** Alias for `providerLayer` for concise use in worker layers. */
85
95
  export const layer = providerLayer;
96
+
97
+ /** Build a `ConfigProvider` layer with a custom `env` conversion function. */
98
+ export const layerWith = (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) =>
99
+ ConfigProvider.layer(providerWith(makeProvider));
86
100
  }
package/src/Worker.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
2
- import { Cause, Context, Effect, Exit, Layer, ManagedRuntime, type Scope } from "effect";
3
- import { HttpServerRequest } from "effect/unstable/http";
4
-
5
- import { WorkerEnvironment, type WorkerEnv } from "./Environment";
2
+ import {
3
+ Cause,
4
+ ConfigProvider,
5
+ Context,
6
+ Effect,
7
+ Exit,
8
+ Layer,
9
+ ManagedRuntime,
10
+ type Scope,
11
+ } from "effect";
12
+ import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
13
+
14
+ import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
6
15
  import type * as RpcDefinition from "./RpcDefinition";
7
16
  import * as WorkerDefinition from "./WorkerDefinition";
8
17
  import * as Entrypoint from "./internal/Entrypoint";
@@ -122,7 +131,13 @@ type RuntimeContext<ROut> = WorkerBaseContext<ROut>;
122
131
 
123
132
  const RunSymbol = Symbol.for("effect-cf/Worker/run");
124
133
 
125
- export type WorkerHandler<ROut, A = unknown> = Effect.Effect<A, unknown, WorkerFetchContext<ROut>>;
134
+ export type WorkerFetchSuccess = Response | HttpServerResponse.HttpServerResponse;
135
+
136
+ export type WorkerHandler<ROut, A = WorkerFetchSuccess> = Effect.Effect<
137
+ A,
138
+ unknown,
139
+ WorkerFetchContext<ROut>
140
+ >;
126
141
 
127
142
  export type WorkerRpcHandler<ROut, A = unknown> = Effect.Effect<A, unknown, WorkerRpcContext<ROut>>;
128
143
 
@@ -153,10 +168,14 @@ export type RpcHandlers<ROut, Api> = {
153
168
  };
154
169
 
155
170
  export interface WorkerOptions<ROut, Rpc extends WorkerRpc<ROut>> {
156
- readonly fetch?: Effect.Effect<Response, unknown, WorkerFetchContext<ROut>>;
171
+ readonly fetch?: Effect.Effect<WorkerFetchSuccess, unknown, WorkerFetchContext<ROut>>;
157
172
  readonly rpc?: Rpc;
158
173
  }
159
174
 
175
+ export type FetchWorkerOptions<ROut> = Omit<WorkerOptions<ROut, Record<never, never>>, "rpc"> & {
176
+ readonly rpc?: never;
177
+ };
178
+
160
179
  export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
161
180
  ctx: globalThis.ExecutionContext,
162
181
  env: WorkerEnv,
@@ -164,6 +183,34 @@ export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
164
183
  fetch(request: Request): Promise<Response>;
165
184
  } & WorkerRpcShape<Rpc, ROut>;
166
185
 
186
+ export interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
187
+ readonly fetch: (
188
+ request: Request,
189
+ env: Env,
190
+ ctx: globalThis.ExecutionContext,
191
+ ) => Promise<Response>;
192
+ }
193
+
194
+ export const renderHttpResponse = <A extends HttpServerResponse.HttpServerResponse, E, R>(
195
+ effect: Effect.Effect<A, E, R>,
196
+ ): Effect.Effect<Response, E, R> =>
197
+ Effect.flatMap(effect, (response) =>
198
+ Effect.map(Effect.context<never>(), (context) =>
199
+ HttpServerResponse.toWeb(response, { context }),
200
+ ),
201
+ );
202
+
203
+ const renderFetchSuccess = <E, R>(
204
+ effect: Effect.Effect<WorkerFetchSuccess, E, R>,
205
+ ): Effect.Effect<Response, E, R> =>
206
+ Effect.flatMap(effect, (response) =>
207
+ response instanceof Response
208
+ ? Effect.succeed(response)
209
+ : Effect.map(Effect.context<never>(), (context) =>
210
+ HttpServerResponse.toWeb(response, { context }),
211
+ ),
212
+ );
213
+
167
214
  export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Record<never, never>>(
168
215
  layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
169
216
  options: WorkerOptions<ROut, Rpc>,
@@ -179,14 +226,19 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
179
226
 
180
227
  const services = Layer.mergeAll(
181
228
  Layer.succeed(ExecutionContext, ctx),
229
+ ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))),
182
230
  Layer.succeed(
183
231
  WorkerContext,
184
232
  fromExecutionContext(ctx, (effect) => runWaitUntilEffect(effect)),
185
233
  ),
186
234
  Layer.succeed(WorkerEnvironment, env),
187
- );
235
+ ) as Layer.Layer<ExecutionContext | WorkerContext | WorkerEnvironment, never, never>;
188
236
 
189
- const runtimeLayer = Entrypoint.provideEntrypointServices(layer, services);
237
+ const runtimeLayer = Entrypoint.provideEntrypointServices<
238
+ ROut,
239
+ LayerError,
240
+ ExecutionContext | WorkerContext | WorkerEnvironment
241
+ >(layer, services);
190
242
 
191
243
  this.runtime = ManagedRuntime.make(runtimeLayer);
192
244
  runWaitUntilEffect = <A, E>(effect: Effect.Effect<A, E, never>) =>
@@ -204,14 +256,17 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
204
256
  return Promise.resolve(new Response("Not Found", { status: 404 }));
205
257
  }
206
258
 
259
+ const requestServices = Layer.mergeAll(
260
+ Layer.succeed(NativeRequest, request),
261
+ Layer.succeed(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromWeb(request)),
262
+ );
263
+
207
264
  return this[RunSymbol](
208
- fetchHandler.pipe(
209
- Effect.provideService(NativeRequest, request),
210
- Effect.provideService(
211
- HttpServerRequest.HttpServerRequest,
212
- HttpServerRequest.fromWeb(request),
213
- ),
214
- ),
265
+ renderFetchSuccess(fetchHandler).pipe(Effect.provide(requestServices)) as Effect.Effect<
266
+ Response,
267
+ unknown,
268
+ RuntimeContext<ROut> | Scope.Scope
269
+ >,
215
270
  );
216
271
  }
217
272
  }
@@ -227,6 +282,17 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
227
282
  return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut>>(EffectWorker);
228
283
  };
229
284
 
285
+ export const makeFetchHandler = <ROut, LayerError, Env extends WorkerEnv = WorkerEnv>(
286
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
287
+ options: FetchWorkerOptions<ROut>,
288
+ ): FetchHandler<Env> => {
289
+ const WorkerClass = make(layer, options);
290
+
291
+ return {
292
+ fetch: (request, env, ctx) => Promise.resolve(new WorkerClass(ctx, env).fetch(request)),
293
+ };
294
+ };
295
+
230
296
  export const Tag = WorkerDefinition.Tag;
231
297
 
232
298
  export const method = WorkerDefinition.method;