@j0hanz/memdb 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -24
- package/dist/core/database.d.ts.map +1 -1
- package/dist/core/database.js +3 -2
- package/dist/core/database.js.map +1 -1
- package/dist/core/db-helpers.d.ts +10 -0
- package/dist/core/db-helpers.d.ts.map +1 -0
- package/dist/core/db-helpers.js +17 -0
- package/dist/core/db-helpers.js.map +1 -0
- package/dist/core/memory-helpers.d.ts +3 -0
- package/dist/core/memory-helpers.d.ts.map +1 -0
- package/dist/core/memory-helpers.js +18 -0
- package/dist/core/memory-helpers.js.map +1 -0
- package/dist/core/memory-service.d.ts +5 -4
- package/dist/core/memory-service.d.ts.map +1 -1
- package/dist/core/memory-service.js +15 -108
- package/dist/core/memory-service.js.map +1 -1
- package/dist/core/memory-stats.d.ts +3 -0
- package/dist/core/memory-stats.d.ts.map +1 -0
- package/dist/core/memory-stats.js +44 -0
- package/dist/core/memory-stats.js.map +1 -0
- package/dist/core/memory-updates.d.ts +11 -0
- package/dist/core/memory-updates.d.ts.map +1 -0
- package/dist/core/memory-updates.js +102 -0
- package/dist/core/memory-updates.js.map +1 -0
- package/dist/core/relation-queries.d.ts.map +1 -1
- package/dist/core/relation-queries.js +26 -25
- package/dist/core/relation-queries.js.map +1 -1
- package/dist/core/search.d.ts +9 -0
- package/dist/core/search.d.ts.map +1 -0
- package/dist/core/search.js +57 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/tag-helpers.d.ts +2 -0
- package/dist/core/tag-helpers.d.ts.map +1 -0
- package/dist/core/tag-helpers.js +22 -0
- package/dist/core/tag-helpers.js.map +1 -0
- package/dist/index.js +52 -56
- package/dist/index.js.map +1 -1
- package/dist/schemas/inputs.d.ts +16 -0
- package/dist/schemas/inputs.d.ts.map +1 -1
- package/dist/schemas/inputs.js +40 -0
- package/dist/schemas/inputs.js.map +1 -1
- package/dist/schemas/outputs.d.ts.map +1 -1
- package/dist/schemas/outputs.js +25 -2
- package/dist/schemas/outputs.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +18 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +28 -6
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -12
- package/dist/utils/logger.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,9 +15,9 @@ A SQLite-backed MCP memory server (on-disk by default, in-memory optional).
|
|
|
15
15
|
| Feature | Description |
|
|
16
16
|
| :---------------- | :---------------------------------------------------------------- |
|
|
17
17
|
| Memory Storage | Store text memories with tags, importance, and type |
|
|
18
|
-
| Full-Text Search | FTS5-backed
|
|
18
|
+
| Full-Text Search | FTS5-backed tokenized search with relevance ranking |
|
|
19
19
|
| Graph Connections | Link memories and traverse relationships |
|
|
20
|
-
| Stats | Memory and relationship counts
|
|
20
|
+
| Stats | Memory, tag, and relationship counts + activity range |
|
|
21
21
|
| Local Privacy | All data stored locally in SQLite (`.memdb/memory.db` by default) |
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
@@ -67,12 +67,14 @@ The path is resolved to an absolute path unless you use `:memory:`.
|
|
|
67
67
|
|
|
68
68
|
- `MEMDB_PATH`: Override the database path (`:memory:` for in-memory).
|
|
69
69
|
- `MEMDB_LOG_LEVEL`: `info`, `warn`, or `error` (default: `info`).
|
|
70
|
+
- `MEMDB_SHUTDOWN_TIMEOUT`: Shutdown timeout in ms (1000-60000, default: `5000`).
|
|
70
71
|
|
|
71
72
|
### CLI Flags
|
|
72
73
|
|
|
73
74
|
- `--db <path>`: Override the database path.
|
|
74
75
|
- `--memory`: Use in-memory database (`:memory:`).
|
|
75
76
|
- `--log-level <level>`: `info`, `warn`, or `error`.
|
|
77
|
+
- `--shutdown-timeout <ms>`: Shutdown timeout in ms (1000-60000).
|
|
76
78
|
|
|
77
79
|
Precedence: CLI flags > environment variables > defaults.
|
|
78
80
|
|
|
@@ -128,6 +130,7 @@ Full-text search with filters.
|
|
|
128
130
|
| :------------- | :------- | :------- | :------ | :-------------------------------- |
|
|
129
131
|
| `query` | string | Yes | - | Search query (1-1000 chars) |
|
|
130
132
|
| `limit` | number | No | `10` | Maximum number of results (1-100) |
|
|
133
|
+
| `offset` | number | No | `0` | Pagination offset (0-1000) |
|
|
131
134
|
| `tags` | string[] | No | - | Filter by tags (max 50) |
|
|
132
135
|
| `minRelevance` | number | No | - | Minimum relevance score (0-1) |
|
|
133
136
|
|
|
@@ -169,21 +172,37 @@ Create a relationship between two memories.
|
|
|
169
172
|
|
|
170
173
|
Get memories related to a given memory.
|
|
171
174
|
|
|
172
|
-
| Parameter | Type | Required | Default
|
|
173
|
-
| :------------- | :----- | :------- |
|
|
174
|
-
| `hash` | string | Yes | -
|
|
175
|
-
| `relationType` | string | No | -
|
|
176
|
-
| `depth` | number | No | `1`
|
|
175
|
+
| Parameter | Type | Required | Default | Description |
|
|
176
|
+
| :------------- | :----- | :------- | :--------- | :----------------------------- |
|
|
177
|
+
| `hash` | string | Yes | - | Hash of the memory (32 chars) |
|
|
178
|
+
| `relationType` | string | No | - | Filter by relationship type |
|
|
179
|
+
| `depth` | number | No | `1` | Traversal depth (1-3) |
|
|
180
|
+
| `direction` | string | No | `outgoing` | `outgoing`, `incoming`, `both` |
|
|
177
181
|
|
|
178
182
|
**Returns:** Array of related memories (`Memory` + `relation_type`, `depth`).
|
|
179
183
|
|
|
180
184
|
### `memory_stats`
|
|
181
185
|
|
|
182
|
-
Get database statistics.
|
|
186
|
+
Get database statistics and memory type breakdown.
|
|
183
187
|
|
|
184
188
|
_No parameters required._
|
|
185
189
|
|
|
186
|
-
**Returns:** `{ memoryCount, relationshipCount }`.
|
|
190
|
+
**Returns:** `{ memoryCount, relationshipCount, tagCount, memoryTypes, oldestMemory, newestMemory }`.
|
|
191
|
+
|
|
192
|
+
### `update_memory`
|
|
193
|
+
|
|
194
|
+
Update memory metadata (content cannot be changed).
|
|
195
|
+
|
|
196
|
+
| Parameter | Type | Required | Default | Description |
|
|
197
|
+
| :----------- | :------- | :------- | :------ | :------------------------------------------ |
|
|
198
|
+
| `hash` | string | Yes | - | MD5 hash (32 chars) |
|
|
199
|
+
| `importance` | number | No | - | New importance score (0-10) |
|
|
200
|
+
| `memoryType` | string | No | - | New memory type (1-50 chars) |
|
|
201
|
+
| `tags` | string[] | No | - | Replace all tags (max 100, each 1-50 chars) |
|
|
202
|
+
| `addTags` | string[] | No | - | Tags to add (max 100, each 1-50 chars) |
|
|
203
|
+
| `removeTags` | string[] | No | - | Tags to remove (max 100, each 1-50 chars) |
|
|
204
|
+
|
|
205
|
+
**Returns:** `{ updated: true, hash }`.
|
|
187
206
|
|
|
188
207
|
### Memory Fields
|
|
189
208
|
|
|
@@ -249,26 +268,29 @@ Add to your `claude_desktop_config.json`:
|
|
|
249
268
|
|
|
250
269
|
## Limits & Constraints
|
|
251
270
|
|
|
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
|
|
259
|
-
| **Max
|
|
260
|
-
| **Max
|
|
261
|
-
| **Max
|
|
262
|
-
| **Max
|
|
263
|
-
| **
|
|
264
|
-
| **
|
|
265
|
-
| **
|
|
266
|
-
| **
|
|
271
|
+
| Constraint | Value | Description |
|
|
272
|
+
| :---------------------------- | :------------ | :-------------------------------------------------------- |
|
|
273
|
+
| **Max content length** | 100,000 chars | Maximum characters in memory content |
|
|
274
|
+
| **Max query length** | 1,000 chars | Maximum characters in search query |
|
|
275
|
+
| **Max search results** | 100 | Maximum results returned from `search_memories` |
|
|
276
|
+
| **Default search limit** | 10 | Default `limit` for `search_memories` |
|
|
277
|
+
| **Max search offset** | 1,000 | Maximum `offset` for `search_memories` |
|
|
278
|
+
| **Max tags per memory** | 100 | Maximum number of tags when storing a memory |
|
|
279
|
+
| **Max tag length** | 50 chars | Maximum characters per tag |
|
|
280
|
+
| **Max tags in search filter** | 50 | Maximum tags when filtering search results |
|
|
281
|
+
| **Max related memories** | 1,000 | Maximum results from `get_related` queries |
|
|
282
|
+
| **Max traversal depth** | 3 | Maximum depth for relationship traversal |
|
|
283
|
+
| **Importance range** | 0-10 | Allowed range for `importance` |
|
|
284
|
+
| **Min relevance range** | 0-1 | Allowed range for `minRelevance` |
|
|
285
|
+
| **Hash length** | 32 chars | MD5 hash length |
|
|
286
|
+
| **Search mode** | Tokenized OR | Each term is quoted and OR'ed; FTS5 operators are escaped |
|
|
267
287
|
|
|
268
288
|
### Notes
|
|
269
289
|
|
|
270
290
|
- **Content deduplication**: Memories are deduplicated using MD5 hashes.
|
|
271
291
|
- **Search errors**: If FTS5 is unavailable, `search_memories` returns an error indicating the index is missing. Invalid query syntax returns an error with details.
|
|
292
|
+
- **Tag behavior**: Tags are de-duplicated per memory; exceeding tag limits throws an error.
|
|
293
|
+
- **Bidirectional depth**: `get_related` with `direction: "both"` caps traversal depth at 2.
|
|
272
294
|
- **Local storage**: All data is stored locally in `.memdb/memory.db` unless `:memory:` is used.
|
|
273
295
|
|
|
274
296
|
## Development
|
|
@@ -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;AA0E3C,eAAO,MAAM,EAAE,cAAqD,CAAC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA0E3C,eAAO,MAAM,EAAE,cAAqD,CAAC;AAUrE,eAAO,MAAM,OAAO,QAAO,IAG1B,CAAC"}
|
package/dist/core/database.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { DatabaseSync } from 'node:sqlite';
|
|
4
4
|
import { config } from '../utils/config.js';
|
|
@@ -67,9 +67,10 @@ const FTS_SYNC_SQL = `
|
|
|
67
67
|
WHERE id NOT IN (SELECT rowid FROM memories_fts);
|
|
68
68
|
`;
|
|
69
69
|
if (config.dbPath !== ':memory:') {
|
|
70
|
-
|
|
70
|
+
await mkdir(path.dirname(config.dbPath), { recursive: true });
|
|
71
71
|
}
|
|
72
72
|
export const db = new DatabaseSync(config.dbPath, { timeout: 5000 });
|
|
73
|
+
db.enableDefensive?.(true);
|
|
73
74
|
db.exec(SCHEMA_SQL);
|
|
74
75
|
db.exec(FTS_SYNC_SQL);
|
|
75
76
|
export const closeDb = () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DlB,CAAC;AAEF,MAAM,YAAY,GAAG;;;;CAIpB,CAAC;AAEF,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;IACjC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AAEnE,EAGD,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC;AAE1B,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,10 @@
|
|
|
1
|
+
import type { StatementSync } from 'node:sqlite';
|
|
2
|
+
import type { DbRow } from './row-mappers.js';
|
|
3
|
+
export type SqlParam = string | number | bigint | null | Uint8Array;
|
|
4
|
+
export declare const executeAll: (stmt: StatementSync, ...params: SqlParam[]) => DbRow[];
|
|
5
|
+
export declare const executeGet: (stmt: StatementSync, ...params: SqlParam[]) => DbRow | undefined;
|
|
6
|
+
export declare const executeRun: (stmt: StatementSync, ...params: SqlParam[]) => {
|
|
7
|
+
changes: number | bigint;
|
|
8
|
+
};
|
|
9
|
+
export declare const withImmediateTransaction: <T>(operation: () => T) => T;
|
|
10
|
+
//# sourceMappingURL=db-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-helpers.d.ts","sourceRoot":"","sources":["../../src/core/db-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,UAAU,CAAC;AAEpE,eAAO,MAAM,UAAU,GACrB,MAAM,aAAa,EACnB,GAAG,QAAQ,QAAQ,EAAE,KACpB,KAAK,EAAoC,CAAC;AAE7C,eAAO,MAAM,UAAU,GACrB,MAAM,aAAa,EACnB,GAAG,QAAQ,QAAQ,EAAE,KACpB,KAAK,GAAG,SAAqD,CAAC;AAEjE,eAAO,MAAM,UAAU,GACrB,MAAM,aAAa,EACnB,GAAG,QAAQ,QAAQ,EAAE,KACpB;IAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;CAG1B,CAAC;AAEJ,eAAO,MAAM,wBAAwB,GAAI,CAAC,EAAE,WAAW,MAAM,CAAC,KAAG,CAUhE,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { db } from './database.js';
|
|
2
|
+
export const executeAll = (stmt, ...params) => stmt.all(...params);
|
|
3
|
+
export const executeGet = (stmt, ...params) => stmt.get(...params);
|
|
4
|
+
export const executeRun = (stmt, ...params) => stmt.run(...params);
|
|
5
|
+
export const withImmediateTransaction = (operation) => {
|
|
6
|
+
db.exec('BEGIN IMMEDIATE');
|
|
7
|
+
try {
|
|
8
|
+
const result = operation();
|
|
9
|
+
db.exec('COMMIT');
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
db.exec('ROLLBACK');
|
|
14
|
+
throw err;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=db-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-helpers.js","sourceRoot":"","sources":["../../src/core/db-helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAKnC,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,IAAmB,EACnB,GAAG,MAAkB,EACZ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAY,CAAC;AAE7C,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,IAAmB,EACnB,GAAG,MAAkB,EACF,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAsB,CAAC;AAEjE,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,IAAmB,EACnB,GAAG,MAAkB,EACS,EAAE,CAChC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAEjB,CAAC;AAEJ,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAI,SAAkB,EAAK,EAAE;IACnE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClB,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpB,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-helpers.d.ts","sourceRoot":"","sources":["../../src/core/memory-helpers.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,SAO1D,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,EAAE,MAAM,SAAS,MAAM,EAAE,KAAG,IAQtE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { db } from './database.js';
|
|
2
|
+
import { executeGet, executeRun } from './db-helpers.js';
|
|
3
|
+
import { toSafeInteger } from './row-mappers.js';
|
|
4
|
+
export const findMemoryIdByHash = (hash) => {
|
|
5
|
+
const row = executeGet(db.prepare('SELECT id FROM memories WHERE hash = ?'), hash);
|
|
6
|
+
if (!row)
|
|
7
|
+
return undefined;
|
|
8
|
+
return toSafeInteger(row.id, 'id');
|
|
9
|
+
};
|
|
10
|
+
export const insertTags = (memoryId, tags) => {
|
|
11
|
+
if (tags.length === 0)
|
|
12
|
+
return;
|
|
13
|
+
const placeholders = tags.map(() => '(?, ?)').join(', ');
|
|
14
|
+
const params = tags.flatMap((tag) => [memoryId, tag]);
|
|
15
|
+
const stmt = db.prepare(`INSERT OR IGNORE INTO tags (memory_id, tag) VALUES ${placeholders}`);
|
|
16
|
+
executeRun(stmt, ...params);
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=memory-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-helpers.js","sourceRoot":"","sources":["../../src/core/memory-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAsB,EAAE;IACrE,MAAM,GAAG,GAAG,UAAU,CACpB,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,EACpD,IAAI,CACL,CAAC;IACF,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,OAAO,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAgB,EAAE,IAAuB,EAAQ,EAAE;IAC5E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,MAAM,GAAe,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CACrB,sDAAsD,YAAY,EAAE,CACrE,CAAC;IACF,UAAU,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC;AAC9B,CAAC,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { Memory, MemoryInsertResult,
|
|
1
|
+
import type { Memory, MemoryInsertResult, RelatedMemory, SearchResult, StatementResult } from '../types/index.js';
|
|
2
2
|
export declare const createMemory: (content: string, tags?: readonly string[], importance?: number, memoryType?: string) => MemoryInsertResult;
|
|
3
|
-
export declare const searchMemories: (query: string, limit?: number, tags?: readonly string[], minRelevance?: number) => SearchResult[];
|
|
3
|
+
export declare const searchMemories: (query: string, limit?: number, tags?: readonly string[], minRelevance?: number, offset?: number) => SearchResult[];
|
|
4
4
|
export declare const getMemory: (hash: string) => Memory | undefined;
|
|
5
5
|
export declare const deleteMemory: (hash: string) => StatementResult;
|
|
6
6
|
export declare const linkMemories: (fromHash: string, toHash: string, relationType: string) => StatementResult;
|
|
7
|
-
|
|
7
|
+
type RelationDirection = 'outgoing' | 'incoming' | 'both';
|
|
8
8
|
export declare const getRelated: (hash: string, relationType?: string, depth?: number, direction?: RelationDirection) => RelatedMemory[];
|
|
9
|
-
export
|
|
9
|
+
export { getStats } from './memory-stats.js';
|
|
10
|
+
export { updateMemory } from './memory-updates.js';
|
|
10
11
|
//# sourceMappingURL=memory-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-service.d.ts","sourceRoot":"","sources":["../../src/core/memory-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory-service.d.ts","sourceRoot":"","sources":["../../src/core/memory-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,eAAe,EAChB,MAAM,mBAAmB,CAAC;AA2B3B,eAAO,MAAM,YAAY,GACvB,SAAS,MAAM,EACf,OAAM,SAAS,MAAM,EAAO,EAC5B,mBAAc,EACd,mBAAsB,KACrB,kBAeC,CAAC;AAEL,eAAO,MAAM,cAAc,GACzB,OAAO,MAAM,EACb,cAAU,EACV,OAAM,SAAS,MAAM,EAAO,EAC5B,eAAe,MAAM,EACrB,SAAS,MAAM,KACd,YAAY,EAUd,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,SAMjD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,eAM3C,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,EAChB,QAAQ,MAAM,EACd,cAAc,MAAM,KACnB,eAcF,CAAC;AAEF,KAAK,iBAAiB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAY1D,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,EACZ,eAAe,MAAM,EACrB,cAAS,EACT,YAAW,iBAA8B,KACxC,aAAa,EASf,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1,100 +1,12 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import { db } from './database.js';
|
|
3
|
+
import { executeGet, executeRun, withImmediateTransaction, } from './db-helpers.js';
|
|
4
|
+
import { findMemoryIdByHash, insertTags } from './memory-helpers.js';
|
|
3
5
|
import { deduplicateByHash, queryBothDirect, queryIncomingDirect, queryIncomingRecursive, queryOutgoingDirect, queryOutgoingRecursive, } from './relation-queries.js';
|
|
4
6
|
import { mapRowToMemory, mapRowToSearchResult, toSafeInteger, } from './row-mappers.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const executeGet = (stmt, ...params) => stmt.get(...params);
|
|
8
|
-
const executeRun = (stmt, ...params) => stmt.run(...params);
|
|
9
|
-
const buildSearchQuery = (query, limit, tags, minRelevance) => {
|
|
10
|
-
const sanitizedQuery = `"${query.replace(/"/g, '""')}"`;
|
|
11
|
-
const relevanceExpr = '1.0 / (1.0 + abs(bm25(memories_fts)))';
|
|
12
|
-
const whereParts = ['memories_fts MATCH ?'];
|
|
13
|
-
const params = [sanitizedQuery];
|
|
14
|
-
if (tags.length > 0) {
|
|
15
|
-
whereParts.push(`m.id IN (SELECT memory_id FROM tags WHERE tag IN (${tags
|
|
16
|
-
.map(() => '?')
|
|
17
|
-
.join(', ')}))`);
|
|
18
|
-
params.push(...tags);
|
|
19
|
-
}
|
|
20
|
-
let sql = `
|
|
21
|
-
WITH ranked AS (
|
|
22
|
-
SELECT m.*, ${relevanceExpr} as relevance
|
|
23
|
-
FROM memories m
|
|
24
|
-
JOIN memories_fts fts ON m.id = fts.rowid
|
|
25
|
-
WHERE ${whereParts.join(' AND ')}
|
|
26
|
-
)
|
|
27
|
-
SELECT * FROM ranked
|
|
28
|
-
`;
|
|
29
|
-
if (minRelevance !== undefined) {
|
|
30
|
-
sql += ' WHERE relevance >= ?';
|
|
31
|
-
params.push(minRelevance);
|
|
32
|
-
}
|
|
33
|
-
sql += ' ORDER BY relevance DESC LIMIT ?';
|
|
34
|
-
params.push(limit);
|
|
35
|
-
return { sql, params };
|
|
36
|
-
};
|
|
37
|
-
const executeSearch = (sql, params) => {
|
|
38
|
-
try {
|
|
39
|
-
const stmt = db.prepare(sql);
|
|
40
|
-
return executeAll(stmt, ...params);
|
|
41
|
-
}
|
|
42
|
-
catch (err) {
|
|
43
|
-
const mappedError = toSearchError(err);
|
|
44
|
-
if (mappedError) {
|
|
45
|
-
throw mappedError;
|
|
46
|
-
}
|
|
47
|
-
throw err;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
7
|
+
import { buildSearchQuery, executeSearch } from './search.js';
|
|
8
|
+
import { normalizeTags } from './tag-helpers.js';
|
|
50
9
|
const buildHash = (content) => crypto.createHash('md5').update(content).digest('hex');
|
|
51
|
-
const assertValidTag = (tag) => {
|
|
52
|
-
if (tag.length === 0) {
|
|
53
|
-
throw new Error('Tag must be at least 1 character');
|
|
54
|
-
}
|
|
55
|
-
if (tag.length > 50) {
|
|
56
|
-
throw new Error('Tag exceeds 50 characters');
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
const normalizeTags = (tags, maxTags) => {
|
|
60
|
-
if (tags.length === 0)
|
|
61
|
-
return [];
|
|
62
|
-
if (tags.length > maxTags) {
|
|
63
|
-
throw new Error('Too many tags (max ' + String(maxTags) + ')');
|
|
64
|
-
}
|
|
65
|
-
const seen = new Set();
|
|
66
|
-
for (const tag of tags) {
|
|
67
|
-
assertValidTag(tag);
|
|
68
|
-
seen.add(tag);
|
|
69
|
-
}
|
|
70
|
-
return [...seen];
|
|
71
|
-
};
|
|
72
|
-
const withImmediateTransaction = (operation) => {
|
|
73
|
-
db.exec('BEGIN IMMEDIATE');
|
|
74
|
-
try {
|
|
75
|
-
const result = operation();
|
|
76
|
-
db.exec('COMMIT');
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
db.exec('ROLLBACK');
|
|
81
|
-
throw err;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
const findMemoryIdByHash = (hash) => {
|
|
85
|
-
const row = executeGet(db.prepare('SELECT id FROM memories WHERE hash = ?'), hash);
|
|
86
|
-
if (!row)
|
|
87
|
-
return undefined;
|
|
88
|
-
return toSafeInteger(row.id, 'id');
|
|
89
|
-
};
|
|
90
|
-
const insertTags = (memoryId, tags) => {
|
|
91
|
-
if (tags.length === 0)
|
|
92
|
-
return;
|
|
93
|
-
const insertTag = db.prepare('INSERT OR IGNORE INTO tags (memory_id, tag) VALUES (?, ?)');
|
|
94
|
-
for (const tag of tags) {
|
|
95
|
-
executeRun(insertTag, memoryId, tag);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
10
|
export const createMemory = (content, tags = [], importance = 0, memoryType = 'general') => withImmediateTransaction(() => {
|
|
99
11
|
const hash = buildHash(content);
|
|
100
12
|
const normalizedTags = normalizeTags(tags, 100);
|
|
@@ -108,8 +20,8 @@ export const createMemory = (content, tags = [], importance = 0, memoryType = 'g
|
|
|
108
20
|
insertTags(id, normalizedTags);
|
|
109
21
|
return { id, hash, isNew: toSafeInteger(result.changes, 'changes') === 1 };
|
|
110
22
|
});
|
|
111
|
-
export const searchMemories = (query, limit = 10, tags = [], minRelevance) => {
|
|
112
|
-
const { sql, params } = buildSearchQuery(query, limit, normalizeTags(tags, 50), minRelevance);
|
|
23
|
+
export const searchMemories = (query, limit = 10, tags = [], minRelevance, offset) => {
|
|
24
|
+
const { sql, params } = buildSearchQuery(query, limit, normalizeTags(tags, 50), minRelevance, offset);
|
|
113
25
|
const rows = executeSearch(sql, params);
|
|
114
26
|
return rows.map((row) => mapRowToSearchResult(row));
|
|
115
27
|
};
|
|
@@ -132,28 +44,24 @@ export const linkMemories = (fromHash, toHash, relationType) => {
|
|
|
132
44
|
const result = executeRun(insert, fromId, toId, relationType);
|
|
133
45
|
return { changes: toSafeInteger(result.changes, 'changes') };
|
|
134
46
|
};
|
|
135
|
-
const
|
|
47
|
+
const resolveMaxDepth = (depth, direction) => {
|
|
48
|
+
if (direction === 'both') {
|
|
49
|
+
return Math.min(depth, 2);
|
|
50
|
+
}
|
|
51
|
+
return Math.max(1, depth);
|
|
52
|
+
};
|
|
136
53
|
export const getRelated = (hash, relationType, depth = 1, direction = 'outgoing') => {
|
|
137
54
|
const memoryId = findMemoryIdByHash(hash);
|
|
138
55
|
if (memoryId === undefined)
|
|
139
56
|
return [];
|
|
140
|
-
const maxDepth =
|
|
57
|
+
const maxDepth = resolveMaxDepth(depth, direction);
|
|
141
58
|
if (maxDepth === 1) {
|
|
142
59
|
return getRelatedDirect(memoryId, relationType, direction);
|
|
143
60
|
}
|
|
144
61
|
return getRelatedRecursive(memoryId, relationType, maxDepth, direction);
|
|
145
62
|
};
|
|
146
|
-
export
|
|
147
|
-
|
|
148
|
-
const relationshipRow = executeGet(db.prepare('SELECT COUNT(*) as count FROM relationships'));
|
|
149
|
-
if (!memoryRow || !relationshipRow) {
|
|
150
|
-
throw new Error('Failed to load database stats');
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
memoryCount: toSafeInteger(memoryRow.count, 'memoryCount'),
|
|
154
|
-
relationshipCount: toSafeInteger(relationshipRow.count, 'relationshipCount'),
|
|
155
|
-
};
|
|
156
|
-
};
|
|
63
|
+
export { getStats } from './memory-stats.js';
|
|
64
|
+
export { updateMemory } from './memory-updates.js';
|
|
157
65
|
const getRelatedDirect = (memoryId, relationType, direction = 'outgoing') => {
|
|
158
66
|
if (direction === 'outgoing')
|
|
159
67
|
return queryOutgoingDirect(memoryId, relationType);
|
|
@@ -168,7 +76,6 @@ const getRelatedRecursive = (memoryId, relationType, maxDepth, direction = 'outg
|
|
|
168
76
|
if (direction === 'incoming') {
|
|
169
77
|
return queryIncomingRecursive(memoryId, relationType, maxDepth);
|
|
170
78
|
}
|
|
171
|
-
// direction === 'both'
|
|
172
79
|
const outgoing = queryOutgoingRecursive(memoryId, relationType, maxDepth);
|
|
173
80
|
const incoming = queryIncomingRecursive(memoryId, relationType, maxDepth);
|
|
174
81
|
return deduplicateByHash([...outgoing, ...incoming]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-service.js","sourceRoot":"","sources":["../../src/core/memory-service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-service.js","sourceRoot":"","sources":["../../src/core/memory-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-stats.d.ts","sourceRoot":"","sources":["../../src/core/memory-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA0CrD,eAAO,MAAM,QAAQ,QAAO,WAwB3B,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { db } from './database.js';
|
|
2
|
+
import { executeAll, executeGet } from './db-helpers.js';
|
|
3
|
+
import { toSafeInteger } from './row-mappers.js';
|
|
4
|
+
const buildMemoryTypes = (typeRows) => {
|
|
5
|
+
const memoryTypes = {};
|
|
6
|
+
for (const row of typeRows) {
|
|
7
|
+
const rawType = row.memory_type;
|
|
8
|
+
const typeKey = typeof rawType === 'string' ? rawType : 'unknown';
|
|
9
|
+
memoryTypes[typeKey] = toSafeInteger(row.count, 'typeCount');
|
|
10
|
+
}
|
|
11
|
+
return memoryTypes;
|
|
12
|
+
};
|
|
13
|
+
const toDateString = (value) => {
|
|
14
|
+
if (value == null)
|
|
15
|
+
return null;
|
|
16
|
+
if (typeof value === 'string')
|
|
17
|
+
return value;
|
|
18
|
+
if (typeof value === 'number')
|
|
19
|
+
return String(value);
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
const queryCounts = () => {
|
|
23
|
+
const memoryRow = executeGet(db.prepare('SELECT COUNT(*) as count FROM memories'));
|
|
24
|
+
const relationshipRow = executeGet(db.prepare('SELECT COUNT(*) as count FROM relationships'));
|
|
25
|
+
const tagRow = executeGet(db.prepare('SELECT COUNT(DISTINCT tag) as count FROM tags'));
|
|
26
|
+
if (!memoryRow || !relationshipRow || !tagRow) {
|
|
27
|
+
throw new Error('Failed to load database stats');
|
|
28
|
+
}
|
|
29
|
+
return { memoryRow, relationshipRow, tagRow };
|
|
30
|
+
};
|
|
31
|
+
export const getStats = () => {
|
|
32
|
+
const { memoryRow, relationshipRow, tagRow } = queryCounts();
|
|
33
|
+
const typeRows = executeAll(db.prepare('SELECT memory_type, COUNT(*) as count FROM memories GROUP BY memory_type'));
|
|
34
|
+
const dateRow = executeGet(db.prepare('SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories'));
|
|
35
|
+
return {
|
|
36
|
+
memoryCount: toSafeInteger(memoryRow.count, 'memoryCount'),
|
|
37
|
+
relationshipCount: toSafeInteger(relationshipRow.count, 'relationshipCount'),
|
|
38
|
+
tagCount: toSafeInteger(tagRow.count, 'tagCount'),
|
|
39
|
+
memoryTypes: buildMemoryTypes(typeRows),
|
|
40
|
+
oldestMemory: toDateString(dateRow?.oldest),
|
|
41
|
+
newestMemory: toDateString(dateRow?.newest),
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=memory-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-stats.js","sourceRoot":"","sources":["../../src/core/memory-stats.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAc,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE7D,MAAM,gBAAgB,GAAG,CAAC,QAAiB,EAA0B,EAAE;IACrE,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC;QAChC,MAAM,OAAO,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,WAAW,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,KAAc,EAAiB,EAAE;IACrD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACpD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,GAIlB,EAAE;IACF,MAAM,SAAS,GAAG,UAAU,CAC1B,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CACrD,CAAC;IACF,MAAM,eAAe,GAAG,UAAU,CAChC,EAAE,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAC1D,CAAC;IACF,MAAM,MAAM,GAAG,UAAU,CACvB,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAC5D,CAAC;IACF,IAAI,CAAC,SAAS,IAAI,CAAC,eAAe,IAAI,CAAC,MAAM,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAgB,EAAE;IACxC,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IAC7D,MAAM,QAAQ,GAAG,UAAU,CACzB,EAAE,CAAC,OAAO,CACR,0EAA0E,CAC3E,CACF,CAAC;IACF,MAAM,OAAO,GAAG,UAAU,CACxB,EAAE,CAAC,OAAO,CACR,2EAA2E,CAC5E,CACF,CAAC;IAEF,OAAO;QACL,WAAW,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC;QAC1D,iBAAiB,EAAE,aAAa,CAC9B,eAAe,CAAC,KAAK,EACrB,mBAAmB,CACpB;QACD,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC;QACjD,WAAW,EAAE,gBAAgB,CAAC,QAAQ,CAAC;QACvC,YAAY,EAAE,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC;QAC3C,YAAY,EAAE,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC;KAC5C,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MemoryUpdateResult } from '../types/index.js';
|
|
2
|
+
interface UpdateMemoryOptions {
|
|
3
|
+
importance?: number | undefined;
|
|
4
|
+
memoryType?: string | undefined;
|
|
5
|
+
tags?: readonly string[] | undefined;
|
|
6
|
+
addTags?: readonly string[] | undefined;
|
|
7
|
+
removeTags?: readonly string[] | undefined;
|
|
8
|
+
}
|
|
9
|
+
export declare const updateMemory: (hash: string, options: UpdateMemoryOptions) => MemoryUpdateResult;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=memory-updates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-updates.d.ts","sourceRoot":"","sources":["../../src/core/memory-updates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA4B5D,UAAU,mBAAmB;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACxC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC5C;AAyGD,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,SAAS,mBAAmB,KAC3B,kBASF,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { db } from './database.js';
|
|
2
|
+
import { executeAll, executeRun, withImmediateTransaction, } from './db-helpers.js';
|
|
3
|
+
import { findMemoryIdByHash, insertTags } from './memory-helpers.js';
|
|
4
|
+
import { normalizeTags } from './tag-helpers.js';
|
|
5
|
+
const MAX_TAGS = 100;
|
|
6
|
+
const loadTagsForMemory = (memoryId) => {
|
|
7
|
+
const rows = executeAll(db.prepare('SELECT tag FROM tags WHERE memory_id = ?'), memoryId);
|
|
8
|
+
const tags = new Set();
|
|
9
|
+
for (const row of rows) {
|
|
10
|
+
if (typeof row.tag !== 'string') {
|
|
11
|
+
throw new Error('Invalid tag');
|
|
12
|
+
}
|
|
13
|
+
tags.add(row.tag);
|
|
14
|
+
}
|
|
15
|
+
return tags;
|
|
16
|
+
};
|
|
17
|
+
const updateMetadataFields = (memoryId, options) => {
|
|
18
|
+
if (options.importance === undefined && options.memoryType === undefined) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const updates = [];
|
|
22
|
+
const params = [];
|
|
23
|
+
if (options.importance !== undefined) {
|
|
24
|
+
updates.push('importance = ?');
|
|
25
|
+
params.push(options.importance);
|
|
26
|
+
}
|
|
27
|
+
if (options.memoryType !== undefined) {
|
|
28
|
+
updates.push('memory_type = ?');
|
|
29
|
+
params.push(options.memoryType);
|
|
30
|
+
}
|
|
31
|
+
params.push(memoryId);
|
|
32
|
+
executeRun(db.prepare(`UPDATE memories SET ${updates.join(', ')} WHERE id = ?`), ...params);
|
|
33
|
+
};
|
|
34
|
+
const replaceTags = (memoryId, tags) => {
|
|
35
|
+
executeRun(db.prepare('DELETE FROM tags WHERE memory_id = ?'), memoryId);
|
|
36
|
+
insertTags(memoryId, normalizeTags(tags, MAX_TAGS));
|
|
37
|
+
};
|
|
38
|
+
const filterTagsToInsert = (tags, removeTags) => {
|
|
39
|
+
if (removeTags.length === 0)
|
|
40
|
+
return [...tags];
|
|
41
|
+
const removeSet = new Set(removeTags);
|
|
42
|
+
return tags.filter((tag) => !removeSet.has(tag));
|
|
43
|
+
};
|
|
44
|
+
const removeTagsFromSet = (tags, tagSet) => {
|
|
45
|
+
for (const tag of tags) {
|
|
46
|
+
tagSet.delete(tag);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const enforceTagLimit = (existingTags, tagsToInsert, maxTags) => {
|
|
50
|
+
let projectedCount = existingTags.size;
|
|
51
|
+
for (const tag of tagsToInsert) {
|
|
52
|
+
if (existingTags.has(tag))
|
|
53
|
+
continue;
|
|
54
|
+
projectedCount += 1;
|
|
55
|
+
if (projectedCount > maxTags) {
|
|
56
|
+
throw new Error('Too many tags (max ' + String(maxTags) + ')');
|
|
57
|
+
}
|
|
58
|
+
existingTags.add(tag);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const addTagsToMemory = (memoryId, tags, removeTags = []) => {
|
|
62
|
+
if (tags.length === 0)
|
|
63
|
+
return;
|
|
64
|
+
const normalizedTags = normalizeTags(tags, MAX_TAGS);
|
|
65
|
+
const tagsToInsert = filterTagsToInsert(normalizedTags, removeTags);
|
|
66
|
+
if (tagsToInsert.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
const existingTags = loadTagsForMemory(memoryId);
|
|
69
|
+
removeTagsFromSet(removeTags, existingTags);
|
|
70
|
+
enforceTagLimit(existingTags, tagsToInsert, MAX_TAGS);
|
|
71
|
+
insertTags(memoryId, tagsToInsert);
|
|
72
|
+
};
|
|
73
|
+
const removeTagsFromMemory = (memoryId, tags) => {
|
|
74
|
+
if (tags.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
const placeholders = tags.map(() => '?').join(', ');
|
|
77
|
+
const stmt = db.prepare(`DELETE FROM tags WHERE memory_id = ? AND tag IN (${placeholders})`);
|
|
78
|
+
executeRun(stmt, memoryId, ...tags);
|
|
79
|
+
};
|
|
80
|
+
const updateTags = (memoryId, options) => {
|
|
81
|
+
if (options.tags !== undefined) {
|
|
82
|
+
replaceTags(memoryId, options.tags);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (options.addTags !== undefined) {
|
|
86
|
+
addTagsToMemory(memoryId, options.addTags, options.removeTags ?? []);
|
|
87
|
+
}
|
|
88
|
+
if (options.removeTags !== undefined) {
|
|
89
|
+
removeTagsFromMemory(memoryId, options.removeTags);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export const updateMemory = (hash, options) => {
|
|
93
|
+
const memoryId = findMemoryIdByHash(hash);
|
|
94
|
+
if (memoryId === undefined)
|
|
95
|
+
throw new Error('Memory not found');
|
|
96
|
+
return withImmediateTransaction(() => {
|
|
97
|
+
updateMetadataFields(memoryId, options);
|
|
98
|
+
updateTags(memoryId, options);
|
|
99
|
+
return { updated: true, hash };
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=memory-updates.js.map
|