@j0hanz/memdb 1.0.1 β†’ 1.0.3

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 (71) hide show
  1. package/README.md +157 -66
  2. package/dist/core/database.d.ts +2 -7
  3. package/dist/core/database.d.ts.map +1 -1
  4. package/dist/core/database.js +72 -81
  5. package/dist/core/database.js.map +1 -1
  6. package/dist/core/db-worker-client.d.ts +8 -0
  7. package/dist/core/db-worker-client.d.ts.map +1 -0
  8. package/dist/core/db-worker-client.js +67 -0
  9. package/dist/core/db-worker-client.js.map +1 -0
  10. package/dist/core/db-worker-protocol.d.ts +77 -0
  11. package/dist/core/db-worker-protocol.d.ts.map +1 -0
  12. package/dist/core/db-worker-protocol.js +2 -0
  13. package/dist/core/db-worker-protocol.js.map +1 -0
  14. package/dist/core/db-worker.d.ts +2 -0
  15. package/dist/core/db-worker.d.ts.map +1 -0
  16. package/dist/core/db-worker.js +73 -0
  17. package/dist/core/db-worker.js.map +1 -0
  18. package/dist/core/memory-mappers.d.ts +11 -0
  19. package/dist/core/memory-mappers.d.ts.map +1 -0
  20. package/dist/core/memory-mappers.js +52 -0
  21. package/dist/core/memory-mappers.js.map +1 -0
  22. package/dist/core/memory-search.d.ts +9 -0
  23. package/dist/core/memory-search.d.ts.map +1 -0
  24. package/dist/core/memory-search.js +46 -0
  25. package/dist/core/memory-search.js.map +1 -0
  26. package/dist/core/memory-service-core.d.ts +9 -0
  27. package/dist/core/memory-service-core.d.ts.map +1 -0
  28. package/dist/core/memory-service-core.js +229 -0
  29. package/dist/core/memory-service-core.js.map +1 -0
  30. package/dist/core/memory-service.d.ts +8 -30
  31. package/dist/core/memory-service.d.ts.map +1 -1
  32. package/dist/core/memory-service.js +184 -169
  33. package/dist/core/memory-service.js.map +1 -1
  34. package/dist/core/memory-types.d.ts +18 -0
  35. package/dist/core/memory-types.d.ts.map +1 -0
  36. package/dist/core/memory-types.js +2 -0
  37. package/dist/core/memory-types.js.map +1 -0
  38. package/dist/core/row-mappers.d.ts +7 -0
  39. package/dist/core/row-mappers.d.ts.map +1 -0
  40. package/dist/core/row-mappers.js +52 -0
  41. package/dist/core/row-mappers.js.map +1 -0
  42. package/dist/core/search-errors.d.ts +2 -0
  43. package/dist/core/search-errors.d.ts.map +1 -0
  44. package/dist/core/search-errors.js +21 -0
  45. package/dist/core/search-errors.js.map +1 -0
  46. package/dist/index.js +86 -18
  47. package/dist/index.js.map +1 -1
  48. package/dist/lib/errors.d.ts +3 -2
  49. package/dist/lib/errors.d.ts.map +1 -1
  50. package/dist/lib/errors.js.map +1 -1
  51. package/dist/schemas/inputs.d.ts.map +1 -1
  52. package/dist/schemas/inputs.js +5 -5
  53. package/dist/schemas/inputs.js.map +1 -1
  54. package/dist/tools/index.d.ts.map +1 -1
  55. package/dist/tools/index.js +130 -14
  56. package/dist/tools/index.js.map +1 -1
  57. package/dist/types/index.d.ts +25 -17
  58. package/dist/types/index.d.ts.map +1 -1
  59. package/dist/utils/config.d.ts +4 -0
  60. package/dist/utils/config.d.ts.map +1 -1
  61. package/dist/utils/config.js +48 -1
  62. package/dist/utils/config.js.map +1 -1
  63. package/dist/utils/logger.d.ts +6 -2
  64. package/dist/utils/logger.d.ts.map +1 -1
  65. package/dist/utils/logger.js +25 -10
  66. package/dist/utils/logger.js.map +1 -1
  67. package/dist/utils/protocol.d.ts +2 -0
  68. package/dist/utils/protocol.d.ts.map +1 -0
  69. package/dist/utils/protocol.js +7 -0
  70. package/dist/utils/protocol.js.map +1 -0
  71. package/package.json +5 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # memdb
