@soulcraft/brainy 3.30.0 → 3.30.2

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.
@@ -9,15 +9,24 @@ import { BaseStorage } from '../baseStorage.js';
9
9
  * Uses Maps to store data in memory
10
10
  */
11
11
  export class MemoryStorage extends BaseStorage {
12
+ // Backward compatibility aliases
13
+ get metadata() {
14
+ return this.objectStore;
15
+ }
16
+ get nounMetadata() {
17
+ return this.objectStore;
18
+ }
19
+ get verbMetadata() {
20
+ return this.objectStore;
21
+ }
12
22
  constructor() {
13
23
  super();
14
24
  // Single map of noun ID to noun
15
25
  this.nouns = new Map();
16
26
  this.verbs = new Map();
17
- this.metadata = new Map();
18
- this.nounMetadata = new Map();
19
- this.verbMetadata = new Map();
20
27
  this.statistics = null;
28
+ // Unified object store for primitive operations (replaces metadata, nounMetadata, verbMetadata)
29
+ this.objectStore = new Map();
21
30
  }
22
31
  /**
23
32
  * Initialize the storage adapter
@@ -412,20 +421,43 @@ export class MemoryStorage extends BaseStorage {
412
421
  this.verbs.delete(id);
413
422
  }
414
423
  /**
415
- * Save metadata to storage
424
+ * Primitive operation: Write object to path
425
+ * All metadata operations use this internally via base class routing
416
426
  */
417
- async saveMetadata(id, metadata) {
418
- this.metadata.set(id, JSON.parse(JSON.stringify(metadata)));
427
+ async writeObjectToPath(path, data) {
428
+ // Store in unified object store using path as key
429
+ this.objectStore.set(path, JSON.parse(JSON.stringify(data)));
419
430
  }
420
431
  /**
421
- * Get metadata from storage
432
+ * Primitive operation: Read object from path
433
+ * All metadata operations use this internally via base class routing
422
434
  */
423
- async getMetadata(id) {
424
- const metadata = this.metadata.get(id);
425
- if (!metadata) {
435
+ async readObjectFromPath(path) {
436
+ const data = this.objectStore.get(path);
437
+ if (!data) {
426
438
  return null;
427
439
  }
428
- return JSON.parse(JSON.stringify(metadata));
440
+ return JSON.parse(JSON.stringify(data));
441
+ }
442
+ /**
443
+ * Primitive operation: Delete object from path
444
+ * All metadata operations use this internally via base class routing
445
+ */
446
+ async deleteObjectFromPath(path) {
447
+ this.objectStore.delete(path);
448
+ }
449
+ /**
450
+ * Primitive operation: List objects under path prefix
451
+ * All metadata operations use this internally via base class routing
452
+ */
453
+ async listObjectsUnderPath(prefix) {
454
+ const paths = [];
455
+ for (const key of this.objectStore.keys()) {
456
+ if (key.startsWith(prefix)) {
457
+ paths.push(key);
458
+ }
459
+ }
460
+ return paths.sort();
429
461
  }
430
462
  /**
431
463
  * Get multiple metadata objects in batches (CRITICAL: Prevents socket exhaustion)
@@ -435,61 +467,20 @@ export class MemoryStorage extends BaseStorage {
435
467
  const results = new Map();
436
468
  // Memory storage can handle all IDs at once since it's in-memory
437
469
  for (const id of ids) {
438
- const metadata = this.metadata.get(id);
470
+ const metadata = await this.getMetadata(id);
439
471
  if (metadata) {
440
- // Deep clone to prevent mutation
441
- results.set(id, JSON.parse(JSON.stringify(metadata)));
472
+ results.set(id, metadata);
442
473
  }
443
474
  }
444
475
  return results;
445
476
  }
446
- /**
447
- * Save noun metadata to storage (internal implementation)
448
- */
449
- async saveNounMetadata_internal(id, metadata) {
450
- this.nounMetadata.set(id, JSON.parse(JSON.stringify(metadata)));
451
- }
452
- /**
453
- * Get noun metadata from storage
454
- */
455
- async getNounMetadata(id) {
456
- const metadata = this.nounMetadata.get(id);
457
- if (!metadata) {
458
- return null;
459
- }
460
- return JSON.parse(JSON.stringify(metadata));
461
- }
462
- /**
463
- * Save verb metadata to storage (internal implementation)
464
- */
465
- async saveVerbMetadata_internal(id, metadata) {
466
- const isNew = !this.verbMetadata.has(id);
467
- this.verbMetadata.set(id, JSON.parse(JSON.stringify(metadata)));
468
- // Update counts for new verbs
469
- if (isNew) {
470
- const type = metadata?.verb || metadata?.type || 'default';
471
- this.incrementVerbCount(type);
472
- }
473
- }
474
- /**
475
- * Get verb metadata from storage
476
- */
477
- async getVerbMetadata(id) {
478
- const metadata = this.verbMetadata.get(id);
479
- if (!metadata) {
480
- return null;
481
- }
482
- return JSON.parse(JSON.stringify(metadata));
483
- }
484
477
  /**
485
478
  * Clear all data from storage
486
479
  */
487
480
  async clear() {
488
481
  this.nouns.clear();
489
482
  this.verbs.clear();
490
- this.metadata.clear();
491
- this.nounMetadata.clear();
492
- this.verbMetadata.clear();
483
+ this.objectStore.clear();
493
484
  this.statistics = null;
494
485
  // Clear the statistics cache
495
486
  this.statisticsCache = null;
@@ -506,7 +497,7 @@ export class MemoryStorage extends BaseStorage {
506
497
  details: {
507
498
  nodeCount: this.nouns.size,
508
499
  edgeCount: this.verbs.size,
509
- metadataCount: this.metadata.size
500
+ metadataCount: this.objectStore.size
510
501
  }
511
502
  };
512
503
  }
@@ -129,34 +129,30 @@ export declare class OPFSStorage extends BaseStorage {
129
129
  */
130
130
  protected deleteEdge(id: string): Promise<void>;
131
131
  /**
132
- * Save metadata to storage
132
+ * Primitive operation: Write object to path
133
+ * All metadata operations use this internally via base class routing
133
134
  */
134
- saveMetadata(id: string, metadata: any): Promise<void>;
135
+ protected writeObjectToPath(path: string, data: any): Promise<void>;
135
136
  /**
136
- * Get metadata from storage
137
+ * Primitive operation: Read object from path
138
+ * All metadata operations use this internally via base class routing
137
139
  */
138
- getMetadata(id: string): Promise<any | null>;
140
+ protected readObjectFromPath(path: string): Promise<any | null>;
139
141
  /**
140
- * Get multiple metadata objects in batches (CRITICAL: Prevents socket exhaustion)
141
- * OPFS implementation uses controlled concurrency for file operations
142
- */
143
- getMetadataBatch(ids: string[]): Promise<Map<string, any>>;
144
- /**
145
- * Save verb metadata to storage
142
+ * Primitive operation: Delete object from path
143
+ * All metadata operations use this internally via base class routing
146
144
  */
147
- protected saveVerbMetadata_internal(id: string, metadata: any): Promise<void>;
145
+ protected deleteObjectFromPath(path: string): Promise<void>;
148
146
  /**
149
- * Get verb metadata from storage
147
+ * Primitive operation: List objects under path prefix
148
+ * All metadata operations use this internally via base class routing
150
149
  */
151
- getVerbMetadata(id: string): Promise<any | null>;
150
+ protected listObjectsUnderPath(prefix: string): Promise<string[]>;
152
151
  /**
153
- * Save noun metadata to storage
154
- */
155
- protected saveNounMetadata_internal(id: string, metadata: any): Promise<void>;
156
- /**
157
- * Get noun metadata from storage
152
+ * Get multiple metadata objects in batches (CRITICAL: Prevents socket exhaustion)
153
+ * OPFS implementation uses controlled concurrency for file operations
158
154
  */
159
- getNounMetadata(id: string): Promise<any | null>;
155
+ getMetadataBatch(ids: string[]): Promise<Map<string, any>>;
160
156
  /**
161
157
  * Clear all data from storage
162
158
  */
@@ -511,43 +511,132 @@ export class OPFSStorage extends BaseStorage {
511
511
  }
512
512
  }
513
513
  /**
514
- * Save metadata to storage
514
+ * Primitive operation: Write object to path
515
+ * All metadata operations use this internally via base class routing
515
516
  */
516
- async saveMetadata(id, metadata) {
517
+ async writeObjectToPath(path, data) {
517
518
  await this.ensureInitialized();
518
519
  try {
519
- // Create or get the file for this metadata
520
- const fileHandle = await this.metadataDir.getFileHandle(`${id}.json`, {
521
- create: true
522
- });
523
- // Write the metadata to the file
520
+ // Parse path to get directory structure and filename
521
+ // Path format: "dir1/dir2/file.json"
522
+ const parts = path.split('/');
523
+ const filename = parts.pop();
524
+ // Navigate to the correct directory, creating as needed
525
+ let currentDir = this.rootDir;
526
+ for (const dirName of parts) {
527
+ currentDir = await currentDir.getDirectoryHandle(dirName, { create: true });
528
+ }
529
+ // Create or get the file
530
+ const fileHandle = await currentDir.getFileHandle(filename, { create: true });
531
+ // Write the data to the file
524
532
  const writable = await fileHandle.createWritable();
525
- await writable.write(JSON.stringify(metadata));
533
+ await writable.write(JSON.stringify(data, null, 2));
526
534
  await writable.close();
527
535
  }
528
536
  catch (error) {
529
- console.error(`Failed to save metadata ${id}:`, error);
530
- throw new Error(`Failed to save metadata ${id}: ${error}`);
537
+ console.error(`Failed to write object to ${path}:`, error);
538
+ throw new Error(`Failed to write object to ${path}: ${error}`);
531
539
  }
532
540
  }
533
541
  /**
534
- * Get metadata from storage
542
+ * Primitive operation: Read object from path
543
+ * All metadata operations use this internally via base class routing
535
544
  */
536
- async getMetadata(id) {
545
+ async readObjectFromPath(path) {
537
546
  await this.ensureInitialized();
538
547
  try {
539
- // Get the file handle for this metadata
540
- const fileHandle = await this.metadataDir.getFileHandle(`${id}.json`);
541
- // Read the metadata from the file
548
+ // Parse path to get directory structure and filename
549
+ const parts = path.split('/');
550
+ const filename = parts.pop();
551
+ // Navigate to the correct directory
552
+ let currentDir = this.rootDir;
553
+ for (const dirName of parts) {
554
+ currentDir = await currentDir.getDirectoryHandle(dirName);
555
+ }
556
+ // Get the file handle
557
+ const fileHandle = await currentDir.getFileHandle(filename);
558
+ // Read the data from the file
542
559
  const file = await fileHandle.getFile();
543
560
  const text = await file.text();
544
561
  return JSON.parse(text);
545
562
  }
546
563
  catch (error) {
547
- // Metadata not found or other error
564
+ // NotFoundError means object doesn't exist
565
+ if (error.name === 'NotFoundError') {
566
+ return null;
567
+ }
568
+ console.error(`Failed to read object from ${path}:`, error);
548
569
  return null;
549
570
  }
550
571
  }
572
+ /**
573
+ * Primitive operation: Delete object from path
574
+ * All metadata operations use this internally via base class routing
575
+ */
576
+ async deleteObjectFromPath(path) {
577
+ await this.ensureInitialized();
578
+ try {
579
+ // Parse path to get directory structure and filename
580
+ const parts = path.split('/');
581
+ const filename = parts.pop();
582
+ // Navigate to the correct directory
583
+ let currentDir = this.rootDir;
584
+ for (const dirName of parts) {
585
+ currentDir = await currentDir.getDirectoryHandle(dirName);
586
+ }
587
+ // Delete the file
588
+ await currentDir.removeEntry(filename);
589
+ }
590
+ catch (error) {
591
+ // NotFoundError is ok (already deleted)
592
+ if (error.name === 'NotFoundError') {
593
+ return;
594
+ }
595
+ console.error(`Failed to delete object from ${path}:`, error);
596
+ throw new Error(`Failed to delete object from ${path}: ${error}`);
597
+ }
598
+ }
599
+ /**
600
+ * Primitive operation: List objects under path prefix
601
+ * All metadata operations use this internally via base class routing
602
+ */
603
+ async listObjectsUnderPath(prefix) {
604
+ await this.ensureInitialized();
605
+ try {
606
+ const paths = [];
607
+ // Parse prefix to get directory structure
608
+ const parts = prefix.split('/');
609
+ // Navigate to the directory
610
+ let currentDir = this.rootDir;
611
+ for (const dirName of parts) {
612
+ if (dirName) {
613
+ currentDir = await currentDir.getDirectoryHandle(dirName);
614
+ }
615
+ }
616
+ // Recursively list all files
617
+ const listFiles = async (dir, pathPrefix) => {
618
+ for await (const [name, handle] of dir.entries()) {
619
+ const fullPath = pathPrefix ? `${pathPrefix}/${name}` : name;
620
+ if (handle.kind === 'file') {
621
+ paths.push(`${prefix}${fullPath}`);
622
+ }
623
+ else if (handle.kind === 'directory') {
624
+ await listFiles(handle, fullPath);
625
+ }
626
+ }
627
+ };
628
+ await listFiles(currentDir, '');
629
+ return paths;
630
+ }
631
+ catch (error) {
632
+ // NotFoundError means directory doesn't exist
633
+ if (error.name === 'NotFoundError') {
634
+ return [];
635
+ }
636
+ console.error(`Failed to list objects under ${prefix}:`, error);
637
+ throw new Error(`Failed to list objects under ${prefix}: ${error}`);
638
+ }
639
+ }
551
640
  /**
552
641
  * Get multiple metadata objects in batches (CRITICAL: Prevents socket exhaustion)
553
642
  * OPFS implementation uses controlled concurrency for file operations
@@ -580,86 +669,6 @@ export class OPFSStorage extends BaseStorage {
580
669
  }
581
670
  return results;
582
671
  }
583
- /**
584
- * Save verb metadata to storage
585
- */
586
- async saveVerbMetadata_internal(id, metadata) {
587
- await this.ensureInitialized();
588
- // Use UUID-based sharding for metadata (consistent with verb vectors)
589
- const shardId = getShardIdFromUuid(id);
590
- // Get or create the shard directory
591
- const shardDir = await this.verbMetadataDir.getDirectoryHandle(shardId, { create: true });
592
- // Create or get the file in the shard directory
593
- const fileName = `${id}.json`;
594
- const fileHandle = await shardDir.getFileHandle(fileName, { create: true });
595
- const writable = await fileHandle.createWritable();
596
- await writable.write(JSON.stringify(metadata, null, 2));
597
- await writable.close();
598
- }
599
- /**
600
- * Get verb metadata from storage
601
- */
602
- async getVerbMetadata(id) {
603
- await this.ensureInitialized();
604
- // Use UUID-based sharding for metadata (consistent with verb vectors)
605
- const shardId = getShardIdFromUuid(id);
606
- const fileName = `${id}.json`;
607
- try {
608
- // Get the shard directory
609
- const shardDir = await this.verbMetadataDir.getDirectoryHandle(shardId);
610
- // Get the file from the shard directory
611
- const fileHandle = await shardDir.getFileHandle(fileName);
612
- const file = await safeGetFile(fileHandle);
613
- const text = await file.text();
614
- return JSON.parse(text);
615
- }
616
- catch (error) {
617
- if (error.name !== 'NotFoundError') {
618
- console.error(`Error reading verb metadata ${id}:`, error);
619
- }
620
- return null;
621
- }
622
- }
623
- /**
624
- * Save noun metadata to storage
625
- */
626
- async saveNounMetadata_internal(id, metadata) {
627
- await this.ensureInitialized();
628
- // Use UUID-based sharding for metadata (consistent with noun vectors)
629
- const shardId = getShardIdFromUuid(id);
630
- // Get or create the shard directory
631
- const shardDir = await this.nounMetadataDir.getDirectoryHandle(shardId, { create: true });
632
- // Create or get the file in the shard directory
633
- const fileName = `${id}.json`;
634
- const fileHandle = await shardDir.getFileHandle(fileName, { create: true });
635
- const writable = await fileHandle.createWritable();
636
- await writable.write(JSON.stringify(metadata, null, 2));
637
- await writable.close();
638
- }
639
- /**
640
- * Get noun metadata from storage
641
- */
642
- async getNounMetadata(id) {
643
- await this.ensureInitialized();
644
- // Use UUID-based sharding for metadata (consistent with noun vectors)
645
- const shardId = getShardIdFromUuid(id);
646
- const fileName = `${id}.json`;
647
- try {
648
- // Get the shard directory
649
- const shardDir = await this.nounMetadataDir.getDirectoryHandle(shardId);
650
- // Get the file from the shard directory
651
- const fileHandle = await shardDir.getFileHandle(fileName);
652
- const file = await safeGetFile(fileHandle);
653
- const text = await file.text();
654
- return JSON.parse(text);
655
- }
656
- catch (error) {
657
- if (error.name !== 'NotFoundError') {
658
- console.error(`Error reading noun metadata ${id}:`, error);
659
- }
660
- return null;
661
- }
662
- }
663
672
  /**
664
673
  * Clear all data from storage
665
674
  */
@@ -377,21 +377,25 @@ export declare class S3CompatibleStorage extends BaseStorage {
377
377
  */
378
378
  protected deleteEdge(id: string): Promise<void>;
379
379
  /**
380
- * Save metadata to storage
380
+ * Primitive operation: Write object to path
381
+ * All metadata operations use this internally via base class routing
381
382
  */
382
- saveMetadata(id: string, metadata: any): Promise<void>;
383
+ protected writeObjectToPath(path: string, data: any): Promise<void>;
383
384
  /**
384
- * Save verb metadata to storage
385
+ * Primitive operation: Read object from path
386
+ * All metadata operations use this internally via base class routing
385
387
  */
386
- protected saveVerbMetadata_internal(id: string, metadata: any): Promise<void>;
388
+ protected readObjectFromPath(path: string): Promise<any | null>;
387
389
  /**
388
- * Get verb metadata from storage
390
+ * Primitive operation: Delete object from path
391
+ * All metadata operations use this internally via base class routing
389
392
  */
390
- getVerbMetadata(id: string): Promise<any | null>;
393
+ protected deleteObjectFromPath(path: string): Promise<void>;
391
394
  /**
392
- * Save noun metadata to storage
395
+ * Primitive operation: List objects under path prefix
396
+ * All metadata operations use this internally via base class routing
393
397
  */
394
- protected saveNounMetadata_internal(id: string, metadata: any): Promise<void>;
398
+ protected listObjectsUnderPath(prefix: string): Promise<string[]>;
395
399
  /**
396
400
  * Get multiple metadata objects in batches (CRITICAL: Prevents socket exhaustion)
397
401
  * This is the solution to the metadata reading socket exhaustion during initialization
@@ -401,14 +405,6 @@ export declare class S3CompatibleStorage extends BaseStorage {
401
405
  * Get multiple verb metadata objects in batches (prevents socket exhaustion)
402
406
  */
403
407
  getVerbMetadataBatch(ids: string[]): Promise<Map<string, any>>;
404
- /**
405
- * Get noun metadata from storage
406
- */
407
- getNounMetadata(id: string): Promise<any | null>;
408
- /**
409
- * Get metadata from storage
410
- */
411
- getMetadata(id: string): Promise<any | null>;
412
408
  /**
413
409
  * Clear all data from storage
414
410
  */