everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
131 lines (129 loc) • 4.73 kB
JavaScript
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
const require_db = require('../db.cjs');
let node_fs = require("node:fs");
let node_path = require("node:path");
let node_crypto = require("node:crypto");
//#region src/cli/db-doctor.ts
function hashFile(raw) {
return (0, node_crypto.createHash)("sha256").update(raw).digest("hex");
}
function readLocalMigrations(workspaceDir) {
const migrationsDir = (0, node_path.resolve)(workspaceDir, "src/db/migrations");
const journalPath = (0, node_path.join)((0, node_path.join)(migrationsDir, "meta"), "_journal.json");
if (!(0, node_fs.existsSync)(journalPath)) return [];
return JSON.parse((0, node_fs.readFileSync)(journalPath, "utf8")).entries.map((entry) => {
const sqlPath = (0, node_path.join)(migrationsDir, `${entry.tag}.sql`);
if (!(0, node_fs.existsSync)(sqlPath)) return {
tag: entry.tag,
hash: "",
sql: []
};
const raw = (0, node_fs.readFileSync)(sqlPath, "utf8");
const sql = raw.split("--> statement-breakpoint").map((s) => s.trim());
return {
tag: entry.tag,
hash: hashFile(raw),
sql
};
});
}
async function diagnosePlugin(info) {
const { Pool } = await Promise.resolve().then(() => require("../node_modules/pg/esm/index.cjs"));
const slug = require_db.pluginMigrationSlug(info.key);
const journalTable = `__drizzle_migrations_${slug}`;
const journalSchema = "drizzle";
const journalRef = `"${journalSchema}"."${journalTable}"`;
const localMigrations = info.workspaceDir ? readLocalMigrations(info.workspaceDir) : [];
const expectedTables = require_db.extractExpectedTables(localMigrations);
const localHashes = localMigrations.map((m) => m.hash).filter(Boolean);
const migrationHashes = localHashes;
const pool = new Pool({
connectionString: info.databaseUrl,
ssl: info.databaseUrl.includes("localhost") || info.databaseUrl.includes("127.0.0.1") ? false : { rejectUnauthorized: false },
max: 1,
connectionTimeoutMillis: 1e4
});
try {
const journalExists = await pool.query(`
SELECT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = '${journalSchema}' AND table_name = '${journalTable}'
) AS exists
`);
let appliedHashCount = 0;
if (journalExists.rows[0]?.exists) appliedHashCount = (await pool.query(`SELECT hash FROM ${journalRef}`)).rows.length;
let missingTables = [];
if (expectedTables.length > 0) {
const tableResult = await pool.query(`
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = ANY($1)
`, [expectedTables]);
const existing = new Set(tableResult.rows.map((r) => r.table_name));
missingTables = expectedTables.filter((t) => !existing.has(t));
}
const legacyCount = await countLegacyHashes(pool, localHashes);
let diagnosis;
if (localMigrations.length === 0) diagnosis = "no-local-migrations";
else if (appliedHashCount === 0 && localHashes.length > 0) diagnosis = legacyCount > 0 ? "legacy-importable" : "unapplied";
else if (missingTables.length === 0) diagnosis = "healthy";
else if (missingTables.length === expectedTables.length) diagnosis = "drift-safe-repair";
else diagnosis = "drift-manual";
const masked = info.databaseUrl.replace(/\/\/[^:]+:[^@]+@/, "//***:***@");
return {
plugin: info.key,
slug,
journalTable,
journalSchema,
dbSecret: info.databaseSecret,
dbUrl: masked,
workspaceDir: info.workspaceDir,
localMigrationCount: localMigrations.length,
appliedHashCount,
expectedTables,
missingTables,
migrationHashes,
diagnosis,
legacyCount
};
} catch (error) {
return {
plugin: info.key,
slug,
journalTable,
journalSchema,
dbSecret: info.databaseSecret,
dbUrl: info.databaseUrl.replace(/\/\/[^:]+:[^@]+@/, "//***:***@"),
workspaceDir: info.workspaceDir,
localMigrationCount: localMigrations.length,
appliedHashCount: 0,
expectedTables,
missingTables: [],
migrationHashes: [],
diagnosis: "error",
legacyCount: 0,
error: error instanceof Error ? error.message : String(error)
};
} finally {
await pool.end().catch(() => {});
}
}
async function countLegacyHashes(pool, localHashes) {
if (localHashes.length === 0) return 0;
const candidates = [{
schema: "drizzle",
table: "__drizzle_migrations"
}, {
schema: "public",
table: "drizzle_migrations"
}];
let total = 0;
for (const c of candidates) try {
const result = await pool.query(`SELECT count(*)::int AS cnt FROM "${c.schema}"."${c.table}" WHERE hash = ANY($1)`, [localHashes]);
total += result.rows[0]?.cnt ?? 0;
} catch {}
return total;
}
//#endregion
exports.diagnosePlugin = diagnosePlugin;
//# sourceMappingURL=db-doctor.cjs.map