2
2
 
3
- A memory-based MCP server using SQLite in-memory database.
3
+ A SQLite-backed MCP memory server (on-disk by default, in-memory optional).
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@j0hanz/memdb.svg)](https://www.npmjs.com/package/@j0hanz/memdb)
6
6
 
@@ -10,17 +10,17 @@ A memory-based MCP server using SQLite in-memory database.
10
10
 
11
11
  [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=memdb&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBqMGhhbnovbWVtZGJAbGF0ZXN0Il19)
12
12
 
13
- ## ✨ Features
13
+ ## Features
14
14
 
15
- | Feature | Description |
16
- | :----------------------- | :-------------------------------------------------------- |
17
- | 🧠 **Memory Storage** | Store text-based memories with tags and importance scores |
18
- | πŸ” **Full-Text Search** | Search memories using FTS5 with relevance ranking |
19
- | πŸ•ΈοΈ **Graph Connections** | Link memories together to create knowledge graphs |
20
- | πŸ“Š **Analytics** | Track memory statistics and database health |
21
- | πŸ”’ **Local Privacy** | All data stored locally in SQLite (`data/memory.db`) |
15
+ | Feature | Description |
16
+ | :---------------- | :---------------------------------------------------------------- |
17
+ | Memory Storage | Store text memories with tags, importance, and type |
18
+ | Full-Text Search | FTS5-backed phrase search with relevance ranking |
19
+ | Graph Connections | Link memories and traverse relationships |
20
+ | Stats | Memory and relationship counts |
21
+ | Local Privacy | All data stored locally in SQLite (`.memdb/memory.db` by default) |
22
22
 
23
- ## πŸš€ Quick Start
23
+ ## Quick Start
24
24
 
25
25
  ### VS Code / Cursor
26
26
 
@@ -35,7 +35,7 @@ Add this to your `mcpServers` configuration:
35
35
  }
36
36
  ```
37
37
 
38
- ## πŸ“¦ Installation
38
+ ## Installation
39
39
 
40
40
  ### NPX (Recommended)
41
41
 
@@ -58,91 +58,147 @@ npm install
58
58
  npm run build
59
59
  ```
60
60
 
61
- ## βš™οΈ Configuration
61
+ ## Configuration
62
62
 
63
- The server uses a local SQLite database located at `data/memory.db` relative to the working directory. No environment variables are required for basic operation.
63
+ The server uses a local SQLite database at `<cwd>/.memdb/memory.db` by default.
64
+ The path is resolved to an absolute path unless you use `:memory:`.
64
65
 
65
- ## πŸ”§ Tools
66
+ ### Environment Variables
67
+
68
+ - `MEMDB_PATH`: Override the database path (`:memory:` for in-memory).
69
+ - `MEMDB_LOG_LEVEL`: `info`, `warn`, or `error` (default: `info`).
70
+
71
+ ### CLI Flags
72
+
73
+ - `--db <path>`: Override the database path.
74
+ - `--memory`: Use in-memory database (`:memory:`).
75
+ - `--log-level <level>`: `info`, `warn`, or `error`.
76
+
77
+ Precedence: CLI flags > environment variables > defaults.
78
+
79
+ ## Tool Response Format
80
+
81
+ All tools return structured JSON in both `content` and `structuredContent`.
82
+
83
+ Success:
84
+
85
+ ```json
86
+ {
87
+ "ok": true,
88
+ "result": { "...": "..." }
89
+ }
90
+ ```
91
+
92
+ Error:
93
+
94
+ ```json
95
+ {
96
+ "ok": false,
97
+ "error": {
98
+ "code": "E_CODE",
99
+ "message": "Human-readable message"
100
+ }
101
+ }
102
+ ```
103
+
104
+ ## Tools
66
105
 
