Skip to content

Commit b26c218

Browse files
dcramercodex
andcommitted
ref(migrations): Inject the database adapter
Expose the exact host database adapter through MigrationContextV1 instead of reconstructing a partial SQL capability. Rebuild the SQL-heavy frozen migrations without connection setup and driver implementations so migrations retain transformation logic without duplicating database infrastructure. Co-Authored-By: GPT-5.6 Codex <noreply@openai.com>
1 parent efd628c commit b26c218

12 files changed

Lines changed: 67 additions & 612 deletions

File tree

packages/junior-migrations/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ then replaces the empty SQL file with a `MigrationV1` TypeScript scaffold.
3030
Drizzle Kit remains the supported authoring tool. Drizzle ORM's stock
3131
`migrate()` function is not a supported executor for mixed journals because it
3232
requires every entry to have a SQL file. Call `runMigrationJournal` instead and
33-
provide the SQL, state, loader, and locking capabilities owned by the host.
33+
provide the host database adapter, state adapter, and TypeScript loader. The
34+
same database adapter drives the journal ledger and is exposed as
35+
`context.database`, so migration files never own connection or driver setup.
3436

3537
The runner rejects runtime imports of application source, relative modules,
3638
and `@sentry/junior`. External package imports are allowed when the migration

packages/junior-migrations/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ export type { GenerateTypeScriptMigrationOptions } from "./generate";
55
export type { RunMigrationJournalOptions } from "./runner";
66
export type {
77
MigrationContextV1,
8+
MigrationDatabaseAdapter,
89
MigrationJsonValue,
910
MigrationJournalEntry,
1011
MigrationLockV1,
1112
MigrationProgressV1,
1213
MigrationRedisV1,
1314
MigrationRunResult,
14-
MigrationSqlExecutor,
1515
MigrationStateV1,
1616
MigrationV1,
1717
ResolvedMigration,

packages/junior-migrations/src/runner.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type {
22
MigrationContextV1,
3+
MigrationDatabaseAdapter,
34
MigrationRunResult,
4-
MigrationSqlExecutor,
55
MigrationV1,
66
ResolvedMigration,
77
TypeScriptMigrationLoader,
@@ -17,7 +17,7 @@ interface MigrationRow {
1717

1818
interface RunMigrationJournalBaseOptions {
1919
beforeRun?: () => Promise<void>;
20-
executor: MigrationSqlExecutor;
20+
executor: MigrationDatabaseAdapter;
2121
migrationsFolder: string;
2222
migrationsTable: string;
2323
}
@@ -54,7 +54,7 @@ function qualifiedTable(table: string): string {
5454
}
5555

5656
async function ensureMigrationTable(
57-
executor: MigrationSqlExecutor,
57+
executor: MigrationDatabaseAdapter,
5858
table: string,
5959
): Promise<void> {
6060
const qualified = qualifiedTable(table);
@@ -90,7 +90,7 @@ CREATE TABLE IF NOT EXISTS ${qualified} (
9090
}
9191

9292
async function migrationRows(
93-
executor: MigrationSqlExecutor,
93+
executor: MigrationDatabaseAdapter,
9494
table: string,
9595
): Promise<Map<number, MigrationRow>> {
9696
const rows = await executor.query<MigrationRow>(`
@@ -107,7 +107,7 @@ ORDER BY created_at ASC, id ASC
107107
}
108108

109109
async function adoptLegacySqlPrefix(args: {
110-
executor: MigrationSqlExecutor;
110+
executor: MigrationDatabaseAdapter;
111111
migrations: readonly ResolvedMigration[];
112112
rows: Map<number, MigrationRow>;
113113
table: string;
@@ -160,7 +160,7 @@ function migrationV1(value: unknown, tag: string): MigrationV1 {
160160
}
161161

162162
async function runSqlMigration(args: {
163-
executor: MigrationSqlExecutor;
163+
executor: MigrationDatabaseAdapter;
164164
migration: ResolvedMigration;
165165
table: string;
166166
}): Promise<void> {
@@ -179,7 +179,7 @@ async function runSqlMigration(args: {
179179

180180
async function runTypeScriptMigration(args: {
181181
createContext: RunAllMigrationJournalOptions["createContext"];
182-
executor: MigrationSqlExecutor;
182+
executor: MigrationDatabaseAdapter;
183183
loadTypeScript: TypeScriptMigrationLoader;
184184
migration: ResolvedMigration;
185185
row: MigrationRow | undefined;

packages/junior-migrations/src/types.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
/** SQL capabilities available to the mixed migration runner. */
2-
export interface MigrationSqlExecutor {
1+
/** Database adapter used by the mixed migration runner and TypeScript entries. */
2+
export interface MigrationDatabaseAdapter {
3+
db(): unknown;
34
execute(statement: string, parameters?: readonly unknown[]): Promise<void>;
45
query<T = unknown>(
56
statement: string,
67
parameters?: readonly unknown[],
78
): Promise<T[]>;
89
transaction<T>(callback: () => Promise<T>): Promise<T>;
10+
withLock<T>(lockName: string, callback: () => Promise<T>): Promise<T>;
911
withMigrationLock<T>(
1012
migrationTable: string,
1113
callback: () => Promise<T>,
@@ -58,12 +60,10 @@ export type MigrationJsonValue =
5860

5961
/** Permanent capability contract for apiVersion 1 migrations. */
6062
export interface MigrationContextV1 {
63+
database: MigrationDatabaseAdapter;
6164
log(message: string): void;
6265
progress: MigrationProgressV1;
6366
redis?: MigrationRedisV1;
64-
sql: Pick<MigrationSqlExecutor, "execute" | "query" | "transaction"> & {
65-
db(): unknown;
66-
};
6767
state: MigrationStateV1;
6868
}
6969

packages/junior-migrations/tests/runner.test.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { afterEach, describe, expect, it } from "vitest";
55
import { runMigrationJournal } from "../src/runner";
66
import type {
77
MigrationContextV1,
8-
MigrationSqlExecutor,
8+
MigrationDatabaseAdapter,
99
MigrationV1,
1010
} from "../src/types";
1111

@@ -16,7 +16,7 @@ interface StoredRow {
1616
status: string | null;
1717
}
1818

19-
class FakeExecutor implements MigrationSqlExecutor {
19+
class FakeExecutor implements MigrationDatabaseAdapter {
2020
readonly rows = new Map<number, StoredRow>();
2121
readonly statements: string[] = [];
2222

@@ -89,6 +89,10 @@ class FakeExecutor implements MigrationSqlExecutor {
8989
): Promise<T> {
9090
return await callback();
9191
}
92+
93+
async withLock<T>(_lockName: string, callback: () => Promise<T>): Promise<T> {
94+
return await callback();
95+
}
9296
}
9397

9498
function fakeMigrationState(): MigrationContextV1["state"] {
@@ -166,7 +170,7 @@ describe("runMigrationJournal", () => {
166170
createContext: ({ progress }) => ({
167171
log: () => {},
168172
progress,
169-
sql: executor,
173+
database: executor,
170174
state: fakeMigrationState(),
171175
}),
172176
}),
@@ -186,7 +190,7 @@ describe("runMigrationJournal", () => {
186190
createContext: ({ progress }) => ({
187191
log: () => {},
188192
progress,
189-
sql: executor,
193+
database: executor,
190194
state: fakeMigrationState(),
191195
}),
192196
}),
@@ -236,7 +240,7 @@ describe("runMigrationJournal", () => {
236240
}) => ({
237241
log: () => {},
238242
progress,
239-
sql: executor,
243+
database: executor,
240244
state: fakeMigrationState(),
241245
}),
242246
};

packages/junior-scheduler/migrations/0002_scheduler_state_to_sql.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const migration = {
121121
continue;
122122
}
123123
tasks.push(task);
124-
const [stored] = await context.sql.query<{ id: string }>(
124+
const [stored] = await context.database.query<{ id: string }>(
125125
"SELECT id FROM junior_scheduler_tasks WHERE id = $1 LIMIT 1",
126126
[id],
127127
);
@@ -130,7 +130,7 @@ const migration = {
130130
continue;
131131
}
132132
const destination = record(task.destination)!;
133-
await context.sql.execute(
133+
await context.database.execute(
134134
`INSERT INTO junior_scheduler_tasks
135135
(id, team_id, status, next_run_at_ms, run_now_at_ms, created_at_ms, record)
136136
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)
@@ -171,15 +171,15 @@ const migration = {
171171
}
172172

173173
for (const run of runs) {
174-
const [stored] = await context.sql.query<{ id: string }>(
174+
const [stored] = await context.database.query<{ id: string }>(
175175
"SELECT id FROM junior_scheduler_runs WHERE id = $1 LIMIT 1",
176176
[run.id],
177177
);
178178
if (stored) {
179179
existing += 1;
180180
continue;
181181
}
182-
await context.sql.execute(
182+
await context.database.execute(
183183
`INSERT INTO junior_scheduler_runs
184184
(id, task_id, status, scheduled_for_ms, record)
185185
VALUES ($1, $2, $3, $4, $5::jsonb)

0 commit comments

Comments
 (0)