import { Buffer } from 'buffer'; import { createHMAC, createSHA1, createSHA256, sha1 } from 'hash-wasm'; export { Buffer }; export type ByteStream = ReadableStream; export class FormData {} export type AgentOptions = any; export type Agent = any; export type HashName = 'sha1'; export type DigestHashType = 'base64'; export class utilLib { static inspect = { custom: 'inspect.custom', }; } export class Hash { hash: any; algorithm: HashName; constructor({ algorithm }: { algorithm: HashName }) { this.algorithm = algorithm; this.hash = undefined; } async initializeBrowserHash() { switch (this.algorithm) { case 'sha1': this.hash = await createSHA1(); this.hash.init(); break; default: throw new Error(`Unsupported algorithm: ${this.algorithm}`); } } async updateHash(data: Buffer) { if (!this.hash) { await this.initializeBrowserHash(); } this.hash.update(data); } async digestHash(encoding: DigestHashType = 'base64'): Promise { if (!this.hash) { await this.initializeBrowserHash(); } const d = this.hash.digest('binary'); switch (encoding) { case 'base64': return Buffer.from(d).toString('base64'); default: throw new Error(`Unsupported encoding: ${encoding}`); } } } export function generateByteBuffer(size: number): Buffer { // Maximum size for crypto.getRandomValues is 65536 bytes const MAX_CHUNK_SIZE = 65536; const buffer = new Uint8Array(size); for (let offset = 0; offset < size; offset += MAX_CHUNK_SIZE) { const length = Math.min(MAX_CHUNK_SIZE, size - offset); const chunk = new Uint8Array(length); window.crypto.getRandomValues(chunk); buffer.set(chunk, offset); } return Buffer.from(buffer); } export function generateReadableStreamFromFile( file: any, chunkSize: number = 1024 * 1024, ): ReadableStream { let offset = 0; return new ReadableStream({ async pull(controller) { if (offset >= file.size) { controller.close(); return; } const chunk = file.slice(offset, offset + chunkSize); const arrayBuffer = await chunk.arrayBuffer(); controller.enqueue(new Uint8Array(arrayBuffer)); offset += chunkSize; }, }); } export function generateByteStreamFromBuffer( buffer: Buffer | ArrayBuffer, ): ReadableStream { return new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(buffer)); controller.close(); }, }); } export function decodeBase64ByteStream(data: string): ReadableStream { return new ReadableStream({ start(controller) { const decodedStr = atob(data); const buffer = new ArrayBuffer(decodedStr.length); const array = new Uint8Array(buffer); for (let i = 0; i < decodedStr.length; i++) { array[i] = decodedStr.charCodeAt(i); } controller.enqueue(array); controller.close(); }, }); } export function stringToByteStream(data: string): ReadableStream { return new ReadableStream({ start(controller) { const buffer = new ArrayBuffer(data.length); const array = new Uint8Array(buffer); for (let i = 0; i < data.length; i++) { array[i] = data.charCodeAt(i); } controller.enqueue(array); controller.close(); }, }); } export function getEnvVar(name: string): string { if ( typeof window !== 'undefined' && (window as any).env && (window as any).env[name] ) { return (window as any).env[name]; } return ''; } export function setEnvVar(name: string, value: string): void { if (typeof window === 'undefined') { throw new Error('This function requires a browser environment'); } if (!(window as any).env) { (window as any).env = {}; } (window as any).env[name] = value; } export async function readByteStream(byteStream: ByteStream): Promise { const buffers: Buffer[] = []; // Browser ReadableStream const reader = byteStream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; buffers.push(Buffer.from(value)); } return Buffer.concat(buffers); } export async function* iterateChunks( stream: ByteStream, chunkSize: number, fileSize: number, ): AsyncGenerator { let buffers: Buffer[] = []; let totalSize = 0; let consumedSize = 0; const reader = stream.getReader(); while (consumedSize < fileSize) { const { done, value } = await reader.read(); if (done) break; const data = Buffer.from(value); consumedSize += data.length; buffers.push(data); totalSize += data.length; yield* yieldChunks(); } if (consumedSize !== fileSize) { throw new Error( `Stream size ${consumedSize} does not match expected file size ${fileSize}`, ); } if (totalSize > 0) { yield await generateByteStreamFromBuffer(Buffer.concat(buffers)); } /** * Yields chunks of the desired `chunkSize` if enough data is available. */ async function* yieldChunks() { if (totalSize < chunkSize) return; const buffer = Buffer.concat(buffers); let start = 0; while (totalSize >= chunkSize) { yield await generateByteStreamFromBuffer( buffer.subarray(start, start + chunkSize), ); start += chunkSize; totalSize -= chunkSize; } buffers = totalSize > 0 ? [buffer.subarray(start)] : []; } } export type JwtKey = any; export type JwtAlgorithm = any; export type JwtSignOptions = any; export interface PrivateKeyDecryptor {} export class DefaultPrivateKeyDecryptor implements PrivateKeyDecryptor { constructor(fields: any) {} } /** * Creates a JWT assertion. * * @param claims * @param key * @param options * @returns */ export async function createJwtAssertion( claims: { readonly [key: string]: any; }, key: JwtKey, options: JwtSignOptions, ): Promise { throw new Error('This function is not supported in the browser'); } /** * Reads a text file and returns its content. */ export function readTextFromFile(filepath: string): string { throw new Error('This function is not supported in the browser'); } /** * Create web agent from proxy agent options. */ export function createAgent(options?: AgentOptions, proxyConfig?: any): Agent { return undefined; } /** * Stringify JSON with escaped multibyte Unicode characters to ensure computed signatures match PHP's default behavior * * @param {Object} body - The parsed JSON object * @returns {string} - Stringified JSON with escaped multibyte Unicode characters * @private */ export function jsonStringifyWithEscapedUnicode(body: string) { return body .replace( /[\u007f-\uffff]/g, (char) => `\\u${`0000${char.charCodeAt(0).toString(16)}`.slice(-4)}`, ) .replace(/(? { if (headers['box-signature-version'] !== '1') { return null; } if (headers['box-signature-algorithm'] !== 'HmacSHA256') { return null; } let signature: string | null = null; const escapedBody = escapeBody ? jsonStringifyWithEscapedUnicode(body) : body; const hashFunc = createSHA256(); const hmac = await createHMAC(hashFunc, signatureKey); hmac.init(); hmac.update(escapedBody); hmac.update(headers['box-delivery-timestamp']); const result = await hmac.digest('binary'); signature = Buffer.from(result).toString('base64'); return signature; } export async function compareSignatures( expectedSignature: string | null, receivedSignature: string | null, ): Promise { if (!expectedSignature || !receivedSignature) { return false; } if (expectedSignature.length !== receivedSignature.length) return false; let result = 0; for (let i = 0; i < expectedSignature.length; i++) { result |= expectedSignature.charCodeAt(i) ^ receivedSignature.charCodeAt(i); } return result === 0; } export async function calculateMD5Hash(data: string | Buffer): Promise { return await sha1(data); }