effect-cf 0.7.0 → 0.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-cf",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,6 +21,11 @@
21
21
  "import": "./dist/index.mjs",
22
22
  "default": "./dist/index.mjs"
23
23
  },
24
+ "./hyperdrive-pg": {
25
+ "types": "./dist/HyperdrivePg.d.mts",
26
+ "import": "./dist/HyperdrivePg.mjs",
27
+ "default": "./dist/HyperdrivePg.mjs"
28
+ },
24
29
  "./package.json": "./package.json"
25
30
  },
26
31
  "publishConfig": {
@@ -37,6 +42,7 @@
37
42
  "@cloudflare/vitest-pool-workers": "^0.15.0",
38
43
  "@cloudflare/workers-types": "^4.20260421.1",
39
44
  "@effect/sql-d1": "catalog:",
45
+ "@effect/sql-pg": "4.0.0-beta.65",
40
46
  "@effect/vitest": "catalog:",
41
47
  "effect": "catalog:",
42
48
  "typescript": "^6.0.2",
@@ -45,6 +51,12 @@
45
51
  },
46
52
  "peerDependencies": {
47
53
  "@effect/sql-d1": "^4.0.0-beta.65",
54
+ "@effect/sql-pg": "^4.0.0-beta.65",
48
55
  "effect": "^4.0.0-beta.65"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "@effect/sql-pg": {
59
+ "optional": true
60
+ }
49
61
  }
50
62
  }
package/src/D1.ts CHANGED
@@ -1,17 +1,10 @@
1
1
  import { D1Client } from "@effect/sql-d1";
2
- import { Data, Effect, Layer } from "effect";
2
+ import { Effect, Layer } from "effect";
3
3
 
4
4
  import * as Binding from "./Binding";
5
5
 
6
6
  const TypeId = "effect-cf/D1" as const;
7
7
 
8
- /** Error raised when a D1 operation fails. */
9
- export class D1OperationError extends Data.TaggedError("D1OperationError")<{
10
- readonly binding: string;
11
- readonly operation: string;
12
- readonly cause: unknown;
13
- }> {}
14
-
15
8
  /** Typed D1 binding definition. */
16
9
  export interface D1Definition {
17
10
  /** Binding name as configured in `wrangler.jsonc`. */
@@ -44,29 +37,6 @@ const isD1Database = (value: unknown): value is D1Database => {
44
37
  );
45
38
  };
46
39
 
47
- const d1Error = (binding: string, operation: string, cause: unknown) =>
48
- new D1OperationError({ binding, operation, cause });
49
-
50
- const tryD1Promise = <A>(
51
- binding: string,
52
- operation: string,
53
- evaluate: () => Promise<A>,
54
- ): Effect.Effect<A, D1OperationError> =>
55
- Effect.tryPromise({
56
- try: evaluate,
57
- catch: (cause) => d1Error(binding, operation, cause),
58
- });
59
-
60
- const tryD1Sync = <A>(
61
- binding: string,
62
- operation: string,
63
- evaluate: () => A,
64
- ): Effect.Effect<A, D1OperationError> =>
65
- Effect.try({
66
- try: evaluate,
67
- catch: (cause) => d1Error(binding, operation, cause),
68
- });
69
-
70
40
  /**
71
41
  * Creates a typed D1 service tag plus Effect helpers.
72
42
  */
