hzl-core 1.5.1 → 1.7.0

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 (161) hide show
  1. package/dist/__tests__/backup/backup-restore.test.js +13 -12
  2. package/dist/__tests__/backup/backup-restore.test.js.map +1 -1
  3. package/dist/__tests__/backup/import-export.test.js +41 -39
  4. package/dist/__tests__/backup/import-export.test.js.map +1 -1
  5. package/dist/__tests__/concurrency/stress.test.js +2 -2
  6. package/dist/__tests__/concurrency/stress.test.js.map +1 -1
  7. package/dist/__tests__/concurrency/worker.js +3 -2
  8. package/dist/__tests__/concurrency/worker.js.map +1 -1
  9. package/dist/__tests__/migrations/upgrade.test.js +132 -117
  10. package/dist/__tests__/migrations/upgrade.test.js.map +1 -1
  11. package/dist/__tests__/projections/rebuild-equivalence.test.js +2 -2
  12. package/dist/__tests__/projections/rebuild-equivalence.test.js.map +1 -1
  13. package/dist/__tests__/properties/invariants.test.js +2 -2
  14. package/dist/__tests__/properties/invariants.test.js.map +1 -1
  15. package/dist/db/__tests__/append-only.test.d.ts +2 -0
  16. package/dist/db/__tests__/append-only.test.d.ts.map +1 -0
  17. package/dist/db/__tests__/append-only.test.js +46 -0
  18. package/dist/db/__tests__/append-only.test.js.map +1 -0
  19. package/dist/db/__tests__/datastore.test.d.ts +2 -0
  20. package/dist/db/__tests__/datastore.test.d.ts.map +1 -0
  21. package/dist/db/__tests__/datastore.test.js +68 -0
  22. package/dist/db/__tests__/datastore.test.js.map +1 -0
  23. package/dist/db/__tests__/libsql-compat.test.d.ts +2 -0
  24. package/dist/db/__tests__/libsql-compat.test.d.ts.map +1 -0
  25. package/dist/db/__tests__/libsql-compat.test.js +56 -0
  26. package/dist/db/__tests__/libsql-compat.test.js.map +1 -0
  27. package/dist/db/__tests__/lock.test.d.ts +2 -0
  28. package/dist/db/__tests__/lock.test.d.ts.map +1 -0
  29. package/dist/db/__tests__/lock.test.js +77 -0
  30. package/dist/db/__tests__/lock.test.js.map +1 -0
  31. package/dist/db/__tests__/meta.test.d.ts +2 -0
  32. package/dist/db/__tests__/meta.test.d.ts.map +1 -0
  33. package/dist/db/__tests__/meta.test.js +50 -0
  34. package/dist/db/__tests__/meta.test.js.map +1 -0
  35. package/dist/db/__tests__/migrations.test.d.ts +2 -0
  36. package/dist/db/__tests__/migrations.test.d.ts.map +1 -0
  37. package/dist/db/__tests__/migrations.test.js +57 -0
  38. package/dist/db/__tests__/migrations.test.js.map +1 -0
  39. package/dist/db/__tests__/sync-policy.test.d.ts +2 -0
  40. package/dist/db/__tests__/sync-policy.test.d.ts.map +1 -0
  41. package/dist/db/__tests__/sync-policy.test.js +118 -0
  42. package/dist/db/__tests__/sync-policy.test.js.map +1 -0
  43. package/dist/db/__tests__/types.test.d.ts +2 -0
  44. package/dist/db/__tests__/types.test.d.ts.map +1 -0
  45. package/dist/db/__tests__/types.test.js +91 -0
  46. package/dist/db/__tests__/types.test.js.map +1 -0
  47. package/dist/db/datastore.d.ts +16 -0
  48. package/dist/db/datastore.d.ts.map +1 -0
  49. package/dist/db/datastore.js +159 -0
  50. package/dist/db/datastore.js.map +1 -0
  51. package/dist/db/lock.d.ts +38 -0
  52. package/dist/db/lock.d.ts.map +1 -0
  53. package/dist/db/lock.js +114 -0
  54. package/dist/db/lock.js.map +1 -0
  55. package/dist/db/meta.d.ts +28 -0
  56. package/dist/db/meta.d.ts.map +1 -0
  57. package/dist/db/meta.js +95 -0
  58. package/dist/db/meta.js.map +1 -0
  59. package/dist/db/migrations.d.ts +23 -3
  60. package/dist/db/migrations.d.ts.map +1 -1
  61. package/dist/db/migrations.js +69 -99
  62. package/dist/db/migrations.js.map +1 -1
  63. package/dist/db/migrations.test.js +40 -105
  64. package/dist/db/migrations.test.js.map +1 -1
  65. package/dist/db/schema.d.ts +2 -1
  66. package/dist/db/schema.d.ts.map +1 -1
  67. package/dist/db/schema.js +52 -10
  68. package/dist/db/schema.js.map +1 -1
  69. package/dist/db/sync-policy.d.ts +21 -0
  70. package/dist/db/sync-policy.d.ts.map +1 -0
  71. package/dist/db/sync-policy.js +62 -0
  72. package/dist/db/sync-policy.js.map +1 -0
  73. package/dist/db/test-utils.d.ts +33 -0
  74. package/dist/db/test-utils.d.ts.map +1 -0
  75. package/dist/db/test-utils.js +58 -0
  76. package/dist/db/test-utils.js.map +1 -0
  77. package/dist/db/{connection.d.ts → transaction.d.ts} +2 -4
  78. package/dist/db/transaction.d.ts.map +1 -0
  79. package/dist/db/{connection.js → transaction.js} +1 -23
  80. package/dist/db/transaction.js.map +1 -0
  81. package/dist/db/types.d.ts +199 -0
  82. package/dist/db/types.d.ts.map +1 -0
  83. package/dist/db/types.js +43 -0
  84. package/dist/db/types.js.map +1 -0
  85. package/dist/events/store.d.ts +1 -1
  86. package/dist/events/store.d.ts.map +1 -1
  87. package/dist/events/store.test.js +2 -4
  88. package/dist/events/store.test.js.map +1 -1
  89. package/dist/index.d.ts +7 -2
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +6 -2
  92. package/dist/index.js.map +1 -1
  93. package/dist/index.test.js +13 -11
  94. package/dist/index.test.js.map +1 -1
  95. package/dist/projections/comments-checkpoints.d.ts +1 -1
  96. package/dist/projections/comments-checkpoints.d.ts.map +1 -1
  97. package/dist/projections/comments-checkpoints.test.js +3 -4
  98. package/dist/projections/comments-checkpoints.test.js.map +1 -1
  99. package/dist/projections/dependencies.d.ts +1 -1
  100. package/dist/projections/dependencies.d.ts.map +1 -1
  101. package/dist/projections/dependencies.test.js +3 -4
  102. package/dist/projections/dependencies.test.js.map +1 -1
  103. package/dist/projections/engine.d.ts +9 -2
  104. package/dist/projections/engine.d.ts.map +1 -1
  105. package/dist/projections/engine.js +22 -6
  106. package/dist/projections/engine.js.map +1 -1
  107. package/dist/projections/engine.test.js +3 -4
  108. package/dist/projections/engine.test.js.map +1 -1
  109. package/dist/projections/projects.d.ts +1 -1
  110. package/dist/projections/projects.d.ts.map +1 -1
  111. package/dist/projections/projects.test.js +2 -20
  112. package/dist/projections/projects.test.js.map +1 -1
  113. package/dist/projections/rebuild.d.ts +1 -1
  114. package/dist/projections/rebuild.d.ts.map +1 -1
  115. package/dist/projections/rebuild.test.js +3 -4
  116. package/dist/projections/rebuild.test.js.map +1 -1
  117. package/dist/projections/search.d.ts +1 -1
  118. package/dist/projections/search.d.ts.map +1 -1
  119. package/dist/projections/search.test.js +3 -4
  120. package/dist/projections/search.test.js.map +1 -1
  121. package/dist/projections/tags.d.ts +1 -1
  122. package/dist/projections/tags.d.ts.map +1 -1
  123. package/dist/projections/tags.test.js +3 -4
  124. package/dist/projections/tags.test.js.map +1 -1
  125. package/dist/projections/tasks-current.d.ts +1 -1
  126. package/dist/projections/tasks-current.d.ts.map +1 -1
  127. package/dist/projections/tasks-current.test.js +3 -4
  128. package/dist/projections/tasks-current.test.js.map +1 -1
  129. package/dist/projections/types.d.ts +1 -1
  130. package/dist/projections/types.d.ts.map +1 -1
  131. package/dist/services/backup-service.d.ts +2 -2
  132. package/dist/services/backup-service.d.ts.map +1 -1
  133. package/dist/services/backup-service.js +13 -3
  134. package/dist/services/backup-service.js.map +1 -1
  135. package/dist/services/project-service.d.ts +1 -1
  136. package/dist/services/project-service.d.ts.map +1 -1
  137. package/dist/services/project-service.js +1 -1
  138. package/dist/services/project-service.js.map +1 -1
  139. package/dist/services/project-service.test.js +2 -13
  140. package/dist/services/project-service.test.js.map +1 -1
  141. package/dist/services/search-service.d.ts +1 -1
  142. package/dist/services/search-service.d.ts.map +1 -1
  143. package/dist/services/search-service.test.js +3 -4
  144. package/dist/services/search-service.test.js.map +1 -1
  145. package/dist/services/task-service.d.ts +1 -1
  146. package/dist/services/task-service.d.ts.map +1 -1
  147. package/dist/services/task-service.js +1 -1
  148. package/dist/services/task-service.js.map +1 -1
  149. package/dist/services/task-service.test.js +3 -4
  150. package/dist/services/task-service.test.js.map +1 -1
  151. package/dist/services/validation-service.d.ts +1 -1
  152. package/dist/services/validation-service.d.ts.map +1 -1
  153. package/dist/services/validation-service.test.js +2 -4
  154. package/dist/services/validation-service.test.js.map +1 -1
  155. package/package.json +4 -4
  156. package/dist/db/connection.d.ts.map +0 -1
  157. package/dist/db/connection.js.map +0 -1
  158. package/dist/db/connection.test.d.ts +0 -2
  159. package/dist/db/connection.test.d.ts.map +0 -1
  160. package/dist/db/connection.test.js +0 -63
  161. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=libsql-compat.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=lock.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=meta.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=migrations.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sync-policy.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.test.d.ts.map
@@ -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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"}