2025-07-16 13:51:00 -07:00
|
|
|
import { isNode } from './environment.js'
|
2025-07-14 11:12:51 -07:00
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// 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
|
|
|
|
|
}
|
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
|
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-16 13:51:00 -07:00
|
|
|
* Monkeypatch TensorFlow.js's PlatformNode class to fix TextEncoder/TextDecoder issues
|
|
|
|
|
* CRITICAL: This runs immediately at the top level when this module is imported
|
2025-07-04 14:42:33 -07:00
|
|
|
*/
|
2025-07-16 13:51:00 -07:00
|
|
|
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
|
|
|
|
|
}
|
2025-07-14 11:12:51 -07:00
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// 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
|
|
|
|
|
}
|
2025-07-14 11:12:51 -07:00
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// 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)
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-16 13:51:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2025-07-28 16:00:05 -07:00
|
|
|
// 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
|
|
|
|
|
})()
|
2025-07-16 13:51:00 -07:00
|
|
|
|
2025-07-28 16:00:05 -07:00
|
|
|
if (globalObj && globalObj.Float32Array) {
|
|
|
|
|
const originalFloat32Array = globalObj.Float32Array
|
|
|
|
|
|
|
|
|
|
// Create a patched Float32Array class that handles alignment issues
|
|
|
|
|
const PatchedFloat32Array = class extends originalFloat32Array {
|
2025-07-16 13:51:00 -07:00
|
|
|
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
|
2025-07-14 11:12:51 -07:00
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// Check if the buffer slice is properly aligned
|
|
|
|
|
if (
|
|
|
|
|
(arg.byteLength - alignedByteOffset) % 4 !== 0 &&
|
|
|
|
|
length === undefined
|
|
|
|
|
) {
|
2025-07-28 16:00:05 -07:00
|
|
|
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)
|
|
|
|
|
}
|
2025-07-16 13:51:00 -07:00
|
|
|
} else {
|
|
|
|
|
super(arg, alignedByteOffset, alignedLength)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
super(arg, byteOffset, length)
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-16 13:51:00 -07:00
|
|
|
} as any
|
2025-07-14 11:12:51 -07:00
|
|
|
|
2025-07-28 16:00:05 -07:00
|
|
|
// 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)
|
|
|
|
|
}
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// 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
|
|
|
|
|
}
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// 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
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
2025-07-04 14:42:33 -07:00
|
|
|
}
|
2025-07-16 13:51:00 -07:00
|
|
|
} else {
|
|
|
|
|
checkAndPatchUtilShim(global._utilShim)
|
2025-07-14 11:12:51 -07:00
|
|
|
}
|
2025-07-07 10:19:04 -07:00
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// Also ensure it's available on globalThis
|
|
|
|
|
if (
|
|
|
|
|
typeof globalThis !== 'undefined' &&
|
|
|
|
|
!(globalThis as GlobalThis)._utilShim
|
|
|
|
|
) {
|
|
|
|
|
;(globalThis as GlobalThis)._utilShim = global._utilShim
|
2025-07-07 10:19:04 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-16 13:51:00 -07:00
|
|
|
// 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
|
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-16 13:51:00 -07:00
|
|
|
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<void> {
|
|
|
|
|
// 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
|
2025-07-04 14:42:33 -07:00
|
|
|
}
|
2025-07-14 11:12:51 -07:00
|
|
|
|
|
|
|
|
patchApplied = true
|
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
|
|
|
}
|
|
|
|
|
}
|
2025-07-16 13:51:00 -07:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
})
|