@j0hanz/memdb 1.0.11 → 1.1.1

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 (87) hide show
  1. package/README.md +82 -120
  2. package/dist/config.d.ts +4 -0
  3. package/dist/config.js +13 -0
  4. package/dist/core/database-schema.d.ts +0 -1
  5. package/dist/core/database-schema.js +0 -1
  6. package/dist/core/database.d.ts +0 -1
  7. package/dist/core/database.js +0 -1
  8. package/dist/core/db.d.ts +16 -0
  9. package/dist/core/db.js +232 -0
  10. package/dist/core/memory-create.d.ts +0 -1
  11. package/dist/core/memory-create.js +13 -7
  12. package/dist/core/memory-db.d.ts +0 -1
  13. package/dist/core/memory-db.js +0 -1
  14. package/dist/core/memory-read.d.ts +2 -2
  15. package/dist/core/memory-read.js +32 -4
  16. package/dist/core/memory-relations.d.ts +0 -1
  17. package/dist/core/memory-relations.js +0 -1
  18. package/dist/core/memory-search.d.ts +8 -7
  19. package/dist/core/memory-search.js +15 -9
  20. package/dist/core/memory-stats.d.ts +0 -1
  21. package/dist/core/memory-stats.js +0 -1
  22. package/dist/core/memory-updates.d.ts +0 -1
  23. package/dist/core/memory-updates.js +1 -2
  24. package/dist/core/memory-write.d.ts +13 -0
  25. package/dist/core/memory-write.js +113 -0
  26. package/dist/core/relation-queries.d.ts +0 -1
  27. package/dist/core/relation-queries.js +0 -1
  28. package/dist/core/relations.d.ts +10 -0
  29. package/dist/core/relations.js +177 -0
  30. package/dist/core/row-mappers.d.ts +0 -1
  31. package/dist/core/row-mappers.js +0 -1
  32. package/dist/core/search-errors.d.ts +0 -1
  33. package/dist/core/search-errors.js +0 -1
  34. package/dist/core/search.d.ts +5 -12
  35. package/dist/core/search.js +77 -56
  36. package/dist/core/sqlite.d.ts +0 -1
  37. package/dist/core/sqlite.js +0 -3
  38. package/dist/core/tags.d.ts +0 -1
  39. package/dist/core/tags.js +1 -2
  40. package/dist/index.d.ts +0 -1
  41. package/dist/index.js +58 -37
  42. package/dist/lib/errors.d.ts +0 -1
  43. package/dist/lib/errors.js +0 -1
  44. package/dist/logger.d.ts +5 -0
  45. package/dist/logger.js +17 -0
  46. package/dist/protocol-version-guard.d.ts +17 -0
  47. package/dist/protocol-version-guard.js +100 -0
  48. package/dist/schemas/inputs.d.ts +0 -1
  49. package/dist/schemas/inputs.js +0 -1
  50. package/dist/schemas/outputs.d.ts +0 -1
  51. package/dist/schemas/outputs.js +0 -1
  52. package/dist/schemas.d.ts +28 -0
  53. package/dist/schemas.js +65 -0
  54. package/dist/tools/definitions/memory-core.d.ts +0 -1
  55. package/dist/tools/definitions/memory-core.js +0 -1
  56. package/dist/tools/definitions/memory-relations.d.ts +0 -1
  57. package/dist/tools/definitions/memory-relations.js +0 -1
  58. package/dist/tools/definitions/memory-search.d.ts +0 -1
  59. package/dist/tools/definitions/memory-search.js +1 -11
  60. package/dist/tools/definitions/memory-stats.d.ts +0 -1
  61. package/dist/tools/definitions/memory-stats.js +0 -1
  62. package/dist/tools/index.d.ts +0 -1
  63. package/dist/tools/index.js +0 -1
  64. package/dist/tools/tool-handlers.d.ts +0 -1
  65. package/dist/tools/tool-handlers.js +0 -1
  66. package/dist/tools/tool-types.d.ts +0 -1
  67. package/dist/tools/tool-types.js +0 -1
  68. package/dist/tools.d.ts +18 -0
  69. package/dist/tools.js +167 -0
  70. package/dist/tsconfig.tsbuildinfo +1 -0
  71. package/dist/types/index.d.ts +0 -1
  72. package/dist/types/index.js +0 -1
  73. package/dist/types.d.ts +30 -0
  74. package/dist/types.js +1 -0
  75. package/dist/utils/config.d.ts +0 -1
  76. package/dist/utils/config.js +0 -1
  77. package/dist/utils/logger.d.ts +0 -1
  78. package/dist/utils/logger.js +0 -1
  79. package/dist/utils.d.ts +11 -0
  80. package/dist/utils.js +118 -0
  81. package/dist/worker/db-worker-client.d.ts +9 -0
  82. package/dist/worker/db-worker-client.js +93 -0
  83. package/dist/worker/db-worker.d.ts +1 -0
  84. package/dist/worker/db-worker.js +174 -0
  85. package/dist/worker/protocol.d.ts +9 -0
  86. package/dist/worker/protocol.js +14 -0
  87. package/package.json +8 -5
