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/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
2
2
  import { Cause, Clock, Config, ConfigProvider, Context, Data, DateTime, Duration, Effect, Exit, Layer, ManagedRuntime, Option, Queue, Schema } from "effect";
3
+ import { D1Client } from "@effect/sql-d1";
3
4
  import { DurableObject, RpcStub, RpcTarget, WorkerEntrypoint, WorkflowEntrypoint } from "cloudflare:workers";
4
5
  import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
5
6
  import * as RpcMessage from "effect/unstable/rpc/RpcMessage";
@@ -28,11 +29,11 @@ let WorkerConfig;
28
29
  var Binding_exports = /* @__PURE__ */ __exportAll({
29
30
  BindingNotFoundError: () => BindingNotFoundError,
30
31
  BindingValidationError: () => BindingValidationError,
31
- Service: () => Service$5,
32
- TypeId: () => TypeId$5
32
+ Service: () => Service$6,
33
+ TypeId: () => TypeId$6
33
34
  });
34
35
  /** Internal type id marker used by binding helper services. */
35
- const TypeId$5 = "effect-cf/Binding";
36
+ const TypeId$6 = "effect-cf/Binding";
36
37
  /** Error raised when a configured binding does not exist on `env`. */
37
38
  var BindingNotFoundError = class extends Data.TaggedError("BindingNotFoundError") {};
38
39
  /** Error raised when a binding exists but does not match the expected shape. */
@@ -45,21 +46,101 @@ const getBinding = (env, binding, isResource) => Effect.gen(function* () {
45
46
  if (!isResource(resource)) return yield* Effect.fail(new BindingValidationError({ binding }));
46
47
  return resource;
47
48
  });
