{"version":3,"file":"drizzle-d1-migrations.mjs","names":[],"sources":["../../src/internal/drizzle-d1-migrations.ts"],"sourcesContent":["import * as D1Client from \"@effect/sql-d1/D1Client\";\nimport {\n  PersistenceConfigurationError,\n  PersistenceMappingError,\n} from \"@yielded/auth-persistence/Adapter\";\nimport { reportPersistenceFailure } from \"@yielded/auth/Persistence\";\nimport { sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/d1\";\nimport { formatToMillis, getMigrationsToRun } from \"drizzle-orm/migrator.utils\";\nimport * as Cause from \"effect/Cause\";\nimport * as Digest from \"effect/crypto/Digest\";\nimport * as DateTime from \"effect/DateTime\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Schema from \"effect/Schema\";\n\nconst MigrationFiles = Schema.Record(Schema.String, Schema.NonEmptyString);\nconst MigrationName = Schema.String.check(Schema.isPattern(/^\\d{14}_.+$/));\nconst JournalColumns = Schema.Array(Schema.Struct({ name: Schema.String }));\n\nconst configurationError = () =>\n  PersistenceConfigurationError.make({\n    reason: \"Could not apply D1 migrations; check the bundled SQL and database binding\",\n  });\n\nconst migrationFailure = (cause: unknown) =>\n  PersistenceMappingError.make({ operation: \"migration\", cause });\n\nconst JournalRows = Schema.Array(\n  Schema.Struct({\n    id: Schema.Int,\n    hash: Schema.String,\n    created_at: Schema.String,\n    name: Schema.NullOr(Schema.String),\n  }),\n);\n\n/** Drizzle's D1 migration journal and selection, with bundled SQL in place of\n * filesystem loading. Pending statements and journal entries share one D1 batch.\n * Applications own when migrations run; do not run competing migration owners.\n */\nexport const d1MigrationsLayer = (options: {\n  readonly migrations: Readonly<Record<string, string>>;\n  readonly migrationsTable?: string;\n}) =>\n  Layer.effectDiscard(\n    Effect.gen(function* () {\n      const files = yield* Schema.decodeEffect(MigrationFiles)(options.migrations);\n      const client = yield* D1Client.D1Client;\n      const digests = yield* Digest.Digest;\n      const appliedAt = DateTime.formatIso(yield* DateTime.now);\n\n      const migrations = yield* Effect.forEach(\n        Object.entries(files).sort(([left], [right]) => left.localeCompare(right)),\n        Effect.fnUntraced(function* ([name, contents]) {\n          yield* Schema.decodeEffect(MigrationName)(name);\n\n          const folderMillis = yield* Schema.decodeEffect(Schema.Int)(\n            formatToMillis(name.slice(0, 14)),\n          );\n\n          const digest = yield* digests.digest(Digest.sha256, new TextEncoder().encode(contents));\n\n          return {\n            name,\n            folderMillis,\n            hash: Array.from(digest, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\"),\n            sql: contents.split(\"--> statement-breakpoint\"),\n            bps: true,\n          };\n        }),\n      );\n\n      yield* (function* () {\n        const db = drizzle(client.config.db);\n        const name = options.migrationsTable ?? \"__drizzle_migrations\";\n        const table = sql.identifier(name);\n\n        const columns = (yield* Schema.decodeUnknownEffect(JournalColumns)(\n          yield* Effect.tryPromise({\n            try: () => db.all(sql`select name from pragma_table_info(${name})`),\n            catch: migrationFailure,\n          }),\n        )).map((column) => column.name);\n\n        const newDb = columns.length === 0;\n        const legacy = !newDb && !columns.includes(\"name\");\n\n        if (\n          !newDb &&\n          (![\"id\", \"hash\", \"created_at\"].every((column) => columns.includes(column)) ||\n            legacy === columns.includes(\"applied_at\"))\n        )\n          return yield* configurationError();\n\n        // Original Drizzle SQLite journals can have NULL serial IDs. Use the\n        // physical rowid to backfill each retained entry without changing it.\n        const journal = newDb\n          ? []\n          : (yield* Schema.decodeUnknownEffect(JournalRows)(\n              yield* Effect.tryPromise({\n                try: () =>\n                  db.all(sql`select rowid as id, hash, cast(created_at as text) as created_at,\n              ${legacy ? sql`null` : sql`name`} as name from ${table} order by rowid`),\n                catch: migrationFailure,\n              }),\n            )).map((entry) => ({ ...entry }));\n\n        const journalStatements = newDb\n          ? [\n              db.run(sql`create table ${table} (\n                id integer primary key, hash text not null, created_at numeric,\n                name text, applied_at text\n              )`),\n            ]\n          : [];\n\n        if (legacy) {\n          // Match retained entries using Drizzle's timestamp/hash convention,\n          // but stage the upgrade with pending SQL: D1 cannot run BEGIN.\n          journalStatements.push(\n            db.run(sql`alter table ${table} add column name text`),\n            db.run(sql`alter table ${table} add column applied_at text`),\n          );\n          for (const entry of journal) {\n            const millis = Math.floor(Number(entry.created_at) / 1000) * 1000;\n\n            const candidates = migrations.filter((migration) => migration.folderMillis === millis);\n\n            const matches =\n              candidates.length === 1\n                ? candidates\n                : (candidates.length === 0 ? migrations : candidates).filter(\n                    (migration) => migration.hash === entry.hash,\n                  );\n\n            const match = matches[0];\n\n            if (matches.length !== 1 || match === undefined) return yield* configurationError();\n            entry.name = match.name;\n            journalStatements.push(\n              db.run(sql`update ${table} set name = ${match.name} where rowid = ${entry.id}`),\n            );\n          }\n        }\n\n        const statements = [\n          ...journalStatements,\n          ...getMigrationsToRun({\n            localMigrations: migrations,\n            dbMigrations: [...journal],\n          }).flatMap((migration) => [\n            ...migration.sql.map((statement) => db.run(sql.raw(statement))),\n            db.run(sql`insert into ${table} (hash, created_at, name, applied_at)\n                values (${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${appliedAt})`),\n          ]),\n        ];\n\n        const [first, ...rest] = statements;\n\n        if (first !== undefined)\n          yield* Effect.tryPromise({\n            try: () => db.batch([first, ...rest]),\n            catch: migrationFailure,\n          });\n      })();\n    }).pipe(\n      (effect) =>\n        reportPersistenceFailure(effect, Schema.is(PersistenceConfigurationError), {\n          markReported: true,\n        }),\n      Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, configurationError))),\n    ),\n  );\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,iBAAiB,OAAO,OAAO,OAAO,QAAQ,OAAO,cAAc;AACzE,MAAM,gBAAgB,OAAO,OAAO,MAAM,OAAO,UAAU,aAAa,CAAC;AACzE,MAAM,iBAAiB,OAAO,MAAM,OAAO,OAAO,EAAE,MAAM,OAAO,OAAO,CAAC,CAAC;AAE1E,MAAM,2BACJ,8BAA8B,KAAK,EACjC,QAAQ,4EACV,CAAC;AAEH,MAAM,oBAAoB,UACxB,wBAAwB,KAAK;CAAE,WAAW;CAAa;AAAM,CAAC;AAEhE,MAAM,cAAc,OAAO,MACzB,OAAO,OAAO;CACZ,IAAI,OAAO;CACX,MAAM,OAAO;CACb,YAAY,OAAO;CACnB,MAAM,OAAO,OAAO,OAAO,MAAM;AACnC,CAAC,CACH;;;;;AAMA,MAAa,qBAAqB,YAIhC,MAAM,cACJ,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,OAAO,OAAO,aAAa,cAAc,CAAC,CAAC,QAAQ,UAAU;CAC3E,MAAM,SAAS,OAAO,SAAS;CAC/B,MAAM,UAAU,OAAO,OAAO;CAC9B,MAAM,YAAY,SAAS,UAAU,OAAO,SAAS,GAAG;CAExD,MAAM,aAAa,OAAO,OAAO,QAC/B,OAAO,QAAQ,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,KAAK,CAAC,GACzE,OAAO,WAAW,WAAW,CAAC,MAAM,WAAW;EAC7C,OAAO,OAAO,aAAa,aAAa,CAAC,CAAC,IAAI;EAE9C,MAAM,eAAe,OAAO,OAAO,aAAa,OAAO,GAAG,CAAC,CACzD,eAAe,KAAK,MAAM,GAAG,EAAE,CAAC,CAClC;EAEA,MAAM,SAAS,OAAO,QAAQ,OAAO,OAAO,QAAQ,IAAI,YAAY,CAAC,CAAC,OAAO,QAAQ,CAAC;EAEtF,OAAO;GACL;GACA;GACA,MAAM,MAAM,KAAK,SAAS,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;GAC9E,KAAK,SAAS,MAAM,0BAA0B;GAC9C,KAAK;EACP;CACF,CAAC,CACH;CAEA,QAAQ,aAAa;EACnB,MAAM,KAAK,QAAQ,OAAO,OAAO,EAAE;EACnC,MAAM,OAAO,QAAQ,mBAAmB;EACxC,MAAM,QAAQ,IAAI,WAAW,IAAI;EAEjC,MAAM,WAAW,OAAO,OAAO,oBAAoB,cAAc,CAAC,CAChE,OAAO,OAAO,WAAW;GACvB,WAAW,GAAG,IAAI,GAAG,sCAAsC,KAAK,EAAE;GAClE,OAAO;EACT,CAAC,CACH,EAAA,CAAG,KAAK,WAAW,OAAO,IAAI;EAE9B,MAAM,QAAQ,QAAQ,WAAW;EACjC,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,SAAS,MAAM;EAEjD,IACE,CAAC,UACA,CAAC;GAAC;GAAM;GAAQ;EAAY,CAAC,CAAC,OAAO,WAAW,QAAQ,SAAS,MAAM,CAAC,KACvE,WAAW,QAAQ,SAAS,YAAY,IAE1C,OAAO,OAAO,mBAAmB;EAInC,MAAM,UAAU,QACZ,CAAC,KACA,OAAO,OAAO,oBAAoB,WAAW,CAAC,CAC7C,OAAO,OAAO,WAAW;GACvB,WACE,GAAG,IAAI,GAAG;gBACZ,SAAS,GAAG,SAAS,GAAG,OAAO,gBAAgB,MAAM,gBAAgB;GACrE,OAAO;EACT,CAAC,CACH,EAAA,CAAG,KAAK,WAAW,EAAE,GAAG,MAAM,EAAE;EAEpC,MAAM,oBAAoB,QACtB,CACE,GAAG,IAAI,GAAG,gBAAgB,MAAM;;;gBAG9B,CACJ,IACA,CAAC;EAEL,IAAI,QAAQ;GAGV,kBAAkB,KAChB,GAAG,IAAI,GAAG,eAAe,MAAM,sBAAsB,GACrD,GAAG,IAAI,GAAG,eAAe,MAAM,4BAA4B,CAC7D;GACA,KAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,SAAS,KAAK,MAAM,OAAO,MAAM,UAAU,IAAI,GAAI,IAAI;IAE7D,MAAM,aAAa,WAAW,QAAQ,cAAc,UAAU,iBAAiB,MAAM;IAErF,MAAM,UACJ,WAAW,WAAW,IAClB,cACC,WAAW,WAAW,IAAI,aAAa,WAAA,CAAY,QACjD,cAAc,UAAU,SAAS,MAAM,IAC1C;IAEN,MAAM,QAAQ,QAAQ;IAEtB,IAAI,QAAQ,WAAW,KAAK,UAAU,KAAA,GAAW,OAAO,OAAO,mBAAmB;IAClF,MAAM,OAAO,MAAM;IACnB,kBAAkB,KAChB,GAAG,IAAI,GAAG,UAAU,MAAM,cAAc,MAAM,KAAK,iBAAiB,MAAM,IAAI,CAChF;GACF;EACF;EAcA,MAAM,CAAC,OAAO,GAAG,QAAQ,CAXvB,GAAG,mBACH,GAAG,mBAAmB;GACpB,iBAAiB;GACjB,cAAc,CAAC,GAAG,OAAO;EAC3B,CAAC,CAAC,CAAC,SAAS,cAAc,CACxB,GAAG,UAAU,IAAI,KAAK,cAAc,GAAG,IAAI,IAAI,IAAI,SAAS,CAAC,CAAC,GAC9D,GAAG,IAAI,GAAG,eAAe,MAAM;0BACjB,UAAU,KAAK,IAAI,UAAU,aAAa,IAAI,UAAU,KAAK,IAAI,UAAU,EAAE,CAC7F,CAAC,CAG+B;EAElC,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO,WAAW;GACvB,WAAW,GAAG,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;GACpC,OAAO;EACT,CAAC;CACL,EAAA,CAAG;AACL,CAAC,CAAC,CAAC,MACA,WACC,yBAAyB,QAAQ,OAAO,GAAG,6BAA6B,GAAG,EACzE,cAAc,KAChB,CAAC,GACH,OAAO,YAAY,UAAU,OAAO,UAAU,MAAM,IAAI,OAAO,kBAAkB,CAAC,CAAC,CACrF,CACF"}