@@ -76,9 +46,8 @@ export const make = <Id extends string>(id: Id, definition: D1Definition) =>
76
46
  /**
77
47
  * Builds a D1 service around a Cloudflare D1 database binding.
78
48
  *
79
- * The returned service exposes the raw `D1Database` binding, small native D1
80
- * helpers, and `sqlLayer(...)` for providing `effect/unstable/sql` via
81
- * `@effect/sql-d1`.
49
+ * The returned service exposes the raw `D1Database` binding and `sqlLayer(...)`
50
+ * for providing `effect/unstable/sql` via `@effect/sql-d1`.
82
51
  *
83
52
  * @example
84
53
  * ```ts
@@ -102,34 +71,9 @@ export const Service =
102
71
  }),
103
72
  ).pipe(Layer.provide(tag.layer));
104
73
 
105
- const prepare = Effect.fnUntraced(function* (query: string) {
106
- const db = yield* tag;
107
- return yield* tryD1Sync(definition.binding, "prepare", () => db.prepare(query));
108
- });
109
-
110
- const batch = Effect.fnUntraced(function* <T = unknown>(
111
- statements: ReadonlyArray<D1PreparedStatement>,
112
- ) {
113
- const db = yield* tag;
114
- return yield* tryD1Promise(definition.binding, "batch", () => db.batch<T>([...statements]));
115
- });
116
-
117
- const exec = Effect.fnUntraced(function* (query: string) {
118
- const db = yield* tag;
119
- return yield* tryD1Promise(definition.binding, "exec", () => db.exec(query));
120
- });
121
-
122
- const unsafeRaw = Effect.fnUntraced(function* () {
123
- return yield* tag;
124
- });
125
-
126
74
  return Object.assign(tag, {
127
75
  [TypeId]: TypeId,
128
76
  definition,
129
77
  sqlLayer,
130
- prepare,
131
- batch,
132
- exec,
133
- unsafeRaw,
134
78
  });
135
79
  };
@@ -73,6 +73,15 @@ export type RpcHandlers<ROut, Api> = {
73
73
  * Options for creating a Durable Object class backed by Effect handlers.
74
74
  */
75
75
  export interface DurableObjectOptions<ROut, Rpc extends DurableObjectRpc<ROut>> {
76
+ /**
77
+ * Effect run when Cloudflare loads this Durable Object instance into memory.
78
+ *
79
+ * Use `DurableObjectState.blockConcurrencyWhile` inside this hook when
80
+ * incoming events should wait for setup to finish. Cloudflare may construct
81
+ * the same Durable Object id again after eviction or restart; use Durable
82
+ * Object storage if work must happen only once per id.
83
+ */
84
+ readonly initialize?: Effect.Effect<void, unknown, HandlerContext<ROut>>;
76
85
  /** Optional RPC methods exposed as Durable Object instance methods. */
77
86
  readonly rpc?: Rpc;
78
87
  /** Optional fetch handler for HTTP/WebSocket requests. */
@@ -137,6 +146,11 @@ export const make = <
137
146
  const runtimeLayer = Entrypoint.provideEntrypointServices(layer, services);
138
147
 
139
148
  this.runtime = ManagedRuntime.make(runtimeLayer);
149
+
150
+ const initialize = options.initialize;
151
+ if (initialize !== undefined) {
152
+ state.waitUntil(this[RunSymbol](initialize));
153
+ }
140
154
  }
141
155
 
