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/src/ServiceBinding.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Data, Effect, type Scope } from "effect";
|
|
1
|
+
import { Context, Data, Effect, type Scope } from "effect";
|
|
2
2
|
|
|
3
3
|
import * as Binding from "./Binding";
|
|
4
4
|
import * as CloudflareRpc from "./Rpc";
|
|
@@ -74,6 +74,10 @@ type ServiceMethodSuccess<Api, Method extends keyof Api> = RpcInvocation.AsyncMe
|
|
|
74
74
|
Api,
|
|
75
75
|
Method
|
|
76
76
|
>;
|
|
77
|
+
type ServiceMethodCloudflareReturn<
|
|
78
|
+
Api,
|
|
79
|
+
Method extends keyof Api,
|
|
80
|
+
> = RpcInvocation.AsyncMethodCloudflareReturn<Api, Method>;
|
|
77
81
|
|
|
78
82
|
type ServiceCall<R, Api> = <Method extends ServiceMethodKey<Api>>(
|
|
79
83
|
method: Method,
|
|
@@ -98,31 +102,274 @@ export type ServiceBindingEffectClient<
|
|
|
98
102
|
Api extends object,
|
|
99
103
|
Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
|
|
100
104
|
> = DirectMethods<never, Definition> & {
|
|
105
|
+
/**
|
|
106
|
+
* Forwards an HTTP request to the bound Worker service.
|
|
107
|
+
*
|
|
108
|
+
* Use this when the service binding is acting as an HTTP origin rather than a
|
|
109
|
+
* Cloudflare RPC target.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* import { Effect } from "effect";
|
|
114
|
+
*
|
|
115
|
+
* const program = Effect.gen(function* () {
|
|
116
|
+
* const api = yield* ApiWorker;
|
|
117
|
+
* return yield* api.fetch(new Request("https://internal.example/users"));
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
101
121
|
readonly fetch: (
|
|
102
122
|
input: RequestInfo | URL,
|
|
103
123
|
init?: RequestInit,
|
|
104
124
|
) => Effect.Effect<globalThis.Response, ServiceBindingFetchError>;
|
|
125
|
+
/**
|
|
126
|
+
* Invokes a Worker RPC method and returns Cloudflare's raw RPC result.
|
|
127
|
+
*
|
|
128
|
+
* This preserves Cloudflare RPC behavior such as promise-like pipelining and
|
|
129
|
+
* transferable / disposable result objects. It does not resolve the returned
|
|
130
|
+
* promise-like value and it does not decode definition-backed success schemas.
|
|
131
|
+
*
|
|
132
|
+
* Most application code should use {@link call} instead.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { Effect } from "effect";
|
|
137
|
+
*
|
|
138
|
+
* const program = Effect.gen(function* () {
|
|
139
|
+
* const counter = yield* CounterService;
|
|
140
|
+
*
|
|
141
|
+
* const result = yield* counter.rpc("increment", 41);
|
|
142
|
+
* const value = yield* Effect.promise(() => result);
|
|
143
|
+
*
|
|
144
|
+
* return value;
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
105
148
|
readonly rpc: <Method extends ServiceMethodKey<Api>>(
|
|
106
149
|
method: Method,
|
|
107
150
|
...args: ServiceMethodArgs<Api, Method>
|
|
108
|
-
) => Effect.Effect<
|
|
151
|
+
) => Effect.Effect<ServiceMethodCloudflareReturn<Api, Method>, ServiceBindingRpcError>;
|
|
152
|
+
/**
|
|
153
|
+
* Invokes a Worker RPC method, resolves Cloudflare's RPC result, and decodes
|
|
154
|
+
* the success value when the binding was created from a definition.
|
|
155
|
+
*
|
|
156
|
+
* This is the normal choice when application code wants the final typed value.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* import { Effect } from "effect";
|
|
161
|
+
*
|
|
162
|
+
* const program = Effect.gen(function* () {
|
|
163
|
+
* const counter = yield* CounterService;
|
|
164
|
+
* const value = yield* counter.call("increment", 41);
|
|
165
|
+
*
|
|
166
|
+
* return value;
|
|
167
|
+
* });
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
109
170
|
readonly call: <Method extends ServiceMethodKey<Api>>(
|
|
110
171
|
method: Method,
|
|
111
172
|
...args: ServiceMethodArgs<Api, Method>
|
|
112
173
|
) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError>;
|
|
174
|
+
/**
|
|
175
|
+
* Invokes a Worker RPC method in the current `Scope`, resolves Cloudflare's RPC
|
|
176
|
+
* result, decodes definition-backed success values, and disposes the resolved
|
|
177
|
+
* result when the scope closes if it implements `Symbol.dispose`.
|
|
178
|
+
*
|
|
179
|
+
* Use this for RPC methods that return Cloudflare RPC resources or other
|
|
180
|
+
* disposable objects whose lifetime should be tied to an Effect scope.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* import { Effect } from "effect";
|
|
185
|
+
*
|
|
186
|
+
* const program = Effect.scoped(
|
|
187
|
+
* Effect.gen(function* () {
|
|
188
|
+
* const files = yield* FileService;
|
|
189
|
+
* const handle = yield* files.scopedCall("open", "report.csv");
|
|
190
|
+
*
|
|
191
|
+
* return yield* handle.read();
|
|
192
|
+
* }),
|
|
193
|
+
* );
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
113
196
|
readonly scopedCall: <Method extends ServiceMethodKey<Api>>(
|
|
114
197
|
method: Method,
|
|
115
198
|
...args: ServiceMethodArgs<Api, Method>
|
|
116
199
|
) => Effect.Effect<Awaited<ServiceMethodSuccess<Api, Method>>, unknown, Scope.Scope>;
|
|
117
200
|
};
|
|
118
201
|
|
|
202
|
+
export type ServiceBindingStaticClient<
|
|
203
|
+
R,
|
|
204
|
+
Api extends object,
|
|
205
|
+
Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
|
|
206
|
+
> = DirectMethods<R, Definition> & {
|
|
207
|
+
readonly fetch: (
|
|
208
|
+
input: RequestInfo | URL,
|
|
209
|
+
init?: RequestInit,
|
|
210
|
+
) => Effect.Effect<globalThis.Response, ServiceBindingFetchError, R>;
|
|
211
|
+
readonly rpc: <Method extends ServiceMethodKey<Api>>(
|
|
212
|
+
method: Method,
|
|
213
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
214
|
+
) => Effect.Effect<ServiceMethodCloudflareReturn<Api, Method>, ServiceBindingRpcError, R>;
|
|
215
|
+
readonly call: <Method extends ServiceMethodKey<Api>>(
|
|
216
|
+
method: Method,
|
|
217
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
218
|
+
) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError, R>;
|
|
219
|
+
readonly scopedCall: <Method extends ServiceMethodKey<Api>>(
|
|
220
|
+
method: Method,
|
|
221
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
222
|
+
) => Effect.Effect<Awaited<ServiceMethodSuccess<Api, Method>>, unknown, Scope.Scope | R>;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const isServiceBindingClient = <Api extends object>(
|
|
226
|
+
value: unknown,
|
|
227
|
+
): value is ServiceBindingClient<Api> =>
|
|
228
|
+
typeof value === "object" && value !== null && typeof Reflect.get(value, "fetch") === "function";
|
|
229
|
+
|
|
230
|
+
export const makeClient = <
|
|
231
|
+
Api extends object,
|
|
232
|
+
const Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
|
|
233
|
+
>(
|
|
234
|
+
definition: ServiceBindingDefinition<Definition>,
|
|
235
|
+
): ((service: ServiceBindingClient<Api>) => ServiceBindingEffectClient<Api, Definition>) => {
|
|
236
|
+
return (service: ServiceBindingClient<Api>) => {
|
|
237
|
+
const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
|
|
238
|
+
Effect.tryPromise({
|
|
239
|
+
try: () => service.fetch(input, init),
|
|
240
|
+
catch: (cause) => new ServiceBindingFetchError({ binding: definition.binding, cause }),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const rpc = <Method extends ServiceMethodKey<Api>>(
|
|
244
|
+
method: Method,
|
|
245
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
246
|
+
) =>
|
|
247
|
+
Effect.gen(function* () {
|
|
248
|
+
const methodName = String(method);
|
|
249
|
+
const encodedArgs =
|
|
250
|
+
definition.definition === undefined
|
|
251
|
+
? args
|
|
252
|
+
: yield* RpcDefinition.encodeArgs(
|
|
253
|
+
definition.definition,
|
|
254
|
+
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
255
|
+
args as never,
|
|
256
|
+
).pipe(
|
|
257
|
+
Effect.mapError(
|
|
258
|
+
(cause) =>
|
|
259
|
+
new ServiceBindingRpcError({
|
|
260
|
+
binding: definition.binding,
|
|
261
|
+
method: methodName,
|
|
262
|
+
cause,
|
|
263
|
+
}),
|
|
264
|
+
),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
return yield* RpcInvocation.invokeRpcMethod(
|
|
268
|
+
service,
|
|
269
|
+
method,
|
|
270
|
+
encodedArgs as ServiceMethodArgs<Api, Method>,
|
|
271
|
+
(cause) =>
|
|
272
|
+
new ServiceBindingRpcError({
|
|
273
|
+
binding: definition.binding,
|
|
274
|
+
method: methodName,
|
|
275
|
+
cause,
|
|
276
|
+
}),
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const decodeSuccess = <Method extends ServiceMethodKey<Api>>(
|
|
281
|
+
methodName: string,
|
|
282
|
+
value: Awaited<ServiceMethodCloudflareReturn<Api, Method>>,
|
|
283
|
+
) =>
|
|
284
|
+
Effect.gen(function* () {
|
|
285
|
+
if (definition.definition === undefined) {
|
|
286
|
+
return value as ServiceMethodSuccess<Api, Method>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const decoded = yield* RpcDefinition.decodeSuccess(
|
|
290
|
+
definition.definition,
|
|
291
|
+
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
292
|
+
value,
|
|
293
|
+
).pipe(
|
|
294
|
+
Effect.mapError(
|
|
295
|
+
(cause) =>
|
|
296
|
+
new ServiceBindingRpcError({
|
|
297
|
+
binding: definition.binding,
|
|
298
|
+
method: methodName,
|
|
299
|
+
cause,
|
|
300
|
+
}),
|
|
301
|
+
),
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
return decoded as ServiceMethodSuccess<Api, Method>;
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const call = <Method extends ServiceMethodKey<Api>>(
|
|
308
|
+
method: Method,
|
|
309
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
310
|
+
) =>
|
|
311
|
+
Effect.gen(function* () {
|
|
312
|
+
const methodName = String(method);
|
|
313
|
+
const value = yield* CloudflareRpc.resolve(yield* rpc(method, ...args)).pipe(
|
|
314
|
+
Effect.mapError(
|
|
315
|
+
(cause) =>
|
|
316
|
+
new ServiceBindingRpcError({
|
|
317
|
+
binding: definition.binding,
|
|
318
|
+
method: methodName,
|
|
319
|
+
cause,
|
|
320
|
+
}),
|
|
321
|
+
),
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
return yield* decodeSuccess<Method>(methodName, value);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const scopedCall = <Method extends ServiceMethodKey<Api>>(
|
|
328
|
+
method: Method,
|
|
329
|
+
...args: ServiceMethodArgs<Api, Method>
|
|
330
|
+
) =>
|
|
331
|
+
Effect.gen(function* () {
|
|
332
|
+
const methodName = String(method);
|
|
333
|
+
const result = yield* rpc(method, ...args);
|
|
334
|
+
const value = yield* CloudflareRpc.scoped(result);
|
|
335
|
+
return yield* decodeSuccess<Method>(methodName, value);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const directMethods = makeDirectMethods<never, Api, Definition>(definition.definition, call);
|
|
339
|
+
|
|
340
|
+
return Object.assign(directMethods, {
|
|
341
|
+
fetch,
|
|
342
|
+
rpc,
|
|
343
|
+
call,
|
|
344
|
+
scopedCall,
|
|
345
|
+
}) as ServiceBindingEffectClient<Api, Definition>;
|
|
346
|
+
};
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export const layer = <
|
|
350
|
+
Self,
|
|
351
|
+
Api extends object,
|
|
352
|
+
const Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
|
|
353
|
+
>(
|
|
354
|
+
tag: Context.Service<Self, ServiceBindingEffectClient<Api, Definition>>,
|
|
355
|
+
definition: ServiceBindingDefinition<Definition>,
|
|
356
|
+
) =>
|
|
357
|
+
Binding.layer(
|
|
358
|
+
tag,
|
|
359
|
+
definition.binding,
|
|
360
|
+
(value): value is ServiceBindingClient<Api> => isServiceBindingClient<Api>(value),
|
|
361
|
+
makeClient<Api, Definition>(definition),
|
|
362
|
+
);
|
|
363
|
+
|
|
119
364
|
/**
|
|
120
365
|
* Creates a typed Effect service for a Worker service binding.
|
|
121
366
|
*
|
|
122
367
|
* Returned value includes:
|
|
123
368
|
* - a Context tag for dependency injection
|
|
124
369
|
* - `fetch(...)` for raw HTTP forwarding
|
|
125
|
-
* - `
|
|
370
|
+
* - `rpc(...)` for raw Cloudflare RPC results
|
|
371
|
+
* - `call(...)` for resolved and decoded RPC results
|
|
372
|
+
* - `scopedCall(...)` for scoped and decoded disposable RPC results
|
|
126
373
|
* - direct RPC methods when `definition` is provided
|
|
127
374
|
*
|
|
128
375
|
* @example
|
|
@@ -134,10 +381,11 @@ export type ServiceBindingEffectClient<
|
|
|
134
381
|
* }),
|
|
135
382
|
* });
|
|
136
383
|
*
|
|
137
|
-
* const
|
|
384
|
+
* const CounterLive = Counter.layer({ binding: "COUNTER" });
|
|
138
385
|
*
|
|
139
386
|
* const program = Effect.gen(function* () {
|
|
140
|
-
* const
|
|
387
|
+
* const counter = yield* Counter;
|
|
388
|
+
* const next = yield* counter.increment(1);
|
|
141
389
|
* return next;
|
|
142
390
|
* });
|
|
143
391
|
* ```
|
|
@@ -153,119 +401,11 @@ export const Service =
|
|
|
153
401
|
) => {
|
|
154
402
|
type ServiceApi = ApiOrDefinition<Api, Definition>;
|
|
155
403
|
|
|
156
|
-
const makeClient = (
|
|
157
|
-
service: ServiceBindingClient<ServiceApi>,
|
|
158
|
-
): ServiceBindingEffectClient<ServiceApi, Definition> => {
|
|
159
|
-
const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
|
|
160
|
-
Effect.tryPromise({
|
|
161
|
-
try: () => service.fetch(input, init),
|
|
162
|
-
catch: (cause) => new ServiceBindingFetchError({ binding: definition.binding, cause }),
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const rpc = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
166
|
-
method: Method,
|
|
167
|
-
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
168
|
-
) =>
|
|
169
|
-
Effect.gen(function* () {
|
|
170
|
-
const methodName = String(method);
|
|
171
|
-
const encodedArgs =
|
|
172
|
-
definition.definition === undefined
|
|
173
|
-
? args
|
|
174
|
-
: yield* RpcDefinition.encodeArgs(
|
|
175
|
-
definition.definition,
|
|
176
|
-
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
177
|
-
args as never,
|
|
178
|
-
).pipe(
|
|
179
|
-
Effect.mapError(
|
|
180
|
-
(cause) =>
|
|
181
|
-
new ServiceBindingRpcError({
|
|
182
|
-
binding: definition.binding,
|
|
183
|
-
method: methodName,
|
|
184
|
-
cause,
|
|
185
|
-
}),
|
|
186
|
-
),
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
return yield* RpcInvocation.invokeRpcMethod(
|
|
190
|
-
service,
|
|
191
|
-
method,
|
|
192
|
-
encodedArgs as ServiceMethodArgs<ServiceApi, Method>,
|
|
193
|
-
(cause) =>
|
|
194
|
-
new ServiceBindingRpcError({
|
|
195
|
-
binding: definition.binding,
|
|
196
|
-
method: methodName,
|
|
197
|
-
cause,
|
|
198
|
-
}),
|
|
199
|
-
);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
const call = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
203
|
-
method: Method,
|
|
204
|
-
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
205
|
-
) =>
|
|
206
|
-
Effect.gen(function* () {
|
|
207
|
-
const methodName = String(method);
|
|
208
|
-
const value = yield* CloudflareRpc.resolve(yield* rpc(method, ...args)).pipe(
|
|
209
|
-
Effect.mapError(
|
|
210
|
-
(cause) =>
|
|
211
|
-
new ServiceBindingRpcError({
|
|
212
|
-
binding: definition.binding,
|
|
213
|
-
method: methodName,
|
|
214
|
-
cause,
|
|
215
|
-
}),
|
|
216
|
-
),
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
if (definition.definition === undefined) {
|
|
220
|
-
return value as ServiceMethodSuccess<ServiceApi, Method>;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const decoded = yield* RpcDefinition.decodeSuccess(
|
|
224
|
-
definition.definition,
|
|
225
|
-
methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
|
|
226
|
-
value,
|
|
227
|
-
).pipe(
|
|
228
|
-
Effect.mapError(
|
|
229
|
-
(cause) =>
|
|
230
|
-
new ServiceBindingRpcError({
|
|
231
|
-
binding: definition.binding,
|
|
232
|
-
method: methodName,
|
|
233
|
-
cause,
|
|
234
|
-
}),
|
|
235
|
-
),
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
return decoded as ServiceMethodSuccess<ServiceApi, Method>;
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const scopedCall = <Method extends ServiceMethodKey<ServiceApi>>(
|
|
242
|
-
method: Method,
|
|
243
|
-
...args: ServiceMethodArgs<ServiceApi, Method>
|
|
244
|
-
) =>
|
|
245
|
-
Effect.gen(function* () {
|
|
246
|
-
const result = yield* rpc(method, ...args);
|
|
247
|
-
return yield* CloudflareRpc.scoped(result);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
const directMethods = makeDirectMethods<never, ServiceApi, Definition>(
|
|
251
|
-
definition.definition,
|
|
252
|
-
call,
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
return Object.assign(directMethods, {
|
|
256
|
-
fetch,
|
|
257
|
-
rpc,
|
|
258
|
-
call,
|
|
259
|
-
scopedCall,
|
|
260
|
-
}) as ServiceBindingEffectClient<ServiceApi, Definition>;
|
|
261
|
-
};
|
|
262
|
-
|
|
263
404
|
const tag = Binding.Service<Self>()(
|
|
264
405
|
id,
|
|
265
406
|
definition.binding,
|
|
266
|
-
(value): value is ServiceBindingClient<ServiceApi> =>
|
|
267
|
-
|
|
268
|
-
makeClient,
|
|
407
|
+
(value): value is ServiceBindingClient<ServiceApi> => isServiceBindingClient(value),
|
|
408
|
+
makeClient<ServiceApi, Definition>(definition),
|
|
269
409
|
);
|
|
270
410
|
|
|
271
411
|
const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
|
|
@@ -324,7 +464,11 @@ export const Service =
|
|
|
324
464
|
};
|
|
325
465
|
};
|
|
326
466
|
|
|
327
|
-
const makeDirectMethods = <
|
|
467
|
+
export const makeDirectMethods = <
|
|
468
|
+
R,
|
|
469
|
+
Api,
|
|
470
|
+
Definition extends WorkerDefinition.Definition.Any | undefined,
|
|
471
|
+
>(
|
|
328
472
|
rpcDefinition: Definition | undefined,
|
|
329
473
|
call: ServiceCall<R, Api>,
|
|
330
474
|
): DirectMethods<R, Definition> => {
|
package/src/Worker.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
|
|
2
2
|
import { Cause, ConfigProvider, Context, Effect, Layer, ManagedRuntime, type Scope } from "effect";
|
|
3
|
+
import type { Schema as S } from "effect";
|
|
3
4
|
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
4
5
|
|
|
6
|
+
import type * as Binding from "./Binding";
|
|
5
7
|
import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
6
8
|
import { fromMessage, fromMessageBatch, type QueueHandler } from "./Queue";
|
|
9
|
+
import type * as Rpc from "./Rpc";
|
|
7
10
|
import type * as RpcDefinition from "./RpcDefinition";
|
|
11
|
+
import type * as ServiceBinding from "./ServiceBinding";
|
|
8
12
|
import * as WorkerDefinition from "./WorkerDefinition";
|
|
9
13
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
10
14
|
import { fromExecutionContext, type RunWaitUntilEffect } from "./internal/WorkerContext";
|
|
@@ -282,38 +286,128 @@ export const makeFetchHandler = <ROut, LayerError, Env extends WorkerEnv = Worke
|
|
|
282
286
|
};
|
|
283
287
|
};
|
|
284
288
|
|
|
285
|
-
export
|
|
289
|
+
export type ServiceFreeSchema = S.Codec<any, any, never, never>;
|
|
286
290
|
|
|
287
|
-
export
|
|
291
|
+
export interface Method<
|
|
292
|
+
Args extends ReadonlyArray<ServiceFreeSchema> = ReadonlyArray<ServiceFreeSchema>,
|
|
293
|
+
Success extends ServiceFreeSchema = ServiceFreeSchema,
|
|
294
|
+
> {
|
|
295
|
+
readonly args: Args;
|
|
296
|
+
readonly success: Success;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export namespace Method {
|
|
300
|
+
export type Any = Method<ReadonlyArray<ServiceFreeSchema>, ServiceFreeSchema>;
|
|
301
|
+
|
|
302
|
+
type ArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = Args extends readonly []
|
|
303
|
+
? []
|
|
304
|
+
: Args extends readonly [
|
|
305
|
+
infer Head extends ServiceFreeSchema,
|
|
306
|
+
...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
|
|
307
|
+
]
|
|
308
|
+
? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
|
|
309
|
+
: Array<S.Schema.Type<Args[number]>>;
|
|
288
310
|
|
|
289
|
-
export
|
|
311
|
+
export type Args<Self extends Any> = ArgsFromSchemas<Self["args"]>;
|
|
312
|
+
|
|
313
|
+
export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
|
|
314
|
+
}
|
|
290
315
|
|
|
291
|
-
export type
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
>
|
|
316
|
+
export type Methods = Record<string, Method.Any>;
|
|
317
|
+
|
|
318
|
+
export type NoReservedMethods<MethodsShape extends Methods> =
|
|
319
|
+
Extract<keyof MethodsShape, ReservedMethodName> extends never ? MethodsShape : never;
|
|
320
|
+
|
|
321
|
+
export interface Definition<Id extends string = string, MethodsShape extends Methods = Methods> {
|
|
322
|
+
readonly id: Id;
|
|
323
|
+
readonly methods: MethodsShape;
|
|
324
|
+
}
|
|
295
325
|
|
|
296
326
|
export namespace Definition {
|
|
297
|
-
export type Any =
|
|
327
|
+
export type Any = Definition<string, Methods>;
|
|
298
328
|
}
|
|
299
329
|
|
|
300
|
-
export type ServerApi<Self extends
|
|
301
|
-
|
|
330
|
+
export type ServerApi<Self extends Definition.Any> = {
|
|
331
|
+
readonly [Key in keyof Self["methods"]]: (
|
|
332
|
+
...args: Method.Args<Self["methods"][Key]>
|
|
333
|
+
) => Promise<Method.Success<Self["methods"][Key]>>;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
export type Api<Self extends Definition.Any> = Rpc.Provider<ServerApi<Self>, ReservedMethodName>;
|
|
302
337
|
|
|
303
|
-
export type
|
|
338
|
+
export type Handlers<ROut, Self extends Definition.Any> = {
|
|
339
|
+
readonly [Key in keyof Self["methods"]]: (
|
|
340
|
+
...args: Method.Args<Self["methods"][Key]>
|
|
341
|
+
) => WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
|
|
342
|
+
};
|
|
304
343
|
|
|
305
|
-
export
|
|
306
|
-
ROut,
|
|
307
|
-
|
|
308
|
-
>
|
|
344
|
+
export interface Options<ROut, Self extends Definition.Any> extends Omit<
|
|
345
|
+
WorkerOptions<ROut, Handlers<ROut, Self>>,
|
|
346
|
+
"rpc"
|
|
347
|
+
> {
|
|
348
|
+
readonly rpc: Handlers<ROut, Self>;
|
|
349
|
+
}
|
|
309
350
|
|
|
310
|
-
export type
|
|
351
|
+
export type LayerOptions = WorkerDefinition.LayerOptions;
|
|
352
|
+
|
|
353
|
+
export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Context.ServiceClass<
|
|
354
|
+
Self,
|
|
355
|
+
Id,
|
|
356
|
+
ServiceBinding.ServiceBindingEffectClient<
|
|
357
|
+
Api<Definition<Id, MethodsShape>>,
|
|
358
|
+
Definition<Id, MethodsShape>
|
|
359
|
+
>
|
|
360
|
+
> &
|
|
361
|
+
ServiceBinding.ServiceBindingStaticClient<
|
|
362
|
+
Self,
|
|
363
|
+
Api<Definition<Id, MethodsShape>>,
|
|
364
|
+
Definition<Id, MethodsShape>
|
|
365
|
+
> & {
|
|
366
|
+
readonly id: Id;
|
|
367
|
+
readonly methods: MethodsShape;
|
|
368
|
+
readonly make: <ROut, LayerError>(
|
|
369
|
+
layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
|
|
370
|
+
options: Options<ROut, Definition<Id, MethodsShape>>,
|
|
371
|
+
) => WorkerClass<Handlers<ROut, Definition<Id, MethodsShape>>, ROut>;
|
|
372
|
+
readonly layer: (
|
|
373
|
+
options: LayerOptions,
|
|
374
|
+
) => Layer.Layer<
|
|
375
|
+
Self,
|
|
376
|
+
Binding.BindingNotFoundError | Binding.BindingValidationError,
|
|
377
|
+
WorkerEnvironment
|
|
378
|
+
>;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
export type TagFactory = <Self>() => <Id extends string, const MethodsShape extends Methods>(
|
|
382
|
+
id: Id,
|
|
383
|
+
methods: MethodsShape & NoReservedMethods<MethodsShape>,
|
|
384
|
+
) => TagClass<Self, Id, MethodsShape>;
|
|
385
|
+
|
|
386
|
+
export const Tag = WorkerDefinition.Tag as unknown as TagFactory;
|
|
387
|
+
|
|
388
|
+
export const method = WorkerDefinition.method as {
|
|
389
|
+
<Success extends ServiceFreeSchema>(definition: {
|
|
390
|
+
readonly success: Success;
|
|
391
|
+
}): Method<readonly [], Success>;
|
|
392
|
+
<
|
|
393
|
+
const Args extends ReadonlyArray<ServiceFreeSchema>,
|
|
394
|
+
Success extends ServiceFreeSchema,
|
|
395
|
+
>(definition: {
|
|
396
|
+
readonly args: Args;
|
|
397
|
+
readonly success: Success;
|
|
398
|
+
}): Method<Args, Success>;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
export const implement = WorkerDefinition.implement as unknown as <
|
|
311
402
|
ROut,
|
|
312
|
-
Self
|
|
313
|
-
|
|
403
|
+
const Self extends Definition.Any,
|
|
404
|
+
>(
|
|
405
|
+
_definition: Self,
|
|
406
|
+
handlers: Handlers<ROut, Self>,
|
|
407
|
+
) => Handlers<ROut, Self>;
|
|
314
408
|
|
|
315
409
|
export type HandlerEffect<
|
|
316
410
|
ROut,
|
|
317
|
-
Self extends
|
|
411
|
+
Self extends Definition.Any,
|
|
318
412
|
Key extends keyof Self["methods"],
|
|
319
|
-
> =
|
|
413
|
+
> = WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
|