effect-cf 0.4.0 → 0.6.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 +12 -0
- package/README.md +9 -2
- package/dist/index.d.mts +237 -335
- package/dist/index.mjs +299 -109
- package/package.json +3 -1
- package/src/Binding.ts +21 -5
- package/src/D1.ts +135 -0
- package/src/DurableObjectNamespace.ts +227 -32
- package/src/Kv.ts +133 -80
- package/src/QueueBinding.ts +51 -18
- package/src/ServiceBinding.ts +147 -84
- package/src/index.ts +1 -0
package/src/Kv.ts
CHANGED
|
@@ -84,6 +84,28 @@ export interface KvService<Id extends string, Key, Value, EncodedValue> {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
export interface KvClient<Key, Value, EncodedValue> {
|
|
88
|
+
readonly put: (
|
|
89
|
+
key: Key,
|
|
90
|
+
value: Value,
|
|
91
|
+
options?: KvPutOptions,
|
|
92
|
+
) => Effect.Effect<void, KvOperationError | S.SchemaError>;
|
|
93
|
+
readonly get: (key: Key) => Effect.Effect<Option.Option<Value>, KvOperationError | S.SchemaError>;
|
|
94
|
+
readonly getWithMetadata: <Metadata>(
|
|
95
|
+
key: Key,
|
|
96
|
+
metadataSchema: S.Codec<Metadata, unknown>,
|
|
97
|
+
) => Effect.Effect<
|
|
98
|
+
Option.Option<KvWithMetadata<Value, Metadata>>,
|
|
99
|
+
KvOperationError | S.SchemaError
|
|
100
|
+
>;
|
|
101
|
+
readonly list: <Metadata = unknown>(
|
|
102
|
+
options?: KvListOptions<Metadata>,
|
|
103
|
+
) => Effect.Effect<KvListResult<Key, Metadata>, KvOperationError | S.SchemaError>;
|
|
104
|
+
readonly remove: (key: Key) => Effect.Effect<void, KvOperationError | S.SchemaError>;
|
|
105
|
+
readonly unsafeRaw: Effect.Effect<KVNamespace>;
|
|
106
|
+
readonly definition: KvDefinition<Key, Value, EncodedValue>;
|
|
107
|
+
}
|
|
108
|
+
|
|
87
109
|
declare const BindingServiceTypeId: unique symbol;
|
|
88
110
|
|
|
89
111
|
/** Nominal service marker for KV bindings created from a reusable {@link Definition}. */
|
|
@@ -208,46 +230,123 @@ export const Service =
|
|
|
208
230
|
id: Id,
|
|
209
231
|
definition: KvDefinition<Key, Value, EncodedValue>,
|
|
210
232
|
) => {
|
|
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
233
|
const encodeKey = S.encodeEffect(definition.key);
|
|
228
234
|
const decodeKey = S.decodeUnknownEffect(definition.key);
|
|
229
235
|
const encodeValue = S.encodeEffect(S.fromJsonString(S.toCodecJson(definition.value)));
|
|
230
236
|
const decodeValue = S.decodeUnknownEffect(S.fromJsonString(S.toCodecJson(definition.value)));
|
|
231
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
|
+
);
|
|
341
|
+
|
|
232
342
|
const put = Effect.fnUntraced(function* (key: Key, value: Value, options?: KvPutOptions) {
|
|
233
343
|
const kv = yield* tag;
|
|
234
|
-
|
|
235
|
-
const valueEncoded = yield* encodeValue(value);
|
|
236
|
-
yield* tryKvPromise(definition.binding, "put", () =>
|
|
237
|
-
kv.put(keyEncoded, valueEncoded, options),
|
|
238
|
-
);
|
|
344
|
+
yield* kv.put(key, value, options);
|
|
239
345
|
});
|
|
240
346
|
|
|
241
347
|
const get = Effect.fnUntraced(function* (key: Key) {
|
|
242
348
|
const kv = yield* tag;
|
|
243
|
-
|
|
244
|
-
const valueEncoded = yield* tryKvPromise(definition.binding, "get", () => kv.get(keyEncoded));
|
|
245
|
-
|
|
246
|
-
if (valueEncoded === null) {
|
|
247
|
-
return Option.none();
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return yield* decodeValue(valueEncoded).pipe(Effect.map(Option.some));
|
|
349
|
+
return yield* kv.get(key);
|
|
251
350
|
});
|
|
252
351
|
|
|
253
352
|
const getWithMetadata = Effect.fnUntraced(function* <Metadata>(
|
|
@@ -255,70 +354,24 @@ export const Service =
|
|
|
255
354
|
metadataSchema: S.Codec<Metadata, unknown>,
|
|
256
355
|
) {
|
|
257
356
|
const kv = yield* tag;
|
|
258
|
-
|
|
259
|
-
const result = yield* tryKvPromise(definition.binding, "getWithMetadata", () =>
|
|
260
|
-
kv.getWithMetadata<Metadata>(keyEncoded),
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
if (result.value === null) {
|
|
264
|
-
return Option.none();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const value = yield* decodeValue(result.value);
|
|
268
|
-
const metadata =
|
|
269
|
-
result.metadata === null
|
|
270
|
-
? Option.none<Metadata>()
|
|
271
|
-
: Option.some(yield* S.decodeUnknownEffect(metadataSchema)(result.metadata));
|
|
272
|
-
|
|
273
|
-
return Option.some({
|
|
274
|
-
value,
|
|
275
|
-
metadata,
|
|
276
|
-
cacheStatus: maybeString(result.cacheStatus),
|
|
277
|
-
});
|
|
357
|
+
return yield* kv.getWithMetadata(key, metadataSchema);
|
|
278
358
|
});
|
|
279
359
|
|
|
280
360
|
const list = Effect.fnUntraced(function* <Metadata = unknown>(
|
|
281
361
|
options?: KvListOptions<Metadata>,
|
|
282
362
|
) {
|
|
283
363
|
const kv = yield* tag;
|
|
284
|
-
|
|
285
|
-
const result = yield* tryKvPromise(definition.binding, "list", () =>
|
|
286
|
-
kv.list<Metadata>(kvOptions),
|
|
287
|
-
);
|
|
288
|
-
const keys: Array<KvListKey<Key, Metadata>> = [];
|
|
289
|
-
|
|
290
|
-
for (const key of result.keys) {
|
|
291
|
-
const decodedName = yield* decodeKey(key.name);
|
|
292
|
-
const decodedMetadata =
|
|
293
|
-
key.metadata === undefined
|
|
294
|
-
? Option.none<Metadata>()
|
|
295
|
-
: metadataSchema === undefined
|
|
296
|
-
? Option.some(key.metadata as Metadata)
|
|
297
|
-
: Option.some(yield* S.decodeUnknownEffect(metadataSchema)(key.metadata));
|
|
298
|
-
|
|
299
|
-
keys.push({
|
|
300
|
-
name: decodedName,
|
|
301
|
-
expiration: maybeNumber(key.expiration),
|
|
302
|
-
metadata: decodedMetadata,
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return {
|
|
307
|
-
keys,
|
|
308
|
-
listComplete: result.list_complete,
|
|
309
|
-
cursor: maybeString("cursor" in result ? result.cursor : undefined),
|
|
310
|
-
cacheStatus: maybeString(result.cacheStatus),
|
|
311
|
-
};
|
|
364
|
+
return yield* kv.list(options);
|
|
312
365
|
});
|
|
313
366
|
|
|
314
367
|
const remove = Effect.fnUntraced(function* (key: Key) {
|
|
315
368
|
const kv = yield* tag;
|
|
316
|
-
|
|
317
|
-
yield* tryKvPromise(definition.binding, "delete", () => kv.delete(keyEncoded));
|
|
369
|
+
yield* kv.remove(key);
|
|
318
370
|
});
|
|
319
371
|
|
|
320
372
|
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
321
|
-
|
|
373
|
+
const kv = yield* tag;
|
|
374
|
+
return yield* kv.unsafeRaw;
|
|
322
375
|
});
|
|
323
376
|
|
|
324
377
|
return Object.assign(tag, {
|
package/src/QueueBinding.ts
CHANGED
|
@@ -19,6 +19,19 @@ export interface QueueBindingDefinition<Message extends RpcDefinition.ServiceFre
|
|
|
19
19
|
readonly message: Message;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export interface QueueBindingClient<Message extends RpcDefinition.ServiceFreeSchema> {
|
|
23
|
+
readonly send: (
|
|
24
|
+
message: S.Schema.Type<Message>,
|
|
25
|
+
options?: QueueSendOptions,
|
|
26
|
+
) => Effect.Effect<void, QueueOperationError | S.SchemaError>;
|
|
27
|
+
readonly sendBatch: (
|
|
28
|
+
messages: Iterable<MessageSendRequest<S.Schema.Type<Message>>>,
|
|
29
|
+
options?: QueueSendBatchOptions,
|
|
30
|
+
) => Effect.Effect<void, QueueOperationError | S.SchemaError>;
|
|
31
|
+
readonly metrics: () => Effect.Effect<QueueMetrics, QueueOperationError>;
|
|
32
|
+
readonly unsafeRaw: Effect.Effect<globalThis.Queue<S.Codec.Encoded<Message>>>;
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
declare const QueueServiceTypeId: unique symbol;
|
|
23
36
|
|
|
24
37
|
/** Nominal service marker for Queue services created with {@link make}. */
|
|
@@ -81,19 +94,45 @@ export const Service =
|
|
|
81
94
|
type Body = S.Schema.Type<Message>;
|
|
82
95
|
type EncodedBody = S.Codec.Encoded<Message>;
|
|
83
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
|
+
|
|
84
126
|
const tag = Binding.Service<Self>()(
|
|
85
127
|
id,
|
|
86
128
|
definition.binding,
|
|
87
129
|
(value): value is globalThis.Queue<EncodedBody> => isQueue<EncodedBody>(value),
|
|
130
|
+
makeClient,
|
|
88
131
|
);
|
|
89
132
|
|
|
90
|
-
const encodeMessage = S.encodeEffect(definition.message);
|
|
91
|
-
|
|
92
133
|
const send = Effect.fnUntraced(function* (message: Body, options?: QueueSendOptions) {
|
|
93
134
|
const queue = yield* tag;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return yield* tryQueuePromise(definition.binding, "send", () => queue.send(encoded, options));
|
|
135
|
+
yield* queue.send(message, options);
|
|
97
136
|
});
|
|
98
137
|
|
|
99
138
|
const sendBatch = Effect.fnUntraced(function* (
|
|
@@ -101,24 +140,17 @@ export const Service =
|
|
|
101
140
|
options?: QueueSendBatchOptions,
|
|
102
141
|
) {
|
|
103
142
|
const queue = yield* tag;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
for (const message of messages) {
|
|
107
|
-
encodedMessages.push({
|
|
108
|
-
...message,
|
|
109
|
-
body: yield* encodeMessage(message.body),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return yield* tryQueuePromise(definition.binding, "sendBatch", () =>
|
|
114
|
-
queue.sendBatch(encodedMessages, options),
|
|
115
|
-
);
|
|
143
|
+
yield* queue.sendBatch(messages, options);
|
|
116
144
|
});
|
|
117
145
|
|
|
118
146
|
const metrics = Effect.fnUntraced(function* () {
|
|
119
147
|
const queue = yield* tag;
|
|
148
|
+
return yield* queue.metrics();
|
|
149
|
+
});
|
|
120
150
|
|
|
121
|
-
|
|
151
|
+
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
152
|
+
const queue = yield* tag;
|
|
153
|
+
return yield* queue.unsafeRaw;
|
|
122
154
|
});
|
|
123
155
|
|
|
124
156
|
return Object.assign(tag, {
|
|
@@ -127,5 +159,6 @@ export const Service =
|
|
|
127
159
|
send,
|
|
128
160
|
sendBatch,
|
|
129
161
|
metrics,
|
|
162
|
+
unsafeRaw,
|
|
130
163
|
});
|
|
131
164
|
};
|
package/src/ServiceBinding.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Data, Effect } from "effect";
|
|
1
|
+
import { Data, Effect, type Scope } from "effect";
|
|
2
2
|
|
|
3
3
|
import * as Binding from "./Binding";
|
|
4
4
|
import * as CloudflareRpc from "./Rpc";
|
|
@@ -75,25 +75,47 @@ type ServiceMethodSuccess<Api, Method extends keyof Api> = RpcInvocation.AsyncMe
|
|
|
75
75
|
Method
|
|
76
76
|
>;
|
|
77
77
|
|
|
78
|
-
type ServiceCall<
|
|
78
|
+
type ServiceCall<R, Api> = <Method extends ServiceMethodKey<Api>>(
|
|
79
79
|
method: Method,
|
|
80
80
|
...args: ServiceMethodArgs<Api, Method>
|
|
81
|
-
) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError,
|
|
81
|
+
) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError, R>;
|
|
82
82
|
|
|
83
|
-
type DefinitionDirectMethods<
|
|
83
|
+
type DefinitionDirectMethods<R, Definition extends WorkerDefinition.Definition.Any> = {
|
|
84
84
|
readonly [Method in RpcDefinition.Definition.MethodNames<Definition>]: (
|
|
85
85
|
...args: RpcDefinition.Method.Args<Definition["methods"][Method]>
|
|
86
86
|
) => Effect.Effect<
|
|
87
87
|
RpcDefinition.Method.Success<Definition["methods"][Method]>,
|
|
88
88
|
ServiceBindingRpcError,
|
|
89
|
-
|
|
89
|
+
R
|
|
90
90
|
>;
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
-
type DirectMethods<
|
|
94
|
-
? DefinitionDirectMethods<
|
|
93
|
+
type DirectMethods<R, Definition> = Definition extends WorkerDefinition.Definition.Any
|
|
94
|
+
? DefinitionDirectMethods<R, Definition>
|
|
95
95
|
: {};
|
|
96
96
|
|
|
97
|
+
export type ServiceBindingEffectClient<
|
|
98
|
+
Api extends object,
|
|
99
|
+
Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
|
|
100
|
+
> = DirectMethods<never, Definition> & {
|
|
101
|
+
readonly fetch: (
|
|
102
|
+
input: RequestInfo | URL,
|
|
103
|
+
init?: RequestInit,
|
|
104
|
+
) => Effect.Effect<globalThis.Response, ServiceBindingFetchError>;
|
|
105
|
+
readonly rpc: <Method extends ServiceMethodKey<Api>>(
|
|
106
|
+
method: Method,
|
|
107
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
108
|
+
) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError>;
|
|
109
|
+
readonly call: <Method extends ServiceMethodKey<Api>>(
|
|
110
|
+
method: Method,
|
|
111
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
112
|
+
) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError>;
|
|
113
|
+
readonly scopedCall: <Method extends ServiceMethodKey<Api>>(
|
|
114
|
+
method: Method,
|
|
115
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
116
|
+
) => Effect.Effect<Awaited<ServiceMethodSuccess<Api, Method>>, unknown, Scope.Scope>;
|
|
117
|
+
};
|
|
118
|
+
|
|
97
119
|
/**
|
|
98
120
|
* Creates a typed Effect service for a Worker service binding.
|
|
99
121
|
*
|
|
@@ -131,21 +153,125 @@ export const Service =
|
|
|
131
153
|
) => {
|
|
132
154
|
type ServiceApi = ApiOrDefinition<Api, Definition>;
|
|
133
155
|
|
|
156
|
+
const makeClient = (
|
|
157
|
+
service: ServiceBindingClient<ServiceApi>,
|
|
158
|
+
): ServiceBindingEffectClient<ServiceApi, Definition> => {
|
|
159
|
+
const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
|
|
160
|
+
Effect.tryPromise({
|
|
161
|
+
try: () => service.fetch(input, init),
|
|
162
|
+
catch: (cause) => new ServiceBindingFetchError({ binding: definition.binding, cause }),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const rpc = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
166
|
+
method: Method,
|
|
167
|
+
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
168
|
+
) =>
|
|
169
|
+
Effect.gen(function* () {
|
|
170
|
+
const methodName = String(method);
|
|
171
|
+
const encodedArgs =
|
|
172
|
+
definition.definition === undefined
|
|
173
|
+
? args
|
|
174
|
+
: yield* RpcDefinition.encodeArgs(
|
|
175
|
+
definition.definition,
|
|
176
|
+
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
177
|
+
args as never,
|
|
178
|
+
).pipe(
|
|
179
|
+
Effect.mapError(
|
|
180
|
+
(cause) =>
|
|
181
|
+
new ServiceBindingRpcError({
|
|
182
|
+
binding: definition.binding,
|
|
183
|
+
method: methodName,
|
|
184
|
+
cause,
|
|
185
|
+
}),
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return yield* RpcInvocation.invokeRpcMethod(
|
|
190
|
+
service,
|
|
191
|
+
method,
|
|
192
|
+
encodedArgs as ServiceMethodArgs<ServiceApi, Method>,
|
|
193
|
+
(cause) =>
|
|
194
|
+
new ServiceBindingRpcError({
|
|
195
|
+
binding: definition.binding,
|
|
196
|
+
method: methodName,
|
|
197
|
+
cause,
|
|
198
|
+
}),
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const call = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
203
|
+
method: Method,
|
|
204
|
+
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
205
|
+
) =>
|
|
206
|
+
Effect.gen(function* () {
|
|
207
|
+
const methodName = String(method);
|
|
208
|
+
const value = yield* CloudflareRpc.resolve(yield* rpc(method, ...args)).pipe(
|
|
209
|
+
Effect.mapError(
|
|
210
|
+
(cause) =>
|
|
211
|
+
new ServiceBindingRpcError({
|
|
212
|
+
binding: definition.binding,
|
|
213
|
+
method: methodName,
|
|
214
|
+
cause,
|
|
215
|
+
}),
|
|
216
|
+
),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (definition.definition === undefined) {
|
|
220
|
+
return value as ServiceMethodSuccess<ServiceApi, Method>;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const decoded = yield* RpcDefinition.decodeSuccess(
|
|
224
|
+
definition.definition,
|
|
225
|
+
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
226
|
+
value,
|
|
227
|
+
).pipe(
|
|
228
|
+
Effect.mapError(
|
|
229
|
+
(cause) =>
|
|
230
|
+
new ServiceBindingRpcError({
|
|
231
|
+
binding: definition.binding,
|
|
232
|
+
method: methodName,
|
|
233
|
+
cause,
|
|
234
|
+
}),
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
return decoded as ServiceMethodSuccess<ServiceApi, Method>;
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const scopedCall = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
242
|
+
method: Method,
|
|
243
|
+
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
244
|
+
) =>
|
|
245
|
+
Effect.gen(function* () {
|
|
246
|
+
const result = yield* rpc(method, ...args);
|
|
247
|
+
return yield* CloudflareRpc.scoped(result);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const directMethods = makeDirectMethods<never, ServiceApi, Definition>(
|
|
251
|
+
definition.definition,
|
|
252
|
+
call,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
return Object.assign(directMethods, {
|
|
256
|
+
fetch,
|
|
257
|
+
rpc,
|
|
258
|
+
call,
|
|
259
|
+
scopedCall,
|
|
260
|
+
}) as ServiceBindingEffectClient<ServiceApi, Definition>;
|
|
261
|
+
};
|
|
262
|
+
|
|
134
263
|
const tag = Binding.Service<Self>()(
|
|
135
264
|
id,
|
|
136
265
|
definition.binding,
|
|
137
266
|
(value): value is ServiceBindingClient<ServiceApi> =>
|
|
138
267
|
typeof value === "object" && value !== null && "fetch" in value,
|
|
268
|
+
makeClient,
|
|
139
269
|
);
|
|
140
270
|
|
|
141
271
|
const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
|
|
142
272
|
Effect.gen(function* () {
|
|
143
273
|
const service = yield* tag;
|
|
144
|
-
|
|
145
|
-
return yield* Effect.tryPromise({
|
|
146
|
-
try: () => service.fetch(input, init),
|
|
147
|
-
catch: (cause) => new ServiceBindingFetchError({ binding: definition.binding, cause }),
|
|
148
|
-
});
|
|
274
|
+
return yield* service.fetch(input, init);
|
|
149
275
|
});
|
|
150
276
|
|
|
151
277
|
const rpc = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
@@ -154,36 +280,7 @@ export const Service =
|
|
|
154
280
|
) =>
|
|
155
281
|
Effect.gen(function* () {
|
|
156
282
|
const service = yield* tag;
|
|
157
|
-
|
|
158
|
-
const encodedArgs =
|
|
159
|
-
definition.definition === undefined
|
|
160
|
-
? args
|
|
161
|
-
: yield* RpcDefinition.encodeArgs(
|
|
162
|
-
definition.definition,
|
|
163
|
-
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
164
|
-
args as never,
|
|
165
|
-
).pipe(
|
|
166
|
-
Effect.mapError(
|
|
167
|
-
(cause) =>
|
|
168
|
-
new ServiceBindingRpcError({
|
|
169
|
-
binding: definition.binding,
|
|
170
|
-
method: methodName,
|
|
171
|
-
cause,
|
|
172
|
-
}),
|
|
173
|
-
),
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
return yield* RpcInvocation.invokeRpcMethod(
|
|
177
|
-
service,
|
|
178
|
-
method,
|
|
179
|
-
encodedArgs as ServiceMethodArgs<ServiceApi, Method>,
|
|
180
|
-
(cause) =>
|
|
181
|
-
new ServiceBindingRpcError({
|
|
182
|
-
binding: definition.binding,
|
|
183
|
-
method: methodName,
|
|
184
|
-
cause,
|
|
185
|
-
}),
|
|
186
|
-
);
|
|
283
|
+
return yield* service.rpc(method, ...args);
|
|
187
284
|
});
|
|
188
285
|
|
|
189
286
|
const call = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
@@ -191,38 +288,8 @@ export const Service =
|
|
|
191
288
|
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
192
289
|
) =>
|
|
193
290
|
Effect.gen(function* () {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
Effect.mapError(
|
|
197
|
-
(cause) =>
|
|
198
|
-
new ServiceBindingRpcError({
|
|
199
|
-
binding: definition.binding,
|
|
200
|
-
method: methodName,
|
|
201
|
-
cause,
|
|
202
|
-
}),
|
|
203
|
-
),
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
if (definition.definition === undefined) {
|
|
207
|
-
return value as ServiceMethodSuccess<ServiceApi, Method>;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const decoded = yield* RpcDefinition.decodeSuccess(
|
|
211
|
-
definition.definition,
|
|
212
|
-
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
213
|
-
value,
|
|
214
|
-
).pipe(
|
|
215
|
-
Effect.mapError(
|
|
216
|
-
(cause) =>
|
|
217
|
-
new ServiceBindingRpcError({
|
|
218
|
-
binding: definition.binding,
|
|
219
|
-
method: methodName,
|
|
220
|
-
cause,
|
|
221
|
-
}),
|
|
222
|
-
),
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
return decoded as ServiceMethodSuccess<ServiceApi, Method>;
|
|
291
|
+
const service = yield* tag;
|
|
292
|
+
return yield* service.call(method, ...args);
|
|
226
293
|
});
|
|
227
294
|
|
|
228
295
|
const scopedCall = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
@@ -230,8 +297,8 @@ export const Service =
|
|
|
230
297
|
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
231
298
|
) =>
|
|
232
299
|
Effect.gen(function* () {
|
|
233
|
-
const
|
|
234
|
-
return yield*
|
|
300
|
+
const service = yield* tag;
|
|
301
|
+
return yield* service.scopedCall(method, ...args);
|
|
235
302
|
});
|
|
236
303
|
|
|
237
304
|
const directMethods = makeDirectMethods<Self, ServiceApi, Definition>(
|
|
@@ -257,14 +324,10 @@ export const Service =
|
|
|
257
324
|
};
|
|
258
325
|
};
|
|
259
326
|
|
|
260
|
-
const makeDirectMethods = <
|
|
261
|
-
Self,
|
|
262
|
-
Api,
|
|
263
|
-
Definition extends WorkerDefinition.Definition.Any | undefined,
|
|
264
|
-
>(
|
|
327
|
+
const makeDirectMethods = <R, Api, Definition extends WorkerDefinition.Definition.Any | undefined>(
|
|
265
328
|
rpcDefinition: Definition | undefined,
|
|
266
|
-
call: ServiceCall<
|
|
267
|
-
): DirectMethods<
|
|
329
|
+
call: ServiceCall<R, Api>,
|
|
330
|
+
): DirectMethods<R, Definition> => {
|
|
268
331
|
const methods = {} as Record<string, unknown>;
|
|
269
332
|
|
|
270
333
|
if (rpcDefinition !== undefined) {
|
|
@@ -274,5 +337,5 @@ const makeDirectMethods = <
|
|
|
274
337
|
}
|
|
275
338
|
}
|
|
276
339
|
|
|
277
|
-
return methods as DirectMethods<
|
|
340
|
+
return methods as DirectMethods<R, Definition>;
|
|
278
341
|
};
|
package/src/index.ts
CHANGED