drizzle-orm 1.0.0-beta.22-41a7d21 → 1.0.0-beta.22-bf0fa46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/bun-sql/sqlite/migrator.cjs +1 -1
  2. package/bun-sql/sqlite/migrator.cjs.map +1 -1
  3. package/bun-sql/sqlite/migrator.js +1 -1
  4. package/bun-sql/sqlite/migrator.js.map +1 -1
  5. package/d1/migrator.cjs +1 -1
  6. package/d1/migrator.cjs.map +1 -1
  7. package/d1/migrator.js +1 -1
  8. package/d1/migrator.js.map +1 -1
  9. package/durable-sqlite/migrator.cjs +1 -1
  10. package/durable-sqlite/migrator.js +1 -1
  11. package/libsql/migrator.cjs +1 -1
  12. package/libsql/migrator.cjs.map +1 -1
  13. package/libsql/migrator.js +1 -1
  14. package/libsql/migrator.js.map +1 -1
  15. package/mysql-proxy/migrator.cjs +2 -2
  16. package/mysql-proxy/migrator.cjs.map +1 -1
  17. package/mysql-proxy/migrator.js +2 -2
  18. package/mysql-proxy/migrator.js.map +1 -1
  19. package/op-sqlite/migrator.cjs +1 -1
  20. package/op-sqlite/migrator.cjs.map +1 -1
  21. package/op-sqlite/migrator.js +1 -1
  22. package/op-sqlite/migrator.js.map +1 -1
  23. package/package.json +67 -13
  24. package/pg-proxy/migrator.cjs +2 -2
  25. package/pg-proxy/migrator.cjs.map +1 -1
  26. package/pg-proxy/migrator.js +2 -2
  27. package/pg-proxy/migrator.js.map +1 -1
  28. package/singlestore-proxy/migrator.cjs +2 -2
  29. package/singlestore-proxy/migrator.cjs.map +1 -1
  30. package/singlestore-proxy/migrator.js +2 -2
  31. package/singlestore-proxy/migrator.js.map +1 -1
  32. package/sqlite-cloud/migrator.cjs +1 -1
  33. package/sqlite-cloud/migrator.cjs.map +1 -1
  34. package/sqlite-cloud/migrator.js +1 -1
  35. package/sqlite-cloud/migrator.js.map +1 -1
  36. package/sqlite-core/dialect.cjs +7 -7
  37. package/sqlite-core/dialect.cjs.map +1 -1
  38. package/sqlite-core/dialect.d.cts +2 -1
  39. package/sqlite-core/dialect.d.ts +2 -1
  40. package/sqlite-core/dialect.js +7 -7
  41. package/sqlite-core/dialect.js.map +1 -1
  42. package/sqlite-proxy/migrator.cjs +40 -2
  43. package/sqlite-proxy/migrator.cjs.map +1 -1
  44. package/sqlite-proxy/migrator.js +39 -2
  45. package/sqlite-proxy/migrator.js.map +1 -1
  46. package/tursodatabase/migrator.cjs +1 -1
  47. package/tursodatabase/migrator.cjs.map +1 -1
  48. package/tursodatabase/migrator.js +1 -1
  49. package/tursodatabase/migrator.js.map +1 -1
  50. package/up-migrations/cockroach.cjs +6 -12
  51. package/up-migrations/cockroach.cjs.map +1 -1
  52. package/up-migrations/cockroach.d.cts +1 -5
  53. package/up-migrations/cockroach.d.ts +1 -5
  54. package/up-migrations/cockroach.js +6 -12
  55. package/up-migrations/cockroach.js.map +1 -1
  56. package/up-migrations/effect-pg.cjs +6 -12
  57. package/up-migrations/effect-pg.cjs.map +1 -1
  58. package/up-migrations/effect-pg.d.cts +1 -5
  59. package/up-migrations/effect-pg.d.ts +1 -5
  60. package/up-migrations/effect-pg.js +6 -12
  61. package/up-migrations/effect-pg.js.map +1 -1
  62. package/up-migrations/mssql.cjs +6 -12
  63. package/up-migrations/mssql.cjs.map +1 -1
  64. package/up-migrations/mssql.d.cts +1 -5
  65. package/up-migrations/mssql.d.ts +1 -5
  66. package/up-migrations/mssql.js +6 -12
  67. package/up-migrations/mssql.js.map +1 -1
  68. package/up-migrations/mysql-proxy.cjs +73 -0
  69. package/up-migrations/mysql-proxy.cjs.map +1 -0
  70. package/up-migrations/mysql-proxy.d.cts +16 -0
  71. package/up-migrations/mysql-proxy.d.ts +16 -0
  72. package/up-migrations/mysql-proxy.js +71 -0
  73. package/up-migrations/mysql-proxy.js.map +1 -0
  74. package/up-migrations/mysql.cjs +12 -28
  75. package/up-migrations/mysql.cjs.map +1 -1
  76. package/up-migrations/mysql.d.cts +1 -5
  77. package/up-migrations/mysql.d.ts +1 -5
  78. package/up-migrations/mysql.js +12 -28
  79. package/up-migrations/mysql.js.map +1 -1
  80. package/up-migrations/pg-proxy.cjs +79 -0
  81. package/up-migrations/pg-proxy.cjs.map +1 -0
  82. package/up-migrations/pg-proxy.d.cts +16 -0
  83. package/up-migrations/pg-proxy.d.ts +16 -0
  84. package/up-migrations/pg-proxy.js +77 -0
  85. package/up-migrations/pg-proxy.js.map +1 -0
  86. package/up-migrations/pg.cjs +9 -13
  87. package/up-migrations/pg.cjs.map +1 -1
  88. package/up-migrations/pg.d.cts +2 -6
  89. package/up-migrations/pg.d.ts +2 -6
  90. package/up-migrations/pg.js +9 -13
  91. package/up-migrations/pg.js.map +1 -1
  92. package/up-migrations/singlestore-proxy.cjs +73 -0
  93. package/up-migrations/singlestore-proxy.cjs.map +1 -0
  94. package/up-migrations/singlestore-proxy.d.cts +16 -0
  95. package/up-migrations/singlestore-proxy.d.ts +16 -0
  96. package/up-migrations/singlestore-proxy.js +71 -0
  97. package/up-migrations/singlestore-proxy.js.map +1 -0
  98. package/up-migrations/singlestore.cjs +9 -25
  99. package/up-migrations/singlestore.cjs.map +1 -1
  100. package/up-migrations/singlestore.d.cts +1 -5
  101. package/up-migrations/singlestore.d.ts +1 -5
  102. package/up-migrations/singlestore.js +9 -25
  103. package/up-migrations/singlestore.js.map +1 -1
  104. package/up-migrations/sqlite-proxy.cjs +80 -0
  105. package/up-migrations/sqlite-proxy.cjs.map +1 -0
  106. package/up-migrations/sqlite-proxy.d.cts +16 -0
  107. package/up-migrations/sqlite-proxy.d.ts +16 -0
  108. package/up-migrations/sqlite-proxy.js +78 -0
  109. package/up-migrations/sqlite-proxy.js.map +1 -0
  110. package/up-migrations/sqlite.cjs +17 -48
  111. package/up-migrations/sqlite.cjs.map +1 -1
  112. package/up-migrations/sqlite.d.cts +3 -10
  113. package/up-migrations/sqlite.d.ts +5 -12
  114. package/up-migrations/sqlite.js +17 -48
  115. package/up-migrations/sqlite.js.map +1 -1
  116. package/up-migrations/utils.cjs +47 -0
  117. package/up-migrations/utils.cjs.map +1 -0
  118. package/up-migrations/utils.d.cts +25 -0
  119. package/up-migrations/utils.d.ts +25 -0
  120. package/up-migrations/utils.js +44 -0
  121. package/up-migrations/utils.js.map +1 -0
  122. package/sqlite-proxy/migrator.internal.cjs +0 -61
  123. package/sqlite-proxy/migrator.internal.cjs.map +0 -1
  124. package/sqlite-proxy/migrator.internal.d.cts +0 -26
  125. package/sqlite-proxy/migrator.internal.d.ts +0 -26
  126. package/sqlite-proxy/migrator.internal.js +0 -59
  127. package/sqlite-proxy/migrator.internal.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.cjs","names":["sql"],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { SQLiteD1Session } from '~/d1/session.ts';\nimport type { LibSQLSession } from '~/libsql/session.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { SQLiteCloudSession } from '~/sqlite-cloud/session.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport type { SQLiteRemoteSession } from '~/sqlite-proxy/session.ts';\nimport { assertUnreachable } from '~/utils.ts';\n\nconst CURRENT_MIGRATION_TABLE_VERSION = 1;\n\ninterface UpgradeResult {\n\tnewDb?: boolean;\n\tprevVersion?: number;\n\tcurrentVersion?: number;\n}\n\nfunction getVersion(columns: string[]) {\n\tif (columns.includes('name')) return 1;\n\treturn 0;\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nfunction allSync<T>(\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): T[] {\n\tconst result = session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nasync function allAsync<T>(\n\tsession: SQLiteSession<\n\t\t'async',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): Promise<T[]> {\n\tconst result = await session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\t// Check if the table exists at all\n\t// sqlite-proxy returns [ [1] ]\n\t// sqlite returns [{ '1': 1 }]\n\tconst tableExists = allSync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = allSync<{ column_name: string }>(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = allSync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tsession:\n\t\t| SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>\n\t\t| SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>,\n\tlocalMigrations: MigrationMeta[],\n\tmode: 'transaction' | 'run' = 'transaction',\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await allAsync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\t// sqlite-proxy returns [ [string] ]\n\t// sqlite returns [{ column_name: string }]\n\tconst rows = await allAsync(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations, mode);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t\tmode: 'transaction' | 'run',\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations, mode) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await allAsync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: SQL[] = [\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`,\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(updateQuery);\n\t\t}\n\n\t\tif (mode === 'transaction') {\n\t\t\t// for normal sqlite proxy migrate() call from code\n\t\t\tawait session.transaction(async (tx) => {\n\t\t\t\tfor (const statement of statements) {\n\t\t\t\t\tawait tx.run(statement);\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (mode === 'run') {\n\t\t\t// for drizzle-kit migrate call for d1 driver\n\t\t\t// see drizzle-kit/src/cli/connections.ts for sqlite d1\n\t\t\tfor (const statement of statements) {\n\t\t\t\tawait session.run(statement);\n\t\t\t}\n\t\t} else assertUnreachable(mode);\n\t},\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"sqlite.cjs","names":["GET_VERSION_FOR","MIGRATIONS_TABLE_VERSIONS","sql"],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { BaseSQLiteDatabase } from '~/sqlite-core/index.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, type UpgradeResult } from './utils.ts';\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\tconst tableExists = session.all(\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = session.all<{ column_name: string }>(\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t);\n\n\tconst version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = session.all<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tdb: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await db.session.all(\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\tconst rows = await db.session.all<{ column_name: string }>(\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t);\n\n\tconst version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, db, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tdb: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, db, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await db.session.all<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: SQL[] = [\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`,\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(updateQuery);\n\t\t}\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\tfor (const statement of statements) {\n\t\t\t\tawait tx.run(statement);\n\t\t\t}\n\t\t});\n\t},\n};\n"],"mappings":"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"}
