@j0hanz/memdb 1.2.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +110 -287
  2. package/dist/assets/logo.svg +12 -0
  3. package/dist/config.d.ts +0 -1
  4. package/dist/config.js +0 -1
  5. package/dist/core/db.d.ts +6 -4
  6. package/dist/core/db.js +79 -39
  7. package/dist/core/memory-read.d.ts +1 -2
  8. package/dist/core/memory-read.js +28 -14
  9. package/dist/core/memory-write.d.ts +1 -2
  10. package/dist/core/memory-write.js +59 -82
  11. package/dist/core/relationships.d.ts +0 -1
  12. package/dist/core/relationships.js +4 -20
  13. package/dist/core/search.d.ts +0 -1
  14. package/dist/core/search.js +85 -101
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.js +36 -4
  17. package/dist/instructions.md +35 -27
  18. package/dist/logger.d.ts +0 -1
  19. package/dist/logger.js +0 -1
  20. package/dist/protocol-version-guard.d.ts +0 -1
  21. package/dist/protocol-version-guard.js +0 -1
  22. package/dist/schemas.d.ts +0 -1
  23. package/dist/schemas.js +12 -9
  24. package/dist/stdio-transport.d.ts +0 -1
  25. package/dist/stdio-transport.js +0 -1
  26. package/dist/tools.d.ts +1 -2
  27. package/dist/tools.js +194 -214
  28. package/dist/types.d.ts +0 -1
  29. package/dist/types.js +0 -1
  30. package/package.json +19 -17
  31. package/dist/config.d.ts.map +0 -1
  32. package/dist/config.js.map +0 -1
  33. package/dist/core/db.d.ts.map +0 -1
  34. package/dist/core/db.js.map +0 -1
  35. package/dist/core/memory-read.d.ts.map +0 -1
  36. package/dist/core/memory-read.js.map +0 -1
  37. package/dist/core/memory-write.d.ts.map +0 -1
  38. package/dist/core/memory-write.js.map +0 -1
  39. package/dist/core/relationships.d.ts.map +0 -1
  40. package/dist/core/relationships.js.map +0 -1
  41. package/dist/core/search.d.ts.map +0 -1
  42. package/dist/core/search.js.map +0 -1
  43. package/dist/index.d.ts.map +0 -1
  44. package/dist/index.js.map +0 -1
  45. package/dist/logger.d.ts.map +0 -1
  46. package/dist/logger.js.map +0 -1
  47. package/dist/protocol-version-guard.d.ts.map +0 -1
  48. package/dist/protocol-version-guard.js.map +0 -1
  49. package/dist/schemas.d.ts.map +0 -1
  50. package/dist/schemas.js.map +0 -1
  51. package/dist/stdio-transport.d.ts.map +0 -1
  52. package/dist/stdio-transport.js.map +0 -1
  53. package/dist/tools.d.ts.map +0 -1
  54. package/dist/tools.js.map +0 -1
  55. package/dist/types.d.ts.map +0 -1
  56. package/dist/types.js.map +0 -1
package/dist/core/db.js CHANGED
@@ -92,9 +92,6 @@ const enableDefensiveMode = (database) => {
92
92
  extended.enableDefensive(true);
93
93
  }
94
94
  };
95
- const isInTransaction = (database) => {
96
- return database.isTransaction;
97
- };
98
95
  const initializeSchema = (database) => {
99
96
  database.exec(SCHEMA_SQL);
100
97
  database.exec(FTS_SYNC_SQL);
@@ -129,44 +126,59 @@ export const getDb = () => {
129
126
  }
130
127
  return dbInstance;
131
128
  };
