Compare commits
1 commit
d377756988
...
5e42ee8869
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e42ee8869 |
3 changed files with 70 additions and 7 deletions
10
README.md
10
README.md
|
|
@ -37,10 +37,12 @@ DATABASE_URL=file:./data/dimensionlab.sqlite
|
|||
|
||||
SQLite files under `data/` are ignored. Drizzle schema lives in
|
||||
`src/lib/server/db/schema.ts`; tracked migrations live in `drizzle/`. Runtime
|
||||
startup applies the checked-in dashboard migrations before reads or writes. The
|
||||
current driver is `bun:sqlite`, which keeps this repo installable in the Bun
|
||||
workflow. The store boundary is isolated so a later Postgres driver can replace
|
||||
the SQLite connection without changing the dashboard model or renderer.
|
||||
startup applies the checked-in dashboard migrations before reads or writes. If
|
||||
the app is launched from outside the repo tree, set `DASHBOARD_MIGRATIONS_DIR`
|
||||
to the tracked migrations directory. The current driver is `bun:sqlite`, which
|
||||
keeps this repo installable in the Bun workflow. The store boundary is isolated
|
||||
so a later Postgres driver can replace the SQLite connection without changing
|
||||
the dashboard model or renderer.
|
||||
Stored dashboard documents pass through a version migration boundary before
|
||||
reads or writes; the MVP supports `dashboard.v1` and fails unsupported versions
|
||||
with an explicit migration error.
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ import {
|
|||
|
||||
const stores: DashboardStore[] = [];
|
||||
const tempRoots: string[] = [];
|
||||
const originalCwd = process.cwd();
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
process.chdir(originalCwd);
|
||||
stores.splice(0).forEach((store) => store.close());
|
||||
tempRoots.splice(0).forEach((path) => rmSync(path, { force: true, recursive: true }));
|
||||
});
|
||||
|
|
@ -165,6 +167,25 @@ describe("dashboard persistence store", () => {
|
|||
seed.id,
|
||||
]);
|
||||
});
|
||||
|
||||
test("applies migrations when launched outside the repository root", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "dimensionlab-dashboard-cwd-"));
|
||||
tempRoots.push(root);
|
||||
process.chdir(root);
|
||||
vi.resetModules();
|
||||
const { createDashboardStore: createStore } = await import("./dashboard-store");
|
||||
const store = createStore({
|
||||
databaseUrl: `file:${join(root, "dashboard.sqlite")}`,
|
||||
});
|
||||
stores.push(store);
|
||||
|
||||
const seed = store.seedDashboardIfEmpty(genericDashboardFixture, {
|
||||
actor: "test-seed",
|
||||
});
|
||||
|
||||
expect(seed.document.metadata.title).toBe("Operations Console");
|
||||
expect(store.getActiveDashboard()?.currentRevisionId).toBe(seed.id);
|
||||
});
|
||||
});
|
||||
|
||||
async function createTestStore() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,52 @@
|
|||
import { resolve } from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
||||
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
|
||||
|
||||
export const DASHBOARD_MIGRATIONS_FOLDER = resolve("drizzle");
|
||||
const MIGRATION_JOURNAL_PATH = join("meta", "_journal.json");
|
||||
|
||||
export function applyDashboardMigrations(
|
||||
database: BunSQLiteDatabase<Record<string, unknown>>,
|
||||
migrationsFolder = DASHBOARD_MIGRATIONS_FOLDER,
|
||||
migrationsFolder = resolveDashboardMigrationsFolder(),
|
||||
) {
|
||||
migrate(database, { migrationsFolder });
|
||||
}
|
||||
|
||||
export function resolveDashboardMigrationsFolder(
|
||||
explicitFolder = process.env.DASHBOARD_MIGRATIONS_DIR,
|
||||
): string {
|
||||
if (explicitFolder) return assertMigrationFolder(resolve(explicitFolder));
|
||||
|
||||
const cwdFolder = findMigrationFolder(process.cwd());
|
||||
if (cwdFolder) return cwdFolder;
|
||||
|
||||
const moduleFolder = findMigrationFolder(dirname(fileURLToPath(import.meta.url)));
|
||||
if (moduleFolder) return moduleFolder;
|
||||
|
||||
throw new Error(
|
||||
"Unable to locate dashboard Drizzle migrations. Set DASHBOARD_MIGRATIONS_DIR.",
|
||||
);
|
||||
}
|
||||
|
||||
function findMigrationFolder(startPath: string): string | null {
|
||||
let currentPath = resolve(startPath);
|
||||
|
||||
while (true) {
|
||||
const candidate = join(currentPath, "drizzle");
|
||||
if (hasMigrationJournal(candidate)) return candidate;
|
||||
|
||||
const parentPath = dirname(currentPath);
|
||||
if (parentPath === currentPath) return null;
|
||||
currentPath = parentPath;
|
||||
}
|
||||
}
|
||||
|
||||
function assertMigrationFolder(folder: string): string {
|
||||
if (hasMigrationJournal(folder)) return folder;
|
||||
throw new Error(`Dashboard migrations not found in ${folder}`);
|
||||
}
|
||||
|
||||
function hasMigrationJournal(folder: string): boolean {
|
||||
return existsSync(join(folder, MIGRATION_JOURNAL_PATH));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue