@soulcraft/brainy 0.9.37 → 0.11.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/dist/unified.js CHANGED
@@ -2398,133 +2398,84 @@ ieee754.write = function (buffer, value, offset, isLE, mLen, nBytes) {
2398
2398
  * Unified Text Encoding Utilities
2399
2399
  *
2400
2400
  * This module provides a consistent way to handle text encoding/decoding across all environments
2401
- * without relying on TextEncoder/TextDecoder polyfills or patches.
2401
+ * using the native TextEncoder/TextDecoder APIs.
2402
2402
  */
2403
- /**
2404
- * A simple text encoder that works in all environments
2405
- * This avoids the need for TextEncoder polyfills and patches
2406
- */
2407
- class SimpleTextEncoder {
2408
- /**
2409
- * Encode a string to a Uint8Array
2410
- * @param input - The string to encode
2411
- * @returns A Uint8Array containing the encoded string
2412
- */
2413
- encode(input) {
2414
- // Simple UTF-8 encoding implementation that works everywhere
2415
- return new Uint8Array([...input].map((c) => c.charCodeAt(0)));
2416
- }
2417
- }
2418
- /**
2419
- * A simple text decoder that works in all environments
2420
- * This avoids the need for TextDecoder polyfills and patches
2421
- */
2422
- class SimpleTextDecoder {
2423
- /**
2424
- * Decode a Uint8Array to a string
2425
- * @param input - The Uint8Array to decode
2426
- * @returns The decoded string
2427
- */
2428
- decode(input) {
2429
- // Simple UTF-8 decoding implementation that works everywhere
2430
- return String.fromCharCode.apply(null, [...input]);
2431
- }
2432
- }
2433
- /**
2434
- * A constructor function for TextEncoder that works in all environments
2435
- */
2436
- function UniversalTextEncoder() {
2437
- if (!(this instanceof UniversalTextEncoder)) {
2438
- return new UniversalTextEncoder();
2439
- }
2440
- try {
2441
- // Try to use the native TextEncoder if available
2442
- const nativeEncoder = new TextEncoder();
2443
- this.encode = nativeEncoder.encode.bind(nativeEncoder);
2444
- }
2445
- catch (e) {
2446
- // Fall back to our simple implementation
2447
- const simpleEncoder = new SimpleTextEncoder();
2448
- this.encode = simpleEncoder.encode.bind(simpleEncoder);
2449
- }
2450
- }
2451
- /**
2452
- * A constructor function for TextDecoder that works in all environments
2453
- */
2454
- function UniversalTextDecoder() {
2455
- if (!(this instanceof UniversalTextDecoder)) {
2456
- return new UniversalTextDecoder();
2457
- }
2458
- try {
2459
- // Try to use the native TextDecoder if available
2460
- const nativeDecoder = new TextDecoder();
2461
- this.decode = nativeDecoder.decode.bind(nativeDecoder);
2462
- }
2463
- catch (e) {
2464
- // Fall back to our simple implementation
2465
- const simpleDecoder = new SimpleTextDecoder();
2466
- this.decode = simpleDecoder.decode.bind(simpleDecoder);
2467
- }
2468
- }
2469
2403
  /**
2470
2404
  * Get a text encoder that works in the current environment
2471
- * @returns A text encoder object with an encode method
2405
+ * @returns A TextEncoder instance
2472
2406
  */
2473
- function getTextEncoder() {
2474
- return new UniversalTextEncoder();
2475
- }
2476
- /**
2477
- * Get a text decoder that works in the current environment
2478
- * @returns A text decoder object with a decode method
2479
- */
2480
- function getTextDecoder() {
2481
- return new UniversalTextDecoder();
2482
- }
2483
2407
  /**
2484
2408
  * Apply the TensorFlow.js platform patch if needed
2485
2409
  * This function patches the global object to provide a PlatformNode class
2486
- * that uses our text encoding utilities instead of relying on TextEncoder/TextDecoder
2410
+ * that uses native TextEncoder/TextDecoder
2487
2411
  */
2488
2412
  function applyTensorFlowPatch() {
2489
- // Only apply in Node.js environment
2490
- if (typeof global !== 'undefined' &&
2491
- typeof process !== 'undefined' &&
2492
- process.versions &&
2493
- process.versions.node) {
2494
- try {
2495
- // Get encoders/decoders
2496
- const encoder = getTextEncoder();
2497
- const decoder = getTextDecoder();
2498
- // Define a custom PlatformNode class
2499
- class PlatformNode {
2500
- constructor() {
2501
- // Create a util object with necessary methods and constructors
2502
- this.util = {
2503
- isFloat32Array: (arr) => !!(arr instanceof Float32Array ||
2504
- (arr &&
2505
- Object.prototype.toString.call(arr) ===
2506
- '[object Float32Array]')),
2507
- isTypedArray: (arr) => !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)),
2508
- // Add TextEncoder and TextDecoder as constructors
2509
- TextEncoder: UniversalTextEncoder,
2510
- TextDecoder: UniversalTextDecoder
2511
- };
2512
- // Initialize using the constructors from util
2513
- this.textEncoder = new TextEncoder();
2514
- this.textDecoder = new TextDecoder();
2515
- }
2516
- }
2517
- // Assign the PlatformNode class to the global object
2413
+ try {
2414
+ // Define a custom Platform class that works in both Node.js and browser environments
2415
+ class Platform {
2416
+ constructor() {
2417
+ // Create a util object with necessary methods and constructors
2418
+ this.util = {
2419
+ // Use native TextEncoder and TextDecoder
2420
+ TextEncoder: globalThis.TextEncoder || TextEncoder,
2421
+ TextDecoder: globalThis.TextDecoder || TextDecoder
2422
+ };
2423
+ // Initialize using native constructors directly
2424
+ this.textEncoder = new (globalThis.TextEncoder || TextEncoder)();
2425
+ this.textDecoder = new (globalThis.TextDecoder || TextDecoder)();
2426
+ }
2427
+ // Define isFloat32Array directly on the instance
2428
+ isFloat32Array(arr) {
2429
+ return !!(arr instanceof Float32Array ||
2430
+ (arr &&
2431
+ Object.prototype.toString.call(arr) === '[object Float32Array]'));
2432
+ }
2433
+ // Define isTypedArray directly on the instance
2434
+ isTypedArray(arr) {
2435
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
2436
+ }
2437
+ }
2438
+ // Get the global object in a way that works in both Node.js and browser
2439
+ const globalObj = typeof global !== 'undefined'
2440
+ ? global
2441
+ : typeof window !== 'undefined'
2442
+ ? window
2443
+ : typeof self !== 'undefined'
2444
+ ? self
2445
+ : {};
2446
+ // Only apply in Node.js environment
2447
+ if (typeof process !== 'undefined' &&
2448
+ process.versions &&
2449
+ process.versions.node) {
2450
+ // Assign the Platform class to the global object as PlatformNode for Node.js
2518
2451
  ;
2519
- global.PlatformNode = PlatformNode;
2520
- global.platformNode = new PlatformNode();
2452
+ globalObj.PlatformNode = Platform;
2453
+ globalObj.platformNode = new Platform();
2521
2454
  }
2522
- catch (error) {
2523
- console.warn('Failed to apply TensorFlow.js platform patch:', error);
2455
+ else if (typeof window !== 'undefined' || typeof self !== 'undefined') {
2456
+ // In browser environments, we might need to provide similar functionality
2457
+ // but we'll use a different name to avoid conflicts
2458
+ ;
2459
+ globalObj.PlatformBrowser = Platform;
2460
+ globalObj.platformBrowser = new Platform();
2524
2461
  }
2525
2462
  }
2463
+ catch (error) {
2464
+ console.warn('Failed to apply TensorFlow.js platform patch:', error);
2465
+ }
2526
2466
  }
2527
2467
 
2468
+ /**
2469
+ * This file is imported for its side effects to patch the environment
2470
+ * for TensorFlow.js before any other library code runs.
2471
+ *
2472
+ * It ensures that by the time TensorFlow.js is imported by any other
2473
+ * module, the necessary compatibility fixes for the current Node.js
2474
+ * environment are already in place.
2475
+ */
2476
+ // Apply the TensorFlow.js platform patch if needed
2477
+ applyTensorFlowPatch();
2478
+
2528
2479
  // Unique ID creation requires a high quality random # generator. In the browser we therefore
2529
2480
  // require the crypto API and do not support built-in fallback to lower quality random number
2530
2481
  // generators (like Math.random()).
@@ -2853,7 +2804,7 @@ async function areWorkerThreadsAvailable() {
2853
2804
  function areWorkerThreadsAvailableSync() {
2854
2805
  if (!isNode())
2855
2806
  return false;
2856
- // In Node.js 24.3.0+, worker_threads is always available
2807
+ // In Node.js 24.4.0+, worker_threads is always available
2857
2808
  return parseInt(process.versions.node.split('.')[0]) >= 24;
2858
2809
  }
2859
2810
  /**
@@ -2913,9 +2864,16 @@ function executeInThread(fnString, args) {
2913
2864
  fn = new Function(fnString)();
2914
2865
  }
2915
2866
  catch (directError) {
2916
- console.error('Fallback: All approaches to create function failed', directError);
2917
- throw new Error('Failed to create function from string: ' +
2918
- functionError.message);
2867
+ console.warn('Fallback: Direct approach failed, trying with function wrapper', directError);
2868
+ try {
2869
+ // Try wrapping in a function that returns the function expression
2870
+ fn = new Function('return function(args) { return (' + fnString + ')(args); }')();
2871
+ }
2872
+ catch (wrapperError) {
2873
+ console.error('Fallback: All approaches to create function failed', wrapperError);
2874
+ throw new Error('Failed to create function from string: ' +
2875
+ functionError.message);
2876
+ }
2919
2877
  }
2920
2878
  }
2921
2879
  }
@@ -2950,6 +2908,82 @@ function executeInNodeWorker(fnString, args) {
2950
2908
  // Create a new worker
2951
2909
  worker = new Worker(`
2952
2910
  import { parentPort, workerData } from 'node:worker_threads';
2911
+
2912
+ // Add TensorFlow.js platform patch for Node.js
2913
+ if (typeof global !== 'undefined') {
2914
+ try {
2915
+ // Define a custom PlatformNode class
2916
+ class PlatformNode {
2917
+ constructor() {
2918
+ // Create a util object with necessary methods
2919
+ this.util = {
2920
+ // Add isFloat32Array and isTypedArray directly to util
2921
+ isFloat32Array: (arr) => {
2922
+ return !!(
2923
+ arr instanceof Float32Array ||
2924
+ (arr &&
2925
+ Object.prototype.toString.call(arr) === '[object Float32Array]')
2926
+ );
2927
+ },
2928
+ isTypedArray: (arr) => {
2929
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
2930
+ },
2931
+ // Use native TextEncoder and TextDecoder
2932
+ TextEncoder: TextEncoder,
2933
+ TextDecoder: TextDecoder
2934
+ };
2935
+
2936
+ // Initialize encoders using native constructors
2937
+ this.textEncoder = new TextEncoder();
2938
+ this.textDecoder = new TextDecoder();
2939
+ }
2940
+
2941
+ // Define isFloat32Array directly on the instance
2942
+ isFloat32Array(arr) {
2943
+ return !!(
2944
+ arr instanceof Float32Array ||
2945
+ (arr && Object.prototype.toString.call(arr) === '[object Float32Array]')
2946
+ );
2947
+ }
2948
+
2949
+ // Define isTypedArray directly on the instance
2950
+ isTypedArray(arr) {
2951
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
2952
+ }
2953
+ }
2954
+
2955
+ // Assign the PlatformNode class to the global object
2956
+ global.PlatformNode = PlatformNode;
2957
+
2958
+ // Also create an instance and assign it to global.platformNode
2959
+ global.platformNode = new PlatformNode();
2960
+
2961
+ // Ensure global.util exists and has the necessary methods
2962
+ if (!global.util) {
2963
+ global.util = {};
2964
+ }
2965
+
2966
+ // Add isFloat32Array method if it doesn't exist
2967
+ if (!global.util.isFloat32Array) {
2968
+ global.util.isFloat32Array = (arr) => {
2969
+ return !!(
2970
+ arr instanceof Float32Array ||
2971
+ (arr && Object.prototype.toString.call(arr) === '[object Float32Array]')
2972
+ );
2973
+ };
2974
+ }
2975
+
2976
+ // Add isTypedArray method if it doesn't exist
2977
+ if (!global.util.isTypedArray) {
2978
+ global.util.isTypedArray = (arr) => {
2979
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
2980
+ };
2981
+ }
2982
+ } catch (error) {
2983
+ console.warn('Failed to apply TensorFlow.js platform patch:', error);
2984
+ }
2985
+ }
2986
+
2953
2987
  const fn = new Function('return ' + workerData.fnString)();
2954
2988
  const result = fn(workerData.args);
2955
2989
  parentPort.postMessage({ result });
@@ -2969,6 +3003,71 @@ function executeInNodeWorker(fnString, args) {
2969
3003
  worker.terminate();
2970
3004
  worker = new Worker(`
2971
3005
  import { parentPort, workerData } from 'node:worker_threads';
3006
+
3007
+ // Add TensorFlow.js platform patch for Node.js
3008
+ if (typeof global !== 'undefined') {
3009
+ try {
3010
+ // Define a custom PlatformNode class
3011
+ class PlatformNode {
3012
+ constructor() {
3013
+ // Create a util object with necessary methods
3014
+ this.util = {
3015
+ // Use native TextEncoder and TextDecoder
3016
+ TextEncoder: TextEncoder,
3017
+ TextDecoder: TextDecoder
3018
+ };
3019
+
3020
+ // Initialize encoders using native constructors
3021
+ this.textEncoder = new TextEncoder();
3022
+ this.textDecoder = new TextDecoder();
3023
+ }
3024
+
3025
+ // Define isFloat32Array directly on the instance
3026
+ isFloat32Array(arr) {
3027
+ return !!(
3028
+ arr instanceof Float32Array ||
3029
+ (arr && Object.prototype.toString.call(arr) === '[object Float32Array]')
3030
+ );
3031
+ }
3032
+
3033
+ // Define isTypedArray directly on the instance
3034
+ isTypedArray(arr) {
3035
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
3036
+ }
3037
+ }
3038
+
3039
+ // Assign the PlatformNode class to the global object
3040
+ global.PlatformNode = PlatformNode;
3041
+
3042
+ // Also create an instance and assign it to global.platformNode
3043
+ global.platformNode = new PlatformNode();
3044
+
3045
+ // Ensure global.util exists and has the necessary methods
3046
+ if (!global.util) {
3047
+ global.util = {};
3048
+ }
3049
+
3050
+ // Add isFloat32Array method if it doesn't exist
3051
+ if (!global.util.isFloat32Array) {
3052
+ global.util.isFloat32Array = (arr) => {
3053
+ return !!(
3054
+ arr instanceof Float32Array ||
3055
+ (arr && Object.prototype.toString.call(arr) === '[object Float32Array]')
3056
+ );
3057
+ };
3058
+ }
3059
+
3060
+ // Add isTypedArray method if it doesn't exist
3061
+ if (!global.util.isTypedArray) {
3062
+ global.util.isTypedArray = (arr) => {
3063
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
3064
+ };
3065
+ }
3066
+ } catch (error) {
3067
+ console.warn('Failed to apply TensorFlow.js platform patch:', error);
3068
+ }
3069
+ }
3070
+
2972
3071
  const fn = new Function('return ' + workerData.fnString)();
2973
3072
  const result = fn(workerData.args);
2974
3073
  parentPort.postMessage({ result });
@@ -3209,7 +3308,71 @@ let UniversalSentenceEncoder$1 = class UniversalSentenceEncoder {
3209
3308
  !process.versions.node) {
3210
3309
  return;
3211
3310
  }
3212
- // No compatibility patches needed - TensorFlow.js now works correctly with Node.js 24+
3311
+ // Add polyfill for isFloat32Array in Node.js 24.4.0
3312
+ // This fixes the "Cannot read properties of undefined (reading 'isFloat32Array')" error
3313
+ if (typeof global !== 'undefined') {
3314
+ try {
3315
+ // Define a custom PlatformNode class
3316
+ class PlatformNode {
3317
+ constructor() {
3318
+ // Create a util object with necessary methods
3319
+ this.util = {
3320
+ // Add isFloat32Array and isTypedArray directly to util
3321
+ isFloat32Array: (arr) => {
3322
+ return !!(arr instanceof Float32Array ||
3323
+ (arr &&
3324
+ Object.prototype.toString.call(arr) ===
3325
+ '[object Float32Array]'));
3326
+ },
3327
+ isTypedArray: (arr) => {
3328
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
3329
+ },
3330
+ // Use native TextEncoder and TextDecoder
3331
+ TextEncoder: TextEncoder,
3332
+ TextDecoder: TextDecoder
3333
+ };
3334
+ // Initialize encoders using native constructors
3335
+ this.textEncoder = new TextEncoder();
3336
+ this.textDecoder = new TextDecoder();
3337
+ }
3338
+ // Define isFloat32Array directly on the instance
3339
+ isFloat32Array(arr) {
3340
+ return !!(arr instanceof Float32Array ||
3341
+ (arr &&
3342
+ Object.prototype.toString.call(arr) === '[object Float32Array]'));
3343
+ }
3344
+ // Define isTypedArray directly on the instance
3345
+ isTypedArray(arr) {
3346
+ return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView));
3347
+ }
3348
+ }
3349
+ // Assign the PlatformNode class to the global object
3350
+ ;
3351
+ global.PlatformNode = PlatformNode;
3352
+ global.platformNode = new PlatformNode();
3353
+ }
3354
+ catch (error) {
3355
+ console.warn('Failed to define global PlatformNode class:', error);
3356
+ }
3357
+ // Ensure the util object exists
3358
+ if (!global.util) {
3359
+ global.util = {};
3360
+ }
3361
+ // Add isFloat32Array method if it doesn't exist
3362
+ if (!global.util.isFloat32Array) {
3363
+ global.util.isFloat32Array = (obj) => {
3364
+ return !!(obj instanceof Float32Array ||
3365
+ (obj &&
3366
+ Object.prototype.toString.call(obj) === '[object Float32Array]'));
3367
+ };
3368
+ }
3369
+ // Add isTypedArray method if it doesn't exist
3370
+ if (!global.util.isTypedArray) {
3371
+ global.util.isTypedArray = (obj) => {
3372
+ return !!(ArrayBuffer.isView(obj) && !(obj instanceof DataView));
3373
+ };
3374
+ }
3375
+ }
3213
3376
  }
3214
3377
  /**
3215
3378
  * Initialize the embedding model
@@ -9625,12 +9788,40 @@ class BrainyData {
9625
9788
  }
9626
9789
  }
9627
9790
 
9628
- // Import Node.js built-in modules
9629
- // Using dynamic imports for compatibility with ES modules
9791
+ // Dynamically and asynchronously load Node.js modules at the top level.
9792
+ // This ensures they are available as soon as this module is imported,
9793
+ // preventing race conditions with dependencies like TensorFlow.js.
9630
9794
  let fs;
9631
9795
  let path;
9632
- // We'll initialize these modules in the init() method
9633
- // No synchronous loading here to avoid issues with ES modules
9796
+ const nodeModulesPromise = (async () => {
9797
+ // A reliable check for a Node.js environment.
9798
+ const isNode = typeof process !== 'undefined' &&
9799
+ process.versions != null &&
9800
+ process.versions.node != null;
9801
+ if (!isNode) {
9802
+ return { fs: null, path: null };
9803
+ }
9804
+ try {
9805
+ // Use the 'node:' prefix for unambiguous importing of built-in modules.
9806
+ const fsModule = await import('node:fs');
9807
+ const pathModule = await import('node:path');
9808
+ // Return the modules, preferring the default export if it exists.
9809
+ return {
9810
+ fs: fsModule.default || fsModule,
9811
+ path: pathModule.default || pathModule
9812
+ };
9813
+ }
9814
+ catch (error) {
9815
+ console.error('FileSystemStorage: Failed to load Node.js modules. This adapter is not supported in this environment.', error);
9816
+ return { fs: null, path: null };
9817
+ }
9818
+ })();
9819
+ // Immediately assign the modules once the promise resolves.
9820
+ nodeModulesPromise.then((modules) => {
9821
+ fs = modules.fs;
9822
+ path = modules.path;
9823
+ });
9824
+ // --- End of Refactored Code ---
9634
9825
  // Constants for directory and file names
9635
9826
  const ROOT_DIR = 'brainy-data';
9636
9827
  const NOUNS_DIR = 'nouns';
@@ -9670,58 +9861,13 @@ class FileSystemStorage {
9670
9861
  if (this.isInitialized) {
9671
9862
  return;
9672
9863
  }
9864
+ // Wait for the top-level module loading to complete.
9865
+ await nodeModulesPromise;
9866
+ // Check if the modules were loaded successfully.
9867
+ if (!fs || !path) {
9868
+ throw new Error('FileSystemStorage requires a Node.js environment, but `fs` and `path` modules could not be loaded.');
9869
+ }
9673
9870
  try {
9674
- // Check if fs and path modules are available and have required methods
9675
- if (!fs || !path || typeof path.resolve !== 'function') {
9676
- console.log('Node.js modules not properly loaded, attempting to load them now');
9677
- // Try multiple approaches to load the modules
9678
- const loadAttempts = [
9679
- // Attempt 1: Use dynamic import
9680
- async () => {
9681
- console.log('Attempting to load Node.js modules with dynamic import');
9682
- const fsModule = await import('fs');
9683
- const pathModule = await import('path');
9684
- const fsResolved = fsModule.default || fsModule;
9685
- const pathResolved = pathModule.default || pathModule;
9686
- if (!pathResolved || typeof pathResolved.resolve !== 'function') {
9687
- throw new Error('path.resolve is not a function after dynamic import');
9688
- }
9689
- return { fs: fsResolved, path: pathResolved };
9690
- },
9691
- // Attempt 2: Use dynamic import with node: prefix
9692
- async () => {
9693
- console.log('Attempting to load Node.js modules with dynamic import("node:...")');
9694
- const fsModule = await import('node:fs');
9695
- const pathModule = await import('node:path');
9696
- const fsResolved = fsModule.default || fsModule;
9697
- const pathResolved = pathModule.default || pathModule;
9698
- if (!pathResolved || typeof pathResolved.resolve !== 'function') {
9699
- throw new Error('path.resolve is not a function after dynamic import("node:path")');
9700
- }
9701
- return { fs: fsResolved, path: pathResolved };
9702
- }
9703
- ];
9704
- // Try each loading method until one succeeds
9705
- let lastError = null;
9706
- for (const loadAttempt of loadAttempts) {
9707
- try {
9708
- const modules = await loadAttempt();
9709
- fs = modules.fs;
9710
- path = modules.path;
9711
- console.log('Successfully loaded Node.js modules');
9712
- break;
9713
- }
9714
- catch (error) {
9715
- lastError = error;
9716
- console.warn(`Module loading attempt failed:`, error);
9717
- // Continue to the next attempt
9718
- }
9719
- }
9720
- // If all attempts failed, throw an error
9721
- if (!fs || !path || typeof path.resolve !== 'function') {
9722
- throw new Error(`Failed to import Node.js modules after multiple attempts: ${lastError}. This adapter requires a Node.js environment.`);
9723
- }
9724
- }
9725
9871
  // Now set up the directory paths
9726
9872
  const rootDir = this.rootDir || process.cwd();
9727
9873
  this.rootDir = path.resolve(rootDir, ROOT_DIR);
@@ -9741,7 +9887,6 @@ class FileSystemStorage {
9741
9887
  await this.ensureDirectoryExists(this.nounsDir);
9742
9888
  await this.ensureDirectoryExists(this.verbsDir);
9743
9889
  await this.ensureDirectoryExists(this.metadataDir);
9744
- // Create noun type directories
9745
9890
  await this.ensureDirectoryExists(this.personDir);
9746
9891
  await this.ensureDirectoryExists(this.placeDir);
9747
9892
  await this.ensureDirectoryExists(this.thingDir);
@@ -9752,736 +9897,349 @@ class FileSystemStorage {
9752
9897
  this.isInitialized = true;
9753
9898
  }
9754
9899
  catch (error) {
9755
- console.error('Failed to initialize file system storage:', error);
9756
- throw new Error(`Failed to initialize file system storage: ${error}`);
9900
+ console.error('Error initializing FileSystemStorage:', error);
9901
+ throw error;
9757
9902
  }
9758
9903
  }
9759
- /**
9760
- * Save a node to storage
9761
- */
9762
- async saveNoun(noun) {
9763
- await this.ensureInitialized();
9904
+ async ensureDirectoryExists(dirPath) {
9764
9905
  try {
9765
- // Convert connections Map to a serializable format
9766
- const serializableNode = {
9767
- ...noun,
9768
- connections: this.mapToObject(noun.connections, (set) => Array.from(set))
9769
- };
9770
- // Get the appropriate directory based on the node's metadata
9771
- const nodeDir = await this.getNodeDirectory(noun.id);
9772
- const filePath = path.join(nodeDir, `${noun.id}.json`);
9773
- await fs.promises.writeFile(filePath, JSON.stringify(serializableNode, null, 2), 'utf8');
9906
+ await fs.promises.mkdir(dirPath, { recursive: true });
9774
9907
  }
9775
9908
  catch (error) {
9776
- console.error(`Failed to save node ${noun.id}:`, error);
9777
- throw new Error(`Failed to save node ${noun.id}: ${error}`);
9778
- }
9779
- }
9780
- /**
9781
- * Get a node from storage
9782
- */
9783
- async getNoun(id) {
9784
- await this.ensureInitialized();
9785
- try {
9786
- // Get the appropriate directory based on the node's metadata
9787
- const nodeDir = await this.getNodeDirectory(id);
9788
- const filePath = path.join(nodeDir, `${id}.json`);
9789
- // Check if a file exists
9790
- try {
9791
- await fs.promises.access(filePath);
9792
- }
9793
- catch {
9794
- // If the file doesn't exist in the expected directory, try the default directory
9795
- if (nodeDir !== this.defaultDir) {
9796
- const defaultFilePath = path.join(this.defaultDir, `${id}.json`);
9797
- try {
9798
- await fs.promises.access(defaultFilePath);
9799
- // If found in default directory, use that path
9800
- const data = await fs.promises.readFile(defaultFilePath, 'utf8');
9801
- const parsedNode = JSON.parse(data);
9802
- // Convert serialized connections back to Map<number, Set<string>>
9803
- const connections = new Map();
9804
- for (const [level, nodeIds] of Object.entries(parsedNode.connections)) {
9805
- connections.set(Number(level), new Set(nodeIds));
9806
- }
9807
- return {
9808
- id: parsedNode.id,
9809
- vector: parsedNode.vector,
9810
- connections
9811
- };
9812
- }
9813
- catch {
9814
- // If not found in default directory either, try all noun type directories
9815
- const directories = [
9816
- this.personDir,
9817
- this.placeDir,
9818
- this.thingDir,
9819
- this.eventDir,
9820
- this.conceptDir,
9821
- this.contentDir
9822
- ];
9823
- for (const dir of directories) {
9824
- if (dir === nodeDir)
9825
- continue; // Skip the already checked directory
9826
- const dirFilePath = path.join(dir, `${id}.json`);
9827
- try {
9828
- await fs.promises.access(dirFilePath);
9829
- // If found in this directory, use that path
9830
- const data = await fs.promises.readFile(dirFilePath, 'utf8');
9831
- const parsedNode = JSON.parse(data);
9832
- // Convert serialized connections back to Map<number, Set<string>>
9833
- const connections = new Map();
9834
- for (const [level, nodeIds] of Object.entries(parsedNode.connections)) {
9835
- connections.set(Number(level), new Set(nodeIds));
9836
- }
9837
- return {
9838
- id: parsedNode.id,
9839
- vector: parsedNode.vector,
9840
- connections
9841
- };
9842
- }
9843
- catch {
9844
- // Continue to the next directory
9845
- }
9846
- }
9847
- return null; // File doesn't exist in any directory
9848
- }
9849
- }
9850
- return null; // File doesn't exist
9851
- }
9852
- const data = await fs.promises.readFile(filePath, 'utf8');
9853
- const parsedNode = JSON.parse(data);
9854
- // Convert serialized connections back to Map<number, Set<string>>
9855
- const connections = new Map();
9856
- for (const [level, nodeIds] of Object.entries(parsedNode.connections)) {
9857
- connections.set(Number(level), new Set(nodeIds));
9909
+ // Ignore EEXIST error, which means the directory already exists
9910
+ if (error.code !== 'EEXIST') {
9911
+ throw error;
9858
9912
  }
9859
- return {
9860
- id: parsedNode.id,
9861
- vector: parsedNode.vector,
9862
- connections
9863
- };
9864
- }
9865
- catch (error) {
9866
- console.error(`Failed to get node ${id}:`, error);
9867
- return null;
9868
9913
  }
9869
9914
  }
9870
- /**
9871
- * Get nodes by noun type
9872
- * @param nounType The noun type to filter by
9873
- * @returns Promise that resolves to an array of nodes of the specified noun type
9874
- */
9875
- async getNounsByNounType(nounType) {
9876
- await this.ensureInitialized();
9877
- try {
9878
- // Determine the directory based on the noun type
9879
- let dir;
9880
- switch (nounType) {
9915
+ getNounPath(id, nounType) {
9916
+ let typeDir = this.defaultDir;
9917
+ if (nounType) {
9918
+ switch (nounType.toLowerCase()) {
9881
9919
  case 'person':
9882
- dir = this.personDir;
9920
+ typeDir = this.personDir;
9883
9921
  break;
9884
9922
  case 'place':
9885
- dir = this.placeDir;
9923
+ typeDir = this.placeDir;
9886
9924
  break;
9887
9925
  case 'thing':
9888
- dir = this.thingDir;
9926
+ typeDir = this.thingDir;
9889
9927
  break;
9890
9928
  case 'event':
9891
- dir = this.eventDir;
9929
+ typeDir = this.eventDir;
9892
9930
  break;
9893
9931
  case 'concept':
9894
- dir = this.conceptDir;
9932
+ typeDir = this.conceptDir;
9895
9933
  break;
9896
9934
  case 'content':
9897
- dir = this.contentDir;
9935
+ typeDir = this.contentDir;
9898
9936
  break;
9899
9937
  default:
9900
- dir = this.defaultDir;
9938
+ typeDir = this.defaultDir;
9901
9939
  }
9902
- const nodes = [];
9903
- try {
9904
- const files = await fs.promises.readdir(dir);
9905
- const nodePromises = files
9906
- .filter((file) => file.endsWith('.json'))
9907
- .map((file) => {
9908
- // Use the file path directly instead of getNode to avoid redundant searches
9909
- return this.readNodeFromFile(path.join(dir, file));
9910
- });
9911
- const dirNodes = await Promise.all(nodePromises);
9912
- nodes.push(...dirNodes.filter((node) => node !== null));
9913
- }
9914
- catch (dirError) {
9915
- // If directory doesn't exist or can't be read, log a warning
9916
- console.warn(`Could not read directory for noun type ${nounType}:`, dirError);
9917
- }
9918
- return nodes;
9919
- }
9920
- catch (error) {
9921
- console.error(`Failed to get nodes for noun type ${nounType}:`, error);
9922
- throw new Error(`Failed to get nodes for noun type ${nounType}: ${error}`);
9923
9940
  }
9941
+ return path.join(typeDir, `${id}.json`);
9924
9942
  }
9925
- /**
9926
- * Get all nodes from storage
9927
- */
9928
- async getAllNouns() {
9929
- await this.ensureInitialized();
9930
- try {
9931
- // Get all noun types
9932
- const nounTypes = [
9933
- 'person',
9934
- 'place',
9935
- 'thing',
9936
- 'event',
9937
- 'concept',
9938
- 'content',
9939
- 'default'
9940
- ];
9941
- // Run searches in parallel for all noun types
9942
- const nodePromises = nounTypes.map((nounType) => this.getNounsByNounType(nounType));
9943
- const nodeArrays = await Promise.all(nodePromises);
9944
- // Combine all results
9945
- const allNodes = [];
9946
- for (const nodes of nodeArrays) {
9947
- allNodes.push(...nodes);
9948
- }
9949
- return allNodes;
9950
- }
9951
- catch (error) {
9952
- console.error('Failed to get all nodes:', error);
9953
- throw new Error(`Failed to get all nodes: ${error}`);
9954
- }
9943
+ async saveNoun(noun) {
9944
+ if (!this.isInitialized)
9945
+ await this.init();
9946
+ const nounType = noun.metadata?.noun;
9947
+ const filePath = this.getNounPath(noun.id, nounType);
9948
+ await this.ensureDirectoryExists(path.dirname(filePath));
9949
+ await fs.promises.writeFile(filePath, JSON.stringify(noun, null, 2));
9955
9950
  }
9956
- /**
9957
- * Read a node from a file
9958
- */
9959
- async readNodeFromFile(filePath) {
9960
- try {
9961
- const data = await fs.promises.readFile(filePath, 'utf8');
9962
- const parsedNode = JSON.parse(data);
9963
- // Convert serialized connections back to Map<number, Set<string>>
9964
- const connections = new Map();
9965
- for (const [level, nodeIds] of Object.entries(parsedNode.connections)) {
9966
- connections.set(Number(level), new Set(nodeIds));
9951
+ async getNoun(id) {
9952
+ if (!this.isInitialized)
9953
+ await this.init();
9954
+ const nounDirs = [
9955
+ this.personDir,
9956
+ this.placeDir,
9957
+ this.thingDir,
9958
+ this.eventDir,
9959
+ this.conceptDir,
9960
+ this.contentDir,
9961
+ this.defaultDir
9962
+ ];
9963
+ for (const dir of nounDirs) {
9964
+ const filePath = path.join(dir, `${id}.json`);
9965
+ try {
9966
+ const data = await fs.promises.readFile(filePath, 'utf-8');
9967
+ return JSON.parse(data);
9968
+ }
9969
+ catch (error) {
9970
+ if (error.code !== 'ENOENT') {
9971
+ console.error(`Error reading noun ${id}:`, error);
9972
+ }
9967
9973
  }
9968
- return {
9969
- id: parsedNode.id,
9970
- vector: parsedNode.vector,
9971
- connections
9972
- };
9973
- }
9974
- catch (error) {
9975
- console.error(`Failed to read node from file ${filePath}:`, error);
9976
- return null;
9977
9974
  }
9975
+ return null;
9978
9976
  }
9979
- /**
9980
- * Delete a node from storage
9981
- */
9982
9977
  async deleteNoun(id) {
9983
- await this.ensureInitialized();
9984
- try {
9985
- // Get the appropriate directory based on the node's metadata
9986
- const nodeDir = await this.getNodeDirectory(id);
9987
- const filePath = path.join(nodeDir, `${id}.json`);
9988
- // Check if a file exists before attempting to delete
9978
+ if (!this.isInitialized)
9979
+ await this.init();
9980
+ const noun = await this.getNoun(id);
9981
+ if (noun) {
9982
+ const nounType = noun.metadata?.noun;
9983
+ const filePath = this.getNounPath(id, nounType);
9989
9984
  try {
9990
- await fs.promises.access(filePath);
9991
9985
  await fs.promises.unlink(filePath);
9992
- return; // File found and deleted
9993
9986
  }
9994
- catch {
9995
- // If the file doesn't exist in the expected directory, try the default directory
9996
- if (nodeDir !== this.defaultDir) {
9997
- const defaultFilePath = path.join(this.defaultDir, `${id}.json`);
9998
- try {
9999
- await fs.promises.access(defaultFilePath);
10000
- await fs.promises.unlink(defaultFilePath);
10001
- return; // File found and deleted
10002
- }
10003
- catch {
10004
- // If not found in default directory either, try all noun type directories
10005
- const directories = [
10006
- this.personDir,
10007
- this.placeDir,
10008
- this.thingDir,
10009
- this.eventDir,
10010
- this.conceptDir,
10011
- this.contentDir
10012
- ];
10013
- for (const dir of directories) {
10014
- if (dir === nodeDir)
10015
- continue; // Skip the already checked directory
10016
- const dirFilePath = path.join(dir, `${id}.json`);
10017
- try {
10018
- await fs.promises.access(dirFilePath);
10019
- await fs.promises.unlink(dirFilePath);
10020
- return; // File found and deleted
10021
- }
10022
- catch {
10023
- // Continue to the next directory
10024
- }
10025
- }
10026
- return; // File doesn't exist in any directory, nothing to delete
10027
- }
9987
+ catch (error) {
9988
+ if (error.code !== 'ENOENT') {
9989
+ console.error(`Error deleting noun file ${filePath}:`, error);
9990
+ throw error;
10028
9991
  }
10029
- return; // File doesn't exist, nothing to delete
10030
9992
  }
10031
9993
  }
10032
- catch (error) {
10033
- console.error(`Failed to delete node ${id}:`, error);
10034
- throw new Error(`Failed to delete node ${id}: ${error}`);
10035
- }
10036
- }
10037
- /**
10038
- * Save an edge to storage
10039
- */
10040
- async saveVerb(verb) {
10041
- await this.ensureInitialized();
10042
- try {
10043
- // Convert connections Map to a serializable format
10044
- const serializableEdge = {
10045
- ...verb,
10046
- connections: this.mapToObject(verb.connections, (set) => Array.from(set))
10047
- };
10048
- const filePath = path.join(this.verbsDir, `${verb.id}.json`);
10049
- await fs.promises.writeFile(filePath, JSON.stringify(serializableEdge, null, 2), 'utf8');
10050
- }
10051
- catch (error) {
10052
- console.error(`Failed to save edge ${verb.id}:`, error);
10053
- throw new Error(`Failed to save edge ${verb.id}: ${error}`);
10054
- }
10055
9994
  }
10056
- /**
10057
- * Get an edge from storage
10058
- */
10059
- async getVerb(id) {
10060
- await this.ensureInitialized();
10061
- try {
10062
- const filePath = path.join(this.verbsDir, `${id}.json`);
10063
- // Check if a file exists
9995
+ async getAllNouns() {
9996
+ if (!this.isInitialized)
9997
+ await this.init();
9998
+ const allNouns = [];
9999
+ const nounDirs = [
10000
+ this.personDir,
10001
+ this.placeDir,
10002
+ this.thingDir,
10003
+ this.eventDir,
10004
+ this.conceptDir,
10005
+ this.contentDir,
10006
+ this.defaultDir
10007
+ ];
10008
+ for (const dir of nounDirs) {
10064
10009
  try {
10065
- await fs.promises.access(filePath);
10066
- }
10067
- catch {
10068
- return null; // File doesn't exist
10010
+ const files = await fs.promises.readdir(dir);
10011
+ for (const file of files) {
10012
+ if (file.endsWith('.json')) {
10013
+ const filePath = path.join(dir, file);
10014
+ const data = await fs.promises.readFile(filePath, 'utf-8');
10015
+ allNouns.push(JSON.parse(data));
10016
+ }
10017
+ }
10069
10018
  }
10070
- const data = await fs.promises.readFile(filePath, 'utf8');
10071
- const parsedEdge = JSON.parse(data);
10072
- // Convert serialized connections back to Map<number, Set<string>>
10073
- const connections = new Map();
10074
- for (const [level, nodeIds] of Object.entries(parsedEdge.connections)) {
10075
- connections.set(Number(level), new Set(nodeIds));
10019
+ catch (error) {
10020
+ if (error.code !== 'ENOENT') {
10021
+ console.error(`Error reading directory ${dir}:`, error);
10022
+ }
10076
10023
  }
10077
- return {
10078
- id: parsedEdge.id,
10079
- vector: parsedEdge.vector,
10080
- connections,
10081
- sourceId: parsedEdge.sourceId,
10082
- targetId: parsedEdge.targetId,
10083
- type: parsedEdge.type,
10084
- weight: parsedEdge.weight,
10085
- metadata: parsedEdge.metadata
10086
- };
10087
- }
10088
- catch (error) {
10089
- console.error(`Failed to get edge ${id}:`, error);
10090
- return null;
10091
- }
10092
- }
10093
- /**
10094
- * Get all edges from storage
10095
- */
10096
- async getAllVerbs() {
10097
- await this.ensureInitialized();
10098
- try {
10099
- const files = await fs.promises.readdir(this.verbsDir);
10100
- const edgePromises = files
10101
- .filter((file) => file.endsWith('.json'))
10102
- .map((file) => {
10103
- const id = path.basename(file, '.json');
10104
- return this.getVerb(id);
10105
- });
10106
- const edges = await Promise.all(edgePromises);
10107
- return edges.filter((edge) => edge !== null);
10108
- }
10109
- catch (error) {
10110
- console.error('Failed to get all edges:', error);
10111
- throw new Error(`Failed to get all edges: ${error}`);
10112
- }
10113
- }
10114
- /**
10115
- * Get edges by source node ID
10116
- */
10117
- async getVerbsBySource(sourceId) {
10118
- await this.ensureInitialized();
10119
- try {
10120
- const allEdges = await this.getAllVerbs();
10121
- return allEdges.filter((edge) => edge.sourceId === sourceId);
10122
- }
10123
- catch (error) {
10124
- console.error(`Failed to get edges by source ${sourceId}:`, error);
10125
- throw new Error(`Failed to get edges by source ${sourceId}: ${error}`);
10126
- }
10127
- }
10128
- /**
10129
- * Get edges by target node ID
10130
- */
10131
- async getVerbsByTarget(targetId) {
10132
- await this.ensureInitialized();
10133
- try {
10134
- const allEdges = await this.getAllVerbs();
10135
- return allEdges.filter((edge) => edge.targetId === targetId);
10136
- }
10137
- catch (error) {
10138
- console.error(`Failed to get edges by target ${targetId}:`, error);
10139
- throw new Error(`Failed to get edges by target ${targetId}: ${error}`);
10140
10024
  }
10025
+ return allNouns;
10141
10026
  }
10142
10027
  /**
10143
- * Get edges by type
10028
+ * Get nouns by noun type
10029
+ * @param nounType The noun type to filter by
10030
+ * @returns Promise that resolves to an array of nouns of the specified noun type
10144
10031
  */
10145
- async getVerbsByType(type) {
10146
- await this.ensureInitialized();
10147
- try {
10148
- const allEdges = await this.getAllVerbs();
10149
- return allEdges.filter((edge) => edge.type === type);
10150
- }
10151
- catch (error) {
10152
- console.error(`Failed to get edges by type ${type}:`, error);
10153
- throw new Error(`Failed to get edges by type ${type}: ${error}`);
10032
+ async getNounsByNounType(nounType) {
10033
+ if (!this.isInitialized)
10034
+ await this.init();
10035
+ let typeDir;
10036
+ switch (nounType.toLowerCase()) {
10037
+ case 'person':
10038
+ typeDir = this.personDir;
10039
+ break;
10040
+ case 'place':
10041
+ typeDir = this.placeDir;
10042
+ break;
10043
+ case 'thing':
10044
+ typeDir = this.thingDir;
10045
+ break;
10046
+ case 'event':
10047
+ typeDir = this.eventDir;
10048
+ break;
10049
+ case 'concept':
10050
+ typeDir = this.conceptDir;
10051
+ break;
10052
+ case 'content':
10053
+ typeDir = this.contentDir;
10054
+ break;
10055
+ default:
10056
+ typeDir = this.defaultDir;
10154
10057
  }
10155
- }
10156
- /**
10157
- * Delete an edge from storage
10158
- */
10159
- async deleteVerb(id) {
10160
- await this.ensureInitialized();
10058
+ const nouns = [];
10161
10059
  try {
10162
- const filePath = path.join(this.verbsDir, `${id}.json`);
10163
- // Check if a file exists before attempting to delete
10164
- try {
10165
- await fs.promises.access(filePath);
10166
- }
10167
- catch {
10168
- return; // File doesn't exist, nothing to delete
10060
+ const files = await fs.promises.readdir(typeDir);
10061
+ for (const file of files) {
10062
+ if (file.endsWith('.json')) {
10063
+ const filePath = path.join(typeDir, file);
10064
+ const data = await fs.promises.readFile(filePath, 'utf-8');
10065
+ nouns.push(JSON.parse(data));
10066
+ }
10169
10067
  }
10170
- await fs.promises.unlink(filePath);
10171
10068
  }
10172
10069
  catch (error) {
10173
- console.error(`Failed to delete edge ${id}:`, error);
10174
- throw new Error(`Failed to delete edge ${id}: ${error}`);
10070
+ if (error.code !== 'ENOENT') {
10071
+ console.error(`Error reading directory ${typeDir}:`, error);
10072
+ }
10175
10073
  }
10074
+ return nouns;
10176
10075
  }
10177
- /**
10178
- * Save metadata to storage
10179
- */
10180
- async saveMetadata(id, metadata) {
10181
- await this.ensureInitialized();
10182
- try {
10183
- const filePath = path.join(this.metadataDir, `${id}.json`);
10184
- await fs.promises.writeFile(filePath, JSON.stringify(metadata, null, 2), 'utf8');
10185
- }
10186
- catch (error) {
10187
- console.error(`Failed to save metadata for ${id}:`, error);
10188
- throw new Error(`Failed to save metadata for ${id}: ${error}`);
10189
- }
10076
+ async saveVerb(verb) {
10077
+ if (!this.isInitialized)
10078
+ await this.init();
10079
+ const filePath = path.join(this.verbsDir, `${verb.id}.json`);
10080
+ await fs.promises.writeFile(filePath, JSON.stringify(verb, null, 2));
10190
10081
  }
10191
10082
  /**
10192
- * Get metadata from storage
10083
+ * Get a verb by its ID
10084
+ * @param id The ID of the verb to retrieve
10085
+ * @returns Promise that resolves to the verb or null if not found
10193
10086
  */
10194
- async getMetadata(id) {
10195
- await this.ensureInitialized();
10087
+ async getVerb(id) {
10088
+ if (!this.isInitialized)
10089
+ await this.init();
10090
+ const filePath = path.join(this.verbsDir, `${id}.json`);
10196
10091
  try {
10197
- const filePath = path.join(this.metadataDir, `${id}.json`);
10198
- // Check if a file exists
10199
- try {
10200
- await fs.promises.access(filePath);
10201
- }
10202
- catch {
10203
- return null; // File doesn't exist
10204
- }
10205
- const data = await fs.promises.readFile(filePath, 'utf8');
10092
+ const data = await fs.promises.readFile(filePath, 'utf-8');
10206
10093
  return JSON.parse(data);
10207
10094
  }
10208
10095
  catch (error) {
10209
- console.error(`Failed to get metadata for ${id}:`, error);
10096
+ if (error.code !== 'ENOENT') {
10097
+ console.error(`Error reading verb ${id}:`, error);
10098
+ }
10210
10099
  return null;
10211
10100
  }
10212
10101
  }
10213
- /**
10214
- * Clear all data from storage
10215
- */
10216
- async clear() {
10217
- await this.ensureInitialized();
10218
- try {
10219
- // Delete and recreate the nodes, edges, and metadata directories
10220
- await this.deleteDirectory(this.nounsDir);
10221
- await this.deleteDirectory(this.verbsDir);
10222
- await this.deleteDirectory(this.metadataDir);
10223
- await this.ensureDirectoryExists(this.nounsDir);
10224
- await this.ensureDirectoryExists(this.verbsDir);
10225
- await this.ensureDirectoryExists(this.metadataDir);
10226
- // Create noun type directories
10227
- await this.ensureDirectoryExists(this.personDir);
10228
- await this.ensureDirectoryExists(this.placeDir);
10229
- await this.ensureDirectoryExists(this.thingDir);
10230
- await this.ensureDirectoryExists(this.eventDir);
10231
- await this.ensureDirectoryExists(this.conceptDir);
10232
- await this.ensureDirectoryExists(this.contentDir);
10233
- await this.ensureDirectoryExists(this.defaultDir);
10234
- }
10235
- catch (error) {
10236
- console.error('Failed to clear storage:', error);
10237
- throw new Error(`Failed to clear storage: ${error}`);
10238
- }
10102
+ async getVerbsBySource(sourceId) {
10103
+ if (!this.isInitialized)
10104
+ await this.init();
10105
+ const allVerbs = await this.getAllVerbs();
10106
+ return allVerbs.filter((verb) => verb.sourceId === sourceId);
10239
10107
  }
10240
10108
  /**
10241
- * Ensure the storage adapter is initialized
10109
+ * Get verbs by target ID
10110
+ * @param targetId The target ID to filter by
10111
+ * @returns Promise that resolves to an array of verbs with the specified target ID
10242
10112
  */
10243
- async ensureInitialized() {
10244
- if (!this.isInitialized) {
10113
+ async getVerbsByTarget(targetId) {
10114
+ if (!this.isInitialized)
10245
10115
  await this.init();
10246
- }
10116
+ const allVerbs = await this.getAllVerbs();
10117
+ return allVerbs.filter((verb) => verb.targetId === targetId);
10247
10118
  }
10248
10119
  /**
10249
- * Ensure a directory exists, creating it if necessary
10120
+ * Get verbs by type
10121
+ * @param type The verb type to filter by
10122
+ * @returns Promise that resolves to an array of verbs of the specified type
10250
10123
  */
10251
- async ensureDirectoryExists(dirPath) {
10252
- try {
10253
- await fs.promises.access(dirPath);
10254
- }
10255
- catch {
10256
- // Directory doesn't exist, create it
10257
- await fs.promises.mkdir(dirPath, { recursive: true });
10258
- }
10124
+ async getVerbsByType(type) {
10125
+ if (!this.isInitialized)
10126
+ await this.init();
10127
+ const allVerbs = await this.getAllVerbs();
10128
+ return allVerbs.filter((verb) => verb.type === type);
10259
10129
  }
10260
- /**
10261
- * Delete a directory and all its contents recursively
10262
- */
10263
- async deleteDirectory(dirPath) {
10130
+ async getAllVerbs() {
10131
+ if (!this.isInitialized)
10132
+ await this.init();
10133
+ const allVerbs = [];
10264
10134
  try {
10265
- const files = await fs.promises.readdir(dirPath);
10135
+ const files = await fs.promises.readdir(this.verbsDir);
10266
10136
  for (const file of files) {
10267
- const filePath = path.join(dirPath, file);
10268
- const stats = await fs.promises.stat(filePath);
10269
- if (stats.isDirectory()) {
10270
- // Recursively delete subdirectories
10271
- await this.deleteDirectory(filePath);
10272
- }
10273
- else {
10274
- // Delete files
10275
- await fs.promises.unlink(filePath);
10137
+ if (file.endsWith('.json')) {
10138
+ const filePath = path.join(this.verbsDir, file);
10139
+ const data = await fs.promises.readFile(filePath, 'utf-8');
10140
+ allVerbs.push(JSON.parse(data));
10276
10141
  }
10277
10142
  }
10278
- // After all contents are deleted, remove the directory itself
10279
- await fs.promises.rmdir(dirPath);
10280
10143
  }
10281
10144
  catch (error) {
10282
- // If the directory doesn't exist, that's fine
10283
10145
  if (error.code !== 'ENOENT') {
10284
- throw error;
10146
+ console.error(`Error reading verbs directory ${this.verbsDir}:`, error);
10285
10147
  }
10286
10148
  }
10149
+ return allVerbs;
10287
10150
  }
10288
- /**
10289
- * Count the number of JSON files in a directory
10290
- */
10291
- async countFilesInDirectory(dirPath) {
10151
+ async deleteVerb(id) {
10152
+ if (!this.isInitialized)
10153
+ await this.init();
10154
+ const filePath = path.join(this.verbsDir, `${id}.json`);
10292
10155
  try {
10293
- const files = await fs.promises.readdir(dirPath);
10294
- return files.filter((file) => file.endsWith('.json')).length;
10156
+ await fs.promises.unlink(filePath);
10295
10157
  }
10296
10158
  catch (error) {
10297
- // If the directory doesn't exist, return 0
10298
- if (error.code === 'ENOENT') {
10299
- return 0;
10159
+ if (error.code !== 'ENOENT') {
10160
+ console.error(`Error deleting verb file ${filePath}:`, error);
10161
+ throw error;
10300
10162
  }
10301
- throw error;
10302
10163
  }
10303
10164
  }
10304
10165
  /**
10305
- * Convert a Map to a plain object for serialization
10166
+ * Save metadata for an entity
10167
+ * @param id The ID of the entity
10168
+ * @param metadata The metadata to save
10306
10169
  */
10307
- mapToObject(map, valueTransformer = (v) => v) {
10308
- const obj = {};
10309
- for (const [key, value] of map.entries()) {
10310
- obj[key.toString()] = valueTransformer(value);
10311
- }
10312
- return obj;
10170
+ async saveMetadata(id, metadata) {
10171
+ if (!this.isInitialized)
10172
+ await this.init();
10173
+ const filePath = path.join(this.metadataDir, `${id}.json`);
10174
+ await fs.promises.writeFile(filePath, JSON.stringify(metadata, null, 2));
10313
10175
  }
10314
10176
  /**
10315
- * Get the appropriate directory for a node based on its metadata
10177
+ * Get metadata for an entity
10178
+ * @param id The ID of the entity
10179
+ * @returns Promise that resolves to the metadata or null if not found
10316
10180
  */
10317
- async getNodeDirectory(id) {
10181
+ async getMetadata(id) {
10182
+ if (!this.isInitialized)
10183
+ await this.init();
10184
+ const filePath = path.join(this.metadataDir, `${id}.json`);
10318
10185
  try {
10319
- // Try to get the metadata for the node
10320
- const metadata = await this.getMetadata(id);
10321
- // If metadata exists and has a noun field, use the corresponding directory
10322
- if (metadata && metadata.noun) {
10323
- switch (metadata.noun) {
10324
- case 'person':
10325
- return this.personDir;
10326
- case 'place':
10327
- return this.placeDir;
10328
- case 'thing':
10329
- return this.thingDir;
10330
- case 'event':
10331
- return this.eventDir;
10332
- case 'concept':
10333
- return this.conceptDir;
10334
- case 'content':
10335
- return this.contentDir;
10336
- default:
10337
- return this.defaultDir;
10338
- }
10339
- }
10340
- // If no metadata or no noun field, use the default directory
10341
- return this.defaultDir;
10186
+ const data = await fs.promises.readFile(filePath, 'utf-8');
10187
+ return JSON.parse(data);
10342
10188
  }
10343
10189
  catch (error) {
10344
- // If there's an error getting the metadata, use the default directory
10345
- return this.defaultDir;
10190
+ if (error.code !== 'ENOENT') {
10191
+ console.error(`Error reading metadata for ${id}:`, error);
10192
+ }
10193
+ return null;
10346
10194
  }
10347
10195
  }
10348
- /**
10349
- * Get information about storage usage and capacity
10350
- */
10196
+ async clear() {
10197
+ if (!this.isInitialized)
10198
+ await this.init();
10199
+ await fs.promises.rm(this.rootDir, { recursive: true, force: true });
10200
+ this.isInitialized = false; // Reset state
10201
+ await this.init(); // Re-create directories
10202
+ }
10351
10203
  async getStorageStatus() {
10352
- await this.ensureInitialized();
10353
- try {
10354
- // Calculate the total size of all files in the storage directories
10355
- let totalSize = 0;
10356
- // Helper function to calculate directory size
10357
- const calculateDirSize = async (dirPath) => {
10358
- let size = 0;
10359
- try {
10360
- const files = await fs.promises.readdir(dirPath);
10361
- for (const file of files) {
10362
- const filePath = path.join(dirPath, file);
10363
- const stats = await fs.promises.stat(filePath);
10364
- if (stats.isDirectory()) {
10365
- size += await calculateDirSize(filePath);
10366
- }
10367
- else {
10368
- size += stats.size;
10369
- }
10204
+ if (!this.isInitialized)
10205
+ await this.init();
10206
+ const calculateSize = async (dirPath) => {
10207
+ let size = 0;
10208
+ try {
10209
+ const files = await fs.promises.readdir(dirPath, {
10210
+ withFileTypes: true
10211
+ });
10212
+ for (const file of files) {
10213
+ const fullPath = path.join(dirPath, file.name);
10214
+ if (file.isDirectory()) {
10215
+ size += await calculateSize(fullPath);
10370
10216
  }
10371
- }
10372
- catch (error) {
10373
- console.warn(`Error calculating size for ${dirPath}:`, error);
10374
- }
10375
- return size;
10376
- };
10377
- // Calculate size for each directory
10378
- const nodesDirSize = await calculateDirSize(this.nounsDir);
10379
- const edgesDirSize = await calculateDirSize(this.verbsDir);
10380
- const metadataDirSize = await calculateDirSize(this.metadataDir);
10381
- // Calculate sizes of noun type directories
10382
- const personDirSize = await calculateDirSize(this.personDir);
10383
- const placeDirSize = await calculateDirSize(this.placeDir);
10384
- const thingDirSize = await calculateDirSize(this.thingDir);
10385
- const eventDirSize = await calculateDirSize(this.eventDir);
10386
- const conceptDirSize = await calculateDirSize(this.conceptDir);
10387
- const contentDirSize = await calculateDirSize(this.contentDir);
10388
- const defaultDirSize = await calculateDirSize(this.defaultDir);
10389
- // Note: The noun type directories are subdirectories of the nodes directory,
10390
- // so their sizes are already included in nodesDirSize.
10391
- // We don't need to add them again to avoid double counting.
10392
- totalSize = nodesDirSize + edgesDirSize + metadataDirSize;
10393
- // Get filesystem information
10394
- let quota = null;
10395
- let details = {
10396
- nounTypes: {
10397
- person: {
10398
- size: personDirSize,
10399
- count: await this.countFilesInDirectory(this.personDir)
10400
- },
10401
- place: {
10402
- size: placeDirSize,
10403
- count: await this.countFilesInDirectory(this.placeDir)
10404
- },
10405
- thing: {
10406
- size: thingDirSize,
10407
- count: await this.countFilesInDirectory(this.thingDir)
10408
- },
10409
- event: {
10410
- size: eventDirSize,
10411
- count: await this.countFilesInDirectory(this.eventDir)
10412
- },
10413
- concept: {
10414
- size: conceptDirSize,
10415
- count: await this.countFilesInDirectory(this.conceptDir)
10416
- },
10417
- content: {
10418
- size: contentDirSize,
10419
- count: await this.countFilesInDirectory(this.contentDir)
10420
- },
10421
- default: {
10422
- size: defaultDirSize,
10423
- count: await this.countFilesInDirectory(this.defaultDir)
10217
+ else {
10218
+ const stats = await fs.promises.stat(fullPath);
10219
+ size += stats.size;
10424
10220
  }
10425
10221
  }
10426
- };
10427
- try {
10428
- // Try to get disk space information
10429
- const stats = await fs.promises.statfs(this.rootDir);
10430
- if (stats) {
10431
- const availableSpace = stats.bavail * stats.bsize;
10432
- const totalSpace = stats.blocks * stats.bsize;
10433
- quota = totalSpace;
10434
- details = {
10435
- availableSpace,
10436
- totalSpace,
10437
- freePercentage: (availableSpace / totalSpace) * 100
10438
- };
10439
- }
10440
10222
  }
10441
10223
  catch (error) {
10442
- console.warn('Unable to get filesystem stats:', error);
10443
- // If statfs is not available, try to use df command on Unix-like systems
10444
- try {
10445
- const { exec } = await Promise.resolve().then(function () { return _child_processShim; });
10446
- const util = await Promise.resolve().then(function () { return _utilShim$1; });
10447
- const execPromise = util.promisify(exec);
10448
- const { stdout } = await execPromise(`df -k "${this.rootDir}"`);
10449
- const lines = stdout.trim().split('\n');
10450
- if (lines.length > 1) {
10451
- const parts = lines[1].split(/\s+/);
10452
- if (parts.length >= 4) {
10453
- const totalKB = parseInt(parts[1], 10);
10454
- const usedKB = parseInt(parts[2], 10);
10455
- const availableKB = parseInt(parts[3], 10);
10456
- quota = totalKB * 1024;
10457
- details = {
10458
- availableSpace: availableKB * 1024,
10459
- totalSpace: totalKB * 1024,
10460
- freePercentage: (availableKB / totalKB) * 100
10461
- };
10462
- }
10463
- }
10464
- }
10465
- catch (dfError) {
10466
- console.warn('Unable to get disk space using df command:', dfError);
10224
+ if (error.code !== 'ENOENT') {
10225
+ console.error(`Could not calculate size for ${dirPath}:`, error);
10467
10226
  }
10468
10227
  }
10469
- return {
10470
- type: 'filesystem',
10471
- used: totalSize,
10472
- quota,
10473
- details
10474
- };
10475
- }
10476
- catch (error) {
10477
- console.error('Failed to get storage status:', error);
10478
- return {
10479
- type: 'filesystem',
10480
- used: 0,
10481
- quota: null,
10482
- details: { error: String(error) }
10483
- };
10484
- }
10228
+ return size;
10229
+ };
10230
+ const totalSize = await calculateSize(this.rootDir);
10231
+ const nouns = await this.getAllNouns();
10232
+ const verbs = await this.getAllVerbs();
10233
+ return {
10234
+ type: 'FileSystem',
10235
+ used: totalSize,
10236
+ quota: null, // File system quota is not easily available from Node.js
10237
+ details: {
10238
+ rootDir: this.rootDir,
10239
+ nounCount: nouns.length,
10240
+ verbCount: verbs.length
10241
+ }
10242
+ };
10485
10243
  }
10486
10244
  }
10487
10245
 
@@ -14193,20 +13951,15 @@ class BrainyMCPService {
14193
13951
  }
14194
13952
  }
14195
13953
 
14196
- /**
14197
- * OPFS BrainyData
14198
- * A vector database using HNSW indexing with Origin Private File System storage
14199
- */
14200
- // Import unified text encoding utilities first to ensure they're available
14201
- // Apply the TensorFlow.js platform patch if needed
14202
- applyTensorFlowPatch();
14203
-
14204
13954
  // Make Buffer available globally
14205
13955
  if (typeof window !== 'undefined' && typeof globalThis.Buffer === 'undefined') {
14206
13956
  globalThis.Buffer = buffer$1.Buffer;
14207
13957
  }
14208
- // Apply the TensorFlow.js platform patch if needed
14209
- applyTensorFlowPatch();
13958
+ /**
13959
+ * Unified entry point for Brainy
13960
+ * This file exports everything from index.ts
13961
+ * Environment detection is handled here and made available to all components
13962
+ */
14210
13963
  // Export environment information
14211
13964
  const environment = {
14212
13965
  isBrowser: typeof window !== 'undefined',
@@ -22312,7 +22065,7 @@ class PlatformNode {
22312
22065
  this.util = require$$1;
22313
22066
  // According to the spec, the built-in encoder can do only UTF-8 encoding.
22314
22067
  // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/TextEncoder
22315
- this.textEncoder = new TextEncoder();
22068
+ this.textEncoder = new this.util.TextEncoder();
22316
22069
  }
22317
22070
  fetch(path, requestInits) {
22318
22071
  if (env().global.fetch != null) {
@@ -87531,9 +87284,5 @@ var universalSentenceEncoder_esm = /*#__PURE__*/Object.freeze({
87531
87284
  version: version
87532
87285
  });
87533
87286
 
87534
- var _child_processShim = /*#__PURE__*/Object.freeze({
87535
- __proto__: null
87536
- });
87537
-
87538
87287
  export { AugmentationType, BrainyData, BrainyMCPAdapter, BrainyMCPService, ExecutionMode$1 as ExecutionMode, FileSystemStorage, FileSystemStorageAugmentation, HNSWIndex, HNSWIndexOptimized, MCPAugmentationToolset, MCPRequestType, MCP_VERSION, MemoryStorage, MemoryStorageAugmentation, NounType, OPFSStorage, OPFSStorageAugmentation, Pipeline, S3CompatibleStorage as R2Storage, S3CompatibleStorage, SequentialPipeline, ServerSearchActivationAugmentation, ServerSearchConduitAugmentation, StreamlinedExecutionMode, UniversalSentenceEncoder$1 as UniversalSentenceEncoder, VerbType, WebRTCConduitAugmentation, WebSocketConduitAugmentation, addWebSocketSupport, areWebWorkersAvailable, areWorkerThreadsAvailable, areWorkerThreadsAvailableSync, augmentationPipeline$1 as augmentationPipeline, availableAugmentations, cleanupWorkerPools, cosineDistance$1 as cosineDistance, createAugmentationRegistryPlugin, createAugmentationRegistryRollupPlugin, createConduitAugmentation, createEmbeddingFunction, createMemoryAugmentation, createPipeline, createSenseAugmentation, createServerSearchAugmentations, createStorage, createStreamingPipeline, createTensorFlowEmbeddingFunction, createThreadedEmbeddingFunction, defaultEmbeddingFunction, dotProductDistance, environment, euclideanDistance, executeAugmentation, executeByType, executeInThread, executeSingle, executeStreamlined, getAugmentationsByType, initializeAugmentationPipeline, isBrowser$1 as isBrowser, isNode, isThreadingAvailable, isThreadingAvailableAsync, isWebWorker, loadAugmentationModule, loadAugmentationsFromModules, manhattanDistance, pipeline, processStaticData, processStreamingData, registerAugmentation, sequentialPipeline, setAugmentationEnabled };
87539
87288
  //# sourceMappingURL=unified.js.map