1// @effect-diagnostics nodeBuiltinImport:off - Build bootstrap reads optional root env files before an Effect runtime exists.
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4import * as NodeURL from "node:url";
5import * as NodeUtil from "node:util";
6
7export interface T3CodePublicConfig {
8 readonly clerkPublishableKey: string | undefined;
9 readonly clerkJwtTemplate: string | undefined;
10 readonly clerkCliOAuthClientId: string | undefined;
11 readonly relayUrl: string | undefined;
12}
13
14type Environment = Readonly<Record<string, string | undefined>>;
15
16const REPO_ROOT = NodePath.dirname(
17 NodePath.dirname(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url))),
18);
19
20export function loadRepoEnv({
21 baseEnv = process.env,
22 repoRoot = REPO_ROOT,
23}: {
24 readonly baseEnv?: Environment;
25 readonly repoRoot?: string;
26} = {}): Record<string, string | undefined> {
27 const rootEnv = readEnvFile(NodePath.join(repoRoot, ".env"));
28 const localEnv = readEnvFile(NodePath.join(repoRoot, ".env.local"));
29 const config = resolvePublicConfig(baseEnv, localEnv, rootEnv);
30
31 return {
32 ...rootEnv,
33 ...localEnv,
34 ...baseEnv,
35 ...(config.clerkPublishableKey
36 ? {
37 T3CODE_CLERK_PUBLISHABLE_KEY: config.clerkPublishableKey,
38 VITE_CLERK_PUBLISHABLE_KEY: config.clerkPublishableKey,
39 EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: config.clerkPublishableKey,
40 }
41 : {}),
42 ...(config.clerkJwtTemplate
43 ? {
44 T3CODE_CLERK_JWT_TEMPLATE: config.clerkJwtTemplate,
45 VITE_CLERK_JWT_TEMPLATE: config.clerkJwtTemplate,
46 EXPO_PUBLIC_CLERK_JWT_TEMPLATE: config.clerkJwtTemplate,
47 }
48 : {}),
49 ...(config.clerkCliOAuthClientId
50 ? {
51 T3CODE_CLERK_CLI_OAUTH_CLIENT_ID: config.clerkCliOAuthClientId,
52 VITE_CLERK_CLI_OAUTH_CLIENT_ID: config.clerkCliOAuthClientId,
53 }
54 : {}),
55 ...(config.relayUrl
56 ? {
57 T3CODE_RELAY_URL: config.relayUrl,
58 VITE_T3CODE_RELAY_URL: config.relayUrl,
59 }
60 : {}),
61 };
62}
63
64export function resolvePublicConfig(...sources: readonly Environment[]): T3CodePublicConfig {
65 return {
66 clerkPublishableKey: firstNonEmpty(
67 sources,
68 "T3CODE_CLERK_PUBLISHABLE_KEY",
69 "VITE_CLERK_PUBLISHABLE_KEY",
70 "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY",
71 ),
72 clerkJwtTemplate: firstNonEmpty(
73 sources,
74 "T3CODE_CLERK_JWT_TEMPLATE",
75 "VITE_CLERK_JWT_TEMPLATE",
76 "EXPO_PUBLIC_CLERK_JWT_TEMPLATE",
77 ),
78 clerkCliOAuthClientId: firstNonEmpty(
79 sources,
80 "T3CODE_CLERK_CLI_OAUTH_CLIENT_ID",
81 "VITE_CLERK_CLI_OAUTH_CLIENT_ID",
82 ),
83 relayUrl: firstNonEmpty(sources, "T3CODE_RELAY_URL", "VITE_T3CODE_RELAY_URL"),
84 };
85}
86
87function firstNonEmpty(sources: readonly Environment[], ...names: readonly string[]) {
88 for (const source of sources) {
89 for (const name of names) {
90 const value = source[name]?.trim();
91 if (value) {
92 return value;
93 }
94 }
95 }
96 return undefined;
97}
98
99function readEnvFile(path: string): Record<string, string | undefined> {
100 return NodeFS.existsSync(path) ? NodeUtil.parseEnv(NodeFS.readFileSync(path, "utf8")) : {};
101}