67
106
  ### `store_memory`
68
107
 
69
108
  Store a new memory with optional tags and metadata.
70
109
 
71
- | Parameter | Type | Required | Default | Description |
72
- | :----------- | :------- | :------- | :------ | :---------------------------------------------- |
73
- | `content` | string | βœ… | - | The content of the memory |
74
- | `tags` | string[] | ❌ | - | Tags to categorize the memory |
75
- | `importance` | number | ❌ | - | Importance score (0-10) |
76
- | `memoryType` | string | ❌ | - | Type of memory (e.g., conversation, fact, rule) |
110
+ | Parameter | Type | Required | Default | Description |
111
+ | :----------- | :------- | :------- | :-------- | :----------------------------------------- |
112
+ | `content` | string | Yes | - | The content of the memory (1-100000 chars) |
113
+ | `tags` | string[] | No | - | Tags (max 100, each 1-50 chars) |
114
+ | `importance` | number | No | `0` | Importance score (0-10) |
115
+ | `memoryType` | string | No | `general` | Type of memory (1-50 chars) |
116
+
117
+ **Returns:** `{ id, hash, isNew }`
118
+
119
+ Notes:
77
120
 
78
- **Returns:** The created memory object with its hash.
121
+ - Content is deduplicated by MD5 hash. Storing the same content again returns the same hash with `isNew: false`.
79
122
 
80
123
  ### `search_memories`
81
124
 
82
125
  Full-text search with filters.
83
126
 
84
- | Parameter | Type | Required | Default | Description |
85
- | :------------- | :------- | :------- | :------ | :------------------------ |
86
- | `query` | string | βœ… | - | Search query |
87
- | `limit` | number | ❌ | - | Maximum number of results |
88
- | `tags` | string[] | ❌ | - | Filter by tags |
89
- | `minRelevance` | number | ❌ | - | Minimum relevance score |
127
+ | Parameter | Type | Required | Default | Description |
128
+ | :------------- | :------- | :------- | :------ | :-------------------------------- |
129
+ | `query` | string | Yes | - | Search query (1-1000 chars) |
130
+ | `limit` | number | No | `10` | Maximum number of results (1-100) |
131
+ | `tags` | string[] | No | - | Filter by tags (max 50) |
132
+ | `minRelevance` | number | No | - | Minimum relevance score (0-1) |
90
133
 
91
- **Returns:** Array of matching memories.
134
+ **Returns:** Array of search results (`Memory` + `relevance`).
92
135
 
93
136
  ### `get_memory`
94
137
 
95
138
  Retrieve a specific memory by its hash.
96
139
 
97
- | Parameter | Type | Required | Default | Description |
98
- | :-------- | :----- | :------- | :------ | :--------------------- |
99
- | `hash` | string | βœ… | - | MD5 hash of the memory |
140
+ | Parameter | Type | Required | Default | Description |
141
+ | :-------- | :----- | :------- | :------ | :------------------ |
142
+ | `hash` | string | Yes | - | MD5 hash (32 chars) |
100
143
 
101
- **Returns:** The memory object.
144
+ **Returns:** `Memory`.
102
145
 
103
146
  ### `delete_memory`
104
147
 
105
148
  Delete a memory by its hash.
106
149
 
107
- | Parameter | Type | Required | Default | Description |
108
- | :-------- | :----- | :------- | :------ | :--------------------- |
109
- | `hash` | string | βœ… | - | MD5 hash of the memory |
150
+ | Parameter | Type | Required | Default | Description |
151
+ | :-------- | :----- | :------- | :------ | :------------------ |
152
+ | `hash` | string | Yes | - | MD5 hash (32 chars) |
110
153
 
111
- **Returns:** Confirmation of deletion.
154
+ **Returns:** `{ deleted: true }`.
112
155
 
