@smallironman/mcp-memory-keeper 0.12.2-fork1

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 (110) hide show
  1. package/CHANGELOG.md +542 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1281 -0
  4. package/bin/mcp-memory-keeper +54 -0
  5. package/dist/__tests__/e2e/issue33-reproduce.test.js +234 -0
  6. package/dist/__tests__/e2e/server-e2e.test.js +341 -0
  7. package/dist/__tests__/helpers/database-test-helper.js +160 -0
  8. package/dist/__tests__/helpers/test-server.js +92 -0
  9. package/dist/__tests__/integration/advanced-features.test.js +614 -0
  10. package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
  11. package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
  12. package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
  13. package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
  14. package/dist/__tests__/integration/channels.test.js +376 -0
  15. package/dist/__tests__/integration/checkpoint.test.js +251 -0
  16. package/dist/__tests__/integration/concurrent-access.test.js +190 -0
  17. package/dist/__tests__/integration/context-operations.test.js +243 -0
  18. package/dist/__tests__/integration/contextDiff.test.js +852 -0
  19. package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
  20. package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
  21. package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
  22. package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
  23. package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
  24. package/dist/__tests__/integration/contextSearch.test.js +1054 -0
  25. package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
  26. package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
  27. package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
  28. package/dist/__tests__/integration/database-initialization.test.js +134 -0
  29. package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
  30. package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
  31. package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
  32. package/dist/__tests__/integration/error-cases.test.js +411 -0
  33. package/dist/__tests__/integration/export-import.test.js +367 -0
  34. package/dist/__tests__/integration/feature-flags.test.js +542 -0
  35. package/dist/__tests__/integration/file-operations.test.js +264 -0
  36. package/dist/__tests__/integration/filterBySessionId.test.js +251 -0
  37. package/dist/__tests__/integration/git-integration.test.js +241 -0
  38. package/dist/__tests__/integration/index-tools.test.js +496 -0
  39. package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
  40. package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
  41. package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
  42. package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
  43. package/dist/__tests__/integration/issue24-final-fix.test.js +241 -0
  44. package/dist/__tests__/integration/issue24-fix-validation.test.js +158 -0
  45. package/dist/__tests__/integration/issue24-reproduce.test.js +225 -0
  46. package/dist/__tests__/integration/issue24-token-limit.test.js +199 -0
  47. package/dist/__tests__/integration/issue33-array-items-schema.test.js +165 -0
  48. package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
  49. package/dist/__tests__/integration/migrations.test.js +528 -0
  50. package/dist/__tests__/integration/multi-agent.test.js +546 -0
  51. package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
  52. package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
  53. package/dist/__tests__/integration/project-directory.test.js +291 -0
  54. package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
  55. package/dist/__tests__/integration/retention.test.js +513 -0
  56. package/dist/__tests__/integration/search.test.js +333 -0
  57. package/dist/__tests__/integration/semantic-search.test.js +266 -0
  58. package/dist/__tests__/integration/server-initialization.test.js +305 -0
  59. package/dist/__tests__/integration/session-management.test.js +219 -0
  60. package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
  61. package/dist/__tests__/integration/smart-compaction.test.js +230 -0
  62. package/dist/__tests__/integration/summarization.test.js +308 -0
  63. package/dist/__tests__/integration/tokenLimitEnforcement.test.js +134 -0
  64. package/dist/__tests__/integration/tool-profiles-integration.test.js +150 -0
  65. package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
  66. package/dist/__tests__/security/input-validation.test.js +115 -0
  67. package/dist/__tests__/utils/agents.test.js +473 -0
  68. package/dist/__tests__/utils/database.test.js +177 -0
  69. package/dist/__tests__/utils/git.test.js +122 -0
  70. package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
  71. package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
  72. package/dist/__tests__/utils/project-directory-messages.test.js +192 -0
  73. package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
  74. package/dist/__tests__/utils/token-limits.test.js +225 -0
  75. package/dist/__tests__/utils/tool-profiles.test.js +374 -0
  76. package/dist/__tests__/utils/validation.test.js +200 -0
  77. package/dist/__tests__/utils/vector-store.test.js +231 -0
  78. package/dist/handlers/contextWatchHandlers.js +206 -0
  79. package/dist/index.js +4425 -0
  80. package/dist/migrations/003_add_channels.js +174 -0
  81. package/dist/migrations/004_add_context_watch.js +151 -0
  82. package/dist/migrations/005_add_context_watch.js +98 -0
  83. package/dist/migrations/simplify-sharing.js +117 -0
  84. package/dist/repositories/BaseRepository.js +30 -0
  85. package/dist/repositories/CheckpointRepository.js +140 -0
  86. package/dist/repositories/ContextRepository.js +2017 -0
  87. package/dist/repositories/FileRepository.js +104 -0
  88. package/dist/repositories/RepositoryManager.js +62 -0
  89. package/dist/repositories/SessionRepository.js +66 -0
  90. package/dist/repositories/WatcherRepository.js +252 -0
  91. package/dist/repositories/index.js +15 -0
  92. package/dist/test-helpers/database-helper.js +128 -0
  93. package/dist/types/entities.js +3 -0
  94. package/dist/utils/agents.js +791 -0
  95. package/dist/utils/channels.js +150 -0
  96. package/dist/utils/database.js +780 -0
  97. package/dist/utils/feature-flags.js +476 -0
  98. package/dist/utils/git.js +145 -0
  99. package/dist/utils/knowledge-graph.js +264 -0
  100. package/dist/utils/migrationHealthCheck.js +373 -0
  101. package/dist/utils/migrations.js +452 -0
  102. package/dist/utils/retention.js +460 -0
  103. package/dist/utils/timestamps.js +112 -0
  104. package/dist/utils/token-limits.js +350 -0
  105. package/dist/utils/tool-profiles.js +242 -0
  106. package/dist/utils/validation.js +296 -0
  107. package/dist/utils/vector-store.js +247 -0
  108. package/examples/config.json +31 -0
  109. package/examples/project-directory-setup.md +114 -0
  110. package/package.json +85 -0
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
41
+ const fs = __importStar(require("fs"));
42
+ const simple_git_1 = require("simple-git");
43
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
44
+ const uuid_1 = require("uuid");
45
+ describe('Project Directory Feature Tests', () => {
46
+ let tempProjectPath;
47
+ let tempDbPath;
48
+ let db;
49
+ beforeEach(async () => {
50
+ // Create a temporary project directory with git repo
51
+ tempProjectPath = path.join(os.tmpdir(), `test-project-${Date.now()}`);
52
+ fs.mkdirSync(tempProjectPath, { recursive: true });
53
+ // Initialize git repo in the temp project
54
+ const git = (0, simple_git_1.simpleGit)(tempProjectPath);
55
+ await git.init();
56
+ await git.addConfig('user.name', 'Test User');
57
+ await git.addConfig('user.email', 'test@example.com');
58
+ // Use repo-local hooks directory to prevent global hooks from interfering
59
+ const localHooksDir = path.join(tempProjectPath, '.git', 'hooks');
60
+ await git.addConfig('core.hooksPath', localHooksDir);
61
+ await git.addConfig('commit.gpgsign', 'false');
62
+ // Create initial commit
63
+ fs.writeFileSync(path.join(tempProjectPath, 'README.md'), '# Test Project');
64
+ await git.add('.');
65
+ await git.commit('Initial commit');
66
+ // Create test database
67
+ tempDbPath = path.join(os.tmpdir(), `test-db-${Date.now()}.db`);
68
+ db = new better_sqlite3_1.default(tempDbPath);
69
+ // Initialize database schema
70
+ db.exec(`
71
+ CREATE TABLE IF NOT EXISTS sessions (
72
+ id TEXT PRIMARY KEY,
73
+ name TEXT,
74
+ description TEXT,
75
+ branch TEXT,
76
+ parent_id TEXT,
77
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
78
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
79
+ FOREIGN KEY (parent_id) REFERENCES sessions(id)
80
+ );
81
+
82
+ CREATE TABLE IF NOT EXISTS context_items (
83
+ id TEXT PRIMARY KEY,
84
+ session_id TEXT NOT NULL,
85
+ key TEXT NOT NULL,
86
+ value TEXT NOT NULL,
87
+ category TEXT,
88
+ priority TEXT DEFAULT 'normal',
89
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
90
+ FOREIGN KEY (session_id) REFERENCES sessions(id),
91
+ UNIQUE(session_id, key)
92
+ );
93
+
94
+ CREATE TABLE IF NOT EXISTS checkpoints (
95
+ id TEXT PRIMARY KEY,
96
+ session_id TEXT NOT NULL,
97
+ name TEXT NOT NULL,
98
+ description TEXT,
99
+ git_status TEXT,
100
+ git_branch TEXT,
101
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
102
+ FOREIGN KEY (session_id) REFERENCES sessions(id)
103
+ );
104
+
105
+ CREATE TABLE IF NOT EXISTS checkpoint_items (
106
+ id TEXT PRIMARY KEY,
107
+ checkpoint_id TEXT NOT NULL,
108
+ context_item_id TEXT NOT NULL,
109
+ FOREIGN KEY (checkpoint_id) REFERENCES checkpoints(id),
110
+ FOREIGN KEY (context_item_id) REFERENCES context_items(id)
111
+ );
112
+ `);
113
+ });
114
+ afterEach(() => {
115
+ // Clean up
116
+ db.close();
117
+ try {
118
+ fs.unlinkSync(tempDbPath);
119
+ fs.rmSync(tempProjectPath, { recursive: true, force: true });
120
+ }
121
+ catch (_e) {
122
+ // Ignore
123
+ }
124
+ });
125
+ describe('Git operations with project directory', () => {
126
+ it('should detect git repository in project directory', async () => {
127
+ const git = (0, simple_git_1.simpleGit)(tempProjectPath);
128
+ const status = await git.status();
129
+ const branch = await git.branch();
130
+ expect(branch.current).toBeTruthy();
131
+ expect(status.isClean()).toBe(true);
132
+ });
133
+ it('should capture git status for checkpoint', async () => {
134
+ // Create session
135
+ const sessionId = (0, uuid_1.v4)();
136
+ db.prepare('INSERT INTO sessions (id, name, branch) VALUES (?, ?, ?)').run(sessionId, 'Test Session', 'master');
137
+ // Make changes in repo
138
+ fs.writeFileSync(path.join(tempProjectPath, 'new-file.txt'), 'new content');
139
+ // Get git status
140
+ const git = (0, simple_git_1.simpleGit)(tempProjectPath);
141
+ const status = await git.status();
142
+ const branch = await git.branch();
143
+ const gitStatus = JSON.stringify({
144
+ modified: status.modified,
145
+ created: status.created,
146
+ deleted: status.deleted,
147
+ staged: status.staged,
148
+ not_added: status.not_added,
149
+ ahead: status.ahead,
150
+ behind: status.behind,
151
+ });
152
+ // Create checkpoint with git status
153
+ const checkpointId = (0, uuid_1.v4)();
154
+ db.prepare(`
155
+ INSERT INTO checkpoints (id, session_id, name, git_status, git_branch)
156
+ VALUES (?, ?, ?, ?, ?)
157
+ `).run(checkpointId, sessionId, 'Test Checkpoint', gitStatus, branch.current);
158
+ // Verify checkpoint
159
+ const checkpoint = db
160
+ .prepare('SELECT * FROM checkpoints WHERE id = ?')
161
+ .get(checkpointId);
162
+ expect(checkpoint.git_branch).toBe(branch.current);
163
+ const savedStatus = JSON.parse(checkpoint.git_status);
164
+ expect(savedStatus.not_added).toContain('new-file.txt');
165
+ });
166
+ it('should handle git commit with context save', async () => {
167
+ const sessionId = (0, uuid_1.v4)();
168
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
169
+ // Add context items
170
+ db.prepare(`
171
+ INSERT INTO context_items (id, session_id, key, value, category, priority)
172
+ VALUES (?, ?, ?, ?, ?, ?)
173
+ `).run((0, uuid_1.v4)(), sessionId, 'current_task', 'Test git integration', 'task', 'high');
174
+ // Make changes and commit
175
+ fs.writeFileSync(path.join(tempProjectPath, 'test.txt'), 'test content');
176
+ const git = (0, simple_git_1.simpleGit)(tempProjectPath);
177
+ await git.add('.');
178
+ const _commitResult = await git.commit('Test commit');
179
+ // Save commit info as context
180
+ const timestamp = new Date().toISOString();
181
+ db.prepare(`
182
+ INSERT INTO context_items (id, session_id, key, value, category)
183
+ VALUES (?, ?, ?, ?, ?)
184
+ `).run((0, uuid_1.v4)(), sessionId, `commit_${timestamp}`, 'Test commit', 'git');
185
+ // Create checkpoint for the commit
186
+ const gitStatus = await git.status();
187
+ const gitBranch = await git.branch();
188
+ const checkpointId = (0, uuid_1.v4)();
189
+ db.prepare(`
190
+ INSERT INTO checkpoints (id, session_id, name, description, git_status, git_branch)
191
+ VALUES (?, ?, ?, ?, ?, ?)
192
+ `).run(checkpointId, sessionId, `git-commit-${timestamp}`, `Git commit: Test commit`, JSON.stringify(gitStatus), gitBranch.current);
193
+ // Link context items to checkpoint
194
+ const contextItems = db
195
+ .prepare('SELECT id FROM context_items WHERE session_id = ?')
196
+ .all(sessionId);
197
+ contextItems.forEach((item) => {
198
+ db.prepare(`
199
+ INSERT INTO checkpoint_items (id, checkpoint_id, context_item_id)
200
+ VALUES (?, ?, ?)
201
+ `).run((0, uuid_1.v4)(), checkpointId, item.id);
202
+ });
203
+ // Verify
204
+ const checkpoint = db
205
+ .prepare('SELECT * FROM checkpoints WHERE id = ?')
206
+ .get(checkpointId);
207
+ expect(checkpoint.name).toContain('git-commit-');
208
+ expect(checkpoint.git_branch).toBeTruthy();
209
+ const linkedItems = db
210
+ .prepare(`
211
+ SELECT COUNT(*) as count FROM checkpoint_items WHERE checkpoint_id = ?
212
+ `)
213
+ .get(checkpointId);
214
+ expect(linkedItems.count).toBeGreaterThan(0);
215
+ });
216
+ });
217
+ describe('Non-git directory handling', () => {
218
+ it('should handle directory without git gracefully', async () => {
219
+ const nonGitPath = path.join(os.tmpdir(), `non-git-${Date.now()}`);
220
+ fs.mkdirSync(nonGitPath, { recursive: true });
221
+ try {
222
+ const git = (0, simple_git_1.simpleGit)(nonGitPath);
223
+ let isGitRepo = true;
224
+ let gitError = '';
225
+ try {
226
+ await git.status();
227
+ }
228
+ catch (error) {
229
+ isGitRepo = false;
230
+ gitError = error.message;
231
+ }
232
+ expect(isGitRepo).toBe(false);
233
+ expect(gitError).toContain('not a git repository');
234
+ }
235
+ finally {
236
+ fs.rmSync(nonGitPath, { recursive: true, force: true });
237
+ }
238
+ });
239
+ });
240
+ describe('Project directory with special characters', () => {
241
+ it('should handle paths with spaces', async () => {
242
+ const pathWithSpaces = path.join(os.tmpdir(), `test project ${Date.now()}`);
243
+ fs.mkdirSync(pathWithSpaces, { recursive: true });
244
+ const git = (0, simple_git_1.simpleGit)(pathWithSpaces);
245
+ await git.init();
246
+ // Configure git for this test to avoid CI failures
247
+ await git.addConfig('user.name', 'Test User');
248
+ await git.addConfig('user.email', 'test@example.com');
249
+ // Use repo-local hooks directory to prevent global hooks from interfering
250
+ const localHooksDir = path.join(pathWithSpaces, '.git', 'hooks');
251
+ await git.addConfig('core.hooksPath', localHooksDir);
252
+ await git.addConfig('commit.gpgsign', 'false');
253
+ try {
254
+ fs.writeFileSync(path.join(pathWithSpaces, 'test.txt'), 'content');
255
+ await git.add('.');
256
+ await git.commit('Initial commit');
257
+ const status = await git.status();
258
+ expect(status.isClean()).toBe(true);
259
+ }
260
+ finally {
261
+ fs.rmSync(pathWithSpaces, { recursive: true, force: true });
262
+ }
263
+ });
264
+ });
265
+ describe('Session and project directory integration', () => {
266
+ it('should store git branch with session', async () => {
267
+ const git = (0, simple_git_1.simpleGit)(tempProjectPath);
268
+ const branch = await git.branch();
269
+ const sessionId = (0, uuid_1.v4)();
270
+ db.prepare(`
271
+ INSERT INTO sessions (id, name, description, branch)
272
+ VALUES (?, ?, ?, ?)
273
+ `).run(sessionId, 'Feature Development', 'Working on new feature', branch.current);
274
+ const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);
275
+ expect(session.branch).toBe(branch.current);
276
+ });
277
+ it('should update session when switching branches', async () => {
278
+ const git = (0, simple_git_1.simpleGit)(tempProjectPath);
279
+ // Create and switch to new branch
280
+ await git.checkoutLocalBranch('feature-branch');
281
+ const newBranch = await git.branch();
282
+ const sessionId = (0, uuid_1.v4)();
283
+ db.prepare(`
284
+ INSERT INTO sessions (id, name, branch)
285
+ VALUES (?, ?, ?)
286
+ `).run(sessionId, 'Feature Work', newBranch.current);
287
+ const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);
288
+ expect(session.branch).toBe('feature-branch');
289
+ });
290
+ });
291
+ });
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const globals_1 = require("@jest/globals");
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const os = __importStar(require("os"));
40
+ const database_js_1 = require("../../utils/database.js");
41
+ (0, globals_1.describe)('Resource Cleanup Tests', () => {
42
+ let tempDir;
43
+ (0, globals_1.beforeEach)(() => {
44
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mcp-test-'));
45
+ });
46
+ (0, globals_1.afterEach)(() => {
47
+ fs.rmSync(tempDir, { recursive: true, force: true });
48
+ });
49
+ (0, globals_1.it)('should properly close database connections', () => {
50
+ const tempDbPath = path.join(tempDir, 'test.db');
51
+ const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
52
+ const db = dbManager.getDatabase();
53
+ // Verify database is open
54
+ (0, globals_1.expect)(() => {
55
+ db.prepare('SELECT 1').get();
56
+ }).not.toThrow();
57
+ // Close the database
58
+ dbManager.close();
59
+ // Verify database is closed
60
+ (0, globals_1.expect)(() => {
61
+ db.prepare('SELECT 1').get();
62
+ }).toThrow('The database connection is not open');
63
+ });
64
+ (0, globals_1.it)('should release file locks after closing', () => {
65
+ const tempDbPath = path.join(tempDir, 'test.db');
66
+ const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
67
+ // Database should be created
68
+ (0, globals_1.expect)(fs.existsSync(tempDbPath)).toBe(true);
69
+ // Close the database
70
+ dbManager.close();
71
+ // Should be able to delete the file (no locks)
72
+ (0, globals_1.expect)(() => {
73
+ fs.unlinkSync(tempDbPath);
74
+ }).not.toThrow();
75
+ });
76
+ (0, globals_1.it)('should handle errors during initialization gracefully', () => {
77
+ // Try to create database in non-existent directory
78
+ const invalidPath = path.join(tempDir, 'non-existent', 'test.db');
79
+ (0, globals_1.expect)(() => {
80
+ new database_js_1.DatabaseManager({ filename: invalidPath });
81
+ }).toThrow();
82
+ });
83
+ (0, globals_1.it)('should clean up WAL files on close', () => {
84
+ const tempDbPath = path.join(tempDir, 'test.db');
85
+ const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
86
+ const db = dbManager.getDatabase();
87
+ // Perform some operations to ensure WAL files are created
88
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('test', 'Test');
89
+ // WAL files should exist
90
+ (0, globals_1.expect)(fs.existsSync(tempDbPath + '-wal')).toBe(true);
91
+ (0, globals_1.expect)(fs.existsSync(tempDbPath + '-shm')).toBe(true);
92
+ // Close database
93
+ dbManager.close();
94
+ // WAL files should be cleaned up
95
+ // Note: They might still exist but should be minimal size
96
+ if (fs.existsSync(tempDbPath + '-wal')) {
97
+ const walSize = fs.statSync(tempDbPath + '-wal').size;
98
+ (0, globals_1.expect)(walSize).toBe(0);
99
+ }
100
+ });
101
+ (0, globals_1.it)('should handle multiple close calls safely', () => {
102
+ const tempDbPath = path.join(tempDir, 'test.db');
103
+ const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
104
+ // First close
105
+ (0, globals_1.expect)(() => {
106
+ dbManager.close();
107
+ }).not.toThrow();
108
+ // Second close should not throw
109
+ (0, globals_1.expect)(() => {
110
+ dbManager.close();
111
+ }).not.toThrow();
112
+ });
113
+ (0, globals_1.it)('should release memory for large operations', () => {
114
+ const tempDbPath = path.join(tempDir, 'test.db');
115
+ const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
116
+ const db = dbManager.getDatabase();
117
+ // Create session
118
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('test', 'Test');
119
+ // Insert many items
120
+ const stmt = db.prepare(`
121
+ INSERT INTO context_items (id, session_id, key, value, category, priority)
122
+ VALUES (?, ?, ?, ?, ?, ?)
123
+ `);
124
+ const largeValue = 'x'.repeat(1000); // 1KB per value
125
+ // Use transaction for better performance
126
+ const insertMany = db.transaction((count) => {
127
+ for (let i = 0; i < count; i++) {
128
+ stmt.run(`item-${i}`, 'test', `key-${i}`, largeValue, 'test', 'normal');
129
+ }
130
+ });
131
+ // Insert 1000 items (1MB of data)
132
+ insertMany(1000);
133
+ // Verify data was inserted
134
+ const count = db.prepare('SELECT COUNT(*) as count FROM context_items').get();
135
+ (0, globals_1.expect)(count.count).toBe(1000);
136
+ // Close and cleanup
137
+ dbManager.close();
138
+ // Memory should be released (hard to test directly, but no errors should occur)
139
+ });
140
+ (0, globals_1.it)('should handle database file corruption gracefully', () => {
141
+ const tempDbPath = path.join(tempDir, 'corrupt.db');
142
+ // Create a corrupted database file
143
+ fs.writeFileSync(tempDbPath, 'This is not a valid SQLite database');
144
+ // Should throw when trying to open corrupted database
145
+ (0, globals_1.expect)(() => {
146
+ new database_js_1.DatabaseManager({ filename: tempDbPath });
147
+ }).toThrow(/not a database|malformed/);
148
+ });
149
+ });