effect-cf 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +26 -2
- package/dist/index.d.mts +929 -897
- package/dist/index.mjs +92 -53
- package/package.json +1 -1
- package/src/ContainerNamespace.ts +172 -33
- package/src/DurableObjectState.ts +61 -34
- package/src/WorkflowBinding.ts +5 -1
- package/src/internal/WorkerContext.ts +29 -19
package/dist/index.mjs
CHANGED
|
@@ -995,6 +995,45 @@ const handlers = (options) => ({
|
|
|
995
995
|
webSocketError: options.error
|
|
996
996
|
});
|
|
997
997
|
//#endregion
|
|
998
|
+
//#region src/internal/WorkerContext.ts
|
|
999
|
+
const causeError = (cause) => {
|
|
1000
|
+
const squashed = Cause.squash(cause);
|
|
1001
|
+
return squashed instanceof Error ? squashed : new Error(Cause.pretty(cause), { cause: squashed });
|
|
1002
|
+
};
|
|
1003
|
+
const failureHandler = (label, cause, options) => (options?.onFailure?.(cause) ?? Effect.logError(`${label} failed`, Cause.pretty(cause))).pipe(Effect.catchCause((handlerCause) => Effect.logError(`${label} failure handler failed`, Cause.pretty(cause), Cause.pretty(handlerCause))));
|
|
1004
|
+
/**
|
|
1005
|
+
* Builds a `waitUntil` scheduler for any Cloudflare lifecycle that accepts
|
|
1006
|
+
* background promises (`ExecutionContext`, `DurableObjectState`).
|
|
1007
|
+
*
|
|
1008
|
+
* Background effects capture the calling fiber's context via `Effect.context`
|
|
1009
|
+
* before being registered, so the default module-level `Effect.runPromiseExit`
|
|
1010
|
+
* runner is sufficient; entrypoints may still pass a runtime-bound runner.
|
|
1011
|
+
*/
|
|
1012
|
+
const makeWaitUntilScheduler = (label, register, runPromiseExit = (effect) => Effect.runPromiseExit(effect)) => {
|
|
1013
|
+
return (effect, options, mode) => Effect.context().pipe(Effect.flatMap((context) => Effect.sync(() => {
|
|
1014
|
+
const runHandler = (cause) => runPromiseExit(Effect.scoped(Effect.provideContext(failureHandler(label, cause, options), context))).then((exit) => {
|
|
1015
|
+
if (Exit.isFailure(exit)) console.error(`${label} failure handler failed`, Cause.pretty(exit.cause));
|
|
1016
|
+
});
|
|
1017
|
+
register(runPromiseExit(Effect.scoped(Effect.provideContext(effect, context))).then(async (exit) => {
|
|
1018
|
+
if (Exit.isSuccess(exit)) return;
|
|
1019
|
+
await runHandler(exit.cause);
|
|
1020
|
+
if (mode === "propagate") throw causeError(exit.cause);
|
|
1021
|
+
}));
|
|
1022
|
+
})));
|
|
1023
|
+
};
|
|
1024
|
+
/**
|
|
1025
|
+
* Builds the `WorkerContext` service from a native `ExecutionContext`.
|
|
1026
|
+
*/
|
|
1027
|
+
const fromExecutionContext = (ctx, runPromiseExit = (effect) => Effect.runPromiseExit(effect)) => {
|
|
1028
|
+
const schedule = makeWaitUntilScheduler("WorkerContext.waitUntil", (promise) => ctx.waitUntil(promise), runPromiseExit);
|
|
1029
|
+
return {
|
|
1030
|
+
raw: ctx,
|
|
1031
|
+
waitUntil: (effect, options) => schedule(effect, options, options?.mode ?? "observe"),
|
|
1032
|
+
waitUntilPropagating: (effect, options) => schedule(effect, options, "propagate"),
|
|
1033
|
+
passThroughOnException: Effect.sync(() => ctx.passThroughOnException())
|
|
1034
|
+
};
|
|
1035
|
+
};
|
|
1036
|
+
//#endregion
|
|
998
1037
|
//#region src/DurableObjectState.ts
|
|
999
1038
|
var DurableObjectState_exports = /* @__PURE__ */ __exportAll({
|
|
1000
1039
|
DurableObjectState: () => DurableObjectState,
|
|
@@ -1007,23 +1046,27 @@ var DurableObjectState = class extends Context.Service()("effect-cf/DurableObjec
|
|
|
1007
1046
|
/**
|
|
1008
1047
|
* Wraps a native Cloudflare `DurableObjectState` as a {@link DurableObjectStateService}.
|
|
1009
1048
|
*/
|
|
1010
|
-
const fromDurableObjectState = (state) =>
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1049
|
+
const fromDurableObjectState = (state) => {
|
|
1050
|
+
const scheduleWaitUntil = makeWaitUntilScheduler("DurableObjectState.waitUntil", (promise) => state.waitUntil(promise));
|
|
1051
|
+
const waitUntil = ((input, options) => Effect.isEffect(input) ? scheduleWaitUntil(input, options, options?.mode ?? "observe") : Effect.sync(() => state.waitUntil(input)));
|
|
1052
|
+
return {
|
|
1053
|
+
raw: state,
|
|
1054
|
+
id: state.id,
|
|
1055
|
+
storage: fromDurableObjectStorage(state.storage),
|
|
1056
|
+
waitUntil,
|
|
1057
|
+
blockConcurrencyWhile: (effect) => Effect.context().pipe(Effect.flatMap((context) => Effect.promise(() => state.blockConcurrencyWhile(() => runPromiseExitPreservingTypedFailures(context, effect)))), Effect.flatten),
|
|
1058
|
+
blockConcurrencyWhileOrReset: (effect) => Effect.context().pipe(Effect.flatMap((context) => Effect.promise(() => state.blockConcurrencyWhile(() => Effect.runPromiseWith(context)(effect))))),
|
|
1059
|
+
acceptWebSocket: (ws, tags) => Effect.sync(() => state.acceptWebSocket(ws.raw, tags)),
|
|
1060
|
+
getWebSockets: (tag) => Effect.sync(() => state.getWebSockets(tag).map((socket) => fromWebSocket(socket))),
|
|
1061
|
+
setWebSocketAutoResponse: (pair) => Effect.sync(() => state.setWebSocketAutoResponse(pair)),
|
|
1062
|
+
getWebSocketAutoResponse: Effect.sync(() => state.getWebSocketAutoResponse()),
|
|
1063
|
+
getWebSocketAutoResponseTimestamp: (ws) => Effect.sync(() => state.getWebSocketAutoResponseTimestamp(ws.raw)),
|
|
1064
|
+
setHibernatableWebSocketEventTimeout: (timeoutMs) => Effect.sync(() => state.setHibernatableWebSocketEventTimeout(timeoutMs)),
|
|
1065
|
+
getHibernatableWebSocketEventTimeout: Effect.sync(() => state.getHibernatableWebSocketEventTimeout()),
|
|
1066
|
+
getTags: (ws) => Effect.sync(() => state.getTags(ws.raw)),
|
|
1067
|
+
abort: (reason) => Effect.sync(() => state.abort(reason))
|
|
1068
|
+
};
|
|
1069
|
+
};
|
|
1027
1070
|
const runPromiseExitPreservingTypedFailures = async (context, effect) => {
|
|
1028
1071
|
const exit = await Effect.runPromiseExitWith(context)(effect);
|
|
1029
1072
|
if (Exit.isFailure(exit) && (Cause.hasDies(exit.cause) || Cause.hasInterrupts(exit.cause))) throw Cause.squash(exit.cause);
|
|
@@ -1194,6 +1237,12 @@ const makeInstanceClient = (definition, instance, stub) => {
|
|
|
1194
1237
|
instance,
|
|
1195
1238
|
operation
|
|
1196
1239
|
} });
|
|
1240
|
+
const hostListOperation = (operation) => Effect.fn(`ContainerNamespace.${operation}`, spanOptions(operation))(function* (hosts) {
|
|
1241
|
+
return yield* tryContainerPromise(definition, instance, operation, () => stub[operation]([...hosts]));
|
|
1242
|
+
});
|
|
1243
|
+
const hostnameOperation = (operation) => Effect.fn(`ContainerNamespace.${operation}`, spanOptions(operation))(function* (hostname) {
|
|
1244
|
+
return yield* tryContainerPromise(definition, instance, operation, () => stub[operation](hostname));
|
|
1245
|
+
});
|
|
1197
1246
|
return {
|
|
1198
1247
|
rawUnsafe: Effect.succeed(stub),
|
|
1199
1248
|
state: tryContainerPromise(definition, instance, "state", () => stub.getState()).pipe(Effect.flatMap(Schema.decodeUnknownEffect(ContainerState)), Effect.mapError((cause) => cause instanceof ContainerOperationError ? cause : new ContainerOperationError({
|
|
@@ -1211,10 +1260,19 @@ const makeInstanceClient = (definition, instance, stub) => {
|
|
|
1211
1260
|
startAndWaitForPorts: Effect.fn("ContainerNamespace.startAndWaitForPorts", spanOptions("startAndWaitForPorts"))(function* (options) {
|
|
1212
1261
|
return yield* tryContainerPromise(definition, instance, "startAndWaitForPorts", () => stub.startAndWaitForPorts(options));
|
|
1213
1262
|
}),
|
|
1263
|
+
waitForPort: Effect.fn("ContainerNamespace.waitForPort", spanOptions("waitForPort"))(function* (options) {
|
|
1264
|
+
return yield* tryContainerPromise(definition, instance, "waitForPort", () => stub.waitForPort(options));
|
|
1265
|
+
}),
|
|
1214
1266
|
stop: Effect.fn("ContainerNamespace.stop", spanOptions("stop"))(function* (signal) {
|
|
1215
1267
|
return yield* tryContainerPromise(definition, instance, "stop", () => stub.stop(signal));
|
|
1216
1268
|
}),
|
|
1217
|
-
destroy: tryContainerPromise(definition, instance, "destroy", () => stub.destroy()).pipe(Effect.withSpan("ContainerNamespace.destroy", spanOptions("destroy")))
|
|
1269
|
+
destroy: tryContainerPromise(definition, instance, "destroy", () => stub.destroy()).pipe(Effect.withSpan("ContainerNamespace.destroy", spanOptions("destroy"))),
|
|
1270
|
+
setAllowedHosts: hostListOperation("setAllowedHosts"),
|
|
1271
|
+
setDeniedHosts: hostListOperation("setDeniedHosts"),
|
|
1272
|
+
allowHost: hostnameOperation("allowHost"),
|
|
1273
|
+
denyHost: hostnameOperation("denyHost"),
|
|
1274
|
+
removeAllowedHost: hostnameOperation("removeAllowedHost"),
|
|
1275
|
+
removeDeniedHost: hostnameOperation("removeDeniedHost")
|
|
1218
1276
|
};
|
|
1219
1277
|
};
|
|
1220
1278
|
const isContainerNamespaceResource = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "getByName") === "function";
|
|
@@ -1236,8 +1294,15 @@ const makeClient$10 = (definition) => (namespace) => {
|
|
|
1236
1294
|
fetch: (input, init) => Effect.flatMap(instance, (client) => client.fetch(input, init)),
|
|
1237
1295
|
start: (startOptions, waitOptions) => Effect.flatMap(instance, (client) => client.start(startOptions, waitOptions)),
|
|
1238
1296
|
startAndWaitForPorts: (startOptions) => Effect.flatMap(instance, (client) => client.startAndWaitForPorts(startOptions)),
|
|
1297
|
+
waitForPort: (waitOptions) => Effect.flatMap(instance, (client) => client.waitForPort(waitOptions)),
|
|
1239
1298
|
stop: (signal) => Effect.flatMap(instance, (client) => client.stop(signal)),
|
|
1240
|
-
destroy: Effect.flatMap(instance, (client) => client.destroy)
|
|
1299
|
+
destroy: Effect.flatMap(instance, (client) => client.destroy),
|
|
1300
|
+
setAllowedHosts: (hosts) => Effect.flatMap(instance, (client) => client.setAllowedHosts(hosts)),
|
|
1301
|
+
setDeniedHosts: (hosts) => Effect.flatMap(instance, (client) => client.setDeniedHosts(hosts)),
|
|
1302
|
+
allowHost: (hostname) => Effect.flatMap(instance, (client) => client.allowHost(hostname)),
|
|
1303
|
+
denyHost: (hostname) => Effect.flatMap(instance, (client) => client.denyHost(hostname)),
|
|
1304
|
+
removeAllowedHost: (hostname) => Effect.flatMap(instance, (client) => client.removeAllowedHost(hostname)),
|
|
1305
|
+
removeDeniedHost: (hostname) => Effect.flatMap(instance, (client) => client.removeDeniedHost(hostname))
|
|
1241
1306
|
};
|
|
1242
1307
|
};
|
|
1243
1308
|
return {
|
|
@@ -1261,8 +1326,15 @@ const Tag$12 = () => (id) => {
|
|
|
1261
1326
|
fetch: (input, init) => Effect.flatMap(getByName(name, options), (instance) => instance.fetch(input, init)),
|
|
1262
1327
|
start: (startOptions, waitOptions) => Effect.flatMap(getByName(name, options), (instance) => instance.start(startOptions, waitOptions)),
|
|
1263
1328
|
startAndWaitForPorts: (startOptions) => Effect.flatMap(getByName(name, options), (instance) => instance.startAndWaitForPorts(startOptions)),
|
|
1329
|
+
waitForPort: (waitOptions) => Effect.flatMap(getByName(name, options), (instance) => instance.waitForPort(waitOptions)),
|
|
1264
1330
|
stop: (signal) => Effect.flatMap(getByName(name, options), (instance) => instance.stop(signal)),
|
|
1265
1331
|
destroy: Effect.flatMap(getByName(name, options), (instance) => instance.destroy),
|
|
1332
|
+
setAllowedHosts: (hosts) => Effect.flatMap(getByName(name, options), (instance) => instance.setAllowedHosts(hosts)),
|
|
1333
|
+
setDeniedHosts: (hosts) => Effect.flatMap(getByName(name, options), (instance) => instance.setDeniedHosts(hosts)),
|
|
1334
|
+
allowHost: (hostname) => Effect.flatMap(getByName(name, options), (instance) => instance.allowHost(hostname)),
|
|
1335
|
+
denyHost: (hostname) => Effect.flatMap(getByName(name, options), (instance) => instance.denyHost(hostname)),
|
|
1336
|
+
removeAllowedHost: (hostname) => Effect.flatMap(getByName(name, options), (instance) => instance.removeAllowedHost(hostname)),
|
|
1337
|
+
removeDeniedHost: (hostname) => Effect.flatMap(getByName(name, options), (instance) => instance.removeDeniedHost(hostname)),
|
|
1266
1338
|
rawUnsafe: Effect.flatMap(getByName(name, options), (instance) => instance.rawUnsafe)
|
|
1267
1339
|
});
|
|
1268
1340
|
const rawUnsafe = Effect.flatMap(tag, (namespace) => namespace.rawUnsafe);
|
|
@@ -1710,39 +1782,6 @@ const runEventPromise = (runtime, effect, eventLayer) => {
|
|
|
1710
1782
|
return runtime.runPromise(Effect.scoped(withEventLayer));
|
|
1711
1783
|
};
|
|
1712
1784
|
//#endregion
|
|
1713
|
-
//#region src/internal/WorkerContext.ts
|
|
1714
|
-
const causeError = (cause) => {
|
|
1715
|
-
const squashed = Cause.squash(cause);
|
|
1716
|
-
return squashed instanceof Error ? squashed : new Error(Cause.pretty(cause), { cause: squashed });
|
|
1717
|
-
};
|
|
1718
|
-
const failureHandler = (cause, options) => (options?.onFailure?.(cause) ?? Effect.logError("WorkerContext.waitUntil failed", Cause.pretty(cause))).pipe(Effect.catchCause((handlerCause) => Effect.logError("WorkerContext.waitUntil failure handler failed", Cause.pretty(cause), Cause.pretty(handlerCause))));
|
|
1719
|
-
/**
|
|
1720
|
-
* Builds the `WorkerContext` service from a native `ExecutionContext`.
|
|
1721
|
-
*
|
|
1722
|
-
* Background effects capture the calling fiber's context via `Effect.context`
|
|
1723
|
-
* before being handed to `ctx.waitUntil`, so the default module-level
|
|
1724
|
-
* `Effect.runPromiseExit` runner is sufficient; entrypoints may still pass a
|
|
1725
|
-
* runtime-bound runner.
|
|
1726
|
-
*/
|
|
1727
|
-
const fromExecutionContext = (ctx, runPromiseExit = (effect) => Effect.runPromiseExit(effect)) => {
|
|
1728
|
-
const schedule = (effect, options, mode) => Effect.context().pipe(Effect.flatMap((context) => Effect.sync(() => {
|
|
1729
|
-
const runHandler = (cause) => runPromiseExit(Effect.scoped(Effect.provideContext(failureHandler(cause, options), context))).then((exit) => {
|
|
1730
|
-
if (Exit.isFailure(exit)) console.error("WorkerContext.waitUntil failure handler failed", Cause.pretty(exit.cause));
|
|
1731
|
-
});
|
|
1732
|
-
ctx.waitUntil(runPromiseExit(Effect.scoped(Effect.provideContext(effect, context))).then(async (exit) => {
|
|
1733
|
-
if (Exit.isSuccess(exit)) return;
|
|
1734
|
-
await runHandler(exit.cause);
|
|
1735
|
-
if (mode === "propagate") throw causeError(exit.cause);
|
|
1736
|
-
}));
|
|
1737
|
-
})));
|
|
1738
|
-
return {
|
|
1739
|
-
raw: ctx,
|
|
1740
|
-
waitUntil: (effect, options) => schedule(effect, options, options?.mode ?? "observe"),
|
|
1741
|
-
waitUntilPropagating: (effect, options) => schedule(effect, options, "propagate"),
|
|
1742
|
-
passThroughOnException: Effect.sync(() => ctx.passThroughOnException())
|
|
1743
|
-
};
|
|
1744
|
-
};
|
|
1745
|
-
//#endregion
|
|
1746
1785
|
//#region src/Rpc.ts
|
|
1747
1786
|
var Rpc_exports = /* @__PURE__ */ __exportAll({
|
|
1748
1787
|
RpcStub: () => RpcStub,
|
|
@@ -3706,7 +3745,7 @@ const makeClient = (definition) => {
|
|
|
3706
3745
|
terminate: operation("terminate", () => raw.terminate()),
|
|
3707
3746
|
restart: (options) => operation("restart", () => raw.restart(options)),
|
|
3708
3747
|
status: operation("status", () => raw.status()).pipe(Effect.flatMap((status) => Effect.gen(function* () {
|
|
3709
|
-
const output = status.output === void 0 ? Option.none() : Option.some(yield* decodeResult(status.output).pipe(Effect.mapError((cause) => new WorkflowResultDecodeError({
|
|
3748
|
+
const output = status.output === void 0 || status.output === null && status.status !== "complete" ? Option.none() : Option.some(yield* decodeResult(status.output).pipe(Effect.mapError((cause) => new WorkflowResultDecodeError({
|
|
3710
3749
|
binding: definition.binding,
|
|
3711
3750
|
instanceId: raw.id,
|
|
3712
3751
|
cause
|
package/package.json
CHANGED
|
@@ -6,9 +6,12 @@ import * as ErrorMessage from "./internal/ErrorMessage";
|
|
|
6
6
|
|
|
7
7
|
const expectedContainerNamespace = "Container namespace binding with getByName()";
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/** Named signal accepted by Cloudflare Container instances. */
|
|
10
10
|
export type ContainerSignal = "SIGKILL" | "SIGINT" | "SIGTERM";
|
|
11
11
|
|
|
12
|
+
/** Named or numeric signal accepted by Container `stop()`. */
|
|
13
|
+
export type ContainerStopSignal = ContainerSignal | number;
|
|
14
|
+
|
|
12
15
|
/** Per-instance Container startup configuration. */
|
|
13
16
|
export interface ContainerStartOptions {
|
|
14
17
|
readonly envVars?: Record<string, string>;
|
|
@@ -63,8 +66,15 @@ export interface ContainerStub {
|
|
|
63
66
|
getState(): Promise<ContainerState>;
|
|
64
67
|
start(options?: ContainerStartOptions, waitOptions?: ContainerWaitOptions): Promise<void>;
|
|
65
68
|
startAndWaitForPorts(options?: ContainerStartAndWaitForPortsOptions): Promise<void>;
|
|
66
|
-
|
|
69
|
+
waitForPort(options: ContainerWaitOptions): Promise<number>;
|
|
70
|
+
stop(signal?: ContainerStopSignal): Promise<void>;
|
|
67
71
|
destroy(): Promise<void>;
|
|
72
|
+
setAllowedHosts(hosts: Array<string>): Promise<void>;
|
|
73
|
+
setDeniedHosts(hosts: Array<string>): Promise<void>;
|
|
74
|
+
allowHost(hostname: string): Promise<void>;
|
|
75
|
+
denyHost(hostname: string): Promise<void>;
|
|
76
|
+
removeAllowedHost(hostname: string): Promise<void>;
|
|
77
|
+
removeDeniedHost(hostname: string): Promise<void>;
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
/** Native Container namespace binding shape. */
|
|
@@ -75,6 +85,11 @@ export interface ContainerNamespaceResource {
|
|
|
75
85
|
): ContainerStub;
|
|
76
86
|
}
|
|
77
87
|
|
|
88
|
+
/** Exact native stub type produced by a Container namespace binding. */
|
|
89
|
+
export type ContainerStubOf<Namespace extends ContainerNamespaceResource> = ReturnType<
|
|
90
|
+
Namespace["getByName"]
|
|
91
|
+
>;
|
|
92
|
+
|
|
78
93
|
/** Container namespace binding metadata. */
|
|
79
94
|
export interface ContainerNamespaceDefinition {
|
|
80
95
|
/** Binding name as configured in `wrangler.jsonc`. */
|
|
@@ -94,8 +109,8 @@ export class ContainerOperationError extends Data.TaggedError("ContainerOperatio
|
|
|
94
109
|
}
|
|
95
110
|
|
|
96
111
|
/** Effect-wrapped client for one named Container instance. */
|
|
97
|
-
export interface ContainerInstanceClient {
|
|
98
|
-
readonly rawUnsafe: Effect.Effect<
|
|
112
|
+
export interface ContainerInstanceClient<Stub extends ContainerStub = ContainerStub> {
|
|
113
|
+
readonly rawUnsafe: Effect.Effect<Stub, ContainerOperationError>;
|
|
99
114
|
readonly state: Effect.Effect<ContainerState, ContainerOperationError>;
|
|
100
115
|
readonly fetch: (
|
|
101
116
|
input: RequestInfo | URL,
|
|
@@ -108,29 +123,51 @@ export interface ContainerInstanceClient {
|
|
|
108
123
|
readonly startAndWaitForPorts: (
|
|
109
124
|
options?: ContainerStartAndWaitForPortsOptions,
|
|
110
125
|
) => Effect.Effect<void, ContainerOperationError>;
|
|
111
|
-
readonly
|
|
126
|
+
readonly waitForPort: (
|
|
127
|
+
options: ContainerWaitOptions,
|
|
128
|
+
) => Effect.Effect<number, ContainerOperationError>;
|
|
129
|
+
readonly stop: (signal?: ContainerStopSignal) => Effect.Effect<void, ContainerOperationError>;
|
|
112
130
|
readonly destroy: Effect.Effect<void, ContainerOperationError>;
|
|
131
|
+
readonly setAllowedHosts: (
|
|
132
|
+
hosts: ReadonlyArray<string>,
|
|
133
|
+
) => Effect.Effect<void, ContainerOperationError>;
|
|
134
|
+
readonly setDeniedHosts: (
|
|
135
|
+
hosts: ReadonlyArray<string>,
|
|
136
|
+
) => Effect.Effect<void, ContainerOperationError>;
|
|
137
|
+
readonly allowHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
|
|
138
|
+
readonly denyHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
|
|
139
|
+
readonly removeAllowedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
|
|
140
|
+
readonly removeDeniedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
|
|
113
141
|
}
|
|
114
142
|
|
|
115
143
|
/** Effect client for a Container namespace binding. */
|
|
116
|
-
export interface ContainerNamespaceClient
|
|
144
|
+
export interface ContainerNamespaceClient<
|
|
145
|
+
Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
|
|
146
|
+
> {
|
|
117
147
|
readonly definition: ContainerNamespaceDefinition;
|
|
118
148
|
readonly getByName: (
|
|
119
149
|
name: string,
|
|
120
150
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
121
|
-
) => Effect.Effect<ContainerInstanceClient
|
|
151
|
+
) => Effect.Effect<ContainerInstanceClient<ContainerStubOf<Namespace>>, ContainerOperationError>;
|
|
122
152
|
readonly byName: (
|
|
123
153
|
name: string,
|
|
124
154
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
125
|
-
) => ContainerInstanceClient
|
|
126
|
-
readonly rawUnsafe: Effect.Effect<
|
|
155
|
+
) => ContainerInstanceClient<ContainerStubOf<Namespace>>;
|
|
156
|
+
readonly rawUnsafe: Effect.Effect<Namespace>;
|
|
127
157
|
}
|
|
128
158
|
|
|
129
|
-
export type ContainerNamespaceStaticClient<
|
|
159
|
+
export type ContainerNamespaceStaticClient<
|
|
160
|
+
R,
|
|
161
|
+
Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
|
|
162
|
+
> = {
|
|
130
163
|
readonly getByName: (
|
|
131
164
|
name: string,
|
|
132
165
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
133
|
-
) => Effect.Effect<
|
|
166
|
+
) => Effect.Effect<
|
|
167
|
+
ContainerInstanceClient<ContainerStubOf<Namespace>>,
|
|
168
|
+
ContainerOperationError,
|
|
169
|
+
R
|
|
170
|
+
>;
|
|
134
171
|
readonly byName: (
|
|
135
172
|
name: string,
|
|
136
173
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
@@ -147,21 +184,44 @@ export type ContainerNamespaceStaticClient<R> = {
|
|
|
147
184
|
readonly startAndWaitForPorts: (
|
|
148
185
|
options?: ContainerStartAndWaitForPortsOptions,
|
|
149
186
|
) => Effect.Effect<void, ContainerOperationError, R>;
|
|
150
|
-
readonly
|
|
187
|
+
readonly waitForPort: (
|
|
188
|
+
options: ContainerWaitOptions,
|
|
189
|
+
) => Effect.Effect<number, ContainerOperationError, R>;
|
|
190
|
+
readonly stop: (
|
|
191
|
+
signal?: ContainerStopSignal,
|
|
192
|
+
) => Effect.Effect<void, ContainerOperationError, R>;
|
|
151
193
|
readonly destroy: Effect.Effect<void, ContainerOperationError, R>;
|
|
152
|
-
readonly
|
|
194
|
+
readonly setAllowedHosts: (
|
|
195
|
+
hosts: ReadonlyArray<string>,
|
|
196
|
+
) => Effect.Effect<void, ContainerOperationError, R>;
|
|
197
|
+
readonly setDeniedHosts: (
|
|
198
|
+
hosts: ReadonlyArray<string>,
|
|
199
|
+
) => Effect.Effect<void, ContainerOperationError, R>;
|
|
200
|
+
readonly allowHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
|
|
201
|
+
readonly denyHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
|
|
202
|
+
readonly removeAllowedHost: (
|
|
203
|
+
hostname: string,
|
|
204
|
+
) => Effect.Effect<void, ContainerOperationError, R>;
|
|
205
|
+
readonly removeDeniedHost: (
|
|
206
|
+
hostname: string,
|
|
207
|
+
) => Effect.Effect<void, ContainerOperationError, R>;
|
|
208
|
+
readonly rawUnsafe: Effect.Effect<ContainerStubOf<Namespace>, ContainerOperationError, R>;
|
|
153
209
|
};
|
|
154
|
-
readonly rawUnsafe: Effect.Effect<
|
|
210
|
+
readonly rawUnsafe: Effect.Effect<Namespace, never, R>;
|
|
155
211
|
};
|
|
156
212
|
|
|
157
213
|
export type LayerOptions = {
|
|
158
214
|
readonly binding: string;
|
|
159
215
|
};
|
|
160
216
|
|
|
161
|
-
export interface TagClass<
|
|
217
|
+
export interface TagClass<
|
|
218
|
+
Self,
|
|
219
|
+
Id extends string,
|
|
220
|
+
Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
|
|
221
|
+
>
|
|
162
222
|
extends
|
|
163
|
-
Context.ServiceClass<Self, `effect-cf/Container/${Id}`, ContainerNamespaceClient
|
|
164
|
-
ContainerNamespaceStaticClient<Self> {
|
|
223
|
+
Context.ServiceClass<Self, `effect-cf/Container/${Id}`, ContainerNamespaceClient<Namespace>>,
|
|
224
|
+
ContainerNamespaceStaticClient<Self, Namespace> {
|
|
165
225
|
readonly id: Id;
|
|
166
226
|
readonly layer: (
|
|
167
227
|
options: LayerOptions,
|
|
@@ -189,14 +249,34 @@ const tryContainerPromise = <A>(
|
|
|
189
249
|
}),
|
|
190
250
|
});
|
|
191
251
|
|
|
192
|
-
const makeInstanceClient = (
|
|
252
|
+
const makeInstanceClient = <Stub extends ContainerStub>(
|
|
193
253
|
definition: ContainerNamespaceDefinition,
|
|
194
254
|
instance: string,
|
|
195
|
-
stub:
|
|
196
|
-
): ContainerInstanceClient => {
|
|
255
|
+
stub: Stub,
|
|
256
|
+
): ContainerInstanceClient<Stub> => {
|
|
197
257
|
const spanOptions = (operation: string) => ({
|
|
198
258
|
attributes: { binding: definition.binding, instance, operation },
|
|
199
259
|
});
|
|
260
|
+
const hostListOperation = (operation: "setAllowedHosts" | "setDeniedHosts") =>
|
|
261
|
+
Effect.fn(
|
|
262
|
+
`ContainerNamespace.${operation}`,
|
|
263
|
+
spanOptions(operation),
|
|
264
|
+
)(function* (hosts: ReadonlyArray<string>) {
|
|
265
|
+
return yield* tryContainerPromise(definition, instance, operation, () =>
|
|
266
|
+
stub[operation]([...hosts]),
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
const hostnameOperation = (
|
|
270
|
+
operation: "allowHost" | "denyHost" | "removeAllowedHost" | "removeDeniedHost",
|
|
271
|
+
) =>
|
|
272
|
+
Effect.fn(
|
|
273
|
+
`ContainerNamespace.${operation}`,
|
|
274
|
+
spanOptions(operation),
|
|
275
|
+
)(function* (hostname: string) {
|
|
276
|
+
return yield* tryContainerPromise(definition, instance, operation, () =>
|
|
277
|
+
stub[operation](hostname),
|
|
278
|
+
);
|
|
279
|
+
});
|
|
200
280
|
|
|
201
281
|
return {
|
|
202
282
|
rawUnsafe: Effect.succeed(stub),
|
|
@@ -238,15 +318,29 @@ const makeInstanceClient = (
|
|
|
238
318
|
stub.startAndWaitForPorts(options),
|
|
239
319
|
);
|
|
240
320
|
}),
|
|
321
|
+
waitForPort: Effect.fn(
|
|
322
|
+
"ContainerNamespace.waitForPort",
|
|
323
|
+
spanOptions("waitForPort"),
|
|
324
|
+
)(function* (options: ContainerWaitOptions) {
|
|
325
|
+
return yield* tryContainerPromise(definition, instance, "waitForPort", () =>
|
|
326
|
+
stub.waitForPort(options),
|
|
327
|
+
);
|
|
328
|
+
}),
|
|
241
329
|
stop: Effect.fn(
|
|
242
330
|
"ContainerNamespace.stop",
|
|
243
331
|
spanOptions("stop"),
|
|
244
|
-
)(function* (signal?:
|
|
332
|
+
)(function* (signal?: ContainerStopSignal) {
|
|
245
333
|
return yield* tryContainerPromise(definition, instance, "stop", () => stub.stop(signal));
|
|
246
334
|
}),
|
|
247
335
|
destroy: tryContainerPromise(definition, instance, "destroy", () => stub.destroy()).pipe(
|
|
248
336
|
Effect.withSpan("ContainerNamespace.destroy", spanOptions("destroy")),
|
|
249
337
|
),
|
|
338
|
+
setAllowedHosts: hostListOperation("setAllowedHosts"),
|
|
339
|
+
setDeniedHosts: hostListOperation("setDeniedHosts"),
|
|
340
|
+
allowHost: hostnameOperation("allowHost"),
|
|
341
|
+
denyHost: hostnameOperation("denyHost"),
|
|
342
|
+
removeAllowedHost: hostnameOperation("removeAllowedHost"),
|
|
343
|
+
removeDeniedHost: hostnameOperation("removeDeniedHost"),
|
|
250
344
|
};
|
|
251
345
|
};
|
|
252
346
|
|
|
@@ -257,13 +351,22 @@ export const isContainerNamespaceResource = (value: unknown): value is Container
|
|
|
257
351
|
|
|
258
352
|
export const makeClient =
|
|
259
353
|
(definition: ContainerNamespaceDefinition) =>
|
|
260
|
-
|
|
354
|
+
<Namespace extends ContainerNamespaceResource>(
|
|
355
|
+
namespace: Namespace,
|
|
356
|
+
): ContainerNamespaceClient<Namespace> => {
|
|
261
357
|
const getByName = (
|
|
262
358
|
name: string,
|
|
263
359
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
264
360
|
) =>
|
|
265
361
|
Effect.try({
|
|
266
|
-
try: () =>
|
|
362
|
+
try: () =>
|
|
363
|
+
makeInstanceClient(
|
|
364
|
+
definition,
|
|
365
|
+
name,
|
|
366
|
+
// getByName has a single call signature, so its return type is
|
|
367
|
+
// exactly the namespace's stub type.
|
|
368
|
+
namespace.getByName(name, options) as ContainerStubOf<Namespace>,
|
|
369
|
+
),
|
|
267
370
|
catch: (cause) =>
|
|
268
371
|
new ContainerOperationError({
|
|
269
372
|
binding: definition.binding,
|
|
@@ -276,7 +379,7 @@ export const makeClient =
|
|
|
276
379
|
const byName = (
|
|
277
380
|
name: string,
|
|
278
381
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
279
|
-
): ContainerInstanceClient => {
|
|
382
|
+
): ContainerInstanceClient<ContainerStubOf<Namespace>> => {
|
|
280
383
|
const instance = getByName(name, options);
|
|
281
384
|
|
|
282
385
|
return {
|
|
@@ -287,8 +390,20 @@ export const makeClient =
|
|
|
287
390
|
Effect.flatMap(instance, (client) => client.start(startOptions, waitOptions)),
|
|
288
391
|
startAndWaitForPorts: (startOptions) =>
|
|
289
392
|
Effect.flatMap(instance, (client) => client.startAndWaitForPorts(startOptions)),
|
|
393
|
+
waitForPort: (waitOptions) =>
|
|
394
|
+
Effect.flatMap(instance, (client) => client.waitForPort(waitOptions)),
|
|
290
395
|
stop: (signal) => Effect.flatMap(instance, (client) => client.stop(signal)),
|
|
291
396
|
destroy: Effect.flatMap(instance, (client) => client.destroy),
|
|
397
|
+
setAllowedHosts: (hosts) =>
|
|
398
|
+
Effect.flatMap(instance, (client) => client.setAllowedHosts(hosts)),
|
|
399
|
+
setDeniedHosts: (hosts) =>
|
|
400
|
+
Effect.flatMap(instance, (client) => client.setDeniedHosts(hosts)),
|
|
401
|
+
allowHost: (hostname) => Effect.flatMap(instance, (client) => client.allowHost(hostname)),
|
|
402
|
+
denyHost: (hostname) => Effect.flatMap(instance, (client) => client.denyHost(hostname)),
|
|
403
|
+
removeAllowedHost: (hostname) =>
|
|
404
|
+
Effect.flatMap(instance, (client) => client.removeAllowedHost(hostname)),
|
|
405
|
+
removeDeniedHost: (hostname) =>
|
|
406
|
+
Effect.flatMap(instance, (client) => client.removeDeniedHost(hostname)),
|
|
292
407
|
};
|
|
293
408
|
};
|
|
294
409
|
|
|
@@ -300,13 +415,21 @@ export const makeClient =
|
|
|
300
415
|
};
|
|
301
416
|
};
|
|
302
417
|
|
|
303
|
-
export const layer = <Self>(
|
|
304
|
-
tag: Context.Service<Self, ContainerNamespaceClient
|
|
418
|
+
export const layer = <Self, Namespace extends ContainerNamespaceResource>(
|
|
419
|
+
tag: Context.Service<Self, ContainerNamespaceClient<Namespace>>,
|
|
305
420
|
definition: ContainerNamespaceDefinition,
|
|
306
421
|
) =>
|
|
307
|
-
Binding.layer(
|
|
308
|
-
|
|
309
|
-
|
|
422
|
+
Binding.layer(
|
|
423
|
+
tag,
|
|
424
|
+
definition.binding,
|
|
425
|
+
// Runtime validation stays structural; the exact namespace type is
|
|
426
|
+
// asserted by the consumer's binding declaration, like env typing.
|
|
427
|
+
isContainerNamespaceResource as (value: unknown) => value is Namespace,
|
|
428
|
+
makeClient(definition),
|
|
429
|
+
{
|
|
430
|
+
expected: expectedContainerNamespace,
|
|
431
|
+
},
|
|
432
|
+
);
|
|
310
433
|
|
|
311
434
|
export const make = <Id extends string>(id: Id) => Tag<ContainerNamespaceService<Id>>()<Id>(id);
|
|
312
435
|
|
|
@@ -321,9 +444,9 @@ export interface ContainerNamespaceService<Id extends string> {
|
|
|
321
444
|
|
|
322
445
|
/** Creates a typed Effect service for a Cloudflare Container namespace. */
|
|
323
446
|
export const Tag =
|
|
324
|
-
<Self>() =>
|
|
447
|
+
<Self, Namespace extends ContainerNamespaceResource = ContainerNamespaceResource>() =>
|
|
325
448
|
<Id extends string>(id: Id) => {
|
|
326
|
-
const tag = Context.Service<Self, ContainerNamespaceClient
|
|
449
|
+
const tag = Context.Service<Self, ContainerNamespaceClient<Namespace>>()(
|
|
327
450
|
`effect-cf/Container/${id}` as const,
|
|
328
451
|
);
|
|
329
452
|
const makeLayer = (definition: LayerOptions) => layer(tag, definition);
|
|
@@ -352,9 +475,25 @@ export const Tag =
|
|
|
352
475
|
Effect.flatMap(getByName(name, options), (instance) =>
|
|
353
476
|
instance.startAndWaitForPorts(startOptions),
|
|
354
477
|
),
|
|
355
|
-
|
|
478
|
+
waitForPort: (waitOptions: ContainerWaitOptions) =>
|
|
479
|
+
Effect.flatMap(getByName(name, options), (instance) => instance.waitForPort(waitOptions)),
|
|
480
|
+
stop: (signal?: ContainerStopSignal) =>
|
|
356
481
|
Effect.flatMap(getByName(name, options), (instance) => instance.stop(signal)),
|
|
357
482
|
destroy: Effect.flatMap(getByName(name, options), (instance) => instance.destroy),
|
|
483
|
+
setAllowedHosts: (hosts: ReadonlyArray<string>) =>
|
|
484
|
+
Effect.flatMap(getByName(name, options), (instance) => instance.setAllowedHosts(hosts)),
|
|
485
|
+
setDeniedHosts: (hosts: ReadonlyArray<string>) =>
|
|
486
|
+
Effect.flatMap(getByName(name, options), (instance) => instance.setDeniedHosts(hosts)),
|
|
487
|
+
allowHost: (hostname: string) =>
|
|
488
|
+
Effect.flatMap(getByName(name, options), (instance) => instance.allowHost(hostname)),
|
|
489
|
+
denyHost: (hostname: string) =>
|
|
490
|
+
Effect.flatMap(getByName(name, options), (instance) => instance.denyHost(hostname)),
|
|
491
|
+
removeAllowedHost: (hostname: string) =>
|
|
492
|
+
Effect.flatMap(getByName(name, options), (instance) =>
|
|
493
|
+
instance.removeAllowedHost(hostname),
|
|
494
|
+
),
|
|
495
|
+
removeDeniedHost: (hostname: string) =>
|
|
496
|
+
Effect.flatMap(getByName(name, options), (instance) => instance.removeDeniedHost(hostname)),
|
|
358
497
|
rawUnsafe: Effect.flatMap(getByName(name, options), (instance) => instance.rawUnsafe),
|
|
359
498
|
});
|
|
360
499
|
|
|
@@ -366,7 +505,7 @@ export const Tag =
|
|
|
366
505
|
getByName,
|
|
367
506
|
byName,
|
|
368
507
|
rawUnsafe,
|
|
369
|
-
}) as TagClass<Self, Id>;
|
|
508
|
+
}) as TagClass<Self, Id, Namespace>;
|
|
370
509
|
};
|
|
371
510
|
|
|
372
511
|
export const ContainerNamespace = Tag;
|