effect-cf 0.0.2 → 0.0.3
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 +7 -0
- package/dist/index.d.mts +37 -12
- package/dist/index.mjs +19 -5
- package/package.json +1 -1
- package/src/Environment.ts +15 -1
- package/src/Worker.ts +121 -21
- package/src/WorkerDefinition.ts +9 -4
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
|
+
Use `requestLayer` on `Worker.make` for per-request services such as auth/session context, tenant metadata, request IDs, or request-local clients. The request layer scope is the handler evaluation scope: it closes when the handler produces a `Response`, not when a streaming response body is later consumed by the platform.
|
|
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 };
|
|
@@ -798,7 +807,7 @@ type BoundaryHandlers<ROut, Self extends Definition$2.Any> = { readonly [Key in
|
|
|
798
807
|
/**
|
|
799
808
|
* Worker constructor options for a specific RPC definition.
|
|
800
809
|
*/
|
|
801
|
-
interface Options$2<ROut, Self extends Definition$2.Any> extends Omit<WorkerOptions<ROut, Handlers$2<ROut, Self
|
|
810
|
+
interface Options$2<ROut, Self extends Definition$2.Any, RRequest = never, RequestLayerError = never> extends Omit<WorkerOptions<ROut, Handlers$2<ROut, Self>, RRequest, RequestLayerError>, "rpc"> {
|
|
802
811
|
readonly rpc: Handlers$2<ROut, Self>;
|
|
803
812
|
}
|
|
804
813
|
/**
|
|
@@ -806,7 +815,7 @@ interface Options$2<ROut, Self extends Definition$2.Any> extends Omit<WorkerOpti
|
|
|
806
815
|
*/
|
|
807
816
|
declare const method$2: typeof method$4;
|
|
808
817
|
declare const make$3: <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$2>) => Definition$2<Id, MethodsShape> & {
|
|
809
|
-
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape
|
|
818
|
+
make: <ROut, LayerError, RRequest = never, RequestLayerError = never>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape>, RRequest, RequestLayerError>) => WorkerClass<BoundaryHandlers<ROut, Definition$2<Id, MethodsShape>>, ROut>;
|
|
810
819
|
Binding: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<ServiceBindingDefinition<Definition$2<Id, MethodsShape>>, "definition">) => _$effect_Context0.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>>>> & {
|
|
811
820
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
812
821
|
binding: string;
|
|
@@ -833,7 +842,7 @@ declare const make$3: <Id extends string, const MethodsShape extends Methods>(id
|
|
|
833
842
|
};
|
|
834
843
|
};
|
|
835
844
|
declare const Tag$2: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$2>) => (abstract new () => object) & (Definition$2<Id, MethodsShape> & {
|
|
836
|
-
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape
|
|
845
|
+
make: <ROut, LayerError, RRequest = never, RequestLayerError = never>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape>, RRequest, RequestLayerError>) => WorkerClass<BoundaryHandlers<ROut, Definition$2<Id, MethodsShape>>, ROut>;
|
|
837
846
|
Binding: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<ServiceBindingDefinition<Definition$2<Id, MethodsShape>>, "definition">) => _$effect_Context0.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>>>> & {
|
|
838
847
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
839
848
|
binding: string;
|
|
@@ -860,7 +869,7 @@ declare const Tag$2: <_Self>() => <Id extends string, const MethodsShape extends
|
|
|
860
869
|
};
|
|
861
870
|
});
|
|
862
871
|
declare const Worker: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$2>) => (abstract new () => object) & (Definition$2<Id, MethodsShape> & {
|
|
863
|
-
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape
|
|
872
|
+
make: <ROut, LayerError, RRequest = never, RequestLayerError = never>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape>, RRequest, RequestLayerError>) => WorkerClass<BoundaryHandlers<ROut, Definition$2<Id, MethodsShape>>, ROut>;
|
|
864
873
|
Binding: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<ServiceBindingDefinition<Definition$2<Id, MethodsShape>>, "definition">) => _$effect_Context0.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>>>> & {
|
|
865
874
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
866
875
|
binding: string;
|
|
@@ -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,21 +1015,37 @@ 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
|
|
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
|
-
interface WorkerOptions<ROut, Rpc extends WorkerRpc<ROut
|
|
1015
|
-
readonly fetch?: Effect.Effect<
|
|
1024
|
+
interface WorkerOptions<ROut, Rpc extends WorkerRpc<ROut>, RRequest = never, RequestLayerError = never> {
|
|
1025
|
+
readonly fetch?: Effect.Effect<WorkerFetchSuccess, unknown, WorkerFetchContext<ROut | RRequest>>;
|
|
1026
|
+
/**
|
|
1027
|
+
* Per-request services provided while evaluating the fetch handler.
|
|
1028
|
+
*
|
|
1029
|
+
* This scope closes when the handler produces a `Response`; it is not kept
|
|
1030
|
+
* open until a streaming response body is consumed by the platform.
|
|
1031
|
+
*/
|
|
1032
|
+
readonly requestLayer?: Layer.Layer<RRequest, RequestLayerError, WorkerFetchContext<ROut>>;
|
|
1016
1033
|
readonly rpc?: Rpc;
|
|
1017
1034
|
}
|
|
1035
|
+
type FetchWorkerOptions<ROut, RRequest = never, RequestLayerError = never> = Omit<WorkerOptions<ROut, Record<never, never>, RRequest, RequestLayerError>, "rpc"> & {
|
|
1036
|
+
readonly rpc?: never;
|
|
1037
|
+
};
|
|
1018
1038
|
type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (ctx: globalThis.ExecutionContext, env: WorkerEnv) => WorkerEntrypoint<WorkerEnv> & {
|
|
1019
1039
|
fetch(request: Request): Promise<Response>;
|
|
1020
1040
|
} & WorkerRpcShape<Rpc, ROut>;
|
|
1021
|
-
|
|
1041
|
+
interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
|
|
1042
|
+
readonly fetch: (request: Request, env: Env, ctx: globalThis.ExecutionContext) => Promise<Response>;
|
|
1043
|
+
}
|
|
1044
|
+
declare const renderHttpResponse: <A extends HttpServerResponse.HttpServerResponse, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<Response, E, R>;
|
|
1045
|
+
declare const make$2: <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Record<never, never>, RRequest = never, RequestLayerError = never>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: WorkerOptions<ROut, Rpc, RRequest, RequestLayerError>) => WorkerClass<Rpc, ROut>;
|
|
1046
|
+
declare const makeFetchHandler: <ROut, LayerError, RRequest = never, RequestLayerError = never, Env extends WorkerEnv = WorkerEnv>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: FetchWorkerOptions<ROut, RRequest, RequestLayerError>) => FetchHandler<Env>;
|
|
1022
1047
|
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
|
-
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape
|
|
1048
|
+
make: <ROut, LayerError, RRequest = never, RequestLayerError = never>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape>, RRequest, RequestLayerError>) => WorkerClass<{ readonly [Key in keyof MethodsShape]: (...args: Array<unknown>) => WorkerRpcHandler<ROut, _$effect_Schema0.Codec.Encoded<MethodsShape[Key]["success"]>> }, ROut>;
|
|
1024
1049
|
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>>>> & {
|
|
1025
1050
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
1026
1051
|
binding: string;
|
|
@@ -1055,7 +1080,7 @@ declare namespace Definition$1 {
|
|
|
1055
1080
|
type ServerApi$2<Self extends Definition$2.Any> = ServerApi$3<Self>;
|
|
1056
1081
|
type Api$1<Self extends Definition$2.Any> = Api$2<Self>;
|
|
1057
1082
|
type Handlers$1<ROut, Self extends Definition$2.Any> = Handlers$2<ROut, Self>;
|
|
1058
|
-
type Options$1<ROut, Self extends Definition$2.Any> = Options$2<ROut, Self>;
|
|
1083
|
+
type Options$1<ROut, Self extends Definition$2.Any, RRequest = never, RequestLayerError = never> = Options$2<ROut, Self, RRequest, RequestLayerError>;
|
|
1059
1084
|
type HandlerEffect$1<ROut, Self extends Definition$2.Any, Key extends keyof Self["methods"]> = HandlerEffect$2<ROut, Self, Key>;
|
|
1060
1085
|
declare namespace DurableObject_d_exports {
|
|
1061
1086
|
export { Api, Definition, DurableObjectClass, DurableObjectHandler, DurableObjectOptions, DurableObjectRpc, DurableObjectRpcShape, HandlerEffect, Handlers, Options, RpcHandlers, ServerApi$1 as ServerApi, Tag, implement, make$1 as make, method };
|
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
|
|
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
|
-
|
|
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,19 @@ 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
|
-
|
|
486
|
+
const requestServices = Layer.mergeAll(Layer.succeed(NativeRequest, request), Layer.succeed(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromWeb(request)));
|
|
487
|
+
const fetchEffect = renderFetchSuccess(fetchHandler);
|
|
488
|
+
const requestEffect = options.requestLayer === void 0 ? fetchEffect : fetchEffect.pipe(Effect.provide(options.requestLayer));
|
|
489
|
+
return this[RunSymbol$1](requestEffect.pipe(Effect.provide(requestServices)));
|
|
480
490
|
}
|
|
481
491
|
}
|
|
482
492
|
defineEntrypointRpcMethods("Worker", EffectWorker.prototype, options.rpc, reservedMethodNames$2, (self, effect) => self[RunSymbol$1](effect));
|
|
483
493
|
return assumeEntrypointClass(EffectWorker);
|
|
484
494
|
};
|
|
495
|
+
const makeFetchHandler = (layer, options) => {
|
|
496
|
+
const WorkerClass = make$3(layer, options);
|
|
497
|
+
return { fetch: (request, env, ctx) => Promise.resolve(new WorkerClass(ctx, env).fetch(request)) };
|
|
498
|
+
};
|
|
485
499
|
const Tag$2 = Tag$3;
|
|
486
500
|
const method$2 = method$3;
|
|
487
501
|
const implement$2 = implement$3;
|
package/package.json
CHANGED
package/src/Environment.ts
CHANGED
|
@@ -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 =
|
|
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 {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
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
|
|
|
@@ -152,11 +167,30 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
152
167
|
: never;
|
|
153
168
|
};
|
|
154
169
|
|
|
155
|
-
export interface WorkerOptions<
|
|
156
|
-
|
|
170
|
+
export interface WorkerOptions<
|
|
171
|
+
ROut,
|
|
172
|
+
Rpc extends WorkerRpc<ROut>,
|
|
173
|
+
RRequest = never,
|
|
174
|
+
RequestLayerError = never,
|
|
175
|
+
> {
|
|
176
|
+
readonly fetch?: Effect.Effect<WorkerFetchSuccess, unknown, WorkerFetchContext<ROut | RRequest>>;
|
|
177
|
+
/**
|
|
178
|
+
* Per-request services provided while evaluating the fetch handler.
|
|
179
|
+
*
|
|
180
|
+
* This scope closes when the handler produces a `Response`; it is not kept
|
|
181
|
+
* open until a streaming response body is consumed by the platform.
|
|
182
|
+
*/
|
|
183
|
+
readonly requestLayer?: Layer.Layer<RRequest, RequestLayerError, WorkerFetchContext<ROut>>;
|
|
157
184
|
readonly rpc?: Rpc;
|
|
158
185
|
}
|
|
159
186
|
|
|
187
|
+
export type FetchWorkerOptions<ROut, RRequest = never, RequestLayerError = never> = Omit<
|
|
188
|
+
WorkerOptions<ROut, Record<never, never>, RRequest, RequestLayerError>,
|
|
189
|
+
"rpc"
|
|
190
|
+
> & {
|
|
191
|
+
readonly rpc?: never;
|
|
192
|
+
};
|
|
193
|
+
|
|
160
194
|
export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
|
|
161
195
|
ctx: globalThis.ExecutionContext,
|
|
162
196
|
env: WorkerEnv,
|
|
@@ -164,9 +198,43 @@ export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
|
|
|
164
198
|
fetch(request: Request): Promise<Response>;
|
|
165
199
|
} & WorkerRpcShape<Rpc, ROut>;
|
|
166
200
|
|
|
167
|
-
export
|
|
201
|
+
export interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
|
|
202
|
+
readonly fetch: (
|
|
203
|
+
request: Request,
|
|
204
|
+
env: Env,
|
|
205
|
+
ctx: globalThis.ExecutionContext,
|
|
206
|
+
) => Promise<Response>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export const renderHttpResponse = <A extends HttpServerResponse.HttpServerResponse, E, R>(
|
|
210
|
+
effect: Effect.Effect<A, E, R>,
|
|
211
|
+
): Effect.Effect<Response, E, R> =>
|
|
212
|
+
Effect.flatMap(effect, (response) =>
|
|
213
|
+
Effect.map(Effect.context<never>(), (context) =>
|
|
214
|
+
HttpServerResponse.toWeb(response, { context }),
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const renderFetchSuccess = <E, R>(
|
|
219
|
+
effect: Effect.Effect<WorkerFetchSuccess, E, R>,
|
|
220
|
+
): Effect.Effect<Response, E, R> =>
|
|
221
|
+
Effect.flatMap(effect, (response) =>
|
|
222
|
+
response instanceof Response
|
|
223
|
+
? Effect.succeed(response)
|
|
224
|
+
: Effect.map(Effect.context<never>(), (context) =>
|
|
225
|
+
HttpServerResponse.toWeb(response, { context }),
|
|
226
|
+
),
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
export const make = <
|
|
230
|
+
ROut,
|
|
231
|
+
LayerError,
|
|
232
|
+
const Rpc extends WorkerRpc<ROut> = Record<never, never>,
|
|
233
|
+
RRequest = never,
|
|
234
|
+
RequestLayerError = never,
|
|
235
|
+
>(
|
|
168
236
|
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
169
|
-
options: WorkerOptions<ROut, Rpc>,
|
|
237
|
+
options: WorkerOptions<ROut, Rpc, RRequest, RequestLayerError>,
|
|
170
238
|
): WorkerClass<Rpc, ROut> => {
|
|
171
239
|
class EffectWorker extends CloudflareWorkerEntrypoint<WorkerEnv> {
|
|
172
240
|
readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
|
|
@@ -179,14 +247,19 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
|
|
|
179
247
|
|
|
180
248
|
const services = Layer.mergeAll(
|
|
181
249
|
Layer.succeed(ExecutionContext, ctx),
|
|
250
|
+
ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))),
|
|
182
251
|
Layer.succeed(
|
|
183
252
|
WorkerContext,
|
|
184
253
|
fromExecutionContext(ctx, (effect) => runWaitUntilEffect(effect)),
|
|
185
254
|
),
|
|
186
255
|
Layer.succeed(WorkerEnvironment, env),
|
|
187
|
-
)
|
|
256
|
+
) as Layer.Layer<ExecutionContext | WorkerContext | WorkerEnvironment, never, never>;
|
|
188
257
|
|
|
189
|
-
const runtimeLayer = Entrypoint.provideEntrypointServices
|
|
258
|
+
const runtimeLayer = Entrypoint.provideEntrypointServices<
|
|
259
|
+
ROut,
|
|
260
|
+
LayerError,
|
|
261
|
+
ExecutionContext | WorkerContext | WorkerEnvironment
|
|
262
|
+
>(layer, services);
|
|
190
263
|
|
|
191
264
|
this.runtime = ManagedRuntime.make(runtimeLayer);
|
|
192
265
|
runWaitUntilEffect = <A, E>(effect: Effect.Effect<A, E, never>) =>
|
|
@@ -204,14 +277,22 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
|
|
|
204
277
|
return Promise.resolve(new Response("Not Found", { status: 404 }));
|
|
205
278
|
}
|
|
206
279
|
|
|
280
|
+
const requestServices = Layer.mergeAll(
|
|
281
|
+
Layer.succeed(NativeRequest, request),
|
|
282
|
+
Layer.succeed(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromWeb(request)),
|
|
283
|
+
);
|
|
284
|
+
const fetchEffect = renderFetchSuccess(fetchHandler);
|
|
285
|
+
const requestEffect =
|
|
286
|
+
options.requestLayer === undefined
|
|
287
|
+
? fetchEffect
|
|
288
|
+
: fetchEffect.pipe(Effect.provide(options.requestLayer));
|
|
289
|
+
|
|
207
290
|
return this[RunSymbol](
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
),
|
|
214
|
-
),
|
|
291
|
+
requestEffect.pipe(Effect.provide(requestServices)) as Effect.Effect<
|
|
292
|
+
Response,
|
|
293
|
+
unknown,
|
|
294
|
+
RuntimeContext<ROut> | Scope.Scope
|
|
295
|
+
>,
|
|
215
296
|
);
|
|
216
297
|
}
|
|
217
298
|
}
|
|
@@ -227,6 +308,23 @@ export const make = <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Recor
|
|
|
227
308
|
return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut>>(EffectWorker);
|
|
228
309
|
};
|
|
229
310
|
|
|
311
|
+
export const makeFetchHandler = <
|
|
312
|
+
ROut,
|
|
313
|
+
LayerError,
|
|
314
|
+
RRequest = never,
|
|
315
|
+
RequestLayerError = never,
|
|
316
|
+
Env extends WorkerEnv = WorkerEnv,
|
|
317
|
+
>(
|
|
318
|
+
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
319
|
+
options: FetchWorkerOptions<ROut, RRequest, RequestLayerError>,
|
|
320
|
+
): FetchHandler<Env> => {
|
|
321
|
+
const WorkerClass = make(layer, options);
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
fetch: (request, env, ctx) => Promise.resolve(new WorkerClass(ctx, env).fetch(request)),
|
|
325
|
+
};
|
|
326
|
+
};
|
|
327
|
+
|
|
230
328
|
export const Tag = WorkerDefinition.Tag;
|
|
231
329
|
|
|
232
330
|
export const method = WorkerDefinition.method;
|
|
@@ -252,10 +350,12 @@ export type Handlers<
|
|
|
252
350
|
Self extends WorkerDefinition.Definition.Any,
|
|
253
351
|
> = WorkerDefinition.Handlers<ROut, Self>;
|
|
254
352
|
|
|
255
|
-
export type Options<
|
|
353
|
+
export type Options<
|
|
256
354
|
ROut,
|
|
257
|
-
Self
|
|
258
|
-
|
|
355
|
+
Self extends WorkerDefinition.Definition.Any,
|
|
356
|
+
RRequest = never,
|
|
357
|
+
RequestLayerError = never,
|
|
358
|
+
> = WorkerDefinition.Options<ROut, Self, RRequest, RequestLayerError>;
|
|
259
359
|
|
|
260
360
|
export type HandlerEffect<
|
|
261
361
|
ROut,
|
package/src/WorkerDefinition.ts
CHANGED
|
@@ -74,8 +74,13 @@ type BoundaryHandlers<ROut, Self extends Definition.Any> = {
|
|
|
74
74
|
/**
|
|
75
75
|
* Worker constructor options for a specific RPC definition.
|
|
76
76
|
*/
|
|
77
|
-
export interface Options<
|
|
78
|
-
|
|
77
|
+
export interface Options<
|
|
78
|
+
ROut,
|
|
79
|
+
Self extends Definition.Any,
|
|
80
|
+
RRequest = never,
|
|
81
|
+
RequestLayerError = never,
|
|
82
|
+
> extends Omit<
|
|
83
|
+
WorkerEntrypoint.WorkerOptions<ROut, Handlers<ROut, Self>, RRequest, RequestLayerError>,
|
|
79
84
|
"rpc"
|
|
80
85
|
> {
|
|
81
86
|
readonly rpc: Handlers<ROut, Self>;
|
|
@@ -108,13 +113,13 @@ const makeDefinition = <Id extends string, const MethodsShape extends RpcDefinit
|
|
|
108
113
|
const definition: SelfDefinition = RpcDefinition.make(id, methods);
|
|
109
114
|
|
|
110
115
|
return Object.assign(definition, {
|
|
111
|
-
make: <ROut, LayerError>(
|
|
116
|
+
make: <ROut, LayerError, RRequest = never, RequestLayerError = never>(
|
|
112
117
|
layer: Layer.Layer<
|
|
113
118
|
ROut,
|
|
114
119
|
LayerError,
|
|
115
120
|
WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
|
|
116
121
|
>,
|
|
117
|
-
options: Options<ROut, SelfDefinition>,
|
|
122
|
+
options: Options<ROut, SelfDefinition, RRequest, RequestLayerError>,
|
|
118
123
|
) =>
|
|
119
124
|
WorkerEntrypoint.make(layer, {
|
|
120
125
|
...options,
|