effect-cf 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#110](https://github.com/danieljvdm/effect-cf/pull/110) [`9037864`](https://github.com/danieljvdm/effect-cf/commit/9037864c1acbd15e03b6a71733d49fa123244046) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Add `setAlarmAfter` to Durable Object storage and transaction wrappers for scheduling alarms with Effect `Duration.Input` values.
8
+
9
+ - [#112](https://github.com/danieljvdm/effect-cf/pull/112) [`cbe0206`](https://github.com/danieljvdm/effect-cf/commit/cbe0206557b101c7bb4050e53366e53af9aa8e5f) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Add `effect-cf/sandbox`: an Effect-native client for the Cloudflare Sandbox SDK 1.0 API (`@cloudflare/sandbox@next`, an optional peer dependency loaded lazily on first use).
10
+
11
+ `Sandbox.Tag`/`Sandbox.make` create a typed service for a Sandbox Durable Object namespace binding, and `layer({ binding })` validates the binding from `WorkerEnvironment`. The instance client covers the complete `ISandbox` contract plus lifecycle, preview-port, tunnel, bucket-mount, and backup operations: `exec` returns a process handle with typed log `Stream`s and interruption-aware waits, `watch` decodes file-watch Server-Sent Events into an Effect `Stream`, `readFileStream` streams raw bytes, and terminals are wrapped as Effect handles. Failures are reported as a typed `SandboxOperationError` that preserves the SDK's error classes as `cause`, and `Sandbox.proxyToSandbox` routes preview-URL traffic as an `Option<Response>` Effect.
12
+
3
13
  ## 0.29.0
4
14
 
5
15
  ### Minor Changes
package/README.md CHANGED
@@ -33,6 +33,14 @@ Workspace Git operations additionally require `@platformatic/vfs`,
33
33
  bun add "@platformatic/vfs@^0.4.0"
34
34
  ```
35
35
 
36
+ The Sandbox integration is an optional peer. Install the `@next` release line
37
+ of `@cloudflare/sandbox` (the Sandbox SDK 1.0 API) only in applications that
38
+ import `effect-cf/sandbox`:
39
+
40
+ ```bash
41
+ bun add "@cloudflare/sandbox@next"
42
+ ```
43
+
36
44
  ## Goal
37
45
 
38
46
  Cloudflare APIs return promises and expose platform-specific bindings. `effect-cf` wraps those boundaries as `Context`, `Layer`, and `Effect` values so application code stays inside one managed Effect runtime.
@@ -54,6 +62,7 @@ Runtime creation belongs at Cloudflare entrypoints, not inside binding helpers.
54
62
  - `effect-cf/computer-workspace` - scoped Effect wrappers for the complete Cloudflare Computer filesystem, Git, runtime, Assets, Artifacts, and Think-compatible client surfaces
55
63
  - `effect-cf/computer-artifacts` - session-isolated Artifacts repositories backed by `@cloudflare/computer/artifacts`
56
64
  - `effect-cf/computer-workspace-host` - optional Durable Object host mixin, worker-shell backend wiring, and `WorkspaceServiceProxy`
65
+ - `effect-cf/sandbox` - Effect-native client for Cloudflare Sandbox containers: processes with log streams, files and watches, preview URLs, tunnels, terminals, bucket mounts, and backups
57
66
  - `Hyperdrive` - typed Hyperdrive binding helper for connection strings and optional Postgres SQL integration
58
67
  - `Images` - typed Cloudflare Images binding helper with transformation APIs and optional hosted image operations
59
68
  - `Email` - typed Cloudflare Email Service binding helper for `send_email` bindings, with limit validation and typed error codes
@@ -322,6 +331,75 @@ const stub = Sandboxes.byName("codex").rawUnsafe;
322
331
  See [`examples/containers/README.md`](../../examples/containers/README.md) for
323
332
  the corresponding Wrangler configuration and entrypoint responsibilities.
324
333
 
334
+ ## Sandbox Example
335
+
336
+ `effect-cf/sandbox` wraps the [Cloudflare Sandbox SDK](https://github.com/cloudflare/sandbox-sdk)
337
+ 1.0 API (`@cloudflare/sandbox@next`). The Sandbox Durable Object class remains
338
+ owned by `@cloudflare/sandbox`; `effect-cf` wraps the namespace binding used by
339
+ a calling Worker and loads the SDK lazily, so applications without the optional
340
+ peer are unaffected.
341
+
342
+ ```ts
343
+ import { Effect, Option } from "effect";
344
+ import { Worker } from "effect-cf";
345
+ import * as Sandbox from "effect-cf/sandbox";
346
+
347
+ export { Sandbox as SandboxDurableObject } from "@cloudflare/sandbox";
348
+
349
+ class Sandboxes extends Sandbox.Tag<Sandboxes>()("Sandboxes") {}
350
+
351
+ const SandboxesLive = Sandboxes.layer({ binding: "Sandbox" });
352
+
353
+ export default Worker.make(SandboxesLive, {
354
+ fetch: Effect.gen(function* () {
355
+ const request = yield* Worker.NativeRequest;
356
+
357
+ // Route preview-URL traffic before any other routing.
358
+ const proxied = yield* Sandbox.proxyToSandbox(request);
359
+ if (Option.isSome(proxied)) {
360
+ return proxied.value;
361
+ }
362
+
363
+ const sandbox = yield* Sandboxes.get("my-sandbox");
364
+ const handle = yield* sandbox.exec(["python3", "-c", "print(2 + 2)"]);
365
+ const output = yield* handle.outputText();
366
+
367
+ return new Response(output.stdout);
368
+ }),
369
+ });
370
+ ```
371
+
372
+ The instance client mirrors the complete `ISandbox` client contract plus the
373
+ lifecycle, port, tunnel, and backup operations the SDK client proxies to the
374
+ Sandbox Durable Object. `exec` returns a process handle whose `logs` is an
375
+ Effect `Stream` of typed log events; `watch` decodes the SDK's Server-Sent
376
+ Events into a `Stream` of file-watch events; `readFileStream` streams raw
377
+ bytes. Interruptible waits (`waitForExit`, `waitForLog`, `output`,
378
+ `waitForPort`) abort the underlying SDK call when the Effect is interrupted.
379
+ Failures carry a typed `SandboxOperationError` whose `cause` preserves the
380
+ SDK's rich error classes (`ProcessNotFoundError`, `PortNotExposedError`, and
381
+ friends) for inspection.
382
+
383
+ The corresponding `wrangler.jsonc` declares the container image and Durable
384
+ Object binding; note the SDK requires the exported Durable Object class to be
385
+ reachable under the configured `class_name`:
386
+
387
+ ```jsonc
388
+ {
389
+ "containers": [
390
+ {
391
+ "class_name": "SandboxDurableObject",
392
+ "image": "./Dockerfile",
393
+ "instance_type": "lite",
394
+ },
395
+ ],
396
+ "durable_objects": {
397
+ "bindings": [{ "class_name": "SandboxDurableObject", "name": "Sandbox" }],
398
+ },
399
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["SandboxDurableObject"] }],
400
+ }
401
+ ```
402
+
325
403
  ## Queue Example
326
404
 
327
405
  Queues define the message contract once. The same class is the producer service tag and the consumer Worker definition: use `YourQueue.layer({ binding })` to provide a Cloudflare producer binding from `WorkerEnvironment`, and use `.make(...)` for the consumer Worker entrypoint.
@@ -1,131 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { a as WorkerEnvironment, t as causeMessage } from "./ErrorMessage-BM0K-Nik.mjs";
3
- import { Context, Data, Effect, Layer, Predicate, Schema } from "effect";
4
- //#region src/Binding.ts
5
- var Binding_exports = /* @__PURE__ */ __exportAll({
6
- BindingNotFoundError: () => BindingNotFoundError,
7
- BindingValidationError: () => BindingValidationError,
8
- Service: () => Service,
9
- TypeId: () => TypeId,
10
- layer: () => layer$1
11
- });
12
- /** Internal type id marker used by binding helper services. */
13
- const TypeId = "~effect-cf/Binding";
14
- /** Error raised when a configured binding does not exist on `env`. */
15
- var BindingNotFoundError = class extends Data.TaggedError("BindingNotFoundError") {};
16
- /** Error raised when a binding exists but does not match the expected shape. */
17
- var BindingValidationError = class extends Data.TaggedError("BindingValidationError") {};
18
- const defaultExpected = "Cloudflare binding resource";
19
- const isPropertyTarget = (value) => Predicate.isObjectOrArray(value) || Predicate.isFunction(value);
20
- const getObjectName = (value) => {
21
- const tag = (() => {
22
- try {
23
- return Object.prototype.toString.call(value).slice(8, -1);
24
- } catch {
25
- return Predicate.isFunction(value) ? "function" : "object";
26
- }
27
- })();
28
- const constructorName = (() => {
29
- try {
30
- return "constructor" in value && Predicate.isFunction(value.constructor) && Predicate.isString(value.constructor.name) ? value.constructor.name : void 0;
31
- } catch {
32
- return;
33
- }
34
- })();
35
- if (tag !== "Object") return tag;
36
- if (constructorName !== void 0 && constructorName !== "" && constructorName !== "Object") return constructorName;
37
- return tag;
38
- };
39
- const propertyNames = (value) => {
40
- const names = /* @__PURE__ */ new Set();
41
- for (const target of [value, Object.getPrototypeOf(value)]) {
42
- if (target === null || target === Object.prototype || target === Function.prototype) continue;
43
- try {
44
- for (const name of Object.getOwnPropertyNames(target)) names.add(name);
45
- } catch {
46
- continue;
47
- }
48
- }
49
- return [...names].filter((name) => name !== "constructor").sort();
50
- };
51
- const isMethod = (value, name) => {
52
- try {
53
- return Predicate.hasProperty(value, name) && Predicate.isFunction(value[name]);
54
- } catch {
55
- return false;
56
- }
57
- };
58
- const describeActual = (value) => {
59
- if (value === null) return "null";
60
- if (!isPropertyTarget(value)) {
61
- if (Predicate.isString(value)) return "string";
62
- if (Predicate.isNumber(value)) return "number";
63
- if (Predicate.isBoolean(value)) return "boolean";
64
- if (Predicate.isBigInt(value)) return "bigint";
65
- if (Predicate.isSymbol(value)) return "symbol";
66
- return "undefined";
67
- }
68
- const names = propertyNames(value);
69
- const methods = names.filter((name) => isMethod(value, name));
70
- const properties = names.filter((name) => !methods.includes(name));
71
- const details = [methods.length > 0 ? `methods ${methods.join(", ")}` : void 0, properties.length > 0 ? `properties ${properties.join(", ")}` : void 0].filter((detail) => detail !== void 0);
72
- if (details.length === 0) return getObjectName(value);
73
- return `${getObjectName(value)} with ${details.join("; ")}`;
74
- };
75
- const getBinding = (env, binding, isResource, options) => Effect.gen(function* () {
76
- if (!isPropertyTarget(env)) {
77
- const actual = describeActual(env);
78
- return yield* Effect.fail(new BindingValidationError({
79
- binding,
80
- expected: "WorkerEnvironment object",
81
- actual,
82
- message: `Cloudflare binding "${binding}" failed validation. Expected WorkerEnvironment object; got ${actual}`
83
- }));
84
- }
85
- const resource = Predicate.hasProperty(env, binding) ? env[binding] : void 0;
86
- if (resource === void 0) return yield* Effect.fail(new BindingNotFoundError({
87
- binding,
88
- message: `Cloudflare binding "${binding}" was not found in WorkerEnvironment`
89
- }));
90
- if (!isResource(resource)) {
91
- const expected = options?.expected ?? defaultExpected;
92
- const actual = describeActual(resource);
93
- return yield* Effect.fail(new BindingValidationError({
94
- binding,
95
- expected,
96
- actual,
97
- message: `Cloudflare binding "${binding}" failed validation. Expected ${expected}; got ${actual}`
98
- }));
99
- }
100
- return resource;
101
- });
102
- /**
103
- * The overloads on {@link layer} and {@link Service} guarantee that
104
- * `Service = Resource` whenever `wrap` is absent, making the fallback cast
105
- * safe.
106
- */
107
- const makeBindingLayer = (tag, binding, isResource, wrap, options) => Layer.effect(tag, Effect.gen(function* () {
108
- const env = yield* WorkerEnvironment;
109
- const resource = yield* getBinding(env, binding, isResource, options);
110
- return wrap === void 0 ? resource : wrap(resource);
111
- }));
112
- function layer$1(tag, binding, isResource, wrap, options) {
113
- return makeBindingLayer(tag, binding, isResource, wrap, options);
114
- }
115
- const Service = () => {
116
- function makeService(id, binding, isResource, wrap, options) {
117
- const tag = Context.Service()(`effect-cf/Binding/${id}`);
118
- const serviceLayer = makeBindingLayer(tag, binding, isResource, wrap, options);
119
- return Object.assign(tag, {
120
- [TypeId]: TypeId,
121
- id,
122
- binding,
123
- layer: serviceLayer
124
- });
125
- }
126
- return makeService;
127
- };
128
- //#endregion
2
+ import { t as causeMessage } from "./ErrorMessage-BM0K-Nik.mjs";
3
+ import { r as layer$1 } from "./Binding-pfilNJmP.mjs";
4
+ import { Context, Data, Effect, Predicate, Schema } from "effect";
129
5
  //#region src/Artifacts.ts
130
6
  var Artifacts_exports = /* @__PURE__ */ __exportAll({
131
7
  ArtifactsOperationError: () => ArtifactsOperationError,
@@ -254,4 +130,4 @@ const Tag = () => (id) => {
254
130
  });
255
131
  };
256
132
  //#endregion
257
- export { layer$1 as a, Service as i, Artifacts_exports as n, Binding_exports as r, ArtifactsOperationError as t };
133
+ export { Artifacts_exports as n, ArtifactsOperationError as t };
@@ -0,0 +1,129 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { a as WorkerEnvironment } from "./ErrorMessage-BM0K-Nik.mjs";
3
+ import { Context, Data, Effect, Layer, Predicate } from "effect";
4
+ //#region src/Binding.ts
5
+ var Binding_exports = /* @__PURE__ */ __exportAll({
6
+ BindingNotFoundError: () => BindingNotFoundError,
7
+ BindingValidationError: () => BindingValidationError,
8
+ Service: () => Service,
9
+ TypeId: () => TypeId,
10
+ layer: () => layer
11
+ });
12
+ /** Internal type id marker used by binding helper services. */
13
+ const TypeId = "~effect-cf/Binding";
14
+ /** Error raised when a configured binding does not exist on `env`. */
15
+ var BindingNotFoundError = class extends Data.TaggedError("BindingNotFoundError") {};
16
+ /** Error raised when a binding exists but does not match the expected shape. */
17
+ var BindingValidationError = class extends Data.TaggedError("BindingValidationError") {};
18
+ const defaultExpected = "Cloudflare binding resource";
19
+ const isPropertyTarget = (value) => Predicate.isObjectOrArray(value) || Predicate.isFunction(value);
20
+ const getObjectName = (value) => {
21
+ const tag = (() => {
22
+ try {
23
+ return Object.prototype.toString.call(value).slice(8, -1);
24
+ } catch {
25
+ return Predicate.isFunction(value) ? "function" : "object";
26
+ }
27
+ })();
28
+ const constructorName = (() => {
29
+ try {
30
+ return "constructor" in value && Predicate.isFunction(value.constructor) && Predicate.isString(value.constructor.name) ? value.constructor.name : void 0;
31
+ } catch {
32
+ return;
33
+ }
34
+ })();
35
+ if (tag !== "Object") return tag;
36
+ if (constructorName !== void 0 && constructorName !== "" && constructorName !== "Object") return constructorName;
37
+ return tag;
38
+ };
39
+ const propertyNames = (value) => {
40
+ const names = /* @__PURE__ */ new Set();
41
+ for (const target of [value, Object.getPrototypeOf(value)]) {
42
+ if (target === null || target === Object.prototype || target === Function.prototype) continue;
43
+ try {
44
+ for (const name of Object.getOwnPropertyNames(target)) names.add(name);
45
+ } catch {
46
+ continue;
47
+ }
48
+ }
49
+ return [...names].filter((name) => name !== "constructor").sort();
50
+ };
51
+ const isMethod = (value, name) => {
52
+ try {
53
+ return Predicate.hasProperty(value, name) && Predicate.isFunction(value[name]);
54
+ } catch {
55
+ return false;
56
+ }
57
+ };
58
+ const describeActual = (value) => {
59
+ if (value === null) return "null";
60
+ if (!isPropertyTarget(value)) {
61
+ if (Predicate.isString(value)) return "string";
62
+ if (Predicate.isNumber(value)) return "number";
63
+ if (Predicate.isBoolean(value)) return "boolean";
64
+ if (Predicate.isBigInt(value)) return "bigint";
65
+ if (Predicate.isSymbol(value)) return "symbol";
66
+ return "undefined";
67
+ }
68
+ const names = propertyNames(value);
69
+ const methods = names.filter((name) => isMethod(value, name));
70
+ const properties = names.filter((name) => !methods.includes(name));
71
+ const details = [methods.length > 0 ? `methods ${methods.join(", ")}` : void 0, properties.length > 0 ? `properties ${properties.join(", ")}` : void 0].filter((detail) => detail !== void 0);
72
+ if (details.length === 0) return getObjectName(value);
73
+ return `${getObjectName(value)} with ${details.join("; ")}`;
74
+ };
75
+ const getBinding = (env, binding, isResource, options) => Effect.gen(function* () {
76
+ if (!isPropertyTarget(env)) {
77
+ const actual = describeActual(env);
78
+ return yield* Effect.fail(new BindingValidationError({
79
+ binding,
80
+ expected: "WorkerEnvironment object",
81
+ actual,
82
+ message: `Cloudflare binding "${binding}" failed validation. Expected WorkerEnvironment object; got ${actual}`
83
+ }));
84
+ }
85
+ const resource = Predicate.hasProperty(env, binding) ? env[binding] : void 0;
86
+ if (resource === void 0) return yield* Effect.fail(new BindingNotFoundError({
87
+ binding,
88
+ message: `Cloudflare binding "${binding}" was not found in WorkerEnvironment`
89
+ }));
90
+ if (!isResource(resource)) {
91
+ const expected = options?.expected ?? defaultExpected;
92
+ const actual = describeActual(resource);
93
+ return yield* Effect.fail(new BindingValidationError({
94
+ binding,
95
+ expected,
96
+ actual,
97
+ message: `Cloudflare binding "${binding}" failed validation. Expected ${expected}; got ${actual}`
98
+ }));
99
+ }
100
+ return resource;
101
+ });
102
+ /**
103
+ * The overloads on {@link layer} and {@link Service} guarantee that
104
+ * `Service = Resource` whenever `wrap` is absent, making the fallback cast
105
+ * safe.
106
+ */
107
+ const makeBindingLayer = (tag, binding, isResource, wrap, options) => Layer.effect(tag, Effect.gen(function* () {
108
+ const env = yield* WorkerEnvironment;
109
+ const resource = yield* getBinding(env, binding, isResource, options);
110
+ return wrap === void 0 ? resource : wrap(resource);
111
+ }));
112
+ function layer(tag, binding, isResource, wrap, options) {
113
+ return makeBindingLayer(tag, binding, isResource, wrap, options);
114
+ }
115
+ const Service = () => {
116
+ function makeService(id, binding, isResource, wrap, options) {
117
+ const tag = Context.Service()(`effect-cf/Binding/${id}`);
118
+ const serviceLayer = makeBindingLayer(tag, binding, isResource, wrap, options);
119
+ return Object.assign(tag, {
120
+ [TypeId]: TypeId,
121
+ id,
122
+ binding,
123
+ layer: serviceLayer
124
+ });
125
+ }
126
+ return makeService;
127
+ };
128
+ //#endregion
129
+ export { Service as n, layer as r, Binding_exports as t };
@@ -1,4 +1,4 @@
1
- import { t as ArtifactsOperationError } from "./Artifacts-rq3hUKQE.mjs";
1
+ import { t as ArtifactsOperationError } from "./Artifacts-CSIk1Oml.mjs";
2
2
  import { Effect, Predicate } from "effect";
3
3
  //#region src/ComputerArtifacts.ts
4
4
  const artifactsErrorDetails = (cause) => {
@@ -1,5 +1,5 @@
1
1
  import { ComputerArtifactsClient } from "./ComputerArtifacts.mjs";
2
- import { t as DurableObjectState } from "./DurableObjectState-OupNMMUl.mjs";
2
+ import { t as DurableObjectState } from "./DurableObjectState-D3k-WRnd.mjs";
3
3
  import { Context, Effect, Layer, Schema, Scope, Stream } from "effect";
4
4
  import { CommitView, GitClient, GitCloneOptions, GitCommitOptions, GitDiffOptions, GitFetchOptions, GitLogOptions, GitUpdateRefOptions } from "@cloudflare/computer/git";
5
5
  import { ExecSyncResult, RuntimeExecOptions, RuntimeGetOptions, RuntimeKillOptions, ShellValue, SkippedEntry, ThinkWorkspaceCompatibility, WorkspaceClient, WorkspaceRuntimeResult, WorkspaceRuntimeValue as WorkspaceRuntimeValue$1 } from "@cloudflare/computer";
@@ -1,4 +1,4 @@
1
- import { t as DurableObjectState } from "./DurableObjectState-DnnLoHV-.mjs";
1
+ import { t as DurableObjectState } from "./DurableObjectState-CepagPkX.mjs";
2
2
  import { fromClient } from "./ComputerArtifacts.mjs";
3
3
  import { t as lookup } from "./ComputerWorkspaceHostRegistry-CY5Fv4Ng.mjs";
4
4
  import { Context, Effect, Layer, Predicate, Schema, Stream } from "effect";
@@ -0,0 +1,155 @@
1
+ import { c as WorkerEnvironment, r as BindingValidationError, t as BindingNotFoundError } from "./Binding-xJJJPmiD.mjs";
2
+ import { Context, Effect, Layer, Schema } from "effect";
3
+ declare namespace ContainerNamespace_d_exports {
4
+ export { ContainerInstanceClient, ContainerNamespace, ContainerNamespaceClient, ContainerNamespaceDefinition, ContainerNamespaceResource, ContainerNamespaceService, ContainerNamespaceStaticClient, ContainerOperationError, ContainerReadinessOptions, ContainerSignal, ContainerStartAndWaitForPortsOptions, ContainerStartOptions, ContainerState, ContainerStopSignal, ContainerStub, ContainerStubOf, ContainerWaitOptions, LayerOptions, Tag, TagClass, isContainerNamespaceResource, layer, make, makeClient };
5
+ }
6
+ /** Named signal accepted by Cloudflare Container instances. */
7
+ type ContainerSignal = "SIGKILL" | "SIGINT" | "SIGTERM";
8
+ /** Named or numeric signal accepted by Container `stop()`. */
9
+ type ContainerStopSignal = ContainerSignal | number;
10
+ /** Per-instance Container startup configuration. */
11
+ interface ContainerStartOptions {
12
+ readonly envVars?: Record<string, string>;
13
+ readonly entrypoint?: Array<string>;
14
+ readonly enableInternet?: boolean;
15
+ readonly labels?: Record<string, string>;
16
+ }
17
+ /** Serializable retry settings used while starting a Container. */
18
+ interface ContainerWaitOptions {
19
+ readonly portToCheck: number;
20
+ readonly retries?: number;
21
+ readonly waitInterval?: number;
22
+ }
23
+ /** Serializable timeout settings used while waiting for Container ports. */
24
+ interface ContainerReadinessOptions {
25
+ readonly instanceGetTimeoutMS?: number;
26
+ readonly portReadyTimeoutMS?: number;
27
+ readonly waitInterval?: number;
28
+ }
29
+ /** Options for starting a Container and waiting for its ports. */
30
+ interface ContainerStartAndWaitForPortsOptions {
31
+ readonly startOptions?: ContainerStartOptions;
32
+ readonly ports?: number | Array<number>;
33
+ readonly cancellationOptions?: ContainerReadinessOptions;
34
+ }
35
+ /** Serializable state reported by a Cloudflare Container instance. */
36
+ declare const ContainerState: Schema.Union<readonly [Schema.Struct<{
37
+ readonly lastChange: Schema.Finite;
38
+ readonly status: Schema.Literals<readonly ["running", "stopping", "stopped", "healthy"]>;
39
+ }>, Schema.Struct<{
40
+ readonly lastChange: Schema.Finite;
41
+ readonly status: Schema.Literal<"stopped_with_code">;
42
+ readonly exitCode: Schema.optionalKey<Schema.Int>;
43
+ }>]>;
44
+ type ContainerState = typeof ContainerState.Type;
45
+ /**
46
+ * Native Container stub shape exposed by a Container namespace binding.
47
+ *
48
+ * This is structural so using Container bindings does not make
49
+ * `@cloudflare/containers` a runtime dependency of effect-cf.
50
+ */
51
+ interface ContainerStub {
52
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
53
+ getState(): Promise<ContainerState>;
54
+ start(options?: ContainerStartOptions, waitOptions?: ContainerWaitOptions): Promise<void>;
55
+ startAndWaitForPorts(options?: ContainerStartAndWaitForPortsOptions): Promise<void>;
56
+ waitForPort(options: ContainerWaitOptions): Promise<number>;
57
+ stop(signal?: ContainerStopSignal): Promise<void>;
58
+ destroy(): Promise<void>;
59
+ setAllowedHosts(hosts: Array<string>): Promise<void>;
60
+ setDeniedHosts(hosts: Array<string>): Promise<void>;
61
+ allowHost(hostname: string): Promise<void>;
62
+ denyHost(hostname: string): Promise<void>;
63
+ removeAllowedHost(hostname: string): Promise<void>;
64
+ removeDeniedHost(hostname: string): Promise<void>;
65
+ }
66
+ /** Native Container namespace binding shape. */
67
+ interface ContainerNamespaceResource {
68
+ getByName(name: string, options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions): ContainerStub;
69
+ }
70
+ /** Exact native stub type produced by a Container namespace binding. */
71
+ type ContainerStubOf<Namespace extends ContainerNamespaceResource> = ReturnType<Namespace["getByName"]>;
72
+ /** Container namespace binding metadata. */
73
+ interface ContainerNamespaceDefinition {
74
+ /** Binding name as configured in `wrangler.jsonc`. */
75
+ readonly binding: string;
76
+ }
77
+ declare const ContainerOperationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
78
+ readonly _tag: "ContainerOperationError";
79
+ } & Readonly<A>;
80
+ /** Failure raised when invoking a named Container operation. */
81
+ declare class ContainerOperationError extends ContainerOperationError_base<{
82
+ readonly binding: string;
83
+ readonly instance: string;
84
+ readonly operation: string;
85
+ readonly cause: unknown;
86
+ }> {
87
+ get message(): string;
88
+ }
89
+ /** Effect-wrapped client for one named Container instance. */
90
+ interface ContainerInstanceClient<Stub extends ContainerStub = ContainerStub> {
91
+ readonly rawUnsafe: Effect.Effect<Stub, ContainerOperationError>;
92
+ readonly state: Effect.Effect<ContainerState, ContainerOperationError>;
93
+ readonly fetch: (input: RequestInfo | URL, init?: RequestInit) => Effect.Effect<Response, ContainerOperationError>;
94
+ readonly start: (options?: ContainerStartOptions, waitOptions?: ContainerWaitOptions) => Effect.Effect<void, ContainerOperationError>;
95
+ readonly startAndWaitForPorts: (options?: ContainerStartAndWaitForPortsOptions) => Effect.Effect<void, ContainerOperationError>;
96
+ readonly waitForPort: (options: ContainerWaitOptions) => Effect.Effect<number, ContainerOperationError>;
97
+ readonly stop: (signal?: ContainerStopSignal) => Effect.Effect<void, ContainerOperationError>;
98
+ readonly destroy: Effect.Effect<void, ContainerOperationError>;
99
+ readonly setAllowedHosts: (hosts: ReadonlyArray<string>) => Effect.Effect<void, ContainerOperationError>;
100
+ readonly setDeniedHosts: (hosts: ReadonlyArray<string>) => Effect.Effect<void, ContainerOperationError>;
101
+ readonly allowHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
102
+ readonly denyHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
103
+ readonly removeAllowedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
104
+ readonly removeDeniedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
105
+ }
106
+ /** Effect client for a Container namespace binding. */
107
+ interface ContainerNamespaceClient<Namespace extends ContainerNamespaceResource = ContainerNamespaceResource> {
108
+ readonly definition: ContainerNamespaceDefinition;
109
+ readonly getByName: (name: string, options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions) => Effect.Effect<ContainerInstanceClient<ContainerStubOf<Namespace>>, ContainerOperationError>;
110
+ readonly byName: (name: string, options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions) => ContainerInstanceClient<ContainerStubOf<Namespace>>;
111
+ readonly rawUnsafe: Effect.Effect<Namespace>;
112
+ }
113
+ type ContainerNamespaceStaticClient<R, Namespace extends ContainerNamespaceResource = ContainerNamespaceResource> = {
114
+ readonly getByName: (name: string, options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions) => Effect.Effect<ContainerInstanceClient<ContainerStubOf<Namespace>>, ContainerOperationError, R>;
115
+ readonly byName: (name: string, options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions) => {
116
+ readonly state: Effect.Effect<ContainerState, ContainerOperationError, R>;
117
+ readonly fetch: (input: RequestInfo | URL, init?: RequestInit) => Effect.Effect<Response, ContainerOperationError, R>;
118
+ readonly start: (options?: ContainerStartOptions, waitOptions?: ContainerWaitOptions) => Effect.Effect<void, ContainerOperationError, R>;
119
+ readonly startAndWaitForPorts: (options?: ContainerStartAndWaitForPortsOptions) => Effect.Effect<void, ContainerOperationError, R>;
120
+ readonly waitForPort: (options: ContainerWaitOptions) => Effect.Effect<number, ContainerOperationError, R>;
121
+ readonly stop: (signal?: ContainerStopSignal) => Effect.Effect<void, ContainerOperationError, R>;
122
+ readonly destroy: Effect.Effect<void, ContainerOperationError, R>;
123
+ readonly setAllowedHosts: (hosts: ReadonlyArray<string>) => Effect.Effect<void, ContainerOperationError, R>;
124
+ readonly setDeniedHosts: (hosts: ReadonlyArray<string>) => Effect.Effect<void, ContainerOperationError, R>;
125
+ readonly allowHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
126
+ readonly denyHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
127
+ readonly removeAllowedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
128
+ readonly removeDeniedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
129
+ readonly rawUnsafe: Effect.Effect<ContainerStubOf<Namespace>, ContainerOperationError, R>;
130
+ };
131
+ readonly rawUnsafe: Effect.Effect<Namespace, never, R>;
132
+ };
133
+ type LayerOptions = {
134
+ readonly binding: string;
135
+ };
136
+ interface TagClass<Self, Id extends string, Namespace extends ContainerNamespaceResource = ContainerNamespaceResource> extends Context.ServiceClass<Self, `effect-cf/Container/${Id}`, ContainerNamespaceClient<Namespace>>, ContainerNamespaceStaticClient<Self, Namespace> {
137
+ readonly id: Id;
138
+ readonly layer: (options: LayerOptions) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
139
+ }
140
+ declare const isContainerNamespaceResource: <Candidate>(value: Candidate) => value is Candidate & ContainerNamespaceResource;
141
+ declare const makeClient: (definition: ContainerNamespaceDefinition) => <Namespace extends ContainerNamespaceResource>(namespace: Namespace) => ContainerNamespaceClient<Namespace>;
142
+ declare const layer: <Self, Namespace extends ContainerNamespaceResource>(tag: Context.Service<Self, ContainerNamespaceClient<Namespace>>, definition: ContainerNamespaceDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
143
+ declare const make: <Id extends string>(id: Id) => TagClass<ContainerNamespaceService<Id>, Id, ContainerNamespaceResource>;
144
+ declare const ContainerNamespaceServiceTypeId: unique symbol;
145
+ /** Nominal service marker for Container namespaces created with {@link make}. */
146
+ interface ContainerNamespaceService<Id extends string> {
147
+ readonly [ContainerNamespaceServiceTypeId]: {
148
+ readonly id: Id;
149
+ };
150
+ }
151
+ /** Creates a typed Effect service for a Cloudflare Container namespace. */
152
+ declare const Tag: <Self, Namespace extends ContainerNamespaceResource = ContainerNamespaceResource>() => <Id extends string>(id: Id) => TagClass<Self, Id, Namespace>;
153
+ declare const ContainerNamespace: typeof Tag;
154
+ //#endregion
155
+ export { ContainerStartOptions as n, ContainerStopSignal as r, ContainerNamespace_d_exports as t };
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { t as causeMessage } from "./ErrorMessage-BM0K-Nik.mjs";
3
- import { Cause, Context, Data, Effect, Exit, Option, Predicate, Result, Schema } from "effect";
3
+ import { Cause, Clock, Context, Data, Duration, Effect, Exit, Option, Predicate, Result, Schema } from "effect";
4
4
  //#region src/DurableObjectStorage.ts
5
5
  var DurableObjectStorage_exports = /* @__PURE__ */ __exportAll({
6
6
  StorageOperationError: () => StorageOperationError,
@@ -24,6 +24,11 @@ const tryStoragePromise = (operation, evaluate) => Effect.tryPromise({
24
24
  try: evaluate,
25
25
  catch: (cause) => storageError(operation, cause)
26
26
  });
27
+ const setAlarmAfter = Effect.fn("DurableObjectStorage.setAlarmAfter")(function* (operation, setAlarm, delay, options) {
28
+ const now = yield* Clock.currentTimeMillis;
29
+ const delayMillis = Math.ceil(Duration.toMillis(delay));
30
+ yield* tryStoragePromise(operation, () => setAlarm(now + delayMillis, options));
31
+ });
27
32
  const fromSqlCursor = (cursor) => ({
28
33
  next: () => tryStorageSync("sql.next", () => cursor.next()),
29
34
  toArray: () => tryStorageSync("sql.toArray", () => cursor.toArray()),
@@ -85,6 +90,7 @@ const fromDurableObjectTransaction = (txn) => ({
85
90
  rollback: () => tryStorageSync("transaction.rollback", () => txn.rollback()),
86
91
  getAlarm: (options) => tryStoragePromise("transaction.getAlarm", () => txn.getAlarm(options)),
87
92
  setAlarm: (scheduledTime, options) => tryStoragePromise("transaction.setAlarm", () => txn.setAlarm(scheduledTime, options)),
93
+ setAlarmAfter: (delay, options) => setAlarmAfter("transaction.setAlarm", (scheduledTime, options) => txn.setAlarm(scheduledTime, options), delay, options),
88
94
  deleteAlarm: (options) => tryStoragePromise("transaction.deleteAlarm", () => txn.deleteAlarm(options))
89
95
  });
90
96
  /**
@@ -97,6 +103,7 @@ const fromDurableObjectStorage = (storage) => ({
97
103
  deleteAll: (options) => tryStoragePromise("deleteAll", () => storage.deleteAll(options)),
98
104
  getAlarm: (options) => tryStoragePromise("getAlarm", () => storage.getAlarm(options)),
99
105
  setAlarm: (scheduledTime, options) => tryStoragePromise("setAlarm", () => storage.setAlarm(scheduledTime, options)),
106
+ setAlarmAfter: (delay, options) => setAlarmAfter("setAlarm", (scheduledTime, options) => storage.setAlarm(scheduledTime, options), delay, options),
100
107
  deleteAlarm: (options) => tryStoragePromise("deleteAlarm", () => storage.deleteAlarm(options)),
101
108
  transactionSync: (closure) => Effect.context().pipe(Effect.flatMap((context) => Effect.suspend(() => {
102
109
  let aborted;
@@ -1,5 +1,5 @@
1
1
  import { c as WorkerEnvironment, n as BindingService, r as BindingValidationError, s as WorkerEnv, t as BindingNotFoundError } from "./Binding-xJJJPmiD.mjs";
2
- import { Cause, Context, Effect, Layer, Option, Predicate, Schema, Scope } from "effect";
2
+ import { Cause, Context, Duration, Effect, Layer, Option, Predicate, Schema, Scope } from "effect";
3
3
  import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
4
4
  import { RpcStub, RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
5
5
  declare namespace DurableObjectStorage_d_exports {
@@ -84,6 +84,8 @@ interface DurableObjectTransaction {
84
84
  rollback(): StorageEffect<void>;
85
85
  getAlarm(options?: globalThis.DurableObjectGetAlarmOptions): StorageEffect<number | null>;
86
86
  setAlarm(scheduledTime: number | Date, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
87
+ /** Schedules the alarm after a delay measured by the Effect clock. */
88
+ setAlarmAfter(delay: Duration.Input, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
87
89
  deleteAlarm(options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
88
90
  }
89
91
  /**
@@ -110,6 +112,8 @@ interface DurableObjectStorage {
110
112
  deleteAll(options?: globalThis.DurableObjectPutOptions): StorageEffect<void>;
111
113
  getAlarm(options?: globalThis.DurableObjectGetAlarmOptions): StorageEffect<number | null>;
112
114
  setAlarm(scheduledTime: number | Date, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
115
+ /** Schedules the alarm after a delay measured by the Effect clock. */
116
+ setAlarmAfter(delay: Duration.Input, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
113
117
  deleteAlarm(options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
114
118
  /**
115
119
  * SQLite-backed Durable Object storage only.