@@ -1,4 +1,4 @@
1
- import type { Memory, StatementResult } from '../types/index.js';
1
+ import type { 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
- //# sourceMappingURL=memory-read.d.ts.map
4
+ export declare const getStats: () => MemoryStats;
@@ -1,6 +1,4 @@
1
- import { db } from './database.js';
2
- import { mapRowToMemory, toSafeInteger } from './row-mappers.js';
3
- import { executeGet, executeRun } from './sqlite.js';
1
+ import { db, executeGet, executeRun, mapRowToMemory, toSafeInteger, } from './db.js';
4
2
  const stmtGetMemoryByHash = db.prepare('SELECT * FROM memories WHERE hash = ?');
5
3
  const stmtDeleteMemoryByHash = db.prepare('DELETE FROM memories WHERE hash = ?');
6
4
  export const getMemory = (hash) => {
@@ -11,4 +9,34 @@ export const deleteMemory = (hash) => {
11
9
  const result = executeRun(stmtDeleteMemoryByHash, hash);
12
10
  return { changes: toSafeInteger(result.changes, 'changes') };
13
11
  };
14
- //# sourceMappingURL=memory-read.js.map
12
+ const stmtMemoryCount = db.prepare('SELECT COUNT(*) as count FROM memories');
13
+ const stmtTagCount = db.prepare('SELECT COUNT(DISTINCT tag) as count FROM tags');
14
+ const stmtDateRange = db.prepare('SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories');
15
+ const toDateString = (value) => {
16
+ if (value == null)
17
+ return null;
18
+ if (typeof value === 'string')
19
+ return value;
20
+ if (typeof value === 'number')
21
+ return String(value);
22
+ return null;
23
+ };
24
+ const queryCounts = () => {
25
+ const memoryRow = executeGet(stmtMemoryCount);
26
+ const tagRow = executeGet(stmtTagCount);
27
+ if (!memoryRow)
28
+ throw new Error('Failed to load memory stats');
29
+ if (!tagRow)
30
+ throw new Error('Failed to load tag stats');
31
+ return { memoryRow, tagRow };
32
+ };
33
+ export const getStats = () => {
34
+ const { memoryRow, tagRow } = queryCounts();
35
+ const dateRow = executeGet(stmtDateRange);
36
+ return {
37
+ memoryCount: toSafeInteger(memoryRow.count, 'memoryCount'),
38
+ tagCount: toSafeInteger(tagRow.count, 'tagCount'),
39
+ oldestMemory: toDateString(dateRow?.oldest),
40
+ newestMemory: toDateString(dateRow?.newest),
41
+ };
42
+ };
@@ -8,4 +8,3 @@ export declare const getRelated: (input: {
8
8
  direction?: RelationDirection;
9
9
  }) => RelatedMemory[];
10
10
  export {};
11
- //# sourceMappingURL=memory-relations.d.ts.map
@@ -55,4 +55,3 @@ const getRelatedRecursive = (input) => {
55
55
  const incoming = queryIncomingRecursive(memoryId, relationType, maxDepth);
56
56
  return deduplicateByHash([...outgoing, ...incoming]);
57
57
  };
58
- //# sourceMappingURL=memory-relations.js.map
@@ -1,9 +1,10 @@
1
1
  import type { SearchResult } from '../types/index.js';
2
- export declare const searchMemories: (input: {
2
+ interface SearchInput {
3
3
  query: string;
4
- limit?: number;
5
- tags?: readonly string[];
6
- minRelevance?: number;
7
- offset?: number;
8
- }) => SearchResult[];
9
- //# sourceMappingURL=memory-search.d.ts.map
4
+ limit?: number | undefined;
5
+ tags?: readonly string[] | undefined;
6
+ minRelevance?: number | undefined;
7
+ offset?: number | undefined;
8
+ }
9
+ export declare const searchMemories: (input: SearchInput) => SearchResult[];
10
+ export {};
@@ -1,17 +1,23 @@
1
1
  import { mapRowToSearchResult } from './row-mappers.js';
2
2
  import { buildSearchQuery, executeSearch } from './search.js';
3
3
  import { normalizeTags } from './tags.js';
4
- export const searchMemories = (input) => {
5
- const { query, limit = 10, tags = [], minRelevance, offset } = input;
6
- const searchInput = {
7
- query,
8
- limit,
9
- tags: normalizeTags(tags, 50),
10
- ...(minRelevance !== undefined ? { minRelevance } : {}),
11
- ...(offset !== undefined ? { offset } : {}),
4
+ const buildSearchInput = (input) => {
5
+ const result = {
6
+ query: input.query,
7
+ limit: input.limit ?? 10,
8
+ tags: normalizeTags(input.tags ?? [], 50),
12
9
  };
10
+ if (input.minRelevance !== undefined) {
11
+ result.minRelevance = input.minRelevance;
12
+ }
13
+ if (input.offset !== undefined) {
14
+ result.offset = input.offset;
15
+ }
16
+ return result;
17
+ };
18
+ export const searchMemories = (input) => {
19
+ const searchInput = buildSearchInput(input);
13
20
  const { sql, params } = buildSearchQuery(searchInput);
14
21
  const rows = executeSearch(sql, params);
15
22
  return rows.map((row) => mapRowToSearchResult(row));
16
23
  };
17
- //# sourceMappingURL=memory-search.js.map
@@ -1,3 +1,2 @@
1
1
  import type { MemoryStats } from '../types/index.js';
2
2
  export declare const getStats: () => MemoryStats;
3
- //# sourceMappingURL=memory-stats.d.ts.map
@@ -49,4 +49,3 @@ export const getStats = () => {
49
49
  newestMemory: toDateString(dateRow?.newest),
50
50
  };
51
51
  };
52
- //# sourceMappingURL=memory-stats.js.map
@@ -8,4 +8,3 @@ interface UpdateMemoryOptions {
8
8
  }
9
9
  export declare const updateMemory: (hash: string, options: UpdateMemoryOptions) => MemoryUpdateResult;
10
10
  export {};
11
- //# sourceMappingURL=memory-updates.d.ts.map
@@ -68,7 +68,7 @@ const enforceTagLimit = (existingTags, tagsToInsert, maxTags) => {
68
68
  continue;
69
69
  projectedCount += 1;
70
70
  if (projectedCount > maxTags) {
71
- throw new Error('Too many tags (max ' + String(maxTags) + ')');
71
+ throw new Error(`Too many tags (max ${maxTags})`);
72
72
  }
73
73
  existingTags.add(tag);
74
74
  }
@@ -113,4 +113,3 @@ export const updateMemory = (hash, options) => {
113
113
  return { updated: true, hash };
114
114
  });
115
115
  };
116
- //# sourceMappingURL=memory-updates.js.map
@@ -0,0 +1,13 @@
1
+ import type { MemoryInsertResult, MemoryUpdateResult } from '../types.js';
2
+ export declare const normalizeTags: (tags: readonly string[], maxTags: number) => string[];
3
+ export declare const findMemoryIdByHash: (hash: string) => number | undefined;
4
+ export declare const createMemory: (input: {
5
+ content: string;
6
+ tags?: readonly string[];
7
+ }) => MemoryInsertResult;
8
+ interface UpdateMemoryOptions {
9
+ content: string;
10
+ tags?: readonly string[] | undefined;
11
+ }
12
+ export declare const updateMemory: (hash: string, options: UpdateMemoryOptions) => MemoryUpdateResult;
13
+ export {};
@@ -0,0 +1,113 @@
1
+ import crypto from 'node:crypto';
2
+ import { db, executeGet, executeRun, toSafeInteger, withImmediateTransaction, } from './db.js';
3
+ const MAX_TAGS = 100;
4
+ const validateTag = (tag) => {
5
+ if (tag.length === 0) {
6
+ throw new Error('Tag must be at least 1 character');
7
+ }
8
+ if (tag.length > 50) {
9
+ throw new Error('Tag exceeds 50 characters');
10
+ }
11
+ };
12
+ const validateTagCount = (tags, maxTags) => {
13
+ if (tags.length > maxTags) {
14
+ throw new Error(`Too many tags (max ${maxTags})`);
15
+ }
16
+ };
17
+ const dedupeTags = (tags) => {
18
+ const seen = new Set();
19
+ for (const tag of tags) {
20
+ validateTag(tag);
21
+ seen.add(tag);
22
+ }
23
+ return [...seen];
24
+ };
25
+ export const normalizeTags = (tags, maxTags) => {
26
+ if (tags.length === 0)
27
+ return [];
28
+ validateTagCount(tags, maxTags);
29
+ return dedupeTags(tags);
30
+ };
31
+ const stmtFindMemoryIdByHash = db.prepare('SELECT id FROM memories WHERE hash = ?');
32
+ const buildTagInsert = (memoryId, tags) => {
33
+ const params = tags.flatMap((tag) => [memoryId, tag]);
34
+ return { params };
35
+ };
36
+ const tagInsertStatements = [];
37
+ const getInsertTagsStatement = (tagCount) => {
38
+ const cached = tagInsertStatements[tagCount];
39
+ if (cached)
40
+ return cached;
41
+ const placeholders = Array.from({ length: tagCount }, () => '(?, ?)').join(', ');
42
+ const stmt = db.prepare(`INSERT OR IGNORE INTO tags (memory_id, tag) VALUES ${placeholders}`);
43
+ tagInsertStatements[tagCount] = stmt;
44
+ return stmt;
45
+ };
46
+ export const findMemoryIdByHash = (hash) => {
47
+ const row = executeGet(stmtFindMemoryIdByHash, hash);
48
+ if (!row)
49
+ return undefined;
50
+ return toSafeInteger(row.id, 'id');
51
+ };
52
+ const insertTags = (memoryId, tags) => {
53
+ if (tags.length === 0)
54
+ return;
55
+ const { params } = buildTagInsert(memoryId, tags);
56
+ const stmt = getInsertTagsStatement(tags.length);
57
+ executeRun(stmt, ...params);
58
+ };
59
+ const buildHash = (content) => {
60
+ // eslint-disable-next-line sonarjs/hashing -- MD5 used for non-security deduplication only.
61
+ return crypto.createHash('md5').update(content).digest('hex');
62
+ };
63
+ const stmtInsertMemory = db.prepare('INSERT OR IGNORE INTO memories (content, hash) VALUES (?, ?) RETURNING id');
64
+ const requireMemoryId = (id) => {
65
+ if (id === undefined) {
66
+ throw new Error('Failed to resolve memory id');
67
+ }
68
+ return id;
69
+ };
70
+ const resolveMemoryId = (content, hash) => {
71
+ const inserted = executeGet(stmtInsertMemory, content, hash);
72
+ if (inserted) {
73
+ return { id: toSafeInteger(inserted.id, 'id'), isNew: true };
74
+ }
75
+ const id = requireMemoryId(findMemoryIdByHash(hash));
76
+ return { id, isNew: false };
77
+ };
78
+ export const createMemory = (input) => withImmediateTransaction(() => {
79
+ const { content, tags = [] } = input;
80
+ const hash = buildHash(content);
81
+ const normalizedTags = normalizeTags(tags, MAX_TAGS);
82
+ const { id, isNew } = resolveMemoryId(content, hash);
83
+ insertTags(id, normalizedTags);
84
+ return { id, hash, isNew };
85
+ });
86
+ const stmtDeleteTagsForMemory = db.prepare('DELETE FROM tags WHERE memory_id = ?');
87
+ const stmtUpdateContent = db.prepare('UPDATE memories SET content = ?, hash = ? WHERE id = ?');
88
+ const replaceTags = (memoryId, tags) => {
89
+ executeRun(stmtDeleteTagsForMemory, memoryId);
90
+ insertTags(memoryId, normalizeTags(tags, MAX_TAGS));
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
+ const newHash = buildHash(options.content);
98
+ // Check if new content would create a duplicate
99
+ if (newHash !== hash) {
100
+ const existingId = findMemoryIdByHash(newHash);
101
+ if (existingId !== undefined) {
102
+ throw new Error('Content already exists as another memory');
103
+ }
104
+ }
105
+ // Update content and hash
106
+ executeRun(stmtUpdateContent, options.content, newHash, memoryId);
107
+ // Update tags if provided, otherwise preserve existing tags
108
+ if (options.tags !== undefined) {
109
+ replaceTags(memoryId, options.tags);
110
+ }
111
+ return { updated: true, oldHash: hash, newHash };
112
+ });
113
+ };
@@ -5,4 +5,3 @@ export declare const queryBothDirect: (memoryId: number, relationType?: string)
5
5
  export declare const queryOutgoingRecursive: (memoryId: number, relationType: string | undefined, maxDepth: number) => RelatedMemory[];
