2026-06-01 16:22:25 -05:00
|
|
|
import { Cause, Effect, Layer, Queue, Scope } from "effect";
|
|
|
|
|
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
|
|
|
import * as RpcServer from "effect/unstable/rpc/RpcServer";
|
|
|
|
|
import type * as Socket from "effect/unstable/socket/Socket";
|
|
|
|
|
import { errorMessage } from "@trustgraph/base";
|
|
|
|
|
import type { DispatcherManager } from "./dispatch/manager.js";
|
|
|
|
|
import { DispatchError, DispatchPayload, DispatchStreamChunk, TrustGraphRpcs } from "./rpc-contract.js";
|
|
|
|
|
import { makeSocketRpcProtocol } from "./rpc-protocol.js";
|
|
|
|
|
|
|
|
|
|
export interface GatewayRpcServer {
|
|
|
|
|
readonly onSocket: (
|
|
|
|
|
socket: Socket.Socket,
|
|
|
|
|
headers?: ReadonlyArray<[string, string]>,
|
|
|
|
|
) => Effect.Effect<void, never, Scope.Scope>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const makeGatewayRpcServer = Effect.fn("makeGatewayRpcServer")(function* (
|
|
|
|
|
dispatcher: DispatcherManager,
|
|
|
|
|
) {
|
|
|
|
|
const { onSocket, protocol } = yield* makeSocketRpcProtocol;
|
|
|
|
|
|
|
|
|
|
const serverLayer = RpcServer.layer(TrustGraphRpcs, {
|
|
|
|
|
disableFatalDefects: true,
|
|
|
|
|
}).pipe(
|
|
|
|
|
Layer.provide(Layer.succeed(RpcServer.Protocol, protocol)),
|
|
|
|
|
Layer.provide(makeGatewayRpcHandlers(dispatcher)),
|
|
|
|
|
Layer.provide(RpcSerialization.layerNdjson),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
yield* Layer.launch(serverLayer).pipe(Effect.forkScoped);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
onSocket: Effect.fn("GatewayRpc.onSocket")(function* (socket, headers) {
|
|
|
|
|
yield* onSocket(socket, headers);
|
|
|
|
|
}),
|
|
|
|
|
} satisfies GatewayRpcServer;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const makeGatewayRpcHandlers = (dispatcher: DispatcherManager) =>
|
|
|
|
|
TrustGraphRpcs.toLayer(Effect.succeed(
|
|
|
|
|
TrustGraphRpcs.of({
|
|
|
|
|
Dispatch: (payload) =>
|
|
|
|
|
Effect.tryPromise({
|
|
|
|
|
try: () => dispatchOne(dispatcher, payload),
|
2026-06-01 23:19:54 -05:00
|
|
|
catch: (cause) => DispatchError.make({ message: errorMessage(cause) }),
|
2026-06-01 16:22:25 -05:00
|
|
|
}),
|
|
|
|
|
DispatchStream: Effect.fn("GatewayRpc.DispatchStream")(function* (payload) {
|
|
|
|
|
const context = yield* Effect.context<never>();
|
|
|
|
|
const runPromise = Effect.runPromiseWith(context);
|
|
|
|
|
const queue = yield* Queue.bounded<DispatchStreamChunk, DispatchError | Cause.Done>(16);
|
|
|
|
|
yield* Effect.addFinalizer(() => Queue.shutdown(queue));
|
|
|
|
|
|
|
|
|
|
yield* Effect.tryPromise({
|
|
|
|
|
try: () =>
|
2026-06-01 23:19:54 -05:00
|
|
|
dispatchStream(dispatcher, payload, (response, complete) =>
|
|
|
|
|
runPromise(Queue.offer(queue, DispatchStreamChunk.make({ response, complete }))).then(() => complete),
|
|
|
|
|
),
|
|
|
|
|
catch: (cause) => DispatchError.make({ message: errorMessage(cause) }),
|
2026-06-01 16:22:25 -05:00
|
|
|
}).pipe(
|
|
|
|
|
Effect.flatMap(() => Queue.end(queue)),
|
|
|
|
|
Effect.catch((error) => Queue.fail(queue, error)),
|
|
|
|
|
Effect.forkScoped,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return queue;
|
|
|
|
|
}),
|
|
|
|
|
}),
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
function dispatchOne(
|
|
|
|
|
dispatcher: DispatcherManager,
|
|
|
|
|
payload: DispatchPayload,
|
|
|
|
|
): Promise<unknown> {
|
|
|
|
|
if (payload.scope === "flow") {
|
|
|
|
|
return dispatcher.dispatchFlowService(
|
|
|
|
|
payload.flow ?? "default",
|
|
|
|
|
payload.service,
|
|
|
|
|
payload.request,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return dispatcher.dispatchGlobalService(payload.service, payload.request);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-01 23:19:54 -05:00
|
|
|
function dispatchStream(
|
2026-06-01 16:22:25 -05:00
|
|
|
dispatcher: DispatcherManager,
|
|
|
|
|
payload: DispatchPayload,
|
|
|
|
|
responder: (response: unknown, complete: boolean) => Promise<boolean>,
|
|
|
|
|
): Promise<void> {
|
2026-06-01 23:19:54 -05:00
|
|
|
const send = (response: unknown, complete: boolean): Promise<void> =>
|
|
|
|
|
responder(response, complete).then(() => undefined);
|
2026-06-01 16:22:25 -05:00
|
|
|
|
|
|
|
|
if (payload.scope === "flow") {
|
2026-06-01 23:19:54 -05:00
|
|
|
return dispatcher.dispatchFlowServiceStreaming(
|
2026-06-01 16:22:25 -05:00
|
|
|
payload.flow ?? "default",
|
|
|
|
|
payload.service,
|
|
|
|
|
payload.request,
|
|
|
|
|
send,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-01 23:19:54 -05:00
|
|
|
return dispatcher.dispatchGlobalServiceStreaming(
|
2026-06-01 16:22:25 -05:00
|
|
|
payload.service,
|
|
|
|
|
payload.request,
|
|
|
|
|
send,
|
|
|
|
|
);
|
|
|
|
|
}
|