@opensip-cli/datastore 0.1.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 (102) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +31 -0
  4. package/dist/__tests__/baseline-repo.test.d.ts +2 -0
  5. package/dist/__tests__/baseline-repo.test.d.ts.map +1 -0
  6. package/dist/__tests__/baseline-repo.test.js +85 -0
  7. package/dist/__tests__/baseline-repo.test.js.map +1 -0
  8. package/dist/__tests__/data-store.test.d.ts +10 -0
  9. package/dist/__tests__/data-store.test.d.ts.map +1 -0
  10. package/dist/__tests__/data-store.test.js +68 -0
  11. package/dist/__tests__/data-store.test.js.map +1 -0
  12. package/dist/__tests__/factory.test.d.ts +2 -0
  13. package/dist/__tests__/factory.test.d.ts.map +1 -0
  14. package/dist/__tests__/factory.test.js +168 -0
  15. package/dist/__tests__/factory.test.js.map +1 -0
  16. package/dist/__tests__/migration-integrity.test.d.ts +2 -0
  17. package/dist/__tests__/migration-integrity.test.d.ts.map +1 -0
  18. package/dist/__tests__/migration-integrity.test.js +78 -0
  19. package/dist/__tests__/migration-integrity.test.js.map +1 -0
  20. package/dist/__tests__/tool-state-repo.test.d.ts +7 -0
  21. package/dist/__tests__/tool-state-repo.test.d.ts.map +1 -0
  22. package/dist/__tests__/tool-state-repo.test.js +54 -0
  23. package/dist/__tests__/tool-state-repo.test.js.map +1 -0
  24. package/dist/__tests__/version-guard.test.d.ts +2 -0
  25. package/dist/__tests__/version-guard.test.d.ts.map +1 -0
  26. package/dist/__tests__/version-guard.test.js +110 -0
  27. package/dist/__tests__/version-guard.test.js.map +1 -0
  28. package/dist/backends/memory.d.ts +3 -0
  29. package/dist/backends/memory.d.ts.map +1 -0
  30. package/dist/backends/memory.js +5 -0
  31. package/dist/backends/memory.js.map +1 -0
  32. package/dist/backends/shared.d.ts +3 -0
  33. package/dist/backends/shared.d.ts.map +1 -0
  34. package/dist/backends/shared.js +31 -0
  35. package/dist/backends/shared.js.map +1 -0
  36. package/dist/backends/sqlite.d.ts +5 -0
  37. package/dist/backends/sqlite.d.ts.map +1 -0
  38. package/dist/backends/sqlite.js +8 -0
  39. package/dist/backends/sqlite.js.map +1 -0
  40. package/dist/baseline-repo.d.ts +50 -0
  41. package/dist/baseline-repo.d.ts.map +1 -0
  42. package/dist/baseline-repo.js +159 -0
  43. package/dist/baseline-repo.js.map +1 -0
  44. package/dist/data-store.d.ts +78 -0
  45. package/dist/data-store.d.ts.map +1 -0
  46. package/dist/data-store.js +71 -0
  47. package/dist/data-store.js.map +1 -0
  48. package/dist/factory.d.ts +47 -0
  49. package/dist/factory.d.ts.map +1 -0
  50. package/dist/factory.js +151 -0
  51. package/dist/factory.js.map +1 -0
  52. package/dist/index.d.ts +10 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +11 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/schema/baseline.d.ts +178 -0
  57. package/dist/schema/baseline.d.ts.map +1 -0
  58. package/dist/schema/baseline.js +31 -0
  59. package/dist/schema/baseline.js.map +1 -0
  60. package/dist/schema/tool-state.d.ts +110 -0
  61. package/dist/schema/tool-state.d.ts.map +1 -0
  62. package/dist/schema/tool-state.js +20 -0
  63. package/dist/schema/tool-state.js.map +1 -0
  64. package/dist/schema-version.d.ts +23 -0
  65. package/dist/schema-version.d.ts.map +1 -0
  66. package/dist/schema-version.js +55 -0
  67. package/dist/schema-version.js.map +1 -0
  68. package/dist/tool-state-repo.d.ts +51 -0
  69. package/dist/tool-state-repo.d.ts.map +1 -0
  70. package/dist/tool-state-repo.js +110 -0
  71. package/dist/tool-state-repo.js.map +1 -0
  72. package/migrations/.gitkeep +0 -0
  73. package/migrations/0000_sticky_white_tiger.sql +39 -0
  74. package/migrations/0001_easy_harry_osborn.sql +18 -0
  75. package/migrations/0002_plain_amazoness.sql +5 -0
  76. package/migrations/0003_mysterious_khan.sql +6 -0
  77. package/migrations/0004_narrow_bloodscream.sql +3 -0
  78. package/migrations/0005_lying_luke_cage.sql +7 -0
  79. package/migrations/0006_mean_photon.sql +12 -0
  80. package/migrations/0007_parallel_chamber.sql +3 -0
  81. package/migrations/0008_flaky_victor_mancha.sql +7 -0
  82. package/migrations/0009_stable_tool_identity.sql +9 -0
  83. package/migrations/0010_add_timestamp_iso_and_payload_version.sql +11 -0
  84. package/migrations/0011_payload_version_safety_and_notes.sql +21 -0
  85. package/migrations/0012_overrated_talon.sql +21 -0
  86. package/migrations/0013_lovely_zarda.sql +1 -0
  87. package/migrations/meta/0000_snapshot.json +269 -0
  88. package/migrations/meta/0001_snapshot.json +369 -0
  89. package/migrations/meta/0002_snapshot.json +400 -0
  90. package/migrations/meta/0003_snapshot.json +441 -0
  91. package/migrations/meta/0004_snapshot.json +270 -0
  92. package/migrations/meta/0005_snapshot.json +315 -0
  93. package/migrations/meta/0006_snapshot.json +382 -0
  94. package/migrations/meta/0007_snapshot.json +303 -0
  95. package/migrations/meta/0008_snapshot.json +346 -0
  96. package/migrations/meta/0009_snapshot.json +367 -0
  97. package/migrations/meta/0010_snapshot.json +382 -0
  98. package/migrations/meta/0011_snapshot.json +382 -0
  99. package/migrations/meta/0012_snapshot.json +512 -0
  100. package/migrations/meta/0013_snapshot.json +458 -0
  101. package/migrations/meta/_journal.json +104 -0
  102. package/package.json +56 -0
