effect-cf 0.5.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 +6 -0
- package/README.md +7 -1
- package/dist/index.d.mts +173 -345
- package/dist/index.mjs +210 -103
- package/package.json +1 -1
- package/src/Binding.ts +21 -5
- package/src/DurableObjectNamespace.ts +227 -32
- package/src/Kv.ts +133 -80
- package/src/QueueBinding.ts +51 -18
- package/src/ServiceBinding.ts +147 -84
|
@@ -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";
|
|
@@ -110,21 +110,21 @@ type StubMethodSuccess<Api, Method extends keyof Api> = RpcInvocation.AsyncMetho
|
|
|
110
110
|
Method
|
|
111
111
|
>;
|
|
112
112
|
|
|
113
|
-
type StubCall<Api extends object> = <Method extends StubMethodKey<Api>>(
|
|
113
|
+
type StubCall<R, Api extends object> = <Method extends StubMethodKey<Api>>(
|
|
114
114
|
stub: DurableObjectStubClient<Api>,
|
|
115
115
|
method: Method,
|
|
116
116
|
...args: StubMethodArgs<Api, Method>
|
|
117
|
-
) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError>;
|
|
117
|
+
) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError, R>;
|
|
118
118
|
|
|
119
|
-
type DurableObjectDirectClient<
|
|
119
|
+
type DurableObjectDirectClient<R, Api extends object> = {
|
|
120
120
|
readonly fetch: (
|
|
121
121
|
input: RequestInfo | URL,
|
|
122
122
|
init?: RequestInit,
|
|
123
|
-
) => Effect.Effect<globalThis.Response, DurableObjectFetchError,
|
|
123
|
+
) => Effect.Effect<globalThis.Response, DurableObjectFetchError, R>;
|
|
124
124
|
} & {
|
|
125
125
|
readonly [Method in StubMethodKey<Api>]: (
|
|
126
126
|
...args: StubMethodArgs<Api, Method>
|
|
127
|
-
) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError,
|
|
127
|
+
) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError, R>;
|
|
128
128
|
};
|
|
129
129
|
|
|
130
130
|
type DefinitionNamespaceDirectMethods<
|
|
@@ -141,46 +141,89 @@ type DefinitionNamespaceDirectMethods<
|
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
type DefinitionDurableObjectDirectClient<
|
|
144
|
-
|
|
144
|
+
R,
|
|
145
145
|
Definition extends DurableObjectDefinition.Definition.Any,
|
|
146
146
|
> = {
|
|
147
147
|
readonly fetch: (
|
|
148
148
|
input: RequestInfo | URL,
|
|
149
149
|
init?: RequestInit,
|
|
150
|
-
) => Effect.Effect<globalThis.Response, DurableObjectFetchError,
|
|
150
|
+
) => Effect.Effect<globalThis.Response, DurableObjectFetchError, R>;
|
|
151
151
|
} & {
|
|
152
152
|
readonly [Method in RpcDefinition.Definition.MethodNames<Definition>]: (
|
|
153
153
|
...args: RpcDefinition.Method.Args<Definition["methods"][Method]>
|
|
154
154
|
) => Effect.Effect<
|
|
155
155
|
RpcDefinition.Method.Success<Definition["methods"][Method]>,
|
|
156
156
|
DurableObjectRpcError,
|
|
157
|
-
|
|
157
|
+
R
|
|
158
158
|
>;
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
type DefinitionNamespaceDirectClientMethods<
|
|
162
|
-
|
|
162
|
+
R,
|
|
163
163
|
Definition extends DurableObjectDefinition.Definition.Any,
|
|
164
164
|
> = {
|
|
165
165
|
readonly byName: (
|
|
166
166
|
name: string,
|
|
167
167
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
168
|
-
) => DefinitionDurableObjectDirectClient<
|
|
168
|
+
) => DefinitionDurableObjectDirectClient<R, Definition>;
|
|
169
169
|
readonly byId: (
|
|
170
170
|
id: globalThis.DurableObjectId,
|
|
171
171
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
172
|
-
) => DefinitionDurableObjectDirectClient<
|
|
172
|
+
) => DefinitionDurableObjectDirectClient<R, Definition>;
|
|
173
173
|
};
|
|
174
174
|
|
|
175
175
|
type DirectMethods<
|
|
176
|
-
|
|
176
|
+
R,
|
|
177
177
|
Api extends object,
|
|
178
178
|
Definition,
|
|
179
179
|
> = Definition extends DurableObjectDefinition.Definition.Any
|
|
180
180
|
? DefinitionNamespaceDirectMethods<Api, Definition> &
|
|
181
|
-
DefinitionNamespaceDirectClientMethods<
|
|
181
|
+
DefinitionNamespaceDirectClientMethods<R, Definition>
|
|
182
182
|
: {};
|
|
183
183
|
|
|
184
|
+
export type DurableObjectNamespaceEffectClient<
|
|
185
|
+
Api extends object,
|
|
186
|
+
Definition extends DurableObjectDefinition.Definition.Any | undefined = undefined,
|
|
187
|
+
> = DirectMethods<never, Api, Definition> & {
|
|
188
|
+
readonly newUniqueId: (
|
|
189
|
+
options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions,
|
|
190
|
+
) => Effect.Effect<globalThis.DurableObjectId>;
|
|
191
|
+
readonly idFromName: (name: string) => Effect.Effect<globalThis.DurableObjectId>;
|
|
192
|
+
readonly idFromString: (id: string) => Effect.Effect<globalThis.DurableObjectId>;
|
|
193
|
+
readonly get: (
|
|
194
|
+
id: globalThis.DurableObjectId,
|
|
195
|
+
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
196
|
+
) => Effect.Effect<DurableObjectStubClient<Api>>;
|
|
197
|
+
readonly getByName: (
|
|
198
|
+
name: string,
|
|
199
|
+
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
200
|
+
) => Effect.Effect<DurableObjectStubClient<Api>>;
|
|
201
|
+
readonly jurisdiction: (
|
|
202
|
+
jurisdiction: globalThis.DurableObjectJurisdiction,
|
|
203
|
+
) => Effect.Effect<DurableObjectNamespaceClient<Api>>;
|
|
204
|
+
readonly fetch: (
|
|
205
|
+
stub: DurableObjectStubClient<Api>,
|
|
206
|
+
input: RequestInfo | URL,
|
|
207
|
+
init?: RequestInit,
|
|
208
|
+
) => Effect.Effect<globalThis.Response, DurableObjectFetchError>;
|
|
209
|
+
readonly rpc: <Method extends StubMethodKey<Api>>(
|
|
210
|
+
stub: DurableObjectStubClient<Api>,
|
|
211
|
+
method: Method,
|
|
212
|
+
...args: StubMethodArgs<Api, Method>
|
|
213
|
+
) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError>;
|
|
214
|
+
readonly call: <Method extends StubMethodKey<Api>>(
|
|
215
|
+
stub: DurableObjectStubClient<Api>,
|
|
216
|
+
method: Method,
|
|
217
|
+
...args: StubMethodArgs<Api, Method>
|
|
218
|
+
) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError>;
|
|
219
|
+
readonly scopedCall: <Method extends StubMethodKey<Api>>(
|
|
220
|
+
stub: DurableObjectStubClient<Api>,
|
|
221
|
+
method: Method,
|
|
222
|
+
...args: StubMethodArgs<Api, Method>
|
|
223
|
+
) => Effect.Effect<Awaited<StubMethodSuccess<Api, Method>>, unknown, Scope.Scope>;
|
|
224
|
+
readonly unsafeRaw: Effect.Effect<DurableObjectNamespaceClient<Api>>;
|
|
225
|
+
};
|
|
226
|
+
|
|
184
227
|
/**
|
|
185
228
|
* Creates a typed Effect service for a Durable Object namespace binding.
|
|
186
229
|
*
|
|
@@ -230,25 +273,170 @@ export const Service =
|
|
|
230
273
|
hasFunction(value, "newUniqueId") &&
|
|
231
274
|
hasFunction(value, "jurisdiction");
|
|
232
275
|
|
|
233
|
-
const
|
|
234
|
-
|
|
276
|
+
const makeClient = (
|
|
277
|
+
namespace: NamespaceClient,
|
|
278
|
+
): DurableObjectNamespaceEffectClient<NamespaceApi, Definition> => {
|
|
279
|
+
const newUniqueId = (options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions) =>
|
|
280
|
+
Effect.sync(() => namespace.newUniqueId(options));
|
|
281
|
+
|
|
282
|
+
const idFromName = (name: string) => Effect.sync(() => namespace.idFromName(name));
|
|
283
|
+
|
|
284
|
+
const idFromString = (id: string) => Effect.sync(() => namespace.idFromString(id));
|
|
285
|
+
|
|
286
|
+
const get = (
|
|
287
|
+
id: globalThis.DurableObjectId,
|
|
288
|
+
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
289
|
+
) => Effect.sync(() => namespace.get(id, options));
|
|
290
|
+
|
|
291
|
+
const getByName = (
|
|
292
|
+
name: string,
|
|
293
|
+
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
294
|
+
) => Effect.sync(() => namespace.getByName(name, options));
|
|
295
|
+
|
|
296
|
+
const jurisdiction = (value: globalThis.DurableObjectJurisdiction) =>
|
|
297
|
+
Effect.sync(() => namespace.jurisdiction(value));
|
|
298
|
+
|
|
299
|
+
const fetch = (stub: StubClient, input: RequestInfo | URL, init?: RequestInit) =>
|
|
300
|
+
Effect.tryPromise({
|
|
301
|
+
try: () => stub.fetch(input, init),
|
|
302
|
+
catch: (cause) => new DurableObjectFetchError({ binding: definition.binding, cause }),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const rpc = <Method extends StubMethodKey<NamespaceApi>>(
|
|
306
|
+
stub: StubClient,
|
|
307
|
+
method: Method,
|
|
308
|
+
...args: StubMethodArgs<NamespaceApi, Method>
|
|
309
|
+
) =>
|
|
310
|
+
Effect.gen(function* () {
|
|
311
|
+
const methodName = String(method);
|
|
312
|
+
const encodedArgs =
|
|
313
|
+
definition.definition === undefined
|
|
314
|
+
? args
|
|
315
|
+
: yield* RpcDefinition.encodeArgs(
|
|
316
|
+
definition.definition,
|
|
317
|
+
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
318
|
+
args as never,
|
|
319
|
+
).pipe(
|
|
320
|
+
Effect.mapError(
|
|
321
|
+
(cause) =>
|
|
322
|
+
new DurableObjectRpcError({
|
|
323
|
+
binding: definition.binding,
|
|
324
|
+
method: methodName,
|
|
325
|
+
cause,
|
|
326
|
+
}),
|
|
327
|
+
),
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return yield* RpcInvocation.invokeRpcMethod(
|
|
331
|
+
stub,
|
|
332
|
+
method,
|
|
333
|
+
encodedArgs as StubMethodArgs<NamespaceApi, Method>,
|
|
334
|
+
(cause) =>
|
|
335
|
+
new DurableObjectRpcError({
|
|
336
|
+
binding: definition.binding,
|
|
337
|
+
method: methodName,
|
|
338
|
+
cause,
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const call = <Method extends StubMethodKey<NamespaceApi>>(
|
|
344
|
+
stub: StubClient,
|
|
345
|
+
method: Method,
|
|
346
|
+
...args: StubMethodArgs<NamespaceApi, Method>
|
|
347
|
+
) =>
|
|
348
|
+
Effect.gen(function* () {
|
|
349
|
+
const methodName = String(method);
|
|
350
|
+
const value = yield* CloudflareRpc.resolve(yield* rpc(stub, method, ...args)).pipe(
|
|
351
|
+
Effect.mapError(
|
|
352
|
+
(cause) =>
|
|
353
|
+
new DurableObjectRpcError({
|
|
354
|
+
binding: definition.binding,
|
|
355
|
+
method: methodName,
|
|
356
|
+
cause,
|
|
357
|
+
}),
|
|
358
|
+
),
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (definition.definition === undefined) {
|
|
362
|
+
return value as StubMethodSuccess<NamespaceApi, Method>;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const decoded = yield* RpcDefinition.decodeSuccess(
|
|
366
|
+
definition.definition,
|
|
367
|
+
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
368
|
+
value,
|
|
369
|
+
).pipe(
|
|
370
|
+
Effect.mapError(
|
|
371
|
+
(cause) =>
|
|
372
|
+
new DurableObjectRpcError({
|
|
373
|
+
binding: definition.binding,
|
|
374
|
+
method: methodName,
|
|
375
|
+
cause,
|
|
376
|
+
}),
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
return decoded as StubMethodSuccess<NamespaceApi, Method>;
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const scopedCall = <Method extends StubMethodKey<NamespaceApi>>(
|
|
384
|
+
stub: StubClient,
|
|
385
|
+
method: Method,
|
|
386
|
+
...args: StubMethodArgs<NamespaceApi, Method>
|
|
387
|
+
) =>
|
|
388
|
+
Effect.gen(function* () {
|
|
389
|
+
const result = yield* rpc(stub, method, ...args);
|
|
390
|
+
return yield* CloudflareRpc.scoped(result);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
const directMethods = makeDirectMethods<never, NamespaceApi, Definition>(
|
|
394
|
+
definition.definition,
|
|
395
|
+
{
|
|
396
|
+
call,
|
|
397
|
+
fetch,
|
|
398
|
+
get,
|
|
399
|
+
getByName,
|
|
400
|
+
},
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
return Object.assign(directMethods, {
|
|
404
|
+
newUniqueId,
|
|
405
|
+
idFromName,
|
|
406
|
+
idFromString,
|
|
407
|
+
get,
|
|
408
|
+
getByName,
|
|
409
|
+
jurisdiction,
|
|
410
|
+
fetch,
|
|
411
|
+
rpc,
|
|
412
|
+
call,
|
|
413
|
+
scopedCall,
|
|
414
|
+
unsafeRaw: Effect.succeed(namespace),
|
|
415
|
+
}) as DurableObjectNamespaceEffectClient<NamespaceApi, Definition>;
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const tag = Binding.Service<Self>()(
|
|
419
|
+
id,
|
|
420
|
+
definition.binding,
|
|
421
|
+
(value): value is NamespaceClient => isDurableObjectNamespaceClient(value),
|
|
422
|
+
makeClient,
|
|
235
423
|
);
|
|
236
424
|
|
|
237
425
|
const newUniqueId = Effect.fnUntraced(function* (
|
|
238
426
|
options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions,
|
|
239
427
|
) {
|
|
240
428
|
const namespace = yield* tag;
|
|
241
|
-
return namespace.newUniqueId(options);
|
|
429
|
+
return yield* namespace.newUniqueId(options);
|
|
242
430
|
});
|
|
243
431
|
|
|
244
432
|
const idFromName = Effect.fnUntraced(function* (name: string) {
|
|
245
433
|
const namespace = yield* tag;
|
|
246
|
-
return namespace.idFromName(name);
|
|
434
|
+
return yield* namespace.idFromName(name);
|
|
247
435
|
});
|
|
248
436
|
|
|
249
437
|
const idFromString = Effect.fnUntraced(function* (id: string) {
|
|
250
438
|
const namespace = yield* tag;
|
|
251
|
-
return namespace.idFromString(id);
|
|
439
|
+
return yield* namespace.idFromString(id);
|
|
252
440
|
});
|
|
253
441
|
|
|
254
442
|
const get = Effect.fnUntraced(function* (
|
|
@@ -256,7 +444,7 @@ export const Service =
|
|
|
256
444
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
257
445
|
) {
|
|
258
446
|
const namespace = yield* tag;
|
|
259
|
-
return namespace.get(id, options);
|
|
447
|
+
return yield* namespace.get(id, options);
|
|
260
448
|
});
|
|
261
449
|
|
|
262
450
|
const getByName = Effect.fnUntraced(function* (
|
|
@@ -264,12 +452,12 @@ export const Service =
|
|
|
264
452
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
265
453
|
) {
|
|
266
454
|
const namespace = yield* tag;
|
|
267
|
-
return namespace.getByName(name, options);
|
|
455
|
+
return yield* namespace.getByName(name, options);
|
|
268
456
|
});
|
|
269
457
|
|
|
270
458
|
const jurisdiction = Effect.fnUntraced(function* (value: globalThis.DurableObjectJurisdiction) {
|
|
271
459
|
const namespace = yield* tag;
|
|
272
|
-
return namespace.jurisdiction(value);
|
|
460
|
+
return yield* namespace.jurisdiction(value);
|
|
273
461
|
});
|
|
274
462
|
|
|
275
463
|
const fetch = (stub: StubClient, input: RequestInfo | URL, init?: RequestInit) =>
|
|
@@ -366,6 +554,11 @@ export const Service =
|
|
|
366
554
|
return yield* CloudflareRpc.scoped(result);
|
|
367
555
|
});
|
|
368
556
|
|
|
557
|
+
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
558
|
+
const namespace = yield* tag;
|
|
559
|
+
return yield* namespace.unsafeRaw;
|
|
560
|
+
});
|
|
561
|
+
|
|
369
562
|
const directMethods = makeDirectMethods<Self, NamespaceApi, Definition>(definition.definition, {
|
|
370
563
|
call,
|
|
371
564
|
fetch,
|
|
@@ -386,6 +579,7 @@ export const Service =
|
|
|
386
579
|
rpc,
|
|
387
580
|
call,
|
|
388
581
|
scopedCall,
|
|
582
|
+
unsafeRaw,
|
|
389
583
|
}) as typeof tag &
|
|
390
584
|
DirectMethods<Self, NamespaceApi, Definition> & {
|
|
391
585
|
readonly [TypeId]: typeof TypeId;
|
|
@@ -400,32 +594,33 @@ export const Service =
|
|
|
400
594
|
readonly rpc: typeof rpc;
|
|
401
595
|
readonly call: typeof call;
|
|
402
596
|
readonly scopedCall: typeof scopedCall;
|
|
597
|
+
readonly unsafeRaw: typeof unsafeRaw;
|
|
403
598
|
};
|
|
404
599
|
};
|
|
405
600
|
|
|
406
601
|
const makeDirectMethods = <
|
|
407
|
-
|
|
602
|
+
R,
|
|
408
603
|
Api extends object,
|
|
409
604
|
Definition extends DurableObjectDefinition.Definition.Any | undefined,
|
|
410
605
|
>(
|
|
411
606
|
rpcDefinition: Definition | undefined,
|
|
412
607
|
helpers: {
|
|
413
|
-
readonly call: StubCall<Api>;
|
|
608
|
+
readonly call: StubCall<R, Api>;
|
|
414
609
|
readonly fetch: (
|
|
415
610
|
stub: DurableObjectStubClient<Api>,
|
|
416
611
|
input: RequestInfo | URL,
|
|
417
612
|
init?: RequestInit,
|
|
418
|
-
) => Effect.Effect<globalThis.Response, DurableObjectFetchError>;
|
|
613
|
+
) => Effect.Effect<globalThis.Response, DurableObjectFetchError, R>;
|
|
419
614
|
readonly get: (
|
|
420
615
|
id: globalThis.DurableObjectId,
|
|
421
616
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
422
|
-
) => Effect.Effect<DurableObjectStubClient<Api>, never,
|
|
617
|
+
) => Effect.Effect<DurableObjectStubClient<Api>, never, R>;
|
|
423
618
|
readonly getByName: (
|
|
424
619
|
name: string,
|
|
425
620
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
426
|
-
) => Effect.Effect<DurableObjectStubClient<Api>, never,
|
|
621
|
+
) => Effect.Effect<DurableObjectStubClient<Api>, never, R>;
|
|
427
622
|
},
|
|
428
|
-
): DirectMethods<
|
|
623
|
+
): DirectMethods<R, Api, Definition> => {
|
|
429
624
|
const methods = {} as Record<string, unknown>;
|
|
430
625
|
|
|
431
626
|
if (rpcDefinition !== undefined) {
|
|
@@ -441,8 +636,8 @@ const makeDirectMethods = <
|
|
|
441
636
|
}
|
|
442
637
|
|
|
443
638
|
const makeClient = (
|
|
444
|
-
getStub: () => Effect.Effect<DurableObjectStubClient<Api>, never,
|
|
445
|
-
): DurableObjectDirectClient<
|
|
639
|
+
getStub: () => Effect.Effect<DurableObjectStubClient<Api>, never, R>,
|
|
640
|
+
): DurableObjectDirectClient<R, Api> => {
|
|
446
641
|
const client = {
|
|
447
642
|
fetch: (input: RequestInfo | URL, init?: RequestInit) =>
|
|
448
643
|
Effect.gen(function* () {
|
|
@@ -465,7 +660,7 @@ const makeDirectMethods = <
|
|
|
465
660
|
});
|
|
466
661
|
}
|
|
467
662
|
|
|
468
|
-
return client as DurableObjectDirectClient<
|
|
663
|
+
return client as DurableObjectDirectClient<R, Api>;
|
|
469
664
|
};
|
|
470
665
|
|
|
471
666
|
methods.byName = (
|
|
@@ -479,5 +674,5 @@ const makeDirectMethods = <
|
|
|
479
674
|
) => makeClient(() => helpers.get(id, options));
|
|
480
675
|
}
|
|
481
676
|
|
|
482
|
-
return methods as DirectMethods<
|
|
677
|
+
return methods as DirectMethods<R, Api, Definition>;
|
|
483
678
|
};
|
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, {
|