effect-cf 0.6.0 → 0.8.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,21 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#22](https://github.com/danieljvdm/effect-cf/pull/22) [`24c27ee`](https://github.com/danieljvdm/effect-cf/commit/24c27ee2884aacc72ab51ab5a208b795e1fa9738) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Add Effect-native R2 bucket, Hyperdrive, and Cloudflare Images binding tags, including optional hosted Images operations, ArrayBuffer image inputs, and an optional Hyperdrive Postgres SQL layer integration.
8
+
9
+ - [#22](https://github.com/danieljvdm/effect-cf/pull/22) [`24c27ee`](https://github.com/danieljvdm/effect-cf/commit/24c27ee2884aacc72ab51ab5a208b795e1fa9738) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Remove non-canonical static operation helpers from existing KV and D1 resource bindings. Use yielded binding services for resource operations; D1 keeps `sqlLayer()`.
10
+
11
+ ## 0.7.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [#20](https://github.com/danieljvdm/effect-cf/pull/20) [`f57a59c`](https://github.com/danieljvdm/effect-cf/commit/f57a59c14d317438348b36ac0341dc921fe72be2) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Tighten binding APIs around Cloudflare RPC and the single-tag KV model. `rpc` now exposes the raw Cloudflare RPC result, while `call` and `scopedCall` resolve and decode definition-backed success values. Durable Object static direct helpers now keep the namespace layer requirement in their effect environment, and the old concrete `Kv.make` / `Kv.Service` constructors have been removed in favor of `Kv.Tag(...).layer({ binding })`.
16
+
17
+ - [#19](https://github.com/danieljvdm/effect-cf/pull/19) [`37b4883`](https://github.com/danieljvdm/effect-cf/commit/37b4883de9790df151ddbb16f2fd432b2d4348b5) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Replace separate binding classes with a single exported tag class API for Queues, Workflows, KV namespaces, Worker service bindings, and Durable Object namespaces. These tags now expose `layer({ binding })` directly, consumers use `const service = yield* Service`, and the old definition `.Binding(...)` / `.binding(...)` / `.Namespace(...)` / `.namespace(...)` helpers have been removed.
18
+
3
19
  ## 0.6.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -32,20 +32,22 @@ Runtime creation belongs at Cloudflare entrypoints, not inside binding helpers.
32
32
  - `DurableObjectWebSocket` - WebSocket upgrade helpers for Durable Objects
33
33
  - `Kv` - typed KV namespace helper
34
34
  - `D1` - typed D1 database binding helper with an `@effect/sql-d1` backed SQL layer
35
- - `Queue` / `QueueBinding` - typed Queue producer bindings and consumer handlers
36
- - `Workflow` / `WorkflowBinding` - typed Workflow entrypoints, steps, and starter bindings
35
+ - `R2` - typed R2 bucket binding helper with Effect-wrapped object and multipart operations
36
+ - `Hyperdrive` - typed Hyperdrive binding helper for connection strings and optional Postgres SQL integration
37
+ - `Images` - typed Cloudflare Images binding helper with transformation APIs and optional hosted image operations
38
+ - `Queue` - typed Queue producer/consumer tags plus client and error types
39
+ - `Workflow` - typed Workflow entrypoints, steps, starter clients, and instance types
37
40
  - `Rpc` - Cloudflare RPC type helpers and scoped disposal utilities
38
41
  - `WorkerConfig` - Effect `Config` helpers backed by Cloudflare `env`
39
42
 
40
43
  ## Worker Example
41
44
 
42
45
  ```ts
43
- import { HttpServerResponse } from "effect";
46
+ import { Effect, Layer } from "effect";
47
+ import { HttpServerResponse } from "effect/unstable/http";
44
48
  import { Worker } from "effect-cf";
45
49
 
46
- export default Worker.make({
47
- fetch: () => HttpServerResponse.text("ok"),
48
- });
50
+ export default Worker.make(Layer.empty, Effect.succeed(HttpServerResponse.text("ok")));
49
51
  ```
50
52
 
51
53
  ## Durable Object Example
@@ -70,27 +72,30 @@ export const CounterDurableObject = Counter.make(Layer.empty, {
70
72
  }),
71
73
  },
72
74
  });
