effect-cf 0.3.0 → 0.5.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 +2 -1
- package/dist/index.d.mts +459 -305
- package/dist/index.mjs +149 -35
- package/package.json +3 -1
- package/src/D1.ts +135 -0
- package/src/Kv.ts +90 -0
- package/src/index.ts +1 -0
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$
|
|
32
|
-
TypeId: () => TypeId$
|
|
32
|
+
Service: () => Service$6,
|
|
33
|
+
TypeId: () => TypeId$6
|
|
33
34
|
});
|
|
34
35
|
/** Internal type id marker used by binding helper services. */
|
|
35
|
-
const TypeId$
|
|
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. */
|
|
@@ -48,18 +49,100 @@ const getBinding = (env, binding, isResource) => Effect.gen(function* () {
|
|
|
48
49
|
/**
|
|
49
50
|
* Creates a Context tag + layer for reading and validating a Cloudflare binding.
|
|
50
51
|
*/
|
|
51
|
-
const Service$
|
|
52
|
+
const Service$6 = () => (id, binding, isResource) => {
|
|
52
53
|
const tag = Context.Service()(id);
|
|
53
54
|
const layer = Layer.effect(tag, Effect.gen(function* () {
|
|
54
55
|
return yield* getBinding(yield* WorkerEnvironment, binding, isResource);
|
|
55
56
|
}));
|
|
56
57
|
return Object.assign(tag, {
|
|
57
|
-
[TypeId$
|
|
58
|
+
[TypeId$6]: TypeId$6,
|
|
58
59
|
binding,
|
|
59
60
|
layer
|
|
60
61
|
});
|
|
61
62
|
};
|
|
62
63
|
//#endregion
|
|
64
|
+
//#region src/D1.ts
|
|
65
|
+
var D1_exports = /* @__PURE__ */ __exportAll({
|
|
66
|
+
D1OperationError: () => D1OperationError,
|
|
67
|
+
Service: () => Service$5,
|
|
68
|
+
make: () => make$11
|
|
69
|
+
});
|
|
70
|
+
const TypeId$5 = "effect-cf/D1";
|
|
71
|
+
/** Error raised when a D1 operation fails. */
|
|
72
|
+
var D1OperationError = class extends Data.TaggedError("D1OperationError") {};
|
|
73
|
+
const isD1Database = (value) => {
|
|
74
|
+
if (typeof value !== "object" || value === null) return false;
|
|
75
|
+
const resource = value;
|
|
76
|
+
return typeof resource.prepare === "function" && typeof resource.batch === "function" && typeof resource.exec === "function";
|
|
77
|
+
};
|
|
78
|
+
const d1Error = (binding, operation, cause) => new D1OperationError({
|
|
79
|
+
binding,
|
|
80
|
+
operation,
|
|
81
|
+
cause
|
|
82
|
+
});
|
|
83
|
+
const tryD1Promise = (binding, operation, evaluate) => Effect.tryPromise({
|
|
84
|
+
try: evaluate,
|
|
85
|
+
catch: (cause) => d1Error(binding, operation, cause)
|
|
86
|
+
});
|
|
87
|
+
const tryD1Sync = (binding, operation, evaluate) => Effect.try({
|
|
88
|
+
try: evaluate,
|
|
89
|
+
catch: (cause) => d1Error(binding, operation, cause)
|
|
90
|
+
});
|
|
91
|
+
/**
|
|
92
|
+
* Creates a typed D1 service tag plus Effect helpers.
|
|
93
|
+
*/
|
|
94
|
+
const make$11 = (id, definition) => Service$5()(id, definition);
|
|
95
|
+
/**
|
|
96
|
+
* Builds a D1 service around a Cloudflare D1 database binding.
|
|
97
|
+
*
|
|
98
|
+
* The returned service exposes the raw `D1Database` binding, small native D1
|
|
99
|
+
* helpers, and `sqlLayer(...)` for providing `effect/unstable/sql` via
|
|
100
|
+
* `@effect/sql-d1`.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* class TodoDatabase extends D1.Service<TodoDatabase>()("TodoDatabase", {
|
|
105
|
+
* binding: "TODO_DB",
|
|
106
|
+
* }) {}
|
|
107
|
+
*
|
|
108
|
+
* const SqlLive = TodoDatabase.sqlLayer();
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
const Service$5 = () => (id, definition) => {
|
|
112
|
+
const tag = Service$6()(id, definition.binding, isD1Database);
|
|
113
|
+
const sqlLayer = (options) => Layer.unwrap(Effect.gen(function* () {
|
|
114
|
+
const db = yield* tag;
|
|
115
|
+
return D1Client.layer({
|
|
116
|
+
...options,
|
|
117
|
+
db
|
|
118
|
+
});
|
|
119
|
+
})).pipe(Layer.provide(tag.layer));
|
|
120
|
+
const prepare = Effect.fnUntraced(function* (query) {
|
|
121
|
+
const db = yield* tag;
|
|
122
|
+
return yield* tryD1Sync(definition.binding, "prepare", () => db.prepare(query));
|
|
123
|
+
});
|
|
124
|
+
const batch = Effect.fnUntraced(function* (statements) {
|
|
125
|
+
const db = yield* tag;
|
|
126
|
+
return yield* tryD1Promise(definition.binding, "batch", () => db.batch([...statements]));
|
|
127
|
+
});
|
|
128
|
+
const exec = Effect.fnUntraced(function* (query) {
|
|
129
|
+
const db = yield* tag;
|
|
130
|
+
return yield* tryD1Promise(definition.binding, "exec", () => db.exec(query));
|
|
131
|
+
});
|
|
132
|
+
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
133
|
+
return yield* tag;
|
|
134
|
+
});
|
|
135
|
+
return Object.assign(tag, {
|
|
136
|
+
[TypeId$5]: TypeId$5,
|
|
137
|
+
definition,
|
|
138
|
+
sqlLayer,
|
|
139
|
+
prepare,
|
|
140
|
+
batch,
|
|
141
|
+
exec,
|
|
142
|
+
unsafeRaw
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
//#endregion
|
|
63
146
|
//#region src/QueueBinding.ts
|
|
64
147
|
var QueueBinding_exports = /* @__PURE__ */ __exportAll({
|
|
65
148
|
QueueOperationError: () => QueueOperationError,
|
|
@@ -88,7 +171,7 @@ const make$10 = (id, definition) => Service$4()(id, definition);
|
|
|
88
171
|
* Builds an Effect service around a Cloudflare Queue producer binding.
|
|
89
172
|
*/
|
|
90
173
|
const Service$4 = () => (id, definition) => {
|
|
91
|
-
const tag = Service$
|
|
174
|
+
const tag = Service$6()(id, definition.binding, (value) => isQueue(value));
|
|
92
175
|
const encodeMessage = Schema.encodeEffect(definition.message);
|
|
93
176
|
const send = Effect.fnUntraced(function* (message, options) {
|
|
94
177
|
const queue = yield* tag;
|
|
@@ -120,11 +203,11 @@ const Service$4 = () => (id, definition) => {
|
|
|
120
203
|
//#region src/QueueDefinition.ts
|
|
121
204
|
var QueueDefinition_exports = /* @__PURE__ */ __exportAll({
|
|
122
205
|
Queue: () => Queue$1,
|
|
123
|
-
Tag: () => Tag$
|
|
206
|
+
Tag: () => Tag$8,
|
|
124
207
|
implement: () => implement$7,
|
|
125
208
|
make: () => make$9
|
|
126
209
|
});
|
|
127
|
-
const makeDefinition$
|
|
210
|
+
const makeDefinition$4 = (id, definition) => {
|
|
128
211
|
const queueDefinition = {
|
|
129
212
|
id,
|
|
130
213
|
message: definition.message
|
|
@@ -144,9 +227,9 @@ const makeDefinition$3 = (id, definition) => {
|
|
|
144
227
|
})
|
|
145
228
|
});
|
|
146
229
|
};
|
|
147
|
-
const make$9 = makeDefinition$
|
|
148
|
-
const Tag$
|
|
149
|
-
const queueDefinition = makeDefinition$
|
|
230
|
+
const make$9 = makeDefinition$4;
|
|
231
|
+
const Tag$8 = () => (id, definition) => {
|
|
232
|
+
const queueDefinition = makeDefinition$4(id, definition);
|
|
150
233
|
class QueueDefinitionClass {
|
|
151
234
|
static id = queueDefinition.id;
|
|
152
235
|
static message = queueDefinition.message;
|
|
@@ -156,7 +239,7 @@ const Tag$7 = () => (id, definition) => {
|
|
|
156
239
|
}
|
|
157
240
|
return QueueDefinitionClass;
|
|
158
241
|
};
|
|
159
|
-
const Queue$1 = Tag$
|
|
242
|
+
const Queue$1 = Tag$8;
|
|
160
243
|
const wrapHandler$1 = (definition, handler) => {
|
|
161
244
|
const decodeBody = Schema.decodeUnknownEffect(definition.message);
|
|
162
245
|
return (batch) => Effect.gen(function* () {
|
|
@@ -168,7 +251,7 @@ const implement$7 = (_definition, handler) => handler;
|
|
|
168
251
|
//#region src/Queue.ts
|
|
169
252
|
var Queue_exports = /* @__PURE__ */ __exportAll({
|
|
170
253
|
QueueMessageDecodeError: () => QueueMessageDecodeError,
|
|
171
|
-
Tag: () => Tag$
|
|
254
|
+
Tag: () => Tag$7,
|
|
172
255
|
decodeBatch: () => decodeBatch,
|
|
173
256
|
fromMessage: () => fromMessage,
|
|
174
257
|
fromMessageBatch: () => fromMessageBatch,
|
|
@@ -206,7 +289,7 @@ const decodeBatch = (batch, decodeBody) => Effect.gen(function* () {
|
|
|
206
289
|
}
|
|
207
290
|
return fromMessageBatch(batch, messages);
|
|
208
291
|
});
|
|
209
|
-
const Tag$
|
|
292
|
+
const Tag$7 = Tag$8;
|
|
210
293
|
const implement$6 = implement$7;
|
|
211
294
|
//#endregion
|
|
212
295
|
//#region src/RpcDefinition.ts
|
|
@@ -421,7 +504,7 @@ var ServiceBindingRpcError = class extends Data.TaggedError("ServiceBindingRpcEr
|
|
|
421
504
|
* ```
|
|
422
505
|
*/
|
|
423
506
|
const Service$3 = () => (id, definition) => {
|
|
424
|
-
const tag = Service$
|
|
507
|
+
const tag = Service$6()(id, definition.binding, (value) => typeof value === "object" && value !== null && "fetch" in value);
|
|
425
508
|
const fetch = (input, init) => Effect.gen(function* () {
|
|
426
509
|
const service = yield* tag;
|
|
427
510
|
return yield* Effect.tryPromise({
|
|
@@ -480,7 +563,7 @@ const makeDirectMethods$1 = (rpcDefinition, call) => {
|
|
|
480
563
|
//#endregion
|
|
481
564
|
//#region src/WorkerDefinition.ts
|
|
482
565
|
var WorkerDefinition_exports = /* @__PURE__ */ __exportAll({
|
|
483
|
-
Tag: () => Tag$
|
|
566
|
+
Tag: () => Tag$6,
|
|
484
567
|
Worker: () => Worker,
|
|
485
568
|
implement: () => implement$5,
|
|
486
569
|
make: () => make$7,
|
|
@@ -519,7 +602,7 @@ const method$3 = method$4;
|
|
|
519
602
|
* });
|
|
520
603
|
* ```
|
|
521
604
|
*/
|
|
522
|
-
const makeDefinition$
|
|
605
|
+
const makeDefinition$3 = (id, methods) => {
|
|
523
606
|
assertNoReservedMethods("Worker", methods, reservedMethodNames$3);
|
|
524
607
|
const definition = make$8(id, methods);
|
|
525
608
|
return Object.assign(definition, {
|
|
@@ -537,9 +620,9 @@ const makeDefinition$2 = (id, methods) => {
|
|
|
537
620
|
})
|
|
538
621
|
});
|
|
539
622
|
};
|
|
540
|
-
const make$7 = makeDefinition$
|
|
541
|
-
const Tag$
|
|
542
|
-
const definition = makeDefinition$
|
|
623
|
+
const make$7 = makeDefinition$3;
|
|
624
|
+
const Tag$6 = () => (id, methods) => {
|
|
625
|
+
const definition = makeDefinition$3(id, methods);
|
|
543
626
|
class WorkerDefinitionClass {
|
|
544
627
|
static id = definition.id;
|
|
545
628
|
static methods = definition.methods;
|
|
@@ -549,7 +632,7 @@ const Tag$5 = () => (id, methods) => {
|
|
|
549
632
|
}
|
|
550
633
|
return WorkerDefinitionClass;
|
|
551
634
|
};
|
|
552
|
-
const Worker = Tag$
|
|
635
|
+
const Worker = Tag$6;
|
|
553
636
|
const wrapHandlers$1 = (definition, handlers) => {
|
|
554
637
|
const wrapped = {};
|
|
555
638
|
for (const key of Object.keys(definition.methods)) {
|
|
@@ -605,7 +688,7 @@ const fromExecutionContext = (ctx, runPromiseExit) => {
|
|
|
605
688
|
var Worker_exports = /* @__PURE__ */ __exportAll({
|
|
606
689
|
ExecutionContext: () => ExecutionContext,
|
|
607
690
|
NativeRequest: () => NativeRequest,
|
|
608
|
-
Tag: () => Tag$
|
|
691
|
+
Tag: () => Tag$5,
|
|
609
692
|
WorkerContext: () => WorkerContext,
|
|
610
693
|
implement: () => implement$4,
|
|
611
694
|
isWebSocketUpgrade: () => isWebSocketUpgrade,
|
|
@@ -672,7 +755,7 @@ const makeFetchHandler = (layer, options) => {
|
|
|
672
755
|
const WorkerClass = make$6(layer, options);
|
|
673
756
|
return { fetch: (request, env, ctx) => Promise.resolve(new WorkerClass(ctx, env).fetch(request)) };
|
|
674
757
|
};
|
|
675
|
-
const Tag$
|
|
758
|
+
const Tag$5 = Tag$6;
|
|
676
759
|
const method$2 = method$3;
|
|
677
760
|
const implement$4 = implement$5;
|
|
678
761
|
//#endregion
|
|
@@ -1006,7 +1089,7 @@ var DurableObjectFetchError = class extends Data.TaggedError("DurableObjectFetch
|
|
|
1006
1089
|
const Service$2 = () => (id, definition) => {
|
|
1007
1090
|
const hasFunction = (value, key) => typeof Reflect.get(value, key) === "function";
|
|
1008
1091
|
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$
|
|
1092
|
+
const tag = Service$6()(id, definition.binding, (value) => isDurableObjectNamespaceClient(value));
|
|
1010
1093
|
const newUniqueId = Effect.fnUntraced(function* (options) {
|
|
1011
1094
|
return (yield* tag).newUniqueId(options);
|
|
1012
1095
|
});
|
|
@@ -1106,7 +1189,7 @@ const makeDirectMethods = (rpcDefinition, helpers) => {
|
|
|
1106
1189
|
//#region src/DurableObjectDefinition.ts
|
|
1107
1190
|
var DurableObjectDefinition_exports = /* @__PURE__ */ __exportAll({
|
|
1108
1191
|
DurableObject: () => DurableObject$1,
|
|
1109
|
-
Tag: () => Tag$
|
|
1192
|
+
Tag: () => Tag$4,
|
|
1110
1193
|
implement: () => implement$3,
|
|
1111
1194
|
make: () => make$5,
|
|
1112
1195
|
method: () => method$1
|
|
@@ -1137,7 +1220,7 @@ const method$1 = method$4;
|
|
|
1137
1220
|
* });
|
|
1138
1221
|
* ```
|
|
1139
1222
|
*/
|
|
1140
|
-
const makeDefinition$
|
|
1223
|
+
const makeDefinition$2 = (id, methods) => {
|
|
1141
1224
|
assertNoReservedMethods("Durable Object", methods, reservedMethodNames$1);
|
|
1142
1225
|
const definition = make$8(id, methods);
|
|
1143
1226
|
return Object.assign(definition, {
|
|
@@ -1155,9 +1238,9 @@ const makeDefinition$1 = (id, methods) => {
|
|
|
1155
1238
|
})
|
|
1156
1239
|
});
|
|
1157
1240
|
};
|
|
1158
|
-
const make$5 = makeDefinition$
|
|
1159
|
-
const Tag$
|
|
1160
|
-
const definition = makeDefinition$
|
|
1241
|
+
const make$5 = makeDefinition$2;
|
|
1242
|
+
const Tag$4 = () => (id, methods) => {
|
|
1243
|
+
const definition = makeDefinition$2(id, methods);
|
|
1161
1244
|
class DurableObjectDefinitionClass {
|
|
1162
1245
|
static id = definition.id;
|
|
1163
1246
|
static methods = definition.methods;
|
|
@@ -1167,7 +1250,7 @@ const Tag$3 = () => (id, methods) => {
|
|
|
1167
1250
|
}
|
|
1168
1251
|
return DurableObjectDefinitionClass;
|
|
1169
1252
|
};
|
|
1170
|
-
const DurableObject$1 = Tag$
|
|
1253
|
+
const DurableObject$1 = Tag$4;
|
|
1171
1254
|
const wrapHandlers = (definition, handlers) => {
|
|
1172
1255
|
const wrapped = {};
|
|
1173
1256
|
for (const key of Object.keys(definition.methods)) {
|
|
@@ -1185,7 +1268,7 @@ const implement$3 = (_definition, handlers) => handlers;
|
|
|
1185
1268
|
//#endregion
|
|
1186
1269
|
//#region src/DurableObject.ts
|
|
1187
1270
|
var DurableObject_exports = /* @__PURE__ */ __exportAll({
|
|
1188
|
-
Tag: () => Tag$
|
|
1271
|
+
Tag: () => Tag$3,
|
|
1189
1272
|
implement: () => implement$2,
|
|
1190
1273
|
make: () => make$4,
|
|
1191
1274
|
method: () => method
|
|
@@ -1242,7 +1325,7 @@ const make$4 = (layer, options = {}) => {
|
|
|
1242
1325
|
defineEntrypointRpcMethods("Durable Object", EffectDurableObject.prototype, options.rpc, reservedMethodNames, (self, effect) => self[RunSymbol](effect));
|
|
1243
1326
|
return assumeEntrypointClass(EffectDurableObject);
|
|
1244
1327
|
};
|
|
1245
|
-
const Tag$
|
|
1328
|
+
const Tag$3 = Tag$4;
|
|
1246
1329
|
const method = method$1;
|
|
1247
1330
|
const implement$2 = implement$3;
|
|
1248
1331
|
//#endregion
|
|
@@ -1698,6 +1781,7 @@ const writeAttachment = (socket, key, clientId) => {
|
|
|
1698
1781
|
var Kv_exports = /* @__PURE__ */ __exportAll({
|
|
1699
1782
|
KvOperationError: () => KvOperationError,
|
|
1700
1783
|
Service: () => Service$1,
|
|
1784
|
+
Tag: () => Tag$2,
|
|
1701
1785
|
make: () => make$3
|
|
1702
1786
|
});
|
|
1703
1787
|
const TypeId$1 = "effect-cf/Kv";
|
|
@@ -1718,6 +1802,36 @@ const tryKvPromise = (binding, operation, evaluate) => Effect.tryPromise({
|
|
|
1718
1802
|
* Creates a typed KV service tag plus Effect helpers.
|
|
1719
1803
|
*/
|
|
1720
1804
|
const make$3 = (id, definition) => Service$1()(id, definition);
|
|
1805
|
+
const makeDefinition$1 = (id, definition) => {
|
|
1806
|
+
const kvDefinition = {
|
|
1807
|
+
id,
|
|
1808
|
+
key: definition.key,
|
|
1809
|
+
value: definition.value
|
|
1810
|
+
};
|
|
1811
|
+
return Object.assign(kvDefinition, {
|
|
1812
|
+
Binding: () => (bindingId, binding) => Service$1()(bindingId, {
|
|
1813
|
+
...binding,
|
|
1814
|
+
key: definition.key,
|
|
1815
|
+
value: definition.value
|
|
1816
|
+
}),
|
|
1817
|
+
binding: (bindingId, binding) => Service$1()(bindingId, {
|
|
1818
|
+
...binding,
|
|
1819
|
+
key: definition.key,
|
|
1820
|
+
value: definition.value
|
|
1821
|
+
})
|
|
1822
|
+
});
|
|
1823
|
+
};
|
|
1824
|
+
const Tag$2 = () => (id, definition) => {
|
|
1825
|
+
const kvDefinition = makeDefinition$1(id, definition);
|
|
1826
|
+
class KvDefinitionClass {
|
|
1827
|
+
static id = kvDefinition.id;
|
|
1828
|
+
static key = kvDefinition.key;
|
|
1829
|
+
static value = kvDefinition.value;
|
|
1830
|
+
static Binding = kvDefinition.Binding;
|
|
1831
|
+
static binding = kvDefinition.binding;
|
|
1832
|
+
}
|
|
1833
|
+
return KvDefinitionClass;
|
|
1834
|
+
};
|
|
1721
1835
|
/**
|
|
1722
1836
|
* Builds a KV service around a Cloudflare KV namespace binding.
|
|
1723
1837
|
*
|
|
@@ -1739,7 +1853,7 @@ const make$3 = (id, definition) => Service$1()(id, definition);
|
|
|
1739
1853
|
* ```
|
|
1740
1854
|
*/
|
|
1741
1855
|
const Service$1 = () => (id, definition) => {
|
|
1742
|
-
const tag = Service$
|
|
1856
|
+
const tag = Service$6()(id, definition.binding, (value) => {
|
|
1743
1857
|
if (typeof value !== "object" || value === null) return false;
|
|
1744
1858
|
const resource = value;
|
|
1745
1859
|
return typeof resource.get === "function" && typeof resource.put === "function" && typeof resource.delete === "function" && typeof resource.getWithMetadata === "function" && typeof resource.list === "function";
|
|
@@ -1841,7 +1955,7 @@ const isWorkflow = (value) => {
|
|
|
1841
1955
|
};
|
|
1842
1956
|
const make$2 = (id, definition) => Service()(id, definition);
|
|
1843
1957
|
const Service = () => (id, definition) => {
|
|
1844
|
-
const tag = Service$
|
|
1958
|
+
const tag = Service$6()(id, definition.binding, (value) => isWorkflow(value));
|
|
1845
1959
|
const encodePayload = Schema.encodeEffect(definition.payload);
|
|
1846
1960
|
const decodeResult = Schema.decodeUnknownEffect(definition.result);
|
|
1847
1961
|
const wrapInstance = (raw) => {
|
|
@@ -2025,4 +2139,4 @@ const waitForEvent = (name, options) => Effect.flatMap(WorkflowStep$1.asEffect()
|
|
|
2025
2139
|
const Tag = Tag$1;
|
|
2026
2140
|
const implement = implement$1;
|
|
2027
2141
|
//#endregion
|
|
2028
|
-
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 };
|
|
2142
|
+
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.
|
|
3
|
+
"version": "0.5.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/D1.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { D1Client } from "@effect/sql-d1";
|
|
2
|
+
import { Data, Effect, Layer } from "effect";
|
|
3
|
+
|
|
4
|
+
import * as Binding from "./Binding";
|
|
5
|
+
|
|
6
|
+
const TypeId = "effect-cf/D1" as const;
|
|
7
|
+
|
|
8
|
+
/** Error raised when a D1 operation fails. */
|
|
9
|
+
export class D1OperationError extends Data.TaggedError("D1OperationError")<{
|
|
10
|
+
readonly binding: string;
|
|
11
|
+
readonly operation: string;
|
|
12
|
+
readonly cause: unknown;
|
|
13
|
+
}> {}
|
|
14
|
+
|
|
15
|
+
/** Typed D1 binding definition. */
|
|
16
|
+
export interface D1Definition {
|
|
17
|
+
/** Binding name as configured in `wrangler.jsonc`. */
|
|
18
|
+
readonly binding: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Options forwarded to `@effect/sql-d1` when building a SQL client layer. */
|
|
22
|
+
export type D1SqlLayerOptions = Omit<D1Client.D1ClientConfig, "db">;
|
|
23
|
+
|
|
24
|
+
declare const D1ServiceTypeId: unique symbol;
|
|
25
|
+
|
|
26
|
+
/** Nominal service marker for D1 services created with {@link make}. */
|
|
27
|
+
export interface D1Service<Id extends string> {
|
|
28
|
+
readonly [D1ServiceTypeId]: {
|
|
29
|
+
readonly id: Id;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const isD1Database = (value: unknown): value is D1Database => {
|
|
34
|
+
if (typeof value !== "object" || value === null) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const resource = value as Record<string, unknown>;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
typeof resource.prepare === "function" &&
|
|
42
|
+
typeof resource.batch === "function" &&
|
|
43
|
+
typeof resource.exec === "function"
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const d1Error = (binding: string, operation: string, cause: unknown) =>
|
|
48
|
+
new D1OperationError({ binding, operation, cause });
|
|
49
|
+
|
|
50
|
+
const tryD1Promise = <A>(
|
|
51
|
+
binding: string,
|
|
52
|
+
operation: string,
|
|
53
|
+
evaluate: () => Promise<A>,
|
|
54
|
+
): Effect.Effect<A, D1OperationError> =>
|
|
55
|
+
Effect.tryPromise({
|
|
56
|
+
try: evaluate,
|
|
57
|
+
catch: (cause) => d1Error(binding, operation, cause),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const tryD1Sync = <A>(
|
|
61
|
+
binding: string,
|
|
62
|
+
operation: string,
|
|
63
|
+
evaluate: () => A,
|
|
64
|
+
): Effect.Effect<A, D1OperationError> =>
|
|
65
|
+
Effect.try({
|
|
66
|
+
try: evaluate,
|
|
67
|
+
catch: (cause) => d1Error(binding, operation, cause),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates a typed D1 service tag plus Effect helpers.
|
|
72
|
+
*/
|
|
73
|
+
export const make = <Id extends string>(id: Id, definition: D1Definition) =>
|
|
74
|
+
Service<D1Service<Id>>()(id, definition);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Builds a D1 service around a Cloudflare D1 database binding.
|
|
78
|
+
*
|
|
79
|
+
* The returned service exposes the raw `D1Database` binding, small native D1
|
|
80
|
+
* helpers, and `sqlLayer(...)` for providing `effect/unstable/sql` via
|
|
81
|
+
* `@effect/sql-d1`.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* class TodoDatabase extends D1.Service<TodoDatabase>()("TodoDatabase", {
|
|
86
|
+
* binding: "TODO_DB",
|
|
87
|
+
* }) {}
|
|
88
|
+
*
|
|
89
|
+
* const SqlLive = TodoDatabase.sqlLayer();
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export const Service =
|
|
93
|
+
<Self>() =>
|
|
94
|
+
<Id extends string>(id: Id, definition: D1Definition) => {
|
|
95
|
+
const tag = Binding.Service<Self>()(id, definition.binding, isD1Database);
|
|
96
|
+
|
|
97
|
+
const sqlLayer = (options?: D1SqlLayerOptions) =>
|
|
98
|
+
Layer.unwrap(
|
|
99
|
+
Effect.gen(function* () {
|
|
100
|
+
const db = yield* tag;
|
|
101
|
+
return D1Client.layer({ ...options, db });
|
|
102
|
+
}),
|
|
103
|
+
).pipe(Layer.provide(tag.layer));
|
|
104
|
+
|
|
105
|
+
const prepare = Effect.fnUntraced(function* (query: string) {
|
|
106
|
+
const db = yield* tag;
|
|
107
|
+
return yield* tryD1Sync(definition.binding, "prepare", () => db.prepare(query));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const batch = Effect.fnUntraced(function* <T = unknown>(
|
|
111
|
+
statements: ReadonlyArray<D1PreparedStatement>,
|
|
112
|
+
) {
|
|
113
|
+
const db = yield* tag;
|
|
114
|
+
return yield* tryD1Promise(definition.binding, "batch", () => db.batch<T>([...statements]));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const exec = Effect.fnUntraced(function* (query: string) {
|
|
118
|
+
const db = yield* tag;
|
|
119
|
+
return yield* tryD1Promise(definition.binding, "exec", () => db.exec(query));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
123
|
+
return yield* tag;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return Object.assign(tag, {
|
|
127
|
+
[TypeId]: TypeId,
|
|
128
|
+
definition,
|
|
129
|
+
sqlLayer,
|
|
130
|
+
prepare,
|
|
131
|
+
batch,
|
|
132
|
+
exec,
|
|
133
|
+
unsafeRaw,
|
|
134
|
+
});
|
|
135
|
+
};
|
package/src/Kv.ts
CHANGED
|
@@ -52,6 +52,26 @@ export interface KvDefinition<Key, Value, EncodedValue> {
|
|
|
52
52
|
readonly value: S.Codec<Value, EncodedValue>;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Reusable typed KV resource definition without a concrete Cloudflare binding name.
|
|
57
|
+
*/
|
|
58
|
+
export interface Definition<
|
|
59
|
+
Id extends string = string,
|
|
60
|
+
Key = unknown,
|
|
61
|
+
Value = unknown,
|
|
62
|
+
EncodedValue = unknown,
|
|
63
|
+
> {
|
|
64
|
+
readonly id: Id;
|
|
65
|
+
/** Codec used to encode/decode keys. */
|
|
66
|
+
readonly key: S.Codec<Key, string>;
|
|
67
|
+
/** Codec used to encode/decode values. */
|
|
68
|
+
readonly value: S.Codec<Value, EncodedValue>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export namespace Definition {
|
|
72
|
+
export type Any = Definition<string, any, any, any>;
|
|
73
|
+
}
|
|
74
|
+
|
|
55
75
|
declare const KvServiceTypeId: unique symbol;
|
|
56
76
|
|
|
57
77
|
/** Nominal service marker for KV services created with {@link make}. */
|
|
@@ -64,6 +84,16 @@ export interface KvService<Id extends string, Key, Value, EncodedValue> {
|
|
|
64
84
|
};
|
|
65
85
|
}
|
|
66
86
|
|
|
87
|
+
declare const BindingServiceTypeId: unique symbol;
|
|
88
|
+
|
|
89
|
+
/** Nominal service marker for KV bindings created from a reusable {@link Definition}. */
|
|
90
|
+
export interface BindingService<Id extends string, Self extends Definition.Any> {
|
|
91
|
+
readonly [BindingServiceTypeId]: {
|
|
92
|
+
readonly id: Id;
|
|
93
|
+
readonly definition: Self;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
const maybeString = (value: string | null | undefined): Option.Option<string> =>
|
|
68
98
|
value == null ? Option.none() : Option.some(value);
|
|
69
99
|
|
|
@@ -92,6 +122,66 @@ export const make = <Id extends string, Key, Value, EncodedValue>(
|
|
|
92
122
|
) =>
|
|
93
123
|
Service<KvService<Id, Key, Value, EncodedValue>>()<Id, Key, Value, EncodedValue>(id, definition);
|
|
94
124
|
|
|
125
|
+
const makeDefinition = <Id extends string, Key, Value, EncodedValue>(
|
|
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
|
+
};
|
|
135
|
+
|
|
136
|
+
return Object.assign(kvDefinition, {
|
|
137
|
+
Binding:
|
|
138
|
+
<Self>() =>
|
|
139
|
+
<BindingId extends string>(
|
|
140
|
+
bindingId: BindingId,
|
|
141
|
+
binding: Omit<KvDefinition<Key, Value, EncodedValue>, "key" | "value">,
|
|
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
|
+
});
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const Tag =
|
|
164
|
+
<_Self>() =>
|
|
165
|
+
<Id extends string, Key, Value, EncodedValue>(
|
|
166
|
+
id: Id,
|
|
167
|
+
definition: {
|
|
168
|
+
readonly key: S.Codec<Key, string>;
|
|
169
|
+
readonly value: S.Codec<Value, EncodedValue>;
|
|
170
|
+
},
|
|
171
|
+
) => {
|
|
172
|
+
const kvDefinition = makeDefinition(id, definition);
|
|
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
|
+
|
|
95
185
|
/**
|
|
96
186
|
* Builds a KV service around a Cloudflare KV namespace binding.
|
|
97
187
|
*
|
package/src/index.ts
CHANGED