1import * as AWS from "@/AWS";
2import * as Test from "@/Test/Alchemy";
3import { describe, expect } from "alchemy-test";
4import * as Effect from "effect/Effect";
5import * as FileSystem from "effect/FileSystem";
6import * as Path from "effect/Path";
7import * as pathe from "pathe";
8import { cloneFixture } from "../../Cloudflare/Utils/Fixture.ts";
9import { expectUrlContains } from "../../Cloudflare/Utils/Http.ts";
10
11// `dev: true` runs local providers behind the RPC sidecar proxy by default,
12// matching the process topology of the real `alchemy dev` command.
13const { test } = Test.make({ providers: AWS.providers(), dev: true });
14
15const fixtureDir = pathe.resolve(
16 import.meta.dirname,
17 "fixtures",
18 "foldkit-app",
19);
20
21// Clone under the alchemy package so `vite`, `foldkit`, and
22// `@foldkit/vite-plugin` resolve from the workspace's hoisted node_modules
23// (the fixture has none of its own).
24const tempRoot = pathe.resolve(import.meta.dirname, "../../../.tmp");
25
26const fixtureEntries = [
27 ".gitignore",
28 "package.json",
29 "vite.config.ts",
30 "index.html",
31 "src",
32 "public",
33];
34
35describe(
36 "AWS.Website.Foldkit local",
37 { tags: ["provider:aws", "provider:aws:website", "local"] },
38 () => {
39 test.provider(
40 "dev runs the app's own Vite dev server with no cloud resources",
41 (stack) =>
42 Effect.gen(function* () {
43 const fs = yield* FileSystem.FileSystem;
44 const path = yield* Path.Path;
45
46 yield* stack.destroy();
47
48 const rootDir = yield* cloneFixture(fixtureDir, {
49 prefix: "alchemy-foldkit-aws-local-",
50 tempRoot,
51 entries: fixtureEntries,
52 });
53
54 const deployed = yield* stack.deploy(
55 Effect.gen(function* () {
56 const site = yield* AWS.Website.Foldkit("FoldkitSite", {
57 rootDir,
58 });
59 return { site };
60 }),
61 );
62
63 // The site is Vite's own dev server (the Foldkit plugin runs inside
64 // it): a localhost URL and no cloud rows at all — proof no AWS call
65 // ran, and specifically NOT a *.cloudfront.net URL.
66 const url = deployed.site.url! as string;
67 expect(url).toMatch(
68 /^http:\/\/(localhost|127\.0\.0\.1|\[[0-9a-fA-F:]+\])/,
69 );
70 expect(deployed.site.distribution).toBeUndefined();
71 expect(deployed.site.server).toBeUndefined();
72 expect(deployed.site.bucket).toBeUndefined();
73 expect(deployed.site.files).toBeUndefined();
74
75 // The shell serves through Vite's dev server (index.html transformed
76 // on the fly, module scripts served from source).
77 yield* expectUrlContains(`${url}/`, "FOLDKIT_AWS_PAGE_MARKER", {
78 timeout: "120 seconds",
79 label: "dev index page",
80 });
81 // The Foldkit app's source modules serve straight from src/ — no
82 // build ran.
83 yield* expectUrlContains(
84 `${url}/src/main.ts`,
85 "FOLDKIT_AWS_MODULE_MARKER",
86 { label: "dev module source" },
87 );
88
89 // ── HMR surface: edit index.html in place. The stack is NOT
90 // re-applied — Vite's dev server serves the transformed html per
91 // request, so the same URL must show the new marker ───────────────
92 const indexPath = path.join(rootDir, "index.html");
93 const index = yield* fs.readFileString(indexPath);
94 yield* fs.writeFileString(
95 indexPath,
96 index.replaceAll(
97 "FOLDKIT_AWS_PAGE_MARKER",
98 "FOLDKIT_AWS_PAGE_MARKER_V2",
99 ),
100 );
101 yield* expectUrlContains(`${url}/`, "FOLDKIT_AWS_PAGE_MARKER_V2", {
102 timeout: "90 seconds",
103 label: "index page after edit",
104 });
105
106 yield* stack.destroy();
107 }),
108 { timeout: 600_000 },
109 );
110 },
111);