@syncular/migrations 0.0.6-243 → 0.0.6-245

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.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/migrations",
3
- "version": "0.0.6-243",
3
+ "version": "0.0.6-245",
4
4
  "description": "Database migration utilities for Syncular",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -48,10 +48,17 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@syncular/config": "0.0.0",
51
+ "@types/better-sqlite3": "^7.6.13",
51
52
  "kysely": "*"
52
53
  },
53
54
  "files": [
54
55
  "dist",
55
56
  "src"
56
- ]
57
+ ],
58
+ "dependencies": {
59
+ "@electric-sql/pglite": "^0.4.3",
60
+ "better-sqlite3": "^12.8.0",
61
+ "kysely-bun-sqlite": "^0.4.0",
62
+ "kysely-pglite-dialect": "^1.3.1"
63
+ }
57
64
  }
@@ -0,0 +1,528 @@
1
+ import { PGlite } from '@electric-sql/pglite';
2
+ import {
3
+ type CompiledQuery,
4
+ type DatabaseConnection,
5
+ type Dialect,
6
+ type Driver,
7
+ Kysely,
8
+ type Kysely as KyselyInstance,
9
+ PostgresAdapter,
10
+ type QueryResult,
11
+ SqliteAdapter,
12
+ SqliteDialect,
13
+ type TransactionSettings,
14
+ } from 'kysely';
15
+ import { PGliteDialect } from 'kysely-pglite-dialect';
16
+ import type {
17
+ DefinedMigrations,
18
+ MigrationChecksumAlgorithm,
19
+ MigrationChecksumDialect,
20
+ ParsedMigration,
21
+ } from './types';
22
+
23
+ export const DISABLED_MIGRATION_CHECKSUM = '__syncular_checksum_disabled__';
24
+ export const LEGACY_SOURCE_MIGRATION_CHECKSUM_ALGORITHM = 'legacy_source_v1';
25
+ export const SQL_TRACE_MIGRATION_CHECKSUM_ALGORITHM = 'sql_trace_v1';
26
+ export const DISABLED_MIGRATION_CHECKSUM_ALGORITHM = 'disabled';
27
+
28
+ interface TraceSink {
29
+ enabled: boolean;
30
+ entries: string[];
31
+ }
32
+
33
+ const checksumCache = new WeakMap<
34
+ object,
35
+ Map<MigrationChecksumDialect, string | null>
36
+ >();
37
+
38
+ function stripCommentsPreservingStrings(source: string): string {
39
+ let out = '';
40
+ let index = 0;
41
+ let mode:
42
+ | 'code'
43
+ | 'singleQuote'
44
+ | 'doubleQuote'
45
+ | 'template'
46
+ | 'lineComment'
47
+ | 'blockComment' = 'code';
48
+
49
+ while (index < source.length) {
50
+ const char = source[index]!;
51
+ const next = source[index + 1];
52
+
53
+ if (mode === 'lineComment') {
54
+ if (char === '\n') {
55
+ out += '\n';
56
+ mode = 'code';
57
+ }
58
+ index += 1;
59
+ continue;
60
+ }
61
+
62
+ if (mode === 'blockComment') {
63
+ if (char === '*' && next === '/') {
64
+ index += 2;
65
+ mode = 'code';
66
+ continue;
67
+ }
68
+ if (char === '\n') {
69
+ out += '\n';
70
+ }
71
+ index += 1;
72
+ continue;
73
+ }
74
+
75
+ if (mode === 'singleQuote') {
76
+ out += char;
77
+ if (char === '\\' && next !== undefined) {
78
+ out += next;
79
+ index += 2;
80
+ continue;
81
+ }
82
+ if (char === "'") {
83
+ mode = 'code';
84
+ }
85
+ index += 1;
86
+ continue;
87
+ }
88
+
89
+ if (mode === 'doubleQuote') {
90
+ out += char;
91
+ if (char === '\\' && next !== undefined) {
92
+ out += next;
93
+ index += 2;
94
+ continue;
95
+ }
96
+ if (char === '"') {
97
+ mode = 'code';
98
+ }
99
+ index += 1;
100
+ continue;
101
+ }
102
+
103
+ if (mode === 'template') {
104
+ out += char;
105
+ if (char === '\\' && next !== undefined) {
106
+ out += next;
107
+ index += 2;
108
+ continue;
109
+ }
110
+ if (char === '`') {
111
+ mode = 'code';
112
+ }
113
+ index += 1;
114
+ continue;
115
+ }
116
+
117
+ if (char === '/' && next === '/') {
118
+ mode = 'lineComment';
119
+ index += 2;
120
+ continue;
121
+ }
122
+ if (char === '/' && next === '*') {
123
+ mode = 'blockComment';
124
+ index += 2;
125
+ continue;
126
+ }
127
+ if (char === "'") {
128
+ mode = 'singleQuote';
129
+ out += char;
130
+ index += 1;
131
+ continue;
132
+ }
133
+ if (char === '"') {
134
+ mode = 'doubleQuote';
135
+ out += char;
136
+ index += 1;
137
+ continue;
138
+ }
139
+ if (char === '`') {
140
+ mode = 'template';
141
+ out += char;
142
+ index += 1;
143
+ continue;
144
+ }
145
+
146
+ out += char;
147
+ index += 1;
148
+ }
149
+
150
+ return out;
151
+ }
152
+
153
+ function hashString(value: string): string {
154
+ let hash = 0;
155
+
156
+ for (let index = 0; index < value.length; index += 1) {
157
+ hash = (hash * 31 + value.charCodeAt(index)) >>> 0;
158
+ }
159
+
160
+ return hash.toString(16).padStart(8, '0');
161
+ }
162
+
163
+ function normalizeLegacySource(source: string): string {
164
+ return stripCommentsPreservingStrings(source).replace(/\s+/g, ' ').trim();
165
+ }
166
+
167
+ class TracingConnection implements DatabaseConnection {
168
+ readonly #inner: DatabaseConnection;
169
+ readonly #sink: TraceSink;
170
+
171
+ constructor(inner: DatabaseConnection, sink: TraceSink) {
172
+ this.#inner = inner;
173
+ this.#sink = sink;
174
+ }
175
+
176
+ unwrap(): DatabaseConnection {
177
+ return this.#inner;
178
+ }
179
+
180
+ async executeQuery<R>(compiledQuery: CompiledQuery): Promise<QueryResult<R>> {
181
+ if (this.#sink.enabled) {
182
+ this.#sink.entries.push(serializeCompiledQuery(compiledQuery));
183
+ }
184
+ return await this.#inner.executeQuery<R>(compiledQuery);
185
+ }
186
+
187
+ async *streamQuery<R>(
188
+ compiledQuery: CompiledQuery,
189
+ chunkSize?: number
190
+ ): AsyncIterableIterator<QueryResult<R>> {
191
+ if (this.#sink.enabled) {
192
+ this.#sink.entries.push(serializeCompiledQuery(compiledQuery));
193
+ }
194
+ for await (const result of this.#inner.streamQuery<R>(
195
+ compiledQuery,
196
+ chunkSize
197
+ )) {
198
+ yield result;
199
+ }
200
+ }
201
+ }
202
+
203
+ class TracingDriver implements Driver {
204
+ readonly #inner: Driver;
205
+ readonly #sink: TraceSink;
206
+ readonly #innerToWrapped = new Map<DatabaseConnection, TracingConnection>();
207
+
208
+ constructor(inner: Driver, sink: TraceSink) {
209
+ this.#inner = inner;
210
+ this.#sink = sink;
211
+ }
212
+
213
+ async init(): Promise<void> {
214
+ await this.#inner.init();
215
+ }
216
+
217
+ async acquireConnection(): Promise<DatabaseConnection> {
218
+ return this.#wrapConnection(await this.#inner.acquireConnection());
219
+ }
220
+
221
+ async beginTransaction(
222
+ connection: DatabaseConnection,
223
+ settings: TransactionSettings
224
+ ): Promise<void> {
225
+ await this.#inner.beginTransaction(
226
+ this.#unwrapConnection(connection),
227
+ settings
228
+ );
229
+ }
230
+
231
+ async commitTransaction(connection: DatabaseConnection): Promise<void> {
232
+ await this.#inner.commitTransaction(this.#unwrapConnection(connection));
233
+ }
234
+
235
+ async rollbackTransaction(connection: DatabaseConnection): Promise<void> {
236
+ await this.#inner.rollbackTransaction(this.#unwrapConnection(connection));
237
+ }
238
+
239
+ async savepoint?(
240
+ connection: DatabaseConnection,
241
+ savepointName: string,
242
+ compileQuery: Parameters<NonNullable<Driver['savepoint']>>[2]
243
+ ): Promise<void> {
244
+ if (!this.#inner.savepoint) {
245
+ return;
246
+ }
247
+ await this.#inner.savepoint(
248
+ this.#unwrapConnection(connection),
249
+ savepointName,
250
+ compileQuery
251
+ );
252
+ }
253
+
254
+ async rollbackToSavepoint?(
255
+ connection: DatabaseConnection,
256
+ savepointName: string,
257
+ compileQuery: Parameters<NonNullable<Driver['rollbackToSavepoint']>>[2]
258
+ ): Promise<void> {
259
+ if (!this.#inner.rollbackToSavepoint) {
260
+ return;
261
+ }
262
+ await this.#inner.rollbackToSavepoint(
263
+ this.#unwrapConnection(connection),
264
+ savepointName,
265
+ compileQuery
266
+ );
267
+ }
268
+
269
+ async releaseSavepoint?(
270
+ connection: DatabaseConnection,
271
+ savepointName: string,
272
+ compileQuery: Parameters<NonNullable<Driver['releaseSavepoint']>>[2]
273
+ ): Promise<void> {
274
+ if (!this.#inner.releaseSavepoint) {
275
+ return;
276
+ }
277
+ await this.#inner.releaseSavepoint(
278
+ this.#unwrapConnection(connection),
279
+ savepointName,
280
+ compileQuery
281
+ );
282
+ }
283
+
284
+ async releaseConnection(connection: DatabaseConnection): Promise<void> {
285
+ const unwrapped = this.#unwrapConnection(connection);
286
+
287
+ if (connection instanceof TracingConnection) {
288
+ this.#innerToWrapped.delete(unwrapped);
289
+ }
290
+
291
+ await this.#inner.releaseConnection(unwrapped);
292
+ }
293
+
294
+ async destroy(): Promise<void> {
295
+ this.#innerToWrapped.clear();
296
+ await this.#inner.destroy();
297
+ }
298
+
299
+ #wrapConnection(connection: DatabaseConnection): DatabaseConnection {
300
+ const existing = this.#innerToWrapped.get(connection);
301
+ if (existing) {
302
+ return existing;
303
+ }
304
+
305
+ const wrapped = new TracingConnection(connection, this.#sink);
306
+ this.#innerToWrapped.set(connection, wrapped);
307
+ return wrapped;
308
+ }
309
+
310
+ #unwrapConnection(connection: DatabaseConnection): DatabaseConnection {
311
+ if (connection instanceof TracingConnection) {
312
+ return connection.unwrap();
313
+ }
314
+ return connection;
315
+ }
316
+ }
317
+
318
+ function createTracingDialect(baseDialect: Dialect, sink: TraceSink): Dialect {
319
+ return {
320
+ createDriver: () => new TracingDriver(baseDialect.createDriver(), sink),
321
+ createAdapter: () => baseDialect.createAdapter(),
322
+ createQueryCompiler: () => baseDialect.createQueryCompiler(),
323
+ createIntrospector: (db) => baseDialect.createIntrospector(db),
324
+ };
325
+ }
326
+
327
+ function serializeCompiledQuery(compiledQuery: CompiledQuery): string {
328
+ return JSON.stringify({
329
+ sql: compiledQuery.sql,
330
+ parameters: compiledQuery.parameters.map((value) =>
331
+ normalizeParameterValue(value)
332
+ ),
333
+ });
334
+ }
335
+
336
+ function normalizeParameterValue(value: unknown): unknown {
337
+ if (typeof value === 'bigint') {
338
+ return { type: 'bigint', value: value.toString() };
339
+ }
340
+ if (value instanceof Date) {
341
+ return { type: 'date', value: value.toISOString() };
342
+ }
343
+ if (value instanceof Uint8Array) {
344
+ return { type: 'bytes', value: Array.from(value) };
345
+ }
346
+ if (Array.isArray(value)) {
347
+ return value.map((entry) => normalizeParameterValue(entry));
348
+ }
349
+ if (value && typeof value === 'object') {
350
+ return Object.fromEntries(
351
+ Object.entries(value)
352
+ .sort(([left], [right]) => left.localeCompare(right))
353
+ .map(([key, entry]) => [key, normalizeParameterValue(entry)])
354
+ );
355
+ }
356
+ return value;
357
+ }
358
+
359
+ function hashTrace(entries: string[]): string {
360
+ return hashString(entries.join('\n'));
361
+ }
362
+
363
+ async function createSqliteChecksumDb<DB>(sink: TraceSink): Promise<{
364
+ db: Kysely<DB>;
365
+ dispose: () => Promise<void>;
366
+ }> {
367
+ try {
368
+ const bunSqliteSpecifier = 'bun:sqlite';
369
+ const sqliteModule = await import(bunSqliteSpecifier);
370
+ const dialectModule = await import('kysely-bun-sqlite');
371
+ const sqliteDb = new sqliteModule.Database(':memory:');
372
+ const dialect = createTracingDialect(
373
+ new dialectModule.BunSqliteDialect({ database: sqliteDb }),
374
+ sink
375
+ );
376
+
377
+ return {
378
+ db: new Kysely<DB>({ dialect }),
379
+ dispose: async () => {
380
+ sqliteDb.close();
381
+ },
382
+ };
383
+ } catch {
384
+ // Fall back to better-sqlite3 outside Bun.
385
+ }
386
+
387
+ try {
388
+ const sqliteModule = await import('better-sqlite3');
389
+ const sqliteDb = new sqliteModule.default(':memory:');
390
+ const dialect = createTracingDialect(
391
+ new SqliteDialect({ database: sqliteDb }),
392
+ sink
393
+ );
394
+
395
+ return {
396
+ db: new Kysely<DB>({ dialect }),
397
+ dispose: async () => {
398
+ sqliteDb.close();
399
+ },
400
+ };
401
+ } catch (error) {
402
+ const message = error instanceof Error ? error.message : String(error);
403
+ throw new Error(
404
+ `Deterministic migration checksums for sqlite require an in-memory sqlite runtime in this environment. ${message}`
405
+ );
406
+ }
407
+ }
408
+
409
+ async function createPostgresChecksumDb<DB>(sink: TraceSink): Promise<{
410
+ db: Kysely<DB>;
411
+ dispose: () => Promise<void>;
412
+ }> {
413
+ const pglite = await PGlite.create();
414
+ const dialect = createTracingDialect(new PGliteDialect(pglite), sink);
415
+
416
+ return {
417
+ db: new Kysely<DB>({ dialect }),
418
+ dispose: async () => {
419
+ await pglite.close();
420
+ },
421
+ };
422
+ }
423
+
424
+ async function createChecksumDb<DB>(
425
+ dialect: MigrationChecksumDialect,
426
+ sink: TraceSink
427
+ ): Promise<{
428
+ db: Kysely<DB>;
429
+ dispose: () => Promise<void>;
430
+ }> {
431
+ if (dialect === 'postgres') {
432
+ return await createPostgresChecksumDb<DB>(sink);
433
+ }
434
+
435
+ return await createSqliteChecksumDb<DB>(sink);
436
+ }
437
+
438
+ async function computeDeterministicChecksum<DB>(
439
+ migrations: DefinedMigrations<DB>,
440
+ migration: ParsedMigration<DB>,
441
+ dialect: MigrationChecksumDialect
442
+ ): Promise<string> {
443
+ const sink: TraceSink = { enabled: false, entries: [] };
444
+ const { db, dispose } = await createChecksumDb<DB>(dialect, sink);
445
+
446
+ try {
447
+ for (const current of migrations.migrations) {
448
+ if (current.version > migration.version) {
449
+ break;
450
+ }
451
+
452
+ sink.enabled = current.version === migration.version;
453
+ await current.up(db);
454
+ sink.enabled = false;
455
+
456
+ if (current.version === migration.version) {
457
+ break;
458
+ }
459
+ }
460
+
461
+ return hashTrace(sink.entries);
462
+ } finally {
463
+ await db.destroy();
464
+ await dispose();
465
+ }
466
+ }
467
+
468
+ export function inferMigrationChecksumDialect<DB>(
469
+ db: KyselyInstance<DB>
470
+ ): MigrationChecksumDialect | null {
471
+ const adapter = db.getExecutor().adapter;
472
+ const adapterName = adapter.constructor.name;
473
+
474
+ if (adapter instanceof SqliteAdapter || adapterName === 'SqliteAdapter') {
475
+ return 'sqlite';
476
+ }
477
+
478
+ if (adapter instanceof PostgresAdapter || adapterName === 'PostgresAdapter') {
479
+ return 'postgres';
480
+ }
481
+
482
+ return null;
483
+ }
484
+
485
+ export async function getMigrationChecksum<DB>(
486
+ migrations: DefinedMigrations<DB>,
487
+ migration: ParsedMigration<DB>,
488
+ dialect: MigrationChecksumDialect
489
+ ): Promise<string | null> {
490
+ if (migration.checksum === 'disabled') {
491
+ return null;
492
+ }
493
+
494
+ let dialectCache = checksumCache.get(migration);
495
+ if (!dialectCache) {
496
+ dialectCache = new Map();
497
+ checksumCache.set(migration, dialectCache);
498
+ }
499
+
500
+ if (dialectCache.has(dialect)) {
501
+ return dialectCache.get(dialect) ?? null;
502
+ }
503
+
504
+ const checksum = await computeDeterministicChecksum(
505
+ migrations,
506
+ migration,
507
+ dialect
508
+ );
509
+
510
+ dialectCache.set(dialect, checksum);
511
+ return checksum;
512
+ }
513
+
514
+ export function getLegacyMigrationChecksum<DB>(
515
+ migration: ParsedMigration<DB>
516
+ ): string {
517
+ return hashString(normalizeLegacySource(migration.up.toString()));
518
+ }
519
+
520
+ export function getMigrationChecksumAlgorithm<DB>(
521
+ migration: ParsedMigration<DB>
522
+ ): MigrationChecksumAlgorithm {
523
+ if (migration.checksum === 'disabled') {
524
+ return DISABLED_MIGRATION_CHECKSUM_ALGORITHM;
525
+ }
526
+
527
+ return SQL_TRACE_MIGRATION_CHECKSUM_ALGORITHM;
528
+ }