// Worker thread for offloading large JSON parse/stringify + encryption
// This runs in a separate thread so large messages don't block the event loop.
import { parentPort } from 'worker_threads';
import tweetnacl from 'tweetnacl';
import tweetnaclUtil from 'tweetnacl-util';
import zlib from 'zlib';
import { promisify } from 'util';
const gzip = promisify(zlib.gzip);
const gunzip = promisify(zlib.gunzip);
const { encodeBase64, decodeBase64, encodeUTF8, decodeUTF8 } = tweetnaclUtil;
const COMPRESS_THRESHOLD = 512;
function isEncrypted(msg) {
    return msg !== null && typeof msg === 'object'
        && typeof msg.n === 'string'
        && typeof msg.c === 'string';
}
async function decrypt(encrypted, key) {
    try {
        const nonce = decodeBase64(encrypted.n);
        const ciphertext = decodeBase64(encrypted.c);
        const decrypted = tweetnacl.secretbox.open(ciphertext, nonce, key);
        if (!decrypted)
            return null;
        if (encrypted.z) {
            const decompressed = await gunzip(Buffer.from(decrypted));
            return JSON.parse(decompressed.toString('utf8'));
        }
        else {
            return JSON.parse(encodeUTF8(decrypted));
        }
    }
    catch {
        return null;
    }
}
async function encrypt(data, key) {
    const nonce = tweetnacl.randomBytes(24);
    const jsonStr = JSON.stringify(data);
    let message;
    let compressed = false;
    if (jsonStr.length > COMPRESS_THRESHOLD) {
        const compressedBuf = await gzip(Buffer.from(jsonStr, 'utf8'));
        message = new Uint8Array(compressedBuf);
        compressed = true;
    }
    else {
        message = decodeUTF8(jsonStr);
    }
    const encrypted = tweetnacl.secretbox(message, nonce, key);
    const result = {
        n: encodeBase64(nonce),
        c: encodeBase64(encrypted),
    };
    if (compressed)
        result.z = true;
    return result;
}
// Message handlers
async function handleParse(raw, key) {
    try {
        const parsed = JSON.parse(raw);
        if (key && isEncrypted(parsed)) {
            return await decrypt(parsed, key);
        }
        return parsed;
    }
    catch {
        return null;
    }
}
async function handleSerialize(msg, key) {
    if (key) {
        const encrypted = await encrypt(msg, key);
        return JSON.stringify(encrypted);
    }
    else {
        return JSON.stringify(msg);
    }
}
parentPort.on('message', async (task) => {
    try {
        const key = task.key ? decodeBase64(task.key) : null;
        if (task.op === 'parse') {
            const result = await handleParse(task.data, key);
            parentPort.postMessage({ id: task.id, result, error: null });
        }
        else {
            const serialized = await handleSerialize(task.data, key);
            parentPort.postMessage({ id: task.id, result: serialized, error: null });
        }
    }
    catch (err) {
        parentPort.postMessage({ id: task.id, result: null, error: err.message });
    }
});
//# sourceMappingURL=json-worker.js.map