1/**
2 * A Fly.io app that exercises the full module-scope graph:
3 *
4 * - `Site` — parent App (`src/shared.ts`)
5 * - `Marker` — App Secret the Api reads via `Fly.GetSecret`
6 * - `PublicIp` — shared IPv4 so `{app}.fly.dev` answers
7 * - `Api` — HTTP Service on port 3000 (`src/api.ts`)
8 * - `Worker` — background Service that writes a volume marker (`src/worker.ts`)
9 *
10 * N Services share one App; each Service is its own Machine. Disks are
11 * `Fly.MountVolume({ path, sizeGb })` inside the Service — one Volume
12 * per replica, not a standalone Volume resource.
13 */
14import * as Alchemy from "alchemy";
15import * as Fly from "alchemy/Fly";
16import * as Effect from "effect/Effect";
17import Api from "./src/api.ts";
18import { Marker, PublicIp, Site } from "./src/shared.ts";
19import Worker from "./src/worker.ts";
20
21export default Alchemy.Stack(
22 "FlyService",
23 {
24 providers: Fly.providers(),
25 state: Alchemy.localState(),
26 },
27 Effect.gen(function* () {
28 const site = yield* Site;
29 const secret = yield* Marker;
30 const ip = yield* PublicIp;
31 const worker = yield* Worker;
32 const api = yield* Api;
33
34 return {
35 appName: site.appName,
36 appId: site.appId,
37 appUrl: site.url,
38 workerMounts: worker.mounts,
39 secretName: secret.name,
40 ip: ip.ip,
41 workerMachineId: worker.machineId,
42 workerName: worker.name,
43 workerState: worker.state,
44 apiMachineId: api.machineId,
45 apiName: api.name,
46 apiState: api.state,
47 apiUrl: api.url,
48 apiRegion: api.region,
49 };
50 }),
51);