6
6
  export declare const queryIncomingRecursive: (memoryId: number, relationType: string | undefined, maxDepth: number) => RelatedMemory[];
7
7
  export declare const deduplicateByHash: (memories: RelatedMemory[]) => RelatedMemory[];
8
- //# sourceMappingURL=relation-queries.d.ts.map
@@ -123,4 +123,3 @@ export const deduplicateByHash = (memories) => {
123
123
  .sort((a, b) => a.depth - b.depth || a.id - b.id)
124
124
  .slice(0, 1000);
125
125
  };
126
- //# sourceMappingURL=relation-queries.js.map
@@ -0,0 +1,10 @@
1
+ import type { RelatedMemory, StatementResult } from '../types.js';
2
+ type RelationDirection = 'outgoing' | 'incoming' | 'both';
3
+ export declare const linkMemories: (fromHash: string, toHash: string, relationType: string) => StatementResult;
4
+ export declare const getRelated: (input: {
5
+ hash: string;
6
+ relationType?: string;
7
+ depth?: number;
8
+ direction?: RelationDirection;
9
+ }) => RelatedMemory[];
10
+ export {};
@@ -0,0 +1,177 @@
1
+ import { db, executeAll, executeRun, mapRowToRelatedMemory, prepareCached, toSafeInteger, } from './db.js';
2
+ import { findMemoryIdByHash } from './memory-write.js';
3
+ const stmtInsertRelation = db.prepare('INSERT OR IGNORE INTO relationships (from_memory_id, to_memory_id, ' +
4
+ 'relation_type) VALUES (?, ?, ?)');
5
+ const directConfig = {
6
+ outgoing: { joinColumn: 'to_memory_id', whereColumn: 'from_memory_id' },
7
+ incoming: { joinColumn: 'from_memory_id', whereColumn: 'to_memory_id' },
8
+ };
9
+ const recursiveConfig = {
10
+ outgoing: {
11
+ baseColumn: 'from_memory_id',
12
+ joinColumn: 'from_memory_id',
13
+ relsJoinColumn: 'to_id',
14
+ memoryJoinColumn: 'to_id',
15
+ },
16
+ incoming: {
17
+ baseColumn: 'to_memory_id',
18
+ joinColumn: 'to_memory_id',
19
+ relsJoinColumn: 'from_id',
20
+ memoryJoinColumn: 'from_id',
21
+ },
22
+ };
23
+ const typeFilter = (relationType) => relationType
24
+ ? { clause: ' AND r.relation_type = ?', params: [relationType] }
25
+ : { clause: '', params: [] };
26
+ const run = (sql, params) => executeAll(prepareCached(sql), ...params).map((row) => mapRowToRelatedMemory(row));
27
+ const buildDirectSql = (input) => {
28
+ const config = directConfig[input.direction];
29
+ const limitClause = input.includeLimit === false ? '' : '\n LIMIT 1000';
30
+ return `
31
+ SELECT m.*, r.relation_type as relation_type, 1 as depth
32
+ FROM memories m
33
+ JOIN relationships r ON m.id = r.${config.joinColumn}
34
+ WHERE r.${config.whereColumn} = ?${input.clause}${limitClause}
35
+ `;
36
+ };
37
+ const buildRecursiveSql = (input) => {
38
+ const config = recursiveConfig[input.direction];
39
+ return `
40
+ WITH RECURSIVE rels(depth, from_id, to_id, relation_type) AS (
41
+ SELECT 1, r.from_memory_id, r.to_memory_id, r.relation_type
42
+ FROM relationships r
43
+ WHERE r.${config.baseColumn} = ?${input.clause}
44
+ UNION ALL
45
+ SELECT rels.depth + 1, r.from_memory_id, r.to_memory_id, r.relation_type
46
+ FROM relationships r
47
+ JOIN rels ON r.${config.joinColumn} = rels.${config.relsJoinColumn}
48
+ WHERE rels.depth < ?${input.clause}
49
+ )
50
+ SELECT m.*, rels.relation_type as relation_type, MIN(rels.depth) as depth
51
+ FROM rels
52
+ JOIN memories m ON m.id = rels.${config.memoryJoinColumn}
53
+ GROUP BY m.id, rels.relation_type
54
+ ORDER BY depth, m.id
55
+ LIMIT 1000
56
+ `;
57
+ };
58
+ const buildRecursiveParams = (input) => {
59
+ if (!input.relationType) {
60
+ return [input.memoryId, input.maxDepth];
61
+ }
62
+ return [input.memoryId, ...input.params, input.maxDepth, ...input.params];
63
+ };
64
+ const queryOutgoingDirect = (memoryId, relationType) => {
65
+ const { clause, params } = typeFilter(relationType);
66
+ const sql = buildDirectSql({ direction: 'outgoing', clause });
67
+ return run(sql, [memoryId, ...params]);
68
+ };
69
+ const queryIncomingDirect = (memoryId, relationType) => {
70
+ const { clause, params } = typeFilter(relationType);
71
+ const sql = buildDirectSql({ direction: 'incoming', clause });
72
+ return run(sql, [memoryId, ...params]);
73
+ };
74
+ const queryBothDirect = (memoryId, relationType) => {
75
+ const { clause, params } = typeFilter(relationType);
76
+ const outgoingSql = buildDirectSql({
77
+ direction: 'outgoing',
78
+ clause,
79
+ includeLimit: false,
80
+ });
81
+ const incomingSql = buildDirectSql({
82
+ direction: 'incoming',
83
+ clause,
84
+ includeLimit: false,
85
+ });
86
+ const sql = `
87
+ ${outgoingSql}
88
+ UNION
89
+ ${incomingSql}
90
+ LIMIT 1000
91
+ `;
92
+ return run(sql, [memoryId, ...params, memoryId, ...params]);
93
+ };
94
+ const queryOutgoingRecursive = (memoryId, relationType, maxDepth) => {
95
+ const { clause, params } = typeFilter(relationType);
96
+ const sql = buildRecursiveSql({ direction: 'outgoing', clause });
97
+ const sqlParams = buildRecursiveParams({
98
+ memoryId,
99
+ relationType,
100
+ maxDepth,
101
+ params,
102
+ });
103
+ return run(sql, sqlParams);
104
+ };
105
+ const queryIncomingRecursive = (memoryId, relationType, maxDepth) => {
106
+ const { clause, params } = typeFilter(relationType);
107
+ const sql = buildRecursiveSql({ direction: 'incoming', clause });
108
+ const sqlParams = buildRecursiveParams({
109
+ memoryId,
110
+ relationType,
111
+ maxDepth,
112
+ params,
113
+ });
114
+ return run(sql, sqlParams);
115
+ };
116
+ const deduplicateByHash = (memories) => {
117
+ const seen = new Map();
118
+ for (const mem of memories) {
119
+ const existing = seen.get(mem.hash);
120
+ if (!existing || mem.depth < existing.depth) {
121
+ seen.set(mem.hash, mem);
122
+ }
123
+ }
124
+ return [...seen.values()]
125
+ .sort((a, b) => a.depth - b.depth || a.id - b.id)
126
+ .slice(0, 1000);
127
+ };
128
+ const resolveMaxDepth = (depth, direction) => {
129
+ if (direction === 'both') {
130
+ return Math.min(depth, 2);
131
+ }
132
+ return Math.max(1, depth);
133
+ };
134
+ export const linkMemories = (fromHash, toHash, relationType) => {
135
+ const fromId = findMemoryIdByHash(fromHash);
136
+ const toId = findMemoryIdByHash(toHash);
137
+ if (fromId === undefined || toId === undefined) {
138
+ throw new Error('One or both memories not found');
139
+ }
140
+ const result = executeRun(stmtInsertRelation, fromId, toId, relationType);
141
+ return { changes: toSafeInteger(result.changes, 'changes') };
142
+ };
143
+ export const getRelated = (input) => {
144
+ const { hash, relationType, depth = 1, direction = 'outgoing' } = input;
145
+ const memoryId = findMemoryIdByHash(hash);
146
+ if (memoryId === undefined)
147
+ return [];
148
+ const maxDepth = resolveMaxDepth(depth, direction);
149
+ if (maxDepth === 1) {
150
+ return getRelatedDirect(memoryId, relationType, direction);
151
+ }
152
+ return getRelatedRecursive({
153
+ memoryId,
154
+ relationType,
155
+ maxDepth,
156
+ direction,
157
+ });
158
+ };
159
+ const getRelatedDirect = (memoryId, relationType, direction = 'outgoing') => {
160
+ if (direction === 'outgoing')
161
+ return queryOutgoingDirect(memoryId, relationType);
162
+ if (direction === 'incoming')
163
+ return queryIncomingDirect(memoryId, relationType);
164
+ return queryBothDirect(memoryId, relationType);
165
+ };
166
+ const getRelatedRecursive = (input) => {
167
+ const { memoryId, relationType, maxDepth, direction } = input;
168
+ if (direction === 'outgoing') {
169
+ return queryOutgoingRecursive(memoryId, relationType, maxDepth);
170
+ }
171
+ if (direction === 'incoming') {
172
+ return queryIncomingRecursive(memoryId, relationType, maxDepth);
173
+ }
174
+ const outgoing = queryOutgoingRecursive(memoryId, relationType, maxDepth);
175
+ const incoming = queryIncomingRecursive(memoryId, relationType, maxDepth);
176
+ return deduplicateByHash([...outgoing, ...incoming]);
177
+ };
@@ -4,4 +4,3 @@ export declare const toSafeInteger: (value: unknown, field: string) => number;
4
4
  export declare const mapRowToMemory: (row: DbRow) => Memory;