@@ -1,18 +1,11 @@
1
+ import { UpgradeResult } from "./utils.cjs";
1
2
  import { TablesRelationalConfig } from "../_relations.cjs";
2
3
  import { MigrationMeta } from "../migrator.cjs";
3
4
  import { AnyRelations } from "../relations.cjs";
4
5
  import { SQLiteSession } from "../sqlite-core/session.cjs";
5
- import { SQLiteD1Session } from "../d1/session.cjs";
6
- import { LibSQLSession } from "../libsql/session.cjs";
7
- import { SQLiteCloudSession } from "../sqlite-cloud/session.cjs";
8
- import { SQLiteRemoteSession } from "../sqlite-proxy/session.cjs";
6
+ import { BaseSQLiteDatabase } from "../sqlite-core/index.cjs";
9
7
 
10
8
  //#region src/up-migrations/sqlite.d.ts
11
- interface UpgradeResult {
12
- newDb?: boolean;
13
- prevVersion?: number;
14
- currentVersion?: number;
15
- }
16
9
  /**
17
10
  * Detects the current version of the migrations table schema and upgrades it if needed.
18
11
  *
@@ -26,7 +19,7 @@ declare function upgradeSyncIfNeeded(migrationsTable: string, session: SQLiteSes
26
19
  * Version 0: Original schema (id, hash, created_at)
27
20
  * Version 1: Extended schema (id, hash, created_at, name, applied_at)
28
21
  */