113
156
  ### `link_memories`
114
157
 
115
158
  Create a relationship between two memories.
116
159
 
117
- | Parameter | Type | Required | Default | Description |
118
- | :------------- | :----- | :------- | :------ | :------------------------ |
119
- | `fromHash` | string | βœ… | - | Hash of the source memory |
120
- | `toHash` | string | βœ… | - | Hash of the target memory |
121
- | `relationType` | string | βœ… | - | Type of relationship |
160
+ | Parameter | Type | Required | Default | Description |
161
+ | :------------- | :----- | :------- | :------ | :----------------------------------- |
162
+ | `fromHash` | string | Yes | - | Hash of the source memory (32 chars) |
163
+ | `toHash` | string | Yes | - | Hash of the target memory (32 chars) |
164
+ | `relationType` | string | Yes | - | Type of relationship (1-50 chars) |
122
165
 
123
- **Returns:** Confirmation of link creation.
166
+ **Returns:** `{ linked: true }`.
124
167
 
125
168
  ### `get_related`
126
169
 
127
170
  Get memories related to a given memory.
128
171
 
129
- | Parameter | Type | Required | Default | Description |
130
- | :------------- | :----- | :------- | :------ | :-------------------------- |
131
- | `hash` | string | βœ… | - | Hash of the memory |
132
- | `relationType` | string | ❌ | - | Filter by relationship type |
133
- | `depth` | number | ❌ | - | Traversal depth (1-3) |
172
+ | Parameter | Type | Required | Default | Description |
173
+ | :------------- | :----- | :------- | :------ | :---------------------------- |
174
+ | `hash` | string | Yes | - | Hash of the memory (32 chars) |
175
+ | `relationType` | string | No | - | Filter by relationship type |
176
+ | `depth` | number | No | `1` | Traversal depth (1-3) |
134
177
 
135
- **Returns:** Array of related memories.
178
+ **Returns:** Array of related memories (`Memory` + `relation_type`, `depth`).
136
179
 
137
180
  ### `memory_stats`
138
181
 
139
- Get database statistics and health information.
182
+ Get database statistics.
140
183
 
141
184
  _No parameters required._
142
185
 
143
- **Returns:** Database statistics (count, size, etc.).
186
+ **Returns:** `{ memoryCount, relationshipCount }`.
144
187
 
145
- ## πŸ”Œ Client Configuration
188
+ ### Memory Fields
189
+
190
+ All memory-shaped responses include:
191
+
192
+ - `id`: integer ID
193
+ - `content`: original content string
194
+ - `summary`: optional summary (currently unset by tools)
195
+ - `importance`: integer 0-10
196
+ - `memory_type`: string
197
+ - `created_at`: timestamp string
198
+ - `accessed_at`: timestamp string
199
+ - `hash`: MD5 hash
200
+
201
+ ## Client Configuration
146
202
 
147
203
  <details>
148
204
  <summary><b>VS Code</b></summary>
@@ -191,33 +247,68 @@ Add to your `claude_desktop_config.json`:
191
247
 
192
248
  </details>
193
249
 
194
- ## πŸ› οΈ Development
250
+ ## Limits & Constraints
251
+
252
+ | Constraint | Value | Description |
253
+ | :---------------------------- | :------------ | :------------------------------------------------------- |
254
+ | **Max content length** | 100,000 chars | Maximum characters in memory content |
255
+ | **Max query length** | 1,000 chars | Maximum characters in search query |
256
+ | **Max search results** | 100 | Maximum results returned from `search_memories` |
257
+ | **Default search limit** | 10 | Default `limit` for `search_memories` |
258
+ | **Max tags per memory** | 100 | Maximum number of tags when storing a memory |
259
+ | **Max tag length** | 50 chars | Maximum characters per tag |
260
+ | **Max tags in search filter** | 50 | Maximum tags when filtering search results |
261
+ | **Max related memories** | 1,000 | Maximum results from `get_related` queries |
262
+ | **Max traversal depth** | 3 | Maximum depth for relationship traversal |
263
+ | **Importance range** | 0-10 | Allowed range for `importance` |
264
+ | **Min relevance range** | 0-1 | Allowed range for `minRelevance` |
265
+ | **Hash length** | 32 chars | MD5 hash length |
266
+ | **Search mode** | Phrase | Search uses phrase matching (FTS5 operators are escaped) |
267
+
268
+ ### Notes
269
+
270
+ - **Content deduplication**: Memories are deduplicated using MD5 hashes.
271
+ - **Search errors**: If FTS5 is unavailable, `search_memories` returns an error indicating the index is missing. Invalid query syntax returns an error with details.
272
+ - **Local storage**: All data is stored locally in `.memdb/memory.db` unless `:memory:` is used.
273
+
274
+ ## Development
195
275
 