48
- /**
49
- * Creates a Context tag + layer for reading and validating a Cloudflare binding.
50
- */
51
- const Service$5 = () => (id, binding, isResource) => {
49
+ const Service$6 = () => (id, binding, isResource, wrap) => {
52
50
  const tag = Context.Service()(id);
53
51
  const layer = Layer.effect(tag, Effect.gen(function* () {
54
- return yield* getBinding(yield* WorkerEnvironment, binding, isResource);
52
+ const resource = yield* getBinding(yield* WorkerEnvironment, binding, isResource);
53
+ return wrap === void 0 ? resource : wrap(resource);
55
54
  }));
56
55
  return Object.assign(tag, {
57
- [TypeId$5]: TypeId$5,
56
+ [TypeId$6]: TypeId$6,
58
57
  binding,
59
58
  layer
60
59
  });
61
60
  };
62
61
  //#endregion
62
+ //#region src/D1.ts
63
+ var D1_exports = /* @__PURE__ */ __exportAll({
64
+ D1OperationError: () => D1OperationError,
65
+ Service: () => Service$5,
66
+ make: () => make$11
67
+ });
68
+ const TypeId$5 = "effect-cf/D1";
69
+ /** Error raised when a D1 operation fails. */
70
+ var D1OperationError = class extends Data.TaggedError("D1OperationError") {};
71
+ const isD1Database = (value) => {
72
+ if (typeof value !== "object" || value === null) return false;
73
+ const resource = value;
74
+ return typeof resource.prepare === "function" && typeof resource.batch === "function" && typeof resource.exec === "function";
75
+ };
76
+ const d1Error = (binding, operation, cause) => new D1OperationError({
77
+ binding,
78
+ operation,
79
+ cause
80
+ });
81
+ const tryD1Promise = (binding, operation, evaluate) => Effect.tryPromise({
82
+ try: evaluate,
83
+ catch: (cause) => d1Error(binding, operation, cause)
84
+ });
85
+ const tryD1Sync = (binding, operation, evaluate) => Effect.try({
86
+ try: evaluate,
87
+ catch: (cause) => d1Error(binding, operation, cause)
88
+ });
89
+ /**
90
+ * Creates a typed D1 service tag plus Effect helpers.
91
+ */
92
+ const make$11 = (id, definition) => Service$5()(id, definition);
93
+ /**
94
+ * Builds a D1 service around a Cloudflare D1 database binding.
95
+ *
96
+ * The returned service exposes the raw `D1Database` binding, small native D1
97
+ * helpers, and `sqlLayer(...)` for providing `effect/unstable/sql` via
98
+ * `@effect/sql-d1`.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * class TodoDatabase extends D1.Service<TodoDatabase>()("TodoDatabase", {
103
+ * binding: "TODO_DB",
104
+ * }) {}
105
+ *
106
+ * const SqlLive = TodoDatabase.sqlLayer();
107
+ * ```
108
+ */
109
+ const Service$5 = () => (id, definition) => {
110
+ const tag = Service$6()(id, definition.binding, isD1Database);
111
+ const sqlLayer = (options) => Layer.unwrap(Effect.gen(function* () {
112
+ const db = yield* tag;
113
+ return D1Client.layer({
114
+ ...options,
115
+ db
116
+ });
117
+ })).pipe(Layer.provide(tag.layer));
118
+ const prepare = Effect.fnUntraced(function* (query) {
119
+ const db = yield* tag;
120
+ return yield* tryD1Sync(definition.binding, "prepare", () => db.prepare(query));
121
+ });
122
+ const batch = Effect.fnUntraced(function* (statements) {
123
+ const db = yield* tag;
124
+ return yield* tryD1Promise(definition.binding, "batch", () => db.batch([...statements]));
125
+ });
126
+ const exec = Effect.fnUntraced(function* (query) {
127
+ const db = yield* tag;
128
+ return yield* tryD1Promise(definition.binding, "exec", () => db.exec(query));
129
+ });
130
+ const unsafeRaw = Effect.fnUntraced(function* () {
131
+ return yield* tag;
132
+ });
133
+ return Object.assign(tag, {
134
+ [TypeId$5]: TypeId$5,
135
+ definition,
136
+ sqlLayer,
137
+ prepare,
138
+ batch,
139
+ exec,
140
+ unsafeRaw
141
+ });
142
+ };
143
+ //#endregion
63
144
  //#region src/QueueBinding.ts
64
145
  var QueueBinding_exports = /* @__PURE__ */ __exportAll({
65
146
  QueueOperationError: () => QueueOperationError,
@@ -88,32 +169,43 @@ const make$10 = (id, definition) => Service$4()(id, definition);
88
169
  * Builds an Effect service around a Cloudflare Queue producer binding.
89
170
  */
90
171
  const Service$4 = () => (id, definition) => {
91
- const tag = Service$5()(id, definition.binding, (value) => isQueue(value));
92
172
  const encodeMessage = Schema.encodeEffect(definition.message);
173
+ const makeClient = (queue) => ({
174
+ send: Effect.fnUntraced(function* (message, options) {
175
+ const encoded = yield* encodeMessage(message);
176
+ yield* tryQueuePromise(definition.binding, "send", () => queue.send(encoded, options));
177
+ }),
178
+ sendBatch: Effect.fnUntraced(function* (messages, options) {
179
+ const encodedMessages = [];
180
+ for (const message of messages) encodedMessages.push({
181
+ ...message,
182
+ body: yield* encodeMessage(message.body)
183
+ });
184
+ yield* tryQueuePromise(definition.binding, "sendBatch", () => queue.sendBatch(encodedMessages, options));
185
+ }),
186
+ metrics: () => tryQueuePromise(definition.binding, "metrics", () => queue.metrics()),
187
+ unsafeRaw: Effect.succeed(queue)
188
+ });
189
+ const tag = Service$6()(id, definition.binding, (value) => isQueue(value), makeClient);
93
190
  const send = Effect.fnUntraced(function* (message, options) {
94
- const queue = yield* tag;
95
- const encoded = yield* encodeMessage(message);
96
- return yield* tryQueuePromise(definition.binding, "send", () => queue.send(encoded, options));
191
+ yield* (yield* tag).send(message, options);
97
192
  });
98
193
  const sendBatch = Effect.fnUntraced(function* (messages, options) {
99
- const queue = yield* tag;
100
- const encodedMessages = [];
101
- for (const message of messages) encodedMessages.push({
102
- ...message,
103
- body: yield* encodeMessage(message.body)
104
- });
105
- return yield* tryQueuePromise(definition.binding, "sendBatch", () => queue.sendBatch(encodedMessages, options));
194
+ yield* (yield* tag).sendBatch(messages, options);
106
195
  });
107
196
  const metrics = Effect.fnUntraced(function* () {
108
- const queue = yield* tag;
109
- return yield* tryQueuePromise(definition.binding, "metrics", () => queue.metrics());
197
+ return yield* (yield* tag).metrics();
198
+ });
199
+ const unsafeRaw = Effect.fnUntraced(function* () {
200
+ return yield* (yield* tag).unsafeRaw;
110
201
  });
111
202
  return Object.assign(tag, {
112
203
  [TypeId$4]: TypeId$4,
113
204
  binding: definition.binding,
114
205
  send,
115
206
  sendBatch,
116
- metrics
207
+ metrics,
208
+ unsafeRaw
117
209
  });
118
210
  };
119
211
  //#endregion
@@ -421,46 +513,63 @@ var ServiceBindingRpcError = class extends Data.TaggedError("ServiceBindingRpcEr
421
513
  * ```
422
514
  */
423
515
  const Service$3 = () => (id, definition) => {
424
- const tag = Service$5()(id, definition.binding, (value) => typeof value === "object" && value !== null && "fetch" in value);
425
- const fetch = (input, init) => Effect.gen(function* () {
426
- const service = yield* tag;
427
- return yield* Effect.tryPromise({
516
+ const makeClient = (service) => {
517
+ const fetch = (input, init) => Effect.tryPromise({
428
518
  try: () => service.fetch(input, init),
429
519
  catch: (cause) => new ServiceBindingFetchError({
430
520
  binding: definition.binding,
431
521
  cause
432
522
  })
433
523
  });
524
+ const rpc = (method, ...args) => Effect.gen(function* () {
525
+ const methodName = String(method);
526
+ return yield* invokeRpcMethod(service, method, definition.definition === void 0 ? args : yield* encodeArgs(definition.definition, methodName, args).pipe(Effect.mapError((cause) => new ServiceBindingRpcError({
527
+ binding: definition.binding,
528
+ method: methodName,
529
+ cause
530
+ }))), (cause) => new ServiceBindingRpcError({
531
+ binding: definition.binding,
532
+ method: methodName,
533
+ cause
534
+ }));
535
+ });
536
+ const call = (method, ...args) => Effect.gen(function* () {
537
+ const methodName = String(method);
538
+ const value = yield* resolve(yield* rpc(method, ...args)).pipe(Effect.mapError((cause) => new ServiceBindingRpcError({
539
+ binding: definition.binding,
540
+ method: methodName,
541
+ cause
542
+ })));
543
+ if (definition.definition === void 0) return value;
544
+ return yield* decodeSuccess(definition.definition, methodName, value).pipe(Effect.mapError((cause) => new ServiceBindingRpcError({
545
+ binding: definition.binding,
546
+ method: methodName,
547
+ cause
548
+ })));
549
+ });
550
+ const scopedCall = (method, ...args) => Effect.gen(function* () {
551
+ return yield* scoped(yield* rpc(method, ...args));
552
+ });
553
+ const directMethods = makeDirectMethods$1(definition.definition, call);
554
+ return Object.assign(directMethods, {
555
+ fetch,
556
+ rpc,
557
+ call,
558
+ scopedCall
559
+ });
560
+ };
561
+ const tag = Service$6()(id, definition.binding, (value) => typeof value === "object" && value !== null && "fetch" in value, makeClient);
562
+ const fetch = (input, init) => Effect.gen(function* () {
563
+ return yield* (yield* tag).fetch(input, init);
434
564
  });
435
565
  const rpc = (method, ...args) => Effect.gen(function* () {
436
- const service = yield* tag;
437
- const methodName = String(method);
438
- return yield* invokeRpcMethod(service, method, definition.definition === void 0 ? args : yield* encodeArgs(definition.definition, methodName, args).pipe(Effect.mapError((cause) => new ServiceBindingRpcError({
439
- binding: definition.binding,
440
- method: methodName,
441
- cause
442
- }))), (cause) => new ServiceBindingRpcError({
443
- binding: definition.binding,
444
- method: methodName,
445
- cause
446
- }));
566
+ return yield* (yield* tag).rpc(method, ...args);
447
567
  });
448
568
  const call = (method, ...args) => Effect.gen(function* () {
449
- const methodName = String(method);
450
- const value = yield* resolve(yield* rpc(method, ...args)).pipe(Effect.mapError((cause) => new ServiceBindingRpcError({
451
- binding: definition.binding,
452
- method: methodName,
453
- cause
454
- })));
455
- if (definition.definition === void 0) return value;
456
- return yield* decodeSuccess(definition.definition, methodName, value).pipe(Effect.mapError((cause) => new ServiceBindingRpcError({
457
- binding: definition.binding,
458
- method: methodName,
459
- cause
460
- })));
569
+ return yield* (yield* tag).call(method, ...args);
461
570
  });
462
571
  const scopedCall = (method, ...args) => Effect.gen(function* () {
463
- return yield* scoped(yield* rpc(method, ...args));
572
+ return yield* (yield* tag).scopedCall(method, ...args);
464
573
  });
465
574
  const directMethods = makeDirectMethods$1(definition.definition, call);
466
575
  return Object.assign(tag, directMethods, {
@@ -1006,24 +1115,87 @@ var DurableObjectFetchError = class extends Data.TaggedError("DurableObjectFetch
1006
1115
  const Service$2 = () => (id, definition) => {
1007
1116
  const hasFunction = (value, key) => typeof Reflect.get(value, key) === "function";
1008
1117
  const isDurableObjectNamespaceClient = (value) => typeof value === "object" && value !== null && hasFunction(value, "getByName") && hasFunction(value, "get") && hasFunction(value, "idFromName") && hasFunction(value, "idFromString") && hasFunction(value, "newUniqueId") && hasFunction(value, "jurisdiction");
1009
- const tag = Service$5()(id, definition.binding, (value) => isDurableObjectNamespaceClient(value));
1118
+ const makeClient = (namespace) => {
1119
+ const newUniqueId = (options) => Effect.sync(() => namespace.newUniqueId(options));
1120
+ const idFromName = (name) => Effect.sync(() => namespace.idFromName(name));
1121
+ const idFromString = (id) => Effect.sync(() => namespace.idFromString(id));
1122
+ const get = (id, options) => Effect.sync(() => namespace.get(id, options));
1123
+ const getByName = (name, options) => Effect.sync(() => namespace.getByName(name, options));
1124
+ const jurisdiction = (value) => Effect.sync(() => namespace.jurisdiction(value));
1125
+ const fetch = (stub, input, init) => Effect.tryPromise({
1126
+ try: () => stub.fetch(input, init),
1127
+ catch: (cause) => new DurableObjectFetchError({
1128
+ binding: definition.binding,
1129
+ cause
1130
+ })
1131
+ });
1132
+ const rpc = (stub, method, ...args) => Effect.gen(function* () {
1133
+ const methodName = String(method);
1134
+ return yield* invokeRpcMethod(stub, method, definition.definition === void 0 ? args : yield* encodeArgs(definition.definition, methodName, args).pipe(Effect.mapError((cause) => new DurableObjectRpcError({
1135
+ binding: definition.binding,
1136
+ method: methodName,
1137
+ cause
1138
+ }))), (cause) => new DurableObjectRpcError({
1139
+ binding: definition.binding,
1140
+ method: methodName,
1141
+ cause
1142
+ }));
1143
+ });
1144
+ const call = (stub, method, ...args) => Effect.gen(function* () {
1145
+ const methodName = String(method);
1146
+ const value = yield* resolve(yield* rpc(stub, method, ...args)).pipe(Effect.mapError((cause) => new DurableObjectRpcError({
1147
+ binding: definition.binding,
1148
+ method: methodName,
1149
+ cause
1150
+ })));
1151
+ if (definition.definition === void 0) return value;
1152
+ return yield* decodeSuccess(definition.definition, methodName, value).pipe(Effect.mapError((cause) => new DurableObjectRpcError({
1153
+ binding: definition.binding,
1154
+ method: methodName,
1155
+ cause
1156
+ })));
1157
+ });
1158
+ const scopedCall = (stub, method, ...args) => Effect.gen(function* () {
1159
+ return yield* scoped(yield* rpc(stub, method, ...args));
1160
+ });
1161
+ const directMethods = makeDirectMethods(definition.definition, {
1162
+ call,
1163
+ fetch,
1164
+ get,
1165
+ getByName
1166
+ });
1167
+ return Object.assign(directMethods, {
1168
+ newUniqueId,
1169
+ idFromName,
1170
+ idFromString,
1171
+ get,
1172
+ getByName,
1173
+ jurisdiction,
1174
+ fetch,
1175
+ rpc,
1176
+ call,
1177
+ scopedCall,
1178
+ unsafeRaw: Effect.succeed(namespace)
1179
+ });
1180
+ };
1181
+ const tag = Service$6()(id, definition.binding, (value) => isDurableObjectNamespaceClient(value), makeClient);
1010
1182
  const newUniqueId = Effect.fnUntraced(function* (options) {
1011
- return (yield* tag).newUniqueId(options);
1183
+ return yield* (yield* tag).newUniqueId(options);
1012
1184
  });
1013
1185
  const idFromName = Effect.fnUntraced(function* (name) {
1014
- return (yield* tag).idFromName(name);
1186
+ return yield* (yield* tag).idFromName(name);
1015
1187
  });
1016
1188
  const idFromString = Effect.fnUntraced(function* (id) {
1017
- return (yield* tag).idFromString(id);
1189
+ return yield* (yield* tag).idFromString(id);
1018
1190
  });
1019
1191
  const get = Effect.fnUntraced(function* (id, options) {
1020
- return (yield* tag).get(id, options);
1192
+ return yield* (yield* tag).get(id, options);
1021
1193
  });
1022
1194
  const getByName = Effect.fnUntraced(function* (name, options) {
1023
- return (yield* tag).getByName(name, options);
1195
+ return yield* (yield* tag).getByName(name, options);
1024
1196
  });
1025
1197
  const jurisdiction = Effect.fnUntraced(function* (value) {
1026
- return (yield* tag).jurisdiction(value);
1198
+ return yield* (yield* tag).jurisdiction(value);
1027
1199
  });
1028
1200
  const fetch = (stub, input, init) => Effect.tryPromise({
1029
1201
  try: () => stub.fetch(input, init),
@@ -1061,6 +1233,9 @@ const Service$2 = () => (id, definition) => {
1061
1233
  const scopedCall = (stub, method, ...args) => Effect.gen(function* () {
1062
1234
  return yield* scoped(yield* rpc(stub, method, ...args));
1063
1235
  });
1236
+ const unsafeRaw = Effect.fnUntraced(function* () {
1237
+ return yield* (yield* tag).unsafeRaw;
1238
+ });
1064
1239
  const directMethods = makeDirectMethods(definition.definition, {
1065
1240
  call,
1066
1241
  fetch,
@@ -1079,7 +1254,8 @@ const Service$2 = () => (id, definition) => {
1079
1254
  fetch,
1080
1255
  rpc,
1081
1256
  call,
1082
- scopedCall
1257
+ scopedCall,
1258
+ unsafeRaw
1083
1259
  });
1084
1260
  };
1085
1261
  const makeDirectMethods = (rpcDefinition, helpers) => {
@@ -1770,69 +1946,83 @@ const Tag$2 = () => (id, definition) => {
1770
1946
  * ```
1771
1947
  */
1772
1948
  const Service$1 = () => (id, definition) => {
1773
- const tag = Service$5()(id, definition.binding, (value) => {
1774
- if (typeof value !== "object" || value === null) return false;
1775
- const resource = value;
1776
- return typeof resource.get === "function" && typeof resource.put === "function" && typeof resource.delete === "function" && typeof resource.getWithMetadata === "function" && typeof resource.list === "function";
1777
- });
1778
1949
  const encodeKey = Schema.encodeEffect(definition.key);
1779
1950
  const decodeKey = Schema.decodeUnknownEffect(definition.key);
1780
1951
  const encodeValue = Schema.encodeEffect(Schema.fromJsonString(Schema.toCodecJson(definition.value)));
1781
1952
  const decodeValue = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.toCodecJson(definition.value)));
1953
+ const makeClient = (kv) => ({
1954
+ definition,
1955
+ put: Effect.fnUntraced(function* (key, value, options) {
1956
+ const keyEncoded = yield* encodeKey(key);
1957
+ const valueEncoded = yield* encodeValue(value);
1958
+ yield* tryKvPromise(definition.binding, "put", () => kv.put(keyEncoded, valueEncoded, options));
1959
+ }),
1960
+ get: Effect.fnUntraced(function* (key) {
1961
+ const keyEncoded = yield* encodeKey(key);
1962
+ const valueEncoded = yield* tryKvPromise(definition.binding, "get", () => kv.get(keyEncoded));
1963
+ if (valueEncoded === null) return Option.none();
1964
+ return yield* decodeValue(valueEncoded).pipe(Effect.map(Option.some));
1965
+ }),
1966
+ getWithMetadata: Effect.fnUntraced(function* (key, metadataSchema) {
1967
+ const keyEncoded = yield* encodeKey(key);
1968
+ const result = yield* tryKvPromise(definition.binding, "getWithMetadata", () => kv.getWithMetadata(keyEncoded));
1969
+ if (result.value === null) return Option.none();
1970
+ const value = yield* decodeValue(result.value);
1971
+ const metadata = result.metadata === null ? Option.none() : Option.some(yield* Schema.decodeUnknownEffect(metadataSchema)(result.metadata));
1972
+ return Option.some({
1973
+ value,
1974
+ metadata,
1975
+ cacheStatus: maybeString(result.cacheStatus)
1976
+ });
1977
+ }),
1978
+ list: Effect.fnUntraced(function* (options) {
1979
+ const { metadataSchema, ...kvOptions } = options ?? {};
1980
+ const result = yield* tryKvPromise(definition.binding, "list", () => kv.list(kvOptions));
1981
+ const keys = [];
1982
+ for (const key of result.keys) {
1983
+ const decodedName = yield* decodeKey(key.name);
1984
+ const decodedMetadata = key.metadata === void 0 ? Option.none() : metadataSchema === void 0 ? Option.some(key.metadata) : Option.some(yield* Schema.decodeUnknownEffect(metadataSchema)(key.metadata));
1985
+ keys.push({
1986
+ name: decodedName,
1987
+ expiration: maybeNumber(key.expiration),
1988
+ metadata: decodedMetadata
1989
+ });
1990
+ }
1991
+ return {
1992
+ keys,
1993
+ listComplete: result.list_complete,
1994
+ cursor: maybeString("cursor" in result ? result.cursor : void 0),
1995
+ cacheStatus: maybeString(result.cacheStatus)
1996
+ };
1997
+ }),
1998
+ remove: Effect.fnUntraced(function* (key) {
1999
+ const keyEncoded = yield* encodeKey(key);
2000
+ yield* tryKvPromise(definition.binding, "delete", () => kv.delete(keyEncoded));
2001
+ }),
2002
+ unsafeRaw: Effect.succeed(kv)
2003
+ });
2004
+ const tag = Service$6()(id, definition.binding, (value) => {
2005
+ if (typeof value !== "object" || value === null) return false;
2006
+ const resource = value;
2007
+ return typeof resource.get === "function" && typeof resource.put === "function" && typeof resource.delete === "function" && typeof resource.getWithMetadata === "function" && typeof resource.list === "function";
2008
+ }, makeClient);
1782
2009
  const put = Effect.fnUntraced(function* (key, value, options) {
1783
- const kv = yield* tag;
1784
- const keyEncoded = yield* encodeKey(key);
1785
- const valueEncoded = yield* encodeValue(value);
1786
- yield* tryKvPromise(definition.binding, "put", () => kv.put(keyEncoded, valueEncoded, options));
2010
+ yield* (yield* tag).put(key, value, options);
1787
2011
  });
1788
2012
  const get = Effect.fnUntraced(function* (key) {
1789
- const kv = yield* tag;
1790
- const keyEncoded = yield* encodeKey(key);
1791
- const valueEncoded = yield* tryKvPromise(definition.binding, "get", () => kv.get(keyEncoded));
1792
- if (valueEncoded === null) return Option.none();
1793
- return yield* decodeValue(valueEncoded).pipe(Effect.map(Option.some));
2013
+ return yield* (yield* tag).get(key);
1794
2014
  });
1795
2015
  const getWithMetadata = Effect.fnUntraced(function* (key, metadataSchema) {
1796
- const kv = yield* tag;
1797
- const keyEncoded = yield* encodeKey(key);
1798
- const result = yield* tryKvPromise(definition.binding, "getWithMetadata", () => kv.getWithMetadata(keyEncoded));
1799
- if (result.value === null) return Option.none();
1800
- const value = yield* decodeValue(result.value);
1801
- const metadata = result.metadata === null ? Option.none() : Option.some(yield* Schema.decodeUnknownEffect(metadataSchema)(result.metadata));
1802
- return Option.some({
1803
- value,
1804
- metadata,
1805
- cacheStatus: maybeString(result.cacheStatus)
1806
- });
2016
+ return yield* (yield* tag).getWithMetadata(key, metadataSchema);
1807
2017
  });
1808
2018
  const list = Effect.fnUntraced(function* (options) {
1809
- const kv = yield* tag;
1810
- const { metadataSchema, ...kvOptions } = options ?? {};
1811
- const result = yield* tryKvPromise(definition.binding, "list", () => kv.list(kvOptions));
1812
- const keys = [];
1813
- for (const key of result.keys) {
1814
- const decodedName = yield* decodeKey(key.name);
1815
- const decodedMetadata = key.metadata === void 0 ? Option.none() : metadataSchema === void 0 ? Option.some(key.metadata) : Option.some(yield* Schema.decodeUnknownEffect(metadataSchema)(key.metadata));
1816
- keys.push({
1817
- name: decodedName,
1818
- expiration: maybeNumber(key.expiration),
1819
- metadata: decodedMetadata
1820
- });
1821
- }
1822
- return {
1823
- keys,
1824
- listComplete: result.list_complete,
1825
- cursor: maybeString("cursor" in result ? result.cursor : void 0),
1826
- cacheStatus: maybeString(result.cacheStatus)
1827
- };
2019
+ return yield* (yield* tag).list(options);
1828
2020
  });
1829
2021
  const remove = Effect.fnUntraced(function* (key) {
1830
- const kv = yield* tag;
1831
- const keyEncoded = yield* encodeKey(key);
1832
- yield* tryKvPromise(definition.binding, "delete", () => kv.delete(keyEncoded));
2022
+ yield* (yield* tag).remove(key);
1833
2023
  });
1834
2024
  const unsafeRaw = Effect.fnUntraced(function* () {
1835
- return yield* tag;
2025
+ return yield* (yield* tag).unsafeRaw;
1836
2026
  });
1837
2027
  return Object.assign(tag, {
1838
2028
  [TypeId$1]: TypeId$1,
@@ -1872,7 +2062,7 @@ const isWorkflow = (value) => {
1872
2062
  };
1873
2063
  const make$2 = (id, definition) => Service()(id, definition);
1874
2064
  const Service = () => (id, definition) => {
1875
- const tag = Service$5()(id, definition.binding, (value) => isWorkflow(value));
2065
+ const tag = Service$6()(id, definition.binding, (value) => isWorkflow(value));
1876
2066
  const encodePayload = Schema.encodeEffect(definition.payload);
1877
2067
  const decodeResult = Schema.decodeUnknownEffect(definition.result);
1878
2068
  const wrapInstance = (raw) => {
@@ -2056,4 +2246,4 @@ const waitForEvent = (name, options) => Effect.flatMap(WorkflowStep$1.asEffect()
2056
2246
  const Tag = Tag$1;
2057
2247
  const implement = implement$1;
2058
2248
  //#endregion
2059
- export { Binding_exports as Binding, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, ServiceBinding_exports as ServiceBinding, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
2249
+ export { Binding_exports as Binding, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, ServiceBinding_exports as ServiceBinding, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-cf",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,6 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@cloudflare/vitest-pool-workers": "^0.15.0",
38
38
  "@cloudflare/workers-types": "^4.20260421.1",
39
+ "@effect/sql-d1": "catalog:",
39
40
  "@effect/vitest": "catalog:",
40
41
  "effect": "catalog:",
41
42
  "typescript": "^6.0.2",
@@ -43,6 +44,7 @@
43
44
  "vitest": "catalog:"
44
45
  },
45
46
  "peerDependencies": {
47
+ "@effect/sql-d1": "^4.0.0-beta.65",
46
48
  "effect": "^4.0.0-beta.65"
47
49
  }
48
50
  }
package/src/Binding.ts CHANGED
@@ -45,20 +45,36 @@ const getBinding = <Resource>(
45
45
  /**
46
46
  * Creates a Context tag + layer for reading and validating a Cloudflare binding.
47
47
  */
48
+ export interface BindingService<Self, Id extends string, Service> extends Context.ServiceClass<
49
+ Self,
50
+ Id,
51
+ Service
52
+ > {
53
+ readonly [TypeId]: typeof TypeId;
54
+ readonly binding: string;
55
+ readonly layer: Layer.Layer<
56
+ Self,
57
+ BindingNotFoundError | BindingValidationError,
58
+ WorkerEnvironment
59
+ >;
60
+ }
61
+
48
62
  export const Service =
49
63
  <Self>() =>
50
- <Id extends string, Resource>(
64
+ <Id extends string, Resource, Service = Resource>(
51
65
  id: Id,
52
66
  binding: string,
53
67
  isResource: (value: unknown) => value is Resource,
54
- ) => {
55
- const tag = Context.Service<Self, Resource>()(id);
68
+ wrap?: (resource: Resource) => Service,
69
+ ): BindingService<Self, Id, Service> => {
70
+ const tag = Context.Service<Self, Service>()(id);
56
71
 
57
72
  const layer = Layer.effect(
58
73
  tag,
59
74
  Effect.gen(function* () {
60
75
  const env = yield* WorkerEnvironment;
61
- return yield* getBinding(env, binding, isResource);
76
+ const resource = yield* getBinding(env, binding, isResource);
77
+ return wrap === undefined ? (resource as unknown as Service) : wrap(resource);
62
78
  }),
63
79
  );
64
80
 
@@ -66,5 +82,5 @@ export const Service =
66
82
  [TypeId]: TypeId,
67
83
  binding,
68
84
  layer,
69
- });
85
+ }) as BindingService<Self, Id, Service>;
70
86
  };