142
156
  [RunSymbol]<A, E>(effect: Effect.Effect<A, E, HandlerContext<ROut>>): Promise<A> {
@@ -0,0 +1,82 @@
1
+ import { Context, Effect, type Layer } from "effect";
2
+
3
+ import * as Binding from "./Binding";
4
+ import type { WorkerEnvironment } from "./Environment";
5
+
6
+ /** Typed Hyperdrive binding definition. */
7
+ export interface HyperdriveDefinition {
8
+ /** Binding name as configured in `wrangler.jsonc`. */
9
+ readonly binding: string;
10
+ }
11
+
12
+ export interface HyperdriveClient {
13
+ readonly connectionString: string;
14
+ readonly unsafeRaw: Effect.Effect<Hyperdrive>;
15
+ readonly definition: HyperdriveDefinition;
16
+ }
17
+
18
+ declare const HyperdriveServiceTypeId: unique symbol;
19
+
20
+ /** Nominal service marker for Hyperdrive services created with {@link make}. */
21
+ export interface HyperdriveService<Id extends string> {
22
+ readonly [HyperdriveServiceTypeId]: {
23
+ readonly id: Id;
24
+ };
25
+ }
26
+
27
+ export type LayerOptions = {
28
+ readonly binding: string;
29
+ };
30
+
31
+ export interface TagClass<Self, Id extends string> extends Context.ServiceClass<
32
+ Self,
33
+ Id,
34
+ HyperdriveClient
35
+ > {
36
+ readonly id: Id;
37
+ readonly layer: (
38
+ options: LayerOptions,
39
+ ) => Layer.Layer<
40
+ Self,
41
+ Binding.BindingNotFoundError | Binding.BindingValidationError,
42
+ WorkerEnvironment
43
+ >;
44
+ }
45
+
46
+ export const isHyperdrive = (value: unknown): value is Hyperdrive => {
47
+ if (typeof value !== "object" || value === null) {
48
+ return false;
49
+ }
50
+
51
+ const resource = value as Record<string, unknown>;
52
+
53
+ return typeof resource.connectionString === "string";
54
+ };
55
+
56
+ export const makeClient =
57
+ (definition: HyperdriveDefinition) =>
58
+ (hyperdrive: Hyperdrive): HyperdriveClient => ({
59
+ definition,
60
+ connectionString: hyperdrive.connectionString,
61
+ unsafeRaw: Effect.succeed(hyperdrive),
62
+ });
63
+
64
+ export const layer = <Self>(
65
+ tag: Context.Service<Self, HyperdriveClient>,
66
+ definition: HyperdriveDefinition,
67
+ ) => Binding.layer(tag, definition.binding, isHyperdrive, makeClient(definition));
68
+
69
+ export const make = <Id extends string>(id: Id) => Tag<HyperdriveService<Id>>()<Id>(id);
70
+
71
+ export const Tag =
72
+ <Self>() =>
73
+ <Id extends string>(id: Id) => {
74
+ const tag = Context.Service<Self, HyperdriveClient>()(id);
75
+
76
+ const makeLayer = (definition: LayerOptions) => layer(tag, definition);
77
+
78
+ return Object.assign(tag, {
79
+ id,
80
+ layer: makeLayer,
81
+ }) as TagClass<Self, Id>;
82
+ };
@@ -0,0 +1,27 @@
1
+ import { PgClient } from "@effect/sql-pg";
2
+ import { Effect, Layer, Redacted } from "effect";
3
+
4
+ import * as Hyperdrive from "./Hyperdrive";
5
+
6
+ export type PgLayerOptions = Omit<
7
+ PgClient.PgClientConfig & { readonly acquireForStream?: boolean },
8
+ "url" | "host" | "port" | "path" | "ssl" | "database" | "username" | "password" | "stream"
9
+ >;
10
+
11
+ export const layer = <Self, Id extends string>(
12
+ tag: Hyperdrive.TagClass<Self, Id>,
13
+ binding: Hyperdrive.LayerOptions,
14
+ options?: PgLayerOptions,
15
+ ) =>
16
+ Layer.unwrap(
17
+ Effect.gen(function* () {
18
+ const hyperdrive = yield* tag;
19
+
20
+ return PgClient.layerFrom(
21
+ PgClient.makeClient({
22
+ ...options,
23
+ url: Redacted.make(hyperdrive.connectionString),
24
+ }),
25
+ );
26
+ }),
27
+ ).pipe(Layer.provide(tag.layer(binding)));
package/src/Images.ts ADDED
@@ -0,0 +1,332 @@
1
+ import { Context, Data, Effect, Option, type Layer } from "effect";
2
+
3
+ import * as Binding from "./Binding";
4
+ import type { WorkerEnvironment } from "./Environment";
5
+
6
+ const TypeId = "effect-cf/Images/Steps" as const;
7
+
8
+ /** Error raised when a Cloudflare Images operation fails. */
9
+ export class ImagesOperationError extends Data.TaggedError("ImagesOperationError")<{
10
+ readonly binding: string;
11
+ readonly operation: string;
12
+ readonly cause: unknown;
13
+ }> {}
14
+
15
+ /** Typed Cloudflare Images binding definition. */
16
+ export interface ImagesDefinition {
17
+ /** Binding name as configured in `wrangler.jsonc`. */
18
+ readonly binding: string;
19
+ }
20
+
21
+ export type ImageInfoResponse = globalThis.ImageInfoResponse;
22
+ export type ImageTransform = globalThis.ImageTransform;
23
+ export type ImageDrawOptions = globalThis.ImageDrawOptions;
24
+ export type ImageInputOptions = globalThis.ImageInputOptions;
25
+ export type ImageOutputOptions = globalThis.ImageOutputOptions;
26
+ export type ImageTransformationOutputOptions = globalThis.ImageTransformationOutputOptions;
27
+ export type ImageTransformationResult = globalThis.ImageTransformationResult;
28
+ export type ImageUploadOptions = globalThis.ImageUploadOptions;
29
+ export type ImageUpdateOptions = globalThis.ImageUpdateOptions;
30
+ export type ImageListOptions = globalThis.ImageListOptions;
31
+ export type ImageList = globalThis.ImageList;
32
+ export type ImageMetadata = globalThis.ImageMetadata;
33
+ export type ImageInputValue = ReadableStream<Uint8Array> | ArrayBuffer;
34
+ export type ImageUploadValue = ReadableStream<Uint8Array> | ArrayBuffer;
35
+
36
+ export interface DrawStepOptions {
37
+ readonly image: ReadableStream<Uint8Array> | globalThis.ImageTransformer;
38
+ readonly options?: ImageDrawOptions;
39
+ }
40
+
41
+ export type Step = Data.TaggedEnum<{
42
+ readonly Transform: {
43
+ readonly transform: ImageTransform;
44
+ };
45
+ readonly Draw: DrawStepOptions;
46
+ }>;
47
+
48
+ export interface Steps {
49
+ readonly [TypeId]: typeof TypeId;
50
+ readonly steps: ReadonlyArray<Step>;
51
+ }
52
+
53
+ export interface ProcessOptions {
54
+ readonly stream: ImageInputValue;
55
+ readonly inputOptions?: ImageInputOptions;
56
+ readonly outputOptions: ImageOutputOptions;
57
+ }
58
+
59
+ export interface ImagesTransformationResultClient {
60
+ readonly raw: globalThis.ImageTransformationResult;
61
+ readonly response: Effect.Effect<globalThis.Response, ImagesOperationError>;
62
+ readonly contentType: Effect.Effect<string, ImagesOperationError>;
63
+ readonly image: (
64
+ options?: ImageTransformationOutputOptions,
65
+ ) => Effect.Effect<ReadableStream<Uint8Array>, ImagesOperationError>;
66
+ }
67
+
68
+ export interface ImageHandleClient {
69
+ readonly raw: globalThis.ImageHandle;
70
+ readonly details: Effect.Effect<Option.Option<ImageMetadata>, ImagesOperationError>;
71
+ readonly bytes: Effect.Effect<Option.Option<ReadableStream<Uint8Array>>, ImagesOperationError>;
72
+ readonly update: (
73
+ options: ImageUpdateOptions,
74
+ ) => Effect.Effect<ImageMetadata, ImagesOperationError>;
75
+ readonly delete: Effect.Effect<boolean, ImagesOperationError>;
76
+ }
77
+
78
+ export interface HostedImagesClient {
79
+ readonly image: (imageId: string) => ImageHandleClient;
80
+ readonly upload: (
81
+ image: ImageUploadValue,
82
+ options?: ImageUploadOptions,
83
+ ) => Effect.Effect<ImageMetadata, ImagesOperationError>;
84
+ readonly list: (options?: ImageListOptions) => Effect.Effect<ImageList, ImagesOperationError>;
85
+ readonly unsafeRaw: Effect.Effect<globalThis.HostedImagesBinding>;
86
+ }
87
+
88
+ export interface ImagesRuntimeBinding {
89
+ readonly info: (
90
+ image: ImageInputValue,
91
+ options?: ImageInputOptions,
92
+ ) => Promise<ImageInfoResponse>;
93
+ readonly input: (
94
+ image: ImageInputValue,
95
+ options?: ImageInputOptions,
96
+ ) => globalThis.ImageTransformer;
97
+ readonly hosted?: globalThis.HostedImagesBinding;
98
+ }
99
+
100
+ export interface ImagesClient {
101
+ readonly info: (
102
+ image: ImageInputValue,
103
+ options?: ImageInputOptions,
104
+ ) => Effect.Effect<ImageInfoResponse, ImagesOperationError>;
105
+ readonly input: (
106
+ image: ImageInputValue,
107
+ options?: ImageInputOptions,
108
+ ) => Effect.Effect<globalThis.ImageTransformer, ImagesOperationError>;
109
+ readonly process: (
110
+ steps: Steps,
111
+ options: ProcessOptions,
112
+ ) => Effect.Effect<ImagesTransformationResultClient, ImagesOperationError>;
113
+ readonly hosted: Option.Option<HostedImagesClient>;
114
+ readonly unsafeRaw: Effect.Effect<ImagesRuntimeBinding>;
115
+ readonly definition: ImagesDefinition;
116
+ }
117
+
118
+ declare const ImagesServiceTypeId: unique symbol;
119
+
120
+ /** Nominal service marker for Images services created with {@link make}. */
121
+ export interface ImagesService<Id extends string> {
122
+ readonly [ImagesServiceTypeId]: {
123
+ readonly id: Id;
124
+ };
125
+ }
126
+
127
+ export type LayerOptions = {
128
+ readonly binding: string;
129
+ };
130
+
131
+ export interface TagClass<Self, Id extends string> extends Context.ServiceClass<
132
+ Self,
133
+ Id,
134
+ ImagesClient
135
+ > {
136
+ readonly id: Id;
137
+ readonly layer: (
138
+ options: LayerOptions,
139
+ ) => Layer.Layer<
140
+ Self,
141
+ Binding.BindingNotFoundError | Binding.BindingValidationError,
142
+ WorkerEnvironment
143
+ >;
144
+ }
145
+
146
+ const makeSteps = (steps: ReadonlyArray<Step>): Steps => ({
147
+ [TypeId]: TypeId,
148
+ steps,
149
+ });
150
+
151
+ /** Empty Images transformation pipeline. */
152
+ export const empty: Steps = makeSteps([]);
153
+
154
+ export function transform(transform: ImageTransform): (steps: Steps) => Steps;
155
+ export function transform(steps: Steps, transform: ImageTransform): Steps;
156
+ export function transform(
157
+ stepsOrTransform: Steps | ImageTransform,
158
+ transformValue?: ImageTransform,
159
+ ): Steps | ((steps: Steps) => Steps) {
160
+ if (transformValue === undefined) {
161
+ return (steps) => transform(steps, stepsOrTransform as ImageTransform);
162
+ }
163
+
164
+ const steps = stepsOrTransform as Steps;
165
+
166
+ return makeSteps([
167
+ ...steps.steps,
168
+ {
169
+ _tag: "Transform",
170
+ transform: transformValue,
171
+ },
172
+ ]);
173
+ }
174
+
175
+ export function draw(draw: DrawStepOptions): (steps: Steps) => Steps;
176
+ export function draw(steps: Steps, draw: DrawStepOptions): Steps;
177
+ export function draw(
178
+ stepsOrDraw: Steps | DrawStepOptions,
179
+ drawValue?: DrawStepOptions,
180
+ ): Steps | ((steps: Steps) => Steps) {
181
+ if (drawValue === undefined) {
182
+ return (steps) => draw(steps, stepsOrDraw as DrawStepOptions);
183
+ }
184
+
185
+ const steps = stepsOrDraw as Steps;
186
+
187
+ return makeSteps([
188
+ ...steps.steps,
189
+ {
190
+ _tag: "Draw",
191
+ ...drawValue,
192
+ },
193
+ ]);
194
+ }
195
+
196
+ const imagesError = (binding: string, operation: string, cause: unknown) =>
197
+ new ImagesOperationError({ binding, operation, cause });
198
+
199
+ const tryImagesPromise = <A>(
200
+ binding: string,
201
+ operation: string,
202
+ evaluate: () => Promise<A>,
203
+ ): Effect.Effect<A, ImagesOperationError> =>
204
+ Effect.tryPromise({
205
+ try: evaluate,
206
+ catch: (cause) => imagesError(binding, operation, cause),
207
+ });
208
+
209
+ const tryImagesSync = <A>(
210
+ binding: string,
211
+ operation: string,
212
+ evaluate: () => A,
213
+ ): Effect.Effect<A, ImagesOperationError> =>
214
+ Effect.try({
215
+ try: evaluate,
216
+ catch: (cause) => imagesError(binding, operation, cause),
217
+ });
218
+
219
+ const maybe = <A>(value: A | null): Option.Option<A> =>
220
+ value === null ? Option.none() : Option.some(value);
221
+
222
+ const hasFunction = (value: object, key: string): boolean =>
223
+ typeof Reflect.get(value, key) === "function";
224
+
225
+ const isHostedImagesBinding = (value: unknown): value is globalThis.HostedImagesBinding =>
226
+ typeof value === "object" &&
227
+ value !== null &&
228
+ hasFunction(value, "image") &&
229
+ hasFunction(value, "upload") &&
230
+ hasFunction(value, "list");
231
+
232
+ export const isImagesBinding = (value: unknown): value is ImagesRuntimeBinding =>
233
+ typeof value === "object" &&
234
+ value !== null &&
235
+ hasFunction(value, "info") &&
236
+ hasFunction(value, "input");
237
+
238
+ const wrapResult = (
239
+ binding: string,
240
+ result: globalThis.ImageTransformationResult,
241
+ ): ImagesTransformationResultClient => ({
242
+ raw: result,
243
+ response: tryImagesSync(binding, "response", () => result.response()),
244
+ contentType: tryImagesSync(binding, "contentType", () => result.contentType()),
245
+ image: (options) => tryImagesSync(binding, "image", () => result.image(options)),
246
+ });
247
+
248
+ const wrapHandle = (binding: string, handle: globalThis.ImageHandle): ImageHandleClient => ({
249
+ raw: handle,
250
+ details: tryImagesPromise(binding, "details", () => handle.details()).pipe(Effect.map(maybe)),
251
+ bytes: tryImagesPromise(binding, "bytes", () => handle.bytes()).pipe(Effect.map(maybe)),
252
+ update: (options) => tryImagesPromise(binding, "update", () => handle.update(options)),
253
+ delete: tryImagesPromise(binding, "delete", () => handle.delete()),
254
+ });
255
+
256
+ const wrapHosted = (
257
+ binding: string,
258
+ hosted: globalThis.HostedImagesBinding,
259
+ ): HostedImagesClient => ({
260
+ image: (imageId) => wrapHandle(binding, hosted.image(imageId)),
261
+ upload: (image, options) =>
262
+ tryImagesPromise(binding, "upload", () => hosted.upload(image, options)),
263
+ list: (options) => tryImagesPromise(binding, "list", () => hosted.list(options)),
264
+ unsafeRaw: Effect.succeed(hosted),
265
+ });
266
+
267
+ export const makeClient =
268
+ (definition: ImagesDefinition) =>
269
+ (images: ImagesRuntimeBinding): ImagesClient => {
270
+ const input = (image: ImageInputValue, options?: ImageInputOptions) =>
271
+ tryImagesSync(definition.binding, "input", () => images.input(image, options));
272
+
273
+ const process = (steps: Steps, options: ProcessOptions) =>
274
+ Effect.gen(function* () {
275
+ let transformer = yield* input(options.stream, options.inputOptions);
276
+
277
+ for (const step of steps.steps) {
278
+ switch (step._tag) {
279
+ case "Draw": {
280
+ transformer = yield* tryImagesSync(definition.binding, "draw", () =>
281
+ transformer.draw(step.image, step.options),
282
+ );
283
+ break;
284
+ }
285
+ case "Transform": {
286
+ transformer = yield* tryImagesSync(definition.binding, "transform", () =>
287
+ transformer.transform(step.transform),
288
+ );
289
+ break;
290
+ }
291
+ }
292
+ }
293
+
294
+ const result = yield* tryImagesPromise(definition.binding, "output", () =>
295
+ transformer.output(options.outputOptions),
296
+ );
297
+
298
+ return wrapResult(definition.binding, result);
299
+ });
300
+
301
+ return {
302
+ definition,
303
+ info: (image, options) =>
304
+ tryImagesPromise(definition.binding, "info", () => images.info(image, options)),
305
+ input,
306
+ process,
307
+ hosted: isHostedImagesBinding(images.hosted)
308
+ ? Option.some(wrapHosted(definition.binding, images.hosted))
309
+ : Option.none(),
310
+ unsafeRaw: Effect.succeed(images),
311
+ };
312
+ };
313
+
314
+ export const layer = <Self>(
315
+ tag: Context.Service<Self, ImagesClient>,
316
+ definition: ImagesDefinition,
317
+ ) => Binding.layer(tag, definition.binding, isImagesBinding, makeClient(definition));
318
+
319
+ export const make = <Id extends string>(id: Id) => Tag<ImagesService<Id>>()<Id>(id);
320
+
321
+ export const Tag =
322
+ <Self>() =>
323
+ <Id extends string>(id: Id) => {
324
+ const tag = Context.Service<Self, ImagesClient>()(id);
325
+
326
+ const makeLayer = (definition: LayerOptions) => layer(tag, definition);
327
+
328
+ return Object.assign(tag, {
329
+ id,
330
+ layer: makeLayer,
331
+ }) as TagClass<Self, Id>;
332
+ };
package/src/Kv.ts CHANGED
@@ -114,27 +114,6 @@ export interface TagClass<
114
114
  Binding.BindingNotFoundError | Binding.BindingValidationError,
115
115
  WorkerEnvironment
116
116
  >;
117
- readonly put: (
118
- key: Key,
119
- value: Value,
120
- options?: KvPutOptions,
121
- ) => Effect.Effect<void, KvOperationError | S.SchemaError, Self>;
122
- readonly get: (
123
- key: Key,
124
- ) => Effect.Effect<Option.Option<Value>, KvOperationError | S.SchemaError, Self>;
125
- readonly getWithMetadata: <Metadata>(
126
- key: Key,
127
- metadataSchema: S.Codec<Metadata, unknown>,
128
- ) => Effect.Effect<
129
- Option.Option<KvWithMetadata<Value, Metadata>>,
130
- KvOperationError | S.SchemaError,
131
- Self
132
- >;
133
- readonly list: <Metadata = unknown>(
134
- options?: KvListOptions<Metadata>,
135
- ) => Effect.Effect<KvListResult<Key, Metadata>, KvOperationError | S.SchemaError, Self>;
136
- readonly remove: (key: Key) => Effect.Effect<void, KvOperationError | S.SchemaError, Self>;
137
- readonly unsafeRaw: () => Effect.Effect<KVNamespace, never, Self>;
138
117
  }