196
276
  ### Prerequisites
197
277
 
198
- - Node.js >= 22.0.0
278
+ - Node.js >= 22.0.0 (required for `node:sqlite`)
199
279
 
200
280
  ### Scripts
201
281
 
202
- | Command | Description |
203
- | :-------------- | :--------------------------------- |
204
- | `npm run build` | Compile TypeScript to `dist/` |
205
- | `npm run dev` | Run in development mode with watch |
206
- | `npm run test` | Run tests |
207
- | `npm run lint` | Run ESLint |
282
+ | Command | Description |
283
+ | :------------------------ | :----------------------------------------- |
284
+ | `npm run clean` | Remove `dist/` |
285
+ | `npm run build` | Compile TypeScript to `dist/` |
286
+ | `npm run dev` | Run in development mode with watch |
287
+ | `npm run start` | Run compiled server (`node dist/index.js`) |
288
+ | `npm run test` | Run tests |
289
+ | `npm run test:coverage` | Run tests with coverage |
290
+ | `npm run lint` | Run ESLint |
291
+ | `npm run format` | Format code with Prettier |
292
+ | `npm run format:check` | Check code formatting |
293
+ | `npm run type-check` | TypeScript type checking |
294
+ | `npm run type-check:test` | Type-check tests only |
295
+ | `npm run maintainability` | Generate maintainability report |
296
+ | `npm run duplication` | Run duplication report (jscpd) |
297
+ | `npm run bench:memory` | Run memory-service benchmark |
298
+ | `npm run inspector` | Run MCP inspector |
208
299
 
209
300
  ### Project Structure
210
301
 
