import os from "node:os"; import { assert, expect, it } from "@effect/vitest"; import { ConfigProvider, Effect, FileSystem, Layer, Option, Path } from "effect"; import { NetService } from "@t3tools/shared/Net"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { deriveServerPaths } from "./config.ts"; import { resolveServerConfig } from "./cli.ts"; it.layer(NodeServices.layer)("cli config resolution", (it) => { const defaultObservabilityConfig = { traceMinLevel: "Info", traceTimingEnabled: true, traceBatchWindowMs: 200, traceMaxBytes: 10 * 1024 * 1024, traceMaxFiles: 10, otlpTracesUrl: undefined, otlpMetricsUrl: undefined, otlpExportIntervalMs: 10_000, otlpServiceName: "t3-server", } as const; const openBootstrapFd = Effect.fn(function* (payload: Record) { const fs = yield* FileSystem.FileSystem; const filePath = yield* fs.makeTempFileScoped({ prefix: "t3-bootstrap-", suffix: ".ndjson" }); yield* fs.writeFileString(filePath, `${JSON.stringify(payload)}\n`); const { fd } = yield* fs.open(filePath, { flag: "r" }); return fd; }); it.effect("falls back to effect/config values when flags are omitted", () => Effect.gen(function* () { const { join } = yield* Path.Path; const baseDir = join(os.tmpdir(), "t3-cli-config-env-base"); const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:5173")); const resolved = yield* resolveServerConfig( { mode: Option.none(), port: Option.none(), host: Option.none(), baseDir: Option.none(), cwd: Option.none(), devUrl: Option.none(), noBrowser: Option.none(), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.none(), logWebSocketEvents: Option.none(), }, Option.none(), ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer( ConfigProvider.fromEnv({ env: { T3CODE_LOG_LEVEL: "Warn", T3CODE_MODE: "desktop", T3CODE_PORT: "4001", T3CODE_HOST: "0.0.0.0", T3CODE_HOME: baseDir, VITE_DEV_SERVER_URL: "http://127.0.0.1:5173", T3CODE_NO_BROWSER: "true", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false", T3CODE_LOG_WS_EVENTS: "true", }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Warn", ...defaultObservabilityConfig, mode: "desktop", port: 4001, cwd: process.cwd(), baseDir, ...derivedPaths, host: "0.0.0.0", staticDir: undefined, devUrl: new URL("http://127.0.0.1:5173"), noBrowser: true, startupPresentation: "browser", desktopBootstrapToken: undefined, autoBootstrapProjectFromCwd: false, logWebSocketEvents: true, }); }), ); it.effect("uses CLI flags when provided", () => Effect.gen(function* () { const { join } = yield* Path.Path; const baseDir = join(os.tmpdir(), "t3-cli-config-flags-base"); const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:4173")); const resolved = yield* resolveServerConfig( { mode: Option.some("web"), port: Option.some(8788), host: Option.some("127.0.0.1"), baseDir: Option.some(baseDir), cwd: Option.none(), devUrl: Option.some(new URL("http://127.0.0.1:4173")), noBrowser: Option.some(true), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.some(true), logWebSocketEvents: Option.some(true), }, Option.some("Debug"), ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer( ConfigProvider.fromEnv({ env: { T3CODE_LOG_LEVEL: "Warn", T3CODE_MODE: "desktop", T3CODE_PORT: "4001", T3CODE_HOST: "0.0.0.0", T3CODE_HOME: join(os.tmpdir(), "ignored-base"), VITE_DEV_SERVER_URL: "http://127.0.0.1:5173", T3CODE_NO_BROWSER: "false", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false", T3CODE_LOG_WS_EVENTS: "false", }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Debug", ...defaultObservabilityConfig, mode: "web", port: 8788, cwd: process.cwd(), baseDir, ...derivedPaths, host: "127.0.0.1", staticDir: undefined, devUrl: new URL("http://127.0.0.1:4173"), noBrowser: true, startupPresentation: "browser", desktopBootstrapToken: undefined, autoBootstrapProjectFromCwd: true, logWebSocketEvents: true, }); }), ); it.effect("preserves explicit false CLI boolean flags over env and bootstrap values", () => Effect.gen(function* () { const { join } = yield* Path.Path; const baseDir = join(os.tmpdir(), "t3-cli-config-false-flags"); const fd = yield* openBootstrapFd({ noBrowser: true, autoBootstrapProjectFromCwd: true, logWebSocketEvents: true, }); const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:4173")); const resolved = yield* resolveServerConfig( { mode: Option.some("web"), port: Option.some(8788), host: Option.some("127.0.0.1"), baseDir: Option.some(baseDir), cwd: Option.none(), devUrl: Option.some(new URL("http://127.0.0.1:4173")), noBrowser: Option.some(false), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.some(false), logWebSocketEvents: Option.some(false), }, Option.none(), ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer( ConfigProvider.fromEnv({ env: { T3CODE_BOOTSTRAP_FD: String(fd), T3CODE_NO_BROWSER: "true", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "true", T3CODE_LOG_WS_EVENTS: "true", }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Info", ...defaultObservabilityConfig, mode: "web", port: 8788, cwd: process.cwd(), baseDir, ...derivedPaths, host: "127.0.0.1", staticDir: undefined, devUrl: new URL("http://127.0.0.1:4173"), noBrowser: false, startupPresentation: "browser", desktopBootstrapToken: undefined, autoBootstrapProjectFromCwd: false, logWebSocketEvents: false, }); }), ); it.effect("uses bootstrap envelope values as fallbacks when flags and env are absent", () => Effect.gen(function* () { const { join } = yield* Path.Path; const baseDir = "/tmp/t3-bootstrap-home"; const fd = yield* openBootstrapFd({ mode: "desktop", port: 4888, host: "127.0.0.2", t3Home: baseDir, devUrl: "http://127.0.0.1:5173", noBrowser: true, autoBootstrapProjectFromCwd: false, logWebSocketEvents: true, otlpTracesUrl: "http://localhost:4318/v1/traces", otlpMetricsUrl: "http://localhost:4318/v1/metrics", }); const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:5173")); const resolved = yield* resolveServerConfig( { mode: Option.none(), port: Option.none(), host: Option.none(), baseDir: Option.none(), cwd: Option.none(), devUrl: Option.none(), noBrowser: Option.none(), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.none(), logWebSocketEvents: Option.none(), }, Option.none(), ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer( ConfigProvider.fromEnv({ env: { T3CODE_BOOTSTRAP_FD: String(fd), }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Info", ...defaultObservabilityConfig, otlpTracesUrl: "http://localhost:4318/v1/traces", otlpMetricsUrl: "http://localhost:4318/v1/metrics", mode: "desktop", port: 4888, cwd: process.cwd(), baseDir, ...derivedPaths, host: "127.0.0.2", staticDir: undefined, devUrl: new URL("http://127.0.0.1:5173"), noBrowser: true, startupPresentation: "browser", desktopBootstrapToken: undefined, autoBootstrapProjectFromCwd: false, logWebSocketEvents: true, }); assert.equal(join(baseDir, "dev"), resolved.stateDir); }), ); it.effect("creates derived runtime directories during config resolution", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-cli-config-dirs-" }); const customCwd = path.join(baseDir, "nested", "project"); const resolved = yield* resolveServerConfig( { mode: Option.some("desktop"), port: Option.some(4888), host: Option.none(), baseDir: Option.some(baseDir), cwd: Option.some(customCwd), devUrl: Option.some(new URL("http://127.0.0.1:5173")), noBrowser: Option.none(), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.none(), logWebSocketEvents: Option.none(), }, Option.none(), ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })), NetService.layer, ), ), ); for (const directory of [ customCwd, resolved.stateDir, resolved.logsDir, resolved.providerLogsDir, resolved.terminalLogsDir, resolved.attachmentsDir, resolved.worktreesDir, path.dirname(resolved.serverLogPath), path.dirname(resolved.serverTracePath), ]) { expect(yield* fs.exists(directory)).toBe(true); } expect(resolved.cwd).toBe(path.resolve(customCwd)); }), ); it.effect("applies flag then env precedence over bootstrap envelope values", () => Effect.gen(function* () { const { join } = yield* Path.Path; const baseDir = join(os.tmpdir(), "t3-cli-config-env-wins"); const fd = yield* openBootstrapFd({ mode: "desktop", port: 4888, host: "127.0.0.2", t3Home: "/tmp/t3-bootstrap-home", devUrl: "http://127.0.0.1:5173", noBrowser: false, autoBootstrapProjectFromCwd: false, logWebSocketEvents: false, }); const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:4173")); const resolved = yield* resolveServerConfig( { mode: Option.none(), port: Option.some(8788), host: Option.some("127.0.0.1"), baseDir: Option.none(), cwd: Option.none(), devUrl: Option.some(new URL("http://127.0.0.1:4173")), noBrowser: Option.none(), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.none(), logWebSocketEvents: Option.none(), }, Option.some("Debug"), ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer( ConfigProvider.fromEnv({ env: { T3CODE_MODE: "web", T3CODE_BOOTSTRAP_FD: String(fd), T3CODE_HOME: baseDir, T3CODE_NO_BROWSER: "true", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "true", T3CODE_LOG_WS_EVENTS: "true", }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Debug", ...defaultObservabilityConfig, mode: "web", port: 8788, cwd: process.cwd(), baseDir, ...derivedPaths, host: "127.0.0.1", staticDir: undefined, devUrl: new URL("http://127.0.0.1:4173"), noBrowser: true, startupPresentation: "browser", desktopBootstrapToken: undefined, autoBootstrapProjectFromCwd: true, logWebSocketEvents: true, }); }), ); it.effect("falls back to persisted observability settings when env vars are absent", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-cli-config-settings-" }); const derivedPaths = yield* deriveServerPaths(baseDir, undefined); yield* fs.makeDirectory(path.dirname(derivedPaths.settingsPath), { recursive: true }); yield* fs.writeFileString( derivedPaths.settingsPath, `${JSON.stringify({ observability: { otlpTracesUrl: "http://localhost:4318/v1/traces", otlpMetricsUrl: "http://localhost:4318/v1/metrics", }, })}\n`, ); const resolved = yield* resolveServerConfig( { mode: Option.some("desktop"), port: Option.some(4888), host: Option.none(), baseDir: Option.some(baseDir), cwd: Option.none(), devUrl: Option.none(), noBrowser: Option.none(), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.none(), logWebSocketEvents: Option.none(), }, Option.none(), ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })), NetService.layer, ), ), ); expect(resolved.otlpTracesUrl).toBe("http://localhost:4318/v1/traces"); expect(resolved.otlpMetricsUrl).toBe("http://localhost:4318/v1/metrics"); expect(resolved).toEqual({ logLevel: "Info", ...defaultObservabilityConfig, otlpTracesUrl: "http://localhost:4318/v1/traces", otlpMetricsUrl: "http://localhost:4318/v1/metrics", mode: "desktop", port: 4888, cwd: process.cwd(), baseDir, ...derivedPaths, host: "127.0.0.1", staticDir: resolved.staticDir, devUrl: undefined, noBrowser: true, startupPresentation: "browser", desktopBootstrapToken: undefined, autoBootstrapProjectFromCwd: false, logWebSocketEvents: false, }); }), ); it.effect("forces noBrowser and disables auto-bootstrap for headless startup presentation", () => Effect.gen(function* () { const { join } = yield* Path.Path; const baseDir = join(os.tmpdir(), "t3-cli-config-headless-base"); const derivedPaths = yield* deriveServerPaths(baseDir, undefined); const resolved = yield* resolveServerConfig( { mode: Option.some("web"), port: Option.some(3773), host: Option.none(), baseDir: Option.some(baseDir), cwd: Option.none(), devUrl: Option.none(), noBrowser: Option.none(), bootstrapFd: Option.none(), autoBootstrapProjectFromCwd: Option.none(), logWebSocketEvents: Option.none(), }, Option.none(), { startupPresentation: "headless", }, ).pipe( Effect.provide( Layer.mergeAll( ConfigProvider.layer( ConfigProvider.fromEnv({ env: { T3CODE_NO_BROWSER: "false", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "true", }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Info", ...defaultObservabilityConfig, mode: "web", port: 3773, cwd: process.cwd(), baseDir, ...derivedPaths, host: undefined, staticDir: resolved.staticDir, devUrl: undefined, noBrowser: true, startupPresentation: "headless", desktopBootstrapToken: undefined, autoBootstrapProjectFromCwd: false, logWebSocketEvents: false, }); }), ); });