From 4fb1ff7cfc36a608eb4ca1c48a978e5829deb4b2 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Fri, 4 Jul 2025 11:35:19 -0700 Subject: [PATCH] **chore(version): bump version to 0.9.33 and update references** - Updated version to `0.9.33` in `README.md`, `cli-package/package.json`, and `src/utils/version.ts`. - Synced `@soulcraft/brainy` dependency version to `0.9.33` in the CLI package for consistency. - Updated npm version badge in `README.md` to reflect the new version. - Refactored `TextEncoder` and `TextDecoder` implementations in `src/utils/tensorflowUtils.ts`: - Added dynamic environment-aware loading for improved Node.js and browser compatibility. - Enhanced fallback mechanisms for environments without global support. This update ensures consistent versioning across the project and improves the robustness of utility methods for broader environment compatibility. --- README.md | 2 +- cli-package/package.json | 4 +- src/utils/tensorflowUtils.ts | 200 ++++++++++++++++++++++++++++++----- src/utils/version.ts | 2 +- 4 files changed, 175 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index e5575fa1..6c91ad13 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Node.js](https://img.shields.io/badge/node-%3E%3D23.0.0-brightgreen.svg)](https://nodejs.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-5.1.6-blue.svg)](https://www.typescriptlang.org/) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) -[![npm](https://img.shields.io/badge/npm-v0.9.32-blue.svg)](https://www.npmjs.com/package/@soulcraft/brainy) +[![npm](https://img.shields.io/badge/npm-v0.9.33-blue.svg)](https://www.npmjs.com/package/@soulcraft/brainy) [//]: # ([![Cartographer](https://img.shields.io/badge/Cartographer-Official%20Standard-brightgreen)](https://github.com/sodal-project/cartographer)) diff --git a/cli-package/package.json b/cli-package/package.json index 113b8b06..5797478c 100644 --- a/cli-package/package.json +++ b/cli-package/package.json @@ -1,6 +1,6 @@ { "name": "@soulcraft/brainy-cli", - "version": "0.9.32", + "version": "0.9.33", "description": "Command-line interface for the Brainy vector graph database", "type": "module", "bin": { @@ -40,7 +40,7 @@ "url": "git+https://github.com/soulcraft-research/brainy.git" }, "dependencies": { - "@soulcraft/brainy": "0.9.32", + "@soulcraft/brainy": "0.9.33", "commander": "^14.0.0", "omelette": "^0.4.17" }, diff --git a/src/utils/tensorflowUtils.ts b/src/utils/tensorflowUtils.ts index 70213e8e..f6c57dd5 100644 --- a/src/utils/tensorflowUtils.ts +++ b/src/utils/tensorflowUtils.ts @@ -10,6 +10,93 @@ import type { PlatformNodeObject } from '../types/tensorflowTypes.js' +// Flag to track if we're in a Node.js environment +const isNodeEnvironment = + typeof process !== 'undefined' && + process.versions && + process.versions.node + +// Promise to load Node.js TextEncoder and TextDecoder +let nodeUtilPromise: Promise | null = null + +// Function to get TextEncoder based on environment +async function getTextEncoder(): Promise { + // First check if TextEncoder is globally available + if (typeof TextEncoder !== 'undefined') { + return TextEncoder + } + + // Then try to get it from Node.js util module + if (isNodeEnvironment) { + try { + // Check if we can access the util module directly (in case it's already loaded) + // @ts-ignore - Ignore TypeScript error for global.require + if (typeof global !== 'undefined' && global.require && global.require.cache && global.require.cache['util']) { + // @ts-ignore - Ignore TypeScript error for global.require + const cachedUtil = global.require('util') + if (cachedUtil && cachedUtil.TextEncoder) { + return cachedUtil.TextEncoder + } + } + + // Otherwise, use dynamic import + if (!nodeUtilPromise) { + nodeUtilPromise = import('util').catch(error => { + console.warn('Failed to import from util:', error) + return null + }) + } + + const util = await nodeUtilPromise + return util?.TextEncoder || null + } catch (error) { + console.warn('Error accessing TextEncoder:', error) + return null + } + } + + return null +} + +// Function to get TextDecoder based on environment +async function getTextDecoder(): Promise { + // First check if TextDecoder is globally available + if (typeof TextDecoder !== 'undefined') { + return TextDecoder + } + + // Then try to get it from Node.js util module + if (isNodeEnvironment) { + try { + // Check if we can access the util module directly (in case it's already loaded) + // @ts-ignore - Ignore TypeScript error for global.require + if (typeof global !== 'undefined' && global.require && global.require.cache && global.require.cache['util']) { + // @ts-ignore - Ignore TypeScript error for global.require + const cachedUtil = global.require('util') + if (cachedUtil && cachedUtil.TextDecoder) { + return cachedUtil.TextDecoder + } + } + + // Otherwise, use dynamic import + if (!nodeUtilPromise) { + nodeUtilPromise = import('util').catch(error => { + console.warn('Failed to import from util:', error) + return null + }) + } + + const util = await nodeUtilPromise + return util?.TextDecoder || null + } catch (error) { + console.warn('Error accessing TextDecoder:', error) + return null + } + } + + return null +} + // Define a global PlatformNode class for TensorFlow.js compatibility // This is needed because TensorFlow.js creates its own PlatformNode instance // and we need to ensure it uses the correct TextEncoder/TextDecoder @@ -32,39 +119,94 @@ if ( this.util = { isFloat32Array, isTypedArray, - // Use the global TextEncoder/TextDecoder directly - TextEncoder: - typeof TextEncoder !== 'undefined' - ? TextEncoder - : function () { - console.warn( - 'TextEncoder constructor called but not available' - ) - return { encode: (str: string) => new Uint8Array([]) } - }, - TextDecoder: - typeof TextDecoder !== 'undefined' - ? TextDecoder - : function () { - console.warn( - 'TextDecoder constructor called but not available' - ) - return { decode: (bytes: Uint8Array) => '' } + // Provide synchronous constructors that return objects with the expected interface + // TensorFlow.js expects these to be synchronous + TextEncoder: function() { + // For Node.js environments, create a direct implementation + if (isNodeEnvironment) { + return { + encode: (str: string) => { + try { + // Direct implementation for Node.js + // Convert string to UTF-8 encoded Uint8Array + const buffer = Buffer.from(str, 'utf-8') + const uint8Array = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength) + return uint8Array + } catch (error) { + console.warn('Error in Node.js TextEncoder implementation:', error) + return new Uint8Array([]) + } } + } + } + // For browser environments, use the global TextEncoder + else if (typeof TextEncoder !== 'undefined') { + return new TextEncoder() + } + // Fallback for other environments + else { + console.warn('TextEncoder not available in this environment') + return { + encode: (str: string) => new Uint8Array([]) + } + } + }, + TextDecoder: function() { + // For Node.js environments, create a direct implementation + if (isNodeEnvironment) { + return { + decode: (bytes: Uint8Array) => { + try { + // Direct implementation for Node.js + // Convert Uint8Array to string using Buffer + const buffer = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength) + return buffer.toString('utf-8') + } catch (error) { + console.warn('Error in Node.js TextDecoder implementation:', error) + return '' + } + } + } + } + // For browser environments, use the global TextDecoder + else if (typeof TextDecoder !== 'undefined') { + return new TextDecoder() + } + // Fallback for other environments + else { + console.warn('TextDecoder not available in this environment') + return { + decode: (bytes: Uint8Array) => '' + } + } + } } - // Initialize TextEncoder/TextDecoder directly from globals - if (typeof TextEncoder !== 'undefined') { - this.textEncoder = new TextEncoder() - } else { - console.warn('TextEncoder is not available in this environment') + // Initialize TextEncoder/TextDecoder + this.initializeEncoders() + } + + // Initialize TextEncoder and TextDecoder asynchronously + async initializeEncoders() { + try { + const TextEncoderClass = await getTextEncoder() + if (TextEncoderClass) { + this.textEncoder = new TextEncoderClass() + } else { + console.warn('TextEncoder is not available in this environment') + this.textEncoder = { encode: (str: string) => new Uint8Array([]) } + } + + const TextDecoderClass = await getTextDecoder() + if (TextDecoderClass) { + this.textDecoder = new TextDecoderClass() + } else { + console.warn('TextDecoder is not available in this environment') + this.textDecoder = { decode: (bytes: Uint8Array) => '' } + } + } catch (error) { + console.error('Failed to initialize encoders:', error) this.textEncoder = { encode: (str: string) => new Uint8Array([]) } - } - - if (typeof TextDecoder !== 'undefined') { - this.textDecoder = new TextDecoder() - } else { - console.warn('TextDecoder is not available in this environment') this.textDecoder = { decode: (bytes: Uint8Array) => '' } } } diff --git a/src/utils/version.ts b/src/utils/version.ts index b99ece5d..dcf1fed8 100644 --- a/src/utils/version.ts +++ b/src/utils/version.ts @@ -3,4 +3,4 @@ * Do not modify this file directly. */ -export const VERSION = '0.9.32'; +export const VERSION = '0.9.33';