everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
80 lines (78 loc) • 4.38 kB
JavaScript
import { getMigrationStorage, pluginMigrationSlug } from "./core.mjs";
import { createDatabaseDriver, pluginSchemaName } from "./driver.mjs";
import { DatabaseError } from "./errors.mjs";
import { detectDrift, runMigrations } from "./run-migrations.mjs";
import { Effect, Layer } from "effect";
//#region src/db/layer.ts
/**
* The plugin author's whole database surface (ADR 0033): one call that
* acquires the driver against the plugin's lifecycle scope, applies
* migrations on boot (journal-scoped advisory lock, ADR 0019), detects
* drift, and provides the typed drizzle database on the **plugin-owned tag**
* — the swap seam. A different engine, a different drizzle setup, or a
* totally different storage situation is the same interface: provide any
* `Layer` for your own tag and nothing else in the plugin changes.
*
* `namespace: true` (the default) scopes the workspace into the
* `plugin_<slug>` schema on the shared database; `namespace: false` keeps
* the public topology (the api shell, or a dedicated database like auth's).
* `migrations` is the workspace-bound loader Effect (`loadMigrations` from
* this package, instantiated with the workspace's `import.meta.dirname` and
* its bundle's `virtual:drizzle-migrations.sql` module); omit it to skip
* boot migration entirely.
*/
function databaseLayer(tag, url, options) {
return Layer.effect(tag, Effect.gen(function* () {
const { pluginId, schema, migrations, namespace = true } = options;
const slug = pluginMigrationSlug(pluginId);
const schemaName = namespace ? pluginSchemaName(pluginId) : void 0;
const driver = yield* Effect.acquireRelease(Effect.tryPromise({
try: () => createDatabaseDriver(url, schema, schemaName),
catch: (cause) => new DatabaseError({
stage: "driver",
cause
})
}), (driver) => Effect.tryPromise({
try: () => driver.close(),
catch: (cause) => new DatabaseError({
stage: "close",
cause
})
}).pipe(Effect.ignore));
if (!migrations) return driver.db;
const storage = getMigrationStorage(slug);
const { migrations: loaded, source } = yield* migrations;
if (loaded.length === 0) yield* Effect.logWarning(`[Database] No migrations found (source: ${source}) — schema may be missing`);
else {
const applied = (yield* runMigrations(driver.db, loaded, {
journal: storage,
schemaName
})).applied;
if (applied === 0) yield* Effect.logInfo(`[Database] Schema up to date (0 migrations needed, source: ${source})`);
else yield* Effect.logInfo(`[Database] Applied ${applied}/${loaded.length} migration(s) (source: ${source}, journal: ${storage.schema}.${storage.table}, schema: ${schemaName ?? "public"})`);
const drift = yield* detectDrift(driver.db, loaded, storage, schemaName);
if (drift.status === "healthy" || drift.status === "untracked-existing-schema") yield* Effect.logInfo(`[Database] Ready`);
else if (drift.status === "drift-safe-repair") {
yield* Effect.logWarning(`[Database] ⚠️ Migration drift detected: ${drift.missingTables.length} expected table(s) missing: ${drift.missingTables.join(", ")}`);
yield* Effect.logWarning(`[Database] Run \`bos db doctor ${storage.slug}\` to diagnose and \`bos db repair ${storage.slug}\` to fix.`);
return yield* new DatabaseError({
stage: "migration",
migrationTag: "drift-safe-repair",
cause: /* @__PURE__ */ new Error(`Migration journal has ${drift.appliedHashes} applied hashes but all ${drift.expectedTables.length} expected table(s) are missing. Run \`bos db repair ${storage.slug}\` to reset the migration history and reapply migrations. Missing tables: ${drift.missingTables.join(", ")}`)
});
}
if (drift.status === "drift-manual") {
yield* Effect.logWarning(`[Database] ⚠️ Partial migration drift detected: ${drift.missingTables.length}/${drift.expectedTables.length} expected table(s) missing.`);
return yield* new DatabaseError({
stage: "migration",
migrationTag: "drift-manual",
cause: /* @__PURE__ */ new Error(`Partial schema drift — ${drift.missingTables.length}/${drift.expectedTables.length} expected table(s) are missing. Run \`bos db doctor ${storage.slug}\` for details. Manual intervention required. Missing tables: ${drift.missingTables.join(", ")}`)
});
}
}
return driver.db;
}));
}
//#endregion
export { databaseLayer };
//# sourceMappingURL=layer.mjs.map