Skip to content

Commit

Permalink
fix(NODE-6348): Wrap thrown errors in JS Error objects with stacks (#25)
Browse files Browse the repository at this point in the history
Co-authored-by: Durran Jordan <[email protected]>
  • Loading branch information
sophiebits and durran authored Aug 29, 2024
1 parent 3c18460 commit af62c4f
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 154 deletions.
155 changes: 155 additions & 0 deletions bindings.js
Original file line number Diff line number Diff line change
@@ -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;
4 changes: 2 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@

/* auto-generated by NAPI-RS */

export function compress(data: Buffer, level?: number | undefined | null): Promise<Buffer>
export function decompress(data: Buffer): Promise<Buffer>
export declare function compress(data: Buffer, level?: number | undefined | null): Promise<Buffer>
export declare function decompress(data: Buffer): Promise<Buffer>
167 changes: 17 additions & 150 deletions index.js
Original file line number Diff line number Diff line change
@@ -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;
};
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
}
},
"files": [
"bindings.js",
"index.d.ts",
"index.js"
],
Expand All @@ -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",
Expand Down
13 changes: 13 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/);
}
});
});
});
});

0 comments on commit af62c4f

Please sign in to comment.