UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

125 lines (123 loc) • 4.92 kB
import { PGLITE_DEV_DATABASE_URL, PGLITE_TEST_DATABASE_URL } from "../services/infra.mjs"; import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { Data, Effect } from "effect"; import { config } from "dotenv"; //#region src/env/legacy-databases.ts var LegacyMigrationError = class extends Data.TaggedError("LegacyMigrationError") {}; /** * The compose-era local database convention (`bos dev` provisioned Postgres * via the committed docker-compose.yml): every `*_DATABASE_URL` pointed at * `postgres://everythingdev:everythingdev@localhost:<port>/<db>` on the * hardcoded 5432–5435 block. Those are exactly the opt-ins that made two * concurrent local projects fight over host ports. * * The compose-era convention is retired — local persistence is a * file-backed PGlite data dir (`.bos/db`), tests run on in-memory pglite. * `bos dev` repairs leftover state once, idempotently: * 1. `.env` lines whose URL matches the conventional shape (user, host, * and database name — port too, but drift ports like 5532 from the old * +100 allocator also match by name) are rewritten to the new defaults. * Any other `postgres://` URL is operator-owned and left alone. * 2. A leftover scaffold-shaped `docker-compose.yml` is moved to * `.bos/compose-retired/` rather than deleted; operator-authored * compose files (different service names/images) are untouched. */ const migrateLegacyComposeState = (configDir) => Effect.try({ try: () => { const envPath = join(configDir, ".env"); const databaseUrlDrift = []; if (existsSync(envPath)) { const raw = readFileSync(envPath, "utf-8"); const parsed = config({ path: envPath, quiet: true }).parsed ?? {}; const rewritten = rewrittenLegacyDatabaseValues({ file: parsed, raw }); if (rewritten.size > 0) { const updated = raw.split("\n").map((line) => { const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line); if (!match) return line; const replacement = rewritten.get(match[1]); return replacement === void 0 ? line : `${match[1]}=${replacement}`; }); writeFileSync(envPath, updated.join("\n")); for (const [key, to] of rewritten) databaseUrlDrift.push({ key, from: parsed[key] ?? "", to }); } } return { databaseUrlDrift, composeRelocated: relocateLegacyComposeFile(configDir) }; }, catch: (cause) => new LegacyMigrationError({ cause }) }).pipe(Effect.orElseSucceed(() => ({ databaseUrlDrift: [], composeRelocated: false }))); /** Matches the compose-era URL shapes: the scaffolded user + localhost + the convention's database names. */ const LEGACY_DATABASE_NAME = /^(api_db|auth_db|api_test_db|auth_test_db)$/; function legacyMappingForUrl(rawUrl) { try { const url = new URL(rawUrl); if (url.protocol !== "postgres:" && url.protocol !== "postgresql:") return void 0; if (url.hostname !== "localhost" && url.hostname !== "127.0.0.1") return void 0; if (url.username !== "everythingdev" || url.password !== "everythingdev") return void 0; const databaseName = decodeURIComponent(url.pathname.replace(/^\//, "")); if (!LEGACY_DATABASE_NAME.test(databaseName)) return void 0; return databaseName.endsWith("_test_db") ? PGLITE_TEST_DATABASE_URL : PGLITE_DEV_DATABASE_URL; } catch { return; } } function rewrittenLegacyDatabaseValues(input) { const rewritten = /* @__PURE__ */ new Map(); for (const [key, value] of Object.entries(input.file)) { const match = value ? legacyMappingForUrl(value) : void 0; if (match !== void 0) { if (new RegExp(`^${key}=.+$`, "m").test(input.raw)) rewritten.set(key, match); } } return rewritten; } const SCAFFOLD_SERVICE_NAMES = /* @__PURE__ */ new Set([ "postgres-api", "postgres-auth", "postgres-api-test", "postgres-auth-test" ]); const SCAFFOLD_IMAGE_HINTS = [ "public.ecr.aws/docker/library/postgres", "docker.io/library/postgres", "postgres:17-alpine", "ghcr.io/nearbuilders/mirror-postgres" ]; function isScaffoldComposeFile(raw) { if (![...raw.matchAll(/^\s+-?\s*([a-z-]+):\s*$/gm)].map((match) => match[1] ?? "").some((name) => SCAFFOLD_SERVICE_NAMES.has(name))) return false; return SCAFFOLD_IMAGE_HINTS.some((hint) => raw.includes(hint)); } function relocateLegacyComposeFile(configDir) { const composePath = join(configDir, "docker-compose.yml"); if (!existsSync(composePath)) return false; let raw; try { raw = readFileSync(composePath, "utf-8"); } catch { return false; } if (!isScaffoldComposeFile(raw)) return false; const retiredDir = join(configDir, ".bos", "compose-retired"); mkdirSync(retiredDir, { recursive: true }); renameSync(composePath, join(retiredDir, "docker-compose.yml")); return true; } //#endregion export { LegacyMigrationError, migrateLegacyComposeState, rewrittenLegacyDatabaseValues }; //# sourceMappingURL=legacy-databases.mjs.map