78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
import path from "node:path";
|
|
import { adapterEnv, identityShimEnabled, isDeployedEnv } from "../src/config/env";
|
|
import { exitOnInvalidConfig } from "../src/config/fail-fast";
|
|
import { buildPostgresOptions, databaseUrlFor, scrubDbError, type DbPurpose } from "../src/infra/db/connection-policy";
|
|
import { createFileDb } from "../src/infra/db/pglite";
|
|
import { createPostgresDb } from "../src/infra/db/postgres";
|
|
import { assertDatabaseGuard } from "../src/infra/db/target-guard";
|
|
import type { Db } from "../src/infra/db/types";
|
|
|
|
/**
|
|
* Banco para os scripts operacionais. Em staging e produção `DATABASE_URL` é
|
|
* obrigatória: cair em PGlite com um segredo ausente faria uma migração
|
|
* silenciosa num arquivo local parecer sucesso.
|
|
*/
|
|
export async function openOperationalDb(purpose: DbPurpose): Promise<Db> {
|
|
const databaseUrl = databaseUrlFor(purpose);
|
|
if (databaseUrl) {
|
|
const db = createPostgresDb(databaseUrl, buildPostgresOptions(databaseUrl, purpose));
|
|
try {
|
|
await assertDatabaseGuard(db, purpose, databaseUrl);
|
|
return db;
|
|
} catch (error) {
|
|
await db.close().catch(() => undefined);
|
|
throw error;
|
|
}
|
|
}
|
|
if (isDeployedEnv()) {
|
|
throw new Error("DATABASE_URL é obrigatória fora do PGlite de development.");
|
|
}
|
|
const dataDir = path.join(process.cwd(), ".data", "pglite");
|
|
const { mkdir } = await import("node:fs/promises");
|
|
await mkdir(dataDir, { recursive: true });
|
|
return createFileDb(dataDir);
|
|
}
|
|
|
|
/** Recusa configuração inválida antes de tocar em qualquer recurso externo. */
|
|
export function bootstrapScript(name: string): void {
|
|
exitOnInvalidConfig(process.env);
|
|
console.log(`[${name}] ambiente=${adapterEnv()} shim_identidade=${identityShimEnabled() ? "sim" : "nao"}`);
|
|
}
|
|
|
|
export function describeTarget(): string {
|
|
return process.env.DATABASE_URL ? "postgres" : "pglite local";
|
|
}
|
|
|
|
/**
|
|
* Executa o corpo do script.
|
|
*
|
|
* Existe para que nenhum script use top-level await: `tsx` compila `.ts` como
|
|
* CommonJS quando o `package.json` não declara `"type": "module"`, e aí top-level
|
|
* await é erro de transformação. Isso impedia rodar os scripts com
|
|
* `node --import tsx`, que é a forma que entrega SIGTERM ao nosso processo — o
|
|
* CLI do `tsx` cria um processo filho e o sinal não chega ao handler.
|
|
*/
|
|
export function runScript(name: string, body: () => Promise<void>): void {
|
|
body().then(
|
|
() => {
|
|
process.exitCode ??= 0;
|
|
},
|
|
(error: unknown) => {
|
|
console.error(`[${name}] ${scrubDbError(error)}`);
|
|
process.exit(1);
|
|
},
|
|
);
|
|
}
|
|
|
|
/** Abre o banco, executa e fecha, mesmo em caso de falha. */
|
|
export function runWithDb(name: string, purpose: DbPurpose, body: (db: Db) => Promise<void>): void {
|
|
runScript(name, async () => {
|
|
const db = await openOperationalDb(purpose);
|
|
try {
|
|
await body(db);
|
|
} finally {
|
|
await db.close().catch(() => undefined);
|
|
}
|
|
});
|
|
}
|