@maximai/maxim-js
Version:
Maxim AI JS SDK. Visit https://getmaxim.ai for more info.
225 lines • 6.85 kB
JavaScript
;
/*
* React Native platform adapter. Avoids Node built-ins and provides safe
* fallbacks. File IO and CSV are disabled by default. Randomness prefers
* expo-crypto when available, falling back to Math.random-based polyfill.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.reactNativeAdapter = void 0;
// Lazy import for expo-crypto if present
let expoCryptoRandomBytes = null;
let expoCryptoAvailable = false;
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const expoCrypto = require("expo-crypto");
if (expoCrypto && typeof expoCrypto.getRandomBytesAsync === "function") {
// Store sync wrapper for expo-crypto async function
expoCryptoRandomBytes = (size) => {
// For sync usage, we'll need to implement a cache or use different approach
// For now, throw error indicating async version should be used
throw new Error("expo-crypto requires async usage. Use platform.crypto.randomBytesAsync() instead.");
};
expoCryptoAvailable = true;
}
}
catch {
// expo-crypto not available
}
const timers = {
setInterval: (handler, ms) => setInterval(handler, ms),
clearInterval: (handle) => clearInterval(handle),
maybeUnref: (_handle) => {
// No-op on RN; handle is a number
},
};
const rnFs = {
hasAccessToFilesystem() {
// Assume /tmp-like access is not standard in RN; disable
return false;
},
existsSync(_p) {
return false;
},
mkdirpSync(_p) {
// no-op
},
async readFile(_p) {
return { data: new Uint8Array() };
},
readFileSync(_p) {
return new Uint8Array();
},
async writeFile(_p, _data) {
// no-op
},
readdirSync(_p) {
return [];
},
rmSync(_p) {
// no-op
},
statSync(_p) {
return { size: 0 };
},
};
const rnPath = {
basename: (filePath) => filePath.split("/").pop() || filePath,
extname: (filePath) => {
const idx = filePath.lastIndexOf(".");
return idx >= 0 ? filePath.slice(idx) : "";
},
join: (...parts) => parts.join("/").replace(/\/+/, "/"),
};
const rnMime = {
lookup: (filePath) => {
const ext = (filePath.split(".").pop() || "").toLowerCase();
const map = {
jpg: "image/jpeg",
jpeg: "image/jpeg",
png: "image/png",
gif: "image/gif",
pdf: "application/pdf",
txt: "text/plain",
json: "application/json",
html: "text/html",
css: "text/css",
js: "application/javascript",
zip: "application/zip",
csv: "text/csv",
};
return map[ext] || false;
},
};
const net = {
httpAgent: (_options) => {
// Axios uses native stack on RN; no agents needed
return undefined;
},
httpsAgent: (_options) => {
return undefined;
},
};
function insecureRandomBytes(size) {
const bytes = new Uint8Array(size);
for (let i = 0; i < size; i++)
bytes[i] = Math.floor(Math.random() * 256);
return bytes;
}
function toUtf8Bytes(input) {
if (typeof input !== "string")
return input;
if (typeof TextEncoder !== "undefined")
return new TextEncoder().encode(input);
// Fallback: naive UTF-8 encode
const utf8 = [];
for (let i = 0; i < input.length; i++) {
const c = input.charCodeAt(i);
if (c < 0x80)
utf8.push(c);
else if (c < 0x800)
utf8.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
else
utf8.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
}
return new Uint8Array(utf8);
}
function simpleHashHex(input) {
// 32-bit FNV-1a repeated to produce 128-bit hex (non-crypto)
const bytes = toUtf8Bytes(input);
const fnv = (seed) => {
let h = seed >>> 0;
for (let i = 0; i < bytes.length; i++) {
h ^= bytes[i];
h = Math.imul(h, 0x01000193);
}
return (h >>> 0).toString(16).padStart(8, "0");
};
return fnv(0x811c9dc5) + fnv(0x811c9dc5 ^ 0xdeadbeef) + fnv(0x811c9dc5 ^ 0xa5a5a5a5) + fnv(0x811c9dc5 ^ 0x12345678);
}
async function secureRandomBytesAsync(size) {
if (expoCryptoAvailable) {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const expoCrypto = require("expo-crypto");
const result = await expoCrypto.getRandomBytesAsync(size);
return new Uint8Array(result);
}
catch (error) {
console.warn("[Maxim-SDK] expo-crypto failed, falling back to insecure random:", error);
}
}
return insecureRandomBytes(size);
}
const rnCrypto = {
randomBytes: (size) => {
// Synchronous version - falls back to insecure for compatibility
// Recommend using randomBytesAsync for better security on RN
if (expoCryptoAvailable) {
console.warn("[Maxim-SDK] Using insecure randomBytes. Consider using platform.crypto.randomBytesAsync() for better security.");
}
return insecureRandomBytes(size);
},
randomBytesAsync: secureRandomBytesAsync,
createHash: (_algorithm) => ({
update: (data) => ({
digest: (_encoding) => simpleHashHex(data),
}),
}),
hostname: () => "react-native-device",
isSecureRandomAvailable: expoCryptoAvailable,
};
const stream = {
Transform: class Transform {
constructor(options) {
this.options = options;
this._transform = options.transform;
this._flush = options.flush;
this._destroyed = false;
}
transform(chunk, encoding, callback) {
if (this._destroyed)
return;
try {
this._transform(chunk, encoding, callback);
}
catch (err) {
callback(err);
}
}
flush(callback) {
if (this._destroyed)
return;
try {
this._flush(callback);
}
catch (err) {
callback(err);
}
}
destroy() {
this._destroyed = true;
}
pipe(destination) {
// Simple pipe implementation for React Native
return destination;
}
},
};
const features = {
csvSupported: false,
fileIoSupported: false,
};
exports.reactNativeAdapter = {
name: "react-native",
fs: rnFs,
path: rnPath,
mime: rnMime,
timers,
net,
crypto: rnCrypto,
stream,
tmpdir: () => "/tmp",
features,
};
exports.default = exports.reactNativeAdapter;
//# sourceMappingURL=reactNative.js.map