Open History to see the last commit
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 readonly mobileOtlpTracesUrl: string | undefined;
13 readonly mobileOtlpTracesDataset: string | undefined;
14 readonly mobileOtlpTracesToken: string | undefined;
15 readonly relayClientOtlpTracesUrl: string | undefined;
16 readonly relayClientOtlpTracesDataset: string | undefined;
17 readonly relayClientOtlpTracesToken: string | undefined;
18}
19
20type Environment = Readonly<Record<string, string | undefined>>;
21
22const REPO_ROOT = NodePath.resolve(NodeURL.fileURLToPath(import.meta.url), "../../..");
23
24export function loadRepoEnv({
25 baseEnv = process.env,
26 repoRoot = REPO_ROOT,
27}: {
28 readonly baseEnv?: Environment;
29 readonly repoRoot?: string;
30} = {}): Record<string, string | undefined> {
31 const rootEnv = readEnvFile(NodePath.join(repoRoot, ".env"));
32 const localEnv = readEnvFile(NodePath.join(repoRoot, ".env.local"));
33 const config = resolvePublicConfig(baseEnv, localEnv, rootEnv);
34
35 return {
36 ...rootEnv,
37 ...localEnv,
38 ...baseEnv,
39 ...(config.clerkPublishableKey
40 ? {
41 T3CODE_CLERK_PUBLISHABLE_KEY: config.clerkPublishableKey,
42 VITE_CLERK_PUBLISHABLE_KEY: config.clerkPublishableKey,
43 EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: config.clerkPublishableKey,
44 }
45 : {}),
46 ...(config.clerkJwtTemplate
47 ? {
48 T3CODE_CLERK_JWT_TEMPLATE: config.clerkJwtTemplate,
49 VITE_CLERK_JWT_TEMPLATE: config.clerkJwtTemplate,
50 EXPO_PUBLIC_CLERK_JWT_TEMPLATE: config.clerkJwtTemplate,
51 }
52 : {}),
53 ...(config.clerkCliOAuthClientId
54 ? {
55 T3CODE_CLERK_CLI_OAUTH_CLIENT_ID: config.clerkCliOAuthClientId,
56 VITE_CLERK_CLI_OAUTH_CLIENT_ID: config.clerkCliOAuthClientId,
57 }
58 : {}),
59 ...(config.relayUrl
60 ? {
61 T3CODE_RELAY_URL: config.relayUrl,
62 VITE_T3CODE_RELAY_URL: config.relayUrl,
63 }
64 : {}),
65 ...(config.mobileOtlpTracesUrl
66 ? {
67 T3CODE_MOBILE_OTLP_TRACES_URL: config.mobileOtlpTracesUrl,
68 EXPO_PUBLIC_OTLP_TRACES_URL: config.mobileOtlpTracesUrl,
69 }
70 : {}),
71 ...(config.mobileOtlpTracesDataset
72 ? {
73 T3CODE_MOBILE_OTLP_TRACES_DATASET: config.mobileOtlpTracesDataset,
74 EXPO_PUBLIC_OTLP_TRACES_DATASET: config.mobileOtlpTracesDataset,
75 }
76 : {}),
77 ...(config.mobileOtlpTracesToken
78 ? {
79 T3CODE_MOBILE_OTLP_TRACES_TOKEN: config.mobileOtlpTracesToken,
80 EXPO_PUBLIC_OTLP_TRACES_TOKEN: config.mobileOtlpTracesToken,
81 }
82 : {}),
83 ...(config.relayClientOtlpTracesUrl
84 ? {
85 T3CODE_RELAY_CLIENT_OTLP_TRACES_URL: config.relayClientOtlpTracesUrl,
86 VITE_RELAY_OTLP_TRACES_URL: config.relayClientOtlpTracesUrl,
87 }
88 : {}),
89 ...(config.relayClientOtlpTracesDataset
90 ? {
91 T3CODE_RELAY_CLIENT_OTLP_TRACES_DATASET: config.relayClientOtlpTracesDataset,
92 VITE_RELAY_OTLP_TRACES_DATASET: config.relayClientOtlpTracesDataset,
93 }
94 : {}),
95 ...(config.relayClientOtlpTracesToken
96 ? {
97 T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN: config.relayClientOtlpTracesToken,
98 VITE_RELAY_OTLP_TRACES_TOKEN: config.relayClientOtlpTracesToken,
99 }
100 : {}),
101 };
102}
103
104function resolvePublicConfig(...sources: readonly Environment[]): T3CodePublicConfig {
105 return {
106 clerkPublishableKey: firstNonEmpty(
107 sources,
108 "T3CODE_CLERK_PUBLISHABLE_KEY",
109 "VITE_CLERK_PUBLISHABLE_KEY",
110 "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY",
111 ),
112 clerkJwtTemplate: firstNonEmpty(
113 sources,
114 "T3CODE_CLERK_JWT_TEMPLATE",
115 "VITE_CLERK_JWT_TEMPLATE",
116 "EXPO_PUBLIC_CLERK_JWT_TEMPLATE",
117 ),
118 clerkCliOAuthClientId: firstNonEmpty(
119 sources,
120 "T3CODE_CLERK_CLI_OAUTH_CLIENT_ID",
121 "VITE_CLERK_CLI_OAUTH_CLIENT_ID",
122 ),
123 relayUrl: firstNonEmpty(sources, "T3CODE_RELAY_URL", "VITE_T3CODE_RELAY_URL"),
124 mobileOtlpTracesUrl: firstNonEmpty(
125 sources,
126 "T3CODE_MOBILE_OTLP_TRACES_URL",
127 "EXPO_PUBLIC_OTLP_TRACES_URL",
128 ),
129 mobileOtlpTracesDataset: firstNonEmpty(
130 sources,
131 "T3CODE_MOBILE_OTLP_TRACES_DATASET",
132 "EXPO_PUBLIC_OTLP_TRACES_DATASET",
133 ),
134 mobileOtlpTracesToken: firstNonEmpty(
135 sources,
136 "T3CODE_MOBILE_OTLP_TRACES_TOKEN",
137 "EXPO_PUBLIC_OTLP_TRACES_TOKEN",
138 ),
139 relayClientOtlpTracesUrl: firstNonEmpty(
140 sources,
141 "T3CODE_RELAY_CLIENT_OTLP_TRACES_URL",
142 "VITE_RELAY_OTLP_TRACES_URL",
143 ),
144 relayClientOtlpTracesDataset: firstNonEmpty(
145 sources,
146 "T3CODE_RELAY_CLIENT_OTLP_TRACES_DATASET",
147 "VITE_RELAY_OTLP_TRACES_DATASET",
148 ),
149 relayClientOtlpTracesToken: firstNonEmpty(
150 sources,
151 "T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN",
152 "VITE_RELAY_OTLP_TRACES_TOKEN",
153 ),
154 };
155}
156
157function firstNonEmpty(sources: readonly Environment[], ...names: readonly string[]) {
158 for (const source of sources) {
159 for (const name of names) {
160 const value = source[name]?.trim();
161 if (value) {
162 return value;
163 }
164 }
165 }
166 return undefined;
167}
168
169function readEnvFile(path: string): Record<string, string | undefined> {
170 return NodeFS.existsSync(path) ? NodeUtil.parseEnv(NodeFS.readFileSync(path, "utf8")) : {};
171}