"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeBuffer = exports.decodeBufferSync = exports.decodeBuffer = exports.decodeBase64 = exports.encodeBase64 = exports.zstdDecompress = exports.zstdCompress = exports.brotliDecompress = exports.brotliCompress = exports.inflateRaw = exports.inflate = exports.deflate = exports.gunzip = exports.gzip = void 0;
const zlib = require("zlib");
const promisify = require('pify');
exports.gzip = promisify(zlib.gzip);
exports.gunzip = promisify(zlib.gunzip);
exports.deflate = promisify(zlib.deflate);
exports.inflate = promisify(zlib.inflate);
exports.inflateRaw = promisify(zlib.inflateRaw);
// Use Node's new built-in Brotli compression, if available, or
// use the brotli-wasm package if not.
exports.brotliCompress = zlib.brotliCompress
    ? ((buffer, level) => __awaiter(void 0, void 0, void 0, function* () {
        // In node, we just have to convert between the options formats and promisify:
        return new Promise((resolve, reject) => {
            zlib.brotliCompress(buffer, level !== undefined
                ? { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: level } }
                : {}, (err, result) => {
                if (err)
                    reject(err);
                else
                    resolve(result);
            });
        });
    }))
    : ((buffer, level) => __awaiter(void 0, void 0, void 0, function* () {
        const { compress } = yield Promise.resolve().then(() => require('brotli-wasm')); // Sync in node, async in browsers
        return compress(buffer, { quality: level });
    }));
exports.brotliDecompress = zlib.brotliDecompress
    ? promisify(zlib.brotliDecompress)
    : ((buffer) => __awaiter(void 0, void 0, void 0, function* () {
        const { decompress } = yield Promise.resolve().then(() => require('brotli-wasm')); // Sync in node, async in browsers
        return decompress(buffer);
    }));
// Zstd is a non-built-in wasm implementation that initializes async. We handle this by
// loading it when the first zstd buffer is decompressed. That lets us defer loading
// until that point too, which is good since it's large-ish & rarely used.
let zstd;
const getZstd = () => __awaiter(void 0, void 0, void 0, function* () {
    if (!zstd) {
        zstd = new Promise((resolve) => __awaiter(void 0, void 0, void 0, function* () {
            const { ZstdCodec } = yield Promise.resolve().then(() => require('zstd-codec'));
            ZstdCodec.run((binding) => {
                resolve(new binding.Streaming());
            });
        }));
    }
    return yield zstd;
});
const zstdCompress = (buffer, level) => __awaiter(void 0, void 0, void 0, function* () {
    return (yield getZstd()).compress(buffer, level);
});
exports.zstdCompress = zstdCompress;
const zstdDecompress = (buffer) => __awaiter(void 0, void 0, void 0, function* () {
    return (yield getZstd()).decompress(buffer);
});
exports.zstdDecompress = zstdDecompress;
const encodeBase64 = (buffer) => {
    return Buffer.from(asBuffer(buffer).toString('base64'), 'utf8');
};
const decodeBase64 = (buffer) => {
    return Buffer.from(asBuffer(buffer).toString('utf8'), 'base64');
};
// We export promisified versions for consistency
const encodeBase64Promisified = promisify(encodeBase64);
exports.encodeBase64 = encodeBase64Promisified;
const decodeBase64Promisified = promisify(decodeBase64);
exports.decodeBase64 = decodeBase64Promisified;
const asBuffer = (input) => {
    if (Buffer.isBuffer(input)) {
        return input;
    }
    else if (input instanceof ArrayBuffer) {
        return Buffer.from(input);
    }
    else {
        // Offset & length allow us to support all sorts of buffer views:
        return Buffer.from(input.buffer, input.byteOffset, input.byteLength);
    }
};
const IDENTITY_ENCODINGS = [
    // Explicitly unencoded in the standard way:
    'identity',
    // Weird encoding used by some AWS requests, actually just unencoded JSON:
    // https://docs.aws.amazon.com/en_us/AmazonCloudWatch/latest/APIReference/making-api-requests.html
    'amz-1.0',
    // Workaround for Apache's mod_deflate handling of 'identity', used in the wild mostly with PHP.
    // https://github.com/curl/curl/pull/2298
    'none',
    // No idea where these come from, but they definitely exist in real traffic and seem to come
    // from common confusion between content encodings and content types:
    'text',
    'binary',
    'utf8',
    'utf-8'
];
/**
 * Decodes a buffer, using the encodings as specified in a content-encoding header. Returns
 * a Buffer instance in Node, or a Uint8Array in a browser.
 *
 * Throws if any unrecognized/unavailable content-encoding is found.
 */
