1import { newWebSocketRpcSession } from "capnweb";
2import * as Effect from "effect/Effect";
3import * as FileSystem from "effect/FileSystem";
4import * as Layer from "effect/Layer";
5import * as FetchHttpClient from "effect/http/FetchHttpClient";
6import * as HttpBody from "effect/http/HttpBody";
7import * as HttpClient from "effect/http/HttpClient";
8import { unwrapRpcHandlers } from "alchemy/Local/RpcSerialization";
9import type { RpcProxyApi } from "alchemy/Local/RpcServer";
10import {
11 encodeSessionEnvironment,
12 SESSION_ENV_PARAM,
13} from "alchemy/Local/RpcServerEnvironment";
14import { layerServer, RpcSpawner } from "alchemy/Local/RpcSpawner";
15import { PlatformServices, runMain } from "alchemy/Util/PlatformServices";
16
17const program = Effect.gen(function* () {
18 const fs = yield* FileSystem.FileSystem;
19 const input = yield* Effect.sync(
20 () =>
21 JSON.parse(process.env.COMMAND_FIXTURES!) as {
22 home: string;
23 ready: string;
24 commands: Array<{ command: string; env: Record<string, string> }>;
25 },
26 );
27 const spawner = yield* RpcSpawner;
28 const http = yield* HttpClient.HttpClient;
29 const wsUrl = yield* http
30 .post(spawner.url, {
31 body: yield* HttpBody.json({
32 serverEntryUrl: new URL(
33 "../../../src/Local/Sidecar.ts",
34 import.meta.url,
35 ).href,
36 }),
37 })
38 .pipe(Effect.flatMap((response) => response.text));
39 const url = new URL(wsUrl);
40 url.searchParams.set(
41 SESSION_ENV_PARAM,
42 encodeSessionEnvironment({
43 alchemyContext: {
44 dotAlchemy: input.home,
45 updateStateStore: false,
46 dev: true,
47 adopt: false,
48 },
49 stack: { name: "CommandShutdown", stage: "test" },
50 }),
51 );
52 const session = yield* Effect.sync(() =>
53 newWebSocketRpcSession<RpcProxyApi>(url.toString()),
54 );
55 const wrapped = yield* Effect.promise(
56 () =>
57 session.getProvider(
58 "Command.Dev",
59 new URL("../../../src/Command/Local.ts", import.meta.url).href,
60 ) as ReturnType<RpcProxyApi["getProvider"]>,
61 );
62 const provider = unwrapRpcHandlers(wrapped, []);
63 yield* Effect.forEach(
64 input.commands,
65 (news, index) =>
66 provider.reconcile({
67 id: `Command${index}`,
68 fqn: `Command${index}`,
69 instanceId: `Command${index}`,
70 news,
71 olds: undefined,
72 output: undefined,
73 session: {} as never,
74 bindings: [],
75 }),
76 { concurrency: "unbounded" },
77 );
78 yield* fs.writeFileString(input.ready, "ready");
79 yield* Effect.never;
80});
81program.pipe(
82 Effect.provide(
83 Layer.mergeAll(
84 layerServer({ profile: undefined, envFile: undefined }).pipe(
85 Layer.provide(PlatformServices),
86 ),
87 PlatformServices,
88 FetchHttpClient.layer,
89 ),
90 ),
91 Effect.scoped,
92 runMain,
93);