5
5
  export declare const mapRowToSearchResult: (row: DbRow) => SearchResult;
6
6
  export declare const mapRowToRelatedMemory: (row: DbRow) => RelatedMemory;
7
- //# sourceMappingURL=row-mappers.d.ts.map
@@ -50,4 +50,3 @@ export const mapRowToRelatedMemory = (row) => ({
50
50
  relation_type: toString(row.relation_type, 'relation_type'),
51
51
  depth: toSafeInteger(row.depth, 'depth'),
52
52
  });
53
- //# sourceMappingURL=row-mappers.js.map
@@ -1,2 +1 @@
1
1
  export declare const toSearchError: (err: unknown) => Error | undefined;
2
- //# sourceMappingURL=search-errors.d.ts.map
@@ -28,4 +28,3 @@ export const toSearchError = (err) => {
28
28
  }
29
29
  return undefined;
30
30
  };
31
- //# sourceMappingURL=search-errors.js.map
@@ -1,13 +1,6 @@
1
- import type { DbRow } from './row-mappers.js';
2
- export declare const buildSearchQuery: (input: {
1
+ import type { SearchResult } from '../types.js';
2
+ interface SearchInput {
3
3
  query: string;
4
- limit: number;
5
- tags: readonly string[];
6
- minRelevance?: number;
7
- offset?: number;
8
- }) => {
9
- sql: string;
10
- params: (number | string)[];
11
- };
12
- export declare const executeSearch: (sql: string, params: (number | string)[]) => DbRow[];
13
- //# sourceMappingURL=search.d.ts.map
4
+ }
5
+ export declare const searchMemories: (input: SearchInput) => SearchResult[];
6
+ export {};