139
118
 
140
119
  const maybeString = (value: string | null | undefined): Option.Option<string> =>
@@ -311,51 +290,10 @@ export const Tag =
311
290
 
312
291
  const makeLayer = (binding: LayerOptions) => kvDefinition.layer(tag, binding);
313
292
 
314
- const put = Effect.fnUntraced(function* (key: Key, value: Value, options?: KvPutOptions) {
315
- const kv = yield* tag;
316
- yield* kv.put(key, value, options);
317
- });
318
-
319
- const get = Effect.fnUntraced(function* (key: Key) {
320
- const kv = yield* tag;
321
- return yield* kv.get(key);
322
- });
323
-
324
- const getWithMetadata = Effect.fnUntraced(function* <Metadata>(
325
- key: Key,
326
- metadataSchema: S.Codec<Metadata, unknown>,
327
- ) {
328
- const kv = yield* tag;
329
- return yield* kv.getWithMetadata(key, metadataSchema);
330
- });
331
-
332
- const list = Effect.fnUntraced(function* <Metadata = unknown>(
333
- options?: KvListOptions<Metadata>,
334
- ) {
335
- const kv = yield* tag;
336
- return yield* kv.list(options);
337
- });
338
-
339
- const remove = Effect.fnUntraced(function* (key: Key) {
340
- const kv = yield* tag;
341
- yield* kv.remove(key);
342
- });
343
-
344
- const unsafeRaw = Effect.fnUntraced(function* () {
345
- const kv = yield* tag;
346
- return yield* kv.unsafeRaw;
347
- });
348
-
349
293
  return Object.assign(tag, {
350
294
  id: kvDefinition.id,
351
295
  keySchema: kvDefinition.key,
352
296
  valueSchema: kvDefinition.value,
353
297
  layer: makeLayer,
354
- put,
355
- get,
356
- getWithMetadata,
357
- list,
358
- remove,
359
- unsafeRaw,
360
298
  }) as TagClass<Self, Id, Key, Value, EncodedValue>;
361
299
  };