/** * Thin wrapper around @bokuweb/zstd-wasm that defers WASM load until the * first compress/decompress call and memoises the init promise. * * The WASM binary lives alongside the package's ESM entrypoint, so Vite's * default asset handling picks it up. The extension's CSP already allows * `wasm-unsafe-eval` for the existing hash-wasm Argon2id module. */ import { compress as zstdCompress, decompress as zstdDecompress, init as zstdInit, } from "@bokuweb/zstd-wasm"; import { IMPORT_LIMITS, importTooLarge } from "../shared/import-limits"; let initPromise: Promise | null = null; function ensureInit(): Promise { if (!initPromise) { initPromise = zstdInit().catch((err) => { initPromise = null; throw err; }); } return initPromise; } /** Compress bytes with zstd at the given level (default 19 per spec §4.1). */ export async function compressZstd(bytes: Uint8Array, level = 19): Promise { await ensureInit(); return zstdCompress(bytes, level); } /** * Decompress a zstd frame. Throws if the frame is malformed or if the * output exceeds IMPORT_LIMITS.MAX_DECOMPRESSED_BYTES (MV-4 — @bokuweb/ * zstd-wasm materialises the whole output, so the post-hoc check is the * strongest available here; callers must ALSO bound the declared length * before calling). */ export async function decompressZstd(bytes: Uint8Array): Promise { await ensureInit(); const out = zstdDecompress(bytes); if (out.byteLength > IMPORT_LIMITS.MAX_DECOMPRESSED_BYTES) { throw importTooLarge( `decompressed payload ${out.byteLength} bytes exceeds ${IMPORT_LIMITS.MAX_DECOMPRESSED_BYTES}`, ); } return out; }