1import * as Alchemy from "alchemy";
2import * as Cloudflare from "alchemy/Cloudflare";
3import * as GitHub from "alchemy/GitHub";
4import * as Output from "alchemy/Output";
5import * as RemovalPolicy from "alchemy/RemovalPolicy";
6import * as Effect from "effect/Effect";
7import * as Layer from "effect/Layer";
8
9export type WorkerEnv = Cloudflare.InferEnv<typeof Website>;
10
11const Website = Cloudflare.Website.StaticSite(
12 "Website",
13 Effect.gen(function* () {
14 const stack = yield* Alchemy.Stack;
15 const previewParent = stack.stage.startsWith("pr-")
16 ? yield* Cloudflare.Worker.ref("Website", { stage: "preview-base" })
17 : undefined;
18 const name =
19 stack.stage === "preview-base"
20 ? "alchemy-website-preview"
21 : stack.stage === "main"
22 ? "alchemy-website-main"
23 : stack.stage === "prod"
24 ? "alchemy-website-prod"
25 : undefined;
26
27 return {
28 name,
29 command: "bun run build",
30 main: "./src/worker.ts",
31 outdir: "dist",
32 preview: previewParent
33 ? {
34 of: previewParent,
35 name: stack.stage,
36 message: process.env.PULL_REQUEST
37 ? `PR #${process.env.PULL_REQUEST}`
38 : undefined,
39 }
40 : undefined,
41 workersDev: stack.stage === "prod" ? false : undefined,
42 domain:
43 stack.stage === "prod"
44 ? {
45 name: "alchemy.run",
46 redirects: ["v2.alchemy.run"],
47 previews: true,
48 }
49 : stack.stage === "main"
50 ? { name: "main.alchemy.run" }
51 : undefined,
52 memo: {
53 include: [
54 "src/**",
55 "astro.config.mjs",
56 "package.json",
57 "plugins/**",
58 "public/**",
59 "scripts/**",
60 "../bun.lock",
61 ],
62 },
63 compatibility: {
64 date: "2026-04-02",
65 flags: ["nodejs_compat"],
66 },
67 assets: {
68 runWorkerFirst: true,
69 },
70 } satisfies Cloudflare.Website.StaticSiteProps<{}>;
71 }),
72).pipe(
73 RemovalPolicy.retain(
74 Alchemy.Stack.pipe(Effect.map(({ stage }) => !stage.startsWith("pr-"))),
75 ),
76);
77
78export default Alchemy.Stack(
79 "AlchemyEffectWebsite",
80 {
81 providers: Layer.mergeAll(Cloudflare.providers(), GitHub.providers()),
82 state: Cloudflare.state(),
83 },
84 Effect.gen(function* () {
85 const { stage } = yield* Alchemy.Stack;
86 const website = yield* Website;
87
88 if (stage.startsWith("pr-")) {
89 yield* GitHub.Comment("preview-comment", {
90 owner: "alchemy-run",
91 repository: "alchemy",
92 issueNumber: Number(process.env.PULL_REQUEST),
93 body: Output.interpolate`
94 ## Website Preview Deployed
95
96 **URL:** ${website.url}
97
98 Built from commit ${
99 // `BUILD_SHA` is set by .github/workflows/deploy.yml to the
100 // PR head SHA (or `github.sha` for push deploys). The
101 // ambient `GITHUB_SHA` would point at the synthetic merge
102 // commit on `pull_request` events, which is not what
103 // anyone wants to see in the comment.
104 process.env.BUILD_SHA
105 ? `[\`${process.env.BUILD_SHA.slice(0, 7)}\`](https://github.com/alchemy-run/alchemy/commit/${process.env.BUILD_SHA})`
106 : "unknown"
107 }.
108
109 ---
110 _This comment updates automatically with each push._
111 `,
112 });
113 }
114
115 return {
116 url: website.url,
117 };
118 }),
119);