diff --git a/cli-package/rollup.config.js b/cli-package/rollup.config.js index b4ae2e99..d7daa742 100644 --- a/cli-package/rollup.config.js +++ b/cli-package/rollup.config.js @@ -7,6 +7,7 @@ import { terser } from 'rollup-plugin-terser' // CLI configuration export default { input: 'src/cli.ts', + context: 'this', // Preserve 'this' context to fix TensorFlow.js issue output: { dir: 'dist', entryFileNames: 'cli.js', diff --git a/cli-package/src/cli.ts b/cli-package/src/cli.ts index d083856f..e6814e11 100644 --- a/cli-package/src/cli.ts +++ b/cli-package/src/cli.ts @@ -5,6 +5,11 @@ * A command-line interface for interacting with the Brainy vector database */ +// Import the setup file for its side-effects. +// This MUST be the very first import to ensure patches are applied +// before any other module (like TensorFlow.js) is loaded. +import './setup.js' + // Log environment information console.log('Brainy running in Node.js environment') diff --git a/cli-package/src/setup.ts b/cli-package/src/setup.ts new file mode 100644 index 00000000..edd4be4d --- /dev/null +++ b/cli-package/src/setup.ts @@ -0,0 +1,12 @@ +/** + * This file is imported for its side effects to patch the environment + * for TensorFlow.js before any other library code runs. + * + * It ensures that by the time TensorFlow.js is imported by any other + * module, the necessary compatibility fixes for the current Node.js + * environment are already in place. + */ +import { applyTensorFlowPatch } from './utils/textEncoding.js' + +// Apply the TensorFlow.js platform patch if needed +applyTensorFlowPatch() diff --git a/cli-package/src/test-tensorflow-textencoder.ts b/cli-package/src/test-tensorflow-textencoder.ts index a5575f75..5adba19e 100644 --- a/cli-package/src/test-tensorflow-textencoder.ts +++ b/cli-package/src/test-tensorflow-textencoder.ts @@ -7,7 +7,7 @@ import { getTextEncoder, getTextDecoder -} from '@soulcraft/brainy/dist/utils/textEncoding.js' +} from './utils/textEncoding.js' import * as tf from '@tensorflow/tfjs' import '@tensorflow/tfjs-backend-cpu' diff --git a/cli-package/src/utils/textEncoding.ts b/cli-package/src/utils/textEncoding.ts index da0306e8..3bab39cc 100644 --- a/cli-package/src/utils/textEncoding.ts +++ b/cli-package/src/utils/textEncoding.ts @@ -1,101 +1,94 @@ /** - * Unified Text Encoding Utilities for CLI + * Unified Text Encoding Utilities * * This module provides a consistent way to handle text encoding/decoding across all environments * using the native TextEncoder/TextDecoder APIs. */ +/** + * Get a text encoder that works in the current environment + * @returns A TextEncoder instance + */ +export function getTextEncoder(): TextEncoder { + return new TextEncoder() +} + +/** + * Get a text decoder that works in the current environment + * @returns A TextDecoder instance + */ +export function getTextDecoder(): TextDecoder { + return new TextDecoder() +} + /** * Apply the TensorFlow.js platform patch if needed * This function patches the global object to provide a PlatformNode class * that uses native TextEncoder/TextDecoder */ export function applyTensorFlowPatch(): void { - // Only apply in Node.js environment - if ( - typeof global !== 'undefined' && - typeof process !== 'undefined' && - process.versions && - process.versions.node - ) { - try { - // Define a custom PlatformNode class - class PlatformNode { - util: any - textEncoder: TextEncoder - textDecoder: TextDecoder + try { + // Define a custom Platform class that works in both Node.js and browser environments + class Platform { + util: any + textEncoder: TextEncoder + textDecoder: TextDecoder - constructor() { - // Create a util object with necessary methods and constructors - this.util = { - // Add isFloat32Array and isTypedArray directly to util - isFloat32Array: (arr: any) => { - return !!( - arr instanceof Float32Array || - (arr && - Object.prototype.toString.call(arr) === - '[object Float32Array]') - ) - }, - isTypedArray: (arr: any) => { - return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)) - }, - // Use native TextEncoder and TextDecoder - TextEncoder: TextEncoder, - TextDecoder: TextDecoder - } - - // Initialize using native constructors - this.textEncoder = new TextEncoder() - this.textDecoder = new TextDecoder() + constructor() { + // Create a util object with necessary methods and constructors + this.util = { + // Use native TextEncoder and TextDecoder + TextEncoder: globalThis.TextEncoder || TextEncoder, + TextDecoder: globalThis.TextDecoder || TextDecoder } - // Define isFloat32Array directly on the instance - isFloat32Array(arr: any) { - return !!( - arr instanceof Float32Array || - (arr && - Object.prototype.toString.call(arr) === '[object Float32Array]') - ) - } - - // Define isTypedArray directly on the instance - isTypedArray(arr: any) { - return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)) - } + // Initialize using native constructors directly + this.textEncoder = new (globalThis.TextEncoder || TextEncoder)() + this.textDecoder = new (globalThis.TextDecoder || TextDecoder)() } - // Assign the PlatformNode class to the global object - ;(global as any).PlatformNode = PlatformNode - - // Also create an instance and assign it to global.platformNode (lowercase p) - ;(global as any).platformNode = new PlatformNode() - - // Ensure global.util exists and has the necessary methods - // This is needed because TensorFlow.js might look for these methods in global.util - if (!(global as any).util) { - ;(global as any).util = {} + // Define isFloat32Array directly on the instance + isFloat32Array(arr: any) { + return !!( + arr instanceof Float32Array || + (arr && + Object.prototype.toString.call(arr) === '[object Float32Array]') + ) } - // Add isFloat32Array method if it doesn't exist - if (!(global as any).util.isFloat32Array) { - ;(global as any).util.isFloat32Array = (arr: any) => { - return !!( - arr instanceof Float32Array || - (arr && - Object.prototype.toString.call(arr) === '[object Float32Array]') - ) - } + // Define isTypedArray directly on the instance + isTypedArray(arr: any) { + return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)) } - - // Add isTypedArray method if it doesn't exist - if (!(global as any).util.isTypedArray) { - ;(global as any).util.isTypedArray = (arr: any) => { - return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)) - } - } - } catch (error) { - console.warn('Failed to apply TensorFlow.js platform patch:', error) } + + // Get the global object in a way that works in both Node.js and browser + const globalObj = + typeof global !== 'undefined' + ? global + : typeof window !== 'undefined' + ? window + : typeof self !== 'undefined' + ? self + : {} + + // Only apply in Node.js environment + if ( + typeof process !== 'undefined' && + process.versions && + process.versions.node + ) { + // Assign the Platform class to the global object as PlatformNode for Node.js + ;(globalObj as any).PlatformNode = Platform + // Also create an instance and assign it to global.platformNode (lowercase p) + ;(globalObj as any).platformNode = new Platform() + } else if (typeof window !== 'undefined' || typeof self !== 'undefined') { + // In browser environments, we might need to provide similar functionality + // but we'll use a different name to avoid conflicts + ;(globalObj as any).PlatformBrowser = Platform + ;(globalObj as any).platformBrowser = new Platform() + } + } catch (error) { + console.warn('Failed to apply TensorFlow.js platform patch:', error) } } diff --git a/src/utils/textEncoding.ts b/src/utils/textEncoding.ts index 8d64a637..3bab39cc 100644 --- a/src/utils/textEncoding.ts +++ b/src/utils/textEncoding.ts @@ -38,13 +38,13 @@ export function applyTensorFlowPatch(): void { // Create a util object with necessary methods and constructors this.util = { // Use native TextEncoder and TextDecoder - TextEncoder: TextEncoder, - TextDecoder: TextDecoder + TextEncoder: globalThis.TextEncoder || TextEncoder, + TextDecoder: globalThis.TextDecoder || TextDecoder } - // Initialize using native constructors - this.textEncoder = new TextEncoder() - this.textDecoder = new TextDecoder() + // Initialize using native constructors directly + this.textEncoder = new (globalThis.TextEncoder || TextEncoder)() + this.textDecoder = new (globalThis.TextDecoder || TextDecoder)() } // Define isFloat32Array directly on the instance diff --git a/test-fix.js b/test-fix.js new file mode 100644 index 00000000..af000f6f --- /dev/null +++ b/test-fix.js @@ -0,0 +1,47 @@ +// Test script to verify the TextEncoder fix +import { applyTensorFlowPatch } from './dist/utils/textEncoding.js' + +console.log('Testing TextEncoder fix...') + +// Apply the TensorFlow.js platform patch +applyTensorFlowPatch() + +// Check if PlatformNode is defined in the global object +if (typeof global.PlatformNode === 'function') { + console.log('PlatformNode is defined in the global object') + + // Create an instance of PlatformNode + try { + const platform = new global.PlatformNode() + console.log('Successfully created PlatformNode instance') + + // Check if textEncoder is defined + if (platform.textEncoder) { + console.log('textEncoder is defined') + + // Test encoding a string + const testString = 'Hello, world! 👋' + const encoded = platform.textEncoder.encode(testString) + console.log(`Successfully encoded string: ${testString}`) + console.log(`Encoded: [${encoded}]`) + + // Test decoding + const decoded = platform.textDecoder.decode(encoded) + console.log(`Successfully decoded back to: ${decoded}`) + + if (testString === decoded) { + console.log('✅ TextEncoder/TextDecoder test passed!') + } else { + console.error('❌ TextEncoder/TextDecoder test failed!') + } + } else { + console.error('textEncoder is not defined in the platform instance') + } + } catch (error) { + console.error('Error creating PlatformNode instance:', error) + } +} else { + console.error('PlatformNode is not defined in the global object') +} + +console.log('Test completed')