@soulcraft/brainy 5.3.6 → 5.4.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.
- package/CHANGELOG.md +65 -0
- package/dist/brainy.d.ts +61 -0
- package/dist/brainy.js +179 -23
- package/dist/storage/adapters/azureBlobStorage.d.ts +13 -64
- package/dist/storage/adapters/azureBlobStorage.js +78 -388
- package/dist/storage/adapters/fileSystemStorage.d.ts +12 -78
- package/dist/storage/adapters/fileSystemStorage.js +49 -395
- package/dist/storage/adapters/gcsStorage.d.ts +13 -134
- package/dist/storage/adapters/gcsStorage.js +79 -557
- package/dist/storage/adapters/historicalStorageAdapter.d.ts +181 -0
- package/dist/storage/adapters/historicalStorageAdapter.js +332 -0
- package/dist/storage/adapters/memoryStorage.d.ts +4 -113
- package/dist/storage/adapters/memoryStorage.js +34 -471
- package/dist/storage/adapters/opfsStorage.d.ts +14 -127
- package/dist/storage/adapters/opfsStorage.js +44 -693
- package/dist/storage/adapters/r2Storage.d.ts +8 -41
- package/dist/storage/adapters/r2Storage.js +49 -237
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +13 -111
- package/dist/storage/adapters/s3CompatibleStorage.js +77 -596
- package/dist/storage/baseStorage.d.ts +78 -38
- package/dist/storage/baseStorage.js +692 -23
- package/dist/storage/cow/BlobStorage.d.ts +2 -2
- package/dist/storage/cow/BlobStorage.js +4 -4
- package/dist/storage/storageFactory.d.ts +2 -3
- package/dist/storage/storageFactory.js +114 -66
- package/dist/vfs/types.d.ts +6 -2
- package/package.json +1 -1
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Memory Storage Adapter
|
|
3
3
|
* In-memory storage adapter for environments where persistent storage is not available or needed
|
|
4
4
|
*/
|
|
5
|
-
import { NounType } from '../../coreTypes.js';
|
|
6
5
|
import { BaseStorage } from '../baseStorage.js';
|
|
7
6
|
// No type aliases needed - using the original types directly
|
|
8
7
|
/**
|
|
@@ -22,9 +21,7 @@ export class MemoryStorage extends BaseStorage {
|
|
|
22
21
|
}
|
|
23
22
|
constructor() {
|
|
24
23
|
super();
|
|
25
|
-
//
|
|
26
|
-
this.nouns = new Map();
|
|
27
|
-
this.verbs = new Map();
|
|
24
|
+
// v5.4.0: Removed redundant Maps (nouns, verbs) - objectStore handles all storage via type-first paths
|
|
28
25
|
this.statistics = null;
|
|
29
26
|
// Unified object store for primitive operations (replaces metadata, nounMetadata, verbMetadata)
|
|
30
27
|
this.objectStore = new Map();
|
|
@@ -63,456 +60,17 @@ export class MemoryStorage extends BaseStorage {
|
|
|
63
60
|
async init() {
|
|
64
61
|
this.isInitialized = true;
|
|
65
62
|
}
|
|
66
|
-
|
|
67
|
-
* Save a noun to storage (v4.0.0: pure vector only, no metadata)
|
|
68
|
-
* v5.0.1: COW-aware - uses branch-prefixed paths for fork isolation
|
|
69
|
-
*/
|
|
70
|
-
async saveNoun_internal(noun) {
|
|
71
|
-
const isNew = !this.nouns.has(noun.id);
|
|
72
|
-
// Create a deep copy to avoid reference issues
|
|
73
|
-
// v4.0.0: Store ONLY vector data (no metadata field)
|
|
74
|
-
// Metadata is saved separately via saveNounMetadata() by base class
|
|
75
|
-
const nounCopy = {
|
|
76
|
-
id: noun.id,
|
|
77
|
-
vector: [...noun.vector],
|
|
78
|
-
connections: new Map(),
|
|
79
|
-
level: noun.level || 0
|
|
80
|
-
// ✅ NO metadata field in v4.0.0
|
|
81
|
-
};
|
|
82
|
-
// Copy connections
|
|
83
|
-
for (const [level, connections] of noun.connections.entries()) {
|
|
84
|
-
nounCopy.connections.set(level, new Set(connections));
|
|
85
|
-
}
|
|
86
|
-
// v5.0.1: COW-aware write using branch-prefixed path
|
|
87
|
-
// Use synthetic path for vector storage (nouns don't have types in standalone mode)
|
|
88
|
-
const path = `hnsw/nouns/${noun.id}.json`;
|
|
89
|
-
await this.writeObjectToBranch(path, nounCopy);
|
|
90
|
-
// ALSO store in nouns Map for fast iteration (getNouns, initializeCounts)
|
|
91
|
-
// This is redundant but maintains backward compatibility
|
|
92
|
-
this.nouns.set(noun.id, nounCopy);
|
|
93
|
-
// Count tracking happens in baseStorage.saveNounMetadata_internal (v4.1.2)
|
|
94
|
-
// This fixes the race condition where metadata didn't exist yet
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Get a noun from storage (v4.0.0: returns pure vector only)
|
|
98
|
-
* Base class handles combining with metadata
|
|
99
|
-
* v5.0.1: COW-aware - reads from branch-prefixed paths with inheritance
|
|
100
|
-
*/
|
|
101
|
-
async getNoun_internal(id) {
|
|
102
|
-
// v5.0.1: COW-aware read using branch-prefixed path with inheritance
|
|
103
|
-
const path = `hnsw/nouns/${id}.json`;
|
|
104
|
-
const noun = await this.readWithInheritance(path);
|
|
105
|
-
// If not found, return null
|
|
106
|
-
if (!noun) {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
// Return a deep copy to avoid reference issues
|
|
110
|
-
// v4.0.0: Return ONLY vector data (no metadata field)
|
|
111
|
-
const nounCopy = {
|
|
112
|
-
id: noun.id,
|
|
113
|
-
vector: [...noun.vector],
|
|
114
|
-
connections: new Map(),
|
|
115
|
-
level: noun.level || 0
|
|
116
|
-
// ✅ NO metadata field in v4.0.0
|
|
117
|
-
};
|
|
118
|
-
// Copy connections (handle both Map and plain object from JSON)
|
|
119
|
-
const connections = noun.connections instanceof Map ? noun.connections : new Map(Object.entries(noun.connections || {}));
|
|
120
|
-
for (const [level, conns] of connections.entries()) {
|
|
121
|
-
nounCopy.connections.set(Number(level), new Set(conns));
|
|
122
|
-
}
|
|
123
|
-
return nounCopy;
|
|
124
|
-
}
|
|
63
|
+
// v5.4.0: Removed saveNoun_internal and getNoun_internal - using BaseStorage's type-first implementation
|
|
125
64
|
/**
|
|
126
65
|
* Get nouns with pagination and filtering
|
|
127
66
|
* v4.0.0: Returns HNSWNounWithMetadata[] (includes metadata field)
|
|
128
67
|
* @param options Pagination and filtering options
|
|
129
68
|
* @returns Promise that resolves to a paginated result of nouns with metadata
|
|
130
69
|
*/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const offset = pagination.offset || 0;
|
|
136
|
-
const limit = pagination.limit || 100;
|
|
137
|
-
// Convert string types to arrays for consistent handling
|
|
138
|
-
const nounTypes = filter.nounType
|
|
139
|
-
? Array.isArray(filter.nounType) ? filter.nounType : [filter.nounType]
|
|
140
|
-
: undefined;
|
|
141
|
-
const services = filter.service
|
|
142
|
-
? Array.isArray(filter.service) ? filter.service : [filter.service]
|
|
143
|
-
: undefined;
|
|
144
|
-
// First, collect all noun IDs that match the filter criteria
|
|
145
|
-
const matchingIds = [];
|
|
146
|
-
// Iterate through all nouns to find matches
|
|
147
|
-
// v4.0.0: Load metadata from separate storage (no embedded metadata field)
|
|
148
|
-
for (const [nounId, noun] of this.nouns.entries()) {
|
|
149
|
-
// Get metadata from separate storage
|
|
150
|
-
const metadata = await this.getNounMetadata(nounId);
|
|
151
|
-
// Skip if no metadata (shouldn't happen in v4.0.0 but be defensive)
|
|
152
|
-
if (!metadata) {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
// Filter by noun type if specified
|
|
156
|
-
if (nounTypes && metadata.noun && !nounTypes.includes(metadata.noun)) {
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
// Filter by service if specified
|
|
160
|
-
if (services && metadata.service && !services.includes(metadata.service)) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
// Filter by metadata fields if specified
|
|
164
|
-
if (filter.metadata) {
|
|
165
|
-
let metadataMatch = true;
|
|
166
|
-
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
167
|
-
if (metadata[key] !== value) {
|
|
168
|
-
metadataMatch = false;
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
if (!metadataMatch)
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
// If we got here, the noun matches all filters
|
|
176
|
-
matchingIds.push(nounId);
|
|
177
|
-
}
|
|
178
|
-
// Calculate pagination
|
|
179
|
-
const totalCount = matchingIds.length;
|
|
180
|
-
const paginatedIds = matchingIds.slice(offset, offset + limit);
|
|
181
|
-
const hasMore = offset + limit < totalCount;
|
|
182
|
-
// Create cursor for next page if there are more results
|
|
183
|
-
const nextCursor = hasMore ? `${offset + limit}` : undefined;
|
|
184
|
-
// Fetch the actual nouns for the current page
|
|
185
|
-
// v4.0.0: Return HNSWNounWithMetadata (includes metadata field)
|
|
186
|
-
const items = [];
|
|
187
|
-
for (const id of paginatedIds) {
|
|
188
|
-
const noun = this.nouns.get(id);
|
|
189
|
-
if (!noun)
|
|
190
|
-
continue;
|
|
191
|
-
// Get metadata from separate storage
|
|
192
|
-
// FIX v4.7.4: Don't skip nouns without metadata - metadata is optional in v4.0.0
|
|
193
|
-
const metadata = await this.getNounMetadata(id);
|
|
194
|
-
// v4.8.0: Extract standard fields from metadata to top-level
|
|
195
|
-
const metadataObj = (metadata || {});
|
|
196
|
-
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
197
|
-
// v4.8.0: Create HNSWNounWithMetadata with standard fields at top-level
|
|
198
|
-
const nounWithMetadata = {
|
|
199
|
-
id: noun.id,
|
|
200
|
-
vector: [...noun.vector],
|
|
201
|
-
connections: new Map(),
|
|
202
|
-
level: noun.level || 0,
|
|
203
|
-
// v4.8.0: Standard fields at top-level
|
|
204
|
-
type: nounType || NounType.Thing,
|
|
205
|
-
createdAt: createdAt || Date.now(),
|
|
206
|
-
updatedAt: updatedAt || Date.now(),
|
|
207
|
-
confidence: confidence,
|
|
208
|
-
weight: weight,
|
|
209
|
-
service: service,
|
|
210
|
-
data: data,
|
|
211
|
-
createdBy,
|
|
212
|
-
// Only custom user fields in metadata
|
|
213
|
-
metadata: customMetadata
|
|
214
|
-
};
|
|
215
|
-
// Copy connections
|
|
216
|
-
for (const [level, connections] of noun.connections.entries()) {
|
|
217
|
-
nounWithMetadata.connections.set(level, new Set(connections));
|
|
218
|
-
}
|
|
219
|
-
items.push(nounWithMetadata);
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
items,
|
|
223
|
-
totalCount,
|
|
224
|
-
hasMore,
|
|
225
|
-
nextCursor
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Get nouns with pagination - simplified interface for compatibility
|
|
230
|
-
* v4.0.0: Returns HNSWNounWithMetadata[] (includes metadata field)
|
|
231
|
-
*/
|
|
232
|
-
async getNounsWithPagination(options = {}) {
|
|
233
|
-
// Convert to the getNouns format
|
|
234
|
-
const result = await this.getNouns({
|
|
235
|
-
pagination: {
|
|
236
|
-
offset: options.cursor ? parseInt(options.cursor) : 0,
|
|
237
|
-
limit: options.limit || 100
|
|
238
|
-
},
|
|
239
|
-
filter: options.filter
|
|
240
|
-
});
|
|
241
|
-
return {
|
|
242
|
-
items: result.items,
|
|
243
|
-
totalCount: result.totalCount || 0,
|
|
244
|
-
hasMore: result.hasMore,
|
|
245
|
-
nextCursor: result.nextCursor
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Get nouns by noun type
|
|
250
|
-
* @param nounType The noun type to filter by
|
|
251
|
-
* @returns Promise that resolves to an array of nouns of the specified noun type
|
|
252
|
-
* @deprecated Use getNouns() with filter.nounType instead
|
|
253
|
-
*/
|
|
254
|
-
async getNounsByNounType_internal(nounType) {
|
|
255
|
-
const result = await this.getNouns({
|
|
256
|
-
filter: {
|
|
257
|
-
nounType
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
return result.items;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Delete a noun from storage (v4.0.0)
|
|
264
|
-
* v5.0.1: COW-aware - deletes from branch-prefixed paths
|
|
265
|
-
*/
|
|
266
|
-
async deleteNoun_internal(id) {
|
|
267
|
-
// v4.0.0: Get type from separate metadata storage
|
|
268
|
-
const metadata = await this.getNounMetadata(id);
|
|
269
|
-
if (metadata) {
|
|
270
|
-
const type = metadata.noun || 'default';
|
|
271
|
-
this.decrementEntityCount(type);
|
|
272
|
-
}
|
|
273
|
-
// v5.0.1: COW-aware delete using branch-prefixed path
|
|
274
|
-
const path = `hnsw/nouns/${id}.json`;
|
|
275
|
-
await this.deleteObjectFromBranch(path);
|
|
276
|
-
// Also remove from nouns Map for fast iteration
|
|
277
|
-
this.nouns.delete(id);
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Save a verb to storage (v4.0.0: pure vector + core fields, no metadata)
|
|
281
|
-
* v5.0.1: COW-aware - uses branch-prefixed paths for fork isolation
|
|
282
|
-
*/
|
|
283
|
-
async saveVerb_internal(verb) {
|
|
284
|
-
const isNew = !this.verbs.has(verb.id);
|
|
285
|
-
// Create a deep copy to avoid reference issues
|
|
286
|
-
// v4.0.0: Include core relational fields but NO metadata field
|
|
287
|
-
const verbCopy = {
|
|
288
|
-
id: verb.id,
|
|
289
|
-
vector: [...verb.vector],
|
|
290
|
-
connections: new Map(),
|
|
291
|
-
// CORE RELATIONAL DATA (part of HNSWVerb in v4.0.0)
|
|
292
|
-
verb: verb.verb,
|
|
293
|
-
sourceId: verb.sourceId,
|
|
294
|
-
targetId: verb.targetId
|
|
295
|
-
// ✅ NO metadata field in v4.0.0
|
|
296
|
-
};
|
|
297
|
-
// Copy connections
|
|
298
|
-
for (const [level, connections] of verb.connections.entries()) {
|
|
299
|
-
verbCopy.connections.set(level, new Set(connections));
|
|
300
|
-
}
|
|
301
|
-
// v5.0.1: COW-aware write using branch-prefixed path
|
|
302
|
-
const path = `hnsw/verbs/${verb.id}.json`;
|
|
303
|
-
await this.writeObjectToBranch(path, verbCopy);
|
|
304
|
-
// ALSO store in verbs Map for fast iteration (getVerbs, initializeCounts)
|
|
305
|
-
this.verbs.set(verb.id, verbCopy);
|
|
306
|
-
// Note: Count tracking happens in saveVerbMetadata since metadata is separate
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Get a verb from storage (v4.0.0: returns pure vector + core fields)
|
|
310
|
-
* Base class handles combining with metadata
|
|
311
|
-
* v5.0.1: COW-aware - reads from branch-prefixed paths with inheritance
|
|
312
|
-
*/
|
|
313
|
-
async getVerb_internal(id) {
|
|
314
|
-
// v5.0.1: COW-aware read using branch-prefixed path with inheritance
|
|
315
|
-
const path = `hnsw/verbs/${id}.json`;
|
|
316
|
-
const verb = await this.readWithInheritance(path);
|
|
317
|
-
// If not found, return null
|
|
318
|
-
if (!verb) {
|
|
319
|
-
return null;
|
|
320
|
-
}
|
|
321
|
-
// Return a deep copy of the HNSWVerb
|
|
322
|
-
// v4.0.0: Include core relational fields but NO metadata field
|
|
323
|
-
const verbCopy = {
|
|
324
|
-
id: verb.id,
|
|
325
|
-
vector: [...verb.vector],
|
|
326
|
-
connections: new Map(),
|
|
327
|
-
// CORE RELATIONAL DATA (part of HNSWVerb in v4.0.0)
|
|
328
|
-
verb: verb.verb,
|
|
329
|
-
sourceId: verb.sourceId,
|
|
330
|
-
targetId: verb.targetId
|
|
331
|
-
// ✅ NO metadata field in v4.0.0
|
|
332
|
-
};
|
|
333
|
-
// Copy connections (handle both Map and plain object from JSON)
|
|
334
|
-
const connections = verb.connections instanceof Map ? verb.connections : new Map(Object.entries(verb.connections || {}));
|
|
335
|
-
for (const [level, conns] of connections.entries()) {
|
|
336
|
-
verbCopy.connections.set(Number(level), new Set(conns));
|
|
337
|
-
}
|
|
338
|
-
return verbCopy;
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Get verbs with pagination and filtering
|
|
342
|
-
* v4.0.0: Returns HNSWVerbWithMetadata[] (includes metadata field)
|
|
343
|
-
* @param options Pagination and filtering options
|
|
344
|
-
* @returns Promise that resolves to a paginated result of verbs with metadata
|
|
345
|
-
*/
|
|
346
|
-
async getVerbs(options = {}) {
|
|
347
|
-
const pagination = options.pagination || {};
|
|
348
|
-
const filter = options.filter || {};
|
|
349
|
-
// Default values
|
|
350
|
-
const offset = pagination.offset || 0;
|
|
351
|
-
const limit = pagination.limit || 100;
|
|
352
|
-
// Convert string types to arrays for consistent handling
|
|
353
|
-
const verbTypes = filter.verbType
|
|
354
|
-
? Array.isArray(filter.verbType) ? filter.verbType : [filter.verbType]
|
|
355
|
-
: undefined;
|
|
356
|
-
const sourceIds = filter.sourceId
|
|
357
|
-
? Array.isArray(filter.sourceId) ? filter.sourceId : [filter.sourceId]
|
|
358
|
-
: undefined;
|
|
359
|
-
const targetIds = filter.targetId
|
|
360
|
-
? Array.isArray(filter.targetId) ? filter.targetId : [filter.targetId]
|
|
361
|
-
: undefined;
|
|
362
|
-
const services = filter.service
|
|
363
|
-
? Array.isArray(filter.service) ? filter.service : [filter.service]
|
|
364
|
-
: undefined;
|
|
365
|
-
// First, collect all verb IDs that match the filter criteria
|
|
366
|
-
const matchingIds = [];
|
|
367
|
-
// Iterate through all verbs to find matches
|
|
368
|
-
// v4.0.0: Core fields (verb, sourceId, targetId) are in HNSWVerb, not metadata
|
|
369
|
-
for (const [verbId, hnswVerb] of this.verbs.entries()) {
|
|
370
|
-
// Get the metadata for service/data filtering
|
|
371
|
-
const metadata = await this.getVerbMetadata(verbId);
|
|
372
|
-
// Filter by verb type if specified
|
|
373
|
-
// v4.0.0: verb type is in HNSWVerb.verb
|
|
374
|
-
if (verbTypes && !verbTypes.includes(hnswVerb.verb || '')) {
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
// Filter by source ID if specified
|
|
378
|
-
// v4.0.0: sourceId is in HNSWVerb.sourceId
|
|
379
|
-
if (sourceIds && !sourceIds.includes(hnswVerb.sourceId || '')) {
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
// Filter by target ID if specified
|
|
383
|
-
// v4.0.0: targetId is in HNSWVerb.targetId
|
|
384
|
-
if (targetIds && !targetIds.includes(hnswVerb.targetId || '')) {
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
// Filter by metadata fields if specified
|
|
388
|
-
if (filter.metadata && metadata) {
|
|
389
|
-
let metadataMatch = true;
|
|
390
|
-
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
391
|
-
const metadataValue = metadata[key];
|
|
392
|
-
if (metadataValue !== value) {
|
|
393
|
-
metadataMatch = false;
|
|
394
|
-
break;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
if (!metadataMatch)
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
// Filter by service if specified
|
|
401
|
-
if (services && metadata && metadata.service && !services.includes(metadata.service)) {
|
|
402
|
-
continue;
|
|
403
|
-
}
|
|
404
|
-
// If we got here, the verb matches all filters
|
|
405
|
-
matchingIds.push(verbId);
|
|
406
|
-
}
|
|
407
|
-
// Calculate pagination
|
|
408
|
-
const totalCount = matchingIds.length;
|
|
409
|
-
const paginatedIds = matchingIds.slice(offset, offset + limit);
|
|
410
|
-
const hasMore = offset + limit < totalCount;
|
|
411
|
-
// Create cursor for next page if there are more results
|
|
412
|
-
const nextCursor = hasMore ? `${offset + limit}` : undefined;
|
|
413
|
-
// Fetch the actual verbs for the current page
|
|
414
|
-
// v4.0.0: Return HNSWVerbWithMetadata (includes metadata field)
|
|
415
|
-
const items = [];
|
|
416
|
-
for (const id of paginatedIds) {
|
|
417
|
-
const hnswVerb = this.verbs.get(id);
|
|
418
|
-
if (!hnswVerb)
|
|
419
|
-
continue;
|
|
420
|
-
// Get metadata from separate storage
|
|
421
|
-
// FIX v4.7.4: Don't skip verbs without metadata - metadata is optional in v4.0.0
|
|
422
|
-
// Core fields (verb, sourceId, targetId) are in HNSWVerb itself
|
|
423
|
-
const metadata = await this.getVerbMetadata(id);
|
|
424
|
-
// v4.8.0: Extract standard fields from metadata to top-level
|
|
425
|
-
const metadataObj = metadata || {};
|
|
426
|
-
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
427
|
-
// v4.8.0: Create HNSWVerbWithMetadata with standard fields at top-level
|
|
428
|
-
const verbWithMetadata = {
|
|
429
|
-
id: hnswVerb.id,
|
|
430
|
-
vector: [...hnswVerb.vector],
|
|
431
|
-
connections: new Map(),
|
|
432
|
-
// Core relational fields (part of HNSWVerb)
|
|
433
|
-
verb: hnswVerb.verb,
|
|
434
|
-
sourceId: hnswVerb.sourceId,
|
|
435
|
-
targetId: hnswVerb.targetId,
|
|
436
|
-
// v4.8.0: Standard fields at top-level
|
|
437
|
-
createdAt: createdAt || Date.now(),
|
|
438
|
-
updatedAt: updatedAt || Date.now(),
|
|
439
|
-
confidence: confidence,
|
|
440
|
-
weight: weight,
|
|
441
|
-
service: service,
|
|
442
|
-
data: data,
|
|
443
|
-
createdBy,
|
|
444
|
-
// Only custom user fields in metadata
|
|
445
|
-
metadata: customMetadata
|
|
446
|
-
};
|
|
447
|
-
// Copy connections
|
|
448
|
-
for (const [level, connections] of hnswVerb.connections.entries()) {
|
|
449
|
-
verbWithMetadata.connections.set(level, new Set(connections));
|
|
450
|
-
}
|
|
451
|
-
items.push(verbWithMetadata);
|
|
452
|
-
}
|
|
453
|
-
return {
|
|
454
|
-
items,
|
|
455
|
-
totalCount,
|
|
456
|
-
hasMore,
|
|
457
|
-
nextCursor
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
/**
|
|
461
|
-
* Get verbs by source
|
|
462
|
-
* @deprecated Use getVerbs() with filter.sourceId instead
|
|
463
|
-
*/
|
|
464
|
-
async getVerbsBySource_internal(sourceId) {
|
|
465
|
-
const result = await this.getVerbs({
|
|
466
|
-
filter: {
|
|
467
|
-
sourceId
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
return result.items;
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Get verbs by target
|
|
474
|
-
* @deprecated Use getVerbs() with filter.targetId instead
|
|
475
|
-
*/
|
|
476
|
-
async getVerbsByTarget_internal(targetId) {
|
|
477
|
-
const result = await this.getVerbs({
|
|
478
|
-
filter: {
|
|
479
|
-
targetId
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
return result.items;
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Get verbs by type
|
|
486
|
-
* @deprecated Use getVerbs() with filter.verbType instead
|
|
487
|
-
*/
|
|
488
|
-
async getVerbsByType_internal(type) {
|
|
489
|
-
const result = await this.getVerbs({
|
|
490
|
-
filter: {
|
|
491
|
-
verbType: type
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
return result.items;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Delete a verb from storage
|
|
498
|
-
* v5.0.1: COW-aware - deletes from branch-prefixed paths
|
|
499
|
-
*/
|
|
500
|
-
async deleteVerb_internal(id) {
|
|
501
|
-
// CRITICAL: Also delete verb metadata - this is what getVerbs() uses to find verbs
|
|
502
|
-
// Without this, getVerbsBySource() will still find "deleted" verbs via their metadata
|
|
503
|
-
const metadata = await this.getVerbMetadata(id);
|
|
504
|
-
if (metadata) {
|
|
505
|
-
const verbType = metadata.verb || metadata.type || 'default';
|
|
506
|
-
this.decrementVerbCount(verbType);
|
|
507
|
-
// Delete the metadata using the base storage method
|
|
508
|
-
await this.deleteVerbMetadata(id);
|
|
509
|
-
}
|
|
510
|
-
// v5.0.1: COW-aware delete using branch-prefixed path
|
|
511
|
-
const path = `hnsw/verbs/${id}.json`;
|
|
512
|
-
await this.deleteObjectFromBranch(path);
|
|
513
|
-
// Also remove from verbs Map for fast iteration
|
|
514
|
-
this.verbs.delete(id);
|
|
515
|
-
}
|
|
70
|
+
// v5.4.0: Removed public method overrides (getNouns, getNounsWithPagination, getVerbs) - using BaseStorage's type-first implementation
|
|
71
|
+
// v5.4.0: Removed getNounsByNounType_internal and deleteNoun_internal - using BaseStorage's type-first implementation
|
|
72
|
+
// v5.4.0: Removed saveVerb_internal and getVerb_internal - using BaseStorage's type-first implementation
|
|
73
|
+
// v5.4.0: Removed verb *_internal method overrides - using BaseStorage's type-first implementation
|
|
516
74
|
/**
|
|
517
75
|
* Primitive operation: Write object to path
|
|
518
76
|
* All metadata operations use this internally via base class routing
|
|
@@ -571,18 +129,22 @@ export class MemoryStorage extends BaseStorage {
|
|
|
571
129
|
}
|
|
572
130
|
/**
|
|
573
131
|
* Clear all data from storage
|
|
132
|
+
* v5.4.0: Clears objectStore (type-first paths)
|
|
574
133
|
*/
|
|
575
134
|
async clear() {
|
|
576
|
-
this.nouns.clear();
|
|
577
|
-
this.verbs.clear();
|
|
578
135
|
this.objectStore.clear();
|
|
579
136
|
this.statistics = null;
|
|
137
|
+
this.totalNounCount = 0;
|
|
138
|
+
this.totalVerbCount = 0;
|
|
139
|
+
this.entityCounts.clear();
|
|
140
|
+
this.verbCounts.clear();
|
|
580
141
|
// Clear the statistics cache
|
|
581
142
|
this.statisticsCache = null;
|
|
582
143
|
this.statisticsModified = false;
|
|
583
144
|
}
|
|
584
145
|
/**
|
|
585
146
|
* Get information about storage usage and capacity
|
|
147
|
+
* v5.4.0: Uses BaseStorage counts
|
|
586
148
|
*/
|
|
587
149
|
async getStorageStatus() {
|
|
588
150
|
return {
|
|
@@ -590,9 +152,9 @@ export class MemoryStorage extends BaseStorage {
|
|
|
590
152
|
used: 0, // In-memory storage doesn't have a meaningful size
|
|
591
153
|
quota: null, // In-memory storage doesn't have a quota
|
|
592
154
|
details: {
|
|
593
|
-
nodeCount: this.
|
|
594
|
-
edgeCount: this.
|
|
595
|
-
|
|
155
|
+
nodeCount: this.totalNounCount,
|
|
156
|
+
edgeCount: this.totalVerbCount,
|
|
157
|
+
objectStoreSize: this.objectStore.size
|
|
596
158
|
}
|
|
597
159
|
};
|
|
598
160
|
}
|
|
@@ -676,30 +238,30 @@ export class MemoryStorage extends BaseStorage {
|
|
|
676
238
|
* Initialize counts from in-memory storage - O(1) operation (v4.0.0)
|
|
677
239
|
*/
|
|
678
240
|
async initializeCounts() {
|
|
679
|
-
//
|
|
680
|
-
this.totalNounCount = this.nouns.size;
|
|
681
|
-
this.totalVerbCount = this.verbs.size;
|
|
682
|
-
// Initialize type-based counts by scanning metadata storage (v4.0.0)
|
|
241
|
+
// v5.4.0: Scan objectStore paths (type-first structure) to count entities
|
|
683
242
|
this.entityCounts.clear();
|
|
684
243
|
this.verbCounts.clear();
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
244
|
+
let totalNouns = 0;
|
|
245
|
+
let totalVerbs = 0;
|
|
246
|
+
// Scan all paths in objectStore
|
|
247
|
+
for (const path of this.objectStore.keys()) {
|
|
248
|
+
// Count nouns by type (entities/nouns/{type}/vectors/{shard}/{id}.json)
|
|
249
|
+
const nounMatch = path.match(/^entities\/nouns\/([^/]+)\/vectors\//);
|
|
250
|
+
if (nounMatch) {
|
|
251
|
+
const type = nounMatch[1];
|
|
690
252
|
this.entityCounts.set(type, (this.entityCounts.get(type) || 0) + 1);
|
|
253
|
+
totalNouns++;
|
|
691
254
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
if (metadata) {
|
|
697
|
-
// VerbMetadata doesn't have verb type - that's in HNSWVerb now
|
|
698
|
-
// Use the verb's type from the HNSWVerb itself
|
|
699
|
-
const type = verb.verb || 'default';
|
|
255
|
+
// Count verbs by type (entities/verbs/{type}/vectors/{shard}/{id}.json)
|
|
256
|
+
const verbMatch = path.match(/^entities\/verbs\/([^/]+)\/vectors\//);
|
|
257
|
+
if (verbMatch) {
|
|
258
|
+
const type = verbMatch[1];
|
|
700
259
|
this.verbCounts.set(type, (this.verbCounts.get(type) || 0) + 1);
|
|
260
|
+
totalVerbs++;
|
|
701
261
|
}
|
|
702
262
|
}
|
|
263
|
+
this.totalNounCount = totalNouns;
|
|
264
|
+
this.totalVerbCount = totalVerbs;
|
|
703
265
|
}
|
|
704
266
|
/**
|
|
705
267
|
* Persist counts to storage - no-op for memory storage
|
|
@@ -713,9 +275,10 @@ export class MemoryStorage extends BaseStorage {
|
|
|
713
275
|
// =============================================
|
|
714
276
|
/**
|
|
715
277
|
* Get vector for a noun
|
|
278
|
+
* v5.4.0: Uses BaseStorage's type-first implementation
|
|
716
279
|
*/
|
|
717
280
|
async getNounVector(id) {
|
|
718
|
-
const noun = this.
|
|
281
|
+
const noun = await this.getNoun(id);
|
|
719
282
|
return noun ? [...noun.vector] : null;
|
|
720
283
|
}
|
|
721
284
|
/**
|