effect-cf 0.6.0 → 0.8.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 +16 -0
- package/README.md +121 -29
- package/dist/Hyperdrive-By6DrSS_.d.mts +140 -0
- package/dist/Hyperdrive-LuQaVhXU.mjs +86 -0
- package/dist/HyperdrivePg.d.mts +14 -0
- package/dist/HyperdrivePg.mjs +13 -0
- package/dist/index.d.mts +1029 -1162
- package/dist/index.mjs +497 -515
- package/package.json +13 -1
- package/src/Binding.ts +17 -10
- package/src/D1.ts +3 -59
- package/src/DurableObject.ts +117 -23
- package/src/DurableObjectDefinition.ts +251 -72
- package/src/DurableObjectNamespace.ts +239 -287
- package/src/Hyperdrive.ts +82 -0
- package/src/HyperdrivePg.ts +27 -0
- package/src/Images.ts +332 -0
- package/src/Kv.ts +152 -240
- package/src/Queue.ts +18 -4
- package/src/QueueBinding.ts +45 -97
- package/src/QueueDefinition.ts +87 -41
- package/src/R2.ts +230 -0
- package/src/ServiceBinding.ts +260 -116
- package/src/Worker.ts +114 -20
- package/src/WorkerDefinition.ts +187 -63
- package/src/Workflow.ts +23 -4
- package/src/WorkflowBinding.ts +100 -109
- package/src/WorkflowDefinition.ts +116 -51
- package/src/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-cf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Effect-native primitives for Cloudflare Workers and bindings.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
"import": "./dist/index.mjs",
|
|
22
22
|
"default": "./dist/index.mjs"
|
|
23
23
|
},
|
|
24
|
+
"./hyperdrive-pg": {
|
|
25
|
+
"types": "./dist/HyperdrivePg.d.mts",
|
|
26
|
+
"import": "./dist/HyperdrivePg.mjs",
|
|
27
|
+
"default": "./dist/HyperdrivePg.mjs"
|
|
28
|
+
},
|
|
24
29
|
"./package.json": "./package.json"
|
|
25
30
|
},
|
|
26
31
|
"publishConfig": {
|
|
@@ -37,6 +42,7 @@
|
|
|
37
42
|
"@cloudflare/vitest-pool-workers": "^0.15.0",
|
|
38
43
|
"@cloudflare/workers-types": "^4.20260421.1",
|
|
39
44
|
"@effect/sql-d1": "catalog:",
|
|
45
|
+
"@effect/sql-pg": "4.0.0-beta.65",
|
|
40
46
|
"@effect/vitest": "catalog:",
|
|
41
47
|
"effect": "catalog:",
|
|
42
48
|
"typescript": "^6.0.2",
|
|
@@ -45,6 +51,12 @@
|
|
|
45
51
|
},
|
|
46
52
|
"peerDependencies": {
|
|
47
53
|
"@effect/sql-d1": "^4.0.0-beta.65",
|
|
54
|
+
"@effect/sql-pg": "^4.0.0-beta.65",
|
|
48
55
|
"effect": "^4.0.0-beta.65"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"@effect/sql-pg": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
49
61
|
}
|
|
50
62
|
}
|
package/src/Binding.ts
CHANGED
|
@@ -59,6 +59,21 @@ export interface BindingService<Self, Id extends string, Service> extends Contex
|
|
|
59
59
|
>;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export const layer = <Self, Resource, Service = Resource>(
|
|
63
|
+
tag: Context.Service<Self, Service>,
|
|
64
|
+
binding: string,
|
|
65
|
+
isResource: (value: unknown) => value is Resource,
|
|
66
|
+
wrap?: (resource: Resource) => Service,
|
|
67
|
+
): Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment> =>
|
|
68
|
+
Layer.effect(
|
|
69
|
+
tag,
|
|
70
|
+
Effect.gen(function* () {
|
|
71
|
+
const env = yield* WorkerEnvironment;
|
|
72
|
+
const resource = yield* getBinding(env, binding, isResource);
|
|
73
|
+
return wrap === undefined ? (resource as unknown as Service) : wrap(resource);
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
|
|
62
77
|
export const Service =
|
|
63
78
|
<Self>() =>
|
|
64
79
|
<Id extends string, Resource, Service = Resource>(
|
|
@@ -68,19 +83,11 @@ export const Service =
|
|
|
68
83
|
wrap?: (resource: Resource) => Service,
|
|
69
84
|
): BindingService<Self, Id, Service> => {
|
|
70
85
|
const tag = Context.Service<Self, Service>()(id);
|
|
71
|
-
|
|
72
|
-
const layer = Layer.effect(
|
|
73
|
-
tag,
|
|
74
|
-
Effect.gen(function* () {
|
|
75
|
-
const env = yield* WorkerEnvironment;
|
|
76
|
-
const resource = yield* getBinding(env, binding, isResource);
|
|
77
|
-
return wrap === undefined ? (resource as unknown as Service) : wrap(resource);
|
|
78
|
-
}),
|
|
79
|
-
);
|
|
86
|
+
const serviceLayer = layer(tag, binding, isResource, wrap);
|
|
80
87
|
|
|
81
88
|
return Object.assign(tag, {
|
|
82
89
|
[TypeId]: TypeId,
|
|
83
90
|
binding,
|
|
84
|
-
layer,
|
|
91
|
+
layer: serviceLayer,
|
|
85
92
|
}) as BindingService<Self, Id, Service>;
|
|
86
93
|
};
|
package/src/D1.ts
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import { D1Client } from "@effect/sql-d1";
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
3
|
|
|
4
4
|
import * as Binding from "./Binding";
|
|
5
5
|
|
|
6
6
|
const TypeId = "effect-cf/D1" as const;
|
|
7
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
8
|
/** Typed D1 binding definition. */
|
|
16
9
|
export interface D1Definition {
|
|
17
10
|
/** Binding name as configured in `wrangler.jsonc`. */
|
|
@@ -44,29 +37,6 @@ const isD1Database = (value: unknown): value is D1Database => {
|
|
|
44
37
|
);
|
|
45
38
|
};
|
|
46
39
|
|
|
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
40
|
/**
|
|
71
41
|
* Creates a typed D1 service tag plus Effect helpers.
|
|
72
42
|
*/
|
|
@@ -76,9 +46,8 @@ export const make = <Id extends string>(id: Id, definition: D1Definition) =>
|
|
|
76
46
|
/**
|
|
77
47
|
* Builds a D1 service around a Cloudflare D1 database binding.
|
|
78
48
|
*
|
|
79
|
-
* The returned service exposes the raw `D1Database` binding
|
|
80
|
-
*
|
|
81
|
-
* `@effect/sql-d1`.
|
|
49
|
+
* The returned service exposes the raw `D1Database` binding and `sqlLayer(...)`
|
|
50
|
+
* for providing `effect/unstable/sql` via `@effect/sql-d1`.
|
|
82
51
|
*
|
|
83
52
|
* @example
|
|
84
53
|
* ```ts
|
|
@@ -102,34 +71,9 @@ export const Service =
|
|
|
102
71
|
}),
|
|
103
72
|
).pipe(Layer.provide(tag.layer));
|
|
104
73
|
|
|
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
74
|
return Object.assign(tag, {
|
|
127
75
|
[TypeId]: TypeId,
|
|
128
76
|
definition,
|
|
129
77
|
sqlLayer,
|
|
130
|
-
prepare,
|
|
131
|
-
batch,
|
|
132
|
-
exec,
|
|
133
|
-
unsafeRaw,
|
|
134
78
|
});
|
|
135
79
|
};
|
package/src/DurableObject.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
2
|
-
import { Effect, Layer, ManagedRuntime, type Scope } from "effect";
|
|
2
|
+
import { Effect, Layer, ManagedRuntime, type Context, type Scope } from "effect";
|
|
3
|
+
import type { Schema as S } from "effect";
|
|
3
4
|
|
|
4
5
|
import { NativeRequest } from "./Worker";
|
|
5
6
|
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
6
7
|
import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
|
|
7
8
|
import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
|
|
9
|
+
import type * as Binding from "./Binding";
|
|
8
10
|
import * as DurableObjectDefinition from "./DurableObjectDefinition";
|
|
11
|
+
import type * as DurableObjectNamespace from "./DurableObjectNamespace";
|
|
12
|
+
import type * as Rpc from "./Rpc";
|
|
9
13
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
10
14
|
|
|
11
15
|
const reservedMethodNames = new Set<string>([
|
|
@@ -208,40 +212,130 @@ export const make = <
|
|
|
208
212
|
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut>>(EffectDurableObject);
|
|
209
213
|
};
|
|
210
214
|
|
|
211
|
-
export
|
|
215
|
+
export type ServiceFreeSchema = S.Codec<any, any, never, never>;
|
|
212
216
|
|
|
213
|
-
export
|
|
217
|
+
export interface Method<
|
|
218
|
+
Args extends ReadonlyArray<ServiceFreeSchema> = ReadonlyArray<ServiceFreeSchema>,
|
|
219
|
+
Success extends ServiceFreeSchema = ServiceFreeSchema,
|
|
220
|
+
> {
|
|
221
|
+
readonly args: Args;
|
|
222
|
+
readonly success: Success;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export namespace Method {
|
|
226
|
+
export type Any = Method<ReadonlyArray<ServiceFreeSchema>, ServiceFreeSchema>;
|
|
227
|
+
|
|
228
|
+
type ArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = Args extends readonly []
|
|
229
|
+
? []
|
|
230
|
+
: Args extends readonly [
|
|
231
|
+
infer Head extends ServiceFreeSchema,
|
|
232
|
+
...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
|
|
233
|
+
]
|
|
234
|
+
? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
|
|
235
|
+
: Array<S.Schema.Type<Args[number]>>;
|
|
214
236
|
|
|
215
|
-
export
|
|
237
|
+
export type Args<Self extends Any> = ArgsFromSchemas<Self["args"]>;
|
|
238
|
+
|
|
239
|
+
export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
|
|
240
|
+
}
|
|
216
241
|
|
|
217
|
-
export type
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
242
|
+
export type Methods = Record<string, Method.Any>;
|
|
243
|
+
|
|
244
|
+
export type ReservedMethodName = DurableObjectDefinition.ReservedMethodName;
|
|
245
|
+
|
|
246
|
+
export type NoReservedMethods<MethodsShape extends Methods> =
|
|
247
|
+
Extract<keyof MethodsShape, ReservedMethodName> extends never ? MethodsShape : never;
|
|
248
|
+
|
|
249
|
+
export interface Definition<Id extends string = string, MethodsShape extends Methods = Methods> {
|
|
250
|
+
readonly id: Id;
|
|
251
|
+
readonly methods: MethodsShape;
|
|
252
|
+
}
|
|
222
253
|
|
|
223
254
|
export namespace Definition {
|
|
224
|
-
export type Any =
|
|
255
|
+
export type Any = Definition<string, Methods>;
|
|
225
256
|
}
|
|
226
257
|
|
|
227
|
-
export type
|
|
228
|
-
DurableObjectDefinition.ServerApi<Self>;
|
|
258
|
+
export type LayerOptions = DurableObjectDefinition.LayerOptions;
|
|
229
259
|
|
|
230
|
-
export type
|
|
231
|
-
|
|
260
|
+
export type ServerApi<Self extends Definition.Any> = {
|
|
261
|
+
readonly [Key in keyof Self["methods"]]: (
|
|
262
|
+
...args: Method.Args<Self["methods"][Key]>
|
|
263
|
+
) => Promise<Method.Success<Self["methods"][Key]>>;
|
|
264
|
+
};
|
|
232
265
|
|
|
233
|
-
export type
|
|
234
|
-
ROut,
|
|
235
|
-
Self extends DurableObjectDefinition.Definition.Any,
|
|
236
|
-
> = DurableObjectDefinition.Handlers<ROut, Self>;
|
|
266
|
+
export type Api<Self extends Definition.Any> = Rpc.Provider<ServerApi<Self>, ReservedMethodName>;
|
|
237
267
|
|
|
238
|
-
export type
|
|
268
|
+
export type Handlers<ROut, Self extends Definition.Any> = {
|
|
269
|
+
readonly [Key in keyof Self["methods"]]: (
|
|
270
|
+
...args: Method.Args<Self["methods"][Key]>
|
|
271
|
+
) => DurableObjectHandler<ROut, Method.Success<Self["methods"][Key]>>;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export interface Options<ROut, Self extends Definition.Any> extends Omit<
|
|
275
|
+
DurableObjectOptions<ROut, Handlers<ROut, Self>>,
|
|
276
|
+
"rpc"
|
|
277
|
+
> {
|
|
278
|
+
readonly rpc: Handlers<ROut, Self>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Context.ServiceClass<
|
|
282
|
+
Self,
|
|
283
|
+
Id,
|
|
284
|
+
DurableObjectNamespace.DurableObjectNamespaceEffectClient<
|
|
285
|
+
Api<Definition<Id, MethodsShape>>,
|
|
286
|
+
Definition<Id, MethodsShape>
|
|
287
|
+
>
|
|
288
|
+
> &
|
|
289
|
+
DurableObjectNamespace.DurableObjectNamespaceStaticClient<
|
|
290
|
+
Self,
|
|
291
|
+
Api<Definition<Id, MethodsShape>>,
|
|
292
|
+
Definition<Id, MethodsShape>
|
|
293
|
+
> & {
|
|
294
|
+
readonly id: Id;
|
|
295
|
+
readonly methods: MethodsShape;
|
|
296
|
+
readonly make: <ROut, LayerError>(
|
|
297
|
+
layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
|
|
298
|
+
options: Options<ROut, Definition<Id, MethodsShape>>,
|
|
299
|
+
) => DurableObjectClass<Handlers<ROut, Definition<Id, MethodsShape>>, ROut>;
|
|
300
|
+
readonly layer: (
|
|
301
|
+
options: LayerOptions,
|
|
302
|
+
) => Layer.Layer<
|
|
303
|
+
Self,
|
|
304
|
+
Binding.BindingNotFoundError | Binding.BindingValidationError,
|
|
305
|
+
WorkerEnvironment
|
|
306
|
+
>;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
export type TagFactory = <Self>() => <Id extends string, const MethodsShape extends Methods>(
|
|
310
|
+
id: Id,
|
|
311
|
+
methods: MethodsShape & NoReservedMethods<MethodsShape>,
|
|
312
|
+
) => TagClass<Self, Id, MethodsShape>;
|
|
313
|
+
|
|
314
|
+
export const Tag = DurableObjectDefinition.Tag as unknown as TagFactory;
|
|
315
|
+
|
|
316
|
+
export const method = DurableObjectDefinition.method as {
|
|
317
|
+
<Success extends ServiceFreeSchema>(definition: {
|
|
318
|
+
readonly success: Success;
|
|
319
|
+
}): Method<readonly [], Success>;
|
|
320
|
+
<
|
|
321
|
+
const Args extends ReadonlyArray<ServiceFreeSchema>,
|
|
322
|
+
Success extends ServiceFreeSchema,
|
|
323
|
+
>(definition: {
|
|
324
|
+
readonly args: Args;
|
|
325
|
+
readonly success: Success;
|
|
326
|
+
}): Method<Args, Success>;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
export const implement = DurableObjectDefinition.implement as unknown as <
|
|
239
330
|
ROut,
|
|
240
|
-
Self extends
|
|
241
|
-
>
|
|
331
|
+
const Self extends Definition.Any,
|
|
332
|
+
>(
|
|
333
|
+
_definition: Self,
|
|
334
|
+
handlers: Handlers<ROut, Self>,
|
|
335
|
+
) => Handlers<ROut, Self>;
|
|
242
336
|
|
|
243
337
|
export type HandlerEffect<
|
|
244
338
|
ROut,
|
|
245
|
-
Self extends
|
|
339
|
+
Self extends Definition.Any,
|
|
246
340
|
Key extends keyof Self["methods"],
|
|
247
|
-
> =
|
|
341
|
+
> = DurableObjectHandler<ROut, Method.Success<Self["methods"][Key]>>;
|