29
- declare function upgradeAsyncIfNeeded(migrationsTable: string, session: SQLiteSession<'async', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[], mode?: 'transaction' | 'run'): Promise<UpgradeResult>;
22
+ declare function upgradeAsyncIfNeeded(migrationsTable: string, db: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>, localMigrations: MigrationMeta[]): Promise<UpgradeResult>;
30
23
  //#endregion
31
24
  export { upgradeAsyncIfNeeded, upgradeSyncIfNeeded };
32
25
  //# sourceMappingURL=sqlite.d.cts.map
@@ -1,32 +1,25 @@
1
+ import { UpgradeResult } from "./utils.js";
1
2
  import { TablesRelationalConfig } from "../_relations.js";
2
3
  import { MigrationMeta } from "../migrator.js";
3
- import { SQLiteSession } from "../sqlite-core/session.js";
4
+ import { BaseSQLiteDatabase } from "../sqlite-core/index.js";
5
+ import { SQLiteSession as SQLiteSession$1 } from "../sqlite-core/session.js";
4
6
  import { AnyRelations } from "../relations.js";
5
- import { SQLiteD1Session } from "../d1/session.js";
6
- import { LibSQLSession } from "../libsql/session.js";
7
- import { SQLiteCloudSession } from "../sqlite-cloud/session.js";
8
- import { SQLiteRemoteSession } from "../sqlite-proxy/session.js";
9
7
 
10
8
  //#region src/up-migrations/sqlite.d.ts
11
- interface UpgradeResult {
12
- newDb?: boolean;
13
- prevVersion?: number;
14
- currentVersion?: number;
15
- }
16
9
  /**
17
10
  * Detects the current version of the migrations table schema and upgrades it if needed.
18
11
  *
19
12
  * Version 0: Original schema (id, hash, created_at)
20
13
  * Version 1: Extended schema (id, hash, created_at, name, applied_at)
21
14
  */
22
- declare function upgradeSyncIfNeeded(migrationsTable: string, session: SQLiteSession<'sync', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[]): UpgradeResult;
15
+ declare function upgradeSyncIfNeeded(migrationsTable: string, session: SQLiteSession$1<'sync', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[]): UpgradeResult;
23
16
  /**
24
17
  * Detects the current version of the migrations table schema and upgrades it if needed.
25
18
  *
26
19
  * Version 0: Original schema (id, hash, created_at)
27
20
  * Version 1: Extended schema (id, hash, created_at, name, applied_at)
28
21
  */
29
- declare function upgradeAsyncIfNeeded(migrationsTable: string, session: SQLiteSession<'async', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[], mode?: 'transaction' | 'run'): Promise<UpgradeResult>;
22
+ declare function upgradeAsyncIfNeeded(migrationsTable: string, db: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>, localMigrations: MigrationMeta[]): Promise<UpgradeResult>;
30
23
  //#endregion
