1import * as Effect from "effect/Effect";
2import * as Namespace from "../../Namespace.ts";
3import { ref } from "../ref.ts";
4import { REDIS_URL_ENV, type Redis } from "../Redis.ts";
5import { makeFrameworkSite, type FrameworkSiteProps } from "./FrameworkSite.ts";
6
7/**
8 * The vinext Node framework module (it is its own deploy target — not
9 * the Cloudflare Worker source).
10 */
11export const VINEXT_FRAMEWORK_SPECIFIER =
12 "@alchemy.run/frontend-frameworks/vinext/node";
13
14/** The Node container deploy target for the vinext build. */
15export const VINEXT_NODE_TARGET_SPECIFIER =
16 "@alchemy.run/frontend-frameworks/vinext/node";
17
18export interface VinextProps extends FrameworkSiteProps {
19 /**
20 * Optional Redis for ISR / `"use cache"`. Alchemy writes `REDIS_URL`
21 * onto the Service and injects the Redis data-cache adapter automatically.
22 * Missing `REDIS_URL` (`alchemy dev`) falls back to memory.
23 */
24 redis?: Redis;
25}
26
27/**
28 * Deploy a [vinext](https://vinext.dev) application to Railway as a
29 * long-running Node process: vinext's Vite build, then vinext's production
30 * server (`startProdServer`) on `PORT` (default 3000). **Not** the
31 * Cloudflare Worker path — those wrappers are workerd.
32 *
33 * The image `npm install`s `vinext`, `react`, `react-dom`, and
34 * `react-server-dom-webpack`, and bakes `dist/` into `/app`.
35 *
36 * During `alchemy dev` the site is vinext's own dev server (`vinext
37 * dev`) and no cloud resources are declared; `Alchemy.remote()` opts
38 * back into the full live deployment.
39 *
40 * ### Creating vinext Sites
41 * **Example:** Basic vinext App
42 * ```typescript
43 * const site = yield* Railway.Website.Vinext("Web", {
44 * rootDir: "./app",
45 * });
46 * ```
47 *
48 * ### Server Configuration
49 * **Example:** Process Environment
50 * ```typescript
51 * const site = yield* Railway.Website.Vinext("Web", {
52 * rootDir: "./app",
53 * env: {
54 * GREETING: "Hello from vinext on Railway!",
55 * },
56 * });
57 * ```
58 *
59 * **Example:** Redis data cache
60 * ```typescript
61 * const project = yield* Railway.Project("Project");
62 * const redis = yield* Railway.Redis("Cache", { project });
63 * const site = yield* Railway.Website.Vinext("Web", {
64 * project,
65 * redis,
66 * });
67 * ```
68 *
69 * @resource
70 * @product Website
71 */
72export const Vinext = (id: string, propsIn: VinextProps = {}) =>
73 Effect.gen(function* () {
74 const { redis, ...props } = propsIn;
75 return yield* makeFrameworkSite(
76 id,
77 {
78 ...props,
79 env:
80 redis === undefined
81 ? props.env
82 : {
83 [REDIS_URL_ENV]: ref(redis, REDIS_URL_ENV),
84 ...props.env,
85 },
86 },
87 {
88 name: "Vinext",
89 framework: VINEXT_FRAMEWORK_SPECIFIER,
90 target: VINEXT_NODE_TARGET_SPECIFIER,
91 install: ["vinext", "react", "react-dom", "react-server-dom-webpack"],
92 },
93 );
94 }).pipe(Namespace.push(id));