All files / src/utils checksum.ts

100% Statements 27/27
100% Branches 8/8
100% Functions 6/6
100% Lines 26/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67            3x 4x 4x 4x 3x 2x   1x     4x 1x   4x                 3x   14x             14x 11x 11x   1x 1x     3x     3x       12x 1x     1x         11x 11x 352x 11x    
// src/utils/checksum.ts
import { logger } from '../services/logger.client';
 
/**
 * Helper to read file as ArrayBuffer using FileReader (fallback for environments missing file.arrayBuffer)
 */
const readFileAsArrayBuffer = (file: File): Promise<ArrayBuffer> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      if (reader.result instanceof ArrayBuffer) {
        resolve(reader.result);
      } else {
        reject(new Error('FileReader result was not an ArrayBuffer'));
      }
    };
    reader.onerror = () => {
      reject(new Error(`FileReader error: ${reader.error?.message}`));
    };
    reader.readAsArrayBuffer(file);
  });
};
 
/**
 * Generates a SHA-256 checksum for a file.
 * @param file The file to hash.
 * @returns A promise that resolves to the hex string of the checksum.
 */
export const generateFileChecksum = async (file: File): Promise<string> => {
  // Debugging: Log file details to ensure we are receiving what we expect
  logger.debug(
    `generateFileChecksum processing file: name="${file.name}", type="${file.type}", size=${file.size}`,
  );
 
  let buffer: ArrayBuffer;
 
  // Check if arrayBuffer exists on the file object (it may be missing in JSDOM or older environments)
  if (typeof file.arrayBuffer === 'function') {
    try {
      buffer = await file.arrayBuffer();
    } catch (error) {
      logger.warn('file.arrayBuffer() threw an error, falling back to FileReader:', { error });
      buffer = await readFileAsArrayBuffer(file);
    }
  } else {
    logger.warn(
      'file.arrayBuffer is not a function on this File object. Using FileReader fallback.',
    );
    buffer = await readFileAsArrayBuffer(file);
  }
 
  // Debugging: Verify crypto availability
  if (!crypto || !crypto.subtle) {
    logger.error(
      'crypto.subtle is not available in this environment. SHA-256 generation will fail.',
    );
    throw new Error('crypto.subtle is not available. Cannot generate hash.');
  }
 
  // Ensure buffer is a TypedArray or ArrayBuffer that crypto.subtle accepts
  // Wrapping in Uint8Array handles both ArrayBuffer and SharedArrayBuffer, and helps with JSDOM compatibility
  const hashBuffer = await crypto.subtle.digest('SHA-256', new Uint8Array(buffer));
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
  return hashHex;
};