Bonsai-Chat-WebGPU / src /engine /blob-integrity.ts
Valeriy Selitskiy
Release v1.2.0 live model telemetry
c3633b1
Raw
History Blame Contribute Delete
1.62 kB
import { sha256 } from '@noble/hashes/sha2.js';
import { bytesToHex } from '@noble/hashes/utils.js';
import { abortError, throwIfAborted } from './errors';
export interface BlobIntegrityResult {
actualSha256: string;
matches: boolean;
}
export async function verifyBlobSha256(
blob: Blob,
expectedSha256: string,
signal?: AbortSignal,
onProgress?: (loadedBytes: number, totalBytes: number) => void,
): Promise<BlobIntegrityResult> {
if (signal) {
throwIfAborted(signal);
}
const hasher = sha256.create();
const reader = blob.stream().getReader();
const cancelRead = (): void => {
void reader.cancel(abortError()).catch(() => undefined);
};
signal?.addEventListener('abort', cancelRead, { once: true });
let loadedBytes = 0;
let lastProgressAt = 0;
onProgress?.(0, blob.size);
try {
while (true) {
if (signal) {
throwIfAborted(signal);
}
const { done, value } = await reader.read();
if (signal) {
throwIfAborted(signal);
}
if (done) {
break;
}
hasher.update(value);
loadedBytes += value.byteLength;
const now = performance.now();
if (now - lastProgressAt >= 100) {
lastProgressAt = now;
onProgress?.(Math.min(loadedBytes, blob.size), blob.size);
}
}
onProgress?.(blob.size, blob.size);
const actualSha256 = bytesToHex(hasher.digest());
return {
actualSha256,
matches: actualSha256 === expectedSha256,
};
} finally {
signal?.removeEventListener('abort', cancelRead);
reader.releaseLock();
hasher.destroy();
}
}