211
302
  ```text
212
303
  src/
213
- β”œβ”€β”€ index.ts # Entry point
214
- β”œβ”€β”€ core/ # Database and memory service
215
- β”œβ”€β”€ tools/ # Tool implementations
216
- β”œβ”€β”€ schemas/ # Zod input/output schemas
217
- β”œβ”€β”€ lib/ # Utility functions
218
- └── utils/ # Config and logger
304
+ |-- index.ts # Entry point
305
+ |-- core/ # Database and memory service
306
+ |-- tools/ # Tool implementations
307
+ |-- schemas/ # Zod input/output schemas
308
+ |-- lib/ # Error helpers
309
+ `-- utils/ # Config and logger
219
310
  ```
220
311
 
221
- ## 🀝 Contributing
312
+ ## Contributing
222
313
 
223
314
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -1,9 +1,4 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
- export declare class DatabaseManager {
3
- private db;
4
- constructor();
5
- private init;
6
- getDb(): DatabaseSync;
7
- }
8
- export declare const dbManager: DatabaseManager;
2
+ export declare const db: DatabaseSync;
3
+ export declare const closeDb: () => void;
9
4
  //# sourceMappingURL=database.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAe;;IAYzB,OAAO,CAAC,IAAI;IA8EL,KAAK,IAAI,YAAY;CAG7B;AAED,eAAO,MAAM,SAAS,iBAAwB,CAAC"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAwE3C,eAAO,MAAM,EAAE,cAAqD,CAAC;AAKrE,eAAO,MAAM,OAAO,QAAO,IAG1B,CAAC"}
@@ -2,86 +2,77 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { DatabaseSync } from 'node:sqlite';
4
4
  import { config } from '../utils/config.js';
5
- export class DatabaseManager {
6
- db;
7
- constructor() {
8
- const dbDir = path.dirname(config.dbPath);
9
- if (!fs.existsSync(dbDir)) {
10
- fs.mkdirSync(dbDir, { recursive: true });
11
- }
12
- this.db = new DatabaseSync(config.dbPath, { timeout: 5000 });
13
- this.init();
14
- }
15
- init() {
16
- this.db.exec('PRAGMA journal_mode = WAL');
17
- this.db.exec('PRAGMA synchronous = NORMAL');
18
- this.db.exec(`
19
- CREATE TABLE IF NOT EXISTS memories (
20
- id INTEGER PRIMARY KEY AUTOINCREMENT,
21
- content TEXT NOT NULL,
22
- summary TEXT,
23
- importance INTEGER DEFAULT 0,
24
- memory_type TEXT DEFAULT 'general',
25
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
26
- accessed_at TEXT DEFAULT CURRENT_TIMESTAMP,
27
- hash TEXT UNIQUE NOT NULL
28
- ) STRICT;
29
- `);
30
- this.db.exec(`
31
- CREATE TABLE IF NOT EXISTS tags (
32
- memory_id INTEGER NOT NULL,
33
- tag TEXT NOT NULL,
34
- PRIMARY KEY (memory_id, tag),
35
- FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE
36
- ) STRICT;
37
- `);
38
- this.db.exec(`
39
- CREATE TABLE IF NOT EXISTS relationships (
40
- id INTEGER PRIMARY KEY AUTOINCREMENT,
41
- from_memory_id INTEGER NOT NULL,
42
- to_memory_id INTEGER NOT NULL,
43
- relation_type TEXT NOT NULL,
44
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
45
- FOREIGN KEY (from_memory_id) REFERENCES memories(id) ON DELETE CASCADE,
46
- FOREIGN KEY (to_memory_id) REFERENCES memories(id) ON DELETE CASCADE,
47
- UNIQUE(from_memory_id, to_memory_id, relation_type)
48
- ) STRICT;
49
- `);
50
- // FTS5
51
- const ftsRow = this.db
52
- .prepare("SELECT count(*) as count FROM sqlite_master WHERE type='table' AND name='memories_fts'")
53
- .get();
54
- const ftsExists = ftsRow?.count ?? 0;
55
- if (ftsExists === 0) {
56
- this.db.exec(`
57
- CREATE VIRTUAL TABLE memories_fts USING fts5(
58
- content,
59
- summary,
60
- content_rowid='id'
61
- );
62
- `);
63
- this.db.exec(`
64
- CREATE TRIGGER memories_ai AFTER INSERT ON memories BEGIN
65
- INSERT INTO memories_fts(rowid, content, summary)
66
- VALUES (new.id, new.content, new.summary);
67
- END;
68
- `);
69
- this.db.exec(`
70
- CREATE TRIGGER memories_au AFTER UPDATE ON memories BEGIN
71
- DELETE FROM memories_fts WHERE rowid = old.id;
72
- INSERT INTO memories_fts(rowid, content, summary) VALUES (new.id, new.content, new.summary);
73
- END;
74
- `);
75
- this.db.exec(`
76
- CREATE TRIGGER memories_ad AFTER DELETE ON memories BEGIN
77
- DELETE FROM memories_fts WHERE rowid = old.id;
78
- END;
79
- `);
80
- }
81
- }
82
- getDb() {
83
- return this.db;
84
- }
5
+ const SCHEMA_SQL = `
6
+ PRAGMA foreign_keys = ON;
7
+ PRAGMA journal_mode = WAL;
8
+ PRAGMA synchronous = NORMAL;
9
+
10
+ CREATE TABLE IF NOT EXISTS memories (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ content TEXT NOT NULL,
13
+ summary TEXT,
14
+ importance INTEGER DEFAULT 0,
15
+ memory_type TEXT DEFAULT 'general',
16
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
17
+ accessed_at TEXT DEFAULT CURRENT_TIMESTAMP,
18
+ hash TEXT UNIQUE NOT NULL
19
+ ) STRICT;
20
+
21
+ CREATE TABLE IF NOT EXISTS tags (
22
+ memory_id INTEGER NOT NULL,
23
+ tag TEXT NOT NULL,
24
+ PRIMARY KEY (memory_id, tag),
25
+ FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE
26
+ ) STRICT;
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_tags_tag_memory_id ON tags(tag, memory_id);
29
+
30
+ CREATE TABLE IF NOT EXISTS relationships (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ from_memory_id INTEGER NOT NULL,
33
+ to_memory_id INTEGER NOT NULL,
34
+ relation_type TEXT NOT NULL,
35
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
36
+ FOREIGN KEY (from_memory_id) REFERENCES memories(id) ON DELETE CASCADE,
37
+ FOREIGN KEY (to_memory_id) REFERENCES memories(id) ON DELETE CASCADE,
38
+ UNIQUE(from_memory_id, to_memory_id, relation_type)
39
+ ) STRICT;
40
+
41
+ CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
42
+ content,
43
+ summary,
44
+ content_rowid='id'
45
+ );
46
+
47
+ CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
48
+ INSERT INTO memories_fts(rowid, content, summary)
49
+ VALUES (new.id, new.content, new.summary);
50
+ END;
51
+
52
+ CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
53
+ DELETE FROM memories_fts WHERE rowid = old.id;
54
+ INSERT INTO memories_fts(rowid, content, summary)
55
+ VALUES (new.id, new.content, new.summary);
56
+ END;
57
+
58
+ CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
59
+ DELETE FROM memories_fts WHERE rowid = old.id;
60
+ END;
61
+ `;
62
+ const FTS_SYNC_SQL = `
63
+ INSERT INTO memories_fts(rowid, content, summary)
64
+ SELECT id, content, summary FROM memories
65
+ WHERE id NOT IN (SELECT rowid FROM memories_fts);
66
+ `;
67
+ if (config.dbPath !== ':memory:') {
68
+ fs.mkdirSync(path.dirname(config.dbPath), { recursive: true });
85
69
  }
86
- export const dbManager = new DatabaseManager();
70
+ export const db = new DatabaseSync(config.dbPath, { timeout: 5000 });
71
+ db.exec(SCHEMA_SQL);
72
+ db.exec(FTS_SYNC_SQL);
73
+ export const closeDb = () => {
74
+ if (!db.isOpen)
75
+ return;
76
+ db.close();
77
+ };
87
78
  //# sourceMappingURL=database.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,OAAO,eAAe;IAClB,EAAE,CAAe;IAEzB;QACE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAE5C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;KAWZ,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;KAOZ,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;KAWZ,CAAC,CAAC;QAEH,OAAO;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE;aACnB,OAAO,CACN,wFAAwF,CACzF;aACA,GAAG,EAAmC,CAAC;QAC1C,MAAM,SAAS,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC;QAErC,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;SAMV,CAAC,CAAC;YAEL,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;SAKV,CAAC,CAAC;YAEL,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;SAKV,CAAC,CAAC;YAEL,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;SAIV,CAAC,CAAC;QACP,CAAC;IACH,CAAC;IAEM,KAAK;QACV,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwDlB,CAAC;AAEF,MAAM,YAAY,GAAG;;;;CAIpB,CAAC;AAEF,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;IACjC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AAErE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACpB,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAEtB,MAAM,CAAC,MAAM,OAAO,GAAG,GAAS,EAAE;IAChC,IAAI,CAAC,EAAE,CAAC,MAAM;QAAE,OAAO;IACvB,EAAE,CAAC,KAAK,EAAE,CAAC;AACb,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { DbWorkerCallMap } from './db-worker-protocol.js';
2
+ type PayloadFor<K extends keyof DbWorkerCallMap> = DbWorkerCallMap[K]['payload'];
3
+ type ResultFor<K extends keyof DbWorkerCallMap> = DbWorkerCallMap[K]['result'];
4
+ type ArgsFor<K extends keyof DbWorkerCallMap> = undefined extends PayloadFor<K> ? [payload?: PayloadFor<K>] : [payload: PayloadFor<K>];
5
+ export declare const callDbWorker: <K extends keyof DbWorkerCallMap>(type: K, ...args: ArgsFor<K>) => Promise<ResultFor<K>>;
6
+ export declare const closeDbWorker: () => Promise<void>;
7
+ export {};
8
+ //# sourceMappingURL=db-worker-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-worker-client.d.ts","sourceRoot":"","sources":["../../src/core/db-worker-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,yBAAyB,CAAC;AAOjC,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,eAAe,IAC7C,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAChC,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,eAAe,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AAC/E,KAAK,OAAO,CAAC,CAAC,SAAS,MAAM,eAAe,IAC1C,SAAS,SAAS,UAAU,CAAC,CAAC,CAAC,GAC3B,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,GACzB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAkE/B,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,eAAe,EAC1D,MAAM,CAAC,EACP,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,KAClB,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAGtB,CAAC;AAEF,eAAO,MAAM,aAAa,QAAa,OAAO,CAAC,IAAI,CAUlD,CAAC"}
@@ -0,0 +1,67 @@
1
+ import process from 'node:process';
2
+ import { Worker } from 'node:worker_threads';
3
+ let worker;
4
+ let nextId = 1;
5
+ const pending = new Map();
6
+ const rejectAll = (error) => {
7
+ for (const entry of pending.values()) {
8
+ entry.reject(error);
9
+ }
10
+ pending.clear();
11
+ };
12
+ const handleMessage = (message) => {
13
+ const entry = pending.get(message.id);
14
+ if (!entry)
15
+ return;
16
+ pending.delete(message.id);
17
+ if (message.ok) {
18
+ entry.resolve(message.result);
19
+ return;
20
+ }
21
+ entry.reject(new Error(message.error.message));
22
+ };
23
+ const createWorker = () => {
24
+ const workerUrl = new URL(import.meta.url.endsWith('.ts') ? './db-worker.ts' : './db-worker.js', import.meta.url);
25
+ const instance = new Worker(workerUrl, { execArgv: process.execArgv });
26
+ instance.on('message', handleMessage);
27
+ instance.on('error', (error) => {
28
+ rejectAll(error);
29
+ });
30
+ instance.on('exit', (code) => {
31
+ if (code !== 0) {
32
+ rejectAll(new Error(`DB worker exited with code ${String(code)}`));
33
+ }
34
+ if (worker === instance) {
35
+ worker = undefined;
36
+ }
37
+ });
38
+ return instance;
39
+ };
40
+ const getWorker = () => {
41
+ worker ??= createWorker();
42
+ return worker;
43
+ };
44
+ const sendRequest = (instance, type, payload) => new Promise((resolve, reject) => {
45
+ const id = nextId++;
46
+ pending.set(id, { resolve: resolve, reject });
47
+ const request = (payload === undefined ? { id, type } : { id, type, payload });
48
+ instance.postMessage(request);
49
+ });
50
+ export const callDbWorker = (type, ...args) => {
51
+ const payload = args[0];
52
+ return sendRequest(getWorker(), type, payload);
53
+ };
54
+ export const closeDbWorker = async () => {
55
+ if (!worker)
56
+ return;
57
+ const instance = worker;
58
+ try {
59
+ await sendRequest(instance, 'close');
60
+ }
61
+ finally {
62
+ worker = undefined;
63
+ rejectAll(new Error('DB worker closed'));
64
+ await instance.terminate();
65
+ }
66
+ };
67
+ //# sourceMappingURL=db-worker-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-worker-client.js","sourceRoot":"","sources":["../../src/core/db-worker-client.ts"],"names":[],"mappings":"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"}