{"version":3,"file":"run-migrations.mjs","names":[],"sources":["../../src/db/run-migrations.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { sql } from \"drizzle-orm\";\nimport { Effect, Option, Schedule, Schema } from \"effect\";\nimport {\n  DUPLICATE_OBJECT_SQLSTATES,\n  extractExpectedTables,\n  getMigrationStorage,\n  isConcurrentDdlUniqueViolation,\n  isRetryableMigrationExecutionError,\n  type MigrationStorage,\n  toSqlArray,\n  visitCauses,\n} from \"./core\";\nimport { DatabaseError } from \"./errors\";\n\nexport interface Migration {\n  idx: number;\n  when: number;\n  tag: string;\n  hash: string;\n  sql: string[];\n}\n\n/**\n * Minimal structural view of a drizzle PgDatabase — the concrete per-workspace\n * `PgDatabase<PgQueryResultHKT, TSchema>` values satisfy this.\n */\nexport interface MigrationDatabase {\n  execute(query: unknown): Promise<unknown>;\n  transaction<T>(fn: (tx: { execute(query: unknown): Promise<unknown> }) => Promise<T>): Promise<T>;\n}\n\nexport interface LoadedMigrations {\n  migrations: Migration[];\n  source: \"virtual\" | \"disk\";\n}\n\nconst migrationJournalSchema = Schema.Struct({\n  entries: Schema.Array(\n    Schema.Struct({\n      idx: Schema.Finite,\n      when: Schema.Finite,\n      tag: Schema.String,\n    }),\n  ),\n});\n\nexport interface DriftReport {\n  status: \"healthy\" | \"empty\" | \"untracked-existing-schema\" | \"drift-safe-repair\" | \"drift-manual\";\n  expectedTables: string[];\n  missingTables: string[];\n  appliedHashes: number;\n  localHashes: number;\n  storage: MigrationStorage;\n}\n\nexport interface MigrationReport {\n  applied: number;\n  total: number;\n  storage: MigrationStorage;\n  schema: string | undefined;\n}\n\nexport interface RunMigrationsOptions {\n  schemaName?: string;\n  journal?: MigrationStorage;\n  duplicateSqlStates?: readonly string[];\n}\n\nconst DEFAULT_DUPLICATE_SQLSTATES: readonly string[] = DUPLICATE_OBJECT_SQLSTATES;\n\nfunction normalizeRows<T>(result: unknown): T[] {\n  if (Array.isArray(result)) return result as T[];\n  if (result && typeof result === \"object\" && \"rows\" in result) {\n    return (result as { rows: T[] }).rows;\n  }\n  return [];\n}\n\ntype QueryExecutor = { execute(query: unknown): Promise<unknown> };\n\nfunction isDuplicateObjectError(error: unknown, codes: readonly string[]): boolean {\n  return visitCauses(error, 6, (link) => {\n    const code = (link as { code?: unknown }).code;\n    return typeof code === \"string\" && codes.includes(code);\n  });\n}\n\n/**\n * Whether a failed migration statement should be treated as duplicate-DDL\n * tolerance: either an explicit duplicate-object class, or the concurrent-DDL\n * catalog collision (SQLSTATE 23505 on a pg_catalog unique index) that two\n * booting processes can produce when they run the same `CREATE TABLE` against\n * a fresh database at the same time.\n */\nfunction isTolerableDuplicateDdl(error: unknown, codes: readonly string[]): boolean {\n  return isDuplicateObjectError(error, codes) || isConcurrentDdlUniqueViolation(error);\n}\n\n/**\n * Check which of the given expected tables already exist in the target schema.\n * The schema name is bound as a query parameter.\n */\nasync function existingTablesIn(\n  executor: QueryExecutor,\n  tables: string[],\n  schemaName?: string,\n): Promise<Set<string>> {\n  if (tables.length === 0) return new Set<string>();\n  const schema = schemaName ?? \"public\";\n  const result = await executor.execute(sql`\n    SELECT table_name FROM information_schema.tables\n    WHERE table_schema = ${schema}\n      AND table_name = ANY(${sql.raw(toSqlArray(tables))})\n  `);\n  return new Set(normalizeRows<{ table_name: string }>(result).map((r) => r.table_name));\n}\n\n/** Read applied hashes from the migration journal. */\nasync function readAppliedHashesIn(\n  executor: QueryExecutor,\n  ref: ReturnType<typeof sql.raw>,\n): Promise<Set<string>> {\n  const result = await executor.execute(sql`SELECT hash FROM ${ref}`);\n  return new Set(normalizeRows<{ hash: string }>(result).map((r) => r.hash));\n}\n\nexport interface LoadMigrationsOptions {\n  /** Directory containing the workspace's drizzle `migrations/` folder for the disk fallback. */\n  fromDir?: string;\n  /**\n   * Bundler-provided virtual-module loader (e.g. `virtual:drizzle-migrations.sql`\n   * registered by rspack). Lives at the call site — the shared package must not\n   * name the virtual module, which only exists inside plugin bundles.\n   */\n  virtual?: () => Promise<{ default?: Migration[] }>;\n}\n\nexport function loadMigrations(\n  opts: LoadMigrationsOptions = {},\n): Effect.Effect<LoadedMigrations, DatabaseError> {\n  return Effect.gen(function* () {\n    if (opts.virtual) {\n      const mod = yield* Effect.tryPromise({\n        try: () => opts.virtual!(),\n        catch: (cause) => new DatabaseError({ stage: \"load\", cause }),\n      }).pipe(Effect.option);\n      const migrations = Option.getOrUndefined(mod)?.default;\n      if (migrations?.length) {\n        yield* Effect.logInfo(\n          `[Database] Loaded ${migrations.length} migration(s) from virtual module`,\n        );\n        return { migrations, source: \"virtual\" as const };\n      }\n      yield* Effect.logDebug(\"[Database] Virtual migrations unavailable, loading from disk\");\n    }\n\n    const diskMigrations = Option.getOrUndefined(\n      yield* loadMigrationsFromDisk(opts.fromDir).pipe(Effect.option),\n    );\n    if (diskMigrations) {\n      yield* Effect.logInfo(`[Database] Loaded ${diskMigrations.length} migration(s) from disk`);\n      return { migrations: diskMigrations, source: \"disk\" as const };\n    }\n\n    yield* Effect.logWarning(\"[Database] No migrations found from virtual or disk\");\n    return { migrations: [], source: \"disk\" as const };\n  });\n}\n\nfunction loadMigrationsFromDisk(fromDir?: string): Effect.Effect<Migration[], DatabaseError> {\n  return Effect.gen(function* () {\n    const migrationsDir = resolve(fromDir ?? import.meta.dirname, \"migrations\");\n    const journalPath = join(migrationsDir, \"meta\", \"_journal.json\");\n\n    if (!existsSync(journalPath)) {\n      return yield* new DatabaseError({\n        stage: \"load\",\n        cause: new Error(\n          `Migrations journal not found at ${journalPath}. Run \\`db:generate\\` first.`,\n        ),\n      });\n    }\n\n    const journalContents = yield* Effect.try({\n      try: () => readFileSync(journalPath, \"utf8\"),\n      catch: (cause) => new DatabaseError({ stage: \"load\", cause }),\n    });\n    const json = yield* Schema.decodeEffect(Schema.fromJsonString(Schema.Json))(\n      journalContents,\n    ).pipe(Effect.mapError((cause) => new DatabaseError({ stage: \"load\", cause })));\n    const journal = yield* Schema.decodeUnknownEffect(migrationJournalSchema)(json).pipe(\n      Effect.mapError((cause) => new DatabaseError({ stage: \"load\", cause })),\n    );\n\n    return yield* Effect.forEach(journal.entries, (entry) =>\n      Effect.try({\n        try: () => {\n          const sqlPath = join(migrationsDir, `${entry.tag}.sql`);\n          if (!existsSync(sqlPath)) {\n            throw new Error(`Migration SQL file not found: ${sqlPath}`);\n          }\n          const raw = readFileSync(sqlPath, \"utf8\");\n          const sqlStatements = raw\n            .split(\"--> statement-breakpoint\")\n            .map((statement) => statement.trim());\n          const hash = createHash(\"sha256\").update(raw).digest(\"hex\");\n          return { idx: entry.idx, when: entry.when, tag: entry.tag, hash, sql: sqlStatements };\n        },\n        catch: (cause) => new DatabaseError({ stage: \"load\", cause }),\n      }),\n    );\n  });\n}\n\nfunction journalRef(s: MigrationStorage): ReturnType<typeof sql> {\n  return sql.raw(`\"${s.schema}\".\"${s.table}\"`);\n}\n\n/**\n * Apply one statement under a savepoint, tolerating duplicate DDL: either an\n * explicit duplicate-object class or the concurrent-DDL catalog collision a\n * non-participant migrator (drizzle-kit) can still produce.\n */\nasync function applyWithSavepoint(\n  tx: QueryExecutor,\n  query: ReturnType<typeof sql>,\n  migrationTag: string,\n  savepoint: string,\n  duplicateSqlStates: readonly string[],\n): Promise<void> {\n  await tx.execute(sql.raw(`SAVEPOINT ${savepoint}`));\n  try {\n    await tx.execute(query);\n  } catch (cause) {\n    if (isTolerableDuplicateDdl(cause, duplicateSqlStates)) {\n      await tx.execute(sql.raw(`ROLLBACK TO SAVEPOINT ${savepoint}`));\n      return;\n    }\n    throw new DatabaseError({ stage: \"migration\", migrationTag, cause });\n  }\n  await tx.execute(sql.raw(`RELEASE SAVEPOINT ${savepoint}`));\n}\n\ninterface LockedRunArgs {\n  sorted: Migration[];\n  journal: MigrationStorage;\n  ref: ReturnType<typeof sql.raw>;\n  schemaName: string | undefined;\n  duplicateSqlStates: readonly string[];\n}\n\n/**\n * The whole migration run inside ONE transaction that opens with a\n * journal-scoped `pg_advisory_xact_lock`. Drizzle's `db.transaction` pins a\n * single pooled client for the callback, so the lock, the journal read, the\n * DDL, and the journal inserts all share one session; the lock releases at\n * COMMIT/ROLLBACK or on connection death — a crashed migrator can never\n * orphan it. A concurrent migrator blocks (bounded by the driver's\n * `lock_timeout`), then reads the winner's committed journal rows and applies\n * nothing.\n */\nasync function runLockedTransaction(\n  db: MigrationDatabase,\n  args: LockedRunArgs,\n): Promise<{ appliedTags: string[]; logs: string[] }> {\n  const { sorted, journal, ref, schemaName, duplicateSqlStates } = args;\n  const appliedTags: string[] = [];\n  const logs: string[] = [];\n\n  await db.transaction(async (tx) => {\n    await tx.execute(\n      sql`SELECT pg_advisory_xact_lock(hashtextextended(${`${journal.schema}.${journal.table}`}, 0))`,\n    );\n\n    await applyWithSavepoint(\n      tx,\n      sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(`\"${journal.schema}\"`)}`,\n      \"init-schema\",\n      \"sp_init_schema\",\n      duplicateSqlStates,\n    );\n    await applyWithSavepoint(\n      tx,\n      sql`\n        CREATE TABLE IF NOT EXISTS ${ref} (\n          id SERIAL PRIMARY KEY,\n          hash text NOT NULL,\n          created_at bigint\n        )\n      `,\n      \"init-table\",\n      \"sp_init_table\",\n      duplicateSqlStates,\n    );\n    if (schemaName) {\n      await applyWithSavepoint(\n        tx,\n        sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(`\"${schemaName}\"`)}`,\n        \"init-data-schema\",\n        \"sp_init_data_schema\",\n        duplicateSqlStates,\n      );\n    }\n\n    const appliedHashes = await readAppliedHashesIn(tx, ref);\n\n    for (const migration of sorted) {\n      const isApplied =\n        appliedHashes.has(migration.hash) || appliedHashes.has(migration.hash.slice(0, 12));\n      if (isApplied) continue;\n\n      // Preflight: if this migration's expected tables already exist, record it\n      // as applied rather than crashing on a duplicate DDL error.\n      const expectedTables = extractExpectedTables([migration]);\n      if (expectedTables.length > 0) {\n        const existing = await existingTablesIn(tx, expectedTables, schemaName);\n        const missingTables = expectedTables.filter((t) => !existing.has(t));\n        if (missingTables.length === 0) {\n          logs.push(\n            `[Database] All tables for migration ${migration.tag} already exist — ` +\n              `recording as applied without replaying DDL`,\n          );\n          await tx.execute(\n            sql`INSERT INTO ${ref} (hash, created_at) VALUES (${migration.hash}, ${migration.when})`,\n          );\n          appliedHashes.add(migration.hash);\n          appliedTags.push(migration.tag);\n          continue;\n        }\n        if (missingTables.length < expectedTables.length) {\n          logs.push(\n            `[Database] Partial table overlap for migration ${migration.tag}: ` +\n              `${expectedTables.length - missingTables.length} table(s) exist but not all. ` +\n              `Applying migration — existing tables: ${expectedTables.filter((t) => existing.has(t)).join(\", \")}`,\n          );\n        }\n      }\n\n      logs.push(`[Database] Applying migration: ${migration.tag}`);\n\n      for (const [i, statement] of migration.sql.entries()) {\n        const stmt = schemaName ? statement.replace(/\"public\"\\./g, \"\") : statement;\n        await applyWithSavepoint(tx, sql.raw(stmt), migration.tag, `stmt_${i}`, duplicateSqlStates);\n      }\n      await tx.execute(\n        sql`INSERT INTO ${ref} (hash, created_at) VALUES (${migration.hash}, ${migration.when})`,\n      );\n      appliedTags.push(migration.tag);\n    }\n  });\n\n  return { appliedTags, logs };\n}\n\n/**\n * Apply pending migrations to the target database.\n *\n * Namespace model: `opts.schemaName` (`plugin_<slug>`) scopes data tables; the\n * journal lives in `opts.journal` (default the shared `drizzle.__drizzle_migrations`).\n * Undefined `schemaName` means public-schema (api) or dedicated-DB (auth) topology.\n *\n * Concurrency: the run serializes on a journal-scoped Postgres advisory\n * transaction lock, so the dev double-boot (plugin dev-server + the host's\n * in-process load), parallel test files, and overlapping production replicas\n * converge instead of colliding in pg_catalog. SAVEPOINT-based duplicate-DDL\n * tolerance stays as defense-in-depth for non-participants (drizzle-kit).\n *\n * Reliability floor: hash-tracked idempotence with a preflight that records\n * fully-overlapped migrations as applied, everything in one locked\n * transaction, retried on deadlock/serialization/lock-timeout/connection\n * states — a failed attempt rolls back cleanly and re-runs.\n */\nexport function runMigrations(\n  db: MigrationDatabase,\n  migrations: Migration[],\n  opts: RunMigrationsOptions = {},\n): Effect.Effect<MigrationReport, DatabaseError> {\n  return Effect.gen(function* () {\n    const sorted = [...migrations].sort((a, b) => a.idx - b.idx);\n    const journal = opts.journal ?? getMigrationStorage();\n    const schemaName = opts.schemaName;\n    const duplicateSqlStates = opts.duplicateSqlStates ?? DEFAULT_DUPLICATE_SQLSTATES;\n\n    const { appliedTags, logs } = yield* Effect.retry(\n      Effect.tryPromise({\n        try: () =>\n          runLockedTransaction(db, {\n            sorted,\n            journal,\n            ref: journalRef(journal),\n            schemaName,\n            duplicateSqlStates,\n          }),\n        catch: (cause) =>\n          cause instanceof DatabaseError\n            ? cause\n            : new DatabaseError({ stage: \"migration\", migrationTag: \"run\", cause }),\n      }),\n      {\n        schedule: Schedule.spaced(\"500 millis\"),\n        times: 3,\n        while: isRetryableMigrationExecutionError,\n      },\n    );\n\n    for (const line of logs) yield* Effect.logInfo(line);\n\n    return {\n      applied: appliedTags.length,\n      total: sorted.length,\n      storage: journal,\n      schema: schemaName,\n    };\n  });\n}\n\n/**\n * Detect drift between the local migration set and the database journal.\n *\n * Pass an explicit `journal` resolved from the caller's workspace for reliable\n * slug derivation; the default relies on `process.env.npm_package_name`, which is\n * unreliable under bundlers and Module Federation remotes.\n */\nexport function detectDrift(\n  db: MigrationDatabase,\n  migrations: Migration[],\n  journal?: MigrationStorage,\n  schemaName?: string,\n): Effect.Effect<DriftReport, DatabaseError> {\n  return Effect.gen(function* () {\n    const storage = journal ?? getMigrationStorage();\n    const expectedTables = extractExpectedTables(migrations);\n    const ref = journalRef(storage);\n\n    // Lenient by design: drift detection reports on whatever it can read —\n    // a failed journal read just means \"nothing tracked yet\".\n    const appliedHashes = yield* Effect.tryPromise({\n      try: () => readAppliedHashesIn(db, ref),\n      catch: (cause) =>\n        new DatabaseError({ stage: \"migration\", migrationTag: \"read-applied\", cause }),\n    }).pipe(Effect.orElseSucceed(() => new Set<string>()));\n    const appliedCount = appliedHashes.size;\n\n    if (expectedTables.length === 0) {\n      return {\n        status: \"empty\",\n        expectedTables: [],\n        missingTables: [],\n        appliedHashes: appliedCount,\n        localHashes: migrations.length,\n        storage,\n      };\n    }\n\n    const existing = yield* Effect.tryPromise({\n      try: () => existingTablesIn(db, expectedTables, schemaName),\n      catch: (cause) =>\n        new DatabaseError({ stage: \"migration\", migrationTag: \"preflight-table-check\", cause }),\n    }).pipe(Effect.orElseSucceed(() => new Set<string>()));\n    const missingTables = expectedTables.filter((t) => !existing.has(t));\n\n    if (appliedCount === 0 && missingTables.length === 0) {\n      // Journal is empty but all expected tables already exist.\n      return {\n        status: \"untracked-existing-schema\",\n        expectedTables,\n        missingTables: [],\n        appliedHashes: 0,\n        localHashes: migrations.length,\n        storage,\n      };\n    }\n\n    if (missingTables.length === 0) {\n      return {\n        status: \"healthy\",\n        expectedTables,\n        missingTables: [],\n        appliedHashes: appliedCount,\n        localHashes: migrations.length,\n        storage,\n      };\n    }\n\n    if (missingTables.length === expectedTables.length) {\n      return {\n        status: \"drift-safe-repair\",\n        expectedTables,\n        missingTables,\n        appliedHashes: appliedCount,\n        localHashes: migrations.length,\n        storage,\n      };\n    }\n\n    return {\n      status: \"drift-manual\",\n      expectedTables,\n      missingTables,\n      appliedHashes: appliedCount,\n      localHashes: migrations.length,\n      storage,\n    };\n  });\n}\n"],"mappings":"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"}