effect-cf 0.5.0 → 0.7.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/src/Kv.ts CHANGED
@@ -1,8 +1,7 @@
1
- import { Data, Effect, Option, Schema as S } from "effect";
1
+ import { Context, Data, Effect, Option, Schema as S, type Layer } from "effect";
2
2
 
3
3
  import * as Binding from "./Binding";
4
-
5
- const TypeId = "effect-cf/Kv" as const;
4
+ import type { WorkerEnvironment } from "./Environment";
6
5
 
7
6
  /** Error raised when a KV operation fails. */
8
7
  export class KvOperationError extends Data.TaggedError("KvOperationError")<{
@@ -72,26 +71,70 @@ export namespace Definition {
72
71
  export type Any = Definition<string, any, any, any>;
73
72
  }
74
73
 
75
- declare const KvServiceTypeId: unique symbol;
76
-
77
- /** Nominal service marker for KV services created with {@link make}. */
78
- export interface KvService<Id extends string, Key, Value, EncodedValue> {
79
- readonly [KvServiceTypeId]: {
80
- readonly id: Id;
81
- readonly key: Key;
82
- readonly value: Value;
83
- readonly encodedValue: EncodedValue;
84
- };
74
+ export interface KvClient<Key, Value, EncodedValue> {
75
+ readonly put: (
76
+ key: Key,
77
+ value: Value,
78
+ options?: KvPutOptions,
79
+ ) => Effect.Effect<void, KvOperationError | S.SchemaError>;
80
+ readonly get: (key: Key) => Effect.Effect<Option.Option<Value>, KvOperationError | S.SchemaError>;
81
+ readonly getWithMetadata: <Metadata>(
82
+ key: Key,
83
+ metadataSchema: S.Codec<Metadata, unknown>,
84
+ ) => Effect.Effect<
85
+ Option.Option<KvWithMetadata<Value, Metadata>>,
86
+ KvOperationError | S.SchemaError
87
+ >;
88
+ readonly list: <Metadata = unknown>(
89
+ options?: KvListOptions<Metadata>,
90
+ ) => Effect.Effect<KvListResult<Key, Metadata>, KvOperationError | S.SchemaError>;
91
+ readonly remove: (key: Key) => Effect.Effect<void, KvOperationError | S.SchemaError>;
92
+ readonly unsafeRaw: Effect.Effect<KVNamespace>;
93
+ readonly definition: KvDefinition<Key, Value, EncodedValue>;
85
94
  }
86
95
 
87
- declare const BindingServiceTypeId: unique symbol;
96
+ export type LayerOptions = {
97
+ readonly binding: string;
98
+ };
88
99
 
89
- /** Nominal service marker for KV bindings created from a reusable {@link Definition}. */
90
- export interface BindingService<Id extends string, Self extends Definition.Any> {
91
- readonly [BindingServiceTypeId]: {
92
- readonly id: Id;
93
- readonly definition: Self;
94
- };
100
+ export interface TagClass<
101
+ Self,
102
+ Id extends string,
103
+ Key,
104
+ Value,
105
+ EncodedValue,
106
+ > extends Context.ServiceClass<Self, Id, KvClient<Key, Value, EncodedValue>> {
107
+ readonly id: Id;
108
+ readonly keySchema: S.Codec<Key, string>;
109
+ readonly valueSchema: S.Codec<Value, EncodedValue>;
110
+ readonly layer: (
111
+ options: LayerOptions,
112
+ ) => Layer.Layer<
113
+ Self,
114
+ Binding.BindingNotFoundError | Binding.BindingValidationError,
115
+ WorkerEnvironment
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>;
95
138
  }
96
139
 
97
140
  const maybeString = (value: string | null | undefined): Option.Option<string> =>
@@ -113,133 +156,40 @@ const tryKvPromise = <A>(
113
156
  catch: (cause) => kvError(binding, operation, cause),
114
157
  });
115
158
 
116
- /**
117
- * Creates a typed KV service tag plus Effect helpers.
118
- */
119
- export const make = <Id extends string, Key, Value, EncodedValue>(
120
- id: Id,
121
- definition: KvDefinition<Key, Value, EncodedValue>,
122
- ) =>
123
- Service<KvService<Id, Key, Value, EncodedValue>>()<Id, Key, Value, EncodedValue>(id, definition);
159
+ export const isKvNamespace = (value: unknown): value is KVNamespace => {
160
+ if (typeof value !== "object" || value === null) {
161
+ return false;
162
+ }
124
163
 
125
- const makeDefinition = <Id extends string, Key, Value, EncodedValue>(
126
- id: Id,
127
- definition: { readonly key: S.Codec<Key, string>; readonly value: S.Codec<Value, EncodedValue> },
128
- ) => {
129
- type SelfDefinition = Definition<Id, Key, Value, EncodedValue>;
130
- const kvDefinition: SelfDefinition = {
131
- id,
132
- key: definition.key,
133
- value: definition.value,
134
- };
164
+ const resource = value as Record<string, unknown>;
135
165
 
136
- return Object.assign(kvDefinition, {
137
- Binding:
138
- <Self>() =>
139
- <BindingId extends string>(
140
- bindingId: BindingId,
141
- binding: Omit<KvDefinition<Key, Value, EncodedValue>, "key" | "value">,
142
- ) =>
143
- Service<Self>()<BindingId, Key, Value, EncodedValue>(bindingId, {
144
- ...binding,
145
- key: definition.key,
146
- value: definition.value,
147
- }),
148
- binding: <BindingId extends string>(
149
- bindingId: BindingId,
150
- binding: Omit<KvDefinition<Key, Value, EncodedValue>, "key" | "value">,
151
- ) =>
152
- Service<BindingService<BindingId, SelfDefinition>>()<BindingId, Key, Value, EncodedValue>(
153
- bindingId,
154
- {
155
- ...binding,
156
- key: definition.key,
157
- value: definition.value,
158
- },
159
- ),
160
- });
166
+ return (
167
+ typeof resource.get === "function" &&
168
+ typeof resource.put === "function" &&
169
+ typeof resource.delete === "function" &&
170
+ typeof resource.getWithMetadata === "function" &&
171
+ typeof resource.list === "function"
172
+ );
161
173
  };
162
174
 
163
- export const Tag =
164
- <_Self>() =>
165
- <Id extends string, Key, Value, EncodedValue>(
166
- id: Id,
167
- definition: {
168
- readonly key: S.Codec<Key, string>;
169
- readonly value: S.Codec<Value, EncodedValue>;
170
- },
171
- ) => {
172
- const kvDefinition = makeDefinition(id, definition);
173
-
174
- abstract class KvDefinitionClass {
175
- static readonly id = kvDefinition.id;
176
- static readonly key = kvDefinition.key;
177
- static readonly value = kvDefinition.value;
178
- static readonly Binding = kvDefinition.Binding;
179
- static readonly binding = kvDefinition.binding;
180
- }
181
-
182
- return KvDefinitionClass as (abstract new () => object) & typeof kvDefinition;
183
- };
184
-
185
- /**
186
- * Builds a KV service around a Cloudflare KV namespace binding.
187
- *
188
- * Returned service includes schema-aware `put`, `get`, `getWithMetadata`,
189
- * `list`, and `remove` helpers.
190
- *
191
- * @example
192
- * ```ts
193
- * const Sessions = Kv.make("Sessions", {
194
- * binding: "SESSIONS",
195
- * key: Schema.String,
196
- * value: Schema.Struct({ userId: Schema.String }),
197
- * });
198
- *
199
- * const program = Effect.gen(function* () {
200
- * yield* Sessions.put("abc", { userId: "u_1" });
201
- * return yield* Sessions.get("abc");
202
- * });
203
- * ```
204
- */
205
- export const Service =
206
- <Self>() =>
207
- <Id extends string, Key, Value, EncodedValue>(
208
- id: Id,
209
- definition: KvDefinition<Key, Value, EncodedValue>,
210
- ) => {
211
- const tag = Binding.Service<Self>()(id, definition.binding, (value): value is KVNamespace => {
212
- if (typeof value !== "object" || value === null) {
213
- return false;
214
- }
215
-
216
- const resource = value as Record<string, unknown>;
217
-
218
- return (
219
- typeof resource.get === "function" &&
220
- typeof resource.put === "function" &&
221
- typeof resource.delete === "function" &&
222
- typeof resource.getWithMetadata === "function" &&
223
- typeof resource.list === "function"
224
- );
225
- });
226
-
227
- const encodeKey = S.encodeEffect(definition.key);
228
- const decodeKey = S.decodeUnknownEffect(definition.key);
229
- const encodeValue = S.encodeEffect(S.fromJsonString(S.toCodecJson(definition.value)));
230
- const decodeValue = S.decodeUnknownEffect(S.fromJsonString(S.toCodecJson(definition.value)));
231
-
232
- const put = Effect.fnUntraced(function* (key: Key, value: Value, options?: KvPutOptions) {
233
- const kv = yield* tag;
175
+ export const makeClient = <Key, Value, EncodedValue>(
176
+ definition: KvDefinition<Key, Value, EncodedValue>,
177
+ ): ((kv: KVNamespace) => KvClient<Key, Value, EncodedValue>) => {
178
+ const encodeKey = S.encodeEffect(definition.key);
179
+ const decodeKey = S.decodeUnknownEffect(definition.key);
180
+ const encodeValue = S.encodeEffect(S.fromJsonString(S.toCodecJson(definition.value)));
181
+ const decodeValue = S.decodeUnknownEffect(S.fromJsonString(S.toCodecJson(definition.value)));
182
+
183
+ return (kv) => ({
184
+ definition,
185
+ put: Effect.fnUntraced(function* (key: Key, value: Value, options?: KvPutOptions) {
234
186
  const keyEncoded = yield* encodeKey(key);
235
187
  const valueEncoded = yield* encodeValue(value);
236
188
  yield* tryKvPromise(definition.binding, "put", () =>
237
189
  kv.put(keyEncoded, valueEncoded, options),
238
190
  );
239
- });
240
-
241
- const get = Effect.fnUntraced(function* (key: Key) {
242
- const kv = yield* tag;
191
+ }),
192
+ get: Effect.fnUntraced(function* (key: Key) {
243
193
  const keyEncoded = yield* encodeKey(key);
244
194
  const valueEncoded = yield* tryKvPromise(definition.binding, "get", () => kv.get(keyEncoded));
245
195
 
@@ -248,13 +198,11 @@ export const Service =
248
198
  }
249
199
 
250
200
  return yield* decodeValue(valueEncoded).pipe(Effect.map(Option.some));
251
- });
252
-
253
- const getWithMetadata = Effect.fnUntraced(function* <Metadata>(
201
+ }),
202
+ getWithMetadata: Effect.fnUntraced(function* <Metadata>(
254
203
  key: Key,
255
204
  metadataSchema: S.Codec<Metadata, unknown>,
256
205
  ) {
257
- const kv = yield* tag;
258
206
  const keyEncoded = yield* encodeKey(key);
259
207
  const result = yield* tryKvPromise(definition.binding, "getWithMetadata", () =>
260
208
  kv.getWithMetadata<Metadata>(keyEncoded),
@@ -275,12 +223,8 @@ export const Service =
275
223
  metadata,
276
224
  cacheStatus: maybeString(result.cacheStatus),
277
225
  });
278
- });
279
-
280
- const list = Effect.fnUntraced(function* <Metadata = unknown>(
281
- options?: KvListOptions<Metadata>,
282
- ) {
283
- const kv = yield* tag;
226
+ }),
227
+ list: Effect.fnUntraced(function* <Metadata = unknown>(options?: KvListOptions<Metadata>) {
284
228
  const { metadataSchema, ...kvOptions } = options ?? {};
285
229
  const result = yield* tryKvPromise(definition.binding, "list", () =>
286
230
  kv.list<Metadata>(kvOptions),
@@ -309,26 +253,109 @@ export const Service =
309
253
  cursor: maybeString("cursor" in result ? result.cursor : undefined),
310
254
  cacheStatus: maybeString(result.cacheStatus),
311
255
  };
256
+ }),
257
+ remove: Effect.fnUntraced(function* (key: Key) {
258
+ const keyEncoded = yield* encodeKey(key);
259
+ yield* tryKvPromise(definition.binding, "delete", () => kv.delete(keyEncoded));
260
+ }),
261
+ unsafeRaw: Effect.succeed(kv),
262
+ });
263
+ };
264
+
265
+ export const layer = <Self, Key, Value, EncodedValue>(
266
+ tag: Context.Service<Self, KvClient<Key, Value, EncodedValue>>,
267
+ definition: KvDefinition<Key, Value, EncodedValue>,
268
+ ) =>
269
+ Binding.layer(
270
+ tag,
271
+ definition.binding,
272
+ isKvNamespace,
273
+ makeClient<Key, Value, EncodedValue>(definition),
274
+ );
275
+
276
+ const makeDefinition = <Id extends string, Key, Value, EncodedValue>(
277
+ id: Id,
278
+ definition: { readonly key: S.Codec<Key, string>; readonly value: S.Codec<Value, EncodedValue> },
279
+ ) => {
280
+ type SelfDefinition = Definition<Id, Key, Value, EncodedValue>;
281
+ const kvDefinition: SelfDefinition = {
282
+ id,
283
+ key: definition.key,
284
+ value: definition.value,
285
+ };
286
+
287
+ return Object.assign(kvDefinition, {
288
+ layer: <Self>(
289
+ tag: Context.Service<Self, KvClient<Key, Value, EncodedValue>>,
290
+ binding: LayerOptions,
291
+ ) =>
292
+ layer(tag, {
293
+ ...binding,
294
+ key: definition.key,
295
+ value: definition.value,
296
+ }),
297
+ });
298
+ };
299
+
300
+ export const Tag =
301
+ <Self>() =>
302
+ <Id extends string, Key, Value, EncodedValue>(
303
+ id: Id,
304
+ definition: {
305
+ readonly key: S.Codec<Key, string>;
306
+ readonly value: S.Codec<Value, EncodedValue>;
307
+ },
308
+ ) => {
309
+ const kvDefinition = makeDefinition(id, definition);
310
+ const tag = Context.Service<Self, KvClient<Key, Value, EncodedValue>>()(id);
311
+
312
+ const makeLayer = (binding: LayerOptions) => kvDefinition.layer(tag, binding);
313
+
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);
312
337
  });
313
338
 
314
339
  const remove = Effect.fnUntraced(function* (key: Key) {
315
340
  const kv = yield* tag;
316
- const keyEncoded = yield* encodeKey(key);
317
- yield* tryKvPromise(definition.binding, "delete", () => kv.delete(keyEncoded));
341
+ yield* kv.remove(key);
318
342
  });
319
343
 
320
344
  const unsafeRaw = Effect.fnUntraced(function* () {
321
- return yield* tag;
345
+ const kv = yield* tag;
346
+ return yield* kv.unsafeRaw;
322
347
  });
323
348
 
324
349
  return Object.assign(tag, {
325
- [TypeId]: TypeId,
326
- definition,
350
+ id: kvDefinition.id,
351
+ keySchema: kvDefinition.key,
352
+ valueSchema: kvDefinition.value,
353
+ layer: makeLayer,
327
354
  put,
328
355
  get,
329
356
  getWithMetadata,
330
357
  list,
331
358
  remove,
332
359
  unsafeRaw,
333
- });
360
+ }) as TagClass<Self, Id, Key, Value, EncodedValue>;
334
361
  };
