effect-cf 0.29.0 → 0.31.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,25 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.31.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#116](https://github.com/danieljvdm/effect-cf/pull/116) [`98d343b`](https://github.com/danieljvdm/effect-cf/commit/98d343ba7d5a427789ec8e7fc030668987eab1ea) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Move the optional `effect-cf/vitest` peer from `@cloudflare/vitest-pool-workers` to `@cloudflare/vitest-plugin` `^1.0.0`. The package API is unchanged: keep using `cloudflareTest()` and `cloudflare:test`.
8
+
9
+ - [#118](https://github.com/danieljvdm/effect-cf/pull/118) [`80730a8`](https://github.com/danieljvdm/effect-cf/commit/80730a82b26728e824ba6e4ed8bdbe290428baa6) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Pin the Cloudflare Workers runtime stack to workerd `1.20260820.1` (`@cloudflare/workers-types@^5.20260820.1`, wrangler `4.125.0`, recommended `compatibility_date` `2026-08-20`).
10
+
11
+ Images gains text rasterization (`text` / `process` text input), hosted `createDirectUpload` and `signedUrl`, and `response(options?)` for extra headers. Workflow instances gain `delete`, optional `terminate({ rollback })`, and `create`/`createBatch` `locationHint` via the updated types. `Worker.WorkerContext` now wraps the non-experimental `ExecutionContext.abort`.
12
+
13
+ ## 0.30.0
14
+
15
+ ### Minor Changes
16
+
17
+ - [#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.
18
+
19
+ - [#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).
20
+
21
+ `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.
22
+
3
23
  ## 0.29.0
4
24
 
5
25
  ### Minor Changes
package/README.md CHANGED
@@ -4,7 +4,8 @@ Effect-native Cloudflare primitives for Workers, Durable Objects, Containers, bi
4
4
 
5
5
  ## Install
6
6
 
7
- `effect-cf` targets Effect `^4.0.0-beta.105`.
7
+ `effect-cf` targets Effect `^4.0.0-beta.105` and Cloudflare workerd `1.20260820.1`
8
+ (`@cloudflare/workers-types@5.20260820.1`, recommended `compatibility_date` `2026-08-20`).
8
9
 
9
10
  ```bash
10
11
  bun add effect-cf "effect@^4.0.0-beta.105"
@@ -33,6 +34,14 @@ Workspace Git operations additionally require `@platformatic/vfs`,
33
34
  bun add "@platformatic/vfs@^0.4.0"
34
35
  ```
35
36
 
37
+ The Sandbox integration is an optional peer. Install the `@next` release line
38
+ of `@cloudflare/sandbox` (the Sandbox SDK 1.0 API) only in applications that
39
+ import `effect-cf/sandbox`:
40
+
41
+ ```bash
42
+ bun add "@cloudflare/sandbox@next"
43
+ ```
44
+
36
45
  ## Goal
37
46
 
38
47
  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 +63,7 @@ Runtime creation belongs at Cloudflare entrypoints, not inside binding helpers.
54
63
  - `effect-cf/computer-workspace` - scoped Effect wrappers for the complete Cloudflare Computer filesystem, Git, runtime, Assets, Artifacts, and Think-compatible client surfaces
55
64
  - `effect-cf/computer-artifacts` - session-isolated Artifacts repositories backed by `@cloudflare/computer/artifacts`
56
65
  - `effect-cf/computer-workspace-host` - optional Durable Object host mixin, worker-shell backend wiring, and `WorkspaceServiceProxy`
66
+ - `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
67
  - `Hyperdrive` - typed Hyperdrive binding helper for connection strings and optional Postgres SQL integration
58
68
  - `Images` - typed Cloudflare Images binding helper with transformation APIs and optional hosted image operations
59
69
  - `Email` - typed Cloudflare Email Service binding helper for `send_email` bindings, with limit validation and typed error codes
@@ -63,12 +73,12 @@ Runtime creation belongs at Cloudflare entrypoints, not inside binding helpers.
63
73
  - `Rpc` - Cloudflare RPC type helpers and scoped disposal utilities
64
74
  - `WebTransport` - truthful WebTransport/HTTP-3 boundary: typed runtime capabilities and decoded inbound protocol metadata
65
75
  - `WorkerConfig` - Effect `Config` helpers backed by Cloudflare `env`
66
- - `effect-cf/vitest` - Effect-native runners and scoped test helpers for Cloudflare's Vitest Workers pool
76
+ - `effect-cf/vitest` - Effect-native runners and scoped test helpers for Cloudflare's Workers Vitest plugin
67
77
 
68
- ## Vitest Workers Pool
78
+ ## Vitest Workers Plugin
69
79
 
70
- Install and configure a `^0.21.3` release of
71
- [`@cloudflare/vitest-pool-workers`](https://github.com/cloudflare/workers-sdk/tree/main/packages/vitest-pool-workers#readme),
80
+ Install and configure a `^1.0.0` release of
81
+ [`@cloudflare/vitest-plugin`](https://github.com/cloudflare/workers-sdk/tree/main/packages/vitest-plugin#readme),
72
82
  then import the test-only helpers from `effect-cf/vitest`. The `fetch` runner
73
83
  constructs the Worker with the current test environment and waits for all
74
84
  `waitUntil` work before completing the Effect.
@@ -100,7 +110,7 @@ it.effect("reads Wrangler vars", () =>
100
110
  ```
101
111
 
102
112
  Queue consumers can be invoked with typed message bodies while retaining the
103
- pool's acknowledgement and retry result:
113
+ plugin's acknowledgement and retry result:
104
114
 
105
115
  ```ts
106
116
  it.effect("acknowledges a job", () =>
@@ -121,7 +131,7 @@ it.effect("acknowledges a job", () =>
121
131
 
122
132
  Scheduled handlers and Pages Functions use the same lifecycle behavior: their
123
133
  Effects complete only after the event's `waitUntil` work has settled. Pages
124
- tests must configure the `ASSETS` binding required by Pool Workers.
134
+ tests must configure the `ASSETS` binding required by the Workers Vitest plugin.
125
135
 
126
136
  ```ts
127
137
  it.effect("runs the cron handler", () =>
@@ -322,6 +332,75 @@ const stub = Sandboxes.byName("codex").rawUnsafe;
322
332
  See [`examples/containers/README.md`](../../examples/containers/README.md) for
323
333
  the corresponding Wrangler configuration and entrypoint responsibilities.
324
334
 
335
+ ## Sandbox Example
336
+
337
+ `effect-cf/sandbox` wraps the [Cloudflare Sandbox SDK](https://github.com/cloudflare/sandbox-sdk)
338
+ 1.0 API (`@cloudflare/sandbox@next`). The Sandbox Durable Object class remains
339
+ owned by `@cloudflare/sandbox`; `effect-cf` wraps the namespace binding used by
340
+ a calling Worker and loads the SDK lazily, so applications without the optional
341
+ peer are unaffected.
342
+
343
+ ```ts
344
+ import { Effect, Option } from "effect";
345
+ import { Worker } from "effect-cf";
346
+ import * as Sandbox from "effect-cf/sandbox";
347
+
348
+ export { Sandbox as SandboxDurableObject } from "@cloudflare/sandbox";
349
+
350
+ class Sandboxes extends Sandbox.Tag<Sandboxes>()("Sandboxes") {}
351
+
352
+ const SandboxesLive = Sandboxes.layer({ binding: "Sandbox" });
353
+
354
+ export default Worker.make(SandboxesLive, {
355
+ fetch: Effect.gen(function* () {
356
+ const request = yield* Worker.NativeRequest;
357
+
358
+ // Route preview-URL traffic before any other routing.
359
+ const proxied = yield* Sandbox.proxyToSandbox(request);
360
+ if (Option.isSome(proxied)) {
361
+ return proxied.value;
362
+ }
363
+
364
+ const sandbox = yield* Sandboxes.get("my-sandbox");
365
+ const handle = yield* sandbox.exec(["python3", "-c", "print(2 + 2)"]);
366
+ const output = yield* handle.outputText();
367
+
368
+ return new Response(output.stdout);
369
+ }),
370
+ });
371
+ ```
372
+
373
+ The instance client mirrors the complete `ISandbox` client contract plus the
374
+ lifecycle, port, tunnel, and backup operations the SDK client proxies to the
375
+ Sandbox Durable Object. `exec` returns a process handle whose `logs` is an
376
+ Effect `Stream` of typed log events; `watch` decodes the SDK's Server-Sent
377
+ Events into a `Stream` of file-watch events; `readFileStream` streams raw
378
+ bytes. Interruptible waits (`waitForExit`, `waitForLog`, `output`,
379
+ `waitForPort`) abort the underlying SDK call when the Effect is interrupted.
380
+ Failures carry a typed `SandboxOperationError` whose `cause` preserves the
381
+ SDK's rich error classes (`ProcessNotFoundError`, `PortNotExposedError`, and
382
+ friends) for inspection.
383
+
384
+ The corresponding `wrangler.jsonc` declares the container image and Durable
385
+ Object binding; note the SDK requires the exported Durable Object class to be
386
+ reachable under the configured `class_name`:
387
+
388
+ ```jsonc
389
+ {
390
+ "containers": [
391
+ {
392
+ "class_name": "SandboxDurableObject",
393
+ "image": "./Dockerfile",
394
+ "instance_type": "lite",
395
+ },
396
+ ],
397
+ "durable_objects": {
398
+ "bindings": [{ "class_name": "SandboxDurableObject", "name": "Sandbox" }],
399
+ },
400
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["SandboxDurableObject"] }],
401
+ }
402
+ ```
403
+
325
404
  ## Queue Example
326
405
 
327
406
  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.
@@ -641,10 +720,26 @@ export const resizeAvatar = (image: Images.ImageInputValue) =>
641
720
  },
642
721
  );
643
722
 
644
- return yield* result.response;
723
+ return yield* result.response();
645
724
  });
646
725
  ```
647
726
 
727
+ ## Cloudflare runtime pin
728
+
729
+ `effect-cf` develops and tests against a fixed Cloudflare Workers runtime stack. Keep consumer
730
+ `compatibility_date` and optional `@cloudflare/workers-types` peers at or above this pin:
731
+
732
+ | Pin | Version |
733
+ | ------------------------------------------- | -------------- |
734
+ | workerd | `1.20260820.1` |
735
+ | `@cloudflare/workers-types` (optional peer) | `5.20260820.1` |
736
+ | wrangler (repo catalog / examples) | `4.125.0` |
737
+ | Recommended `compatibility_date` | `2026-08-20` |
738
+
739
+ The date in the `workerd` / `@cloudflare/workers-types` version is the API surface this package wraps.
740
+ Repo catalog pins live in the root `package.json` `catalog` field (`@cloudflare/workers-types`, `wrangler`).
741
+ `wrangler@4.125.0` pulls `workerd@1.20260820.1` and peers `@cloudflare/workers-types@^5.20260820.1`.
742
+
648
743
  ## Email Example
649
744
 
650
745
  Email tags expose Cloudflare Email Service `send_email` bindings as Effect-wrapped `send(...)` operations. Structured messages are validated against documented Email Sending limits before reaching the binding, and Cloudflare's `E_*` error codes are surfaced on `EmailOperationError.code`.
@@ -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-DVIgf0sA.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-CxS5Y2hf.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 };