@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.
- package/README.md +29 -5
- package/dist/checksum.d.ts +11 -0
- package/dist/checksum.d.ts.map +1 -0
- package/dist/checksum.js +355 -0
- package/dist/checksum.js.map +1 -0
- package/dist/define.d.ts +22 -18
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +39 -166
- package/dist/define.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runner.d.ts +8 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +69 -15
- package/dist/runner.js.map +1 -1
- package/dist/tracking.d.ts.map +1 -1
- package/dist/tracking.js +32 -3
- package/dist/tracking.js.map +1 -1
- package/dist/types.d.ts +13 -15
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -2
- package/src/checksum.ts +528 -0
- package/src/define.ts +49 -201
- package/src/index.ts +1 -0
- package/src/runner.ts +134 -19
- package/src/tracking.ts +36 -3
- package/src/types.ts +19 -16
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,EAAE,GAAG,OAAO,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,6BAA6B,CAAC,EAAE,GAAG,OAAO;IACzD,kDAAkD;IAClD,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACpB,mDAAmD;IACnD,IAAI,
|
|
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-
|
|
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
|
}
|
package/src/checksum.ts
ADDED
|
@@ -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
|
+
}
|