UNPKG

box-typescript-sdk-gen

Version:
342 lines (294 loc) 8.82 kB
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<string> { 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<Uint8Array>({ start(controller) { controller.enqueue(new Uint8Array(buffer)); controller.close(); }, }); } export function decodeBase64ByteStream(data: string): ReadableStream { return new ReadableStream<Uint8Array>({ 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<Uint8Array>({ 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<Buffer> { 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<ByteStream> { 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<string> { 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(/(?<!\\)\//g, '\\/'); } /** * Compute the message signature * @see {@Link https://developer.box.com/en/guides/webhooks/handle/setup-signatures/} * * @param {string} body - The request body of the webhook message * @param {Object} headers - The request headers of the webhook message * @param {string} signatureKey - The signature to verify the message with * @param {string} escapeBody - Indicates if payload should be escaped or left as is * @returns {?string} - The message signature (or null, if it can't be computed) * @private */ export async function computeWebhookSignature( body: string, headers: { [key: string]: string; }, signatureKey: string, escapeBody: boolean = false, ): Promise<string | null> { 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<boolean> { 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<string> { return await sha1(data); }