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"; import { resolveServerConfig } from "./cli"; it.layer(NodeServices.layer)("cli config resolution", (it) => { 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(), devUrl: Option.none(), noBrowser: Option.none(), authToken: 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_AUTH_TOKEN: "env-token", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false", T3CODE_LOG_WS_EVENTS: "true", }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Warn", 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, authToken: "env-token", 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), devUrl: Option.some(new URL("http://127.0.0.1:4173")), noBrowser: Option.some(true), authToken: Option.some("flag-token"), 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_AUTH_TOKEN: "ignored-token", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false", T3CODE_LOG_WS_EVENTS: "false", }, }), ), NetService.layer, ), ), ); expect(resolved).toEqual({ logLevel: "Debug", 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, authToken: "flag-token", autoBootstrapProjectFromCwd: true, logWebSocketEvents: true, }); }), ); 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, authToken: "bootstrap-token", autoBootstrapProjectFromCwd: false, logWebSocketEvents: true, }); 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(), devUrl: Option.none(), noBrowser: Option.none(), authToken: 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", 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, authToken: "bootstrap-token", 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 resolved = yield* resolveServerConfig( { mode: Option.some("desktop"), port: Option.some(4888), host: Option.none(), baseDir: Option.some(baseDir), devUrl: Option.some(new URL("http://127.0.0.1:5173")), noBrowser: Option.none(), authToken: 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 [ resolved.stateDir, resolved.logsDir, resolved.providerLogsDir, resolved.terminalLogsDir, resolved.attachmentsDir, resolved.worktreesDir, path.dirname(resolved.serverLogPath), ]) { expect(yield* fs.exists(directory)).toBe(true); } }), ); 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, authToken: "bootstrap-token", 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(), devUrl: Option.some(new URL("http://127.0.0.1:4173")), noBrowser: Option.none(), authToken: Option.some("flag-token"), 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", 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, authToken: "flag-token", autoBootstrapProjectFromCwd: true, logWebSocketEvents: true, }); }), ); });