132
- export const closeDb = () => {
133
- if (dbInstance?.isOpen) {
134
- dbInstance.close();
135
- dbInstance = undefined;
136
- statementCache.clear();
129
+ const MAX_CACHED_STATEMENTS = 200;
130
+ class LruStatementCache {
131
+ maxSize;
132
+ cache = new Map();
133
+ constructor(maxSize) {
134
+ this.maxSize = maxSize;
135
+ }
136
+ get(sql) {
137
+ const hit = this.cache.get(sql);
138
+ if (!hit)
139
+ return undefined;
140
+ // Refresh LRU order
141
+ this.cache.delete(sql);
142
+ this.cache.set(sql, hit);
143
+ return hit;
144
+ }
145
+ set(sql, stmt) {
146
+ this.cache.set(sql, stmt);
147
+ if (this.cache.size <= this.maxSize)
148
+ return;
149
+ const oldestKey = this.cache.keys().next().value;
150
+ if (oldestKey)
151
+ this.cache.delete(oldestKey);
137
152
  }
153
+ clear() {
154
+ this.cache.clear();
155
+ }
156
+ }
157
+ const statementCache = new LruStatementCache(MAX_CACHED_STATEMENTS);
158
+ export const closeDb = () => {
159
+ if (!dbInstance?.isOpen)
160
+ return;
161
+ dbInstance.close();
162
+ dbInstance = undefined;
163
+ statementCache.clear();
138
164
  };
139
- const MAX_CACHED_STATEMENTS = 200;
140
- const statementCache = new Map();
141
165
  export const prepareCached = (sql) => {
142
166
  const cached = statementCache.get(sql);
143
- if (cached) {
144
- statementCache.delete(sql);
145
- statementCache.set(sql, cached);
167
+ if (cached)
146
168
  return cached;
147
- }
148
169
  const stmt = getDb().prepare(sql);
149
170
  statementCache.set(sql, stmt);
150
- if (statementCache.size > MAX_CACHED_STATEMENTS) {
151
- const oldestKey = statementCache.keys().next().value;
152
- if (oldestKey)
153
- statementCache.delete(oldestKey);
154
- }
155
171
  return stmt;
156
172
  };
157
- const isDbRow = (value) => {
158
- return typeof value === 'object' && value !== null;
159
- };
173
+ const isDbRow = (value) => typeof value === 'object' && value !== null;
160
174
  const assertDbRow = (value) => {
161
- if (!isDbRow(value)) {
175
+ if (!isDbRow(value))
162
176
  throw new Error('Invalid row');
163
- }
164
177
  return value;
165
178
  };
166
179
  const toDbRowArray = (value) => {
167
- if (!Array.isArray(value)) {
180
+ if (!Array.isArray(value))
168
181
  throw new Error('Expected rows array');
169
- }
170
182
  return value.map(assertDbRow);
171
183
  };
172
184
  const toDbRowOrUndefined = (value) => {
@@ -186,12 +198,11 @@ const toRunResult = (value) => {
186
198
  return { changes };
187
199
  };
188
200
  export const executeAll = (stmt, ...params) => toDbRowArray(stmt.all(...params));
189
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
190
201
  export const executeGet = (stmt, ...params) => toDbRowOrUndefined(stmt.get(...params));
191
202
  export const executeRun = (stmt, ...params) => toRunResult(stmt.run(...params));
192
203
  export const withImmediateTransaction = (operation) => {
193
204
  const db = getDb();
194
- if (isInTransaction(db)) {
205
+ if (db.isTransaction) {
195
206
  throw new Error('Cannot start nested transaction');
196
207
  }
197
208
  db.exec('BEGIN IMMEDIATE');
@@ -205,6 +216,24 @@ export const withImmediateTransaction = (operation) => {
205
216
  throw err;
206
217
  }
207
218
  };
219
+ const SAVEPOINT_NAME_PATTERN = /^[A-Za-z_]\w*$/;
220
+ export const withSavepoint = (name, operation) => {
221
+ if (!SAVEPOINT_NAME_PATTERN.test(name)) {
222
+ throw new Error(`Invalid savepoint name: ${name}`);
223
+ }
224
+ const db = getDb();
225
+ db.exec(`SAVEPOINT ${name}`);
226
+ try {
227
+ const result = operation();
228
+ db.exec(`RELEASE ${name}`);
229
+ return result;
230
+ }
231
+ catch (err) {
232
+ db.exec(`ROLLBACK TO ${name}`);
233
+ db.exec(`RELEASE ${name}`);
234
+ throw err;
235
+ }
236
+ };
208
237
  const createFieldError = (field) => new Error(`Invalid ${field}`);
209
238
  const assertFiniteNumber = (value, field) => {
210
239
  if (!Number.isFinite(value)) {
@@ -233,14 +262,6 @@ const toString = (value, field) => {
233
262
  return value;
234
263
  throw createFieldError(field);
235
264
  };
236
- const isMemoryType = (value) => MEMORY_TYPES.includes(value);
237
- const toMemoryType = (value, field) => {
238
- const str = toString(value, field);
239
- if (!isMemoryType(str)) {
240
- throw createFieldError(field);
241
- }
242
- return str;
243
- };
244
265
  const toOptionalString = (value, field) => {
245
266
  if (value === null || value === undefined)
246
267
  return undefined;
@@ -251,6 +272,13 @@ const toOptionalNumber = (value, field) => {
251
272
  return undefined;
252
273
  return toNumber(value, field);
253
274
  };
275
+ const isMemoryType = (value) => MEMORY_TYPES.includes(value);
276
+ const toMemoryType = (value, field) => {
277
+ const str = toString(value, field);
278
+ if (!isMemoryType(str))
279
+ throw createFieldError(field);
280
+ return str;
281
+ };
254
282
  export const mapRowToMemory = (row, tags = []) => ({
255
283
  id: toSafeInteger(row.id, 'id'),
256
284
  content: toString(row.content, 'content'),
@@ -273,6 +301,19 @@ export const mapRowToRelationship = (row) => ({
273
301
  relation_type: toString(row.relation_type, 'relation_type'),
274
302
  created_at: toString(row.created_at, 'created_at'),
275
303
  });
304
+ export const findMemoryIdByHash = (hash) => {
305
+ const stmt = prepareCached('SELECT id FROM memories WHERE hash = ?');
306
+ const row = executeGet(stmt, hash);
307
+ if (!row)
308
+ return undefined;
309
+ return toSafeInteger(row.id, 'id');
310
+ };
311
+ export const requireMemoryIdByHash = (hash, message = `Memory not found: ${hash}`) => {
312
+ const id = findMemoryIdByHash(hash);
313
+ if (id === undefined)
314
+ throw new Error(message);
315
+ return id;
316
+ };
276
317
  const dedupeIds = (ids) => {
277
318
  const seen = new Set();
278
319
  const unique = [];
@@ -296,8 +337,8 @@ export const loadTagsForMemoryIds = (memoryIds) => {
296
337
  const uniqueIds = dedupeIds(memoryIds);
297
338
  if (uniqueIds.length === 0)
298
339
  return new Map();
299
- const stmtSelectTags = prepareCached('SELECT memory_id, tag FROM tags WHERE memory_id IN (SELECT value FROM json_each(?)) ORDER BY memory_id, tag');
300
- const rows = executeAll(stmtSelectTags, JSON.stringify(uniqueIds));
340
+ const stmt = prepareCached('SELECT memory_id, tag FROM tags WHERE memory_id IN (SELECT value FROM json_each(?)) ORDER BY memory_id, tag');
341
+ const rows = executeAll(stmt, JSON.stringify(uniqueIds));
301
342
  const tagsById = new Map();
302
343
  for (const row of rows) {
303
344
  const memoryId = toSafeInteger(row.memory_id, 'memory_id');
@@ -306,4 +347,3 @@ export const loadTagsForMemoryIds = (memoryIds) => {
306
347
  }
307
348
  return tagsById;
308
349
  };
309
- //# sourceMappingURL=db.js.map
@@ -1,6 +1,5 @@
1
1
  import type { BatchDeleteResult, Memory, MemoryStats, StatementResult } from '../types.js';
2
2
  export declare const getMemory: (hash: string) => Memory | undefined;
3
3
  export declare const deleteMemory: (hash: string) => StatementResult;
4
- export declare const deleteMemories: (hashes: string[]) => BatchDeleteResult;
4
+ export declare const deleteMemories: (hashes: string[], signal?: AbortSignal) => BatchDeleteResult;
5
5
  export declare const getStats: () => MemoryStats;
6
- //# sourceMappingURL=memory-read.d.ts.map
@@ -1,10 +1,22 @@
1
1
  import { executeGet, executeRun, loadTagsForMemoryIds, mapRowToMemory, prepareCached, toSafeInteger, withImmediateTransaction, } from './db.js';
2
+ const throwIfAborted = (signal) => {
3
+ if (!signal)
4
+ return;
5
+ if (typeof signal.throwIfAborted === 'function') {
6
+ signal.throwIfAborted();
7
+ return;
8
+ }
9
+ if (signal.aborted) {
10
+ throw new Error('Operation aborted');
11
+ }
12
+ };
13
+ const loadMemoryRowByHashAndTouch = (hash) => {
14
+ const stmt = prepareCached('UPDATE memories SET accessed_at = CURRENT_TIMESTAMP WHERE hash = ? RETURNING *');
15
+ return executeGet(stmt, hash);
16
+ };
2
17
  export const getMemory = (hash) => {
3
18
  return withImmediateTransaction(() => {
4
- const stmtTouchMemoryByHash = prepareCached('UPDATE memories SET accessed_at = CURRENT_TIMESTAMP WHERE hash = ?');
5
- executeRun(stmtTouchMemoryByHash, hash);
6
- const stmtGetMemoryByHash = prepareCached('SELECT * FROM memories WHERE hash = ?');
7
- const row = executeGet(stmtGetMemoryByHash, hash);
19
+ const row = loadMemoryRowByHashAndTouch(hash);
8
20
  if (!row)
9
21
  return undefined;
10
22
  const id = toSafeInteger(row.id, 'id');
@@ -13,32 +25,35 @@ export const getMemory = (hash) => {
13
25
  });
14
26
  };
15
27
  export const deleteMemory = (hash) => {
16
- const stmtDeleteMemoryByHash = prepareCached('DELETE FROM memories WHERE hash = ?');
17
- const result = executeRun(stmtDeleteMemoryByHash, hash);
28
+ const stmt = prepareCached('DELETE FROM memories WHERE hash = ?');
29
+ const result = executeRun(stmt, hash);
18
30
  return { changes: toSafeInteger(result.changes, 'changes') };
19
31
  };
20
32
  const deleteMemoryForBatch = (hash) => {
21
33
  try {
22
34
  const result = deleteMemory(hash);
23
35
  const deleted = result.changes > 0;
24
- return deleted
25
- ? { item: { hash, deleted: true }, succeeded: true }
26
- : {
36
+ if (!deleted) {
37
+ return {
27
38
  item: { hash, deleted: false, error: 'Memory not found' },
28
39
  succeeded: false,
29
40
  };
41
+ }
42
+ return { item: { hash, deleted: true }, succeeded: true };
30
43
  }
31
44
  catch (err) {
32
45
  const message = err instanceof Error ? err.message : 'Unknown error';
33
46
  return { item: { hash, deleted: false, error: message }, succeeded: false };
34
47
  }
35
48
  };
36
- export const deleteMemories = (hashes) => {
37
- const results = [];
38
- let succeeded = 0;
39
- let failed = 0;
49
+ export const deleteMemories = (hashes, signal) => {
40
50
  return withImmediateTransaction(() => {
51
+ const results = [];
52
+ let succeeded = 0;
53
+ let failed = 0;
54
+ throwIfAborted(signal);
41
55
  for (const hash of hashes) {
56
+ throwIfAborted(signal);
42
57
  const outcome = deleteMemoryForBatch(hash);
43
58
  results.push(outcome.item);
44
59
  succeeded += outcome.succeeded ? 1 : 0;
@@ -78,4 +93,3 @@ export const getStats = () => {
78
93
  newestMemory: toDateString(dateRow?.newest),
79
94
  };
80
95
  };
81
- //# sourceMappingURL=memory-read.js.map
@@ -10,11 +10,10 @@ export declare const createMemories: (items: {
10
10
  tags?: readonly string[];
11
11
  importance?: number;
12
12
  memory_type?: MemoryType;
13
- }[]) => BatchStoreResult;
13
+ }[], signal?: AbortSignal) => BatchStoreResult;
14
14
  interface UpdateMemoryOptions {
15
15
  content: string;
16
16
  tags?: readonly string[] | undefined;
17
17
  }
18
18
  export declare const updateMemory: (hash: string, options: UpdateMemoryOptions) => MemoryUpdateResult;
19
19
  export {};
20
- //# sourceMappingURL=memory-write.d.ts.map
@@ -1,17 +1,25 @@
1
1
  import crypto from 'node:crypto';
2
- import { executeGet, executeRun, prepareCached, toSafeInteger, withImmediateTransaction, } from './db.js';
2
+ import { executeGet, executeRun, findMemoryIdByHash, prepareCached, toSafeInteger, withImmediateTransaction, withSavepoint, } from './db.js';
3
3
  const MAX_TAGS = 100;
4
4
  const TAG_PATTERN = /^\S+$/;
5
+ const throwIfAborted = (signal) => {
6
+ if (!signal)
7
+ return;
8
+ if (typeof signal.throwIfAborted === 'function') {
9
+ signal.throwIfAborted();
10
+ return;
11
+ }
12
+ if (signal.aborted) {
13
+ throw new Error('Operation aborted');
14
+ }
15
+ };
5
16
  const validateTag = (tag) => {
6
- if (tag.length === 0) {
17
+ if (tag.length === 0)
7
18
  throw new Error('Tag must be at least 1 character');
8
- }
9
- if (tag.length > 50) {
19
+ if (tag.length > 50)
10
20
  throw new Error('Tag exceeds 50 characters');
11
- }
12
- if (!TAG_PATTERN.test(tag)) {
21
+ if (!TAG_PATTERN.test(tag))
13
22
  throw new Error('Tag must not contain whitespace');
14
- }
15
23
  };
16
24
  const validateTagCount = (tags, maxTags) => {
17
25
  if (tags.length > maxTags) {
@@ -32,38 +40,30 @@ const normalizeTags = (tags, maxTags) => {
32
40
  validateTagCount(tags, maxTags);
33
41
  return dedupeTags(tags);
34
42
  };
35
- const findMemoryIdByHash = (hash) => {
36
- const stmtFindMemoryIdByHash = prepareCached('SELECT id FROM memories WHERE hash = ?');
37
- const row = executeGet(stmtFindMemoryIdByHash, hash);
38
- if (!row)
39
- return undefined;
40
- return toSafeInteger(row.id, 'id');
41
- };
42
43
  const insertTags = (memoryId, tags) => {
43
44
  if (tags.length === 0)
44
45
  return;
45
- const stmtInsertTags = prepareCached('INSERT OR IGNORE INTO tags (memory_id, tag) SELECT ?, value FROM json_each(?)');
46
- executeRun(stmtInsertTags, memoryId, JSON.stringify(tags));
46
+ const stmt = prepareCached('INSERT OR IGNORE INTO tags (memory_id, tag) SELECT ?, value FROM json_each(?)');
47
+ executeRun(stmt, memoryId, JSON.stringify(tags));
47
48
  };
48
- const buildHash = (content) => {
49
- return crypto.createHash('sha256').update(content).digest('hex');
50
- };
51
- const requireMemoryId = (id) => {
52
- if (id === undefined) {
53
- throw new Error('Failed to resolve memory id');
54
- }
55
- return id;
49
+ const replaceTags = (memoryId, tags) => {
50
+ const stmtDelete = prepareCached('DELETE FROM tags WHERE memory_id = ?');
51
+ executeRun(stmtDelete, memoryId);
52
+ insertTags(memoryId, normalizeTags(tags, MAX_TAGS));
56
53
  };
54
+ const buildHash = (content) => crypto.createHash('sha256').update(content).digest('hex');
57
55
  const resolveMemoryId = (content, hash, importance, memoryType) => {
58
- const stmtInsertMemory = prepareCached('INSERT OR IGNORE INTO memories (content, hash, importance, memory_type) VALUES (?, ?, ?, ?) RETURNING id');
59
- const inserted = executeGet(stmtInsertMemory, content, hash, importance, memoryType);
56
+ const stmtInsert = prepareCached('INSERT OR IGNORE INTO memories (content, hash, importance, memory_type) VALUES (?, ?, ?, ?) RETURNING id');
57
+ const inserted = executeGet(stmtInsert, content, hash, importance, memoryType);
60
58
  if (inserted) {
61
59
  return { id: toSafeInteger(inserted.id, 'id'), isNew: true };
62
60
  }
63
- const id = requireMemoryId(findMemoryIdByHash(hash));
64
- return { id, isNew: false };
61
+ const existingId = findMemoryIdByHash(hash);
62
+ if (existingId === undefined) {
63
+ throw new Error('Failed to resolve memory id');
64
+ }
65
+ return { id: existingId, isNew: false };
65
66
  };
66
- export const createMemory = (input) => withImmediateTransaction(() => createMemoryInTransaction(input));
67
67
  const createMemoryInTransaction = (input) => {
68
68
  const { content, tags = [], importance = 0, memory_type: memoryType = 'general', } = input;
69
69
  const hash = buildHash(content);
@@ -72,62 +72,44 @@ const createMemoryInTransaction = (input) => {
72
72
  insertTags(id, normalizedTags);
73
73
  return { id, hash, isNew };
74
74
  };
75
- export const createMemories = (items) => {
76
- return withImmediateTransaction(() => createMemoriesInTransaction(items));
77
- };
78
- const withSavepoint = (name, fn) => {
79
- executeRun(prepareCached(`SAVEPOINT ${name}`));
75
+ export const createMemory = (input) => withImmediateTransaction(() => createMemoryInTransaction(input));
76
+ const createMemoryWithSavepoint = (index, item, signal) => {
77
+ const savepointName = `mem_item_${index}`;
80
78
  try {
81
- const result = fn();
82
- executeRun(prepareCached(`RELEASE ${name}`));
83
- return result;
79
+ throwIfAborted(signal);
80
+ const created = withSavepoint(savepointName, () => createMemoryInTransaction(item));
81
+ return {
82
+ ok: true,
83
+ index,
84
+ hash: created.hash,
85
+ isNew: created.isNew,
86
+ };
84
87
  }
85
88
  catch (err) {
86
- executeRun(prepareCached(`ROLLBACK TO ${name}`));
87
- executeRun(prepareCached(`RELEASE ${name}`));
88
- throw err;
89
+ const message = err instanceof Error ? err.message : 'Unknown error';
90
+ return { ok: false, index, error: message };
89
91
  }
90
92
  };
91
- const createMemoriesInTransaction = (items) => {
93
+ const createMemoriesInTransaction = (items, signal) => {
92
94
  const results = [];
93
95
  let succeeded = 0;
94
96
  let failed = 0;
97
+ throwIfAborted(signal);
95
98
  for (let i = 0; i < items.length; i++) {
99
+ throwIfAborted(signal);
96
100
  const item = items[i];
97
101
  if (!item)
98
102
  continue;
99
- const result = createMemoryWithSavepoint(i, item);
103
+ const result = createMemoryWithSavepoint(i, item, signal);
100
104
  results.push(result);
101
- if (result.ok) {
105
+ if (result.ok)
102
106
  succeeded++;
103
- }
104
- else {
107
+ else
105
108
  failed++;
106
- }
107
109
  }
108
110
  return { results, succeeded, failed };
109
111
  };
110
- const createMemoryWithSavepoint = (index, item) => {
111
- const savepointName = `mem_item_${index}`;
112
- try {
113
- const created = withSavepoint(savepointName, () => createMemoryInTransaction(item));
114
- return {
115
- ok: true,
116
- index,
117
- hash: created.hash,
118
- isNew: created.isNew,
119
- };
120
- }
121
- catch (err) {
122
- const message = err instanceof Error ? err.message : 'Unknown error';
123
- return { ok: false, index, error: message };
124
- }
125
- };
126
- const replaceTags = (memoryId, tags) => {
127
- const stmtDeleteTagsForMemory = prepareCached('DELETE FROM tags WHERE memory_id = ?');
128
- executeRun(stmtDeleteTagsForMemory, memoryId);
129
- insertTags(memoryId, normalizeTags(tags, MAX_TAGS));
130
- };
112
+ export const createMemories = (items, signal) => withImmediateTransaction(() => createMemoriesInTransaction(items, signal));
131
113
  const assertNoDuplicateOnUpdate = (oldHash, newHash) => {
132
114
  if (newHash === oldHash)
133
115
  return;
@@ -136,22 +118,17 @@ const assertNoDuplicateOnUpdate = (oldHash, newHash) => {
136
118
  throw new Error('Content already exists as another memory');
137
119
  }
138
120
  };
139
- export const updateMemory = (hash, options) => {
121
+ const updateMemoryInTransaction = (hash, options) => {
140
122
  const memoryId = findMemoryIdByHash(hash);
141
123
  if (memoryId === undefined)
142
124
  throw new Error('Memory not found');
143
- return withImmediateTransaction(() => {
144
- const newHash = buildHash(options.content);
145
- // Check if new content would create a duplicate
146
- assertNoDuplicateOnUpdate(hash, newHash);
147
- // Update content and hash
148
- const stmtUpdateContent = prepareCached('UPDATE memories SET content = ?, hash = ? WHERE id = ?');
149
- executeRun(stmtUpdateContent, options.content, newHash, memoryId);
150
- // Update tags if provided, otherwise preserve existing tags
151
- if (options.tags !== undefined) {
152
- replaceTags(memoryId, options.tags);
153
- }
154
- return { updated: true, oldHash: hash, newHash };
155
- });
125
+ const newHash = buildHash(options.content);
126
+ assertNoDuplicateOnUpdate(hash, newHash);
127
+ const stmtUpdateContent = prepareCached('UPDATE memories SET content = ?, hash = ? WHERE id = ?');
128
+ executeRun(stmtUpdateContent, options.content, newHash, memoryId);
129
+ if (options.tags !== undefined) {
130
+ replaceTags(memoryId, options.tags);
131
+ }
132
+ return { updated: true, oldHash: hash, newHash };
156
133
  };
157
- //# sourceMappingURL=memory-write.js.map
134
+ export const updateMemory = (hash, options) => withImmediateTransaction(() => updateMemoryInTransaction(hash, options));
@@ -13,4 +13,3 @@ export declare const deleteRelationship: (input: {
13
13
  to_hash: string;
14
14
  relation_type: string;
15
15
  }) => StatementResult;
16
- //# sourceMappingURL=relationships.d.ts.map
@@ -1,21 +1,8 @@
1
- import { executeAll, executeGet, executeRun, mapRowToRelationship, prepareCached, toSafeInteger, withImmediateTransaction, } from './db.js';
2
- const findMemoryIdByHash = (hash) => {
3
- const stmtFindMemoryIdByHash = prepareCached('SELECT id FROM memories WHERE hash = ?');
4
- const row = executeGet(stmtFindMemoryIdByHash, hash);
5
- if (!row)
6
- return undefined;
7
- return toSafeInteger(row.id, 'id');
8
- };
9
- const requireMemoryId = (hash) => {
10
- const id = findMemoryIdByHash(hash);
11
- if (id === undefined) {
12
- throw new Error(`Memory not found: ${hash}`);
13
- }
14
- return id;
15
- };
1
+ import { executeAll, executeGet, executeRun, mapRowToRelationship, prepareCached, requireMemoryIdByHash, toSafeInteger, withImmediateTransaction, } from './db.js';
2
+ const buildNotFoundMessage = (hash) => `Memory not found: ${hash}`;
16
3
  export const createRelationship = (input) => withImmediateTransaction(() => {
17
- const fromId = requireMemoryId(input.from_hash);
18
- const toId = requireMemoryId(input.to_hash);
4
+ const fromId = requireMemoryIdByHash(input.from_hash, buildNotFoundMessage(input.from_hash));
5
+ const toId = requireMemoryIdByHash(input.to_hash, buildNotFoundMessage(input.to_hash));
19
6
  if (fromId === toId) {
20
7
  throw new Error('Cannot create self-referential relationship');
21
8
  }
@@ -28,7 +15,6 @@ export const createRelationship = (input) => withImmediateTransaction(() => {
28
15
  if (inserted) {
29
16
  return { id: toSafeInteger(inserted.id, 'id'), isNew: true };
30
17
  }
31
- // Relationship already exists, find its ID
32
18
  const stmtFindRelationshipId = prepareCached(`
33
19
  SELECT id FROM relationships
34
20
  WHERE from_memory_id = ? AND to_memory_id = ? AND relation_type = ?
@@ -39,7 +25,6 @@ export const createRelationship = (input) => withImmediateTransaction(() => {
39
25
  }
40
26
  return { id: toSafeInteger(existing.id, 'id'), isNew: false };
41
27
  });
42
- // Query that joins with memories to get hashes instead of IDs
43
28
  const buildGetRelationshipsQuery = (direction) => {
44
29
  const baseSelect = `
45
30
  SELECT r.id, r.relation_type, r.created_at,
@@ -75,4 +60,3 @@ export const deleteRelationship = (input) => {
75
60
  const result = executeRun(stmtDeleteRelationship, input.from_hash, input.to_hash, input.relation_type);
76
61
  return { changes: toSafeInteger(result.changes, 'changes') };
77
62
  };
78
- //# sourceMappingURL=relationships.js.map
@@ -8,4 +8,3 @@ export declare const recallMemories: (input: {
8
8
  depth?: number;
9
9
  }, signal?: AbortSignal) => RecallResult;
10
10
  export {};
11
- //# sourceMappingURL=search.d.ts.map