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