diff --git a/package-lock.json b/package-lock.json index 500da88..fb626e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,20 @@ "version": "3.4.1", "license": "Apache-2.0", "devDependencies": { + "@mongodb-js/zstd": "^1.2.0", "@types/node": "*", + "@types/pako": "^2.0.0", "@vitest/browser": "*", "@vitest/coverage-v8": "*", "eslint": "*", "eslint-config-prettier": "*", + "lz4-napi": "^2.9.0", + "pako": "^2.0.0", "playwright": "*", "prettier": "*", "rollup": "*", "rollup-plugin-sourcemaps": "*", + "snappy": "^7.0.0", "ts-node": "*", "typedoc": "*", "typescript": "*", @@ -26,6 +31,26 @@ }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@mongodb-js/zstd": "^1.2.0", + "lz4-napi": "^2.9.0", + "pako": "^2.0.0", + "snappy": "^7.0.0" + }, + "peerDependenciesMeta": { + "@mongodb-js/zstd": { + "optional": true + }, + "lz4-napi": { + "optional": true + }, + "pako": { + "optional": true + }, + "snappy": { + "optional": true + } } }, "node_modules/@ampproject/remapping": { @@ -41,6 +66,226 @@ "node": ">=6.0.0" } }, + "node_modules/@antoniomuso/lz4-napi-android-arm-eabi": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-android-arm-eabi/-/lz4-napi-android-arm-eabi-2.9.0.tgz", + "integrity": "sha512-aeT/9SoWq7rnmzssWuCKUPaxVt3fzE9q+xq/ZHbnUSmrm8/EhLOACMvQeCOnL0IZsmPh8EpuwIE1TZyM9iQPRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-android-arm64": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-android-arm64/-/lz4-napi-android-arm64-2.9.0.tgz", + "integrity": "sha512-ibQ0qiEvmljXAM97IgOZfh+PeiSQ0Rqf2HErJlZPVm2v4GVJoB67v21v1TUydqNNV5L8bwufVoZ90nheL8X9ZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-darwin-arm64": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-darwin-arm64/-/lz4-napi-darwin-arm64-2.9.0.tgz", + "integrity": "sha512-1su4K1MWa4bcWoZlHajv+luGmFDV1JwIsvjtDF+0HhUveSDPP+8A4Z34zOZidURIr08Sl7M7ViPth6ZQ9SqnAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-darwin-x64": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-darwin-x64/-/lz4-napi-darwin-x64-2.9.0.tgz", + "integrity": "sha512-8Lnbm2MkdJtiJ/nbcRS9zRyGp3G0sG6D+Y/x1vTP8nZs3/f8tBwYNsjxCQyyXNNyHcYWwVGbk68onP/pyDljOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-freebsd-x64": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-freebsd-x64/-/lz4-napi-freebsd-x64-2.9.0.tgz", + "integrity": "sha512-k04EMVOjntKDPrdR4Tf8WyNseuk9PTtSGw8WHyp4CTjoR1s+YJxtp9SMnThe5o2q0TATwk8WGYb/Howrp5OMxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-linux-arm-gnueabihf": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-linux-arm-gnueabihf/-/lz4-napi-linux-arm-gnueabihf-2.9.0.tgz", + "integrity": "sha512-H92F8zPZmgy2r8IhCWh3qIBfLp2BQ5cp18RoDXhtGFWwkh+5gVWrZp11IVznrsdgB0QeW0VR7dAMMHg3WLOPfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-linux-arm64-gnu": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-linux-arm64-gnu/-/lz4-napi-linux-arm64-gnu-2.9.0.tgz", + "integrity": "sha512-25crh0qs/3Rj3fMI8ulYD0DoaKsidUhMBki2aeO69ZK+F8bmQ/e2++FlgJ6f3EgMP5CNxJtnZXKhPOraQWjwAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-linux-arm64-musl": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-linux-arm64-musl/-/lz4-napi-linux-arm64-musl-2.9.0.tgz", + "integrity": "sha512-eJtHp38zuLaYI0/cOV/BKcNQiXUBo4GPx53FTf0Y307yUjLsn48LNeN0vD28Ct9YrbUae3bQvMD5AD86She0ww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-linux-x64-gnu": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-linux-x64-gnu/-/lz4-napi-linux-x64-gnu-2.9.0.tgz", + "integrity": "sha512-mDjS4dyjRKaZQcAP71SphkYH5r3kufB30ih/VETVu/br2toCfBk6Zr1xhL1r+L7FaVAFzF62B7h30CiqrN0Awg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-linux-x64-musl": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-linux-x64-musl/-/lz4-napi-linux-x64-musl-2.9.0.tgz", + "integrity": "sha512-pvU7Z7qjkjn17NkddBtBQ7C2iRqjtZ7WJ3Jqrjtj4XxolY3Q0HaYMvWjkWhzb9AKGZbj5y+EHYtbVoZJ2TSQhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-win32-arm64-msvc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-win32-arm64-msvc/-/lz4-napi-win32-arm64-msvc-2.9.0.tgz", + "integrity": "sha512-aioLlbpJl0QPEXLXhh2bzyitc3T7Jot3f1ap6WdKiRa+CIjMHXw1nxJXy07MLXif10r+qVZr86ic8dvwErgqEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-win32-ia32-msvc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-win32-ia32-msvc/-/lz4-napi-win32-ia32-msvc-2.9.0.tgz", + "integrity": "sha512-VaF4XMTdYb59TsPsiqnWwsNaWKHhgxF33z5p4zg4n0tp20eWozl76hn8B+aXthSs40W0W1N97QhxxV4oXGd8cg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@antoniomuso/lz4-napi-win32-x64-msvc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@antoniomuso/lz4-napi-win32-x64-msvc/-/lz4-napi-win32-x64-msvc-2.9.0.tgz", + "integrity": "sha512-wfA8ShO3eGLxJ1LDwXJo87XL2D4NkMJV1pfHPvLZpD0MWb9u8VfgS+gKK5YhT7XKjzVdeIna9jgFdn2HBnZBxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -141,6 +386,40 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", @@ -644,180 +923,662 @@ "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", "dev": true, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@gerrit0/mini-shiki": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.6.0.tgz", + "integrity": "sha512-KaeJvPNofTEZR9EzVNp/GQzbQqkGfjiu6k3CXKvhVTX+8OoAKSX/k7qxLKOX3B0yh2XqVAc93rsOu48CGt2Qug==", + "dev": true, + "dependencies": { + "@shikijs/engine-oniguruma": "^3.6.0", + "@shikijs/langs": "^3.6.0", + "@shikijs/themes": "^3.6.0", + "@shikijs/types": "^3.6.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mongodb-js/zstd": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd/-/zstd-1.2.2.tgz", + "integrity": "sha512-NRXiFhk2Nl8UMuIZ4pviKkGVZY/e5P37Opam1u0OtgXjEE0kO1HLapA9heTcZ1PUomArnKS426XbiRFr5iaWvw==", + "deprecated": "1.x versions of this package are deprecated, please use 2.x instead", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@mongodb-js/zstd-darwin-arm64": "1.2.2", + "@mongodb-js/zstd-darwin-x64": "1.2.2", + "@mongodb-js/zstd-linux-arm64-gnu": "1.2.2", + "@mongodb-js/zstd-linux-arm64-musl": "1.2.2", + "@mongodb-js/zstd-linux-x64-gnu": "1.2.2", + "@mongodb-js/zstd-linux-x64-musl": "1.2.2", + "@mongodb-js/zstd-win32-x64-msvc": "1.2.2" + } + }, + "node_modules/@mongodb-js/zstd-darwin-arm64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-darwin-arm64/-/zstd-darwin-arm64-1.2.2.tgz", + "integrity": "sha512-hjQgub8fhn3itewwRSCSe3sl8rmnbZOFwuBHOZj4j4xu1Hde7xs+ACkfeEvvmNjUuxAzvc1MnDjHX2UwUlA7qA==", + "cpu": [ + "arm64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-darwin-x64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-darwin-x64/-/zstd-darwin-x64-1.2.2.tgz", + "integrity": "sha512-gHSxcWgAdED/bDKyOqLhiwC0VYlhkhSyQuR0Fqcrl1CMpWSJAfu0jxhaEvM4Ncs6yH0+BPVwTp8xtHW2iJ5DuQ==", + "cpu": [ + "x64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-arm64-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-arm64-gnu/-/zstd-linux-arm64-gnu-1.2.2.tgz", + "integrity": "sha512-DJVSC5IU/GlY9lY4qpKML/655OstZvueN/WZeuO8hyYJ5115x/usUM1pHpp7dbZWyUUhTlRIU4peR62Tk1H3lQ==", + "cpu": [ + "arm64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-arm64-musl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-arm64-musl/-/zstd-linux-arm64-musl-1.2.2.tgz", + "integrity": "sha512-rxTmMF2NGLLijnw+MZCs0ixiE+NylpDYvUUkJemqjyHstlmYG0Ku6i3+SzViB6L0kkktwYSRpaI4y7OG+SFt3w==", + "cpu": [ + "arm64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-x64-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-x64-gnu/-/zstd-linux-x64-gnu-1.2.2.tgz", + "integrity": "sha512-ctz/XY6aX2THxkTjAygOYbyFp2/UYbqZIF3sB1F5Cjcbus9wfD3vPbaDegqRjQhlHBvKia3IjqJDUiC+aZxmww==", + "cpu": [ + "x64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-x64-musl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-x64-musl/-/zstd-linux-x64-musl-1.2.2.tgz", + "integrity": "sha512-EqD271yPIRBGbkMtBJSVTLlUrukTsmi7qt1G3dh+Z9RWVXn5MXnlnEn4znfBXoaVqlniUNZhmzDEaATNIktJpw==", + "cpu": [ + "x64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-win32-x64-msvc": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-win32-x64-msvc/-/zstd-win32-x64-msvc-1.2.2.tgz", + "integrity": "sha512-h3O9NGOrGtfQ7g+rFqs5Hn+GSmgF2ik+xg0/1crNUXWthcBsxcCK5woawHfDcJxWkaiijJe5PVo6/fo8Jm7qCg==", + "cpu": [ + "x64" + ], + "deprecated": "This package is no longer maintained. Please use @mongodb-js/zstd@2.x instead.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-android-arm-eabi": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.3.3.tgz", + "integrity": "sha512-d4vUFFzNBvazGfB/KU8MnEax6itTIgRWXodPdZDnWKHy9HwVBndpCiedQDcSNHcZNYV36rx034rpn7SAuTL2NA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-android-arm64": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm64/-/snappy-android-arm64-7.3.3.tgz", + "integrity": "sha512-Uh+w18dhzjVl85MGhRnojb7OLlX2ErvMsYIunO/7l3Frvc2zQvfqsWsFJanu2dwqlE2YDooeNP84S+ywgN9sxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-darwin-arm64": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-arm64/-/snappy-darwin-arm64-7.3.3.tgz", + "integrity": "sha512-AmJn+6yOu/0V0YNHLKmRUNYkn93iv/1wtPayC7O1OHtfY6YqHQ31/MVeeRBiEYtQW9TwVZxXrDirxSB1PxRdtw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-darwin-x64": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-x64/-/snappy-darwin-x64-7.3.3.tgz", + "integrity": "sha512-biLTXBmPjPmO7HIpv+5BaV9Gy/4+QJSUNJW8Pjx1UlWAVnocPy7um+zbvAWStZssTI5sfn/jOClrAegD4w09UA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-freebsd-x64": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-freebsd-x64/-/snappy-freebsd-x64-7.3.3.tgz", + "integrity": "sha512-E3R3ewm8Mrjm0yL2TC3VgnphDsQaCPixNJqBbGiz3NTshVDhlPlOgPKF0NGYqKiKaDGdD9PKtUgOR4vagUtn7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-linux-arm-gnueabihf": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm-gnueabihf/-/snappy-linux-arm-gnueabihf-7.3.3.tgz", + "integrity": "sha512-ZuNgtmk9j0KyT7TfLyEnvZJxOhbkyNR761nk04F0Q4NTHMICP28wQj0xgEsnCHUsEeA9OXrRL4R7waiLn+rOQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "node_modules/@napi-rs/snappy-linux-arm64-gnu": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-gnu/-/snappy-linux-arm64-gnu-7.3.3.tgz", + "integrity": "sha512-KIzwtq0dAzshzpqZWjg0Q9lUx93iZN7wCCUzCdLYIQ+mvJZKM10VCdn0RcuQze1R3UJTPwpPLXQIVskNMBYyPA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 10" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "node_modules/@napi-rs/snappy-linux-arm64-musl": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-musl/-/snappy-linux-arm64-musl-7.3.3.tgz", + "integrity": "sha512-AAED4cQS74xPvktsyVmz5sy8vSxG/+3d7Rq2FDBZzj3Fv6v5vux6uZnECPCAqpALCdTtJ61unqpOyqO7hZCt1Q==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@eslint/core": "^0.14.0", - "levn": "^0.4.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 10" } }, - "node_modules/@gerrit0/mini-shiki": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.6.0.tgz", - "integrity": "sha512-KaeJvPNofTEZR9EzVNp/GQzbQqkGfjiu6k3CXKvhVTX+8OoAKSX/k7qxLKOX3B0yh2XqVAc93rsOu48CGt2Qug==", + "node_modules/@napi-rs/snappy-linux-ppc64-gnu": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-ppc64-gnu/-/snappy-linux-ppc64-gnu-7.3.3.tgz", + "integrity": "sha512-pofO5eSLg8ZTBwVae4WHHwJxJGZI8NEb4r5Mppvq12J/1/Hq1HecClXmfY3A7bdT2fsS2Td+Q7CI9VdBOj2sbA==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@shikijs/engine-oniguruma": "^3.6.0", - "@shikijs/langs": "^3.6.0", - "@shikijs/themes": "^3.6.0", - "@shikijs/types": "^3.6.0", - "@shikijs/vscode-textmate": "^10.0.2" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@napi-rs/snappy-linux-riscv64-gnu": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-riscv64-gnu/-/snappy-linux-riscv64-gnu-7.3.3.tgz", + "integrity": "sha512-OiHYdeuwj0TVBXADUmmQDQ4lL1TB+8EwmXnFgOutoDVXHaUl0CJFyXLa6tYUXe+gRY8hs1v7eb0vyE97LKY06Q==", + "cpu": [ + "riscv64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">= 10" } }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "node_modules/@napi-rs/snappy-linux-s390x-gnu": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-s390x-gnu/-/snappy-linux-s390x-gnu-7.3.3.tgz", + "integrity": "sha512-66QdmuV9CTq/S/xifZXlMy3PsZTviAgkqqpZ+7vPCmLtuP+nqhaeupShOFf/sIDsS0gZePazPosPTeTBbhkLHg==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">= 10" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@napi-rs/snappy-linux-x64-gnu": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-gnu/-/snappy-linux-x64-gnu-7.3.3.tgz", + "integrity": "sha512-g6KURjOxrgb8yXDEZMuIcHkUr/7TKlDwSiydEQtMtP3n4iI4sNjkcE/WNKlR3+t9bZh1pFGAq7NFRBtouQGHpQ==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">= 10" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@napi-rs/snappy-linux-x64-musl": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-musl/-/snappy-linux-x64-musl-7.3.3.tgz", + "integrity": "sha512-6UvOyczHknpaKjrlKKSlX3rwpOrfJwiMG6qA0NRKJFgbcCAEUxmN9A8JvW4inP46DKdQ0bekdOxwRtAhFiTDfg==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">= 10" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@napi-rs/snappy-openharmony-arm64": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-openharmony-arm64/-/snappy-openharmony-arm64-7.3.3.tgz", + "integrity": "sha512-I5mak/5rTprobf7wMCk0vFhClmWOL/QiIJM4XontysnadmP/R9hAcmuFmoMV2GaxC9MblqLA7Z++gy8ou5hJVw==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">= 10" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@napi-rs/snappy-wasm32-wasi": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-wasm32-wasi/-/snappy-wasm32-wasi-7.3.3.tgz", + "integrity": "sha512-+EroeygVYo9RksOchjF206frhMkfD2PaIun3yH4Zp5j/Y0oIEgs/+VhAYx/f+zHRylQYUIdLzDRclcoepvlR8Q==", + "cpu": [ + "wasm32" + ], "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@napi-rs/wasm-runtime": "^1.0.3" }, "engines": { - "node": ">=12" + "node": ">=14.0.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@napi-rs/snappy-win32-arm64-msvc": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-arm64-msvc/-/snappy-win32-arm64-msvc-7.3.3.tgz", + "integrity": "sha512-rxqfntBsCfzgOha/OlG8ld2hs6YSMGhpMUbFjeQLyVDbooY041fRXv3S7yk52DfO6H4QQhLT5+p7cW0mYdhyiQ==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "node_modules/@napi-rs/snappy-win32-ia32-msvc": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-ia32-msvc/-/snappy-win32-ia32-msvc-7.3.3.tgz", + "integrity": "sha512-joRV16DsRtqjGt0CdSpxGCkO0UlHGeTZ/GqvdscoALpRKbikR2Top4C61dxEchmOd3lSYsXutuwWWGg3Nr++WA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.0.0" + "node": ">= 10" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@napi-rs/snappy-win32-x64-msvc": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-x64-msvc/-/snappy-win32-x64-msvc-7.3.3.tgz", + "integrity": "sha512-cEnQwcsdJyOU7HSZODWsHpKuQoSYM4jaqw/hn9pOXYbRN1+02WxYppD3fdMuKN6TOA6YG5KA5PHRNeVilNX86Q==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.0.0" + "node": ">= 10" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@napi-rs/triples": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@napi-rs/triples/-/triples-1.2.0.tgz", + "integrity": "sha512-HAPjR3bnCsdXBsATpDIP5WCrw0JcACwhhrwIAQhiR46n+jm+a2F8kBsfseAuWtSyQ+H3Yebt2k43B5dy+04yMA==", "dev": true, - "engines": { - "node": ">=6.0.0" - } + "license": "MIT" }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@node-rs/helper": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@node-rs/helper/-/helper-1.6.0.tgz", + "integrity": "sha512-2OTh/tokcLA1qom1zuCJm2gQzaZljCCbtX1YCrwRVd/toz7KxaDRFeLTAPwhs8m9hWgzrBn5rShRm6IaZofCPw==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@napi-rs/triples": "^1.2.0" } }, "node_modules/@nodelib/fs.scandir": { @@ -1243,7 +2004,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -1295,6 +2055,17 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -1342,11 +2113,17 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz", "integrity": "sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==", "dev": true, - "peer": true, "dependencies": { "undici-types": "~7.8.0" } }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -1396,7 +2173,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/types": "8.34.0", @@ -1603,7 +2379,6 @@ "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-3.2.3.tgz", "integrity": "sha512-5HpUb0ixGF8JWSAjb/P1x/VPuTYUkL4pL0+YO6DJiuvQgqJN3PREaUEcXwfXjU4nBc37EahfpRbAwdE9pHs9lQ==", "dev": true, - "peer": true, "dependencies": { "@testing-library/dom": "^10.4.0", "@testing-library/user-event": "^14.6.1", @@ -1780,7 +2555,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2191,7 +2965,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -2862,6 +3635,34 @@ "lz-string": "bin/bin.js" } }, + "node_modules/lz4-napi": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/lz4-napi/-/lz4-napi-2.9.0.tgz", + "integrity": "sha512-ZOWqxBMIK5768aD20tYn5B6Pp9WPM9UG/LHk8neG9p0gC1DtjdzhTtlkxhAjvTRpmJvMtnnqLKlT+COlqAt9cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@node-rs/helper": "^1.3.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@antoniomuso/lz4-napi-android-arm-eabi": "2.9.0", + "@antoniomuso/lz4-napi-android-arm64": "2.9.0", + "@antoniomuso/lz4-napi-darwin-arm64": "2.9.0", + "@antoniomuso/lz4-napi-darwin-x64": "2.9.0", + "@antoniomuso/lz4-napi-freebsd-x64": "2.9.0", + "@antoniomuso/lz4-napi-linux-arm-gnueabihf": "2.9.0", + "@antoniomuso/lz4-napi-linux-arm64-gnu": "2.9.0", + "@antoniomuso/lz4-napi-linux-arm64-musl": "2.9.0", + "@antoniomuso/lz4-napi-linux-x64-gnu": "2.9.0", + "@antoniomuso/lz4-napi-linux-x64-musl": "2.9.0", + "@antoniomuso/lz4-napi-win32-arm64-msvc": "2.9.0", + "@antoniomuso/lz4-napi-win32-ia32-msvc": "2.9.0", + "@antoniomuso/lz4-napi-win32-x64-msvc": "2.9.0" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -3062,6 +3863,13 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3147,7 +3955,6 @@ "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "playwright-core": "1.56.1" }, @@ -3322,7 +4129,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -3443,6 +4249,40 @@ "node": ">=18" } }, + "node_modules/snappy": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/snappy/-/snappy-7.3.3.tgz", + "integrity": "sha512-UDJVCunvgblRpfTOjo/uT7pQzfrTsSICJ4yVS4aq7SsGBaUSpJwaVP15nF//jqinSLpN7boe/BqbUmtWMTQ5MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/snappy-android-arm-eabi": "7.3.3", + "@napi-rs/snappy-android-arm64": "7.3.3", + "@napi-rs/snappy-darwin-arm64": "7.3.3", + "@napi-rs/snappy-darwin-x64": "7.3.3", + "@napi-rs/snappy-freebsd-x64": "7.3.3", + "@napi-rs/snappy-linux-arm-gnueabihf": "7.3.3", + "@napi-rs/snappy-linux-arm64-gnu": "7.3.3", + "@napi-rs/snappy-linux-arm64-musl": "7.3.3", + "@napi-rs/snappy-linux-ppc64-gnu": "7.3.3", + "@napi-rs/snappy-linux-riscv64-gnu": "7.3.3", + "@napi-rs/snappy-linux-s390x-gnu": "7.3.3", + "@napi-rs/snappy-linux-x64-gnu": "7.3.3", + "@napi-rs/snappy-linux-x64-musl": "7.3.3", + "@napi-rs/snappy-openharmony-arm64": "7.3.3", + "@napi-rs/snappy-wasm32-wasi": "7.3.3", + "@napi-rs/snappy-win32-arm64-msvc": "7.3.3", + "@napi-rs/snappy-win32-ia32-msvc": "7.3.3", + "@napi-rs/snappy-win32-x64-msvc": "7.3.3" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3698,7 +4538,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3809,6 +4648,14 @@ } } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3873,7 +4720,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3937,7 +4783,6 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -4068,7 +4913,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -4122,7 +4966,6 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.3.tgz", "integrity": "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==", "dev": true, - "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.3", diff --git a/package.json b/package.json index bd129a8..87f365a 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,33 @@ "url": "https://github.com/cloudamqp/amqp-client.js/issues" }, "homepage": "https://github.com/cloudamqp/amqp-client.js#readme", + "peerDependencies": { + "lz4-napi": "^2.9.0", + "snappy": "^7.0.0", + "@mongodb-js/zstd": "^1.2.0", + "pako": "^2.0.0" + }, + "peerDependenciesMeta": { + "lz4-napi": { + "optional": true + }, + "snappy": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "pako": { + "optional": true + } + }, "devDependencies": { + "@mongodb-js/zstd": "^1.2.0", "@types/node": "*", + "@types/pako": "^2.0.0", + "lz4-napi": "^2.9.0", + "pako": "^2.0.0", + "snappy": "^7.0.0", "@vitest/browser": "*", "@vitest/coverage-v8": "*", "eslint": "*", diff --git a/src/amqp-channel.ts b/src/amqp-channel.ts index ddf6498..1e2003f 100644 --- a/src/amqp-channel.ts +++ b/src/amqp-channel.ts @@ -6,6 +6,7 @@ import { AMQPConsumer, AMQPGeneratorConsumer } from "./amqp-consumer.js" import type { AMQPMessage } from "./amqp-message.js" import type { AMQPBaseClient } from "./amqp-base-client.js" import type { AMQPProperties } from "./amqp-properties.js" +import { compressionRegistry, CompressionError, type PublishOptions } from "./amqp-compression.js" /** * Represents an AMQP Channel. Almost all actions in AMQP are performed on a Channel. @@ -327,6 +328,7 @@ export class AMQPChannel { * @param properties - properties to be published * @param [mandatory] - if the message should be returned if there's no queue to be delivered to * @param [immediate] - if the message should be returned if it can't be delivered to a consumer immediately (not supported in RabbitMQ) + * @param options - publish options including compression settings * @return - fulfilled when the message is enqueue on the socket, or if publish confirm is enabled when the message is confirmed by the server */ async basicPublish( @@ -336,11 +338,15 @@ export class AMQPChannel { properties: AMQPProperties = {}, mandatory = false, immediate = false, + options?: PublishOptions, ): Promise { if (this.closed) return this.rejectClosed() if (this.connection.blocked) return Promise.reject(new AMQPError(`Connection blocked by server: ${this.connection.blocked}`, this.connection)) + const compression = options?.compression + const compressionThreshold = options?.compressionThreshold ?? 0 + let body: Uint8Array if (typeof Buffer !== "undefined" && data instanceof Buffer) { body = data @@ -356,6 +362,20 @@ export class AMQPChannel { throw new TypeError(`Invalid type ${typeof data} for parameter data`) } + // Apply compression if requested and body exceeds threshold + const actualProperties = { ...properties } + if (compression && body.byteLength > compressionThreshold) { + const codec = await compressionRegistry.getCodec(compression) + if (!codec) { + throw new CompressionError( + `Compression codec '${compression}' is not available. Install the required peer dependency.`, + compression, + ) + } + body = codec.compress(body) + actualProperties.contentEncoding = codec.contentEncoding + } + let j = 0 // get a buffer from the pool or create a new, it will later be returned to the pool for reuse let buffer = this.connection.bufferPool.pop() || new AMQPView(new ArrayBuffer(this.connection.frameMax)) @@ -395,7 +415,7 @@ export class AMQPChannel { j += 4 // bodysize (upper 32 of 64 bits) buffer.setUint32(j, body.byteLength) j += 4 // bodysize - j += buffer.setProperties(j, properties) + j += buffer.setProperties(j, actualProperties) buffer.setUint8(j, AMQPFrame.End.CODE) j += 1 buffer.setUint32(headerStart + 3, j - headerStart - 8) // update frameSize diff --git a/src/amqp-compression.ts b/src/amqp-compression.ts new file mode 100644 index 0000000..6fbda0a --- /dev/null +++ b/src/amqp-compression.ts @@ -0,0 +1,163 @@ +/** + * Supported compression algorithms + */ +export type CompressionAlgorithm = "lz4" | "snappy" | "zstd" | "gzip" + +/** + * Codec interface for compression/decompression + */ +export interface CompressionCodec { + /** Compress data */ + compress(data: Uint8Array): Uint8Array + /** Decompress data */ + decompress(data: Uint8Array): Uint8Array + /** Content-Encoding header value */ + readonly contentEncoding: string +} + +/** + * Options for publish with compression + */ +export interface PublishOptions { + /** Compression algorithm to use */ + compression?: CompressionAlgorithm + /** Minimum body size in bytes before compression is applied (default: 0) */ + compressionThreshold?: number +} + +/** + * Error thrown when a required compression codec is not available + */ +export class CompressionError extends Error { + constructor( + message: string, + public readonly algorithm?: CompressionAlgorithm, + ) { + super(message) + this.name = "CompressionError" + } +} + +/** + * Registry for compression codecs with lazy loading + */ +class CompressionRegistry { + private codecs = new Map() + private loadPromises = new Map>() + + /** + * Get a codec by algorithm name, loading it if necessary + * Returns null if the codec library is not installed + */ + async getCodec(algorithm: CompressionAlgorithm): Promise { + // Check cache first + if (this.codecs.has(algorithm)) { + return this.codecs.get(algorithm) || null + } + + // Check if already loading + if (this.loadPromises.has(algorithm)) { + return this.loadPromises.get(algorithm)! + } + + // Start loading + const loadPromise = this.loadCodec(algorithm) + this.loadPromises.set(algorithm, loadPromise) + + const codec = await loadPromise + this.codecs.set(algorithm, codec) + this.loadPromises.delete(algorithm) + + return codec + } + + /** + * Register a custom codec + */ + registerCodec(contentEncoding: string, codec: CompressionCodec): void { + this.codecs.set(contentEncoding, codec) + } + + /** + * Get a codec synchronously (only returns already-loaded codecs) + * Returns null if the codec is not loaded yet + */ + getCodecSync(algorithm: string): CompressionCodec | null { + return this.codecs.get(algorithm) || null + } + + private async loadCodec(algorithm: CompressionAlgorithm): Promise { + try { + switch (algorithm) { + case "gzip": + return await this.loadGzip() + case "lz4": + return await this.loadLz4() + case "snappy": + return await this.loadSnappy() + case "zstd": + return await this.loadZstd() + default: + return null + } + } catch { + return null + } + } + + private async loadGzip(): Promise { + // Use built-in zlib in Node.js, pako in browser + if (typeof process !== "undefined" && process.versions?.node) { + const zlib = await import("zlib") + return { + contentEncoding: "gzip", + compress: (data) => new Uint8Array(zlib.gzipSync(data)), + decompress: (data) => new Uint8Array(zlib.gunzipSync(data)), + } + } else { + // Browser: try pako + const pako = await import("pako") + return { + contentEncoding: "gzip", + compress: (data) => pako.gzip(data), + decompress: (data) => pako.ungzip(data), + } + } + } + + private async loadLz4(): Promise { + try { + // Using lz4-napi for native bindings + const lz4 = await import("lz4-napi") + return { + contentEncoding: "lz4", + compress: (data) => new Uint8Array(lz4.compressSync(Buffer.from(data))), + decompress: (data) => new Uint8Array(lz4.uncompressSync(Buffer.from(data))), + } + } catch { + return null + } + } + + private async loadSnappy(): Promise { + try { + const snappy = await import("snappy") + return { + contentEncoding: "snappy", + compress: (data) => new Uint8Array(snappy.compressSync(data)), + decompress: (data) => new Uint8Array(snappy.uncompressSync(data)), + } + } catch { + return null + } + } + + private async loadZstd(): Promise { + // @mongodb-js/zstd only has async methods, not supported for sync compression + // TODO: Consider using a different zstd library with sync support + return null + } +} + +// Singleton instance +export const compressionRegistry = new CompressionRegistry() diff --git a/src/amqp-message.ts b/src/amqp-message.ts index 899ea7a..3911ac8 100644 --- a/src/amqp-message.ts +++ b/src/amqp-message.ts @@ -1,5 +1,6 @@ import type { AMQPChannel } from "./amqp-channel.js" import type { AMQPProperties } from "./amqp-properties.js" +import { compressionRegistry, CompressionError } from "./amqp-compression.js" /** * AMQP message @@ -31,6 +32,9 @@ export class AMQPMessage { replyCode?: number replyText?: string + /** Cached decompressed body */ + private decompressedBody: Uint8Array | null = null + /** * @param channel - Channel this message was delivered on */ @@ -38,15 +42,53 @@ export class AMQPMessage { this.channel = channel } + /** Known compression encodings */ + private static readonly COMPRESSION_ENCODINGS: readonly string[] = ["gzip", "lz4", "snappy", "zstd"] + + /** + * Get the decompressed body if contentEncoding indicates compression, + * otherwise returns the raw body. + * @throws CompressionError if the required codec is not available/loaded + */ + bodyDecompressed(): Uint8Array | null { + if (!this.body) return null + + // Return cached if available + if (this.decompressedBody) return this.decompressedBody + + const encoding = this.properties.contentEncoding + if (!encoding) return this.body + + // Check if this is a known compression encoding + if (!AMQPMessage.COMPRESSION_ENCODINGS.includes(encoding)) { + // Unknown encoding, return raw body + return this.body + } + + const codec = compressionRegistry.getCodecSync(encoding) + if (!codec) { + throw new CompressionError( + `Cannot decompress message: codec '${encoding}' is not loaded. Ensure the codec was used for compression first.`, + encoding as "gzip" | "lz4" | "snappy" | "zstd", + ) + } + + this.decompressedBody = codec.decompress(this.body) + return this.decompressedBody + } + /** - * Converts the message (which is deliviered as an uint8array) to a string + * Converts the message (which is delivered as an uint8array) to a string, + * auto-decompressing if the contentEncoding indicates compression. */ bodyToString(): string | null { - if (this.body) { - if (typeof Buffer !== "undefined") return Buffer.from(this.body).toString() - else return new TextDecoder().decode(this.body) + const body = this.bodyDecompressed() + if (!body) return null + + if (typeof Buffer !== "undefined") { + return Buffer.from(body).toString() } else { - return null + return new TextDecoder().decode(body) } } diff --git a/src/amqp-queue.ts b/src/amqp-queue.ts index c76e263..aedef78 100644 --- a/src/amqp-queue.ts +++ b/src/amqp-queue.ts @@ -2,6 +2,7 @@ import type { AMQPMessage } from "./amqp-message.js" import type { AMQPChannel, ConsumeParams } from "./amqp-channel.js" import type { AMQPProperties } from "./amqp-properties.js" import type { AMQPConsumer, AMQPGeneratorConsumer } from "./amqp-consumer.js" +import type { PublishOptions } from "./amqp-compression.js" /** * Convenience class for queues @@ -46,15 +47,17 @@ export class AMQPQueue { * Publish a message directly to the queue * @param body - the data to be published, can be a string or an uint8array * @param properties - publish properties + * @param options - publish options including compression settings * @return fulfilled when the message is enqueue on the socket, or if publish confirm is enabled when the message is confirmed by the server */ publish( body: string | Uint8Array | ArrayBuffer | Buffer | null, properties: AMQPProperties = {}, + options?: PublishOptions, ): Promise { return new Promise((resolve, reject) => { this.channel - .basicPublish("", this.name, body, properties) + .basicPublish("", this.name, body, properties, false, false, options) .then(() => resolve(this)) .catch(reject) }) diff --git a/src/compression-codecs.d.ts b/src/compression-codecs.d.ts new file mode 100644 index 0000000..ba7c5ec --- /dev/null +++ b/src/compression-codecs.d.ts @@ -0,0 +1,25 @@ +// Type declarations for optional compression peer dependencies + +declare module "lz4-napi" { + export function compress(data: Buffer): Promise + export function compressSync(data: Buffer): Buffer + export function uncompress(data: Buffer): Promise + export function uncompressSync(data: Buffer): Buffer +} + +declare module "snappy" { + export function compress(data: Uint8Array | Buffer): Promise + export function compressSync(data: Uint8Array | Buffer): Buffer + export function uncompress(data: Uint8Array | Buffer): Promise + export function uncompressSync(data: Uint8Array | Buffer): Buffer +} + +declare module "@mongodb-js/zstd" { + export function compress(data: Buffer): Promise + export function decompress(data: Buffer): Promise +} + +declare module "pako" { + export function gzip(data: Uint8Array): Uint8Array + export function ungzip(data: Uint8Array): Uint8Array +} diff --git a/src/index.ts b/src/index.ts index 4885867..e1b9b04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,3 +7,10 @@ export { AMQPError } from "./amqp-error.js" export { AMQPMessage } from "./amqp-message.js" export { AMQPProperties, Field } from "./amqp-properties.js" export { AMQPTlsOptions } from "./amqp-tls-options.js" +export { + compressionRegistry, + CompressionError, + type CompressionAlgorithm, + type CompressionCodec, + type PublishOptions, +} from "./amqp-compression.js" diff --git a/test/compression.ts b/test/compression.ts new file mode 100644 index 0000000..a20001a --- /dev/null +++ b/test/compression.ts @@ -0,0 +1,263 @@ +import { expect, test, beforeEach, describe } from "vitest" +import { AMQPClient } from "../src/amqp-socket-client.js" +import { compressionRegistry, CompressionError } from "../src/amqp-compression.js" + +function getNewClient(): AMQPClient { + return new AMQPClient("amqp://127.0.0.1") +} + +beforeEach(() => { + expect.hasAssertions() +}) + +describe("Compression", () => { + describe("basicPublish with compression", () => { + test("can publish with gzip compression", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const body = "Hello World".repeat(100) + + await ch.basicPublish("", q.name, body, {}, false, false, { compression: "gzip" }) + + const msg = await ch.basicGet(q.name) + expect(msg).toBeTruthy() + expect(msg?.properties.contentEncoding).toBe("gzip") + + // Raw body should be compressed (smaller) + expect(msg?.body?.byteLength).toBeLessThan(body.length) + + // Decompressed body should match original + const decompressed = msg?.bodyToString() + expect(decompressed).toBe(body) + + await conn.close() + }) + + test("respects compressionThreshold - no compression for small body", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const smallBody = "small" + + await ch.basicPublish("", q.name, smallBody, {}, false, false, { + compression: "gzip", + compressionThreshold: 1000, + }) + + const msg = await ch.basicGet(q.name) + expect(msg?.properties.contentEncoding).toBeUndefined() + expect(msg?.bodyString()).toBe(smallBody) + + await conn.close() + }) + + test("compresses when body exceeds threshold", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const largeBody = "large body content".repeat(100) + + await ch.basicPublish("", q.name, largeBody, {}, false, false, { + compression: "gzip", + compressionThreshold: 100, + }) + + const msg = await ch.basicGet(q.name) + expect(msg?.properties.contentEncoding).toBe("gzip") + expect(msg?.bodyToString()).toBe(largeBody) + + await conn.close() + }) + + test("works without options", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + + await ch.basicPublish("", q.name, "test", {}, false, false) + + const msg = await ch.basicGet(q.name) + expect(msg?.bodyString()).toBe("test") + + await conn.close() + }) + }) + + describe("AMQPQueue.publish with compression", () => { + test("can publish with compression via queue", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const body = "Queue publish test".repeat(50) + + await q.publish(body, {}, { compression: "gzip" }) + + const msg = await ch.basicGet(q.name) + expect(msg?.properties.contentEncoding).toBe("gzip") + expect(msg?.bodyToString()).toBe(body) + + await conn.close() + }) + }) + + describe("AMQPMessage decompression", () => { + test("auto-decompresses gzip messages", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const body = "Test message for decompression" + + await ch.basicPublish("", q.name, body, {}, false, false, { compression: "gzip" }) + + const msg = await ch.basicGet(q.name) + expect(msg?.bodyToString()).toBe(body) + + await conn.close() + }) + + test("bodyDecompressed returns raw body for non-compressed messages", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const body = "Non-compressed message" + + await ch.basicPublish("", q.name, body, {}) + + const msg = await ch.basicGet(q.name) + const decompressed = msg?.bodyDecompressed() + expect(decompressed).toEqual(msg?.body) + + await conn.close() + }) + + test("caches decompressed body", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const body = "Test message" + + await ch.basicPublish("", q.name, body, {}, false, false, { compression: "gzip" }) + + const msg = await ch.basicGet(q.name) + const first = msg?.bodyDecompressed() + const second = msg?.bodyDecompressed() + + // Should be the exact same reference (cached) + expect(first).toBe(second) + + await conn.close() + }) + + test("sync bodyString still works for non-compressed messages", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + + await ch.basicPublish("", q.name, "test", {}) + + const msg = await ch.basicGet(q.name) + // Sync method should still work for non-compressed messages + expect(msg?.bodyString()).toBe("test") + + await conn.close() + }) + + test("passes through unknown contentEncoding", async () => { + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const body = "Test with unknown encoding" + + // Publish with a custom contentEncoding that's not compression + await ch.basicPublish("", q.name, body, { contentEncoding: "utf-8" }) + + const msg = await ch.basicGet(q.name) + // Should return raw body since 'utf-8' is not a known compression encoding + const decompressed = msg?.bodyDecompressed() + expect(decompressed).toEqual(msg?.body) + + await conn.close() + }) + }) + + describe("codec registry", () => { + test("gzip codec is available (built-in)", async () => { + const gzipCodec = await compressionRegistry.getCodec("gzip") + expect(gzipCodec).toBeTruthy() + expect(gzipCodec?.contentEncoding).toBe("gzip") + }) + + test("can register custom codec", async () => { + const customCodec = { + contentEncoding: "custom-test", + compress: (data: Uint8Array) => data, + decompress: (data: Uint8Array) => data, + } + + compressionRegistry.registerCodec("custom-test", customCodec) + + // Can use the custom codec + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + + // Manually set contentEncoding to our custom codec + await ch.basicPublish("", q.name, "test", { contentEncoding: "custom-test" }) + + const msg = await ch.basicGet(q.name) + expect(msg?.properties.contentEncoding).toBe("custom-test") + + await conn.close() + }) + }) + + // Test each compression algorithm if available + describe.each(["gzip", "lz4", "snappy", "zstd"] as const)("%s compression", (algorithm) => { + test(`round-trip with ${algorithm}`, async () => { + const codec = await compressionRegistry.getCodec(algorithm) + if (!codec) { + console.log(`Skipping ${algorithm} test - codec not available`) + expect(true).toBe(true) // Ensure at least one assertion + return + } + + const amqp = getNewClient() + const conn = await amqp.connect() + const ch = await conn.channel() + const q = await ch.queue("") + const body = JSON.stringify({ test: "data", array: [1, 2, 3] }) + + await ch.basicPublish("", q.name, body, { contentType: "application/json" }, false, false, { + compression: algorithm, + }) + + const msg = await ch.basicGet(q.name) + expect(msg?.properties.contentEncoding).toBe(algorithm) + expect(msg?.bodyToString()).toBe(body) + + await conn.close() + }) + }) +}) + +describe("CompressionError", () => { + test("has correct name and properties", () => { + const error = new CompressionError("Test error", "gzip") + expect(error.name).toBe("CompressionError") + expect(error.message).toBe("Test error") + expect(error.algorithm).toBe("gzip") + expect(error instanceof Error).toBe(true) + }) +})