function decodeBuffer(body, encoding) {
    return __awaiter(this, void 0, void 0, function* () {
        const bodyBuffer = asBuffer(body);
        if (Array.isArray(encoding) || (typeof encoding === 'string' && encoding.indexOf(', ') >= 0)) {
            const encodings = typeof encoding === 'string' ? encoding.split(', ').reverse() : encoding;
            return encodings.reduce((contentPromise, nextEncoding) => {
                return contentPromise.then((content) => decodeBuffer(content, nextEncoding));
            }, Promise.resolve(bodyBuffer));
        }
        if (!encoding)
            encoding = 'identity';
        else
            encoding = encoding.toLowerCase();
        if (encoding === 'gzip' || encoding === 'x-gzip') {
            return (0, exports.gunzip)(bodyBuffer);
        }
        else if (encoding === 'deflate' || encoding === 'x-deflate') {
            // Deflate is ambiguous, and may or may not have a zlib wrapper.
            // This checks the buffer header directly, based on
            // https://stackoverflow.com/a/37528114/68051
            const lowNibble = bodyBuffer[0] & 0xF;
            if (lowNibble === 8) {
                return (0, exports.inflate)(bodyBuffer);
            }
            else {
                return (0, exports.inflateRaw)(bodyBuffer);
            }
        }
        else if (encoding === 'br') {
            return asBuffer(yield (0, exports.brotliDecompress)(bodyBuffer));
        }
        else if (encoding === 'zstd') {
            return asBuffer(yield (0, exports.zstdDecompress)(bodyBuffer));
        }
        else if (encoding === 'base64') {
            return asBuffer(yield decodeBase64(bodyBuffer));
        }
        else if (IDENTITY_ENCODINGS.includes(encoding)) {
            return asBuffer(bodyBuffer);
        }
        throw new Error(`Unsupported encoding: ${encoding}`);
    });
}
exports.decodeBuffer = decodeBuffer;
;
/**
 * Decodes a buffer, using the encodings as specified in a content-encoding header, synchronously.
 * Returns a Buffer instance in Node, or a Uint8Array in a browser.
 *
 * Zstandard and Brotli decoding are not be supported in synchronous usage.
 *
 * Throws if any unrecognized/unavailable content-encoding is found.
 *
 * @deprecated This is here for convenience with some existing APIs, but for performance & consistency
 * async usage with decodeBuffer is preferable.
 */
function decodeBufferSync(body, encoding) {
    const bodyBuffer = asBuffer(body);
    if (Array.isArray(encoding) || (typeof encoding === 'string' && encoding.indexOf(', ') >= 0)) {
        const encodings = typeof encoding === 'string' ? encoding.split(', ').reverse() : encoding;
        return encodings.reduce((content, nextEncoding) => {
            return decodeBufferSync(content, nextEncoding);
        }, bodyBuffer);
    }
    if (!encoding)
        encoding = 'identity';
    else
        encoding = encoding.toLowerCase();
    if (encoding === 'gzip' || encoding === 'x-gzip') {
        return zlib.gunzipSync(bodyBuffer);
    }
    else if (encoding === 'deflate' || encoding === 'x-deflate') {
        // Deflate is ambiguous, and may or may not have a zlib wrapper.
        // This checks the buffer header directly, based on
        // https://stackoverflow.com/a/37528114/68051
        const lowNibble = bodyBuffer[0] & 0xF;
        if (lowNibble === 8) {
            return zlib.inflateSync(bodyBuffer);
        }
        else {
            return zlib.inflateRawSync(bodyBuffer);
        }
    }
    else if (encoding === 'base64') {
        return asBuffer(decodeBase64(bodyBuffer));
    }
    else if (IDENTITY_ENCODINGS.includes(encoding)) {
        return asBuffer(bodyBuffer);
    }
    throw new Error(`Unsupported encoding: ${encoding}`);
}
exports.decodeBufferSync = decodeBufferSync;
;
/**
 * Encodes a buffer, given a single encoding name (as used in content-encoding headers), and an optional
 * level. Returns a Buffer instance in Node, or a Uint8Array in a browser.
 *
 * Throws if an unrecognized/unavailable encoding is specified
 */
function encodeBuffer(body, encoding, options = {}) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
        const bodyBuffer = asBuffer(body);
        const level = (_a = options.level) !== null && _a !== void 0 ? _a : 4;
        if (!encoding)
            encoding = 'identity';
        else
            encoding = encoding.toLowerCase();
        if (encoding === 'gzip' || encoding === 'x-gzip') {
            return (0, exports.gzip)(bodyBuffer, { level });
        }
        else if (encoding === 'deflate' || encoding === 'x-deflate') {
            return (0, exports.deflate)(bodyBuffer, { level });
        }
        else if (encoding === 'br') {
            return asBuffer(yield (0, exports.brotliCompress)(bodyBuffer, level));
        }
        else if (encoding === 'zstd') {
            return asBuffer(yield (0, exports.zstdCompress)(bodyBuffer, level));
        }
        else if (encoding === 'base64') {
            return asBuffer(encodeBase64(bodyBuffer));
        }
        else if (IDENTITY_ENCODINGS.includes(encoding)) {
            return asBuffer(bodyBuffer);
        }
        else {
            throw new Error(`Unsupported encoding: ${encoding}`);
        }
    });
}
exports.encodeBuffer = encodeBuffer;
;
//# sourceMappingURL=index.js.map