package/src/Queue.ts CHANGED
@@ -91,10 +91,6 @@ export const decodeBatch = <Body>(
91
91
  return fromMessageBatch(batch, messages);
92
92
  });
93
93
 
94
- export const Tag = QueueDefinition.Tag;
95
-
96
- export const implement = QueueDefinition.implement;
97
-
98
94
  export type Definition<
99
95
  Id extends string = string,
100
96
  Message extends QueueDefinition.Definition.Any["message"] =
@@ -105,6 +101,24 @@ export namespace Definition {
105
101
  export type Any = QueueDefinition.Definition.Any;
106
102
  }
107
103
 
104
+ export type LayerOptions = QueueDefinition.LayerOptions;
105
+
106
+ export type TagClass<
107
+ Self,
108
+ Id extends string,
109
+ Message extends QueueDefinition.Definition.Any["message"],
110
+ > = QueueDefinition.TagClass<Self, Id, Message>;
111
+
112
+ export const Tag: <Self>() => <
113
+ Id extends string,
114
+ Message extends QueueDefinition.Definition.Any["message"],
115
+ >(
116
+ id: Id,
117
+ definition: { readonly message: Message },
118
+ ) => TagClass<Self, Id, Message> = QueueDefinition.Tag;
119
+
120
+ export const implement = QueueDefinition.implement;
121
+
108
122
  export type Handler<ROut, Self extends QueueDefinition.Definition.Any> = QueueDefinition.Handler<