31
24
  export { upgradeAsyncIfNeeded, upgradeSyncIfNeeded };
32
25
  //# sourceMappingURL=sqlite.d.ts.map
@@ -1,24 +1,7 @@
1
- import { assertUnreachable } from "../utils.js";
1
+ import { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS } from "./utils.js";
2
2
  import { sql } from "../sql/sql.js";
3
3
 
4
4
  //#region src/up-migrations/sqlite.ts
5
- const CURRENT_MIGRATION_TABLE_VERSION = 1;
6
- function getVersion(columns) {
7
- if (columns.includes("name")) return 1;
8
- return 0;
9
- }
10
- function allSync(session, sqlQuery, resultMapper = () => []) {
11
- const result = session.all(sqlQuery);
12
- if (result.length === 0) return [];
13
- if (Array.isArray(result[0])) return result.map((row) => resultMapper(row));
14
- return result;
15
- }
16
- async function allAsync(session, sqlQuery, resultMapper = () => []) {
17
- const result = await session.all(sqlQuery);
18
- if (result.length === 0) return [];
19
- if (Array.isArray(result[0])) return result.map((row) => resultMapper(row));
20
- return result;
21
- }
22
5
  /**
23
6
  * Detects the current version of the migrations table schema and upgrades it if needed.
24
7
  *
@@ -26,25 +9,19 @@ async function allAsync(session, sqlQuery, resultMapper = () => []) {
26
9
  * Version 1: Extended schema (id, hash, created_at, name, applied_at)
27
10
  */
