alchemy 2.0.0-beta.40 → 2.0.0-beta.41
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/bin/exec.js +16 -3
- package/bin/exec.js.map +1 -1
- package/lib/AWS/EC2/hosted.js +1 -1
- package/lib/AWS/EC2/hosted.js.map +1 -1
- package/lib/AWS/ECS/Task.js +1 -1
- package/lib/AWS/ECS/Task.js.map +1 -1
- package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/Function.js +1 -1
- package/lib/AWS/Lambda/Function.js.map +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
- package/lib/Apply.d.ts.map +1 -1
- package/lib/Apply.js +41 -7
- package/lib/Apply.js.map +1 -1
- package/lib/Cloudflare/Bridge.d.ts +1 -0
- package/lib/Cloudflare/Bridge.d.ts.map +1 -1
- package/lib/Cloudflare/Bridge.js +1 -0
- package/lib/Cloudflare/Bridge.js.map +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.js +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.js.map +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.d.ts +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.js +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.js.map +1 -1
- package/lib/Cloudflare/Fetcher.d.ts +9 -0
- package/lib/Cloudflare/Fetcher.d.ts.map +1 -1
- package/lib/Cloudflare/Fetcher.js +20 -1
- package/lib/Cloudflare/Fetcher.js.map +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +2 -2
- package/lib/Cloudflare/Website/StaticSite.d.ts +5 -4
- package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
- package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
- package/lib/Cloudflare/Website/Vite.d.ts +4 -4
- package/lib/Cloudflare/Website/Vite.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Vite.js.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts +17 -0
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/DurableObjectBridge.js +86 -0
- package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -0
- package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts +2 -1
- package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectNamespace.js +10 -39
- package/lib/Cloudflare/Workers/DurableObjectNamespace.js.map +1 -1
- package/lib/Cloudflare/Workers/HttpServer.d.ts +2 -8
- package/lib/Cloudflare/Workers/HttpServer.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/HttpServer.js +29 -33
- package/lib/Cloudflare/Workers/HttpServer.js.map +1 -1
- package/lib/Cloudflare/Workers/InferEnv.d.ts +5 -24
- package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.d.ts +0 -101
- package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.js +9 -286
- package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
- package/lib/Cloudflare/Workers/RpcAsync.d.ts +8 -0
- package/lib/Cloudflare/Workers/RpcAsync.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/RpcAsync.js +45 -0
- package/lib/Cloudflare/Workers/RpcAsync.js.map +1 -0
- package/lib/Cloudflare/Workers/Worker.d.ts +16 -162
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +49 -354
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts +8 -0
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +129 -0
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts +154 -0
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBinding.js +51 -0
- package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts +35 -0
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBridge.js +149 -0
- package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js +18 -84
- package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts +8 -0
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.js +113 -0
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.d.ts +26 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.js +41 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.js.map +1 -0
- package/lib/Cloudflare/Workers/index.d.ts +4 -0
- package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/index.js +4 -0
- package/lib/Cloudflare/Workers/index.js.map +1 -1
- package/lib/Cloudflare/index.d.ts +4 -2
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +4 -2
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Neon/Project.d.ts.map +1 -1
- package/lib/Neon/Project.js +2 -1
- package/lib/Neon/Project.js.map +1 -1
- package/lib/Platform.d.ts +3 -4
- package/lib/Platform.d.ts.map +1 -1
- package/lib/Platform.js.map +1 -1
- package/lib/Rpc.d.ts +17 -0
- package/lib/Rpc.d.ts.map +1 -0
- package/lib/Rpc.js +1 -0
- package/lib/Rpc.js.map +1 -0
- package/lib/RuntimeContext.d.ts +1 -0
- package/lib/RuntimeContext.d.ts.map +1 -1
- package/lib/RuntimeContext.js.map +1 -1
- package/lib/Sidecar/RpcHandler.d.ts.map +1 -1
- package/lib/Sidecar/RpcHandler.js +13 -9
- package/lib/Sidecar/RpcHandler.js.map +1 -1
- package/lib/State/HttpStateApi.d.ts +10 -0
- package/lib/State/HttpStateApi.d.ts.map +1 -1
- package/lib/State/HttpStateApi.js +10 -0
- package/lib/State/HttpStateApi.js.map +1 -1
- package/lib/State/HttpStateStore.d.ts.map +1 -1
- package/lib/State/HttpStateStore.js +2 -1
- package/lib/State/HttpStateStore.js.map +1 -1
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +2 -0
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +2 -0
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/State.d.ts +8 -0
- package/lib/State/State.d.ts.map +1 -1
- package/lib/State/StateEncoding.d.ts.map +1 -1
- package/lib/State/StateEncoding.js +5 -1
- package/lib/State/StateEncoding.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Apply.ts +44 -10
- package/src/Cli/Event.ts +1 -1
- package/src/Cloudflare/Bridge.ts +1 -0
- package/src/Cloudflare/Container/ContainerBinding.ts +1 -1
- package/src/Cloudflare/Fetcher.ts +38 -1
- package/src/Cloudflare/Local/SidecarHandlers.ts +3 -6
- package/src/Cloudflare/Website/StaticSite.ts +10 -5
- package/src/Cloudflare/Website/Vite.ts +7 -4
- package/src/Cloudflare/Workers/DurableObjectBridge.ts +180 -0
- package/src/Cloudflare/Workers/DurableObjectNamespace.ts +22 -70
- package/src/Cloudflare/Workers/HttpServer.ts +25 -39
- package/src/Cloudflare/Workers/InferEnv.ts +42 -78
- package/src/Cloudflare/Workers/Rpc.ts +18 -417
- package/src/Cloudflare/Workers/RpcAsync.ts +63 -0
- package/src/Cloudflare/Workers/Worker.ts +107 -548
- package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +149 -0
- package/src/Cloudflare/Workers/WorkerBinding.ts +103 -0
- package/src/Cloudflare/Workers/WorkerBridge.ts +304 -0
- package/src/Cloudflare/Workers/WorkerBundle.ts +17 -83
- package/src/Cloudflare/Workers/WorkerRuntimeContext.ts +164 -0
- package/src/Cloudflare/Workers/WorkflowBridge.ts +92 -0
- package/src/Cloudflare/Workers/index.ts +4 -0
- package/src/Cloudflare/index.ts +4 -2
- package/src/Neon/Project.ts +3 -1
- package/src/Platform.ts +3 -4
- package/src/Rpc.ts +25 -0
- package/src/RuntimeContext.ts +1 -0
- package/src/Sidecar/RpcHandler.ts +18 -9
- package/src/State/HttpStateApi.ts +11 -0
- package/src/State/HttpStateStore.ts +7 -2
- package/src/State/InMemoryState.ts +2 -0
- package/src/State/LocalState.ts +2 -0
- package/src/State/State.ts +8 -0
- package/src/State/StateEncoding.ts +8 -1
- package/src/index.ts +7 -6
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type * as cf from "@cloudflare/workers-types";
|
|
2
2
|
import * as Cause from "effect/Cause";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
4
5
|
import * as Option from "effect/Option";
|
|
5
6
|
import type { Scope } from "effect/Scope";
|
|
6
|
-
import type { HttpBodyError } from "effect/unstable/http/HttpBody";
|
|
7
|
-
import * as HttpServerError from "effect/unstable/http/HttpServerError";
|
|
8
7
|
import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
|
|
9
8
|
import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
|
|
10
9
|
import * as Http from "../../Http.ts";
|
|
@@ -13,45 +12,31 @@ import { isWorkerEvent, type WorkerServices } from "./Worker.ts";
|
|
|
13
12
|
|
|
14
13
|
export type HttpEffect = Http.HttpEffect<WorkerServices>;
|
|
15
14
|
|
|
16
|
-
export const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
remoteAddress: webRequest.headers.get("cf-connecting-ip") ?? undefined,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
};
|
|
15
|
+
export const makeRequestHandler =
|
|
16
|
+
<Req = never>(
|
|
17
|
+
handler: Http.HttpEffect<Req> | Effect.Effect<Http.HttpEffect<Req>>,
|
|
18
|
+
) =>
|
|
19
|
+
(event: any) =>
|
|
20
|
+
isWorkerEvent(event) && event.type === "fetch"
|
|
21
|
+
? makeRequestEffect(event.input, handler)
|
|
22
|
+
: undefined;
|
|
29
23
|
|
|
30
|
-
export const
|
|
24
|
+
export const makeRequestEffect = <Req = never>(
|
|
31
25
|
webRequest: cf.Request,
|
|
32
|
-
handler: Effect.Effect<
|
|
33
|
-
HttpServerResponse.HttpServerResponse,
|
|
34
|
-
HttpServerError.HttpServerError | HttpBodyError,
|
|
35
|
-
Req
|
|
36
|
-
>,
|
|
37
|
-
options: {
|
|
38
|
-
// Preserve transport metadata when this helper is adapting a request
|
|
39
|
-
// that originated from another runtime surface.
|
|
40
|
-
remoteAddress?: string;
|
|
41
|
-
// Durable Objects need to register the accepted socket on object state
|
|
42
|
-
// instead of calling `server.accept()` directly.
|
|
43
|
-
acceptWebSocket?: (socket: cf.WebSocket) => void;
|
|
44
|
-
} = {},
|
|
26
|
+
handler: Http.HttpEffect<Req> | Effect.Effect<Http.HttpEffect<Req>>,
|
|
45
27
|
): Effect.Effect<
|
|
46
28
|
Response,
|
|
47
29
|
never,
|
|
48
30
|
Exclude<Req, HttpServerRequest.HttpServerRequest | Scope>
|
|
49
|
-
> =>
|
|
50
|
-
|
|
31
|
+
> => {
|
|
32
|
+
const safeHandler = Http.safeHttpEffect(handler);
|
|
33
|
+
return Effect.gen(function* () {
|
|
51
34
|
const request = HttpServerRequest.fromWeb(
|
|
52
35
|
webRequest as any as globalThis.Request,
|
|
53
36
|
).modify({
|
|
54
|
-
remoteAddress: Option.fromUndefinedOr(
|
|
37
|
+
remoteAddress: Option.fromUndefinedOr(
|
|
38
|
+
webRequest.headers.get("cf-connecting-ip") ?? undefined,
|
|
39
|
+
),
|
|
55
40
|
});
|
|
56
41
|
|
|
57
42
|
Object.defineProperty(request, "raw", {
|
|
@@ -61,13 +46,15 @@ export const serveWebRequest = <Req = never>(
|
|
|
61
46
|
}),
|
|
62
47
|
});
|
|
63
48
|
|
|
64
|
-
const response = yield*
|
|
65
|
-
Effect.
|
|
66
|
-
|
|
49
|
+
const response = yield* safeHandler.pipe(
|
|
50
|
+
Effect.provide([
|
|
51
|
+
Layer.succeed(HttpServerRequest.HttpServerRequest, request),
|
|
52
|
+
Layer.succeed(Request, webRequest as any),
|
|
53
|
+
]),
|
|
67
54
|
Effect.catchCause((cause) => {
|
|
68
55
|
const message = Option.match(Cause.findErrorOption(cause), {
|
|
69
56
|
onNone: () => "Internal Server Error",
|
|
70
|
-
onSome: (error) =>
|
|
57
|
+
onSome: (error: any) =>
|
|
71
58
|
error instanceof Error && error.message
|
|
72
59
|
? error.message
|
|
73
60
|
: "Internal Server Error",
|
|
@@ -81,7 +68,6 @@ export const serveWebRequest = <Req = never>(
|
|
|
81
68
|
}),
|
|
82
69
|
);
|
|
83
70
|
|
|
84
|
-
return HttpServerResponse.toWeb(response
|
|
85
|
-
context: yield* Effect.context(),
|
|
86
|
-
});
|
|
71
|
+
return HttpServerResponse.toWeb(response);
|
|
87
72
|
}) as any;
|
|
73
|
+
};
|
|
@@ -2,19 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
import type * as Effect from "effect/Effect";
|
|
4
4
|
import type * as Stream from "effect/Stream";
|
|
5
|
-
import type { Rpc } from "../../
|
|
6
|
-
import type { UnwrapEffect } from "../../Util/effect.ts";
|
|
5
|
+
import type { Rpc } from "../../Rpc.ts";
|
|
7
6
|
import type * as Cloudflare from "../index.ts";
|
|
8
7
|
import type { RpcErrorEnvelope, RpcStreamEnvelope } from "./Rpc.ts";
|
|
9
8
|
import type { Worker } from "./Worker.ts";
|
|
10
9
|
|
|
11
|
-
export type InferEnv<W> =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
export type InferEnv<W> =
|
|
11
|
+
W extends Effect.Effect<infer A, infer _E, infer _R>
|
|
12
|
+
? InferEnv<A>
|
|
13
|
+
: W extends Worker<any>
|
|
14
|
+
? InferEnv<Exclude<W["Props"]["bindings"], undefined>>
|
|
15
|
+
: {
|
|
16
|
+
[k in keyof W]: GetBindingType<W[k]>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type GetBindingType<T> =
|
|
20
|
+
T extends Effect.Effect<infer A, infer _E, infer _R>
|
|
21
|
+
? GetBindingType<A>
|
|
22
|
+
: T extends Cloudflare.Assets
|
|
23
|
+
? Service
|
|
24
|
+
: T extends Rpc<infer Shape extends object>
|
|
25
|
+
? RpcWireShape<Shape> & Service
|
|
26
|
+
: T extends Cloudflare.D1Database
|
|
27
|
+
? D1Database
|
|
28
|
+
: T extends Cloudflare.R2Bucket
|
|
29
|
+
? R2Bucket
|
|
30
|
+
: T extends Cloudflare.KVNamespace
|
|
31
|
+
? KVNamespace
|
|
32
|
+
: T extends Cloudflare.Queue
|
|
33
|
+
? Queue<unknown>
|
|
34
|
+
: T extends Cloudflare.AiGateway
|
|
35
|
+
? Ai
|
|
36
|
+
: T extends Cloudflare.SendEmail
|
|
37
|
+
? SendEmail
|
|
38
|
+
: T extends Cloudflare.AnalyticsEngineDataset
|
|
39
|
+
? AnalyticsEngineDataset
|
|
40
|
+
: T extends Cloudflare.Artifacts
|
|
41
|
+
? Artifacts
|
|
42
|
+
: T extends Cloudflare.Images
|
|
43
|
+
? ImagesBinding
|
|
44
|
+
: T extends Cloudflare.Hyperdrive
|
|
45
|
+
? Hyperdrive
|
|
46
|
+
: T extends Cloudflare.DurableObjectNamespaceLike
|
|
47
|
+
? DurableObjectNamespace<
|
|
48
|
+
Exclude<T["Shape"], undefined>
|
|
49
|
+
>
|
|
50
|
+
: T;
|
|
18
51
|
|
|
19
52
|
/**
|
|
20
53
|
* Cloudflare service-binding wire shape for an Effect-native Worker.
|
|
@@ -43,72 +76,3 @@ export type RpcWireShape<Shape> = {
|
|
|
43
76
|
? (...args: A) => Promise<Awaited<R>>
|
|
44
77
|
: Promise<Shape[K]>;
|
|
45
78
|
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Recover the user's RPC `Shape` from any of the forms a caller might pass
|
|
49
|
-
* to {@link toPromiseApi}:
|
|
50
|
-
*
|
|
51
|
-
* - the Worker class value's type, e.g. `typeof Backend`, which extends
|
|
52
|
-
* `Effect.Effect<Worker & Rpc<Shape>, …>`
|
|
53
|
-
* - the unwrapped `Worker & Rpc<Shape>` type
|
|
54
|
-
* - a bare `Shape` (when the caller types it explicitly)
|
|
55
|
-
*/
|
|
56
|
-
export type ExtractRpcShape<W> =
|
|
57
|
-
W extends Effect.Effect<infer R, any, any>
|
|
58
|
-
? R extends Rpc<infer Shape>
|
|
59
|
-
? Shape
|
|
60
|
-
: R
|
|
61
|
-
: W extends Rpc<infer Shape>
|
|
62
|
-
? Shape
|
|
63
|
-
: W;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Promise-flavored consumer view returned by {@link toPromiseApi}: error
|
|
67
|
-
* envelopes are thrown as exceptions and stream envelopes are unwrapped to
|
|
68
|
-
* their raw `ReadableStream<Uint8Array>` body.
|
|
69
|
-
*/
|
|
70
|
-
export type RpcPromiseShape<Shape> = {
|
|
71
|
-
[K in keyof Shape as K extends "fetch" ? never : K]: Shape[K] extends (
|
|
72
|
-
...args: infer A
|
|
73
|
-
) => Effect.Effect<infer T, any, any>
|
|
74
|
-
? (...args: A) => Promise<T>
|
|
75
|
-
: Shape[K] extends (...args: infer A) => Stream.Stream<any, any, any>
|
|
76
|
-
? (...args: A) => Promise<ReadableStream<Uint8Array>>
|
|
77
|
-
: Shape[K] extends Effect.Effect<infer T, any, any>
|
|
78
|
-
? Promise<T>
|
|
79
|
-
: Shape[K] extends Stream.Stream<any, any, any>
|
|
80
|
-
? Promise<ReadableStream<Uint8Array>>
|
|
81
|
-
: Shape[K] extends (...args: infer A) => infer R
|
|
82
|
-
? (...args: A) => Promise<Awaited<R>>
|
|
83
|
-
: Promise<Shape[K]>;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export type GetBindingType<T> = T extends Cloudflare.Assets
|
|
87
|
-
? Service
|
|
88
|
-
: T extends Rpc<infer Shape extends object>
|
|
89
|
-
? RpcWireShape<Shape> & Service
|
|
90
|
-
: T extends Cloudflare.D1Database
|
|
91
|
-
? D1Database
|
|
92
|
-
: T extends Cloudflare.R2Bucket
|
|
93
|
-
? R2Bucket
|
|
94
|
-
: T extends Cloudflare.KVNamespace
|
|
95
|
-
? KVNamespace
|
|
96
|
-
: T extends Cloudflare.Queue
|
|
97
|
-
? Queue<unknown>
|
|
98
|
-
: T extends Cloudflare.AiGateway
|
|
99
|
-
? Ai
|
|
100
|
-
: T extends Cloudflare.SendEmail
|
|
101
|
-
? SendEmail
|
|
102
|
-
: T extends Cloudflare.AnalyticsEngineDataset
|
|
103
|
-
? AnalyticsEngineDataset
|
|
104
|
-
: T extends Cloudflare.Artifacts
|
|
105
|
-
? Artifacts
|
|
106
|
-
: T extends Cloudflare.Images
|
|
107
|
-
? ImagesBinding
|
|
108
|
-
: T extends Cloudflare.Hyperdrive
|
|
109
|
-
? Hyperdrive
|
|
110
|
-
: T extends Cloudflare.DurableObjectNamespaceLike
|
|
111
|
-
? DurableObjectNamespace<
|
|
112
|
-
Exclude<T["Shape"], undefined>
|
|
113
|
-
>
|
|
114
|
-
: never;
|
|
@@ -7,12 +7,8 @@ import * as Option from "effect/Option";
|
|
|
7
7
|
import * as Sink from "effect/Sink";
|
|
8
8
|
import * as Stream from "effect/Stream";
|
|
9
9
|
import * as Socket from "effect/unstable/socket/Socket";
|
|
10
|
-
import type { HttpEffect } from "../../Http.ts";
|
|
11
10
|
import { isYieldableEffect } from "../../Util/effect.ts";
|
|
12
11
|
import { fromCloudflareFetcher } from "../Fetcher.ts";
|
|
13
|
-
import { serveWebRequest } from "./HttpServer.ts";
|
|
14
|
-
import type { ExtractRpcShape, RpcPromiseShape } from "./InferEnv.ts";
|
|
15
|
-
import { fromWebSocket } from "./WebSocket.ts";
|
|
16
12
|
|
|
17
13
|
export const StreamTag = "~alchemy/rpc/stream";
|
|
18
14
|
export const ErrorTag = "~alchemy/rpc/error";
|
|
@@ -231,419 +227,6 @@ export const makeRpcStub = <Shape>(
|
|
|
231
227
|
}) as Shape;
|
|
232
228
|
};
|
|
233
229
|
|
|
234
|
-
/**
|
|
235
|
-
* Wrap a raw Cloudflare service binding (the wire-typed view emitted by
|
|
236
|
-
* `InferEnv`) into a Promise-flavored API for non-Effect consumers.
|
|
237
|
-
*
|
|
238
|
-
* Effect-native worker methods serialize their results as envelopes:
|
|
239
|
-
* `Effect.fail` becomes `RpcErrorEnvelope`, `Stream` becomes
|
|
240
|
-
* `RpcStreamEnvelope`, and `Effect.succeed`/raw values pass through. This
|
|
241
|
-
* proxy auto-decodes those envelopes — error envelopes are thrown so the
|
|
242
|
-
* caller's `await` rejects, and stream envelopes are unwrapped to their raw
|
|
243
|
-
* `ReadableStream<Uint8Array>` body.
|
|
244
|
-
*
|
|
245
|
-
* `Service.fetch` and `Service.connect` pass through untouched, so the
|
|
246
|
-
* returned proxy is still a usable `Service` binding.
|
|
247
|
-
*
|
|
248
|
-
* For Effect-to-Effect cross-worker calls, prefer `bindWorker(Worker)` —
|
|
249
|
-
* it preserves typed errors and decoded streams.
|
|
250
|
-
*
|
|
251
|
-
* The generic parameter is the Worker *class* (or its Effect form), the
|
|
252
|
-
* same value you would pass to `bindWorker`. Shape is recovered from the
|
|
253
|
-
* embedded `Rpc<Shape>`.
|
|
254
|
-
*
|
|
255
|
-
* @example
|
|
256
|
-
* ```ts
|
|
257
|
-
* import * as Cloudflare from "alchemy/Cloudflare";
|
|
258
|
-
* import Backend from "./backend.ts";
|
|
259
|
-
*
|
|
260
|
-
* const backend = Cloudflare.toPromiseApi<typeof Backend>(env.BACKEND);
|
|
261
|
-
* const greeting = await backend.hello(key); // throws on Effect.fail
|
|
262
|
-
* ```
|
|
263
|
-
*/
|
|
264
|
-
export const toPromiseApi = <W>(
|
|
265
|
-
stub: any,
|
|
266
|
-
): RpcPromiseShape<ExtractRpcShape<W>> & Service =>
|
|
267
|
-
new Proxy(stub, {
|
|
268
|
-
get: (target, prop) => {
|
|
269
|
-
// `Service` methods (fetch/connect) and any non-string keys (Symbol.dispose, etc.)
|
|
270
|
-
// pass through to the underlying Cloudflare binding unchanged.
|
|
271
|
-
if (typeof prop !== "string" || prop === "fetch" || prop === "connect") {
|
|
272
|
-
const value = (target as any)[prop];
|
|
273
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return async (...args: unknown[]) => {
|
|
277
|
-
const result = await (target as any)[prop](...args);
|
|
278
|
-
if (isRpcErrorEnvelope(result)) {
|
|
279
|
-
throw decodeRpcThrowable(result.error);
|
|
280
|
-
}
|
|
281
|
-
if (isRpcStreamEnvelope(result)) {
|
|
282
|
-
return result.body;
|
|
283
|
-
}
|
|
284
|
-
return result;
|
|
285
|
-
};
|
|
286
|
-
},
|
|
287
|
-
}) as any;
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Reconstruct a throwable from {@link encodeRpcError}'s wire form. Plain
|
|
291
|
-
* `Error` payloads are rebuilt as `Error` instances so `.message`/`.name`/
|
|
292
|
-
* `.stack` survive. Tagged errors (anything with a `_tag`) and primitives
|
|
293
|
-
* are thrown as-is so `try { ... } catch (e) { if (e._tag === ...) }` keeps
|
|
294
|
-
* working.
|
|
295
|
-
*/
|
|
296
|
-
const decodeRpcThrowable = (error: unknown): unknown => {
|
|
297
|
-
if (error === null || typeof error !== "object") return error;
|
|
298
|
-
const obj = error as Record<string, unknown>;
|
|
299
|
-
if ("_tag" in obj) return obj;
|
|
300
|
-
if (typeof obj.message === "string" || typeof obj.name === "string") {
|
|
301
|
-
const e = new Error(typeof obj.message === "string" ? obj.message : "");
|
|
302
|
-
if (typeof obj.name === "string") e.name = obj.name;
|
|
303
|
-
if (typeof obj.stack === "string") e.stack = obj.stack;
|
|
304
|
-
return e;
|
|
305
|
-
}
|
|
306
|
-
return error;
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Create a DurableObjectBridge class that proxies RPC method calls through
|
|
311
|
-
* the Effect runtime, encoding success/fail/stream results as RPC envelopes.
|
|
312
|
-
*
|
|
313
|
-
* Accepts the `DurableObject` base class and a `getExport` resolver so the
|
|
314
|
-
* implementation lives in real TypeScript instead of a generated string template.
|
|
315
|
-
*/
|
|
316
|
-
export const makeDurableObjectBridge =
|
|
317
|
-
(
|
|
318
|
-
DurableObject: abstract new (
|
|
319
|
-
state: unknown,
|
|
320
|
-
env: unknown,
|
|
321
|
-
) => cf.DurableObject,
|
|
322
|
-
getExport: (
|
|
323
|
-
name: string,
|
|
324
|
-
) => Promise<
|
|
325
|
-
(state: unknown, env: unknown) => Effect.Effect<Record<string, unknown>>
|
|
326
|
-
>,
|
|
327
|
-
) =>
|
|
328
|
-
(className: string) =>
|
|
329
|
-
class DurableObjectBridge extends DurableObject {
|
|
330
|
-
readonly object: Promise<DurableObjectShape>;
|
|
331
|
-
|
|
332
|
-
async fetch(request: cf.Request): Promise<cf.Response> {
|
|
333
|
-
const methods = await this.object;
|
|
334
|
-
if (methods.fetch) {
|
|
335
|
-
const fetch = methods.fetch as HttpEffect<never>;
|
|
336
|
-
const response = await serveWebRequest(request, fetch).pipe(
|
|
337
|
-
Effect.runPromise,
|
|
338
|
-
);
|
|
339
|
-
return response as any;
|
|
340
|
-
} else {
|
|
341
|
-
return new Response("Method not found", { status: 404 }) as any;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
async alarm(alarmInfo?: cf.AlarmInvocationInfo) {
|
|
346
|
-
const methods = await this.object;
|
|
347
|
-
if (methods.alarm) {
|
|
348
|
-
await Effect.runPromise(methods.alarm(alarmInfo));
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
async webSocketMessage(ws: cf.WebSocket, message: string | ArrayBuffer) {
|
|
353
|
-
const methods = await this.object;
|
|
354
|
-
if (methods.webSocketMessage) {
|
|
355
|
-
const socket = fromWebSocket(ws);
|
|
356
|
-
const value = methods.webSocketMessage(socket, message);
|
|
357
|
-
if (Effect.isEffect(value)) {
|
|
358
|
-
await Effect.runPromise(value as Effect.Effect<void>);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
async webSocketClose(
|
|
364
|
-
ws: cf.WebSocket,
|
|
365
|
-
code: number,
|
|
366
|
-
reason: string,
|
|
367
|
-
wasClean: boolean,
|
|
368
|
-
) {
|
|
369
|
-
const methods = await this.object;
|
|
370
|
-
if (methods.webSocketClose) {
|
|
371
|
-
const socket = fromWebSocket(ws);
|
|
372
|
-
const value = methods.webSocketClose(socket, code, reason, wasClean);
|
|
373
|
-
if (Effect.isEffect(value)) {
|
|
374
|
-
await Effect.runPromise(value as Effect.Effect<void>);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
constructor(
|
|
380
|
-
state: {
|
|
381
|
-
blockConcurrencyWhile: (
|
|
382
|
-
fn: () => Promise<unknown>,
|
|
383
|
-
) => Promise<unknown>;
|
|
384
|
-
},
|
|
385
|
-
env: unknown,
|
|
386
|
-
) {
|
|
387
|
-
super(state, env);
|
|
388
|
-
|
|
389
|
-
this.object = state.blockConcurrencyWhile(async () => {
|
|
390
|
-
const makeDurableObject = await getExport(className);
|
|
391
|
-
return await Effect.runPromise(makeDurableObject(state, env));
|
|
392
|
-
}) as Promise<any>;
|
|
393
|
-
|
|
394
|
-
return new Proxy(this, {
|
|
395
|
-
get: (target: any, prop) =>
|
|
396
|
-
prop in target
|
|
397
|
-
? target[prop]
|
|
398
|
-
: async (...args: unknown[]) => {
|
|
399
|
-
const methods = await this.object;
|
|
400
|
-
const method = methods[
|
|
401
|
-
prop as keyof DurableObjectShape
|
|
402
|
-
] as any; // TODO(sam): what should the type be? Is it safe to always call it?
|
|
403
|
-
const value = method(...args);
|
|
404
|
-
if (Effect.isEffect(value)) {
|
|
405
|
-
const exit = await Effect.runPromiseExit(
|
|
406
|
-
value as Effect.Effect<unknown, never>,
|
|
407
|
-
);
|
|
408
|
-
if (exit._tag === "Success") {
|
|
409
|
-
if (Stream.isStream(exit.value)) {
|
|
410
|
-
return await Effect.runPromise(
|
|
411
|
-
toRpcStream(
|
|
412
|
-
exit.value,
|
|
413
|
-
) as Effect.Effect<RpcStreamEnvelope>,
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
return exit.value;
|
|
417
|
-
}
|
|
418
|
-
const failReason = exit.cause.reasons.find(
|
|
419
|
-
Cause.isFailReason,
|
|
420
|
-
);
|
|
421
|
-
if (failReason) {
|
|
422
|
-
return {
|
|
423
|
-
_tag: ErrorTag,
|
|
424
|
-
error: encodeRpcError(failReason.error),
|
|
425
|
-
} satisfies RpcErrorEnvelope;
|
|
426
|
-
}
|
|
427
|
-
const dieReason = exit.cause.reasons.find(
|
|
428
|
-
Cause.isDieReason,
|
|
429
|
-
);
|
|
430
|
-
throw (
|
|
431
|
-
dieReason?.defect ??
|
|
432
|
-
new Error("RPC method failed with an unexpected cause")
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
return value;
|
|
436
|
-
},
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Create a `WorkerBridge` class — a `WorkerEntrypoint` subclass that
|
|
443
|
-
* Cloudflare instantiates per request and dispatches both standard
|
|
444
|
-
* handlers (`fetch`/`scheduled`/…) and RPC method calls into.
|
|
445
|
-
*
|
|
446
|
-
* The bridge is intentionally layer-ignorant: all runtime-layer plumbing
|
|
447
|
-
* (services, scope, `WorkerExecutionContext`) lives in the platform's
|
|
448
|
-
* `runtimeContext.exports` flow, which builds:
|
|
449
|
-
*
|
|
450
|
-
* - `getDefault()` → `{ fetch, scheduled, … }` — handler dispatchers
|
|
451
|
-
* that the bridge forwards `(input, env, ctx)` to.
|
|
452
|
-
* - `getRpc()` → `{ method: (args, ctx) => Promise<envelope> }` —
|
|
453
|
-
* pre-wrapped RPC dispatchers that already run the user effect with
|
|
454
|
-
* the right runtime layer and envelope-encode the result.
|
|
455
|
-
*
|
|
456
|
-
* Cloudflare's script-validate step requires standard handler methods
|
|
457
|
-
* (`fetch`/`scheduled`/etc.) to be visible on the class prototype. We
|
|
458
|
-
* declare static stubs there and override them per-instance with
|
|
459
|
-
* closure-bound forwarders that capture `ctx`/`env` from the constructor
|
|
460
|
-
* (prototype-level `this` doesn't survive the proxy + RPC dispatch
|
|
461
|
-
* round-trip in some Cloudflare runtime versions).
|
|
462
|
-
*/
|
|
463
|
-
export const makeWorkerBridge = (
|
|
464
|
-
WorkerEntrypoint: abstract new (
|
|
465
|
-
ctx: unknown,
|
|
466
|
-
env: unknown,
|
|
467
|
-
) => { fetch?(request: cf.Request): Promise<cf.Response> | cf.Response },
|
|
468
|
-
ExportedHandlerMethods: ReadonlyArray<string>,
|
|
469
|
-
getDefault: () => Promise<Record<string, any>>,
|
|
470
|
-
getRpc: () => Promise<Record<string, any>>,
|
|
471
|
-
) => {
|
|
472
|
-
class WorkerBridge extends WorkerEntrypoint {
|
|
473
|
-
constructor(ctx: unknown, env: unknown) {
|
|
474
|
-
super(ctx, env);
|
|
475
|
-
|
|
476
|
-
// Override handler methods per-instance with closure-bound forwarders.
|
|
477
|
-
// We read `this.ctx`/`this.env` at *call time* rather than capturing
|
|
478
|
-
// the constructor args because Cloudflare's runtime appears to
|
|
479
|
-
// populate those instance fields differently depending on whether
|
|
480
|
-
// the entrypoint is invoked via direct fetch or RPC — `super(ctx, env)`
|
|
481
|
-
// alone isn't enough to guarantee they survive into the call.
|
|
482
|
-
const self = this as unknown as { ctx: unknown; env: unknown };
|
|
483
|
-
// Cloudflare invokes `WorkerEntrypoint.fetch(request)` with only the
|
|
484
|
-
// primary input on the *parameter list* — `env`/`ctx` live on `this`
|
|
485
|
-
// (and the runtime sometimes passes `(request, undefined, undefined)`
|
|
486
|
-
// anyway for legacy compat). Other handlers, including `scheduled`, may
|
|
487
|
-
// receive the classic `(input, env, ctx)` arguments. The platform's
|
|
488
|
-
// underlying handler dispatch (`handle("fetch")`, `handle("scheduled")`,
|
|
489
|
-
// etc.) follows the plain-export signature `(input, env, context)`, so
|
|
490
|
-
// prefer explicit handler args when Cloudflare provides them and fall
|
|
491
|
-
// back to the WorkerEntrypoint instance fields.
|
|
492
|
-
for (const method of ExportedHandlerMethods) {
|
|
493
|
-
(this as any)[method] = async (
|
|
494
|
-
input: unknown,
|
|
495
|
-
envArg?: unknown,
|
|
496
|
-
ctxArg?: unknown,
|
|
497
|
-
) => {
|
|
498
|
-
const def = await getDefault();
|
|
499
|
-
return def?.[method]?.(input, envArg ?? self.env, ctxArg ?? self.ctx);
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// RPC method dispatch — anything Cloudflare RPC tries to invoke that
|
|
504
|
-
// isn't an own property goes through the dispatcher table built by
|
|
505
|
-
// the platform's exports flow.
|
|
506
|
-
return new Proxy(this, {
|
|
507
|
-
get: (target, prop) => {
|
|
508
|
-
if (typeof prop !== "string") return (target as any)[prop];
|
|
509
|
-
if (prop in target) return (target as any)[prop];
|
|
510
|
-
return async (...args: unknown[]) => {
|
|
511
|
-
const rpc = await getRpc();
|
|
512
|
-
const dispatcher = rpc?.[prop];
|
|
513
|
-
if (typeof dispatcher !== "function") {
|
|
514
|
-
throw new Error(
|
|
515
|
-
`Method "${prop}" not found on worker. ` +
|
|
516
|
-
`Make sure it's returned from the worker's default export.`,
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
return dispatcher(args, self.ctx);
|
|
520
|
-
};
|
|
521
|
-
},
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Stub prototype methods so Cloudflare's script-validate detects the
|
|
527
|
-
// standard handler set; per-instance overrides above are what actually
|
|
528
|
-
// run.
|
|
529
|
-
for (const method of ExportedHandlerMethods) {
|
|
530
|
-
Object.defineProperty(WorkerBridge.prototype, method, {
|
|
531
|
-
value: function () {
|
|
532
|
-
throw new Error(
|
|
533
|
-
`Bridge method '${method}' was called before instance setup`,
|
|
534
|
-
);
|
|
535
|
-
},
|
|
536
|
-
writable: true,
|
|
537
|
-
configurable: true,
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return WorkerBridge;
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Create a WorkflowBridge class that extends `WorkflowEntrypoint` and
|
|
546
|
-
* delegates the `run(event, step)` call to the Effect-native workflow body
|
|
547
|
-
* registered via `worker.export(...)`.
|
|
548
|
-
*
|
|
549
|
-
* The bridge provides `WorkflowEvent` and `WorkflowStep` as Effect
|
|
550
|
-
* services so the user writes `yield* WorkflowEvent` and `yield* task(...)`
|
|
551
|
-
* instead of receiving callback parameters.
|
|
552
|
-
*/
|
|
553
|
-
export const makeWorkflowBridge =
|
|
554
|
-
(
|
|
555
|
-
WorkflowEntrypoint: abstract new (
|
|
556
|
-
ctx: unknown,
|
|
557
|
-
env: unknown,
|
|
558
|
-
) => { run(event: any, step: any): Promise<unknown> },
|
|
559
|
-
getExport: (
|
|
560
|
-
name: string,
|
|
561
|
-
) => Promise<
|
|
562
|
-
(
|
|
563
|
-
env: unknown,
|
|
564
|
-
) => Effect.Effect<(input: unknown) => Effect.Effect<unknown, never, any>>
|
|
565
|
-
>,
|
|
566
|
-
) =>
|
|
567
|
-
(className: string) =>
|
|
568
|
-
class WorkflowBridge extends WorkflowEntrypoint {
|
|
569
|
-
readonly fn: Promise<
|
|
570
|
-
(input: unknown) => Effect.Effect<unknown, never, any>
|
|
571
|
-
>;
|
|
572
|
-
readonly env: unknown;
|
|
573
|
-
|
|
574
|
-
constructor(ctx: unknown, env: unknown) {
|
|
575
|
-
super(ctx, env);
|
|
576
|
-
this.env = env;
|
|
577
|
-
this.fn = getExport(className).then((factory) =>
|
|
578
|
-
Effect.runPromise(factory(env)),
|
|
579
|
-
);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
async run(event: any, step: any): Promise<unknown> {
|
|
583
|
-
const fn = await this.fn;
|
|
584
|
-
return Effect.runPromise(
|
|
585
|
-
fn(event.payload).pipe(
|
|
586
|
-
Effect.provideService(
|
|
587
|
-
WorkflowEventService,
|
|
588
|
-
wrapWorkflowEvent(event),
|
|
589
|
-
),
|
|
590
|
-
Effect.provideService(WorkflowStep, wrapWorkflowStep(step)),
|
|
591
|
-
) as Effect.Effect<unknown>,
|
|
592
|
-
);
|
|
593
|
-
}
|
|
594
|
-
};
|
|
595
|
-
|
|
596
|
-
import type { DurableObjectShape } from "./DurableObjectNamespace.ts";
|
|
597
|
-
import {
|
|
598
|
-
WorkflowEvent as WorkflowEventService,
|
|
599
|
-
WorkflowStep,
|
|
600
|
-
} from "./Workflow.ts";
|
|
601
|
-
|
|
602
|
-
const wrapWorkflowEvent = (
|
|
603
|
-
event: any,
|
|
604
|
-
): { payload: unknown; timestamp: Date; instanceId: string } => ({
|
|
605
|
-
payload: event.payload,
|
|
606
|
-
timestamp:
|
|
607
|
-
event.timestamp instanceof Date
|
|
608
|
-
? event.timestamp
|
|
609
|
-
: new Date(event.timestamp),
|
|
610
|
-
instanceId: event.instanceId ?? "",
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
const wrapWorkflowStep = (step: any): WorkflowStep["Service"] => ({
|
|
614
|
-
do: <T>(name: string, effect: Effect.Effect<T>): Effect.Effect<T> =>
|
|
615
|
-
Effect.tryPromise(
|
|
616
|
-
() => step.do(name, () => Effect.runPromise(effect)) as Promise<T>,
|
|
617
|
-
),
|
|
618
|
-
sleep: (name: string, duration: string | number): Effect.Effect<void> =>
|
|
619
|
-
Effect.tryPromise(() => step.sleep(name, duration)),
|
|
620
|
-
sleepUntil: (name: string, timestamp: Date | number): Effect.Effect<void> =>
|
|
621
|
-
Effect.tryPromise(() =>
|
|
622
|
-
step.sleepUntil(
|
|
623
|
-
name,
|
|
624
|
-
timestamp instanceof Date ? timestamp.toISOString() : timestamp,
|
|
625
|
-
),
|
|
626
|
-
),
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
const encodeStreamErrorMarker = (cause: Cause.Cause<unknown>): string => {
|
|
630
|
-
const failReason = cause.reasons.find(Cause.isFailReason);
|
|
631
|
-
const error = failReason ? encodeRpcError(failReason.error) : undefined;
|
|
632
|
-
return (
|
|
633
|
-
JSON.stringify({
|
|
634
|
-
_tag: StreamErrorTag,
|
|
635
|
-
error,
|
|
636
|
-
} satisfies RpcStreamErrorMarker) + "\n"
|
|
637
|
-
);
|
|
638
|
-
};
|
|
639
|
-
|
|
640
|
-
const appendStreamErrors = (s: Stream.Stream<string, unknown>) =>
|
|
641
|
-
s.pipe(
|
|
642
|
-
Stream.catchCause((cause) =>
|
|
643
|
-
Stream.succeed(encodeStreamErrorMarker(cause)),
|
|
644
|
-
),
|
|
645
|
-
);
|
|
646
|
-
|
|
647
230
|
export const toRpcStream = (stream: Stream.Stream<any, any, any>) =>
|
|
648
231
|
Effect.scoped(
|
|
649
232
|
Effect.gen(function* () {
|
|
@@ -690,3 +273,21 @@ export const toRpcStream = (stream: Stream.Stream<any, any, any>) =>
|
|
|
690
273
|
return Effect.die(Cause.squash(cause));
|
|
691
274
|
}),
|
|
692
275
|
);
|
|
276
|
+
|
|
277
|
+
const encodeStreamErrorMarker = (cause: Cause.Cause<unknown>): string => {
|
|
278
|
+
const failReason = cause.reasons.find(Cause.isFailReason);
|
|
279
|
+
const error = failReason ? encodeRpcError(failReason.error) : undefined;
|
|
280
|
+
return (
|
|
281
|
+
JSON.stringify({
|
|
282
|
+
_tag: StreamErrorTag,
|
|
283
|
+
error,
|
|
284
|
+
} satisfies RpcStreamErrorMarker) + "\n"
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const appendStreamErrors = (s: Stream.Stream<string, unknown>) =>
|
|
289
|
+
s.pipe(
|
|
290
|
+
Stream.catchCause((cause) =>
|
|
291
|
+
Stream.succeed(encodeStreamErrorMarker(cause)),
|
|
292
|
+
),
|
|
293
|
+
);
|