build: regenerate dist/ for v0.1.0
All checks were successful
ci / test (pull_request) Successful in 17s

Pre-compiled ESM JS + .d.ts from tsconfig.build.json. Consumers install
via 'git+https://gitea.bchen.dev/...#v0.1.0' so the built output must
travel with the source. CI verifies in-sync via 'git diff --exit-code
dist/'.
This commit is contained in:
2026-05-12 02:17:02 -07:00
parent 5096ca174f
commit accd3ca340
6 changed files with 234 additions and 0 deletions

113
dist/migrate.js vendored Normal file
View File

@@ -0,0 +1,113 @@
import { readdirSync, readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { createHash } from 'node:crypto';
const FILENAME_RE = /^(\d{4,})_([a-z0-9_]+)\.sql$/;
const GENESIS_VERSION = '0001';
const SCHEMA_MIGRATIONS_DDL = `
CREATE TABLE IF NOT EXISTS schema_migrations (
version TEXT PRIMARY KEY,
name TEXT NOT NULL,
checksum TEXT NOT NULL,
applied_at INTEGER NOT NULL
);
`;
function sha256Hex(body) {
return createHash('sha256').update(body).digest('hex');
}
function tableExists(db, name) {
const row = db
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name = ?`)
.get(name);
return row !== undefined;
}
export function listMigrations(migrationsDir) {
if (!existsSync(migrationsDir)) {
throw new Error(`migrations directory not found: ${migrationsDir}`);
}
const entries = readdirSync(migrationsDir).filter((f) => f.endsWith('.sql'));
const files = [];
for (const filename of entries.sort()) {
const match = FILENAME_RE.exec(filename);
if (!match) {
throw new Error(`invalid migration filename: ${filename} (must match NNNN_name.sql)`);
}
const version = match[1];
const name = filename.replace(/\.sql$/, '');
const body = readFileSync(join(migrationsDir, filename), 'utf8');
files.push({ version, name, checksum: sha256Hex(body), body });
}
return files;
}
export function readAppliedRows(db) {
if (!tableExists(db, 'schema_migrations')) {
return [];
}
return db
.prepare(`SELECT version, name, checksum, applied_at FROM schema_migrations ORDER BY version`)
.all();
}
export function applyMigrations(db, migrationsDir, opts = {}) {
const log = opts.logger ?? (() => { });
const probeTable = opts.genesisProbeTable ?? 'users';
const files = listMigrations(migrationsDir);
if (files.length === 0) {
return { applied: 0, pending: 0, alreadyApplied: 0, stamped: [] };
}
const schemaMigrationsExists = tableExists(db, 'schema_migrations');
const stamped = [];
const genesisFile = files.find((f) => f.version === GENESIS_VERSION);
const shouldStampGenesis = !schemaMigrationsExists &&
genesisFile !== undefined &&
(opts.stampGenesis === true || tableExists(db, probeTable));
if (shouldStampGenesis && genesisFile) {
db.exec(SCHEMA_MIGRATIONS_DDL);
const reason = opts.stampGenesis
? 'stampGenesis option set'
: `detected pre-existing '${probeTable}' table`;
log(`WARN genesis-stamp: marked ${genesisFile.name} as applied without executing (${reason})`);
db.prepare(`INSERT INTO schema_migrations (version, name, checksum, applied_at) VALUES (?, ?, ?, ?)`).run(genesisFile.version, genesisFile.name, genesisFile.checksum, Date.now());
stamped.push(genesisFile.version);
}
else {
db.exec(SCHEMA_MIGRATIONS_DDL);
}
const appliedRows = readAppliedRows(db);
const appliedByVersion = new Map(appliedRows.map((r) => [r.version, r]));
let applied = 0;
let alreadyApplied = 0;
const insertRow = db.prepare(`INSERT INTO schema_migrations (version, name, checksum, applied_at) VALUES (?, ?, ?, ?)`);
for (const file of files) {
const existing = appliedByVersion.get(file.version);
if (existing) {
if (existing.checksum !== file.checksum) {
throw new Error(`migration checksum mismatch: ${file.name}.sql (expected ${existing.checksum}, got ${file.checksum})`);
}
alreadyApplied += 1;
continue;
}
const runMigration = db.transaction(() => {
db.exec(file.body);
insertRow.run(file.version, file.name, file.checksum, Date.now());
});
runMigration();
log(`applied migration ${file.name}`);
applied += 1;
}
return { applied, pending: 0, alreadyApplied, stamped };
}
export function stampMigration(db, migrationsDir, version) {
const files = listMigrations(migrationsDir);
const file = files.find((f) => f.version === version);
if (!file) {
throw new Error(`migration not found for version: ${version}`);
}
db.exec(SCHEMA_MIGRATIONS_DDL);
const existing = db
.prepare(`SELECT version, name, checksum, applied_at FROM schema_migrations WHERE version = ?`)
.get(file.version);
if (existing) {
throw new Error(`migration already applied: ${file.name}`);
}
db.prepare(`INSERT INTO schema_migrations (version, name, checksum, applied_at) VALUES (?, ?, ?, ?)`).run(file.version, file.name, file.checksum, Date.now());
return file;
}