28
11
  function upgradeSyncIfNeeded(migrationsTable, session, localMigrations) {
29
- if (allSync(session, sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`, (row) => ({ "1": row[0] })).length === 0) return { newDb: true };
30
- const version = getVersion(allSync(session, sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`, (row) => ({ column_name: row[0] })).map((r) => r.column_name));
31
- for (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {
12
+ if (session.all(sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`).length === 0) return { newDb: true };
13
+ const rows = session.all(sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`);
14
+ const version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));
15
+ for (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {
32
16
  const upgradeFn = upgradeSyncFunctions[v];
33
17
  if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
34
18
  upgradeFn(migrationsTable, session, localMigrations);
35
19
  }
36
- return {
37
- prevVersion: version,
38
- currentVersion: CURRENT_MIGRATION_TABLE_VERSION
39
- };
20
+ return { newDb: false };
40
21
  }
41
22
  const upgradeSyncFunctions = { 0: (migrationsTable, session, localMigrations) => {
42
23
  const table = sql`${sql.identifier(migrationsTable)}`;
43
- const dbRows = allSync(session, sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`, (row) => ({
44
- id: row[0],
45
- hash: row[1],
46
- created_at: row[2]
47
- }));
24
+ const dbRows = session.all(sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`);
48
25
  localMigrations.sort((a, b) => a.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? "").localeCompare(b.name ?? ""));
49
26
  const byMillis = /* @__PURE__ */ new Map();
50
27
  const byHash = /* @__PURE__ */ new Map();
@@ -99,26 +76,20 @@ const upgradeSyncFunctions = { 0: (migrationsTable, session, localMigrations) =>
99
76
  * Version 0: Original schema (id, hash, created_at)
100
77
  * Version 1: Extended schema (id, hash, created_at, name, applied_at)
101
78
  */
102
- async function upgradeAsyncIfNeeded(migrationsTable, session, localMigrations, mode = "transaction") {
103
- if ((await allAsync(session, sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`, (row) => ({ "1": row[0] }))).length === 0) return { newDb: true };
104
- const version = getVersion((await allAsync(session, sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`, (row) => ({ column_name: row[0] }))).map((r) => r.column_name));
105
- for (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {
79
+ async function upgradeAsyncIfNeeded(migrationsTable, db, localMigrations) {
80
+ if ((await db.session.all(sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`)).length === 0) return { newDb: true };
81
+ const rows = await db.session.all(sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`);
82
+ const version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));
83
+ for (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {
106
84
  const upgradeFn = upgradeAsyncFunctions[v];
107
85
  if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
108
- await upgradeFn(migrationsTable, session, localMigrations, mode);
86
+ await upgradeFn(migrationsTable, db, localMigrations);
109
87
  }
110
- return {
111
- prevVersion: version,
112
- currentVersion: CURRENT_MIGRATION_TABLE_VERSION
113
- };
88
+ return { newDb: false };
114
89
  }
115
- const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrations, mode) => {
90
+ const upgradeAsyncFunctions = { 0: async (migrationsTable, db, localMigrations) => {
116
91
  const table = sql`${sql.identifier(migrationsTable)}`;
117
- const dbRows = await allAsync(session, sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`, (row) => ({
118
- id: row[0],
119
- hash: row[1],
120
- created_at: row[2]
121
- }));
92
+ const dbRows = await db.session.all(sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`);
122
93
  localMigrations.sort((a, b) => a.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? "").localeCompare(b.name ?? ""));
123
94
  const byMillis = /* @__PURE__ */ new Map();
124
95
  const byHash = /* @__PURE__ */ new Map();
@@ -163,11 +134,9 @@ const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrati
163
134
  else updateQuery.append(sql` ${sql.identifier("hash")} = ${backfillEntry.hash}`);
164
135
  statements.push(updateQuery);
165
136
  }
166
- if (mode === "transaction") await session.transaction(async (tx) => {
137
+ await db.transaction(async (tx) => {
167
138
  for (const statement of statements) await tx.run(statement);
168
139
  });
169
- else if (mode === "run") for (const statement of statements) await session.run(statement);
170
- else assertUnreachable(mode);
171
140
  } };
172
141
 
173
142
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.js","names":[],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { SQLiteD1Session } from '~/d1/session.ts';\nimport type { LibSQLSession } from '~/libsql/session.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { SQLiteCloudSession } from '~/sqlite-cloud/session.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport type { SQLiteRemoteSession } from '~/sqlite-proxy/session.ts';\nimport { assertUnreachable } from '~/utils.ts';\n\nconst CURRENT_MIGRATION_TABLE_VERSION = 1;\n\ninterface UpgradeResult {\n\tnewDb?: boolean;\n\tprevVersion?: number;\n\tcurrentVersion?: number;\n}\n\nfunction getVersion(columns: string[]) {\n\tif (columns.includes('name')) return 1;\n\treturn 0;\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nfunction allSync<T>(\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): T[] {\n\tconst result = session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nasync function allAsync<T>(\n\tsession: SQLiteSession<\n\t\t'async',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): Promise<T[]> {\n\tconst result = await session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\t// Check if the table exists at all\n\t// sqlite-proxy returns [ [1] ]\n\t// sqlite returns [{ '1': 1 }]\n\tconst tableExists = allSync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = allSync<{ column_name: string }>(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = allSync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tsession:\n\t\t| SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>\n\t\t| SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>,\n\tlocalMigrations: MigrationMeta[],\n\tmode: 'transaction' | 'run' = 'transaction',\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await allAsync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\t// sqlite-proxy returns [ [string] ]\n\t// sqlite returns [{ column_name: string }]\n\tconst rows = await allAsync(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations, mode);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t\tmode: 'transaction' | 'run',\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations, mode) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await allAsync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: SQL[] = [\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`,\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(updateQuery);\n\t\t}\n\n\t\tif (mode === 'transaction') {\n\t\t\t// for normal sqlite proxy migrate() call from code\n\t\t\tawait session.transaction(async (tx) => {\n\t\t\t\tfor (const statement of statements) {\n\t\t\t\t\tawait tx.run(statement);\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (mode === 'run') {\n\t\t\t// for drizzle-kit migrate call for d1 driver\n\t\t\t// see drizzle-kit/src/cli/connections.ts for sqlite d1\n\t\t\tfor (const statement of statements) {\n\t\t\t\tawait session.run(statement);\n\t\t\t}\n\t\t} else assertUnreachable(mode);\n\t},\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"sqlite.js","names":[],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { BaseSQLiteDatabase } from '~/sqlite-core/index.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, type UpgradeResult } from './utils.ts';\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\tconst tableExists = session.all(\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = session.all<{ column_name: string }>(\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t);\n\n\tconst version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = session.all<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tdb: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await db.session.all(\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\tconst rows = await db.session.all<{ column_name: string }>(\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t);\n\n\tconst version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, db, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tdb: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, db, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await db.session.all<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: SQL[] = [\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`,\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(updateQuery);\n\t\t}\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\tfor (const statement of statements) {\n\t\t\t\tawait tx.run(statement);\n\t\t\t}\n\t\t});\n\t},\n};\n"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ //#region src/up-migrations/utils.ts
4
+ const MIGRATIONS_TABLE_VERSIONS = {
5
+ sqlite: 1,
6
+ pg: 1,
7
+ effect: 1,
8
+ mysql: 1,
9
+ mssql: 1,
10
+ cockroach: 1,
11
+ singlestore: 1
12
+ };
13
+ const GET_VERSION_FOR = {
14
+ mysql: (columns) => {
15
+ if (columns.includes("name")) return 1;
16
+ return 0;
17
+ },
18
+ pg: (columns) => {
19
+ if (columns.includes("name")) return 1;
20
+ return 0;
21
+ },
22
+ effect: (columns) => {
23
+ if (columns.includes("name")) return 1;
24
+ return 0;
25
+ },
26
+ mssql: (columns) => {
27
+ if (columns.includes("name")) return 1;
28
+ return 0;
29
+ },
30
+ cockroach: (columns) => {
31
+ if (columns.includes("name")) return 1;
32
+ return 0;
33
+ },
34
+ singlestore: (columns) => {
35
+ if (columns.includes("name")) return 1;
36
+ return 0;
37
+ },
38
+ sqlite: (columns) => {
39
+ if (columns.includes("name")) return 1;
40
+ return 0;
41
+ }
42
+ };
43
+
44
+ //#endregion
45
+ exports.GET_VERSION_FOR = GET_VERSION_FOR;
46
+ exports.MIGRATIONS_TABLE_VERSIONS = MIGRATIONS_TABLE_VERSIONS;
47
+ //# sourceMappingURL=utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.cjs","names":[],"sources":["../../src/up-migrations/utils.ts"],"sourcesContent":["export interface UpgradeResult {\n\tnewDb: boolean;\n}\n\nexport const MIGRATIONS_TABLE_VERSIONS = {\n\tsqlite: 1,\n\tpg: 1,\n\teffect: 1,\n\tmysql: 1,\n\tmssql: 1,\n\tcockroach: 1,\n\tsinglestore: 1,\n} as const;\n\nexport const GET_VERSION_FOR = {\n\tmysql: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tpg: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\teffect: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tmssql: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tcockroach: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tsinglestore: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tsqlite: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n} as const;\n"],"mappings":";;;AAIA,MAAa,4BAA4B;CACxC,QAAQ;CACR,IAAI;CACJ,QAAQ;CACR,OAAO;CACP,OAAO;CACP,WAAW;CACX,aAAa;CACb;AAED,MAAa,kBAAkB;CAC9B,QAAQ,YAA8B;AACrC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,KAAK,YAA8B;AAClC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,SAAS,YAA8B;AACtC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,QAAQ,YAA8B;AACrC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,YAAY,YAA8B;AACzC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,cAAc,YAA8B;AAC3C,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,SAAS,YAA8B;AACtC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER"}
@@ -0,0 +1,25 @@
1
+ //#region src/up-migrations/utils.d.ts
2
+ interface UpgradeResult {
3
+ newDb: boolean;
4
+ }
5
+ declare const MIGRATIONS_TABLE_VERSIONS: {
6
+ readonly sqlite: 1;
7
+ readonly pg: 1;
8
+ readonly effect: 1;
9
+ readonly mysql: 1;
10
+ readonly mssql: 1;
11
+ readonly cockroach: 1;
12
+ readonly singlestore: 1;
13
+ };
14
+ declare const GET_VERSION_FOR: {
15
+ readonly mysql: (columns: string[]) => number;
16
+ readonly pg: (columns: string[]) => number;
17
+ readonly effect: (columns: string[]) => number;
18
+ readonly mssql: (columns: string[]) => number;
19
+ readonly cockroach: (columns: string[]) => number;
20
+ readonly singlestore: (columns: string[]) => number;
21
+ readonly sqlite: (columns: string[]) => number;
22
+ };
23
+ //#endregion
24
+ export { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, UpgradeResult };
25
+ //# sourceMappingURL=utils.d.cts.map
@@ -0,0 +1,25 @@
1
+ //#region src/up-migrations/utils.d.ts
2
+ interface UpgradeResult {
3
+ newDb: boolean;
4
+ }
5
+ declare const MIGRATIONS_TABLE_VERSIONS: {
6
+ readonly sqlite: 1;
7
+ readonly pg: 1;
8
+ readonly effect: 1;
9
+ readonly mysql: 1;
10
+ readonly mssql: 1;
11
+ readonly cockroach: 1;
12
+ readonly singlestore: 1;
13
+ };
14
+ declare const GET_VERSION_FOR: {
15
+ readonly mysql: (columns: string[]) => number;
16
+ readonly pg: (columns: string[]) => number;
17
+ readonly effect: (columns: string[]) => number;
18
+ readonly mssql: (columns: string[]) => number;
19
+ readonly cockroach: (columns: string[]) => number;
20
+ readonly singlestore: (columns: string[]) => number;
21
+ readonly sqlite: (columns: string[]) => number;
22
+ };
23
+ //#endregion
24
+ export { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, UpgradeResult };
25
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,44 @@
1
+ //#region src/up-migrations/utils.ts
2
+ const MIGRATIONS_TABLE_VERSIONS = {
3
+ sqlite: 1,
4
+ pg: 1,
5
+ effect: 1,
6
+ mysql: 1,
7
+ mssql: 1,
8
+ cockroach: 1,
9
+ singlestore: 1
10
+ };
11
+ const GET_VERSION_FOR = {
12
+ mysql: (columns) => {
13
+ if (columns.includes("name")) return 1;
14
+ return 0;
15
+ },
16
+ pg: (columns) => {
17
+ if (columns.includes("name")) return 1;
18
+ return 0;
19
+ },
20
+ effect: (columns) => {
21
+ if (columns.includes("name")) return 1;
22
+ return 0;
23
+ },
24
+ mssql: (columns) => {
25
+ if (columns.includes("name")) return 1;
26
+ return 0;
27
+ },
28
+ cockroach: (columns) => {
29
+ if (columns.includes("name")) return 1;
30
+ return 0;
31
+ },
32
+ singlestore: (columns) => {
33
+ if (columns.includes("name")) return 1;
34
+ return 0;
35
+ },
36
+ sqlite: (columns) => {
37
+ if (columns.includes("name")) return 1;
38
+ return 0;
39
+ }
40
+ };
41
+
42
+ //#endregion
43
+ export { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS };
44
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/up-migrations/utils.ts"],"sourcesContent":["export interface UpgradeResult {\n\tnewDb: boolean;\n}\n\nexport const MIGRATIONS_TABLE_VERSIONS = {\n\tsqlite: 1,\n\tpg: 1,\n\teffect: 1,\n\tmysql: 1,\n\tmssql: 1,\n\tcockroach: 1,\n\tsinglestore: 1,\n} as const;\n\nexport const GET_VERSION_FOR = {\n\tmysql: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tpg: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\teffect: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tmssql: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tcockroach: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tsinglestore: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n\tsqlite: (columns: string[]): number => {\n\t\tif (columns.includes('name')) return 1;\n\t\treturn 0;\n\t},\n} as const;\n"],"mappings":";AAIA,MAAa,4BAA4B;CACxC,QAAQ;CACR,IAAI;CACJ,QAAQ;CACR,OAAO;CACP,OAAO;CACP,WAAW;CACX,aAAa;CACb;AAED,MAAa,kBAAkB;CAC9B,QAAQ,YAA8B;AACrC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,KAAK,YAA8B;AAClC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,SAAS,YAA8B;AACtC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,QAAQ,YAA8B;AACrC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,YAAY,YAA8B;AACzC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,cAAc,YAA8B;AAC3C,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER,SAAS,YAA8B;AACtC,MAAI,QAAQ,SAAS,OAAO,CAAE,QAAO;AACrC,SAAO;;CAER"}
@@ -1,61 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
- let __sql_sql_ts = require("../sql/sql.cjs");
4
- let __migrator_ts = require("../migrator.cjs");
5
- let __migrator_utils_ts = require("../migrator.utils.cjs");
6
- let __up_migrations_sqlite_ts = require("../up-migrations/sqlite.cjs");
7
-
8
- //#region src/sqlite-proxy/migrator.internal.ts
9
- /**
10
- * - `migrate` - Public API for end users. Uses 'transaction' mode by default
11
- * - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter
12
- *
13
- * Why `mode` parameter exists:
14
- * - 'transaction':
15
- * Used by normal sqlite proxy driver
16
- *
17
- * - 'run':
18
- * Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements
19
- * @see "drizzle-kit/src/cli/connections.ts"
20
- */
21
- async function migrateInternal(db, callback, config, mode) {
22
- const migrations = (0, __migrator_ts.readMigrationFiles)(config);
23
- const migrationsTable = typeof config === "string" ? "__drizzle_migrations" : config.migrationsTable ?? "__drizzle_migrations";
24
- const { newDb } = await (0, __up_migrations_sqlite_ts.upgradeAsyncIfNeeded)(migrationsTable, db.session, migrations, mode);
25
- if (newDb) {
26
- const migrationTableCreate = __sql_sql_ts.sql`
27
- CREATE TABLE IF NOT EXISTS ${__sql_sql_ts.sql.identifier(migrationsTable)} (
28
- id INTEGER PRIMARY KEY,
29
- hash text NOT NULL,
30
- created_at numeric,
31
- name text,
32
- applied_at TEXT
33
- );`;
34
- await db.run(migrationTableCreate);
35
- }
36
- const dbMigrations = (await db.values(__sql_sql_ts.sql`SELECT id, hash, created_at, name FROM ${__sql_sql_ts.sql.identifier(migrationsTable)}`)).map(([id, hash, created_at, name]) => ({
37
- id,
38
- hash,
39
- created_at,
40
- name
41
- }));
42
- if (typeof config === "object" && config.init) {
43
- if (dbMigrations.length) return { exitCode: "databaseMigrations" };
44
- if (migrations.length > 1) return { exitCode: "localMigrations" };
45
- const [migration] = migrations;
46
- if (!migration) return;
47
- await callback([db.dialect.sqlToQuery(__sql_sql_ts.sql`insert into ${__sql_sql_ts.sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql]);
48
- return;
49
- }
50
- const migrationsToRun = (0, __migrator_utils_ts.getMigrationsToRun)({
51
- localMigrations: migrations,
52
- dbMigrations
53
- });
54
- const queriesToRun = [];
55
- for (const migration of migrationsToRun) queriesToRun.push(...migration.sql, db.dialect.sqlToQuery(__sql_sql_ts.sql`insert into ${__sql_sql_ts.sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql);
56
- await callback(queriesToRun);
57
- }
58
-
59
- //#endregion
60
- exports.migrateInternal = migrateInternal;
61
- //# sourceMappingURL=migrator.internal.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrator.internal.cjs","names":["sql"],"sources":["../../src/sqlite-proxy/migrator.internal.ts"],"sourcesContent":["import type { MigrationConfig } from '~/migrator.ts';\nimport { readMigrationFiles } from '~/migrator.ts';\nimport { getMigrationsToRun } from '~/migrator.utils.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { sql } from '~/sql/sql.ts';\nimport { upgradeAsyncIfNeeded } from '~/up-migrations/sqlite.ts';\nimport type { SqliteRemoteDatabase } from './driver.ts';\nimport type { ProxyMigrator } from './migrator.ts';\n\n/**\n * - `migrate` - Public API for end users. Uses 'transaction' mode by default\n * - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter\n *\n * Why `mode` parameter exists:\n * - 'transaction':\n * Used by normal sqlite proxy driver\n *\n * - 'run':\n * Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements\n * @see \"drizzle-kit/src/cli/connections.ts\"\n */\nexport async function migrateInternal<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(\n\tdb: SqliteRemoteDatabase<TSchema, TRelations>,\n\tcallback: ProxyMigrator,\n\tconfig: MigrationConfig,\n\tmode: 'transaction' | 'run',\n) {\n\tconst migrations = readMigrationFiles(config);\n\n\tconst migrationsTable = typeof config === 'string'\n\t\t? '__drizzle_migrations'\n\t\t: config.migrationsTable ?? '__drizzle_migrations';\n\n\t// Detect DB version and upgrade table schema if needed\n\tconst { newDb } = await upgradeAsyncIfNeeded(migrationsTable, db.session, migrations, mode);\n\n\tif (newDb) {\n\t\tconst migrationTableCreate = sql`\n\t\tCREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (\n\t\t\tid INTEGER PRIMARY KEY,\n\t\t\thash text NOT NULL,\n\t\t\tcreated_at numeric,\n\t\t\tname text,\n\t\t\tapplied_at TEXT\n\t\t);`;\n\n\t\tawait db.run(migrationTableCreate);\n\t}\n\n\tconst dbMigrations = (await db.values<[number, string, string, string | null]>(\n\t\tsql`SELECT id, hash, created_at, name FROM ${sql.identifier(migrationsTable)}`,\n\t)).map(([id, hash, created_at, name]) => ({ id, hash, created_at, name }));\n\n\tif (typeof config === 'object' && config.init) {\n\t\tif (dbMigrations.length) {\n\t\t\treturn { exitCode: 'databaseMigrations' as const };\n\t\t}\n\n\t\tif (migrations.length > 1) {\n\t\t\treturn { exitCode: 'localMigrations' as const };\n\t\t}\n\n\t\tconst [migration] = migrations;\n\n\t\tif (!migration) return;\n\n\t\tawait callback(\n\t\t\t[\n\t\t\t\tdb.dialect.sqlToQuery(\n\t\t\t\t\tsql`insert into ${\n\t\t\t\t\t\tsql.identifier(migrationsTable)\n\t\t\t\t\t} (\"hash\", \"created_at\", \"name\", \"applied_at\") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${\n\t\t\t\t\t\tnew Date().toISOString()\n\t\t\t\t\t})`\n\t\t\t\t\t\t.inlineParams(),\n\t\t\t\t).sql,\n\t\t\t],\n\t\t);\n\n\t\treturn;\n\t}\n\n\tconst migrationsToRun = getMigrationsToRun({ localMigrations: migrations, dbMigrations });\n\tconst queriesToRun: string[] = [];\n\tfor (const migration of migrationsToRun) {\n\t\tqueriesToRun.push(\n\t\t\t...migration.sql,\n\t\t\tdb.dialect.sqlToQuery(\n\t\t\t\tsql`insert into ${\n\t\t\t\t\tsql.identifier(migrationsTable)\n\t\t\t\t} (\"hash\", \"created_at\", \"name\", \"applied_at\") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${\n\t\t\t\t\tnew Date().toISOString()\n\t\t\t\t})`\n\t\t\t\t\t.inlineParams(),\n\t\t\t).sql,\n\t\t);\n\t}\n\n\tawait callback(queriesToRun);\n\n\treturn;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,eAAsB,gBACrB,IACA,UACA,QACA,MACC;CACD,MAAM,mDAAgC,OAAO;CAE7C,MAAM,kBAAkB,OAAO,WAAW,WACvC,yBACA,OAAO,mBAAmB;CAG7B,MAAM,EAAE,UAAU,0DAA2B,iBAAiB,GAAG,SAAS,YAAY,KAAK;AAE3F,KAAI,OAAO;EACV,MAAM,uBAAuB,gBAAG;+BACHA,iBAAI,WAAW,gBAAgB,CAAC;;;;;;;AAQ7D,QAAM,GAAG,IAAI,qBAAqB;;CAGnC,MAAM,gBAAgB,MAAM,GAAG,OAC9B,gBAAG,0CAA0CA,iBAAI,WAAW,gBAAgB,GAC5E,EAAE,KAAK,CAAC,IAAI,MAAM,YAAY,WAAW;EAAE;EAAI;EAAM;EAAY;EAAM,EAAE;AAE1E,KAAI,OAAO,WAAW,YAAY,OAAO,MAAM;AAC9C,MAAI,aAAa,OAChB,QAAO,EAAE,UAAU,sBAA+B;AAGnD,MAAI,WAAW,SAAS,EACvB,QAAO,EAAE,UAAU,mBAA4B;EAGhD,MAAM,CAAC,aAAa;AAEpB,MAAI,CAAC,UAAW;AAEhB,QAAM,SACL,CACC,GAAG,QAAQ,WACV,gBAAG,eACFA,iBAAI,WAAW,gBAAgB,CAC/B,uDAAuD,UAAU,KAAK,IAAI,UAAU,aAAa,IAAI,UAAU,KAAK,qBACpH,IAAI,MAAM,EAAC,aAAa,CACxB,GACC,cAAc,CAChB,CAAC,IACF,CACD;AAED;;CAGD,MAAM,8DAAqC;EAAE,iBAAiB;EAAY;EAAc,CAAC;CACzF,MAAM,eAAyB,EAAE;AACjC,MAAK,MAAM,aAAa,gBACvB,cAAa,KACZ,GAAG,UAAU,KACb,GAAG,QAAQ,WACV,gBAAG,eACFA,iBAAI,WAAW,gBAAgB,CAC/B,uDAAuD,UAAU,KAAK,IAAI,UAAU,aAAa,IAAI,UAAU,KAAK,qBACpH,IAAI,MAAM,EAAC,aAAa,CACxB,GACC,cAAc,CAChB,CAAC,IACF;AAGF,OAAM,SAAS,aAAa"}