1import * as Effect from "effect/Effect";
2import * as Namespace from "../../Namespace.ts";
3import type { Redis } from "../Redis.ts";
4import { makeFrameworkSite, type FrameworkSiteProps } from "./FrameworkSite.ts";
5
6/**
7 * The vinext-on-Node framework module (vinext's Vite build +
8 * `startProdServer`). Not the Cloudflare Worker source.
9 */
10export const VINEXT_NODE_FRAMEWORK_SPECIFIER =
11 "@alchemy.run/frontend-frameworks/vinext/node";
12
13export interface VinextProps extends FrameworkSiteProps {
14 /**
15 * Optional Upstash Redis for ISR / `"use cache"`. Bound onto the
16 * hosted Service so Alchemy writes `REDIS_URL` as an App secret.
17 * The resource injects the Redis data-cache adapter automatically.
18 */
19 redis?: Redis;
20}
21
22/**
23 * Deploy a [vinext](https://vinext.dev) application to Fly as a
24 * long-running Node process: vinext's Vite build, then vinext's production
25 * server (`startProdServer`). The `dist/` output is baked into the
26 * image; `vinext` is installed unbundled.
27 *
28 * Do not use the Cloudflare Worker entry (`vinext/server/fetch-handler`)
29 * — that is workerd.
30 *
31 * ISR / `"use cache"` persist in Redis when you pass {@link VinextProps.redis}.
32 * The resource injects the adapter automatically. Missing `REDIS_URL`
33 * (local `vinext start` / `alchemy dev`) falls back to memory.
34 *
35 * During `alchemy dev` the site is `vinext dev` and no cloud resources
36 * are declared; `Alchemy.remote()` opts back into the live Service path.
37 *
38 * ### Creating vinext Sites
39 * **Example:** Basic vinext App
40 * ```typescript
41 * const site = yield* Fly.Website.Vinext("Web", {
42 * rootDir: "./app",
43 * });
44 * ```
45 *
46 * **Example:** Redis data cache
47 * ```typescript
48 * const redis = yield* Fly.Redis("Cache", { eviction: true });
49 * const site = yield* Fly.Website.Vinext("Web", {
50 * redis,
51 * });
52 * ```
53 *
54 * **Example:** Custom Domain
55 * ```typescript
56 * const site = yield* Fly.Website.Vinext("Web", {
57 * rootDir: "./app",
58 * domain: "app.example.com",
59 * });
60 * ```
61 *
62 * @resource
63 * @product Website
64 */
65export const Vinext = (id: string, propsIn: VinextProps = {}) =>
66 Effect.gen(function* () {
67 const { redis, ...props } = propsIn;
68 const site = yield* makeFrameworkSite(id, props, {
69 name: "Vinext",
70 framework: VINEXT_NODE_FRAMEWORK_SPECIFIER,
71 target: VINEXT_NODE_FRAMEWORK_SPECIFIER,
72 install: ["vinext", "react", "react-dom", "react-server-dom-webpack"],
73 });
74 if (redis !== undefined && site.service !== undefined) {
75 yield* site.service.bind`${redis}`({
76 redis: { name: redis.name, id: redis.redisId },
77 });
78 }
79 return site;
80 }).pipe(Namespace.push(id));