109
123
  ROut,
110
124
  Self
@@ -1,10 +1,8 @@
1
- import { Data, Effect, Schema as S } from "effect";
1
+ import { Context, Data, Effect, Schema as S } from "effect";
2
2
 
3
3
  import * as Binding from "./Binding";
4
4
  import type * as RpcDefinition from "./RpcDefinition";
5
5
 
6
- const TypeId = "effect-cf/QueueBinding" as const;
7
-
8
6
  export type QueueSendOptions = globalThis.QueueSendOptions;
9
7
  export type QueueSendResponse = globalThis.QueueSendResponse;
10
8
  export type QueueSendBatchOptions = globalThis.QueueSendBatchOptions;
@@ -19,15 +17,17 @@ export interface QueueBindingDefinition<Message extends RpcDefinition.ServiceFre
19
17
  readonly message: Message;
20
18
  }
21
19
 
22
- declare const QueueServiceTypeId: unique symbol;
23
-
24
- /** Nominal service marker for Queue services created with {@link make}. */
25
- export interface QueueService<Id extends string, Message extends RpcDefinition.ServiceFreeSchema> {
26
- readonly [QueueServiceTypeId]: {
27
- readonly id: Id;
28
- readonly message: S.Schema.Type<Message>;
29
- readonly encodedMessage: S.Codec.Encoded<Message>;
30
- };
20
+ export interface QueueBindingClient<Message extends RpcDefinition.ServiceFreeSchema> {
21
+ readonly send: (
22
+ message: S.Schema.Type<Message>,
23
+ options?: QueueSendOptions,
24
+ ) => Effect.Effect<void, QueueOperationError | S.SchemaError>;
25
+ readonly sendBatch: (
26
+ messages: Iterable<MessageSendRequest<S.Schema.Type<Message>>>,
27
+ options?: QueueSendBatchOptions,
28
+ ) => Effect.Effect<void, QueueOperationError | S.SchemaError>;
29
+ readonly metrics: () => Effect.Effect<QueueMetrics, QueueOperationError>;
30
+ readonly unsafeRaw: Effect.Effect<globalThis.Queue<S.Codec.Encoded<Message>>>;
31
31
  }
32
32
 
33
33
  export class QueueOperationError extends Data.TaggedError("QueueOperationError")<{
@@ -49,7 +49,7 @@ const tryQueuePromise = <A>(
49
49
  catch: (cause) => queueError(binding, operation, cause),
50
50
  });
51
51
 
52
- const isQueue = <Body>(value: unknown): value is globalThis.Queue<Body> => {
52
+ export const isQueue = <Body>(value: unknown): value is globalThis.Queue<Body> => {
53
53
  if (typeof value !== "object" || value === null) {
54
54
  return false;
55
55
  }
@@ -63,44 +63,24 @@ const isQueue = <Body>(value: unknown): value is globalThis.Queue<Body> => {
63
63
  );
64
64
  };
65
65
 
66
- /** Creates a typed Queue binding service tag plus Effect helpers. */
67
- export const make = <Id extends string, Message extends RpcDefinition.ServiceFreeSchema>(
68
- id: Id,
66
+ export const makeClient = <Message extends RpcDefinition.ServiceFreeSchema>(
69
67
  definition: QueueBindingDefinition<Message>,
70
- ) => Service<QueueService<Id, Message>>()<Id, Message>(id, definition);
71
-
72
- /**
73
- * Builds an Effect service around a Cloudflare Queue producer binding.
74
- */
75
- export const Service =
76
- <Self>() =>
77
- <Id extends string, Message extends RpcDefinition.ServiceFreeSchema>(
78
- id: Id,
79
- definition: QueueBindingDefinition<Message>,
80
- ) => {
81
- type Body = S.Schema.Type<Message>;
82
- type EncodedBody = S.Codec.Encoded<Message>;
83
-
84
- const tag = Binding.Service<Self>()(
85
- id,
86
- definition.binding,
87
- (value): value is globalThis.Queue<EncodedBody> => isQueue<EncodedBody>(value),
88
- );
89
-
90
- const encodeMessage = S.encodeEffect(definition.message);
91
-
92
- const send = Effect.fnUntraced(function* (message: Body, options?: QueueSendOptions) {
93
- const queue = yield* tag;
94
- const encoded = yield* encodeMessage(message);
68
+ ): ((queue: globalThis.Queue<S.Codec.Encoded<Message>>) => QueueBindingClient<Message>) => {
69
+ type Body = S.Schema.Type<Message>;
70
+ type EncodedBody = S.Codec.Encoded<Message>;
95
71
 
96
- return yield* tryQueuePromise(definition.binding, "send", () => queue.send(encoded, options));
97
- });
72
+ const encodeMessage = S.encodeEffect(definition.message);
73
+
74
+ return (queue) => ({
75
+ send: Effect.fnUntraced(function* (message: Body, options?: QueueSendOptions) {
76
+ const encoded = yield* encodeMessage(message);
98
77
 
99
- const sendBatch = Effect.fnUntraced(function* (
78
+ yield* tryQueuePromise(definition.binding, "send", () => queue.send(encoded, options));
79
+ }),
80
+ sendBatch: Effect.fnUntraced(function* (
100
81
  messages: Iterable<MessageSendRequest<Body>>,
101
82
  options?: QueueSendBatchOptions,
102
83
  ) {
103
- const queue = yield* tag;
104
84
  const encodedMessages: Array<MessageSendRequest<EncodedBody>> = [];
105
85
 
106
86
  for (const message of messages) {
@@ -110,22 +90,23 @@ export const Service =
110
90
  });
111
91
  }
112
92
 
113
- return yield* tryQueuePromise(definition.binding, "sendBatch", () =>
93
+ yield* tryQueuePromise(definition.binding, "sendBatch", () =>
114
94
  queue.sendBatch(encodedMessages, options),
115
95
  );
116
- });
117
-
118
- const metrics = Effect.fnUntraced(function* () {
119
- const queue = yield* tag;
120
-
121
- return yield* tryQueuePromise(definition.binding, "metrics", () => queue.metrics());
122
- });
123
-
124
- return Object.assign(tag, {
125
- [TypeId]: TypeId,
126
- binding: definition.binding,
127
- send,
128
- sendBatch,
129
- metrics,
130
- });
131
- };
96
+ }),
97
+ metrics: () => tryQueuePromise(definition.binding, "metrics", () => queue.metrics()),
98
+ unsafeRaw: Effect.succeed(queue),
99
+ });
100
+ };
101
+
102
+ export const layer = <Self, Message extends RpcDefinition.ServiceFreeSchema>(
103
+ tag: Context.Service<Self, QueueBindingClient<Message>>,
104
+ definition: QueueBindingDefinition<Message>,
105
+ ) =>
106
+ Binding.layer(
107
+ tag,
108
+ definition.binding,
109
+ (value): value is globalThis.Queue<S.Codec.Encoded<Message>> =>
110
+ isQueue<S.Codec.Encoded<Message>>(value),
111
+ makeClient(definition),
112
+ );