2025-07-14 11:12:51 -07:00
// In: @soulcraft/brainy/src/utils/textEncoding.ts
2025-07-04 14:42:33 -07:00
/ * *
2025-07-14 11:12:51 -07:00
* Checks if the code is running in a Node . js environment .
2025-07-04 14:42:33 -07:00
* /
2025-07-14 11:12:51 -07:00
function isNode ( ) : boolean {
return (
typeof process !== 'undefined' &&
process . versions != null &&
process . versions . node != null
)
}
2025-07-04 14:42:33 -07:00
/ * *
2025-07-14 11:12:51 -07:00
* Global flag to track if TensorFlow . js has been initialized
* This helps prevent multiple registrations of the same kernels
2025-07-04 14:42:33 -07:00
* /
2025-07-14 11:12:51 -07:00
const TENSORFLOW_INITIALIZED = Symbol ( 'TENSORFLOW_INITIALIZED' )
2025-07-04 14:42:33 -07:00
/ * *
2025-07-14 11:12:51 -07:00
* Flag to track if the patch has been applied
* This prevents multiple applications of the patch
2025-07-04 14:42:33 -07:00
* /
2025-07-14 11:12:51 -07:00
let patchApplied = false
2025-07-04 14:42:33 -07:00
/ * *
2025-07-14 11:12:51 -07:00
* CRITICAL : Applies a compatibility patch for TensorFlow . js when running in a modern
* Node . js ES Module environment . This must be called before any TensorFlow . js
* modules are imported .
*
* This function prevents the "TextEncoder is not a constructor" error by preemptively
* creating a compliant PlatformNode class with proper TextEncoder / TextDecoder support
* and placing it on the global object where TensorFlow . js expects to find it .
*
* The race condition occurs because TensorFlow . js ' s platform detection might run
* before the necessary global objects are properly initialized in certain Node . js
* environments , particularly when the package is being used by other applications .
*
* This function is called from setup . ts , which must be the first import in unified . ts
* to ensure the patch is applied before any TensorFlow . js code is executed .
*
* It also applies a patch to prevent duplicate kernel registrations when TensorFlow . js
* is imported multiple times .
2025-07-04 14:42:33 -07:00
* /
export function applyTensorFlowPatch ( ) : void {
2025-07-14 11:12:51 -07:00
// Prevent multiple applications of the patch
if ( patchApplied ) {
return
}
if ( ! isNode ( ) ) {
return // Patch is only for Node.js
}
// In modern Node.js with ES Modules, TensorFlow.js can fail during its
// initial platform detection. This patch preempts that logic by creating
// a compliant "Platform" class that uses the standard global TextEncoder
// and placing it on the global object where TensorFlow.js expects to find it.
2025-07-07 10:19:04 -07:00
try {
2025-07-14 11:12:51 -07:00
// Ensure TextEncoder and TextDecoder are available
const nodeUtil = require ( 'util' )
const TextEncoderPolyfill = nodeUtil . TextEncoder || global . TextEncoder
const TextDecoderPolyfill = nodeUtil . TextDecoder || global . TextDecoder
if ( ! TextEncoderPolyfill || ! TextDecoderPolyfill ) {
console . warn (
'Brainy: TextEncoder or TextDecoder not available, attempting to polyfill'
)
// If still not available, try to use a simple polyfill
if ( ! TextEncoderPolyfill ) {
class SimpleTextEncoder {
encode ( input : string ) : Uint8Array {
const buf = Buffer . from ( input , 'utf8' )
return new Uint8Array ( buf . buffer , buf . byteOffset , buf . byteLength )
}
}
global . TextEncoder = SimpleTextEncoder
}
if ( ! TextDecoderPolyfill ) {
class SimpleTextDecoder {
decode ( input? : Uint8Array ) : string {
if ( ! input ) return ''
return Buffer . from (
input . buffer ,
input . byteOffset ,
input . byteLength
) . toString ( 'utf8' )
}
}
global . TextDecoder = SimpleTextDecoder
}
} else {
// Ensure they're available globally
global . TextEncoder = TextEncoderPolyfill
global . TextDecoder = TextDecoderPolyfill
}
// Create a PlatformNode implementation that uses the polyfilled TextEncoder/TextDecoder
class BrainyPlatformNode {
// Use the polyfilled TextEncoder/TextDecoder
readonly util = {
TextEncoder : global.TextEncoder ,
TextDecoder : global.TextDecoder ,
// Add utility functions that TensorFlow.js might need
isTypedArray : ( arr : any ) : boolean = > {
return ArrayBuffer . isView ( arr ) && ! ( arr instanceof DataView )
} ,
isFloat32Array : ( arr : any ) : boolean = > {
return (
arr instanceof Float32Array ||
( arr &&
Object . prototype . toString . call ( arr ) === '[object Float32Array]' )
)
}
}
// Create instances of the encoder/decoder
readonly textEncoder : any
readonly textDecoder : any
2025-07-07 10:19:04 -07:00
constructor ( ) {
2025-07-14 11:12:51 -07:00
try {
// Initialize encoders using constructors
this . textEncoder = new global . TextEncoder ( )
this . textDecoder = new global . TextDecoder ( )
} catch ( e ) {
console . warn (
'Brainy: Error creating TextEncoder/TextDecoder instances:' ,
e
)
// Provide fallback implementations if instantiation fails
this . textEncoder = {
encode : ( input : string ) : Uint8Array = > {
const buf = Buffer . from ( input , 'utf8' )
return new Uint8Array ( buf . buffer , buf . byteOffset , buf . byteLength )
}
}
this . textDecoder = {
decode : ( input? : Uint8Array ) : string = > {
if ( ! input ) return ''
return Buffer . from (
input . buffer ,
input . byteOffset ,
input . byteLength
) . toString ( 'utf8' )
}
}
2025-07-04 14:42:33 -07:00
}
2025-07-14 11:12:51 -07:00
}
2025-07-07 10:19:04 -07:00
2025-07-14 11:12:51 -07:00
isTypedArray ( arr : any ) : arr is Float32Array | Int32Array | Uint8Array {
return ArrayBuffer . isView ( arr ) && ! ( arr instanceof DataView )
2025-07-07 10:19:04 -07:00
}
2025-07-14 11:12:51 -07:00
isFloat32Array ( arr : any ) : arr is Float32Array {
return (
2025-07-07 10:19:04 -07:00
arr instanceof Float32Array ||
( arr &&
2025-07-11 11:11:56 -07:00
Object . prototype . toString . call ( arr ) === '[object Float32Array]' )
2025-07-07 10:19:04 -07:00
)
2025-07-04 14:42:33 -07:00
}
2025-07-07 10:19:04 -07:00
}
2025-07-04 14:42:33 -07:00
2025-07-14 11:12:51 -07:00
// Assign the custom platform class to the global scope.
// TensorFlow.js specifically looks for `PlatformNode`.
global . PlatformNode = BrainyPlatformNode
// Also create an instance and assign it to global.platformNode (lowercase p)
// This is needed for some TensorFlow.js versions
global . platformNode = new BrainyPlatformNode ( )
// Set up a global flag to track TensorFlow.js initialization
global [ TENSORFLOW_INITIALIZED ] = false
// Monkey patch the registerKernel function to prevent duplicate registrations
// This will be applied when TensorFlow.js is imported
const originalRegisterKernel = global . registerKernel
if ( ! originalRegisterKernel ) {
// Set up a handler to intercept the registerKernel function when it's defined
Object . defineProperty ( global , 'registerKernel' , {
set : function ( newRegisterKernel ) {
// Replace the setter with our patched version
Object . defineProperty ( global , 'registerKernel' , {
value : function ( kernel : any ) {
// Check if this kernel is already registered
const kernelName = kernel . kernelName
const backendName = kernel . backendName
const key = ` ${ kernelName } _ ${ backendName } `
// Use a global registry to track registered kernels
if ( ! global . __REGISTERED_KERNELS__ ) {
global . __REGISTERED_KERNELS__ = new Set ( )
}
// If this kernel is already registered, skip it
if ( global . __REGISTERED_KERNELS__ . has ( key ) ) {
return
}
// Otherwise, register it and add it to our registry
global . __REGISTERED_KERNELS__ . add ( key )
return newRegisterKernel ( kernel )
} ,
configurable : true ,
writable : true
} )
} ,
configurable : true
} )
2025-07-04 14:42:33 -07:00
}
2025-07-14 11:12:51 -07:00
// Mark the patch as applied
patchApplied = true
console . log ( 'Brainy: Successfully applied TensorFlow.js platform patch' )
2025-07-07 10:19:04 -07:00
} catch ( error ) {
2025-07-14 11:12:51 -07:00
console . warn ( 'Brainy: Failed to apply TensorFlow.js platform patch:' , error )
2025-07-04 14:42:33 -07:00
}
}