import { isNode } from './environment.js' // This module must be run BEFORE any TensorFlow.js code initializes // It directly patches the global environment to fix TextEncoder/TextDecoder issues // Also extend the globalThis interface interface GlobalThis { _utilShim?: any __TextEncoder__?: typeof TextEncoder __TextDecoder__?: typeof TextDecoder __brainy_util__?: any __utilShim?: any } /** * Flag to track if the patch has been applied */ let patchApplied = false /** * Monkeypatch TensorFlow.js's PlatformNode class to fix TextEncoder/TextDecoder issues * CRITICAL: This runs immediately at the top level when this module is imported */ if (typeof globalThis !== 'undefined' && isNode()) { try { // Ensure TextEncoder/TextDecoder are globally available if (typeof globalThis.TextEncoder === 'undefined') { globalThis.TextEncoder = TextEncoder } if (typeof globalThis.TextDecoder === 'undefined') { globalThis.TextDecoder = TextDecoder } // Patch global objects to handle the TensorFlow.js constructor issue // This is needed because TF accesses TextEncoder/TextDecoder as constructors via this.util if (typeof global !== 'undefined') { if (!global.TextEncoder) { global.TextEncoder = TextEncoder } if (!global.TextDecoder) { global.TextDecoder = TextDecoder } // Also set the special global constructors that TensorFlow can use safely global.__TextEncoder__ = TextEncoder global.__TextDecoder__ = TextDecoder } // CRITICAL FIX: Create a custom util object that TensorFlow.js can use // We'll make this available globally so TensorFlow.js can find it const customUtil = { TextEncoder: TextEncoder, TextDecoder: TextDecoder, types: { isFloat32Array: (arr: any) => arr instanceof Float32Array, isInt32Array: (arr: any) => arr instanceof Int32Array, isUint8Array: (arr: any) => arr instanceof Uint8Array, isUint8ClampedArray: (arr: any) => arr instanceof Uint8ClampedArray } } // Make the custom util available globally if (typeof global !== 'undefined') { global.__brainy_util__ = customUtil } // Try to patch the global require cache if possible if ( typeof global !== 'undefined' && global.require && global.require.cache ) { // Find the util module in the cache and patch it for (const key in global.require.cache) { if (key.endsWith('/util.js') || key === 'util') { const utilModule = global.require.cache[key] if (utilModule && utilModule.exports) { Object.assign(utilModule.exports, customUtil) } } } } // CRITICAL: Patch the Node.js util module directly try { const util = require('util') // Ensure TextEncoder and TextDecoder are available as constructors util.TextEncoder = TextEncoder as typeof util.TextEncoder util.TextDecoder = TextDecoder as typeof util.TextDecoder } catch (error) { // Ignore if util module is not available } // CRITICAL: Patch Float32Array to handle buffer alignment issues // This fixes the "byte length of Float32Array should be a multiple of 4" error // Get the appropriate global object for the current environment const globalObj = (() => { if (typeof globalThis !== 'undefined') return globalThis if (typeof global !== 'undefined') return global if (typeof self !== 'undefined') return self if (typeof window !== 'undefined') return window return {} as any // Fallback for unknown environments })() if (globalObj && globalObj.Float32Array) { const originalFloat32Array = globalObj.Float32Array // Create a patched Float32Array class that handles alignment issues const PatchedFloat32Array = class extends originalFloat32Array { constructor(arg?: any, byteOffset?: number, length?: number) { if (arg instanceof ArrayBuffer) { // Ensure buffer is properly aligned for Float32Array (multiple of 4 bytes) const alignedByteOffset = byteOffset || 0 const alignedLength = length !== undefined ? length : (arg.byteLength - alignedByteOffset) / 4 // Check if the buffer slice is properly aligned if ( (arg.byteLength - alignedByteOffset) % 4 !== 0 && length === undefined ) { try { // Create a new aligned buffer if the original isn't properly aligned const alignedByteLength = Math.floor((arg.byteLength - alignedByteOffset) / 4) * 4 const alignedBuffer = new ArrayBuffer(alignedByteLength) const sourceView = new Uint8Array( arg, alignedByteOffset, alignedByteLength ) const targetView = new Uint8Array(alignedBuffer) targetView.set(sourceView) super(alignedBuffer) } catch (error) { // If alignment fails, try the original approach console.warn('Float32Array alignment failed, using original constructor:', error) super(arg, alignedByteOffset, alignedLength) } } else { super(arg, alignedByteOffset, alignedLength) } } else { super(arg, byteOffset, length) } } } as any // Apply the patch to the global object try { // Preserve static methods and properties Object.setPrototypeOf(PatchedFloat32Array, originalFloat32Array) Object.defineProperty(PatchedFloat32Array, 'name', { value: 'Float32Array' }) Object.defineProperty(PatchedFloat32Array, 'BYTES_PER_ELEMENT', { value: 4 }) // Replace the global Float32Array with our patched version globalObj.Float32Array = PatchedFloat32Array } catch (error) { console.warn('Failed to patch Float32Array:', error) } } // CRITICAL: Patch any empty util shims that bundlers might create // This handles cases where bundlers provide empty shims for Node.js modules if (typeof global !== 'undefined') { // Look for common patterns of util shims in bundled code const checkAndPatchUtilShim = (obj: any) => { if (obj && typeof obj === 'object' && !obj.TextEncoder) { obj.TextEncoder = TextEncoder obj.TextDecoder = TextDecoder obj.types = obj.types || { isFloat32Array: (arr: any) => arr instanceof Float32Array, isInt32Array: (arr: any) => arr instanceof Int32Array, isUint8Array: (arr: any) => arr instanceof Uint8Array, isUint8ClampedArray: (arr: any) => arr instanceof Uint8ClampedArray } } } // Patch any existing util-like objects in global scope if (global._utilShim) { checkAndPatchUtilShim(global._utilShim) } // CRITICAL: Patch the bundled util shim directly // In bundled code, there's often a _utilShim object that needs patching if ( typeof globalThis !== 'undefined' && (globalThis as GlobalThis)._utilShim ) { checkAndPatchUtilShim((globalThis as GlobalThis)._utilShim) } // CRITICAL: Create and patch a global _utilShim if it doesn't exist // This ensures the bundled code will find the patched version if (!global._utilShim) { global._utilShim = { TextEncoder: TextEncoder, TextDecoder: TextDecoder, types: { isFloat32Array: (arr: any) => arr instanceof Float32Array, isInt32Array: (arr: any) => arr instanceof Int32Array, isUint8Array: (arr: any) => arr instanceof Uint8Array, isUint8ClampedArray: (arr: any) => arr instanceof Uint8ClampedArray } } } else { checkAndPatchUtilShim(global._utilShim) } // Also ensure it's available on globalThis if ( typeof globalThis !== 'undefined' && !(globalThis as GlobalThis)._utilShim ) { ;(globalThis as GlobalThis)._utilShim = global._utilShim } // Set up a property descriptor to catch util shim assignments try { Object.defineProperty(global, '_utilShim', { get() { return this.__utilShim || {} }, set(value) { checkAndPatchUtilShim(value) this.__utilShim = value }, configurable: true }) } catch (e) { // Ignore if property can't be defined } // Also set up property descriptor on globalThis try { Object.defineProperty(globalThis, '_utilShim', { get() { return this.__utilShim || {} }, set(value) { checkAndPatchUtilShim(value) this.__utilShim = value }, configurable: true }) } catch (e) { // Ignore if property can't be defined } } console.log( 'Brainy: Successfully patched TensorFlow.js PlatformNode at module load time' ) patchApplied = true } catch (error) { console.warn( 'Brainy: Failed to apply early TensorFlow.js platform patch:', error ) } } /** * Apply the TensorFlow.js platform patch if it hasn't been applied already * This is a safety measure in case the module-level patch didn't run * Now works across all environments: browser, Node.js, and serverless/server */ export async function applyTensorFlowPatch(): Promise { // Apply patches for all non-browser environments that might need TensorFlow.js compatibility // This includes Node.js, serverless environments, and other server environments const isBrowserEnv = typeof window !== 'undefined' && typeof document !== 'undefined' if (isBrowserEnv) { return // Browser environments don't need these patches } // Get the appropriate global object for the current environment const globalObj = (() => { if (typeof globalThis !== 'undefined') return globalThis if (typeof global !== 'undefined') return global if (typeof self !== 'undefined') return self return {} as any // Fallback for unknown environments })() // Check if the critical globals exist, not just the flag // This allows re-patching if globals have been deleted const needsPatch = !patchApplied || typeof globalObj.__TextEncoder__ === 'undefined' || typeof globalObj.__TextDecoder__ === 'undefined' if (!needsPatch) { return } try { console.log( 'Brainy: Applying TensorFlow.js platform patch via function call' ) // CRITICAL FIX: Patch the global environment to ensure TextEncoder/TextDecoder are available // This approach works by ensuring the global constructors are available before TensorFlow.js loads // Now works across all environments: Node.js, serverless, and other server environments // Make sure TextEncoder and TextDecoder are available globally if (!globalObj.TextEncoder) { globalObj.TextEncoder = TextEncoder } if (!globalObj.TextDecoder) { globalObj.TextDecoder = TextDecoder } // Also set the special global constructors that TensorFlow can use safely ;(globalObj as any).__TextEncoder__ = TextEncoder ;(globalObj as any).__TextDecoder__ = TextDecoder // Also patch process.versions to ensure TensorFlow.js detects Node.js correctly if (typeof process !== 'undefined' && process.versions) { // Ensure TensorFlow.js sees this as a Node.js environment if (!process.versions.node) { process.versions.node = process.version } } // CRITICAL: Patch the Node.js util module directly try { const util = await import('util') // Ensure TextEncoder and TextDecoder are available as constructors util.TextEncoder = TextEncoder as typeof util.TextEncoder util.TextDecoder = TextDecoder as typeof util.TextDecoder } catch (error) { // Ignore if util module is not available } patchApplied = true } catch (error) { console.warn('Brainy: Failed to apply TensorFlow.js platform patch:', error) } } export function getTextEncoder(): TextEncoder { return new TextEncoder() } export function getTextDecoder(): TextDecoder { return new TextDecoder() } // Apply patch immediately applyTensorFlowPatch().catch((error) => { console.warn('Failed to apply TensorFlow patch at module load:', error) })