everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 7.28 kB
Source Map (JSON)
{"version":3,"file":"layer.mjs","names":[],"sources":["../../src/db/layer.ts"],"sourcesContent":["import type { PgDatabase, PgQueryResultHKT } from \"drizzle-orm/pg-core\";\nimport { type Context, Effect, Layer } from \"effect\";\nimport { getMigrationStorage, pluginMigrationSlug } from \"./core\";\nimport { createDatabaseDriver, pluginSchemaName } from \"./driver\";\nimport { DatabaseError } from \"./errors\";\nimport { detectDrift, type LoadedMigrations, runMigrations } from \"./run-migrations\";\n\n/**\n * The plugin author's whole database surface (ADR 0033): one call that\n * acquires the driver against the plugin's lifecycle scope, applies\n * migrations on boot (journal-scoped advisory lock, ADR 0019), detects\n * drift, and provides the typed drizzle database on the **plugin-owned tag**\n * — the swap seam. A different engine, a different drizzle setup, or a\n * totally different storage situation is the same interface: provide any\n * `Layer` for your own tag and nothing else in the plugin changes.\n *\n * `namespace: true` (the default) scopes the workspace into the\n * `plugin_<slug>` schema on the shared database; `namespace: false` keeps\n * the public topology (the api shell, or a dedicated database like auth's).\n * `migrations` is the workspace-bound loader Effect (`loadMigrations` from\n * this package, instantiated with the workspace's `import.meta.dirname` and\n * its bundle's `virtual:drizzle-migrations.sql` module); omit it to skip\n * boot migration entirely.\n */\nexport function databaseLayer<Identifier, TSchema extends Record<string, unknown>>(\n tag: Context.Service<Identifier, PgDatabase<PgQueryResultHKT, TSchema>>,\n url: string,\n options: {\n pluginId: string;\n schema: TSchema;\n migrations?: Effect.Effect<LoadedMigrations, DatabaseError>;\n namespace?: boolean;\n },\n): Layer.Layer<Identifier, DatabaseError> {\n return Layer.effect(\n tag,\n Effect.gen(function* () {\n const { pluginId, schema, migrations, namespace = true } = options;\n const slug = pluginMigrationSlug(pluginId);\n const schemaName = namespace ? pluginSchemaName(pluginId) : undefined;\n\n const driver = yield* Effect.acquireRelease(\n Effect.tryPromise({\n try: () => createDatabaseDriver(url, schema, schemaName),\n catch: (cause) => new DatabaseError({ stage: \"driver\", cause }),\n }),\n (driver) =>\n Effect.tryPromise({\n try: () => driver.close(),\n catch: (cause) => new DatabaseError({ stage: \"close\", cause }),\n }).pipe(Effect.ignore),\n );\n\n if (!migrations) {\n return driver.db;\n }\n\n const storage = getMigrationStorage(slug);\n const { migrations: loaded, source } = yield* migrations;\n\n if (loaded.length === 0) {\n yield* Effect.logWarning(\n `[Database] No migrations found (source: ${source}) — schema may be missing`,\n );\n } else {\n const report = yield* runMigrations(driver.db, loaded, {\n journal: storage,\n schemaName,\n });\n const applied = report.applied;\n\n if (applied === 0) {\n yield* Effect.logInfo(\n `[Database] Schema up to date (0 migrations needed, source: ${source})`,\n );\n } else {\n yield* Effect.logInfo(\n `[Database] Applied ${applied}/${loaded.length} migration(s) (source: ${source}, journal: ${storage.schema}.${storage.table}, schema: ${schemaName ?? \"public\"})`,\n );\n }\n\n const drift = yield* detectDrift(driver.db, loaded, storage, schemaName);\n if (drift.status === \"healthy\" || drift.status === \"untracked-existing-schema\") {\n yield* Effect.logInfo(`[Database] Ready`);\n } else if (drift.status === \"drift-safe-repair\") {\n yield* Effect.logWarning(\n `[Database] ⚠️ Migration drift detected: ${drift.missingTables.length} expected table(s) missing: ${drift.missingTables.join(\", \")}`,\n );\n yield* Effect.logWarning(\n `[Database] Run \\`bos db doctor ${storage.slug}\\` to diagnose and \\`bos db repair ${storage.slug}\\` to fix.`,\n );\n return yield* new DatabaseError({\n stage: \"migration\",\n migrationTag: \"drift-safe-repair\",\n cause: new Error(\n `Migration journal has ${drift.appliedHashes} applied hashes but all ${drift.expectedTables.length} expected table(s) are missing. ` +\n `Run \\`bos db repair ${storage.slug}\\` to reset the migration history and reapply migrations. ` +\n `Missing tables: ${drift.missingTables.join(\", \")}`,\n ),\n });\n }\n if (drift.status === \"drift-manual\") {\n yield* Effect.logWarning(\n `[Database] ⚠️ Partial migration drift detected: ${drift.missingTables.length}/${drift.expectedTables.length} expected table(s) missing.`,\n );\n return yield* new DatabaseError({\n stage: \"migration\",\n migrationTag: \"drift-manual\",\n cause: new Error(\n `Partial schema drift — ${drift.missingTables.length}/${drift.expectedTables.length} expected table(s) are missing. ` +\n `Run \\`bos db doctor ${storage.slug}\\` for details. Manual intervention required. ` +\n `Missing tables: ${drift.missingTables.join(\", \")}`,\n ),\n });\n }\n }\n\n return driver.db;\n }),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,cACd,KACA,KACA,SAMwC;CACxC,OAAO,MAAM,OACX,KACA,OAAO,IAAI,aAAa;EACtB,MAAM,EAAE,UAAU,QAAQ,YAAY,YAAY,SAAS;EAC3D,MAAM,OAAO,oBAAoB,QAAQ;EACzC,MAAM,aAAa,YAAY,iBAAiB,QAAQ,IAAI;EAE5D,MAAM,SAAS,OAAO,OAAO,eAC3B,OAAO,WAAW;GAChB,WAAW,qBAAqB,KAAK,QAAQ,UAAU;GACvD,QAAQ,UAAU,IAAI,cAAc;IAAE,OAAO;IAAU;GAAM,CAAC;EAChE,CAAC,IACA,WACC,OAAO,WAAW;GAChB,WAAW,OAAO,MAAM;GACxB,QAAQ,UAAU,IAAI,cAAc;IAAE,OAAO;IAAS;GAAM,CAAC;EAC/D,CAAC,CAAC,CAAC,KAAK,OAAO,MAAM,CACzB;EAEA,IAAI,CAAC,YACH,OAAO,OAAO;EAGhB,MAAM,UAAU,oBAAoB,IAAI;EACxC,MAAM,EAAE,YAAY,QAAQ,WAAW,OAAO;EAE9C,IAAI,OAAO,WAAW,GACpB,OAAO,OAAO,WACZ,2CAA2C,OAAO,0BACpD;OACK;GAKL,MAAM,WAAU,OAJM,cAAc,OAAO,IAAI,QAAQ;IACrD,SAAS;IACT;GACF,CAAC,EACqB,CAAC;GAEvB,IAAI,YAAY,GACd,OAAO,OAAO,QACZ,8DAA8D,OAAO,EACvE;QAEA,OAAO,OAAO,QACZ,sBAAsB,QAAQ,GAAG,OAAO,OAAO,yBAAyB,OAAO,aAAa,QAAQ,OAAO,GAAG,QAAQ,MAAM,YAAY,cAAc,SAAS,EACjK;GAGF,MAAM,QAAQ,OAAO,YAAY,OAAO,IAAI,QAAQ,SAAS,UAAU;GACvE,IAAI,MAAM,WAAW,aAAa,MAAM,WAAW,6BACjD,OAAO,OAAO,QAAQ,kBAAkB;QACnC,IAAI,MAAM,WAAW,qBAAqB;IAC/C,OAAO,OAAO,WACZ,2CAA2C,MAAM,cAAc,OAAO,8BAA8B,MAAM,cAAc,KAAK,IAAI,GACnI;IACA,OAAO,OAAO,WACZ,kCAAkC,QAAQ,KAAK,qCAAqC,QAAQ,KAAK,WACnG;IACA,OAAO,OAAO,IAAI,cAAc;KAC9B,OAAO;KACP,cAAc;KACd,uBAAO,IAAI,MACT,yBAAyB,MAAM,cAAc,0BAA0B,MAAM,eAAe,OAAO,sDAC1E,QAAQ,KAAK,4EACjB,MAAM,cAAc,KAAK,IAAI,GACpD;IACF,CAAC;GACH;GACA,IAAI,MAAM,WAAW,gBAAgB;IACnC,OAAO,OAAO,WACZ,mDAAmD,MAAM,cAAc,OAAO,GAAG,MAAM,eAAe,OAAO,4BAC/G;IACA,OAAO,OAAO,IAAI,cAAc;KAC9B,OAAO;KACP,cAAc;KACd,uBAAO,IAAI,MACT,0BAA0B,MAAM,cAAc,OAAO,GAAG,MAAM,eAAe,OAAO,sDAC3D,QAAQ,KAAK,gEACjB,MAAM,cAAc,KAAK,IAAI,GACpD;IACF,CAAC;GACH;EACF;EAEA,OAAO,OAAO;CAChB,CAAC,CACH;AACF"}