/**
* `scripts/local-db/20261027_error_lists.sql` を ★ローカルの dwatch_local にだけ★ 流す実行部(指示書BD §8。D-184)。
*
* ★実行方法: `npx tsx scripts/local-db/apply-error-lists.ts`★(そのあと `npm run seed:local` で 131行を入れる)
* ★DATABASE_URL がローカル(localhost / 127.0.0.1)の dwatch_local を指していなければ、接続する前に止まる★
* (`lib/db/local-guard.ts`。シードと同じ歯止め)。`.env.local` の DATABASE_URL がステージングを指している間は、
* 実行するシェルでだけ DATABASE_URL をローカルに向けて渡すこと(process.env は .env.local より優先される)。
* ★ステージング・本番には絶対に流さない★ — スキーマは TeamWorks の領域(CLAUDE.md §2 / §7)。
* `prisma db push` / `migrate` の代わりでもある(db push はローカルにだけある列を DROP する。指示書M 禁止事項3・4)。
*
* ★何度流しても同じ★(SQL 側が IF NOT EXISTS と information_schema の分岐で書いてある)。
* ★接続後にも `DATABASE()` が dwatch_local であることを確かめてから流す★(URL の検査だけに頼らない)。
* ★パスワード・接続文字列は出力しない★(CLAUDE.md §9.3)。
*
* `mariadb` は `@prisma/adapter-mariadb` の依存として入っているドライバ(直接の依存ではない)。
* 複数文をまとめて流すため Prisma ではなくこれを使う。
*/
import dotenv from "dotenv";
dotenv.config({ path: ".env.local" });
dotenv.config();
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import mariadb from "mariadb";
import { assertLocalDwatchDatabaseUrl } from "../../lib/db/local-guard";
const LOCAL_DATABASE_NAME = "dwatch_local";
const SQL_FILE = join(dirname(fileURLToPath(import.meta.url)), "20261027_error_lists.sql");
async function main(): Promise<void> {
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("[apply-error-lists] DATABASE_URL が未設定です(値はこのエラーには出しません)。");
}
assertLocalDwatchDatabaseUrl(databaseUrl);
const url = new URL(databaseUrl);
const connection = await mariadb.createConnection({
host: url.hostname,
port: Number(url.port || "3306"),
user: decodeURIComponent(url.username),
password: decodeURIComponent(url.password),
database: LOCAL_DATABASE_NAME,
multipleStatements: true,
});
try {
const [current] = await connection.query<{ db: string | null }[]>("SELECT DATABASE() AS db");
if (current?.db !== LOCAL_DATABASE_NAME) {
throw new Error(`[apply-error-lists] 接続先の DB が ${LOCAL_DATABASE_NAME} ではありません。中断しました。`);
}
await connection.query(readFileSync(SQL_FILE, "utf8"));
const tables = await connection.query<{ n: bigint | number }[]>(
"SELECT COUNT(*) AS n FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'error_lists'",
[LOCAL_DATABASE_NAME]
);
const columns = await connection.query<{ COLUMN_TYPE: string; IS_NULLABLE: string }[]>(
"SELECT COLUMN_TYPE, IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'robot_error_reports' AND COLUMN_NAME = 'error_list_id'",
[LOCAL_DATABASE_NAME]
);
if (Number(tables[0]?.n ?? 0) !== 1 || columns.length !== 1) {
throw new Error("[apply-error-lists] 流したあとも error_lists または error_list_id が見つかりません。");
}
console.log(
`[apply-error-lists] ${LOCAL_DATABASE_NAME}: error_lists あり / robot_error_reports.error_list_id = ${columns[0].COLUMN_TYPE}(NULL 可: ${columns[0].IS_NULLABLE})`
);
} finally {
await connection.end();
}
}
main().catch((error: unknown) => {
console.error(error instanceof Error ? error.message : error);
process.exit(1);
});