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/CHANGELOG.md +14 -0
- package/README.md +36 -29
- package/dist/index.d.mts +854 -1216
- package/dist/index.mjs +469 -441
- package/package.json +1 -1
- package/src/Binding.ts +36 -13
- package/src/DurableObject.ts +117 -23
- package/src/DurableObjectDefinition.ts +251 -72
- package/src/DurableObjectNamespace.ts +305 -158
- package/src/Kv.ts +181 -154
- package/src/Queue.ts +18 -4
- package/src/QueueBinding.ts +43 -62
- package/src/QueueDefinition.ts +87 -41
- package/src/ServiceBinding.ts +288 -81
- package/src/Worker.ts +114 -20
- package/src/WorkerDefinition.ts +187 -63
- package/src/Workflow.ts +23 -4
- package/src/WorkflowBinding.ts +100 -109
- package/src/WorkflowDefinition.ts +116 -51
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
96
|
+
export type LayerOptions = {
|
|
97
|
+
readonly binding: string;
|
|
98
|
+
};
|
|
88
99
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
) => {
|
|
172
|
-
|
|
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
|
-
|
|
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
|
-
|
|
345
|
+
const kv = yield* tag;
|
|
346
|
+
return yield* kv.unsafeRaw;
|
|
322
347
|
});
|
|
323
348
|
|
|
324
349
|
return Object.assign(tag, {
|
|
325
|
-
|
|
326
|
-
|
|
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
|
package/src/QueueBinding.ts
CHANGED
|
@@ -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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
+
yield* tryQueuePromise(definition.binding, "sendBatch", () =>
|
|
114
94
|
queue.sendBatch(encodedMessages, options),
|
|
115
95
|
);
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
+
);
|