orez 0.0.47 → 0.0.49
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/dist/admin/http-proxy.d.ts.map +1 -1
- package/dist/admin/http-proxy.js.map +1 -1
- package/dist/admin/log-store.d.ts.map +1 -1
- package/dist/admin/log-store.js.map +1 -1
- package/dist/admin/server.d.ts +2 -2
- package/dist/admin/server.d.ts.map +1 -1
- package/dist/admin/server.js.map +1 -1
- package/dist/admin/ui.d.ts.map +1 -1
- package/dist/admin/ui.js +2 -2
- package/dist/admin/ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -112
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +0 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -5
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +91 -249
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +0 -9
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +1 -24
- package/dist/log.js.map +1 -1
- package/dist/mutex.d.ts.map +1 -1
- package/dist/mutex.js +2 -13
- package/dist/mutex.js.map +1 -1
- package/dist/pg-proxy.d.ts +2 -3
- package/dist/pg-proxy.d.ts.map +1 -1
- package/dist/pg-proxy.js +167 -377
- package/dist/pg-proxy.js.map +1 -1
- package/dist/pglite-manager.d.ts +0 -1
- package/dist/pglite-manager.d.ts.map +1 -1
- package/dist/pglite-manager.js +1 -1
- package/dist/pglite-manager.js.map +1 -1
- package/dist/replication/change-tracker.d.ts +0 -6
- package/dist/replication/change-tracker.d.ts.map +1 -1
- package/dist/replication/change-tracker.js +0 -74
- package/dist/replication/change-tracker.js.map +1 -1
- package/dist/replication/handler.d.ts.map +1 -1
- package/dist/replication/handler.js +5 -47
- package/dist/replication/handler.js.map +1 -1
- package/dist/vite-plugin.d.ts +0 -3
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +0 -24
- package/dist/vite-plugin.js.map +1 -1
- package/package.json +5 -4
- package/src/admin/http-proxy.ts +5 -1
- package/src/admin/log-store.ts +4 -1
- package/src/admin/server.ts +7 -3
- package/src/admin/ui.ts +682 -680
- package/src/cli.ts +6 -111
- package/src/config.ts +0 -10
- package/src/index.ts +92 -262
- package/src/integration/integration.test.ts +264 -133
- package/src/log.ts +1 -25
- package/src/mutex.ts +2 -12
- package/src/pg-proxy.ts +187 -449
- package/src/pglite-manager.ts +1 -1
- package/src/replication/change-tracker.ts +0 -92
- package/src/replication/handler.ts +4 -50
- package/src/shim/hooks.mjs +34 -1
- package/src/vite-plugin.ts +0 -28
- package/src/wasm-sqlite.test.ts +1 -2
package/src/pglite-manager.ts
CHANGED
|
@@ -67,35 +67,6 @@ export async function installChangeTracking(db: PGlite): Promise<void> {
|
|
|
67
67
|
$$ LANGUAGE plpgsql;
|
|
68
68
|
`)
|
|
69
69
|
|
|
70
|
-
// auto-install change tracking on tables created after startup (e.g. via restore
|
|
71
|
-
// or wire protocol). uses a DDL event trigger that fires on CREATE TABLE.
|
|
72
|
-
await db.exec(`
|
|
73
|
-
CREATE OR REPLACE FUNCTION public._zero_auto_track() RETURNS event_trigger AS $$
|
|
74
|
-
DECLARE
|
|
75
|
-
obj record;
|
|
76
|
-
BEGIN
|
|
77
|
-
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
|
|
78
|
-
WHERE command_tag = 'CREATE TABLE'
|
|
79
|
-
LOOP
|
|
80
|
-
IF obj.schema_name = 'public'
|
|
81
|
-
AND obj.object_identity NOT LIKE '%._zero_%'
|
|
82
|
-
AND obj.object_identity NOT LIKE '%.migrations'
|
|
83
|
-
THEN
|
|
84
|
-
EXECUTE format(
|
|
85
|
-
'CREATE TRIGGER _zero_change_trigger AFTER INSERT OR UPDATE OR DELETE ON %s FOR EACH ROW EXECUTE FUNCTION public._zero_track_change()',
|
|
86
|
-
obj.object_identity
|
|
87
|
-
);
|
|
88
|
-
END IF;
|
|
89
|
-
END LOOP;
|
|
90
|
-
END;
|
|
91
|
-
$$ LANGUAGE plpgsql;
|
|
92
|
-
|
|
93
|
-
DROP EVENT TRIGGER IF EXISTS _zero_auto_track_trigger;
|
|
94
|
-
CREATE EVENT TRIGGER _zero_auto_track_trigger ON ddl_command_end
|
|
95
|
-
WHEN TAG IN ('CREATE TABLE')
|
|
96
|
-
EXECUTE FUNCTION public._zero_auto_track();
|
|
97
|
-
`)
|
|
98
|
-
|
|
99
70
|
// install triggers on all public tables
|
|
100
71
|
await installTriggersOnAllTables(db)
|
|
101
72
|
}
|
|
@@ -169,58 +140,6 @@ async function installTriggersOnAllTables(db: PGlite): Promise<void> {
|
|
|
169
140
|
log.debug.pglite(`installed change tracking triggers on ${count} tables`)
|
|
170
141
|
}
|
|
171
142
|
|
|
172
|
-
/**
|
|
173
|
-
* re-install change tracking triggers on any public tables that don't have them.
|
|
174
|
-
* catches tables created between startup and replication start.
|
|
175
|
-
*/
|
|
176
|
-
export async function ensureChangeTrackingOnAllTables(db: PGlite): Promise<void> {
|
|
177
|
-
const pubName = process.env.ZERO_APP_PUBLICATIONS
|
|
178
|
-
let tables: { tablename: string }[]
|
|
179
|
-
|
|
180
|
-
if (pubName) {
|
|
181
|
-
const result = await db.query<{ tablename: string }>(
|
|
182
|
-
`SELECT tablename FROM pg_publication_tables
|
|
183
|
-
WHERE pubname = $1
|
|
184
|
-
AND schemaname = 'public'
|
|
185
|
-
AND tablename NOT LIKE '_zero_%'`,
|
|
186
|
-
[pubName]
|
|
187
|
-
)
|
|
188
|
-
tables = result.rows
|
|
189
|
-
} else {
|
|
190
|
-
const result = await db.query<{ tablename: string }>(
|
|
191
|
-
`SELECT tablename FROM pg_tables
|
|
192
|
-
WHERE schemaname = 'public'
|
|
193
|
-
AND tablename NOT IN ('migrations', '_zero_changes')
|
|
194
|
-
AND tablename NOT LIKE '_zero_%'`
|
|
195
|
-
)
|
|
196
|
-
tables = result.rows
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// find tables missing the change trigger
|
|
200
|
-
const triggered = await db.query<{ event_object_table: string }>(
|
|
201
|
-
`SELECT DISTINCT event_object_table FROM information_schema.triggers
|
|
202
|
-
WHERE trigger_name = '_zero_change_trigger'
|
|
203
|
-
AND event_object_schema = 'public'`
|
|
204
|
-
)
|
|
205
|
-
const hasTracker = new Set(triggered.rows.map((r) => r.event_object_table))
|
|
206
|
-
|
|
207
|
-
let count = 0
|
|
208
|
-
for (const { tablename } of tables) {
|
|
209
|
-
if (hasTracker.has(tablename)) continue
|
|
210
|
-
const quoted = quoteIdent(tablename)
|
|
211
|
-
await db.exec(`
|
|
212
|
-
CREATE TRIGGER _zero_change_trigger
|
|
213
|
-
AFTER INSERT OR UPDATE OR DELETE ON public.${quoted}
|
|
214
|
-
FOR EACH ROW EXECUTE FUNCTION public._zero_track_change();
|
|
215
|
-
`)
|
|
216
|
-
count++
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (count > 0) {
|
|
220
|
-
log.debug.pglite(`installed change tracking on ${count} new tables`)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
143
|
/**
|
|
225
144
|
* install change tracking triggers on tables in shard schemas.
|
|
226
145
|
* zero-cache creates shard schemas (e.g. chat_0) with clients/mutations
|
|
@@ -276,17 +195,6 @@ export async function getChangesSince(
|
|
|
276
195
|
return result.rows
|
|
277
196
|
}
|
|
278
197
|
|
|
279
|
-
export async function purgeConsumedChanges(
|
|
280
|
-
db: PGlite,
|
|
281
|
-
watermark: number
|
|
282
|
-
): Promise<number> {
|
|
283
|
-
const result = await db.query<{ count: string }>(
|
|
284
|
-
'WITH deleted AS (DELETE FROM public._zero_changes WHERE watermark <= $1 RETURNING 1) SELECT count(*)::text AS count FROM deleted',
|
|
285
|
-
[watermark]
|
|
286
|
-
)
|
|
287
|
-
return Number(result.rows[0]?.count || 0)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
198
|
export async function getCurrentWatermark(db: PGlite): Promise<number> {
|
|
291
199
|
const result = await db.query<{ last_value: string; is_called: boolean }>(
|
|
292
200
|
'SELECT last_value, is_called FROM public._zero_watermark'
|
|
@@ -10,9 +10,7 @@ import { log } from '../log.js'
|
|
|
10
10
|
import {
|
|
11
11
|
getChangesSince,
|
|
12
12
|
getCurrentWatermark,
|
|
13
|
-
purgeConsumedChanges,
|
|
14
13
|
installTriggersOnShardTables,
|
|
15
|
-
ensureChangeTrackingOnAllTables,
|
|
16
14
|
type ChangeRecord,
|
|
17
15
|
} from './change-tracker.js'
|
|
18
16
|
import {
|
|
@@ -33,9 +31,6 @@ import {
|
|
|
33
31
|
import type { Mutex } from '../mutex.js'
|
|
34
32
|
import type { PGlite } from '@electric-sql/pglite'
|
|
35
33
|
|
|
36
|
-
// track concurrent replication handlers to detect reconnect-purge race
|
|
37
|
-
let activeHandlerCount = 0
|
|
38
|
-
|
|
39
34
|
export interface ReplicationWriter {
|
|
40
35
|
write(data: Uint8Array): void
|
|
41
36
|
}
|
|
@@ -266,9 +261,6 @@ export async function handleStartReplication(
|
|
|
266
261
|
db: PGlite,
|
|
267
262
|
mutex: Mutex
|
|
268
263
|
): Promise<void> {
|
|
269
|
-
activeHandlerCount++
|
|
270
|
-
const handlerId = activeHandlerCount
|
|
271
|
-
console.info(`[orez-repl#${handlerId}] START_REPLICATION (active handlers: ${activeHandlerCount})`)
|
|
272
264
|
log.debug.proxy('replication: entering streaming mode')
|
|
273
265
|
|
|
274
266
|
// send CopyBothResponse to enter streaming mode
|
|
@@ -292,9 +284,6 @@ export async function handleStartReplication(
|
|
|
292
284
|
// "already in transaction" errors when they interleave.
|
|
293
285
|
await mutex.acquire()
|
|
294
286
|
try {
|
|
295
|
-
// install change tracking triggers on any tables created after startup
|
|
296
|
-
await ensureChangeTrackingOnAllTables(db)
|
|
297
|
-
|
|
298
287
|
// install change tracking triggers on shard schema tables (e.g. chat_0.clients)
|
|
299
288
|
// these track zero-cache's lastMutationID for .server promise resolution
|
|
300
289
|
await installTriggersOnShardTables(db)
|
|
@@ -463,21 +452,13 @@ export async function handleStartReplication(
|
|
|
463
452
|
mutex.release()
|
|
464
453
|
}
|
|
465
454
|
|
|
466
|
-
console.info(`[orez-repl#${handlerId}] setup complete, starting poll (lastWatermark=${lastWatermark})`)
|
|
467
|
-
|
|
468
455
|
// track which tables we've sent RELATION messages for
|
|
469
456
|
const sentRelations = new Set<string>()
|
|
470
457
|
let txCounter = 1
|
|
471
458
|
|
|
472
459
|
// polling + notification loop
|
|
473
|
-
|
|
474
|
-
const pollIntervalIdle = 500
|
|
475
|
-
const pollIntervalCatchUp = 20
|
|
476
|
-
const batchSize = 2000
|
|
477
|
-
const purgeEveryN = 10
|
|
460
|
+
const pollInterval = 500
|
|
478
461
|
let running = true
|
|
479
|
-
let pollsSincePurge = 0
|
|
480
|
-
let lastIdleLog = 0
|
|
481
462
|
|
|
482
463
|
const poll = async () => {
|
|
483
464
|
while (running) {
|
|
@@ -486,14 +467,12 @@ export async function handleStartReplication(
|
|
|
486
467
|
await mutex.acquire()
|
|
487
468
|
let changes: Awaited<ReturnType<typeof getChangesSince>>
|
|
488
469
|
try {
|
|
489
|
-
changes = await getChangesSince(db, lastWatermark,
|
|
470
|
+
changes = await getChangesSince(db, lastWatermark, 100)
|
|
490
471
|
} finally {
|
|
491
472
|
mutex.release()
|
|
492
473
|
}
|
|
493
474
|
|
|
494
475
|
if (changes.length > 0) {
|
|
495
|
-
const tables = [...new Set(changes.map(c => c.table_name))].join(',')
|
|
496
|
-
console.info(`[orez-repl#${handlerId}] found ${changes.length} changes [${tables}] (wm ${lastWatermark}→${changes[changes.length - 1].watermark}, type=${typeof changes[0].watermark})`)
|
|
497
476
|
await streamChanges(
|
|
498
477
|
changes,
|
|
499
478
|
writer,
|
|
@@ -504,37 +483,13 @@ export async function handleStartReplication(
|
|
|
504
483
|
columnTypeOids
|
|
505
484
|
)
|
|
506
485
|
lastWatermark = changes[changes.length - 1].watermark
|
|
507
|
-
|
|
508
|
-
// purge consumed changes periodically to free wasm memory
|
|
509
|
-
pollsSincePurge++
|
|
510
|
-
if (pollsSincePurge >= purgeEveryN) {
|
|
511
|
-
pollsSincePurge = 0
|
|
512
|
-
await mutex.acquire()
|
|
513
|
-
try {
|
|
514
|
-
const purged = await purgeConsumedChanges(db, lastWatermark)
|
|
515
|
-
if (purged > 0) {
|
|
516
|
-
console.info(`[orez-repl#${handlerId}] purged ${purged} changes (wm<=${lastWatermark})`)
|
|
517
|
-
}
|
|
518
|
-
} finally {
|
|
519
|
-
mutex.release()
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
} else {
|
|
523
|
-
// throttled idle logging (every 10s)
|
|
524
|
-
const now = Date.now()
|
|
525
|
-
if (now - lastIdleLog > 10000) {
|
|
526
|
-
lastIdleLog = now
|
|
527
|
-
console.info(`[orez-repl#${handlerId}] idle (lastWatermark=${lastWatermark}, type=${typeof lastWatermark})`)
|
|
528
|
-
}
|
|
529
486
|
}
|
|
530
487
|
|
|
531
488
|
// send keepalive
|
|
532
489
|
const ts = nowMicros()
|
|
533
490
|
writer.write(encodeKeepalive(currentLsn, ts, false))
|
|
534
491
|
|
|
535
|
-
|
|
536
|
-
const delay = changes.length >= batchSize ? pollIntervalCatchUp : pollIntervalIdle
|
|
537
|
-
await new Promise((resolve) => setTimeout(resolve, delay))
|
|
492
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval))
|
|
538
493
|
} catch (err: unknown) {
|
|
539
494
|
const msg = err instanceof Error ? err.message : String(err)
|
|
540
495
|
log.debug.proxy(`replication poll error: ${msg}`)
|
|
@@ -549,8 +504,7 @@ export async function handleStartReplication(
|
|
|
549
504
|
|
|
550
505
|
log.debug.proxy('replication: starting poll loop')
|
|
551
506
|
await poll()
|
|
552
|
-
|
|
553
|
-
console.info(`[orez-repl#${handlerId}] poll loop exited (remaining handlers: ${activeHandlerCount})`)
|
|
507
|
+
log.debug.proxy('replication: poll loop exited')
|
|
554
508
|
}
|
|
555
509
|
|
|
556
510
|
async function streamChanges(
|
package/src/shim/hooks.mjs
CHANGED
|
@@ -20,6 +20,13 @@ export function load(url, context, nextLoad) {
|
|
|
20
20
|
format: 'module',
|
|
21
21
|
shortCircuit: true,
|
|
22
22
|
source: `
|
|
23
|
+
// catch uncaught exceptions from bedrock-sqlite wasm clearly
|
|
24
|
+
process.on('uncaughtException', (err) => {
|
|
25
|
+
console.error('[orez-shim] UNCAUGHT EXCEPTION:', err?.message || err);
|
|
26
|
+
console.error('[orez-shim] code:', err?.code, 'name:', err?.name);
|
|
27
|
+
console.error('[orez-shim] stack:', err?.stack?.split('\\n').slice(0, 5).join('\\n'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
23
30
|
import { createRequire } from 'node:module';
|
|
24
31
|
const require = createRequire('${BEDROCK_PATH}');
|
|
25
32
|
const mod = require('${BEDROCK_PATH}');
|
|
@@ -27,13 +34,39 @@ const OrigDatabase = mod.Database;
|
|
|
27
34
|
const SqliteError = mod.SqliteError;
|
|
28
35
|
function Database(...args) {
|
|
29
36
|
const db = new OrigDatabase(...args);
|
|
30
|
-
try { db.pragma('busy_timeout = 30000'); db.pragma('synchronous = normal'); } catch(e) {}
|
|
37
|
+
try { db.pragma('journal_mode = delete'); db.pragma('busy_timeout = 30000'); db.pragma('synchronous = normal'); } catch(e) {}
|
|
31
38
|
return db;
|
|
32
39
|
}
|
|
33
40
|
Database.prototype = OrigDatabase.prototype;
|
|
34
41
|
Database.prototype.constructor = Database;
|
|
35
42
|
Object.keys(OrigDatabase).forEach(k => { Database[k] = OrigDatabase[k]; });
|
|
36
43
|
Database.prototype.unsafeMode = function() { return this; };
|
|
44
|
+
// wrap pragma to skip optimize (corrupts wasm vfs) and swallow sqlite errors
|
|
45
|
+
const origPragma = OrigDatabase.prototype.pragma;
|
|
46
|
+
Database.prototype.pragma = function(str, opts) {
|
|
47
|
+
if (str && str.trim().toLowerCase().startsWith('optimize')) return [];
|
|
48
|
+
try { return origPragma.call(this, str, opts); }
|
|
49
|
+
catch(e) { if (e && (e.code === 'SQLITE_CORRUPT' || e.code === 'SQLITE_IOERR')) return []; throw e; }
|
|
50
|
+
};
|
|
51
|
+
// wrap close to swallow wasm errors during shutdown
|
|
52
|
+
const origClose = OrigDatabase.prototype.close;
|
|
53
|
+
Database.prototype.close = function() {
|
|
54
|
+
try { return origClose.call(this); }
|
|
55
|
+
catch(e) { console.error('[orez-shim] close error (swallowed):', e?.message || e); }
|
|
56
|
+
};
|
|
57
|
+
// trace writes to _zero.changeLog and _zero.replicationState to debug view-syncer
|
|
58
|
+
const origRun = OrigDatabase.prototype.run;
|
|
59
|
+
Database.prototype.run = function(sql, ...args) {
|
|
60
|
+
if (typeof sql === 'string') {
|
|
61
|
+
if (sql.includes('_zero.changeLog')) {
|
|
62
|
+
console.info('[orez-shim] changeLog write:', sql.slice(0, 120), args.length ? JSON.stringify(args[0]).slice(0, 80) : '');
|
|
63
|
+
}
|
|
64
|
+
if (sql.includes('_zero.replicationState') && (sql.includes('UPDATE') || sql.includes('INSERT'))) {
|
|
65
|
+
console.info('[orez-shim] replicationState update:', sql.slice(0, 120), args.length ? JSON.stringify(args[0]).slice(0, 80) : '');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return origRun.call(this, sql, ...args);
|
|
69
|
+
};
|
|
37
70
|
if (!Database.prototype.defaultSafeIntegers) Database.prototype.defaultSafeIntegers = function() { return this; };
|
|
38
71
|
if (!Database.prototype.serialize) Database.prototype.serialize = function() { throw new Error('not supported in wasm'); };
|
|
39
72
|
if (!Database.prototype.backup) Database.prototype.backup = function() { throw new Error('not supported in wasm'); };
|
package/src/vite-plugin.ts
CHANGED
|
@@ -7,21 +7,16 @@ import type { Plugin } from 'vite'
|
|
|
7
7
|
export interface OrezPluginOptions extends Partial<ZeroLiteConfig> {
|
|
8
8
|
s3?: boolean
|
|
9
9
|
s3Port?: number
|
|
10
|
-
admin?: boolean
|
|
11
|
-
adminPort?: number
|
|
12
|
-
adminLogs?: boolean
|
|
13
10
|
}
|
|
14
11
|
|
|
15
12
|
export default function orez(options?: OrezPluginOptions): Plugin {
|
|
16
13
|
let stop: (() => Promise<void>) | null = null
|
|
17
14
|
let s3Server: Server | null = null
|
|
18
|
-
let adminServer: Server | null = null
|
|
19
15
|
|
|
20
16
|
return {
|
|
21
17
|
name: 'orez',
|
|
22
18
|
|
|
23
19
|
async configureServer(server) {
|
|
24
|
-
const startTime = Date.now()
|
|
25
20
|
const result = await startZeroLite(options)
|
|
26
21
|
stop = result.stop
|
|
27
22
|
|
|
@@ -33,30 +28,7 @@ export default function orez(options?: OrezPluginOptions): Plugin {
|
|
|
33
28
|
})
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
if (options?.admin && result.logStore) {
|
|
37
|
-
const { findPort } = await import('./port.js')
|
|
38
|
-
const { log } = await import('./log.js')
|
|
39
|
-
const adminPort = options.adminPort || (result.config.zeroPort + 2)
|
|
40
|
-
const resolvedPort = await findPort(adminPort)
|
|
41
|
-
const { startAdminServer } = await import('./admin/server.js')
|
|
42
|
-
adminServer = await startAdminServer({
|
|
43
|
-
port: resolvedPort,
|
|
44
|
-
logStore: result.logStore,
|
|
45
|
-
config: result.config,
|
|
46
|
-
zeroEnv: result.zeroEnv,
|
|
47
|
-
actions: result.actions,
|
|
48
|
-
startTime,
|
|
49
|
-
httpLog: result.httpLogStore || undefined,
|
|
50
|
-
})
|
|
51
|
-
log.orez(`admin: http://127.0.0.1:${resolvedPort}`)
|
|
52
|
-
if (result.config.adminLogs) {
|
|
53
|
-
const { resolve } = await import('node:path')
|
|
54
|
-
log.orez(`logs: ${resolve(result.config.dataDir, 'logs', 'orez.log')}`)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
31
|
server.httpServer?.on('close', async () => {
|
|
59
|
-
adminServer?.close()
|
|
60
32
|
s3Server?.close()
|
|
61
33
|
if (stop) {
|
|
62
34
|
await stop()
|
package/src/wasm-sqlite.test.ts
CHANGED
|
@@ -19,8 +19,7 @@ import { resolve } from 'node:path'
|
|
|
19
19
|
|
|
20
20
|
// import bedrock-sqlite directly (our wasm build)
|
|
21
21
|
// @ts-expect-error - CJS module
|
|
22
|
-
import
|
|
23
|
-
const { Database } = bedrockSqlite
|
|
22
|
+
import { Database } from 'bedrock-sqlite'
|
|
24
23
|
import { describe, test, expect, beforeEach, afterEach } from 'vitest'
|
|
25
24
|
|
|
26
25
|
// helper: temp db file
|