effect-cf 0.20.0 → 0.22.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 +56 -0
- package/README.md +2 -2
- package/dist/{Hyperdrive-RTjgirMV.d.mts → Hyperdrive-COyg7R26.d.mts} +40 -13
- package/dist/HyperdrivePg.d.mts +13 -5
- package/dist/HyperdrivePg.mjs +12 -7
- package/dist/index.d.mts +708 -549
- package/dist/index.mjs +946 -589
- package/package.json +10 -5
- package/src/AiGateway.ts +82 -47
- package/src/AnalyticsEngine.ts +74 -58
- package/src/Binding.ts +66 -13
- package/src/BrowserRendering.ts +30 -7
- package/src/Cache.ts +32 -8
- package/src/CloudflareOtlp.ts +32 -9
- package/src/ContainerNamespace.ts +87 -55
- package/src/D1.ts +13 -3
- package/src/DurableObject.ts +27 -164
- package/src/DurableObjectAlarm.ts +30 -9
- package/src/DurableObjectDefinition.ts +58 -54
- package/src/DurableObjectNamespace.ts +99 -96
- package/src/DurableObjectRpcWebSocket.ts +14 -6
- package/src/DurableObjectSqlite.ts +6 -3
- package/src/DurableObjectState.ts +6 -9
- package/src/DurableObjectStorage.ts +7 -1
- package/src/DurableObjectWebSocket.ts +51 -41
- package/src/Email.ts +40 -13
- package/src/Environment.ts +26 -6
- package/src/Hyperdrive.ts +2 -2
- package/src/HyperdrivePg.ts +25 -13
- package/src/Images.ts +77 -82
- package/src/Kv.ts +37 -27
- package/src/Queue.ts +6 -1
- package/src/QueueBinding.ts +18 -21
- package/src/QueueDefinition.ts +15 -14
- package/src/R2.ts +107 -29
- package/src/Rpc.ts +3 -1
- package/src/RpcDefinition.ts +177 -133
- package/src/ServiceBinding.ts +112 -108
- package/src/Vectorize.ts +59 -14
- package/src/Worker.ts +205 -198
- package/src/WorkerDefinition.ts +5 -10
- package/src/WorkersAi.ts +27 -17
- package/src/Workflow.ts +54 -49
- package/src/WorkflowBinding.ts +19 -11
- package/src/WorkflowDefinition.ts +24 -27
- package/src/index.ts +4 -3
- package/src/internal/Entrypoint.ts +3 -1
- package/src/internal/ErrorMessage.ts +24 -0
- package/src/internal/RpcInvocation.ts +11 -8
- package/src/internal/Runtime.ts +52 -0
- package/src/internal/WorkerContext.ts +14 -2
- package/src/Entry.ts +0 -259
- package/src/internal/Clock.ts +0 -37
package/src/RpcDefinition.ts
CHANGED
|
@@ -1,44 +1,127 @@
|
|
|
1
|
-
import { Data, Effect, Schema as S } from "effect";
|
|
1
|
+
import { Data, Effect, Predicate, Schema as S } from "effect";
|
|
2
2
|
|
|
3
3
|
import type * as Rpc from "./Rpc";
|
|
4
|
+
import * as ErrorMessage from "./internal/ErrorMessage";
|
|
4
5
|
|
|
5
6
|
export class RpcReservedMethodNameError extends Data.TaggedError("RpcReservedMethodNameError")<{
|
|
6
7
|
readonly definition: string;
|
|
7
8
|
readonly method: string;
|
|
8
|
-
}> {
|
|
9
|
+
}> {
|
|
10
|
+
override get message() {
|
|
11
|
+
return `${this.definition} RPC method "${this.method}" is reserved by Cloudflare Workers RPC`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
9
14
|
|
|
10
|
-
export class RpcArgumentCountError extends
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
export class RpcArgumentCountError extends S.TaggedError<RpcArgumentCountError>()(
|
|
16
|
+
"RpcArgumentCountError",
|
|
17
|
+
{
|
|
18
|
+
definition: S.String,
|
|
19
|
+
method: S.String,
|
|
20
|
+
expected: S.Number,
|
|
21
|
+
actual: S.Number,
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
24
|
+
override get message(): string {
|
|
25
|
+
return `${this.definition} RPC method "${this.method}" expected ${this.expected} arguments but received ${this.actual}`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
16
28
|
|
|
17
|
-
export class RpcArgumentDecodeError extends
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
export class RpcArgumentDecodeError extends S.TaggedError<RpcArgumentDecodeError>()(
|
|
30
|
+
"RpcArgumentDecodeError",
|
|
31
|
+
{
|
|
32
|
+
definition: S.String,
|
|
33
|
+
method: S.String,
|
|
34
|
+
cause: S.Defect(),
|
|
35
|
+
},
|
|
36
|
+
) {
|
|
37
|
+
override get message(): string {
|
|
38
|
+
return `${this.definition} RPC method "${this.method}" argument decode failed: ${ErrorMessage.causeMessage(this.cause)}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
23
41
|
|
|
24
42
|
export class RpcArgumentEncodeError extends Data.TaggedError("RpcArgumentEncodeError")<{
|
|
25
43
|
readonly definition: string;
|
|
26
44
|
readonly method: string;
|
|
27
|
-
readonly index: number;
|
|
28
45
|
readonly cause: unknown;
|
|
29
|
-
}> {
|
|
46
|
+
}> {
|
|
47
|
+
override get message(): string {
|
|
48
|
+
return `${this.definition} RPC method "${this.method}" argument encode failed: ${ErrorMessage.causeMessage(this.cause)}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
30
51
|
|
|
31
52
|
export class RpcSuccessDecodeError extends Data.TaggedError("RpcSuccessDecodeError")<{
|
|
32
53
|
readonly definition: string;
|
|
33
54
|
readonly method: string;
|
|
34
55
|
readonly cause: unknown;
|
|
35
|
-
}> {
|
|
56
|
+
}> {
|
|
57
|
+
override get message(): string {
|
|
58
|
+
return `${this.definition} RPC method "${this.method}" success decode failed: ${ErrorMessage.causeMessage(this.cause)}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
36
61
|
|
|
37
|
-
export class RpcSuccessEncodeError extends
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
62
|
+
export class RpcSuccessEncodeError extends S.TaggedError<RpcSuccessEncodeError>()(
|
|
63
|
+
"RpcSuccessEncodeError",
|
|
64
|
+
{
|
|
65
|
+
definition: S.String,
|
|
66
|
+
method: S.String,
|
|
67
|
+
cause: S.Defect(),
|
|
68
|
+
},
|
|
69
|
+
) {
|
|
70
|
+
override get message(): string {
|
|
71
|
+
return `${this.definition} RPC method "${this.method}" success encode failed: ${ErrorMessage.causeMessage(this.cause)}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Errors raised server-side that must survive the Cloudflare RPC wire. */
|
|
76
|
+
export type WireError = RpcArgumentCountError | RpcArgumentDecodeError | RpcSuccessEncodeError;
|
|
77
|
+
|
|
78
|
+
const WireErrorSchema = S.Union([
|
|
79
|
+
RpcArgumentCountError,
|
|
80
|
+
RpcArgumentDecodeError,
|
|
81
|
+
RpcSuccessEncodeError,
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
const wireErrorKey = "effect-cf/RpcDefinition/wireError";
|
|
85
|
+
|
|
86
|
+
export const isWireError = (error: unknown): error is WireError =>
|
|
87
|
+
error instanceof RpcArgumentCountError ||
|
|
88
|
+
error instanceof RpcArgumentDecodeError ||
|
|
89
|
+
error instanceof RpcSuccessEncodeError;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Encodes package RPC errors into a plain `Error` envelope so their tags
|
|
93
|
+
* survive Cloudflare RPC serialization, which drops custom error properties.
|
|
94
|
+
*/
|
|
95
|
+
export const encodeWireError = (error: unknown): unknown => {
|
|
96
|
+
if (!isWireError(error)) {
|
|
97
|
+
return error;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
return new Error(JSON.stringify({ [wireErrorKey]: S.encodeSync(WireErrorSchema)(error) }));
|
|
102
|
+
} catch {
|
|
103
|
+
return error;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** Restores package RPC errors from the {@link encodeWireError} envelope. */
|
|
108
|
+
export const decodeWireError = (cause: unknown): unknown => {
|
|
109
|
+
if (!(cause instanceof Error)) {
|
|
110
|
+
return cause;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const parsed: unknown = JSON.parse(cause.message);
|
|
115
|
+
|
|
116
|
+
if (!Predicate.hasProperty(parsed, wireErrorKey)) {
|
|
117
|
+
return cause;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return S.decodeUnknownSync(WireErrorSchema)(parsed[wireErrorKey]);
|
|
121
|
+
} catch {
|
|
122
|
+
return cause;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
42
125
|
|
|
43
126
|
export const reservedMethodNames = new Set([
|
|
44
127
|
"constructor",
|
|
@@ -71,6 +154,14 @@ export type ReservedMethodName =
|
|
|
71
154
|
|
|
72
155
|
export type ServiceFreeSchema = S.Codec<any, any, never, never>;
|
|
73
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Workers RPC structured-clones every value that crosses an isolate boundary
|
|
159
|
+
* and rejects class instances. Declaration schemas such as `Schema.Result`
|
|
160
|
+
* keep their container instance in their `Encoded` form, so the declared
|
|
161
|
+
* schemas are lowered to their canonical JSON codec before touching the wire.
|
|
162
|
+
*/
|
|
163
|
+
const wireCodec = <Schema extends S.Constraint>(schema: Schema) => S.toCodecJson(schema);
|
|
164
|
+
|
|
74
165
|
export interface Method<
|
|
75
166
|
Args extends ReadonlyArray<ServiceFreeSchema> = ReadonlyArray<ServiceFreeSchema>,
|
|
76
167
|
Success extends ServiceFreeSchema = ServiceFreeSchema,
|
|
@@ -91,15 +182,10 @@ export namespace Method {
|
|
|
91
182
|
? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
|
|
92
183
|
: Array<S.Schema.Type<Args[number]>>;
|
|
93
184
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
infer Head extends ServiceFreeSchema,
|
|
99
|
-
...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
|
|
100
|
-
]
|
|
101
|
-
? [S.Codec.Encoded<Head>, ...EncodedArgsFromSchemas<Tail>]
|
|
102
|
-
: Array<S.Codec.Encoded<Args[number]>>;
|
|
185
|
+
/** Method schemas cross the wire through their canonical JSON codec. */
|
|
186
|
+
type EncodedArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = {
|
|
187
|
+
[Index in keyof Args]: S.Json;
|
|
188
|
+
};
|
|
103
189
|
|
|
104
190
|
export type Args<Self extends Any> = ArgsFromSchemas<Self["args"]>;
|
|
105
191
|
|
|
@@ -107,7 +193,7 @@ export namespace Method {
|
|
|
107
193
|
|
|
108
194
|
export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
|
|
109
195
|
|
|
110
|
-
export type EncodedSuccess<Self extends Any> = S.
|
|
196
|
+
export type EncodedSuccess<Self extends Any> = S.Json;
|
|
111
197
|
}
|
|
112
198
|
|
|
113
199
|
export type Methods = Record<string, Method.Any>;
|
|
@@ -139,18 +225,6 @@ export namespace Definition {
|
|
|
139
225
|
export type MethodNames<Self extends Any> = Extract<keyof Self["methods"], string>;
|
|
140
226
|
}
|
|
141
227
|
|
|
142
|
-
export class ReservedMethodNameError extends Error {
|
|
143
|
-
readonly method: string;
|
|
144
|
-
readonly target: string;
|
|
145
|
-
|
|
146
|
-
constructor(target: string, method: string) {
|
|
147
|
-
super(`${target} RPC method "${method}" is reserved by Cloudflare Workers RPC`);
|
|
148
|
-
this.name = "ReservedMethodNameError";
|
|
149
|
-
this.target = target;
|
|
150
|
-
this.method = method;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
228
|
export const assertNoReservedMethods = (
|
|
155
229
|
target: string,
|
|
156
230
|
methods: Record<string, unknown>,
|
|
@@ -158,7 +232,7 @@ export const assertNoReservedMethods = (
|
|
|
158
232
|
) => {
|
|
159
233
|
for (const method of Object.keys(methods)) {
|
|
160
234
|
if (reserved.has(method)) {
|
|
161
|
-
throw new
|
|
235
|
+
throw new RpcReservedMethodNameError({ definition: target, method });
|
|
162
236
|
}
|
|
163
237
|
}
|
|
164
238
|
};
|
|
@@ -180,110 +254,87 @@ export function method(definition: {
|
|
|
180
254
|
};
|
|
181
255
|
}
|
|
182
256
|
|
|
183
|
-
export const
|
|
184
|
-
definition: Definition.Any,
|
|
185
|
-
): Effect.Effect<void, RpcReservedMethodNameError> =>
|
|
186
|
-
Effect.forEach(Object.keys(definition.methods), (method) =>
|
|
187
|
-
reservedMethodNames.has(method)
|
|
188
|
-
? Effect.fail(new RpcReservedMethodNameError({ definition: definition.id, method }))
|
|
189
|
-
: Effect.void,
|
|
190
|
-
).pipe(Effect.asVoid);
|
|
191
|
-
|
|
192
|
-
export const decodeArgs = <
|
|
257
|
+
export const decodeArgs = Effect.fnUntraced(function* <
|
|
193
258
|
const Self extends Definition.Any,
|
|
194
259
|
MethodName extends Definition.MethodNames<Self>,
|
|
195
260
|
>(
|
|
196
261
|
definition: Self,
|
|
197
262
|
methodName: MethodName,
|
|
198
263
|
args: ReadonlyArray<unknown>,
|
|
199
|
-
): Effect.
|
|
264
|
+
): Effect.fn.Return<
|
|
200
265
|
Method.Args<Self["methods"][MethodName]>,
|
|
201
266
|
RpcArgumentCountError | RpcArgumentDecodeError
|
|
202
|
-
>
|
|
203
|
-
|
|
204
|
-
|
|
267
|
+
> {
|
|
268
|
+
const methodDefinition = definition.methods[methodName];
|
|
269
|
+
|
|
270
|
+
if (args.length !== methodDefinition.args.length) {
|
|
271
|
+
return yield* new RpcArgumentCountError({
|
|
272
|
+
definition: definition.id,
|
|
273
|
+
method: methodName,
|
|
274
|
+
expected: methodDefinition.args.length,
|
|
275
|
+
actual: args.length,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
205
278
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
279
|
+
const decoded = yield* (
|
|
280
|
+
S.decodeUnknownEffect(wireCodec(S.Tuple(methodDefinition.args)))(args) as Effect.Effect<
|
|
281
|
+
unknown,
|
|
282
|
+
unknown
|
|
283
|
+
>
|
|
284
|
+
).pipe(
|
|
285
|
+
Effect.mapError(
|
|
286
|
+
(cause) =>
|
|
287
|
+
new RpcArgumentDecodeError({
|
|
209
288
|
definition: definition.id,
|
|
210
289
|
method: methodName,
|
|
211
|
-
|
|
212
|
-
actual: args.length,
|
|
290
|
+
cause,
|
|
213
291
|
}),
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const decoded: Array<unknown> = [];
|
|
218
|
-
|
|
219
|
-
for (let index = 0; index < methodDefinition.args.length; index++) {
|
|
220
|
-
const schema = methodDefinition.args[index];
|
|
221
|
-
|
|
222
|
-
decoded.push(
|
|
223
|
-
yield* (S.decodeUnknownEffect(schema)(args[index]) as Effect.Effect<unknown, unknown>).pipe(
|
|
224
|
-
Effect.mapError(
|
|
225
|
-
(cause) =>
|
|
226
|
-
new RpcArgumentDecodeError({
|
|
227
|
-
definition: definition.id,
|
|
228
|
-
method: methodName,
|
|
229
|
-
index,
|
|
230
|
-
cause,
|
|
231
|
-
}),
|
|
232
|
-
),
|
|
233
|
-
),
|
|
234
|
-
);
|
|
235
|
-
}
|
|
292
|
+
),
|
|
293
|
+
);
|
|
236
294
|
|
|
237
|
-
|
|
238
|
-
|
|
295
|
+
return decoded as Method.Args<Self["methods"][MethodName]>;
|
|
296
|
+
});
|
|
239
297
|
|
|
240
|
-
export const encodeArgs = <
|
|
298
|
+
export const encodeArgs = Effect.fnUntraced(function* <
|
|
241
299
|
const Self extends Definition.Any,
|
|
242
300
|
MethodName extends Definition.MethodNames<Self>,
|
|
243
301
|
>(
|
|
244
302
|
definition: Self,
|
|
245
303
|
methodName: MethodName,
|
|
246
304
|
args: Method.Args<Self["methods"][MethodName]>,
|
|
247
|
-
): Effect.
|
|
305
|
+
): Effect.fn.Return<
|
|
248
306
|
Method.EncodedArgs<Self["methods"][MethodName]>,
|
|
249
307
|
RpcArgumentCountError | RpcArgumentEncodeError
|
|
250
|
-
>
|
|
251
|
-
|
|
252
|
-
|
|
308
|
+
> {
|
|
309
|
+
const methodDefinition = definition.methods[methodName];
|
|
310
|
+
|
|
311
|
+
if (args.length !== methodDefinition.args.length) {
|
|
312
|
+
return yield* new RpcArgumentCountError({
|
|
313
|
+
definition: definition.id,
|
|
314
|
+
method: methodName,
|
|
315
|
+
expected: methodDefinition.args.length,
|
|
316
|
+
actual: args.length,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
253
319
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
320
|
+
const encoded = yield* (
|
|
321
|
+
S.encodeUnknownEffect(wireCodec(S.Tuple(methodDefinition.args)))(args) as Effect.Effect<
|
|
322
|
+
unknown,
|
|
323
|
+
unknown
|
|
324
|
+
>
|
|
325
|
+
).pipe(
|
|
326
|
+
Effect.mapError(
|
|
327
|
+
(cause) =>
|
|
328
|
+
new RpcArgumentEncodeError({
|
|
257
329
|
definition: definition.id,
|
|
258
330
|
method: methodName,
|
|
259
|
-
|
|
260
|
-
actual: args.length,
|
|
331
|
+
cause,
|
|
261
332
|
}),
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const encoded: Array<unknown> = [];
|
|
266
|
-
|
|
267
|
-
for (let index = 0; index < methodDefinition.args.length; index++) {
|
|
268
|
-
const schema = methodDefinition.args[index];
|
|
269
|
-
|
|
270
|
-
encoded.push(
|
|
271
|
-
yield* (S.encodeEffect(schema)(args[index]) as Effect.Effect<unknown, unknown>).pipe(
|
|
272
|
-
Effect.mapError(
|
|
273
|
-
(cause) =>
|
|
274
|
-
new RpcArgumentEncodeError({
|
|
275
|
-
definition: definition.id,
|
|
276
|
-
method: methodName,
|
|
277
|
-
index,
|
|
278
|
-
cause,
|
|
279
|
-
}),
|
|
280
|
-
),
|
|
281
|
-
),
|
|
282
|
-
);
|
|
283
|
-
}
|
|
333
|
+
),
|
|
334
|
+
);
|
|
284
335
|
|
|
285
|
-
|
|
286
|
-
|
|
336
|
+
return encoded as Method.EncodedArgs<Self["methods"][MethodName]>;
|
|
337
|
+
});
|
|
287
338
|
|
|
288
339
|
export const encodeSuccess = <
|
|
289
340
|
const Self extends Definition.Any,
|
|
@@ -296,7 +347,7 @@ export const encodeSuccess = <
|
|
|
296
347
|
const methodDefinition = definition.methods[methodName];
|
|
297
348
|
|
|
298
349
|
return (
|
|
299
|
-
S.
|
|
350
|
+
S.encodeUnknownEffect(wireCodec(methodDefinition.success))(value) as Effect.Effect<
|
|
300
351
|
Method.EncodedSuccess<Self["methods"][MethodName]>,
|
|
301
352
|
unknown
|
|
302
353
|
>
|
|
@@ -323,7 +374,7 @@ export const decodeSuccess = <
|
|
|
323
374
|
const methodDefinition = definition.methods[methodName];
|
|
324
375
|
|
|
325
376
|
return (
|
|
326
|
-
S.decodeUnknownEffect(methodDefinition.success)(value) as Effect.Effect<
|
|
377
|
+
S.decodeUnknownEffect(wireCodec(methodDefinition.success))(value) as Effect.Effect<
|
|
327
378
|
Method.Success<Self["methods"][MethodName]>,
|
|
328
379
|
unknown
|
|
329
380
|
>
|
|
@@ -343,14 +394,7 @@ export const make = <Id extends string, const MethodsShape extends Methods>(
|
|
|
343
394
|
id: Id,
|
|
344
395
|
methods: MethodsShape,
|
|
345
396
|
): Definition<Id, MethodsShape> => {
|
|
346
|
-
|
|
347
|
-
const reserved = Object.keys(definition.methods).find((methodName) =>
|
|
348
|
-
reservedMethodNames.has(methodName),
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
if (reserved !== undefined) {
|
|
352
|
-
throw new RpcReservedMethodNameError({ definition: id, method: reserved });
|
|
353
|
-
}
|
|
397
|
+
assertNoReservedMethods(id, methods, reservedMethodNames);
|
|
354
398
|
|
|
355
|
-
return
|
|
399
|
+
return { id, methods } as Definition<Id, MethodsShape>;
|
|
356
400
|
};
|