75
+
76
+ export const CounterLayer = Counter.layer({ binding: "COUNTER" });
77
+
78
+ export const readCounter = Effect.gen(function* () {
79
+ const counters = yield* Counter;
80
+ return yield* counters.byName("home").get();
81
+ });
73
82
  ```
74
83
 
75
- Define Wrangler bindings and migrations in the consuming application.
84
+ Define Wrangler bindings and migrations in the consuming application. Durable Object namespace bindings are provided with `YourObject.layer({ binding })`, and consumers use `const namespace = yield* YourObject`.
76
85
 
77
86
  ## Queue Example
78
87
 
79
- Queues use the same definition + binding split as Workers and Durable Objects: define the message contract once, use `.binding(...)` or `.Binding(...)` to produce from any runtime with `WorkerEnvironment`, and use `.make(...)` for the consumer Worker entrypoint.
88
+ 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.
80
89
 
81
90
  ```ts
82
91
  import { Effect, Layer, Schema as S } from "effect";
83
92
  import { Queue } from "effect-cf";
84
93
 
85
- class AvatarJobs extends Queue.Tag<AvatarJobs>()("AvatarJobs", {
94
+ class AvatarQueue extends Queue.Tag<AvatarQueue>()("AvatarQueue", {
86
95
  message: S.Struct({ userId: S.String, imageKey: S.String }),
87
96
  }) {}
88
97
 
89
- export const AvatarQueue = AvatarJobs.binding("AvatarQueue", {
90
- binding: "AVATAR_QUEUE",
91
- });
92
-
93
- export const AvatarQueueConsumer = AvatarJobs.make(Layer.empty, {
98
+ export const AvatarQueueConsumer = AvatarQueue.make(Layer.empty, {
94
99
  queue: (batch) =>
95
100
  Effect.gen(function* () {
96
101
  for (const message of batch.messages) {
@@ -100,6 +105,8 @@ export const AvatarQueueConsumer = AvatarJobs.make(Layer.empty, {
100
105
  }),
101
106
  });
102
107
 
108
+ export const AvatarQueueLayer = AvatarQueue.layer({ binding: "AVATAR_QUEUE" });
109
+
103
110
  export const enqueueAvatar = (userId: string, imageKey: string) =>
104
111
  Effect.gen(function* () {
105
112
  const queue = yield* AvatarQueue;
@@ -107,10 +114,98 @@ export const enqueueAvatar = (userId: string, imageKey: string) =>
107
114
  });
108
115
  ```
109
116
 
110
- Producers should usually use `const queue = yield* AvatarQueue` and then call `queue.send(...)`, `queue.sendBatch(...)`, or `queue.metrics()`. The static `AvatarQueue.send(...)` helpers remain available for compatibility and concise one-off calls.
117
+ Producers should usually use `const queue = yield* AvatarQueue` and then call `queue.send(...)`, `queue.sendBatch(...)`, or `queue.metrics()`. The static `AvatarQueue.send(...)` helpers remain available for concise one-off calls.
111
118
 
112
119
  Queue handlers run inline failures through Cloudflare's normal retry path. If background work scheduled with `WorkerContext.waitUntil(...)` should also make the batch retry, use `WorkerContext.waitUntilPropagating(...)` or `waitUntil(..., { mode: "propagate" })`; the default `waitUntil` mode observes and logs failures without rejecting the native `waitUntil` promise.
113
120
 
121
+ ## R2 Example
122
+
123
+ R2 bucket tags expose Cloudflare object operations as Effects and map nullable reads to `Option`.
124
+
125
+ ```ts
126
+ import { Effect, Layer, Option } from "effect";
127
+ import { R2 } from "effect-cf";
128
+
129
+ class ArtifactBucket extends R2.Tag<ArtifactBucket>()("ArtifactBucket") {}
130
+
131
+ export const ArtifactBucketLayer = ArtifactBucket.layer({ binding: "ARTIFACT_BUCKET" });
132
+
133
+ export const writeArtifact = (key: string, body: string) =>
134
+ Effect.gen(function* () {
135
+ const bucket = yield* ArtifactBucket;
136
+ yield* bucket.put(key, body, {
137
+ httpMetadata: { contentType: "application/json" },
138
+ });
139
+ });
140
+
141
+ export const readArtifact = (key: string) =>
142
+ Effect.gen(function* () {
143
+ const bucket = yield* ArtifactBucket;
144
+ const object = yield* bucket.get(key);
145
+ return Option.isSome(object) ? yield* Effect.promise(() => object.value.text()) : undefined;
146
+ });
147
+ ```
148
+
149
+ Use `createMultipartUpload(...)` or `resumeMultipartUpload(...)` for large objects; returned upload handles wrap `uploadPart`, `complete`, and `abort` in Effect.
150
+
151
+ ## Hyperdrive Example
152
+
153
+ Hyperdrive tags expose the binding `connectionString` directly on the yielded service.
154
+
155
+ ```ts
156
+ import { Effect } from "effect";
157
+ import { Hyperdrive } from "effect-cf";
158
+
159
+ class AppDatabase extends Hyperdrive.Tag<AppDatabase>()("AppDatabase") {}
160
+
161
+ export const AppDatabaseLayer = AppDatabase.layer({ binding: "HYPERDRIVE" });
162
+
163
+ export const databaseUrl = Effect.gen(function* () {
164
+ const hyperdrive = yield* AppDatabase;
165
+ return hyperdrive.connectionString;
166
+ });
167
+ ```
168
+
169
+ If your Worker uses Postgres via `@effect/sql-pg`, install that driver and use the optional subpath integration:
170
+
171
+ ```ts
172
+ import { Hyperdrive } from "effect-cf";
173
+ import * as HyperdrivePg from "effect-cf/hyperdrive-pg";
174
+
175
+ class AppDatabase extends Hyperdrive.Tag<AppDatabase>()("AppDatabase") {}
176
+
177
+ export const SqlLive = HyperdrivePg.layer(AppDatabase, { binding: "HYPERDRIVE" });
178
+ ```
179
+
180
+ The Postgres integration builds an Effect `PgClient` from Hyperdrive's generated connection string with `PgClient.makeClient`. It intentionally does not expose app-side pool options; Hyperdrive manages the underlying database pool.
181
+
182
+ ## Images Example
183
+
184
+ Images tags expose `info`, `input`, optional hosted image operations, and composable transform/draw steps.
185
+
186
+ ```ts
187
+ import { Effect } from "effect";
188
+ import { Images } from "effect-cf";
189
+
190
+ class AvatarImages extends Images.Tag<AvatarImages>()("AvatarImages") {}
191
+
192
+ export const AvatarImagesLayer = AvatarImages.layer({ binding: "IMAGES" });
193
+
194
+ export const resizeAvatar = (image: Images.ImageInputValue) =>
195
+ Effect.gen(function* () {
196
+ const images = yield* AvatarImages;
197
+ const result = yield* images.process(
198
+ Images.transform(Images.empty, { width: 256, height: 256 }),
199
+ {
200
+ stream: image,
201
+ outputOptions: { format: "image/webp" },
202
+ },
203
+ );
204
+
205
+ return yield* result.response;
206
+ });
207
+ ```
208
+
114
209
  ## Workflow Example
115
210
 
116
211
  Workflow definitions type the payload and result. Runtime handlers can access `Workflow.WorkflowEvent`, use durable `Workflow.step(...)`, and use normal binding services inside steps.
@@ -136,12 +231,10 @@ export const ExportWorkflowEntrypoint = ExportWorkflow.make(Layer.empty, {
136
231
  }),
137
232
  });
138
233
 
139
- export const ExportWorkflowBinding = ExportWorkflow.binding("ExportWorkflow", {
140
- binding: "EXPORT_WORKFLOW",
141
- });
234
+ export const ExportWorkflowLayer = ExportWorkflow.layer({ binding: "EXPORT_WORKFLOW" });
142
235
  ```
143
236
 
144
- Use `ExportWorkflowBinding.create(...)`, `createBatch(...)`, and `get(...)` to start and inspect instances.
237
+ Provide `ExportWorkflow.layer({ binding: "EXPORT_WORKFLOW" })`, then use `const workflow = yield* ExportWorkflow` or the static `ExportWorkflow.create(...)`, `createBatch(...)`, and `get(...)` helpers to start and inspect instances.
145
238
 
146
239
  In definition-backed workflows, the `payload` argument is the typed decoded payload and is the source of truth. `Workflow.WorkflowEvent.payload` is also re-provided decoded for convenience; `Workflow.WorkflowEvent.raw.payload` remains the native Cloudflare event payload.
147
240
 
@@ -193,24 +286,23 @@ export const RoomLive = DurableObject.make(layer, {
193
286
 
194
287
  Schema-backed attachments can rehydrate hibernated sockets:
195
288
 
196
- ```ts
197
- const restored =
198
- yield *
199
- Attachments.rehydrate({
200
- tag: "room:general",
201
- onInvalid: "ignore-and-close",
202
- });
289
+ ```text
290
+ const restored = yield* Attachments.rehydrate({
291
+ tag: "room:general",
292
+ onInvalid: "ignore-and-close",
293
+ });
203
294
 
204
295
  for (const { socket, attachment } of restored) {
205
- yield * socket.send(`restored:${attachment.id}`).pipe(Effect.ignore);
296
+ yield* socket.send(`restored:${attachment.id}`).pipe(Effect.ignore);
206
297
  }
207
298
  ```
208
299
 
209
300
  Worker-to-Durable-Object forwarding should stay native so WebSocket upgrade responses are preserved:
210
301
 
211
- ```ts
302
+ ```text
212
303
  if (Worker.isWebSocketUpgrade(request)) {
213
- return yield * ChatRooms.byName(roomId).fetch(request);
304
+ const rooms = yield* ChatRoom;
305
+ return yield* rooms.byName(roomId).fetch(request);
214
306
  }
215
307
  ```
216
308
 
@@ -0,0 +1,140 @@
1
+ import { Config, ConfigProvider, Context, Effect, Layer } from "effect";
2
+ import * as _$effect_Types0 from "effect/Types";
3
+ import * as _$effect_Cause0 from "effect/Cause";
4
+ import * as _$effect_Redacted0 from "effect/Redacted";
5
+ import * as _$effect_Layer0 from "effect/Layer";
6
+
7
+ //#region src/Environment.d.ts
8
+ /** Cloudflare worker environment object (`env`). */
9
+ type WorkerEnv = Cloudflare.Env;
10
+ declare const WorkerEnvironment_base: Context.ServiceClass<WorkerEnvironment, "effect-cf/WorkerEnvironment", Cloudflare.Env>;
11
+ /**
12
+ * Context service for reading worker bindings from the current `env` object.
13
+ */
14
+ declare class WorkerEnvironment extends WorkerEnvironment_base {}
15
+ type ScalarConfigValue = string | number | boolean;
16
+ type ScalarConfigKey = Extract<{ readonly [Key in keyof Cloudflare.Env]-?: NonNullable<Cloudflare.Env[Key]> extends ScalarConfigValue ? Key : never }[keyof Cloudflare.Env], string>;
17
+ /**
18
+ * Effect `Config` helpers for scalar Cloudflare vars and secrets declared on
19
+ * `Cloudflare.Env` / generated `worker-configuration.d.ts`.
20
+ *
21
+ * Users still author their app config explicitly with Effect `Config`:
22
+ *
23
+ * ```ts
24
+ * import { Config, Effect } from "effect";
25
+ * import { WorkerConfig } from "effect-cf";
26
+ *
27
+ * const AppConfig = Config.all({
28
+ * databaseUrl: WorkerConfig.redacted("DATABASE_URL"),
29
+ * port: WorkerConfig.integer("PORT").pipe(Config.withDefault(8787)),
30
+ * });
31
+ *
32
+ * const program = Effect.gen(function* () {
33
+ * const config = yield* AppConfig;
34
+ * // ...
35
+ * }).pipe(Effect.provide(WorkerConfig.layer));
36
+ * ```
37
+ *
38
+ * Keys are constrained to scalar `Cloudflare.Env` properties (`string`,
39
+ * `number`, or `boolean`, including optional scalar properties). Binding
40
+ * objects such as `KVNamespace`, `DurableObjectNamespace`, and service
41
+ * bindings are intentionally excluded; keep using the package binding helpers
42
+ * for those live resources.
43
+ */
44
+ declare namespace WorkerConfig {
45
+ /** Scalar env value types supported by the typed config key helpers. */
46
+ type Scalar = ScalarConfigValue;
47
+ /** Scalar keys available on the current consumer's `Cloudflare.Env`. */
48
+ type Key = ScalarConfigKey;
49
+ /** Read a scalar Cloudflare var or secret as a string. */
50
+ const string: <const Name extends Key>(name: Name) => Config.Config<string>;
51
+ /** Read a scalar Cloudflare secret as a redacted string. */
52
+ const redacted: <const Name extends Key>(name: Name) => Config.Config<_$effect_Redacted0.Redacted<string>>;
53
+ /** Read a scalar Cloudflare var or secret as a number. */
54
+ const number: <const Name extends Key>(name: Name) => Config.Config<number>;
55
+ /** Read a scalar Cloudflare var or secret as an integer. */
56
+ const integer: <const Name extends Key>(name: Name) => Config.Config<number>;
57
+ /** Read a scalar Cloudflare var or secret as a boolean. */
58
+ const boolean: <const Name extends Key>(name: Name) => Config.Config<boolean>;
59
+ /** Build a `ConfigProvider` from a Cloudflare worker `env` object. */
60
+ const providerFromEnv: (env: WorkerEnv) => ConfigProvider.ConfigProvider;
61
+ /**
62
+ * Build a `ConfigProvider` from the current `WorkerEnvironment` with a custom
63
+ * conversion function.
64
+ */
65
+ const providerWith: (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) => Effect.Effect<ConfigProvider.ConfigProvider, never, WorkerEnvironment>;
66
+ /** Build a `ConfigProvider` from the current `WorkerEnvironment`. */
67
+ const provider: Effect.Effect<ConfigProvider.ConfigProvider, never, WorkerEnvironment>;
68
+ /**
69
+ * Replace the active Effect `ConfigProvider` with one backed by the current
70
+ * Cloudflare worker `env` object.
71
+ */
72
+ const providerLayer: _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
73
+ /** Alias for `providerLayer` for concise use in worker layers. */
74
+ const layer: _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
75
+ /** Build a `ConfigProvider` layer with a custom `env` conversion function. */
76
+ const layerWith: (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) => _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
77
+ }
78
+ declare namespace Binding_d_exports {
79
+ export { BindingNotFoundError, BindingService, BindingValidationError, Service, TypeId, layer$1 as layer };
80
+ }
81
+ /** Internal type id marker used by binding helper services. */
82
+ declare const TypeId: "effect-cf/Binding";
83
+ declare const BindingNotFoundError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
84
+ readonly _tag: "BindingNotFoundError";
85
+ } & Readonly<A>;
86
+ /** Error raised when a configured binding does not exist on `env`. */
87
+ declare class BindingNotFoundError extends BindingNotFoundError_base<{
88
+ readonly binding: string;
89
+ }> {}
90
+ declare const BindingValidationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
91
+ readonly _tag: "BindingValidationError";
92
+ } & Readonly<A>;
93
+ /** Error raised when a binding exists but does not match the expected shape. */
94
+ declare class BindingValidationError extends BindingValidationError_base<{
95
+ readonly binding: string;
96
+ }> {}
97
+ /**
98
+ * Creates a Context tag + layer for reading and validating a Cloudflare binding.
99
+ */
100
+ interface BindingService<Self, Id extends string, Service> extends Context.ServiceClass<Self, Id, Service> {
101
+ readonly [TypeId]: typeof TypeId;
102
+ readonly binding: string;
103
+ readonly layer: Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
104
+ }
105
+ declare const layer$1: <Self, Resource, Service = Resource>(tag: Context.Service<Self, Service>, binding: string, isResource: (value: unknown) => value is Resource, wrap?: (resource: Resource) => Service) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
106
+ declare const Service: <Self>() => <Id extends string, Resource, Service = Resource>(id: Id, binding: string, isResource: (value: unknown) => value is Resource, wrap?: (resource: Resource) => Service) => BindingService<Self, Id, Service>;
107
+ declare namespace Hyperdrive_d_exports {
108
+ export { HyperdriveClient, HyperdriveDefinition, HyperdriveService, LayerOptions, Tag, TagClass, isHyperdrive, layer, make, makeClient };
109
+ }
110
+ /** Typed Hyperdrive binding definition. */
111
+ interface HyperdriveDefinition {
112
+ /** Binding name as configured in `wrangler.jsonc`. */
113
+ readonly binding: string;
114
+ }
115
+ interface HyperdriveClient {
116
+ readonly connectionString: string;
117
+ readonly unsafeRaw: Effect.Effect<Hyperdrive>;
118
+ readonly definition: HyperdriveDefinition;
119
+ }
120
+ declare const HyperdriveServiceTypeId: unique symbol;
121
+ /** Nominal service marker for Hyperdrive services created with {@link make}. */
122
+ interface HyperdriveService<Id extends string> {
123
+ readonly [HyperdriveServiceTypeId]: {
124
+ readonly id: Id;
125
+ };
126
+ }
127
+ type LayerOptions = {
128
+ readonly binding: string;
129
+ };
130
+ interface TagClass<Self, Id extends string> extends Context.ServiceClass<Self, Id, HyperdriveClient> {
131
+ readonly id: Id;
132
+ readonly layer: (options: LayerOptions) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
133
+ }
134
+ declare const isHyperdrive: (value: unknown) => value is Hyperdrive;
135
+ declare const makeClient: (definition: HyperdriveDefinition) => (hyperdrive: Hyperdrive) => HyperdriveClient;
136
+ declare const layer: <Self>(tag: Context.Service<Self, HyperdriveClient>, definition: HyperdriveDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
137
+ declare const make: <Id extends string>(id: Id) => TagClass<HyperdriveService<Id>, Id>;
138
+ declare const Tag: <Self>() => <Id extends string>(id: Id) => TagClass<Self, Id>;
139
+ //#endregion
140
+ export { BindingService as a, WorkerConfig as c, BindingNotFoundError as i, WorkerEnv as l, LayerOptions as n, BindingValidationError as o, TagClass as r, Binding_d_exports as s, Hyperdrive_d_exports as t, WorkerEnvironment as u };
@@ -0,0 +1,86 @@
1
+ import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
2
+ import { Config, ConfigProvider, Context, Data, Effect, Layer } from "effect";
3
+ //#region src/Environment.ts
4
+ /**
5
+ * Context service for reading worker bindings from the current `env` object.
6
+ */
7
+ var WorkerEnvironment = class extends Context.Service()("effect-cf/WorkerEnvironment") {};
8
+ let WorkerConfig;
9
+ (function(_WorkerConfig) {
10
+ _WorkerConfig.string = (name) => Config.string(name);
11
+ _WorkerConfig.redacted = (name) => Config.redacted(name);
12
+ _WorkerConfig.number = (name) => Config.number(name);
13
+ _WorkerConfig.integer = (name) => Config.int(name);
14
+ _WorkerConfig.boolean = (name) => Config.boolean(name);
15
+ const providerFromEnv = _WorkerConfig.providerFromEnv = (env) => ConfigProvider.fromUnknown(env);
16
+ const providerWith = _WorkerConfig.providerWith = (makeProvider) => Effect.map(WorkerEnvironment.asEffect(), makeProvider);
17
+ const provider = _WorkerConfig.provider = providerWith(providerFromEnv);
18
+ _WorkerConfig.layer = _WorkerConfig.providerLayer = ConfigProvider.layer(provider);
19
+ _WorkerConfig.layerWith = (makeProvider) => ConfigProvider.layer(providerWith(makeProvider));
20
+ })(WorkerConfig || (WorkerConfig = {}));
21
+ //#endregion
22
+ //#region src/Binding.ts
23
+ var Binding_exports = /* @__PURE__ */ __exportAll({
24
+ BindingNotFoundError: () => BindingNotFoundError,
25
+ BindingValidationError: () => BindingValidationError,
26
+ Service: () => Service,
27
+ TypeId: () => TypeId,
28
+ layer: () => layer$1
29
+ });
30
+ /** Internal type id marker used by binding helper services. */
31
+ const TypeId = "effect-cf/Binding";
32
+ /** Error raised when a configured binding does not exist on `env`. */
33
+ var BindingNotFoundError = class extends Data.TaggedError("BindingNotFoundError") {};
34
+ /** Error raised when a binding exists but does not match the expected shape. */
35
+ var BindingValidationError = class extends Data.TaggedError("BindingValidationError") {};
36
+ const isPropertyTarget = (value) => (typeof value === "object" || typeof value === "function") && value !== null;
37
+ const getBinding = (env, binding, isResource) => Effect.gen(function* () {
38
+ if (!isPropertyTarget(env)) return yield* Effect.fail(new BindingValidationError({ binding }));
39
+ const resource = Reflect.get(env, binding);
40
+ if (resource === void 0) return yield* Effect.fail(new BindingNotFoundError({ binding }));
41
+ if (!isResource(resource)) return yield* Effect.fail(new BindingValidationError({ binding }));
42
+ return resource;
43
+ });
44
+ const layer$1 = (tag, binding, isResource, wrap) => Layer.effect(tag, Effect.gen(function* () {
45
+ const resource = yield* getBinding(yield* WorkerEnvironment, binding, isResource);
46
+ return wrap === void 0 ? resource : wrap(resource);
47
+ }));
48
+ const Service = () => (id, binding, isResource, wrap) => {
49
+ const tag = Context.Service()(id);
50
+ const serviceLayer = layer$1(tag, binding, isResource, wrap);
51
+ return Object.assign(tag, {
52
+ [TypeId]: TypeId,
53
+ binding,
54
+ layer: serviceLayer
55
+ });
56
+ };
57
+ //#endregion
58
+ //#region src/Hyperdrive.ts
59
+ var Hyperdrive_exports = /* @__PURE__ */ __exportAll({
60
+ Tag: () => Tag,
61
+ isHyperdrive: () => isHyperdrive,
62
+ layer: () => layer,
63
+ make: () => make,
64
+ makeClient: () => makeClient
65
+ });
66
+ const isHyperdrive = (value) => {
67
+ if (typeof value !== "object" || value === null) return false;
68
+ return typeof value.connectionString === "string";
69
+ };
70
+ const makeClient = (definition) => (hyperdrive) => ({
71
+ definition,
72
+ connectionString: hyperdrive.connectionString,
73
+ unsafeRaw: Effect.succeed(hyperdrive)
74
+ });
75
+ const layer = (tag, definition) => layer$1(tag, definition.binding, isHyperdrive, makeClient(definition));
76
+ const make = (id) => Tag()(id);
77
+ const Tag = () => (id) => {
78
+ const tag = Context.Service()(id);
79
+ const makeLayer = (definition) => layer(tag, definition);
80
+ return Object.assign(tag, {
81
+ id,
82
+ layer: makeLayer
83
+ });
84
+ };
85
+ //#endregion
86
+ export { WorkerConfig as a, layer$1 as i, Binding_exports as n, WorkerEnvironment as o, Service as r, Hyperdrive_exports as t };
@@ -0,0 +1,14 @@
1
+ import { i as BindingNotFoundError, n as LayerOptions, o as BindingValidationError, r as TagClass, u as WorkerEnvironment } from "./Hyperdrive-By6DrSS_.mjs";
2
+ import { Layer } from "effect";
3
+ import { PgClient } from "@effect/sql-pg";
4
+ import * as _$effect_unstable_sql_SqlClient0 from "effect/unstable/sql/SqlClient";
5
+ import * as _$effect_unstable_sql_SqlError0 from "effect/unstable/sql/SqlError";
6
+ import * as _$effect_Scope0 from "effect/Scope";
7
+
8
+ //#region src/HyperdrivePg.d.ts
9
+ type PgLayerOptions = Omit<PgClient.PgClientConfig & {
10
+ readonly acquireForStream?: boolean;
11
+ }, "url" | "host" | "port" | "path" | "ssl" | "database" | "username" | "password" | "stream">;
12
+ declare const layer: <Self, Id extends string>(tag: TagClass<Self, Id>, binding: LayerOptions, options?: PgLayerOptions) => Layer.Layer<PgClient.PgClient | _$effect_unstable_sql_SqlClient0.SqlClient, BindingNotFoundError | BindingValidationError | _$effect_unstable_sql_SqlError0.SqlError, WorkerEnvironment | Exclude<Exclude<Self, _$effect_Scope0.Scope>, Self>>;
13
+ //#endregion
14
+ export { PgLayerOptions, layer };
@@ -0,0 +1,13 @@
1
+ import "./Hyperdrive-LuQaVhXU.mjs";
2
+ import { Effect, Layer, Redacted } from "effect";
3
+ import { PgClient } from "@effect/sql-pg";
4
+ //#region src/HyperdrivePg.ts
5
+ const layer = (tag, binding, options) => Layer.unwrap(Effect.gen(function* () {
6
+ const hyperdrive = yield* tag;
7
+ return PgClient.layerFrom(PgClient.makeClient({
8
+ ...options,
9
+ url: Redacted.make(hyperdrive.connectionString)
10
+ }));
11
+ })).pipe(Layer.provide(tag.layer(binding)));
12
+ //#endregion
13
+ export { layer };