effect-cf 0.4.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 +6 -0
- package/README.md +2 -1
- package/dist/index.d.mts +78 -4
- package/dist/index.mjs +94 -11
- package/package.json +3 -1
- package/src/D1.ts +135 -0
- package/src/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# effect-cf
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#14](https://github.com/danieljvdm/effect-cf/pull/14) [`a31a930`](https://github.com/danieljvdm/effect-cf/commit/a31a93020a679e42a748aed54626ce7387d7e685) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Add a D1 binding helper with native Effect wrappers and an `@effect/sql-d1` backed SQL layer.
|
|
8
|
+
|
|
3
9
|
## 0.4.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# effect-cf
|
|
2
2
|
|
|
3
|
-
Effect-native Cloudflare primitives for Workers, Durable Objects, bindings, KV, Queues, Workflows, and Durable Object storage.
|
|
3
|
+
Effect-native Cloudflare primitives for Workers, Durable Objects, bindings, KV, D1, Queues, Workflows, and Durable Object storage.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -31,6 +31,7 @@ Runtime creation belongs at Cloudflare entrypoints, not inside binding helpers.
|
|
|
31
31
|
- `DurableObjectState` / `DurableObjectStorage` - Effect wrappers for state, alarms, SQL, and embedded KV
|
|
32
32
|
- `DurableObjectWebSocket` - WebSocket upgrade helpers for Durable Objects
|
|
33
33
|
- `Kv` - typed KV namespace helper
|
|
34
|
+
- `D1` - typed D1 database binding helper with an `@effect/sql-d1` backed SQL layer
|
|
34
35
|
- `Queue` / `QueueBinding` - typed Queue producer bindings and consumer handlers
|
|
35
36
|
- `Workflow` / `WorkflowBinding` - typed Workflow entrypoints, steps, and starter bindings
|
|
36
37
|
- `Rpc` - Cloudflare RPC type helpers and scoped disposal utilities
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Cause, Config, ConfigProvider, Context, DateTime, Duration, Effect, Layer, Option, Schema, Scope } from "effect";
|
|
2
|
+
import { D1Client } from "@effect/sql-d1";
|
|
2
3
|
import { DurableObject, RpcStub, RpcTarget, WorkerEntrypoint, WorkflowEntrypoint, WorkflowEvent, WorkflowSleepDuration, WorkflowStep, WorkflowStepConfig, WorkflowStepContext, WorkflowStepEvent, WorkflowTimeoutDuration } from "cloudflare:workers";
|
|
3
4
|
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
4
5
|
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
@@ -7,9 +8,11 @@ import * as _$effect_Types0 from "effect/Types";
|
|
|
7
8
|
import * as _$effect_Cause0 from "effect/Cause";
|
|
8
9
|
import * as _$effect_Redacted0 from "effect/Redacted";
|
|
9
10
|
import * as _$effect_Layer0 from "effect/Layer";
|
|
10
|
-
import * as _$effect_Schema0 from "effect/Schema";
|
|
11
11
|
import * as _$effect_Context0 from "effect/Context";
|
|
12
|
+
import * as _$effect_unstable_sql_SqlClient0 from "effect/unstable/sql/SqlClient";
|
|
13
|
+
import * as _$effect_Config0 from "effect/Config";
|
|
12
14
|
import * as _$effect_Scope0 from "effect/Scope";
|
|
15
|
+
import * as _$effect_Schema0 from "effect/Schema";
|
|
13
16
|
|
|
14
17
|
//#region src/Environment.d.ts
|
|
15
18
|
/** Cloudflare worker environment object (`env`). */
|
|
@@ -83,7 +86,7 @@ declare namespace WorkerConfig {
|
|
|
83
86
|
const layerWith: (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) => _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
|
|
84
87
|
}
|
|
85
88
|
declare namespace Binding_d_exports {
|
|
86
|
-
export { BindingNotFoundError, BindingValidationError, Service$
|
|
89
|
+
export { BindingNotFoundError, BindingValidationError, Service$6 as Service, TypeId$2 as TypeId };
|
|
87
90
|
}
|
|
88
91
|
/** Internal type id marker used by binding helper services. */
|
|
89
92
|
declare const TypeId$2: "effect-cf/Binding";
|
|
@@ -104,11 +107,82 @@ declare class BindingValidationError extends BindingValidationError_base<{
|
|
|
104
107
|
/**
|
|
105
108
|
* Creates a Context tag + layer for reading and validating a Cloudflare binding.
|
|
106
109
|
*/
|
|
107
|
-
declare const Service$
|
|
110
|
+
declare const Service$6: <Self>() => <Id extends string, Resource>(id: Id, binding: string, isResource: (value: unknown) => value is Resource) => Context.ServiceClass<Self, Id, Resource> & {
|
|
108
111
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
109
112
|
binding: string;
|
|
110
113
|
layer: Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
|
|
111
114
|
};
|
|
115
|
+
declare namespace D1_d_exports {
|
|
116
|
+
export { D1Definition, D1OperationError, D1Service, D1SqlLayerOptions, Service$5 as Service, make$11 as make };
|
|
117
|
+
}
|
|
118
|
+
declare const D1OperationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
119
|
+
readonly _tag: "D1OperationError";
|
|
120
|
+
} & Readonly<A>;
|
|
121
|
+
/** Error raised when a D1 operation fails. */
|
|
122
|
+
declare class D1OperationError extends D1OperationError_base<{
|
|
123
|
+
readonly binding: string;
|
|
124
|
+
readonly operation: string;
|
|
125
|
+
readonly cause: unknown;
|
|
126
|
+
}> {}
|
|
127
|
+
/** Typed D1 binding definition. */
|
|
128
|
+
interface D1Definition {
|
|
129
|
+
/** Binding name as configured in `wrangler.jsonc`. */
|
|
130
|
+
readonly binding: string;
|
|
131
|
+
}
|
|
132
|
+
/** Options forwarded to `@effect/sql-d1` when building a SQL client layer. */
|
|
133
|
+
type D1SqlLayerOptions = Omit<D1Client.D1ClientConfig, "db">;
|
|
134
|
+
declare const D1ServiceTypeId: unique symbol;
|
|
135
|
+
/** Nominal service marker for D1 services created with {@link make}. */
|
|
136
|
+
interface D1Service<Id extends string> {
|
|
137
|
+
readonly [D1ServiceTypeId]: {
|
|
138
|
+
readonly id: Id;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Creates a typed D1 service tag plus Effect helpers.
|
|
143
|
+
*/
|
|
144
|
+
declare const make$11: <Id extends string>(id: Id, definition: D1Definition) => _$effect_Context0.ServiceClass<D1Service<Id>, Id, D1Database> & {
|
|
145
|
+
"effect-cf/Binding": "effect-cf/Binding";
|
|
146
|
+
binding: string;
|
|
147
|
+
layer: Layer.Layer<D1Service<Id>, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
|
|
148
|
+
} & {
|
|
149
|
+
"effect-cf/D1": "effect-cf/D1";
|
|
150
|
+
definition: D1Definition;
|
|
151
|
+
sqlLayer: (options?: D1SqlLayerOptions) => Layer.Layer<D1Client.D1Client | _$effect_unstable_sql_SqlClient0.SqlClient, BindingNotFoundError | BindingValidationError | _$effect_Config0.ConfigError, WorkerEnvironment>;
|
|
152
|
+
prepare: (query: string) => Effect.Effect<D1PreparedStatement, D1OperationError, D1Service<Id>>;
|
|
153
|
+
batch: <T = unknown>(statements: readonly D1PreparedStatement[]) => Effect.Effect<D1Result<T>[], D1OperationError, D1Service<Id>>;
|
|
154
|
+
exec: (query: string) => Effect.Effect<D1ExecResult, D1OperationError, D1Service<Id>>;
|
|
155
|
+
unsafeRaw: () => Effect.Effect<D1Database, never, D1Service<Id>>;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Builds a D1 service around a Cloudflare D1 database binding.
|
|
159
|
+
*
|
|
160
|
+
* The returned service exposes the raw `D1Database` binding, small native D1
|
|
161
|
+
* helpers, and `sqlLayer(...)` for providing `effect/unstable/sql` via
|
|
162
|
+
* `@effect/sql-d1`.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* class TodoDatabase extends D1.Service<TodoDatabase>()("TodoDatabase", {
|
|
167
|
+
* binding: "TODO_DB",
|
|
168
|
+
* }) {}
|
|
169
|
+
*
|
|
170
|
+
* const SqlLive = TodoDatabase.sqlLayer();
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
declare const Service$5: <Self>() => <Id extends string>(id: Id, definition: D1Definition) => _$effect_Context0.ServiceClass<Self, Id, D1Database> & {
|
|
174
|
+
"effect-cf/Binding": "effect-cf/Binding";
|
|
175
|
+
binding: string;
|
|
176
|
+
layer: Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
|
|
177
|
+
} & {
|
|
178
|
+
"effect-cf/D1": "effect-cf/D1";
|
|
179
|
+
definition: D1Definition;
|
|
180
|
+
sqlLayer: (options?: D1SqlLayerOptions) => Layer.Layer<D1Client.D1Client | _$effect_unstable_sql_SqlClient0.SqlClient, BindingNotFoundError | BindingValidationError | _$effect_Config0.ConfigError, WorkerEnvironment | Exclude<Exclude<Self, _$effect_Scope0.Scope>, Self>>;
|
|
181
|
+
prepare: (query: string) => Effect.Effect<D1PreparedStatement, D1OperationError, Self>;
|
|
182
|
+
batch: <T = unknown>(statements: readonly D1PreparedStatement[]) => Effect.Effect<D1Result<T>[], D1OperationError, Self>;
|
|
183
|
+
exec: (query: string) => Effect.Effect<D1ExecResult, D1OperationError, Self>;
|
|
184
|
+
unsafeRaw: () => Effect.Effect<D1Database, never, Self>;
|
|
185
|
+
};
|
|
112
186
|
declare namespace Rpc_d_exports {
|
|
113
187
|
export { DisposableValue, MethodArgs, MethodKey, MethodReturn, Provider, Result, RpcStub, RpcTarget, Serializable, Stub, Stubable, Stubify, dispose, isDisposable, resolve, scoped };
|
|
114
188
|
}
|
|
@@ -2382,4 +2456,4 @@ declare namespace Definition {
|
|
|
2382
2456
|
type Handler<ROut, Self extends Definition$1.Any> = Handler$1<ROut, Self>;
|
|
2383
2457
|
type Options<ROut, Self extends Definition$1.Any> = Options$1<ROut, Self>;
|
|
2384
2458
|
//#endregion
|
|
2385
|
-
export { Binding_d_exports as Binding, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, ServiceBinding_d_exports as ServiceBinding, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
|
|
2459
|
+
export { Binding_d_exports as Binding, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, ServiceBinding_d_exports as ServiceBinding, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
|
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;
|
|
@@ -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({
|
|
@@ -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
|
});
|
|
@@ -1770,7 +1853,7 @@ const Tag$2 = () => (id, definition) => {
|
|
|
1770
1853
|
* ```
|
|
1771
1854
|
*/
|
|
1772
1855
|
const Service$1 = () => (id, definition) => {
|
|
1773
|
-
const tag = Service$
|
|
1856
|
+
const tag = Service$6()(id, definition.binding, (value) => {
|
|
1774
1857
|
if (typeof value !== "object" || value === null) return false;
|
|
1775
1858
|
const resource = value;
|
|
1776
1859
|
return typeof resource.get === "function" && typeof resource.put === "function" && typeof resource.delete === "function" && typeof resource.getWithMetadata === "function" && typeof resource.list === "function";
|
|
@@ -1872,7 +1955,7 @@ const isWorkflow = (value) => {
|
|
|
1872
1955
|
};
|
|
1873
1956
|
const make$2 = (id, definition) => Service()(id, definition);
|
|
1874
1957
|
const Service = () => (id, definition) => {
|
|
1875
|
-
const tag = Service$
|
|
1958
|
+
const tag = Service$6()(id, definition.binding, (value) => isWorkflow(value));
|
|
1876
1959
|
const encodePayload = Schema.encodeEffect(definition.payload);
|
|
1877
1960
|
const decodeResult = Schema.decodeUnknownEffect(definition.result);
|
|
1878
1961
|
const wrapInstance = (raw) => {
|
|
@@ -2056,4 +2139,4 @@ const waitForEvent = (name, options) => Effect.flatMap(WorkflowStep$1.asEffect()
|
|
|
2056
2139
|
const Tag = Tag$1;
|
|
2057
2140
|
const implement = implement$1;
|
|
2058
2141
|
//#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 };
|
|
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/index.ts
CHANGED