hzl-core 1.6.0 → 1.7.1
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/__tests__/backup/backup-restore.test.js +13 -12
- package/dist/__tests__/backup/backup-restore.test.js.map +1 -1
- package/dist/__tests__/backup/import-export.test.js +41 -39
- package/dist/__tests__/backup/import-export.test.js.map +1 -1
- package/dist/__tests__/concurrency/stress.test.js +2 -2
- package/dist/__tests__/concurrency/stress.test.js.map +1 -1
- package/dist/__tests__/concurrency/worker.js +3 -2
- package/dist/__tests__/concurrency/worker.js.map +1 -1
- package/dist/__tests__/migrations/upgrade.test.js +132 -117
- package/dist/__tests__/migrations/upgrade.test.js.map +1 -1
- package/dist/__tests__/projections/rebuild-equivalence.test.js +2 -2
- package/dist/__tests__/projections/rebuild-equivalence.test.js.map +1 -1
- package/dist/__tests__/properties/invariants.test.js +2 -2
- package/dist/__tests__/properties/invariants.test.js.map +1 -1
- package/dist/db/__tests__/append-only.test.d.ts +2 -0
- package/dist/db/__tests__/append-only.test.d.ts.map +1 -0
- package/dist/db/__tests__/append-only.test.js +46 -0
- package/dist/db/__tests__/append-only.test.js.map +1 -0
- package/dist/db/__tests__/datastore.test.d.ts +2 -0
- package/dist/db/__tests__/datastore.test.d.ts.map +1 -0
- package/dist/db/__tests__/datastore.test.js +68 -0
- package/dist/db/__tests__/datastore.test.js.map +1 -0
- package/dist/db/__tests__/libsql-compat.test.d.ts +2 -0
- package/dist/db/__tests__/libsql-compat.test.d.ts.map +1 -0
- package/dist/db/__tests__/libsql-compat.test.js +56 -0
- package/dist/db/__tests__/libsql-compat.test.js.map +1 -0
- package/dist/db/__tests__/lock.test.d.ts +2 -0
- package/dist/db/__tests__/lock.test.d.ts.map +1 -0
- package/dist/db/__tests__/lock.test.js +77 -0
- package/dist/db/__tests__/lock.test.js.map +1 -0
- package/dist/db/__tests__/meta.test.d.ts +2 -0
- package/dist/db/__tests__/meta.test.d.ts.map +1 -0
- package/dist/db/__tests__/meta.test.js +50 -0
- package/dist/db/__tests__/meta.test.js.map +1 -0
- package/dist/db/__tests__/migrations.test.d.ts +2 -0
- package/dist/db/__tests__/migrations.test.d.ts.map +1 -0
- package/dist/db/__tests__/migrations.test.js +57 -0
- package/dist/db/__tests__/migrations.test.js.map +1 -0
- package/dist/db/__tests__/sync-policy.test.d.ts +2 -0
- package/dist/db/__tests__/sync-policy.test.d.ts.map +1 -0
- package/dist/db/__tests__/sync-policy.test.js +118 -0
- package/dist/db/__tests__/sync-policy.test.js.map +1 -0
- package/dist/db/__tests__/types.test.d.ts +2 -0
- package/dist/db/__tests__/types.test.d.ts.map +1 -0
- package/dist/db/__tests__/types.test.js +91 -0
- package/dist/db/__tests__/types.test.js.map +1 -0
- package/dist/db/datastore.d.ts +16 -0
- package/dist/db/datastore.d.ts.map +1 -0
- package/dist/db/datastore.js +159 -0
- package/dist/db/datastore.js.map +1 -0
- package/dist/db/lock.d.ts +38 -0
- package/dist/db/lock.d.ts.map +1 -0
- package/dist/db/lock.js +114 -0
- package/dist/db/lock.js.map +1 -0
- package/dist/db/meta.d.ts +28 -0
- package/dist/db/meta.d.ts.map +1 -0
- package/dist/db/meta.js +95 -0
- package/dist/db/meta.js.map +1 -0
- package/dist/db/migrations.d.ts +23 -3
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +69 -99
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/migrations.test.js +40 -105
- package/dist/db/migrations.test.js.map +1 -1
- package/dist/db/schema.d.ts +2 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +52 -10
- package/dist/db/schema.js.map +1 -1
- package/dist/db/sync-policy.d.ts +21 -0
- package/dist/db/sync-policy.d.ts.map +1 -0
- package/dist/db/sync-policy.js +62 -0
- package/dist/db/sync-policy.js.map +1 -0
- package/dist/db/test-utils.d.ts +33 -0
- package/dist/db/test-utils.d.ts.map +1 -0
- package/dist/db/test-utils.js +58 -0
- package/dist/db/test-utils.js.map +1 -0
- package/dist/db/{connection.d.ts → transaction.d.ts} +2 -4
- package/dist/db/transaction.d.ts.map +1 -0
- package/dist/db/{connection.js → transaction.js} +1 -23
- package/dist/db/transaction.js.map +1 -0
- package/dist/db/types.d.ts +199 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +43 -0
- package/dist/db/types.js.map +1 -0
- package/dist/events/store.d.ts +1 -1
- package/dist/events/store.d.ts.map +1 -1
- package/dist/events/store.test.js +2 -4
- package/dist/events/store.test.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +13 -11
- package/dist/index.test.js.map +1 -1
- package/dist/projections/comments-checkpoints.d.ts +1 -1
- package/dist/projections/comments-checkpoints.d.ts.map +1 -1
- package/dist/projections/comments-checkpoints.test.js +3 -4
- package/dist/projections/comments-checkpoints.test.js.map +1 -1
- package/dist/projections/dependencies.d.ts +1 -1
- package/dist/projections/dependencies.d.ts.map +1 -1
- package/dist/projections/dependencies.test.js +3 -4
- package/dist/projections/dependencies.test.js.map +1 -1
- package/dist/projections/engine.d.ts +9 -2
- package/dist/projections/engine.d.ts.map +1 -1
- package/dist/projections/engine.js +22 -6
- package/dist/projections/engine.js.map +1 -1
- package/dist/projections/engine.test.js +3 -4
- package/dist/projections/engine.test.js.map +1 -1
- package/dist/projections/projects.d.ts +1 -1
- package/dist/projections/projects.d.ts.map +1 -1
- package/dist/projections/projects.test.js +2 -20
- package/dist/projections/projects.test.js.map +1 -1
- package/dist/projections/rebuild.d.ts +1 -1
- package/dist/projections/rebuild.d.ts.map +1 -1
- package/dist/projections/rebuild.test.js +3 -4
- package/dist/projections/rebuild.test.js.map +1 -1
- package/dist/projections/search.d.ts +1 -1
- package/dist/projections/search.d.ts.map +1 -1
- package/dist/projections/search.test.js +3 -4
- package/dist/projections/search.test.js.map +1 -1
- package/dist/projections/tags.d.ts +1 -1
- package/dist/projections/tags.d.ts.map +1 -1
- package/dist/projections/tags.test.js +3 -4
- package/dist/projections/tags.test.js.map +1 -1
- package/dist/projections/tasks-current.d.ts +1 -1
- package/dist/projections/tasks-current.d.ts.map +1 -1
- package/dist/projections/tasks-current.test.js +3 -4
- package/dist/projections/tasks-current.test.js.map +1 -1
- package/dist/projections/types.d.ts +1 -1
- package/dist/projections/types.d.ts.map +1 -1
- package/dist/services/backup-service.d.ts +2 -2
- package/dist/services/backup-service.d.ts.map +1 -1
- package/dist/services/backup-service.js +13 -3
- package/dist/services/backup-service.js.map +1 -1
- package/dist/services/project-service.d.ts +1 -1
- package/dist/services/project-service.d.ts.map +1 -1
- package/dist/services/project-service.js +1 -1
- package/dist/services/project-service.js.map +1 -1
- package/dist/services/project-service.test.js +2 -13
- package/dist/services/project-service.test.js.map +1 -1
- package/dist/services/search-service.d.ts +1 -1
- package/dist/services/search-service.d.ts.map +1 -1
- package/dist/services/search-service.test.js +3 -4
- package/dist/services/search-service.test.js.map +1 -1
- package/dist/services/task-service.d.ts +1 -1
- package/dist/services/task-service.d.ts.map +1 -1
- package/dist/services/task-service.js +1 -1
- package/dist/services/task-service.js.map +1 -1
- package/dist/services/task-service.test.js +3 -4
- package/dist/services/task-service.test.js.map +1 -1
- package/dist/services/validation-service.d.ts +1 -1
- package/dist/services/validation-service.d.ts.map +1 -1
- package/dist/services/validation-service.test.js +2 -4
- package/dist/services/validation-service.test.js.map +1 -1
- package/package.json +4 -4
- package/dist/db/connection.d.ts.map +0 -1
- package/dist/db/connection.js.map +0 -1
- package/dist/db/connection.test.d.ts +0 -2
- package/dist/db/connection.test.d.ts.map +0 -1
- package/dist/db/connection.test.js +0 -63
- package/dist/db/connection.test.js.map +0 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { createDatastore } from '../datastore.js';
|
|
6
|
+
describe('createDatastore', () => {
|
|
7
|
+
const testDir = path.join(os.tmpdir(), `datastore-test-${Date.now()}`);
|
|
8
|
+
let datastore = null;
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
datastore?.close();
|
|
11
|
+
datastore = null;
|
|
12
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
it('creates events.db and cache.db with default paths', () => {
|
|
15
|
+
const config = {
|
|
16
|
+
events: { path: path.join(testDir, 'events.db'), syncMode: 'offline', readYourWrites: true },
|
|
17
|
+
cache: { path: path.join(testDir, 'cache.db') },
|
|
18
|
+
};
|
|
19
|
+
datastore = createDatastore(config);
|
|
20
|
+
expect(fs.existsSync(path.join(testDir, 'events.db'))).toBe(true);
|
|
21
|
+
expect(fs.existsSync(path.join(testDir, 'cache.db'))).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it('initializes events.db schema', () => {
|
|
24
|
+
const config = {
|
|
25
|
+
events: { path: path.join(testDir, 'events.db'), syncMode: 'offline', readYourWrites: true },
|
|
26
|
+
cache: { path: path.join(testDir, 'cache.db') },
|
|
27
|
+
};
|
|
28
|
+
datastore = createDatastore(config);
|
|
29
|
+
// Check events table exists
|
|
30
|
+
const tables = datastore.eventsDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='events'").all();
|
|
31
|
+
expect(tables.length).toBe(1);
|
|
32
|
+
});
|
|
33
|
+
it('initializes cache.db schema', () => {
|
|
34
|
+
const config = {
|
|
35
|
+
events: { path: path.join(testDir, 'events.db'), syncMode: 'offline', readYourWrites: true },
|
|
36
|
+
cache: { path: path.join(testDir, 'cache.db') },
|
|
37
|
+
};
|
|
38
|
+
datastore = createDatastore(config);
|
|
39
|
+
// Check tasks_current table exists
|
|
40
|
+
const tables = datastore.cacheDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='tasks_current'").all();
|
|
41
|
+
expect(tables.length).toBe(1);
|
|
42
|
+
});
|
|
43
|
+
it.skip('reports sync mode when syncUrl configured', () => {
|
|
44
|
+
const config = {
|
|
45
|
+
events: {
|
|
46
|
+
path: path.join(testDir, 'events.db'),
|
|
47
|
+
syncUrl: 'libsql://test.turso.io',
|
|
48
|
+
authToken: 'test-token',
|
|
49
|
+
syncMode: 'offline',
|
|
50
|
+
readYourWrites: true,
|
|
51
|
+
},
|
|
52
|
+
cache: { path: path.join(testDir, 'cache.db') },
|
|
53
|
+
};
|
|
54
|
+
datastore = createDatastore(config);
|
|
55
|
+
expect(datastore.mode).toBe('offline-sync');
|
|
56
|
+
expect(datastore.syncUrl).toBe('libsql://test.turso.io');
|
|
57
|
+
});
|
|
58
|
+
it('reports local-only mode when no syncUrl', () => {
|
|
59
|
+
const config = {
|
|
60
|
+
events: { path: path.join(testDir, 'events.db'), syncMode: 'offline', readYourWrites: true },
|
|
61
|
+
cache: { path: path.join(testDir, 'cache.db') },
|
|
62
|
+
};
|
|
63
|
+
datastore = createDatastore(config);
|
|
64
|
+
expect(datastore.mode).toBe('local-only');
|
|
65
|
+
expect(datastore.syncUrl).toBeUndefined();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=datastore.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datastore.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/datastore.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libsql-compat.test.d.ts","sourceRoot":"","sources":["../../../src/db/__tests__/libsql-compat.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'libsql';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
describe('libsql API compatibility', () => {
|
|
7
|
+
const testDbPath = path.join(os.tmpdir(), `libsql-test-${Date.now()}.db`);
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
if (fs.existsSync(testDbPath)) {
|
|
10
|
+
fs.unlinkSync(testDbPath);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
it('supports better-sqlite3 compatible API', () => {
|
|
14
|
+
const db = new Database(testDbPath);
|
|
15
|
+
// Schema creation
|
|
16
|
+
db.exec('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
|
|
17
|
+
// Prepared statements
|
|
18
|
+
const insert = db.prepare('INSERT INTO test (name) VALUES (?)');
|
|
19
|
+
const result = insert.run('hello');
|
|
20
|
+
expect(result.changes).toBe(1);
|
|
21
|
+
// Query
|
|
22
|
+
const select = db.prepare('SELECT * FROM test WHERE id = ?');
|
|
23
|
+
const row = select.get(result.lastInsertRowid);
|
|
24
|
+
expect(row.name).toBe('hello');
|
|
25
|
+
// Transaction
|
|
26
|
+
const tx = db.transaction(() => {
|
|
27
|
+
insert.run('world');
|
|
28
|
+
return db.prepare('SELECT COUNT(*) as count FROM test').get();
|
|
29
|
+
});
|
|
30
|
+
const txResult = tx();
|
|
31
|
+
expect(txResult.count).toBe(2);
|
|
32
|
+
db.close();
|
|
33
|
+
});
|
|
34
|
+
it('supports sync method when syncUrl not configured', () => {
|
|
35
|
+
const db = new Database(testDbPath);
|
|
36
|
+
// sync() exists on the API surface
|
|
37
|
+
expect(typeof db.sync).toBe('function');
|
|
38
|
+
db.close();
|
|
39
|
+
});
|
|
40
|
+
it('supports backup method via VACUUM INTO', async () => {
|
|
41
|
+
const db = new Database(testDbPath);
|
|
42
|
+
const backupPath = `${testDbPath}.bak`;
|
|
43
|
+
try {
|
|
44
|
+
db.exec(`VACUUM INTO '${backupPath}'`);
|
|
45
|
+
expect(fs.existsSync(backupPath)).toBe(true);
|
|
46
|
+
fs.unlinkSync(backupPath);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.warn('libsql VACUUM INTO not supported, skipping test check');
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
db.close();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=libsql-compat.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libsql-compat.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/libsql-compat.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.test.d.ts","sourceRoot":"","sources":["../../../src/db/__tests__/lock.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { DatabaseLock } from '../lock.js';
|
|
6
|
+
describe('DatabaseLock', () => {
|
|
7
|
+
const testDir = path.join(os.tmpdir(), `lock-test-${Date.now()}`);
|
|
8
|
+
const lockPath = path.join(testDir, 'test.db.lock');
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
it('acquires lock when not held', async () => {
|
|
16
|
+
const lock = new DatabaseLock(lockPath);
|
|
17
|
+
const guard = await lock.acquire(1000);
|
|
18
|
+
expect(guard).toBeDefined();
|
|
19
|
+
expect(fs.existsSync(lockPath)).toBe(true);
|
|
20
|
+
guard.release();
|
|
21
|
+
expect(fs.existsSync(lockPath)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
it('writes metadata to lock file', async () => {
|
|
24
|
+
const lock = new DatabaseLock(lockPath, { command: 'test-cmd', version: '1.0.0' });
|
|
25
|
+
const guard = await lock.acquire(1000);
|
|
26
|
+
const content = fs.readFileSync(lockPath, 'utf-8');
|
|
27
|
+
const metadata = JSON.parse(content);
|
|
28
|
+
expect(metadata.pid).toBe(process.pid);
|
|
29
|
+
expect(metadata.command).toBe('test-cmd');
|
|
30
|
+
expect(metadata.version).toBe('1.0.0');
|
|
31
|
+
expect(typeof metadata.hostname).toBe('string');
|
|
32
|
+
expect(typeof metadata.startedAt).toBe('number');
|
|
33
|
+
guard.release();
|
|
34
|
+
});
|
|
35
|
+
it('fails to acquire when already held by same process', async () => {
|
|
36
|
+
const lock1 = new DatabaseLock(lockPath);
|
|
37
|
+
const guard1 = await lock1.acquire(1000);
|
|
38
|
+
const lock2 = new DatabaseLock(lockPath);
|
|
39
|
+
await expect(lock2.acquire(100)).rejects.toThrow(/lock.*held/i);
|
|
40
|
+
guard1.release();
|
|
41
|
+
});
|
|
42
|
+
it('auto-clears stale lock from dead process', async () => {
|
|
43
|
+
// Write a lock file with a PID that doesn't exist
|
|
44
|
+
const staleLock = {
|
|
45
|
+
pid: 999999999, // Very high PID unlikely to exist
|
|
46
|
+
hostname: os.hostname(),
|
|
47
|
+
startedAt: Date.now() - 60000,
|
|
48
|
+
command: 'dead-process',
|
|
49
|
+
version: '1.0.0',
|
|
50
|
+
};
|
|
51
|
+
fs.writeFileSync(lockPath, JSON.stringify(staleLock));
|
|
52
|
+
const lock = new DatabaseLock(lockPath);
|
|
53
|
+
const guard = await lock.acquire(1000);
|
|
54
|
+
expect(guard).toBeDefined();
|
|
55
|
+
expect(guard.staleLockCleared).toBe(true);
|
|
56
|
+
guard.release();
|
|
57
|
+
});
|
|
58
|
+
it('releases lock on guard disposal', async () => {
|
|
59
|
+
const lock = new DatabaseLock(lockPath);
|
|
60
|
+
{
|
|
61
|
+
const guard = await lock.acquire(1000);
|
|
62
|
+
expect(fs.existsSync(lockPath)).toBe(true);
|
|
63
|
+
guard.release();
|
|
64
|
+
}
|
|
65
|
+
expect(fs.existsSync(lockPath)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
it('acquires uncontested lock quickly (exponential backoff)', async () => {
|
|
68
|
+
const lock = new DatabaseLock(lockPath);
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
const guard = await lock.acquire(1000);
|
|
71
|
+
const elapsed = Date.now() - startTime;
|
|
72
|
+
// Uncontested lock should be acquired in < 10ms (no retry needed)
|
|
73
|
+
expect(elapsed).toBeLessThan(20);
|
|
74
|
+
guard.release();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=lock.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/lock.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.test.d.ts","sourceRoot":"","sources":["../../../src/db/__tests__/meta.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'libsql';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import { createGlobalMetaSchema, createLocalMetaSchema, getInstanceId, setInstanceId, getDeviceId, setDeviceId } from '../meta.js';
|
|
7
|
+
describe('meta tables', () => {
|
|
8
|
+
let db;
|
|
9
|
+
const testDbPath = path.join(os.tmpdir(), `meta-test-${Date.now()}.db`);
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
db = new Database(testDbPath);
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
db.close();
|
|
15
|
+
if (fs.existsSync(testDbPath)) {
|
|
16
|
+
fs.unlinkSync(testDbPath);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
describe('hzl_global_meta', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
db.exec(createGlobalMetaSchema());
|
|
22
|
+
});
|
|
23
|
+
it('stores and retrieves instance ID', () => {
|
|
24
|
+
const instanceId = '01HQ3K5BXYZ123456789ABCDEF';
|
|
25
|
+
setInstanceId(db, instanceId);
|
|
26
|
+
expect(getInstanceId(db)).toBe(instanceId);
|
|
27
|
+
});
|
|
28
|
+
it('returns null when instance ID not set', () => {
|
|
29
|
+
expect(getInstanceId(db)).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
it('prevents overwriting instance ID', () => {
|
|
32
|
+
setInstanceId(db, 'first-id');
|
|
33
|
+
expect(() => setInstanceId(db, 'second-id')).toThrow();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('hzl_local_meta', () => {
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
db.exec(createLocalMetaSchema());
|
|
39
|
+
});
|
|
40
|
+
it('stores and retrieves device ID', () => {
|
|
41
|
+
const deviceId = '01HQ3K5DEVICE123456789ABC';
|
|
42
|
+
setDeviceId(db, deviceId);
|
|
43
|
+
expect(getDeviceId(db)).toBe(deviceId);
|
|
44
|
+
});
|
|
45
|
+
it('returns null when device ID not set', () => {
|
|
46
|
+
expect(getDeviceId(db)).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=meta.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/meta.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,QAAQ,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEnI,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IACzB,IAAI,EAAqB,CAAC;IAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,aAAa,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAExE,UAAU,CAAC,GAAG,EAAE;QACZ,EAAE,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACX,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC7B,UAAU,CAAC,GAAG,EAAE;YACZ,EAAE,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YACxC,MAAM,UAAU,GAAG,4BAA4B,CAAC;YAChD,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAC9B,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC7C,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YACxC,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAC9B,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC3D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC5B,UAAU,CAAC,GAAG,EAAE;YACZ,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACtC,MAAM,QAAQ,GAAG,2BAA2B,CAAC;YAC7C,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC1B,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC3C,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.test.d.ts","sourceRoot":"","sources":["../../../src/db/__tests__/migrations.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'libsql';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import { runMigrationsWithRollback, MigrationError } from '../migrations.js';
|
|
7
|
+
describe('migrations with rollback', () => {
|
|
8
|
+
let db;
|
|
9
|
+
const testDbPath = path.join(os.tmpdir(), `migration-test-${Date.now()}.db`);
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
db = new Database(testDbPath);
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
db.close();
|
|
15
|
+
if (fs.existsSync(testDbPath)) {
|
|
16
|
+
fs.unlinkSync(testDbPath);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
it('applies migrations successfully', () => {
|
|
20
|
+
const migrations = [
|
|
21
|
+
{ id: 'v001', up: 'CREATE TABLE test1 (id INTEGER PRIMARY KEY)' },
|
|
22
|
+
{ id: 'v002', up: 'CREATE TABLE test2 (id INTEGER PRIMARY KEY)' },
|
|
23
|
+
];
|
|
24
|
+
const result = runMigrationsWithRollback(db, migrations);
|
|
25
|
+
expect(result.success).toBe(true);
|
|
26
|
+
expect(result.applied).toEqual(['v001', 'v002']);
|
|
27
|
+
// Tables should exist
|
|
28
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
29
|
+
const tableNames = tables.map((t) => t.name);
|
|
30
|
+
expect(tableNames).toContain('test1');
|
|
31
|
+
expect(tableNames).toContain('test2');
|
|
32
|
+
});
|
|
33
|
+
it('rolls back on partial failure', () => {
|
|
34
|
+
const migrations = [
|
|
35
|
+
{ id: 'v001', up: 'CREATE TABLE test1 (id INTEGER PRIMARY KEY)' },
|
|
36
|
+
{ id: 'v002', up: 'INVALID SQL SYNTAX' }, // This will fail
|
|
37
|
+
];
|
|
38
|
+
expect(() => runMigrationsWithRollback(db, migrations)).toThrow(MigrationError);
|
|
39
|
+
// test1 should NOT exist due to rollback
|
|
40
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
41
|
+
const tableNames = tables.map((t) => t.name);
|
|
42
|
+
expect(tableNames).not.toContain('test1');
|
|
43
|
+
});
|
|
44
|
+
it('skips already applied migrations', () => {
|
|
45
|
+
const migrations = [
|
|
46
|
+
{ id: 'v001', up: 'CREATE TABLE test1 (id INTEGER PRIMARY KEY)' },
|
|
47
|
+
];
|
|
48
|
+
// Apply first time
|
|
49
|
+
runMigrationsWithRollback(db, migrations);
|
|
50
|
+
// Apply again - should skip
|
|
51
|
+
const result = runMigrationsWithRollback(db, migrations);
|
|
52
|
+
expect(result.success).toBe(true);
|
|
53
|
+
expect(result.applied).toEqual([]);
|
|
54
|
+
expect(result.skipped).toEqual(['v001']);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=migrations.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/migrations.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-policy.test.d.ts","sourceRoot":"","sources":["../../../src/db/__tests__/sync-policy.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { createSyncPolicy } from '../sync-policy.js';
|
|
3
|
+
describe('SyncPolicy', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.useFakeTimers();
|
|
6
|
+
vi.setSystemTime(new Date('2026-01-31T12:00:00Z'));
|
|
7
|
+
});
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.useRealTimers();
|
|
10
|
+
});
|
|
11
|
+
describe('manual policy', () => {
|
|
12
|
+
it('never triggers sync automatically', () => {
|
|
13
|
+
const policy = createSyncPolicy({ policy: 'manual' });
|
|
14
|
+
expect(policy.shouldSyncBefore({ lastSyncAt: null, isDirty: true })).toBe(false);
|
|
15
|
+
expect(policy.shouldSyncAfter({ isDirty: true })).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('opportunistic policy', () => {
|
|
19
|
+
const config = {
|
|
20
|
+
policy: 'opportunistic',
|
|
21
|
+
staleAfterMs: 60000,
|
|
22
|
+
minIntervalMs: 15000,
|
|
23
|
+
failureBackoffMs: 60000,
|
|
24
|
+
};
|
|
25
|
+
it('triggers sync before when stale', () => {
|
|
26
|
+
const policy = createSyncPolicy(config);
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
// 2 minutes since last sync (> 60s stale threshold)
|
|
29
|
+
const lastSyncAt = now - 120000;
|
|
30
|
+
expect(policy.shouldSyncBefore({
|
|
31
|
+
lastSyncAt,
|
|
32
|
+
isDirty: false,
|
|
33
|
+
lastSyncAttemptAt: null,
|
|
34
|
+
})).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
it('does not trigger sync before when fresh', () => {
|
|
37
|
+
const policy = createSyncPolicy(config);
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
// 30 seconds since last sync (< 60s stale threshold)
|
|
40
|
+
const lastSyncAt = now - 30000;
|
|
41
|
+
expect(policy.shouldSyncBefore({
|
|
42
|
+
lastSyncAt,
|
|
43
|
+
isDirty: false,
|
|
44
|
+
lastSyncAttemptAt: null,
|
|
45
|
+
})).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
it('triggers sync before when never synced', () => {
|
|
48
|
+
const policy = createSyncPolicy(config);
|
|
49
|
+
expect(policy.shouldSyncBefore({
|
|
50
|
+
lastSyncAt: null,
|
|
51
|
+
isDirty: false,
|
|
52
|
+
lastSyncAttemptAt: null,
|
|
53
|
+
})).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it('respects failure backoff', () => {
|
|
56
|
+
const policy = createSyncPolicy(config);
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
// Stale but recent failed attempt
|
|
59
|
+
expect(policy.shouldSyncBefore({
|
|
60
|
+
lastSyncAt: now - 120000, // Stale
|
|
61
|
+
isDirty: false,
|
|
62
|
+
lastSyncAttemptAt: now - 5000, // Failed 5s ago (< 60s backoff)
|
|
63
|
+
lastSyncError: 'network error',
|
|
64
|
+
})).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
it('triggers sync after write when dirty and interval passed', () => {
|
|
67
|
+
const policy = createSyncPolicy(config);
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
expect(policy.shouldSyncAfter({
|
|
70
|
+
isDirty: true,
|
|
71
|
+
lastSyncAttemptAt: now - 20000, // 20s ago (> 15s interval)
|
|
72
|
+
})).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
it('does not trigger sync after when interval not passed', () => {
|
|
75
|
+
const policy = createSyncPolicy(config);
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
expect(policy.shouldSyncAfter({
|
|
78
|
+
isDirty: true,
|
|
79
|
+
lastSyncAttemptAt: now - 5000, // 5s ago (< 15s interval)
|
|
80
|
+
})).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('strict policy', () => {
|
|
84
|
+
const config = { policy: 'strict' };
|
|
85
|
+
it('always triggers sync before reads', () => {
|
|
86
|
+
const policy = createSyncPolicy(config);
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
expect(policy.shouldSyncBefore({
|
|
89
|
+
lastSyncAt: now - 1000, // Very fresh
|
|
90
|
+
isDirty: false,
|
|
91
|
+
lastSyncAttemptAt: null,
|
|
92
|
+
})).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
it('always triggers sync after writes', () => {
|
|
95
|
+
const policy = createSyncPolicy(config);
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
expect(policy.shouldSyncAfter({
|
|
98
|
+
isDirty: true,
|
|
99
|
+
lastSyncAttemptAt: now - 100, // Very recent
|
|
100
|
+
})).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
it('fails on sync error', () => {
|
|
103
|
+
const policy = createSyncPolicy(config);
|
|
104
|
+
expect(policy.onSyncError(new Error('network error'))).toBe('fail');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe('onSyncError', () => {
|
|
108
|
+
it('opportunistic policy continues on error', () => {
|
|
109
|
+
const policy = createSyncPolicy({ policy: 'opportunistic' });
|
|
110
|
+
expect(policy.onSyncError(new Error('network error'))).toBe('continue');
|
|
111
|
+
});
|
|
112
|
+
it('manual policy continues on error', () => {
|
|
113
|
+
const policy = createSyncPolicy({ policy: 'manual' });
|
|
114
|
+
expect(policy.onSyncError(new Error('network error'))).toBe('continue');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=sync-policy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-policy.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/sync-policy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.test.d.ts","sourceRoot":"","sources":["../../../src/db/__tests__/types.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { DbConfigSchema, SyncPolicySchema, UlidSchema, TursoUrlSchema } from '../types.js';
|
|
3
|
+
describe('UlidSchema', () => {
|
|
4
|
+
it('accepts valid ULIDs', () => {
|
|
5
|
+
expect(UlidSchema.safeParse('01HQ3K5BXYZ123456789ABCDEF').success).toBe(true);
|
|
6
|
+
expect(UlidSchema.safeParse('01ARZ3NDEKTSV4RRFFQ69G5FAV').success).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
it('rejects invalid ULIDs', () => {
|
|
9
|
+
expect(UlidSchema.safeParse('invalid').success).toBe(false);
|
|
10
|
+
expect(UlidSchema.safeParse('01HQ3K5BXYZ12345678').success).toBe(false); // Too short
|
|
11
|
+
expect(UlidSchema.safeParse('01HQ3K5BXYZ123456789ABCDEFGH').success).toBe(false); // Too long
|
|
12
|
+
expect(UlidSchema.safeParse('01HQ3K5BXYZ123456789ABCDEI').success).toBe(false); // Invalid char I
|
|
13
|
+
expect(UlidSchema.safeParse('01HQ3K5BXYZ123456789ABCDEL').success).toBe(false); // Invalid char L
|
|
14
|
+
expect(UlidSchema.safeParse('01HQ3K5BXYZ123456789ABCDEO').success).toBe(false); // Invalid char O
|
|
15
|
+
expect(UlidSchema.safeParse('01HQ3K5BXYZ123456789ABCDEU').success).toBe(false); // Invalid char U
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('TursoUrlSchema', () => {
|
|
19
|
+
it('accepts valid Turso URLs', () => {
|
|
20
|
+
expect(TursoUrlSchema.safeParse('libsql://my-db.turso.io').success).toBe(true);
|
|
21
|
+
expect(TursoUrlSchema.safeParse('libsql://my-db-name.example.com').success).toBe(true);
|
|
22
|
+
expect(TursoUrlSchema.safeParse('https://my-db.turso.io').success).toBe(true);
|
|
23
|
+
expect(TursoUrlSchema.safeParse('libsql://localhost:8080').success).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it('rejects invalid Turso URLs', () => {
|
|
26
|
+
expect(TursoUrlSchema.safeParse('http://my-db.turso.io').success).toBe(false); // http not allowed
|
|
27
|
+
expect(TursoUrlSchema.safeParse('my-db.turso.io').success).toBe(false); // No protocol
|
|
28
|
+
expect(TursoUrlSchema.safeParse('libsql://').success).toBe(false); // No host
|
|
29
|
+
expect(TursoUrlSchema.safeParse('sqlite://local.db').success).toBe(false); // Wrong protocol
|
|
30
|
+
expect(TursoUrlSchema.safeParse('libsql://-invalid.turso.io').success).toBe(false); // Invalid host start
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('DbConfigSchema', () => {
|
|
34
|
+
it('accepts minimal config', () => {
|
|
35
|
+
const result = DbConfigSchema.safeParse({});
|
|
36
|
+
expect(result.success).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('accepts full events config', () => {
|
|
39
|
+
const result = DbConfigSchema.safeParse({
|
|
40
|
+
events: {
|
|
41
|
+
path: '/path/to/events.db',
|
|
42
|
+
syncUrl: 'libsql://my-db.turso.io',
|
|
43
|
+
authToken: 'secret',
|
|
44
|
+
syncMode: 'offline',
|
|
45
|
+
encryptionKey: 'key',
|
|
46
|
+
},
|
|
47
|
+
cache: {
|
|
48
|
+
path: '/path/to/cache.db',
|
|
49
|
+
},
|
|
50
|
+
sync: {
|
|
51
|
+
policy: 'opportunistic',
|
|
52
|
+
staleAfterMs: 60000,
|
|
53
|
+
minIntervalMs: 15000,
|
|
54
|
+
lockTimeoutMs: 3000,
|
|
55
|
+
syncTimeoutMs: 30000,
|
|
56
|
+
maxSyncAttemptsPerMinute: 10,
|
|
57
|
+
conflictStrategy: 'merge',
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
expect(result.success).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
it('rejects invalid syncUrl format', () => {
|
|
63
|
+
const result = DbConfigSchema.safeParse({
|
|
64
|
+
events: { syncUrl: 'http://invalid.com' },
|
|
65
|
+
});
|
|
66
|
+
expect(result.success).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
it('rejects invalid syncMode', () => {
|
|
69
|
+
const result = DbConfigSchema.safeParse({
|
|
70
|
+
events: { syncMode: 'invalid' },
|
|
71
|
+
});
|
|
72
|
+
expect(result.success).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
it('rejects invalid conflictStrategy', () => {
|
|
75
|
+
const result = DbConfigSchema.safeParse({
|
|
76
|
+
sync: { conflictStrategy: 'invalid' },
|
|
77
|
+
});
|
|
78
|
+
expect(result.success).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('SyncPolicySchema', () => {
|
|
82
|
+
it('accepts valid policies', () => {
|
|
83
|
+
expect(SyncPolicySchema.safeParse('manual').success).toBe(true);
|
|
84
|
+
expect(SyncPolicySchema.safeParse('opportunistic').success).toBe(true);
|
|
85
|
+
expect(SyncPolicySchema.safeParse('strict').success).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
it('rejects invalid policies', () => {
|
|
88
|
+
expect(SyncPolicySchema.safeParse('invalid').success).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=types.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/types.test.ts"],"names":[],"mappings":"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"}
|