@@ -0,0 +1,78 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { sql } from 'drizzle-orm';
5
+ import { describe, expect, it } from 'vitest';
6
+ import { DataStoreFactory, requireDrizzleDataStore } from '../index.js';
7
+ /**
8
+ * Migration-integrity guardrail.
9
+ *
10
+ * Drizzle's runtime migrator (`drizzle-orm/better-sqlite3/migrator`) applies
11
+ * ONLY the migrations registered in `meta/_journal.json`; a `.sql` file that is
12
+ * not in the journal is silently ignored. That gap let three hand-authored
13
+ * migrations (0009/0010/0011) ship without journal entries — so the columns
14
+ * they add (`stable_id`, `timestamp_iso`, `payload_version`) never reached any
15
+ * database, and every datastore read failed with `no such column`. These tests
16
+ * make that class of drift fail loudly in CI instead of at a user's terminal.
17
+ */
18
+ const MIGRATIONS_DIR = fileURLToPath(new URL('../../migrations', import.meta.url));
19
+ const META_DIR = fileURLToPath(new URL('../../migrations/meta', import.meta.url));
20
+ async function readJournal() {
21
+ const raw = await readFile(fileURLToPath(new URL('../../migrations/meta/_journal.json', import.meta.url)), 'utf8');
22
+ return JSON.parse(raw).entries;
23
+ }
24
+ /** Migration SQL files, e.g. `0010_add_timestamp_iso_and_payload_version`, sorted by number. */
25
+ function sqlMigrationTags() {
26
+ return readdirSync(MIGRATIONS_DIR)
27
+ .filter((f) => /^\d{4}_.*\.sql$/.test(f))
28
+ .map((f) => f.replace(/\.sql$/, ''))
29
+ .sort();
30
+ }
31
+ describe('migration journal ↔ SQL file parity', () => {
32
+ it('every NNNN_*.sql migration file is registered in _journal.json (and vice versa)', async () => {
33
+ const entries = await readJournal();
34
+ const journalTags = entries.map((e) => e.tag).sort();
35
+ const fileTags = sqlMigrationTags();
36
+ // Set equality, reported as a diff so a missing/extra migration is obvious.
37
+ const unregisteredFiles = fileTags.filter((t) => !journalTags.includes(t));
38
+ const danglingEntries = journalTags.filter((t) => !fileTags.includes(t));
39
+ expect(unregisteredFiles, 'SQL files present on disk but missing from _journal.json').toEqual([]);
40
+ expect(danglingEntries, '_journal.json entries with no matching SQL file').toEqual([]);
41
+ expect(journalTags).toEqual(fileTags);
42
+ });
43
+ it('journal entry indices are contiguous and match the file ordering', async () => {
44
+ const entries = await readJournal();
45
+ const byIdx = [...entries].sort((a, b) => a.idx - b.idx);
46
+ expect(byIdx.map((e) => e.idx)).toEqual(byIdx.map((_, i) => i));
47
+ expect(byIdx.map((e) => e.tag)).toEqual(sqlMigrationTags());
48
+ });
49
+ it('every journal entry has a corresponding meta/NNNN_snapshot.json', async () => {
50
+ const entries = await readJournal();
51
+ const missing = entries
52
+ .map((e) => `${String(e.idx).padStart(4, '0')}_snapshot.json`)
53
+ .filter((name) => !existsSync(`${META_DIR}/${name}`));
54
+ expect(missing, 'journal entries without a drizzle snapshot (breaks future db:generate)').toEqual([]);
55
+ });
56
+ });
57
+ describe('fresh database fully realizes the ORM schema', () => {
58
+ it('applies the whole chain and materializes every hand-migrated column', () => {
59
+ // Uses the real migrations folder (factory default). Throws if any migration
60
+ // fails — e.g. a multi-statement file missing `statement-breakpoint`, or a
61
+ // duplicate `ADD COLUMN`.
62
+ const ds = DataStoreFactory.open({ backend: 'memory' });
63
+ try {
64
+ const cols = (table) => new Set(requireDrizzleDataStore(ds)
65
+ .db.all(sql.raw(`PRAGMA table_info(${table})`))
66
+ .map((r) => r.name));
67
+ expect(cols('sessions').has('timestamp_iso')).toBe(true); // 0010
68
+ expect(cols('session_tool_payload').has('payload_version')).toBe(true); // 0010
69
+ expect(cols('tool_state').has('stable_id')).toBe(true); // 0009
70
+ expect(cols('tool_baseline_entries').has('stable_id')).toBe(true); // 0009
71
+ expect(cols('tool_baseline_meta').has('stable_id')).toBe(true); // 0009
72
+ }
73
+ finally {
74
+ ds.close();
75
+ }
76
+ });
77
+ });
78
+ //# sourceMappingURL=migration-integrity.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-integrity.test.js","sourceRoot":"","sources":["../../src/__tests__/migration-integrity.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ToolStateRepo round-trips (ADR-0042, plan phase 7.5): put/get/list/delete/
3
+ * clear, upsert semantics, per-tool isolation, and the payload cap erroring
4
+ * (never evicting).
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=tool-state-repo.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-state-repo.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/tool-state-repo.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * ToolStateRepo round-trips (ADR-0042, plan phase 7.5): put/get/list/delete/
3
+ * clear, upsert semantics, per-tool isolation, and the payload cap erroring
4
+ * (never evicting).
5
+ */
6
+ import { ValidationError } from '@opensip-cli/core';
7
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
8
+ import { DataStoreFactory } from '../factory.js';
9
+ import { ToolStateRepo, TOOL_STATE_MAX_PAYLOAD_BYTES } from '../tool-state-repo.js';
10
+ let ds;
11
+ let repo;
12
+ beforeEach(() => {
13
+ ds = DataStoreFactory.open({ backend: 'memory' });
14
+ repo = new ToolStateRepo(ds);
15
+ });
16
+ afterEach(() => {
17
+ ds.close();
18
+ });
19
+ describe('ToolStateRepo', () => {
20
+ it('round-trips an opaque JSON payload', () => {
21
+ repo.put('acme-audit', 'cursor', { page: 3, items: ['a', 'b'] });
22
+ expect(repo.get('acme-audit', 'cursor')).toEqual({ page: 3, items: ['a', 'b'] });
23
+ });
24
+ it('get of a never-put key is undefined', () => {
25
+ expect(repo.get('acme-audit', 'nope')).toBeUndefined();
26
+ });
27
+ it('put is an upsert (same key replaces)', () => {
28
+ repo.put('acme-audit', 'k', { v: 1 });
29
+ repo.put('acme-audit', 'k', { v: 2 });
30
+ expect(repo.get('acme-audit', 'k')).toEqual({ v: 2 });
31
+ expect(repo.list('acme-audit')).toEqual(['k']);
32
+ });
33
+ it('list returns this tool’s keys sorted; delete removes one', () => {
34
+ repo.put('acme-audit', 'b', 1);
35
+ repo.put('acme-audit', 'a', 2);
36
+ expect(repo.list('acme-audit')).toEqual(['a', 'b']);
37
+ repo.delete('acme-audit', 'a');
38
+ expect(repo.list('acme-audit')).toEqual(['b']);
39
+ });
40
+ it('tools are isolated: clear(A) never touches B', () => {
41
+ repo.put('tool-a', 'k', 1);
42
+ repo.put('tool-b', 'k', 2);
43
+ expect(repo.clear('tool-a')).toBe(1);
44
+ expect(repo.get('tool-a', 'k')).toBeUndefined();
45
+ expect(repo.get('tool-b', 'k')).toBe(2);
46
+ });
47
+ it('an oversized payload throws ValidationError (error, never evict)', () => {
48
+ const big = 'x'.repeat(TOOL_STATE_MAX_PAYLOAD_BYTES + 1);
49
+ expect(() => repo.put('acme-audit', 'big', big)).toThrow(ValidationError);
50
+ // Nothing was stored — the cap rejects, it does not truncate or evict.
51
+ expect(repo.get('acme-audit', 'big')).toBeUndefined();
52
+ });
53
+ });
54
+ //# sourceMappingURL=tool-state-repo.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-state-repo.test.js","sourceRoot":"","sources":["../../src/__tests__/tool-state-repo.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=version-guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-guard.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/version-guard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,110 @@
1
+ import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import Database from 'better-sqlite3';
6
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
+ import { DataStoreFactory, DataStoreVersionError } from '../index.js';
8
+ import { isDbNewerThanCli, readSupportedDbVersion } from '../schema-version.js';
9
+ /** The bundled migrations folder lives at the package root (mirrors factory's default). */
10
+ const MIGRATIONS_FOLDER = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'migrations');
11
+ /** Read SQLite's `PRAGMA user_version` directly via a throwaway connection. */
12
+ function readUserVersion(path) {
13
+ const db = new Database(path);
14
+ try {
15
+ return Number(db.pragma('user_version', { simple: true }));
16
+ }
17
+ finally {
18
+ db.close();
19
+ }
20
+ }
21
+ /** Force a `PRAGMA user_version` onto an existing file via a throwaway connection. */
22
+ function writeUserVersion(path, version) {
23
+ const db = new Database(path);
24
+ try {
25
+ db.pragma(`user_version = ${version}`);
26
+ }
27
+ finally {
28
+ db.close();
29
+ }
30
+ }
31
+ let tmp;
32
+ beforeEach(() => {
33
+ tmp = mkdtempSync(join(tmpdir(), 'ds-version-'));
34
+ });
35
+ afterEach(() => {
36
+ rmSync(tmp, { recursive: true, force: true });
37
+ });
38
+ describe('readSupportedDbVersion', () => {
39
+ it('equals the bundled migration journal entry count', () => {
40
+ const journal = JSON.parse(readFileSync(join(MIGRATIONS_FOLDER, 'meta', '_journal.json'), 'utf8'));
41
+ expect(readSupportedDbVersion(MIGRATIONS_FOLDER)).toBe(journal.entries.length);
42
+ });
43
+ it('is at least 1 (the package ships migrations)', () => {
44
+ expect(readSupportedDbVersion(MIGRATIONS_FOLDER)).toBeGreaterThanOrEqual(1);
45
+ });
46
+ it('returns undefined for an unreadable journal (broken install)', () => {
47
+ expect(readSupportedDbVersion(join(tmp, 'does-not-exist'))).toBeUndefined();
48
+ });
49
+ });
50
+ describe('isDbNewerThanCli', () => {
51
+ it('blocks only when the db is strictly ahead of the CLI', () => {
52
+ expect(isDbNewerThanCli(7, 6)).toBe(true);
53
+ expect(isDbNewerThanCli(6, 6)).toBe(false); // equal = same schema, safe
54
+ expect(isDbNewerThanCli(0, 6)).toBe(false); // fresh / legacy, safe
55
+ });
56
+ });
57
+ describe('DataStoreFactory.open — version stamp', () => {
58
+ it('stamps a freshly created SQLite db with the supported version', () => {
59
+ const path = join(tmp, 'fresh.sqlite');
60
+ const supported = readSupportedDbVersion(MIGRATIONS_FOLDER);
61
+ const ds = DataStoreFactory.open({ backend: 'sqlite', path });
62
+ ds.close();
63
+ expect(readUserVersion(path)).toBe(supported);
64
+ });
65
+ it('adopts a pre-guard "legacy" db (user_version 0) and re-stamps it', () => {
66
+ const path = join(tmp, 'legacy.sqlite');
67
+ // Create + migrate, then simulate a legacy file by clearing the stamp.
68
+ DataStoreFactory.open({ backend: 'sqlite', path }).close();
69
+ writeUserVersion(path, 0);
70
+ expect(readUserVersion(path)).toBe(0);
71
+ // Reopening must NOT throw, and must re-stamp to the supported version.
72
+ const reopened = DataStoreFactory.open({ backend: 'sqlite', path });
73
+ reopened.close();
74
+ expect(readUserVersion(path)).toBe(readSupportedDbVersion(MIGRATIONS_FOLDER));
75
+ });
76
+ it('reopening an already-current db is a no-op that preserves the stamp', () => {
77
+ const path = join(tmp, 'reopen.sqlite');
78
+ DataStoreFactory.open({ backend: 'sqlite', path }).close();
79
+ const first = readUserVersion(path);
80
+ DataStoreFactory.open({ backend: 'sqlite', path }).close();
81
+ expect(readUserVersion(path)).toBe(first);
82
+ });
83
+ });
84
+ describe('DataStoreFactory.open — downgrade guard', () => {
85
+ it('throws DataStoreVersionError when the db was written by a newer CLI', () => {
86
+ const path = join(tmp, 'future.sqlite');
87
+ DataStoreFactory.open({ backend: 'sqlite', path }).close();
88
+ // Stamp it far ahead of anything this CLI could support.
89
+ writeUserVersion(path, 9999);
90
+ expect(() => DataStoreFactory.open({ backend: 'sqlite', path })).toThrow(DataStoreVersionError);
91
+ });
92
+ it('the error message points at the install script and the delete fallback', () => {
93
+ const path = join(tmp, 'future-msg.sqlite');
94
+ DataStoreFactory.open({ backend: 'sqlite', path }).close();
95
+ writeUserVersion(path, 9999);
96
+ try {
97
+ DataStoreFactory.open({ backend: 'sqlite', path });
98
+ expect.unreachable('open should have thrown DataStoreVersionError');
99
+ }
100
+ catch (error) {
101
+ expect(error).toBeInstanceOf(DataStoreVersionError);
102
+ const err = error;
103
+ expect(err.dbVersion).toBe(9999);
104
+ expect(err.supportedVersion).toBe(readSupportedDbVersion(MIGRATIONS_FOLDER));
105
+ expect(err.message).toContain('curl -fsSL https://opensip.ai/cli/install.sh | bash');
106
+ expect(err.message).toContain(path);
107
+ }
108
+ });
109
+ });
110
+ //# sourceMappingURL=version-guard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-guard.test.js","sourceRoot":"","sources":["../../src/__tests__/version-guard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { DrizzleDataStore } from '../data-store.js';
2
+ export declare function openMemoryBackend(): DrizzleDataStore;
3
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/backends/memory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,wBAAgB,iBAAiB,IAAI,gBAAgB,CAEpD"}
@@ -0,0 +1,5 @@
1
+ import { buildSqliteDataStore } from './shared.js';
2
+ export function openMemoryBackend() {
3
+ return buildSqliteDataStore(':memory:');
4
+ }
5
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/backends/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAInD,MAAM,UAAU,iBAAiB;IAC/B,OAAO,oBAAoB,CAAC,UAAU,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SqliteBackendHandle } from '../data-store.js';
2
+ export declare function buildSqliteDataStore(dbPath: string): SqliteBackendHandle;
3
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/backends/shared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CA0BxE"}
@@ -0,0 +1,31 @@
1
+ import Database from 'better-sqlite3';
2
+ import { drizzle } from 'drizzle-orm/better-sqlite3';
3
+ export function buildSqliteDataStore(dbPath) {
4
+ const sqlite = new Database(dbPath);
5
+ sqlite.pragma('journal_mode = WAL');
6
+ sqlite.pragma('foreign_keys = ON');
7
+ const db = drizzle(sqlite);
8
+ let closed = false;
9
+ return {
10
+ db,
11
+ close() {
12
+ if (closed)
13
+ return;
14
+ sqlite.close();
15
+ closed = true;
16
+ },
17
+ transaction(fn) {
18
+ return db.transaction(fn);
19
+ },
20
+ readUserVersion() {
21
+ // `{ simple: true }` returns the scalar (0 on a fresh DB) rather than a row array.
22
+ return Number(sqlite.pragma('user_version', { simple: true }));
23
+ },
24
+ writeUserVersion(version) {
25
+ // PRAGMA does not accept bound parameters; the value is an integer we own,
26
+ // so interpolating it is safe (and `Number(...)`-guarded by the type).
27
+ sqlite.pragma(`user_version = ${version}`);
28
+ },
29
+ };
30
+ }
31
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/backends/shared.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAIrD,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACpC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACnC,MAAM,EAAE,GAAkB,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,OAAO;QACL,EAAE;QACF,KAAK;YACH,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,WAAW,CAAI,EAA4B;YACzC,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;QACD,eAAe;YACb,mFAAmF;YACnF,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACjE,CAAC;QACD,gBAAgB,CAAC,OAAe;YAC9B,2EAA2E;YAC3E,uEAAuE;YACvE,MAAM,CAAC,MAAM,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { SqliteBackendHandle } from '../data-store.js';
2
+ export declare function openSqliteBackend(opts: {
3
+ path: string;
4
+ }): SqliteBackendHandle;
5
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/backends/sqlite.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,mBAAmB,CAG7E"}
@@ -0,0 +1,8 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { buildSqliteDataStore } from './shared.js';
4
+ export function openSqliteBackend(opts) {
5
+ mkdirSync(dirname(opts.path), { recursive: true });
6
+ return buildSqliteDataStore(opts.path);
7
+ }
8
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/backends/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAInD,MAAM,UAAU,iBAAiB,CAAC,IAAsB;IACtD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,OAAO,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { type Signal } from '@opensip-cli/core';
2
+ import { type DataStore } from './data-store.js';
3
+ /** One baseline entry to persist: its opaque fingerprint + the full Signal payload. */
4
+ export interface BaselineEntry {
5
+ readonly fingerprint: string;
6
+ readonly payload: Signal;
7
+ }
8
+ /** One loaded baseline row: the fingerprint + its stored payload (null if absent). */
9
+ export interface BaselineRow {
10
+ readonly fingerprint: string;
11
+ readonly payload: Signal | null;
12
+ }
13
+ /**
14
+ * The generic host-owned baseline repository (ADR-0036). One repo over the
15
+ * shared `tool_baseline_entries` / `tool_baseline_meta` tables, scoped by the
16
+ * `tool` column so every tool shares the table but never sees another tool's
17
+ * rows. Replaces the per-tool `GraphBaselineRepo` / `FitBaselineRepo`.
18
+ *
19
+ * `save` is an atomic per-tool replace (delete-all + bulk insert + meta upsert in
20
+ * one transaction), mirroring graph's atomic-rename-of-tmp-file semantic. The
21
+ * `payload` column carries the full Signal so the diff can surface full-object
22
+ * `resolved` findings and re-render SARIF.
23
+ */
24
+ export declare class BaselineRepo {
25
+ private readonly datastore;
26
+ constructor(datastore: DataStore);
27
+ /**
28
+ * Replace this tool's entire baseline in one transaction. Entries are deduped
29
+ * by fingerprint (last wins) and sorted by fingerprint for deterministic
30
+ * ordering. The meta row is upserted as the existence marker — an empty
31
+ * baseline is a valid saved state ("saved, no findings" ≠ "never saved").
32
+ */
33
+ save(tool: string, entries: readonly BaselineEntry[]): void;
34
+ /** Load this tool's baseline rows (fingerprint + payload). Empty when none saved. */
35
+ load(tool: string): readonly BaselineRow[];
36
+ /** True iff this tool has saved a baseline (independent of whether it was empty). */
37
+ exists(tool: string): boolean;
38
+ /** When this tool's baseline was captured (ms epoch), or undefined when none exists. */
39
+ capturedAt(tool: string): number | undefined;
40
+ /**
41
+ * Delete this tool's baseline — entries + the meta existence marker, one
42
+ * transaction (`tools data purge`, ADR-0042). After a clear, `exists()` is
43
+ * false (vs. `save(tool, [])`, which leaves an EMPTY-but-saved baseline).
44
+ */
45
+ clear(tool: string): {
46
+ readonly entries: number;
47
+ readonly meta: boolean;
48
+ };
49
+ }
50
+ //# sourceMappingURL=baseline-repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline-repo.d.ts","sourceRoot":"","sources":["../src/baseline-repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAA2B,KAAK,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AAKjG,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;gBAEjC,SAAS,EAAE,SAAS;IAIhC;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI;IAoD3D,qFAAqF;IACrF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,WAAW,EAAE;IAgC1C,qFAAqF;IACrF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAU7B,wFAAwF;IACxF,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAU5C;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE;CAoB1E"}
@@ -0,0 +1,159 @@
1
+ import { logger } from '@opensip-cli/core';
2
+ import { eq, sql } from 'drizzle-orm';
3
+ import { requireDrizzleDataStore } from './data-store.js';
4
+ import { toolBaselineEntries, toolBaselineMeta } from './schema/baseline.js';
5
+ const MODULE_NAME = 'datastore:baseline-repo';
6
+ /**
7
+ * The generic host-owned baseline repository (ADR-0036). One repo over the
8
+ * shared `tool_baseline_entries` / `tool_baseline_meta` tables, scoped by the
9
+ * `tool` column so every tool shares the table but never sees another tool's
10
+ * rows. Replaces the per-tool `GraphBaselineRepo` / `FitBaselineRepo`.
11
+ *
12
+ * `save` is an atomic per-tool replace (delete-all + bulk insert + meta upsert in
13
+ * one transaction), mirroring graph's atomic-rename-of-tmp-file semantic. The
14
+ * `payload` column carries the full Signal so the diff can surface full-object
15
+ * `resolved` findings and re-render SARIF.
16
+ */
17
+ export class BaselineRepo {
18
+ datastore;
19
+ constructor(datastore) {
20
+ this.datastore = requireDrizzleDataStore(datastore);
21
+ }
22
+ /**
23
+ * Replace this tool's entire baseline in one transaction. Entries are deduped
24
+ * by fingerprint (last wins) and sorted by fingerprint for deterministic
25
+ * ordering. The meta row is upserted as the existence marker — an empty
26
+ * baseline is a valid saved state ("saved, no findings" ≠ "never saved").
27
+ */
28
+ save(tool, entries) {
29
+ try {
30
+ const capturedAt = Date.now();
31
+ const byFingerprint = new Map();
32
+ for (const e of entries)
33
+ byFingerprint.set(e.fingerprint, e.payload);
34
+ const rows = [...byFingerprint.entries()]
35
+ .sort(([a], [b]) => Number(a > b) - Number(a < b))
36
+ .map(([fingerprint, payload]) => ({ tool, fingerprint, payload, capturedAt }));
37
+ this.datastore.transaction((tx) => {
38
+ tx.delete(toolBaselineEntries).where(eq(toolBaselineEntries.tool, tool)).run();
39
+ // Chunked insert to avoid hitting SQLite's SQLITE_MAX_VARIABLE_NUMBER (~32766 vars).
40
+ // Each row uses 4 bound parameters (tool, fingerprint, payload, capturedAt).
41
+ // 2000-row chunks are comfortably safe even on older SQLite builds.
42
+ if (rows.length > 0) {
43
+ const CHUNK = 2000;
44
+ for (let i = 0; i < rows.length; i += CHUNK) {
45
+ const chunk = rows.slice(i, i + CHUNK);
46
+ tx.insert(toolBaselineEntries).values(chunk).run();
47
+ }
48
+ }
49
+ // Upsert the existence marker keyed on `tool`.
50
+ tx.insert(toolBaselineMeta)
51
+ .values({ tool, capturedAt })
52
+ .onConflictDoUpdate({
53
+ target: toolBaselineMeta.tool,
54
+ set: { capturedAt: sql `excluded.captured_at` },
55
+ })
56
+ .run();
57
+ });
58
+ logger.info({
59
+ evt: 'datastore.baseline.save.complete',
60
+ module: MODULE_NAME,
61
+ msg: 'Saved tool baseline',
62
+ tool,
63
+ count: rows.length,
64
+ });
65
+ }
66
+ catch (error) {
67
+ /* v8 ignore start */
68
+ logger.error({
69
+ evt: 'datastore.baseline.save.error',
70
+ module: MODULE_NAME,
71
+ msg: 'Failed to save tool baseline',
72
+ tool,
73
+ error: error instanceof Error ? error.message : String(error),
74
+ ...(error instanceof Error && error.stack ? { stack: error.stack } : {}),
75
+ });
76
+ throw error;
77
+ /* v8 ignore stop */
78
+ }
79
+ }
80
+ /** Load this tool's baseline rows (fingerprint + payload). Empty when none saved. */
81
+ load(tool) {
82
+ try {
83
+ const rows = this.datastore.db
84
+ .select({
85
+ fingerprint: toolBaselineEntries.fingerprint,
86
+ payload: toolBaselineEntries.payload,
87
+ })
88
+ .from(toolBaselineEntries)
89
+ .where(eq(toolBaselineEntries.tool, tool))
90
+ .all();
91
+ logger.info({
92
+ evt: 'datastore.baseline.load.complete',
93
+ module: MODULE_NAME,
94
+ msg: 'Loaded tool baseline',
95
+ tool,
96
+ count: rows.length,
97
+ });
98
+ return rows.map((r) => ({ fingerprint: r.fingerprint, payload: r.payload }));
99
+ }
100
+ catch (error) {
101
+ /* v8 ignore start */
102
+ logger.error({
103
+ evt: 'datastore.baseline.load.error',
104
+ module: MODULE_NAME,
105
+ msg: 'Failed to load tool baseline',
106
+ tool,
107
+ error: error instanceof Error ? error.message : String(error),
108
+ });
109
+ throw error;
110
+ /* v8 ignore stop */
111
+ }
112
+ }
113
+ /** True iff this tool has saved a baseline (independent of whether it was empty). */
114
+ exists(tool) {
115
+ const row = this.datastore.db
116
+ .select({ tool: toolBaselineMeta.tool })
117
+ .from(toolBaselineMeta)
118
+ .where(eq(toolBaselineMeta.tool, tool))
119
+ .limit(1)
120
+ .get();
121
+ return row !== undefined;
122
+ }
123
+ /** When this tool's baseline was captured (ms epoch), or undefined when none exists. */
124
+ capturedAt(tool) {
125
+ const row = this.datastore.db
126
+ .select({ capturedAt: toolBaselineMeta.capturedAt })
127
+ .from(toolBaselineMeta)
128
+ .where(eq(toolBaselineMeta.tool, tool))
129
+ .limit(1)
130
+ .get();
131
+ return row?.capturedAt;
132
+ }
133
+ /**
134
+ * Delete this tool's baseline — entries + the meta existence marker, one
135
+ * transaction (`tools data purge`, ADR-0042). After a clear, `exists()` is
136
+ * false (vs. `save(tool, [])`, which leaves an EMPTY-but-saved baseline).
137
+ */
138
+ clear(tool) {
139
+ let entries = 0;
140
+ let meta = false;
141
+ this.datastore.transaction((tx) => {
142
+ entries = tx
143
+ .delete(toolBaselineEntries)
144
+ .where(eq(toolBaselineEntries.tool, tool))
145
+ .run().changes;
146
+ meta = tx.delete(toolBaselineMeta).where(eq(toolBaselineMeta.tool, tool)).run().changes > 0;
147
+ });
148
+ logger.info({
149
+ evt: 'datastore.baseline.clear.complete',
150
+ module: MODULE_NAME,
151
+ msg: 'Cleared tool baseline',
152
+ tool,
153
+ entries,
154
+ meta,
155
+ });
156
+ return { entries, meta };
157
+ }
158
+ }
159
+ //# sourceMappingURL=baseline-repo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline-repo.js","sourceRoot":"","sources":["../src/baseline-repo.ts"],"names":[],"mappings":"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"}