1import { OtlpHeadersFromString, OtlpProtocol } from "@t3tools/shared/observability";
2import * as Config from "effect/Config";
3import * as ConfigProvider from "effect/ConfigProvider";
4import * as Option from "effect/Option";
5
6const trimNonEmptyOption = (value: string): Option.Option<string> => {
7 const trimmed = value.trim();
8 return trimmed.length > 0 ? Option.some(trimmed) : Option.none();
9};
10
11const trimmedString = (name: string) =>
12 Config.String(name).pipe(Config.option, Config.map(Option.flatMap(trimNonEmptyOption)));
13
14const optionalBoolean = (name: string) =>
15 Config.Boolean(name).pipe(Config.option, Config.map(Option.getOrElse(() => false)));
16
17const commaSeparatedStrings = (name: string) =>
18 trimmedString(name).pipe(
19 Config.map(
20 Option.match({
21 onNone: () => [],
22 onSome: (value) =>
23 value
24 .split(",")
25 .map((entry) => entry.trim())
26 .filter((entry) => entry.length > 0),
27 }),
28 ),
29 );
30
31const compactEnv = (env: Readonly<Record<string, string | undefined>>): Record<string, string> =>
32 Object.fromEntries(
33 Object.entries(env).filter((entry): entry is [string, string] => entry[1] !== undefined),
34 );
35
36export const DesktopConfig = Config.all({
37 appDataDirectory: trimmedString("APPDATA"),
38 xdgConfigHome: trimmedString("XDG_CONFIG_HOME"),
39 xdgDataHome: trimmedString("XDG_DATA_HOME"),
40 t3Home: trimmedString("T3CODE_HOME"),
41 devServerUrl: Config.URL("VITE_DEV_SERVER_URL").pipe(Config.option),
42 appUserModelIdOverride: trimmedString("T3CODE_DESKTOP_APP_USER_MODEL_ID"),
43 devRemoteT3ServerEntryPath: trimmedString("T3CODE_DEV_REMOTE_T3_SERVER_ENTRY_PATH"),
44 configuredBackendPort: Config.Port("T3CODE_PORT").pipe(Config.option),
45 commitHashOverride: trimmedString("T3CODE_COMMIT_HASH"),
46 desktopLanHostOverride: trimmedString("T3CODE_DESKTOP_LAN_HOST"),
47 desktopHttpsEndpointUrls: commaSeparatedStrings("T3CODE_DESKTOP_HTTPS_ENDPOINTS"),
48 otlpTracesUrl: trimmedString("T3CODE_OTLP_TRACES_URL"),
49 otlpMetricsUrl: trimmedString("T3CODE_OTLP_METRICS_URL"),
50 otlpLogsUrl: trimmedString("T3CODE_OTLP_LOGS_URL"),
51 otlpExportIntervalMs: Config.Int("T3CODE_OTLP_EXPORT_INTERVAL_MS").pipe(
52 Config.withDefault(10_000),
53 ),
54 otlpHeaders: Config.schema(OtlpHeadersFromString, "T3CODE_OTLP_HEADERS").pipe(Config.option),
55 otlpProtocol: Config.schema(OtlpProtocol, "T3CODE_OTLP_PROTOCOL").pipe(
56 Config.withDefault("http/json"),
57 ),
58 appImagePath: trimmedString("APPIMAGE"),
59 disableAutoUpdate: optionalBoolean("T3CODE_DISABLE_AUTO_UPDATE"),
60 mockUpdates: optionalBoolean("T3CODE_DESKTOP_MOCK_UPDATES"),
61 mockUpdateServerPort: Config.Port("T3CODE_DESKTOP_MOCK_UPDATE_SERVER_PORT").pipe(
62 Config.withDefault(3000),
63 ),
64});
65
66export const layerTest = (env: Readonly<Record<string, string | undefined>>) =>
67 ConfigProvider.layer(ConfigProvider.fromEnv({ env: compactEnv(env) }));