diff --git a/bindings.js b/bindings.js new file mode 100644 index 0000000..e411226 --- /dev/null +++ b/bindings.js @@ -0,0 +1,155 @@ +const { existsSync, readFileSync } = require('fs'); +const { join } = require('path'); + +const { platform, arch } = process; + +let nativeBinding = null; +let localFileExisted = false; +let loadError = null; + +function isMusl() { + // For Node 10 + if (!process.report || typeof process.report.getReport !== 'function') { + try { + return readFileSync('/usr/bin/ldd', 'utf8').includes('musl'); + } catch (e) { + return true; + } + } else { + const { glibcVersionRuntime } = process.report.getReport().header; + return !glibcVersionRuntime; + } +} + +switch (platform) { + case 'win32': + switch (arch) { + case 'x64': + localFileExisted = existsSync(join(__dirname, 'zstd.win32-x64-msvc.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.win32-x64-msvc.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-win32-x64-msvc'); + } + } catch (e) { + loadError = e; + } + break; + default: + throw new Error(`Unsupported architecture on Windows: ${arch}`); + } + break; + case 'darwin': + switch (arch) { + case 'x64': + localFileExisted = existsSync(join(__dirname, 'zstd.darwin-x64.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.darwin-x64.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-darwin-x64'); + } + } catch (e) { + loadError = e; + } + break; + case 'arm64': + localFileExisted = existsSync(join(__dirname, 'zstd.darwin-arm64.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.darwin-arm64.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-darwin-arm64'); + } + } catch (e) { + loadError = e; + } + break; + default: + throw new Error(`Unsupported architecture on macOS: ${arch}`); + } + break; + case 'linux': + switch (arch) { + case 'x64': + if (isMusl()) { + localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-musl.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.linux-x64-musl.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-linux-x64-musl'); + } + } catch (e) { + loadError = e; + } + } else { + localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-gnu.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.linux-x64-gnu.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-linux-x64-gnu'); + } + } catch (e) { + loadError = e; + } + } + break; + case 'arm64': + if (isMusl()) { + localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-musl.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.linux-arm64-musl.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-linux-arm64-musl'); + } + } catch (e) { + loadError = e; + } + } else { + localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-gnu.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.linux-arm64-gnu.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-linux-arm64-gnu'); + } + } catch (e) { + loadError = e; + } + } + break; + case 'arm': + localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm-gnueabihf.node')); + try { + if (localFileExisted) { + nativeBinding = require('./zstd.linux-arm-gnueabihf.node'); + } else { + nativeBinding = require('@mongodb-js/zstd-linux-arm-gnueabihf'); + } + } catch (e) { + loadError = e; + } + break; + default: + throw new Error(`Unsupported architecture on Linux: ${arch}`); + } + break; + default: + throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`); +} + +if (!nativeBinding) { + if (loadError) { + throw loadError; + } + throw new Error(`Failed to load native binding`); +} + +const { compress, decompress } = nativeBinding; + +module.exports.compress = compress; +module.exports.decompress = decompress; diff --git a/index.d.ts b/index.d.ts index 8d16577..a56ed6b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,5 +3,5 @@ /* auto-generated by NAPI-RS */ -export function compress(data: Buffer, level?: number | undefined | null): Promise -export function decompress(data: Buffer): Promise +export declare function compress(data: Buffer, level?: number | undefined | null): Promise +export declare function decompress(data: Buffer): Promise diff --git a/index.js b/index.js index e411226..422ca93 100644 --- a/index.js +++ b/index.js @@ -1,155 +1,22 @@ -const { existsSync, readFileSync } = require('fs'); -const { join } = require('path'); +// NB: If you update any type signatures to diverge from bindings itself, make +// sure to update how index.d.ts is generated (napi build --dts ...) -const { platform, arch } = process; +const { compress: _compress, decompress: _decompress } = require('./bindings'); -let nativeBinding = null; -let localFileExisted = false; -let loadError = null; +// Error objects created via napi don't have JS stacks; wrap them so .stack is present +// https://github.com/nodejs/node/issues/25318#issuecomment-451068073 -function isMusl() { - // For Node 10 - if (!process.report || typeof process.report.getReport !== 'function') { - try { - return readFileSync('/usr/bin/ldd', 'utf8').includes('musl'); - } catch (e) { - return true; - } - } else { - const { glibcVersionRuntime } = process.report.getReport().header; - return !glibcVersionRuntime; +exports.compress = async function compress(data) { + try { + return await _compress(data); + } catch (e) { + throw new Error(`zstd: ${e.message}`); } -} - -switch (platform) { - case 'win32': - switch (arch) { - case 'x64': - localFileExisted = existsSync(join(__dirname, 'zstd.win32-x64-msvc.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.win32-x64-msvc.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-win32-x64-msvc'); - } - } catch (e) { - loadError = e; - } - break; - default: - throw new Error(`Unsupported architecture on Windows: ${arch}`); - } - break; - case 'darwin': - switch (arch) { - case 'x64': - localFileExisted = existsSync(join(__dirname, 'zstd.darwin-x64.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.darwin-x64.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-darwin-x64'); - } - } catch (e) { - loadError = e; - } - break; - case 'arm64': - localFileExisted = existsSync(join(__dirname, 'zstd.darwin-arm64.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.darwin-arm64.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-darwin-arm64'); - } - } catch (e) { - loadError = e; - } - break; - default: - throw new Error(`Unsupported architecture on macOS: ${arch}`); - } - break; - case 'linux': - switch (arch) { - case 'x64': - if (isMusl()) { - localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-musl.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.linux-x64-musl.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-linux-x64-musl'); - } - } catch (e) { - loadError = e; - } - } else { - localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-gnu.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.linux-x64-gnu.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-linux-x64-gnu'); - } - } catch (e) { - loadError = e; - } - } - break; - case 'arm64': - if (isMusl()) { - localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-musl.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.linux-arm64-musl.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-linux-arm64-musl'); - } - } catch (e) { - loadError = e; - } - } else { - localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-gnu.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.linux-arm64-gnu.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-linux-arm64-gnu'); - } - } catch (e) { - loadError = e; - } - } - break; - case 'arm': - localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm-gnueabihf.node')); - try { - if (localFileExisted) { - nativeBinding = require('./zstd.linux-arm-gnueabihf.node'); - } else { - nativeBinding = require('@mongodb-js/zstd-linux-arm-gnueabihf'); - } - } catch (e) { - loadError = e; - } - break; - default: - throw new Error(`Unsupported architecture on Linux: ${arch}`); - } - break; - default: - throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`); -} - -if (!nativeBinding) { - if (loadError) { - throw loadError; +}; +exports.decompress = async function decompress(data) { + try { + return await _decompress(data); + } catch (e) { + throw new Error(`zstd: ${e.message}`); } - throw new Error(`Failed to load native binding`); -} - -const { compress, decompress } = nativeBinding; - -module.exports.compress = compress; -module.exports.decompress = decompress; +}; diff --git a/package.json b/package.json index ad10054..12c90d3 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ } }, "files": [ + "bindings.js", "index.d.ts", "index.js" ], @@ -44,8 +45,8 @@ }, "scripts": { "artifacts": "napi artifacts", - "build": "napi build --platform --release", - "build:debug": "napi build --platform", + "build": "napi build --js bindings.js --platform --release", + "build:debug": "napi build --js bindings.js --platform", "format:js": "prettier --config ./package.json --write *.js", "format:rs": "cargo fmt", "prepublishOnly": "napi prepublish -t npm", diff --git a/test/index.test.js b/test/index.test.js index 781f005..8b90fa1 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -37,4 +37,17 @@ describe('zstd', () => { }); }); }); + + describe('#decompress', () => { + context('when decompressing invalid data', () => { + it('includes a stack trace', async () => { + try { + await decompress(Buffer.from('invalid')); + } catch (error) { + expect(error.message).to.equal('zstd: Unknown frame descriptor'); + expect(error.stack).to.match(/at decompress/); + } + }); + }); + }); });