@cardor/agent-harness-kit 1.11.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -10
- package/dist/agent-templates/builder.md +20 -14
- package/dist/agent-templates/explorer.md +11 -8
- package/dist/agent-templates/lead.md +10 -7
- package/dist/agent-templates/reviewer.md +9 -5
- package/dist/{chunk-6PEIJ2D5.js → chunk-JTACLEGM.js} +45 -16
- package/dist/chunk-JTACLEGM.js.map +1 -0
- package/dist/chunk-URMVLD2S.js +147 -0
- package/dist/chunk-URMVLD2S.js.map +1 -0
- package/dist/cli.js +770 -407
- package/dist/cli.js.map +1 -1
- package/dist/{db-3OXHRFAR.js → db-L3AADJF5.js} +4 -2
- package/dist/index.d.ts +1 -1
- package/dist/{mysql-THKQOXIS.js → mysql-AUPKARWA.js} +11 -5
- package/dist/mysql-AUPKARWA.js.map +1 -0
- package/dist/{postgres-IOQE32DM.js → postgres-BB4GY4PN.js} +11 -5
- package/dist/postgres-BB4GY4PN.js.map +1 -0
- package/dist/{sqlite-TR4D324R.js → sqlite-5OWKTUUZ.js} +11 -5
- package/dist/sqlite-5OWKTUUZ.js.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-6PEIJ2D5.js.map +0 -1
- package/dist/mysql-THKQOXIS.js.map +0 -1
- package/dist/postgres-IOQE32DM.js.map +0 -1
- package/dist/sqlite-TR4D324R.js.map +0 -1
- /package/dist/{db-3OXHRFAR.js.map → db-L3AADJF5.js.map} +0 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resetAutoincrementSequences
|
|
3
|
+
} from "./chunk-JTACLEGM.js";
|
|
4
|
+
|
|
5
|
+
// src/core/drivers/migrate-actions.ts
|
|
6
|
+
var OLD_SUFFIX = "_old_v2migration";
|
|
7
|
+
var ACTION_TABLES = ["actions", "action_sections", "action_files", "action_tools"];
|
|
8
|
+
var CHILD_TABLES = ["action_sections", "action_files", "action_tools"];
|
|
9
|
+
function oldName(table) {
|
|
10
|
+
return `${table}${OLD_SUFFIX}`;
|
|
11
|
+
}
|
|
12
|
+
async function migrateActionsToIntegerIds(driver, dbType, schemaSql) {
|
|
13
|
+
await resumeIfInterrupted(driver, dbType);
|
|
14
|
+
if (!await needsMigration(driver, dbType)) return;
|
|
15
|
+
console.log("[agent-harness-kit] Migrating actions table to integer ids (one-time, automatic) \u2014 do not interrupt.");
|
|
16
|
+
if (dbType === "mysql") {
|
|
17
|
+
await runMigration(driver, dbType, schemaSql);
|
|
18
|
+
} else {
|
|
19
|
+
await driver.transaction((tx) => runMigration(tx, dbType, schemaSql));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function resumeIfInterrupted(driver, dbType) {
|
|
23
|
+
const leftoverTables = [];
|
|
24
|
+
for (const table of ACTION_TABLES) {
|
|
25
|
+
if (await tableExists(driver, dbType, oldName(table))) leftoverTables.push(table);
|
|
26
|
+
}
|
|
27
|
+
if (leftoverTables.length === 0) return;
|
|
28
|
+
console.log("[agent-harness-kit] Detected leftover tables from an interrupted actions-table migration \u2014 resuming.");
|
|
29
|
+
if (await migrationDataComplete(driver, dbType, leftoverTables)) {
|
|
30
|
+
for (const table of leftoverTables) {
|
|
31
|
+
await driver.execRaw(`DROP TABLE IF EXISTS ${oldName(table)}`);
|
|
32
|
+
}
|
|
33
|
+
await resetAutoincrementSequences(driver, dbType);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const table of leftoverTables) {
|
|
37
|
+
await driver.execRaw(`DROP TABLE IF EXISTS ${table}`);
|
|
38
|
+
await driver.execRaw(`ALTER TABLE ${oldName(table)} RENAME TO ${table}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function migrationDataComplete(driver, dbType, tablesWithBackup) {
|
|
42
|
+
for (const table of tablesWithBackup) {
|
|
43
|
+
if (!await tableExists(driver, dbType, table)) return false;
|
|
44
|
+
const liveCount = await countRows(driver, table);
|
|
45
|
+
const oldCount = await countRows(driver, oldName(table));
|
|
46
|
+
if (liveCount !== oldCount) return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
async function countRows(driver, table) {
|
|
51
|
+
const row = await driver.queryOne(`SELECT COUNT(*) as count FROM ${table}`);
|
|
52
|
+
return Number(row?.count ?? 0);
|
|
53
|
+
}
|
|
54
|
+
async function needsMigration(driver, dbType) {
|
|
55
|
+
if (dbType === "sqlite") {
|
|
56
|
+
const cols = await driver.query(`PRAGMA table_info(actions)`);
|
|
57
|
+
const idCol = cols.find((c) => c.name === "id");
|
|
58
|
+
return idCol?.type?.toUpperCase() === "TEXT";
|
|
59
|
+
}
|
|
60
|
+
if (dbType === "postgres") {
|
|
61
|
+
const row2 = await driver.queryOne(
|
|
62
|
+
`SELECT data_type FROM information_schema.columns WHERE table_name = 'actions' AND column_name = 'id'`
|
|
63
|
+
);
|
|
64
|
+
return row2?.data_type === "text";
|
|
65
|
+
}
|
|
66
|
+
const row = await driver.queryOne(
|
|
67
|
+
`SELECT DATA_TYPE as data_type FROM information_schema.columns WHERE TABLE_NAME = 'actions' AND COLUMN_NAME = 'id' AND TABLE_SCHEMA = DATABASE()`
|
|
68
|
+
);
|
|
69
|
+
return row?.data_type === "varchar";
|
|
70
|
+
}
|
|
71
|
+
async function tableExists(driver, dbType, table) {
|
|
72
|
+
if (dbType === "sqlite") {
|
|
73
|
+
const row2 = await driver.queryOne(
|
|
74
|
+
`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`,
|
|
75
|
+
[table]
|
|
76
|
+
);
|
|
77
|
+
return !!row2;
|
|
78
|
+
}
|
|
79
|
+
if (dbType === "postgres") {
|
|
80
|
+
const row2 = await driver.queryOne(
|
|
81
|
+
`SELECT table_name FROM information_schema.tables WHERE table_name = ?`,
|
|
82
|
+
[table]
|
|
83
|
+
);
|
|
84
|
+
return !!row2;
|
|
85
|
+
}
|
|
86
|
+
const row = await driver.queryOne(
|
|
87
|
+
`SELECT TABLE_NAME as table_name FROM information_schema.tables WHERE TABLE_NAME = ? AND TABLE_SCHEMA = DATABASE()`,
|
|
88
|
+
[table]
|
|
89
|
+
);
|
|
90
|
+
return !!row;
|
|
91
|
+
}
|
|
92
|
+
async function runMigration(driver, dbType, schemaSql) {
|
|
93
|
+
const oldOrder = await driver.query(`SELECT id FROM actions ORDER BY created_at, id`);
|
|
94
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
95
|
+
oldOrder.forEach((row, i) => idMap.set(row.id, i + 1));
|
|
96
|
+
if (dbType === "mysql") {
|
|
97
|
+
const renames = ACTION_TABLES.map((table) => `${table} TO ${oldName(table)}`).join(", ");
|
|
98
|
+
await driver.execRaw(`RENAME TABLE ${renames}`);
|
|
99
|
+
} else {
|
|
100
|
+
for (const table of ACTION_TABLES) {
|
|
101
|
+
await driver.execRaw(`ALTER TABLE ${table} RENAME TO ${oldName(table)}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
await applySchema(driver, dbType, schemaSql);
|
|
105
|
+
const oldActions = await driver.query(`SELECT * FROM ${oldName("actions")} ORDER BY created_at, id`);
|
|
106
|
+
for (const row of oldActions) {
|
|
107
|
+
const newId = idMap.get(row.id);
|
|
108
|
+
await driver.exec(
|
|
109
|
+
`INSERT INTO actions (id, task_id, agent, status, created_at, completed_at, summary) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
110
|
+
[newId, row.task_id, row.agent, row.status, row.created_at, row.completed_at, row.summary]
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
await copyChildTable(driver, "action_sections", idMap, ["id", "action_id", "section_type", "content", "created_at"]);
|
|
114
|
+
await copyChildTable(driver, "action_files", idMap, ["id", "action_id", "file_path", "operation", "notes"]);
|
|
115
|
+
await copyChildTable(driver, "action_tools", idMap, ["id", "action_id", "tool_name", "args_json", "result_summary", "called_at"]);
|
|
116
|
+
for (const table of [...CHILD_TABLES, "actions"]) {
|
|
117
|
+
await driver.execRaw(`DROP TABLE IF EXISTS ${oldName(table)}`);
|
|
118
|
+
}
|
|
119
|
+
await resetAutoincrementSequences(driver, dbType);
|
|
120
|
+
}
|
|
121
|
+
async function copyChildTable(driver, table, idMap, columns) {
|
|
122
|
+
const oldRows = await driver.query(`SELECT * FROM ${oldName(table)} ORDER BY id`);
|
|
123
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
124
|
+
for (const row of oldRows) {
|
|
125
|
+
const newActionId = idMap.get(row.action_id);
|
|
126
|
+
if (newActionId === void 0) {
|
|
127
|
+
throw new Error(`actions migration: ${table} row ${String(row.id)} references unknown action_id ${String(row.action_id)}`);
|
|
128
|
+
}
|
|
129
|
+
const values = columns.map((c) => c === "action_id" ? newActionId : row[c]);
|
|
130
|
+
await driver.exec(`INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`, values);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function applySchema(driver, dbType, schemaSql) {
|
|
134
|
+
if (dbType === "mysql") {
|
|
135
|
+
const statements = schemaSql.split(";").map((s) => s.trim()).filter(Boolean);
|
|
136
|
+
for (const stmt of statements) {
|
|
137
|
+
await driver.execRaw(stmt);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
await driver.execRaw(schemaSql);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export {
|
|
145
|
+
migrateActionsToIntegerIds
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=chunk-URMVLD2S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/drivers/migrate-actions.ts"],"sourcesContent":["import { resetAutoincrementSequences } from '../db'\n\nimport type { DBDriver } from './types'\n\n/** Suffix used to rename the pre-migration (UUID/TEXT id) `actions` table\n * and its 3 FK children out of the way while the new autoincrement-INTEGER\n * shape is created and populated. Also the marker `resumeIfInterrupted()`\n * looks for on every startup, so a migration interrupted mid-way (crash,\n * kill -9 — a real risk on MySQL, whose DDL isn't transactional) is\n * detected and cleanly retried rather than silently left half-done. */\nconst OLD_SUFFIX = '_old_v2migration'\n\nconst ACTION_TABLES = ['actions', 'action_sections', 'action_files', 'action_tools'] as const\nconst CHILD_TABLES = ['action_sections', 'action_files', 'action_tools'] as const\n\ntype DbType = 'sqlite' | 'postgres' | 'mysql'\n\nfunction oldName(table: string): string {\n return `${table}${OLD_SUFFIX}`\n}\n\n/** Task #73: migrates `actions.id` (and every `action_id` FK) from a\n * TEXT/VARCHAR UUID primary key to an autoincrement INTEGER one, preserving\n * every existing row — `.harness/harness.db` is this tool's source of\n * truth, so a silent reset is not acceptable here. Runs automatically\n * inside `ensureSchema()` on every server start (same trigger point as the\n * archived_at/updated_at column migrations already in each driver) and is\n * idempotent — a DB already on the new shape is a fast no-op.\n *\n * A plain `ALTER TABLE ... ALTER COLUMN TYPE` isn't possible: existing ids\n * are UUID strings, not castable to integer. This instead does the classic\n * rename-old / recreate-new / copy-remapped / drop-old dance, building the\n * UUID -> sequential-integer mapping in application memory (this is a\n * single project's local action log — small enough that this is simpler\n * and safer than a pure-SQL remap per engine).\n *\n * `schemaSql` is the driver's own SCHEMA constant (same string ensureSchema\n * already runs at startup) — reused both to type-detect the `actions.id`\n * column and to recreate the 4 action tables (+ their indexes) once the old\n * ones are renamed away; the CREATE TABLE/INDEX IF NOT EXISTS statements\n * for tasks/task_acceptance in it are no-ops since those tables are\n * untouched by this migration. */\nexport async function migrateActionsToIntegerIds(driver: DBDriver, dbType: DbType, schemaSql: string): Promise<void> {\n await resumeIfInterrupted(driver, dbType)\n\n if (!(await needsMigration(driver, dbType))) return\n\n console.log('[agent-harness-kit] Migrating actions table to integer ids (one-time, automatic) — do not interrupt.')\n\n if (dbType === 'mysql') {\n // MySQL DDL (RENAME/CREATE/DROP TABLE) auto-commits statement-by-statement,\n // so wrapping this in a transaction would be a false guarantee, not a real\n // one. Safety instead comes from resumeIfInterrupted() above, which\n // detects and cleanly retries a migration a crash left half-done.\n await runMigration(driver, dbType, schemaSql)\n } else {\n await driver.transaction((tx) => runMigration(tx, dbType, schemaSql))\n }\n}\n\n/** Detects leftover `*_old_v2migration` tables from a migration interrupted\n * mid-way. Checked uniformly on every engine for one consistent, testable\n * code path, though it's a hard requirement (not just hygiene) for MySQL\n * specifically, since its DDL isn't transactional.\n *\n * IMPORTANT: the rename-away step (2) and the old-table-drop step (6) are\n * each a loop of 4 *separate*, non-atomic DDL statements — a crash can\n * leave any subset of the 4 `_old_v2migration` tables present. This must\n * NOT assume \"if `actions_old_v2migration` exists, all 4 do\" (that was the\n * original, buggy version of this function — see task #73 review). Two\n * concrete failure modes that assumption caused:\n * 1. Crash mid-rename (only `actions` renamed so far) — the other 3\n * tables are still LIVE under their original names with real data.\n * Unconditionally dropping \"whatever sits under the live names\" would\n * destroy that live data with no backup to restore from.\n * 2. Crash mid-cleanup (children's `_old` tables already dropped,\n * `actions_old_v2migration` not yet dropped) — the live child tables\n * at that point already hold the fully-migrated, correct data.\n * Unconditionally dropping them (because `actions_old_v2migration`\n * still exists) would destroy that data with no `_old` counterpart\n * left to restore from.\n *\n * Correct recovery strategy, decided per table AND checked for\n * data-copy-completeness before touching anything:\n * - If, for every table that still has an `_old` backup, the LIVE table\n * already holds exactly as many rows as its `_old` backup (i.e. the\n * copy step fully finished before the crash — we were interrupted only\n * during cleanup), the `_old` tables are pure leftovers: drop them and\n * touch nothing else.\n * - Otherwise (at least one table's copy is missing or partial), this is\n * an all-or-nothing redo: for every table that has an intact `_old`\n * backup, drop whatever (possibly partial, possibly nonexistent) live\n * table sits under the real name and restore the backup, so the\n * migration below redoes the whole thing cleanly from scratch. A table\n * that was never renamed away (no `_old` counterpart at all) is left\n * completely untouched — it is never safe to drop a live table we have\n * no backup for. */\nasync function resumeIfInterrupted(driver: DBDriver, dbType: DbType): Promise<void> {\n const leftoverTables: string[] = []\n for (const table of ACTION_TABLES) {\n if (await tableExists(driver, dbType, oldName(table))) leftoverTables.push(table)\n }\n if (leftoverTables.length === 0) return\n\n console.log('[agent-harness-kit] Detected leftover tables from an interrupted actions-table migration — resuming.')\n\n if (await migrationDataComplete(driver, dbType, leftoverTables)) {\n // Every table with a surviving `_old` backup already has its full copy\n // live — we crashed only during step-6 cleanup. Finish dropping the\n // leftovers; never touch the (already correct) live tables.\n for (const table of leftoverTables) {\n await driver.execRaw(`DROP TABLE IF EXISTS ${oldName(table)}`)\n }\n // Cleanup may have been interrupted before the final sequence-sync step\n // too — cheap and idempotent to redo here.\n await resetAutoincrementSequences(driver, dbType)\n return\n }\n\n // Incomplete copy somewhere — safe all-or-nothing redo: only ever touch a\n // table that has an intact `_old` backup to fall back to.\n for (const table of leftoverTables) {\n await driver.execRaw(`DROP TABLE IF EXISTS ${table}`)\n await driver.execRaw(`ALTER TABLE ${oldName(table)} RENAME TO ${table}`)\n }\n}\n\n/** True only if, for every table in `tablesWithBackup`, the live table\n * exists and holds exactly as many rows as its `_old` backup — i.e. the\n * data-copy steps (4/5) fully completed for all of them before whatever\n * interrupted the migration. A single incomplete/missing live table makes\n * this false, which triggers the safe \"restore and redo\" path instead of\n * risking treating a partial copy as done. */\nasync function migrationDataComplete(driver: DBDriver, dbType: DbType, tablesWithBackup: string[]): Promise<boolean> {\n for (const table of tablesWithBackup) {\n if (!(await tableExists(driver, dbType, table))) return false\n const liveCount = await countRows(driver, table)\n const oldCount = await countRows(driver, oldName(table))\n if (liveCount !== oldCount) return false\n }\n return true\n}\n\nasync function countRows(driver: DBDriver, table: string): Promise<number> {\n const row = await driver.queryOne<{ count: number }>(`SELECT COUNT(*) as count FROM ${table}`)\n return Number(row?.count ?? 0)\n}\n\nasync function needsMigration(driver: DBDriver, dbType: DbType): Promise<boolean> {\n if (dbType === 'sqlite') {\n const cols = await driver.query<{ name: string; type: string }>(`PRAGMA table_info(actions)`)\n const idCol = cols.find((c) => c.name === 'id')\n return idCol?.type?.toUpperCase() === 'TEXT'\n }\n if (dbType === 'postgres') {\n const row = await driver.queryOne<{ data_type: string }>(\n `SELECT data_type FROM information_schema.columns WHERE table_name = 'actions' AND column_name = 'id'`,\n )\n return row?.data_type === 'text'\n }\n const row = await driver.queryOne<{ data_type: string }>(\n `SELECT DATA_TYPE as data_type FROM information_schema.columns WHERE TABLE_NAME = 'actions' AND COLUMN_NAME = 'id' AND TABLE_SCHEMA = DATABASE()`,\n )\n return row?.data_type === 'varchar'\n}\n\nasync function tableExists(driver: DBDriver, dbType: DbType, table: string): Promise<boolean> {\n if (dbType === 'sqlite') {\n const row = await driver.queryOne<{ name: string }>(\n `SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`,\n [table],\n )\n return !!row\n }\n if (dbType === 'postgres') {\n const row = await driver.queryOne<{ table_name: string }>(\n `SELECT table_name FROM information_schema.tables WHERE table_name = ?`,\n [table],\n )\n return !!row\n }\n const row = await driver.queryOne<{ table_name: string }>(\n `SELECT TABLE_NAME as table_name FROM information_schema.tables WHERE TABLE_NAME = ? AND TABLE_SCHEMA = DATABASE()`,\n [table],\n )\n return !!row\n}\n\ninterface OldActionRow {\n id: string\n task_id: number\n agent: string\n status: string\n created_at: string\n completed_at: string | null\n summary: string | null\n}\n\nasync function runMigration(driver: DBDriver, dbType: DbType, schemaSql: string): Promise<void> {\n // 1. Build the UUID -> sequential-integer map, in creation order.\n const oldOrder = await driver.query<{ id: string }>(`SELECT id FROM actions ORDER BY created_at, id`)\n const idMap = new Map<string, number>()\n oldOrder.forEach((row, i) => idMap.set(row.id, i + 1))\n\n // 2. Rename the 4 old-shape tables out of the way. MySQL supports a\n // single multi-table RENAME TABLE statement that it guarantees is\n // atomic — use it there to close the partial-rename crash window\n // entirely (MySQL's DDL doesn't auto-commit *within* this one\n // statement, unlike a loop of 4 separate ALTER TABLE statements).\n // sqlite/postgres don't support multi-table rename in one statement,\n // but don't need to: this whole function runs inside `driver.transaction()`\n // for those two engines (see migrateActionsToIntegerIds above), so a\n // crash mid-loop there rolls back to nothing-renamed, not partial.\n // resumeIfInterrupted() above is still the last line of defense on all\n // engines regardless.\n if (dbType === 'mysql') {\n const renames = ACTION_TABLES.map((table) => `${table} TO ${oldName(table)}`).join(', ')\n await driver.execRaw(`RENAME TABLE ${renames}`)\n } else {\n for (const table of ACTION_TABLES) {\n await driver.execRaw(`ALTER TABLE ${table} RENAME TO ${oldName(table)}`)\n }\n }\n\n // 3. Recreate them fresh (new INTEGER-id shape) + their indexes, reusing\n // the driver's own schema definition.\n await applySchema(driver, dbType, schemaSql)\n\n // 4. Copy `actions` rows with remapped integer ids.\n const oldActions = await driver.query<OldActionRow>(`SELECT * FROM ${oldName('actions')} ORDER BY created_at, id`)\n for (const row of oldActions) {\n const newId = idMap.get(row.id)!\n await driver.exec(\n `INSERT INTO actions (id, task_id, agent, status, created_at, completed_at, summary) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n [newId, row.task_id, row.agent, row.status, row.created_at, row.completed_at, row.summary],\n )\n }\n\n // 5. Copy the 3 child tables, keeping their own id but remapping action_id.\n await copyChildTable(driver, 'action_sections', idMap, ['id', 'action_id', 'section_type', 'content', 'created_at'])\n await copyChildTable(driver, 'action_files', idMap, ['id', 'action_id', 'file_path', 'operation', 'notes'])\n await copyChildTable(driver, 'action_tools', idMap, ['id', 'action_id', 'tool_name', 'args_json', 'result_summary', 'called_at'])\n\n // 6. Drop the old renamed tables — children first so FK constraints never block it.\n for (const table of [...CHILD_TABLES, 'actions']) {\n await driver.execRaw(`DROP TABLE IF EXISTS ${oldName(table)}`)\n }\n\n // 7. Sync `actions`' autoincrement/serial counter to the ids just inserted\n // (reuses the exact same logic importFullExport() relies on; harmlessly\n // re-syncs the other autoincrement tables too — idempotent).\n await resetAutoincrementSequences(driver, dbType)\n}\n\nasync function copyChildTable(\n driver: DBDriver,\n table: (typeof CHILD_TABLES)[number],\n idMap: Map<string, number>,\n columns: string[],\n): Promise<void> {\n const oldRows = await driver.query<Record<string, unknown>>(`SELECT * FROM ${oldName(table)} ORDER BY id`)\n const placeholders = columns.map(() => '?').join(', ')\n for (const row of oldRows) {\n const newActionId = idMap.get(row.action_id as string)\n if (newActionId === undefined) {\n throw new Error(`actions migration: ${table} row ${String(row.id)} references unknown action_id ${String(row.action_id)}`)\n }\n const values = columns.map((c) => (c === 'action_id' ? newActionId : row[c]))\n await driver.exec(`INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`, values)\n }\n}\n\n/** Applies a driver's full multi-statement SCHEMA string. MySQL's mysql2\n * driver doesn't support multi-statement execution by default, so it must\n * be split and run statement-by-statement — the identical approach each\n * driver's own ensureSchema() already uses. */\nasync function applySchema(driver: DBDriver, dbType: DbType, schemaSql: string): Promise<void> {\n if (dbType === 'mysql') {\n const statements = schemaSql\n .split(';')\n .map((s) => s.trim())\n .filter(Boolean)\n for (const stmt of statements) {\n await driver.execRaw(stmt)\n }\n } else {\n await driver.execRaw(schemaSql)\n }\n}\n"],"mappings":"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","names":["row"]}
|