1/**
2 * A Railway variable-reference template (`${{Service.KEY}}` or
3 * `${{shared.NAME}}`). Pass as {@link Variable} `value` or `Service.env`.
4 *
5 * Distinct from the unexported resource-prop `Ref<T>`. Never export that
6 * `Ref`.
7 */
8export type VariableRef = string;
9
10/**
11 * Something `Railway.ref` can take a namespace from: `"shared"`, an
12 * explicit service name, or a resource's `LogicalId`.
13 */
14export type VariableRefSource =
15 | string
16 | {
17 readonly LogicalId: string;
18 };
19
20const SHARED = "shared";
21
22const namespaceOf = (resource: VariableRefSource): string => {
23 if (typeof resource === "string") {
24 const trimmed = resource.trim();
25 return trimmed.length > 0 ? trimmed : SHARED;
26 }
27 const logical =
28 typeof resource.LogicalId === "string" ? resource.LogicalId.trim() : "";
29 return logical.length > 0 ? logical : SHARED;
30};
31
32/**
33 * Emit a Railway variable-reference template (`${{Service.KEY}}` or
34 * `${{shared.NAME}}`). Distinct from the unexported resource-prop
35 * `Ref<T>` — never export that `Ref`.
36 *
37 * `Railway.ref(Db, "DATABASE_URL")` returns `${{LogicalName.DATABASE_URL}}`
38 * where `LogicalName` is `resource.LogicalId` (`Postgres("Db")` → `Db`).
39 * Shared variables use the `"shared"` namespace:
40 * `Railway.ref("shared", "SENTRY_DSN")` returns `${{shared.SENTRY_DSN}}`.
41 *
42 * Store the string as a {@link Variable} `value` (or on `Service.env`).
43 * Railway keeps the template (`unrendered: true`) and interpolates it at
44 * build/runtime — it is not a resolved URI. Use {@link ConnectPostgres}
45 * when you want a typed client inside an Effect-native Service.
46 *
47 * Railway interpolates by service name. Set `name` on Postgres/Service to
48 * the LogicalId if you need the template to resolve to that service.
49 *
50 * @see https://docs.railway.com/variables/reference#template-syntax
51 * @see https://docs.railway.com/infrastructure-as-code/reference
52 *
53 * ### Reference a service variable
54 * Pass a resource (or its LogicalId) and the variable key. The result is
55 * a template string, not a URI.
56 *
57 * **Example:** Reference Postgres DATABASE_URL
58 * ```typescript
59 * const db = yield* Railway.Postgres("Db", { project: site });
60 * yield* Railway.Variable("DatabaseUrl", {
61 * project: site,
62 * service: api,
63 * name: "DATABASE_URL",
64 * value: Railway.ref(db, "DATABASE_URL"),
65 * });
66 * ```
67 *
68 * ### Shared variables
69 * `"shared"` is the environment-wide namespace (IaC `ctx.shared.NAME`).
70 *
71 * **Example:** Shared variable
72 * ```typescript
73 * env: {
74 * SENTRY_DSN: Railway.ref("shared", "SENTRY_DSN"),
75 * }
76 * ```
77 *
78 * @resource
79 * @product Project
80 */
81export const ref = (resource: VariableRefSource, key: string): VariableRef =>
82 `\${{${namespaceOf(resource)}.${key}}}`;