Skip to content

Commit af62c4f

Browse files
sophiebitsdurran
andauthored
fix(NODE-6348): Wrap thrown errors in JS Error objects with stacks (#25)
Co-authored-by: Durran Jordan <[email protected]>
1 parent 3c18460 commit af62c4f

File tree

5 files changed

+190
-154
lines changed

5 files changed

+190
-154
lines changed

Diff for: bindings.js

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
const { existsSync, readFileSync } = require('fs');
2+
const { join } = require('path');
3+
4+
const { platform, arch } = process;
5+
6+
let nativeBinding = null;
7+
let localFileExisted = false;
8+
let loadError = null;
9+
10+
function isMusl() {
11+
// For Node 10
12+
if (!process.report || typeof process.report.getReport !== 'function') {
13+
try {
14+
return readFileSync('/usr/bin/ldd', 'utf8').includes('musl');
15+
} catch (e) {
16+
return true;
17+
}
18+
} else {
19+
const { glibcVersionRuntime } = process.report.getReport().header;
20+
return !glibcVersionRuntime;
21+
}
22+
}
23+
24+
switch (platform) {
25+
case 'win32':
26+
switch (arch) {
27+
case 'x64':
28+
localFileExisted = existsSync(join(__dirname, 'zstd.win32-x64-msvc.node'));
29+
try {
30+
if (localFileExisted) {
31+
nativeBinding = require('./zstd.win32-x64-msvc.node');
32+
} else {
33+
nativeBinding = require('@mongodb-js/zstd-win32-x64-msvc');
34+
}
35+
} catch (e) {
36+
loadError = e;
37+
}
38+
break;
39+
default:
40+
throw new Error(`Unsupported architecture on Windows: ${arch}`);
41+
}
42+
break;
43+
case 'darwin':
44+
switch (arch) {
45+
case 'x64':
46+
localFileExisted = existsSync(join(__dirname, 'zstd.darwin-x64.node'));
47+
try {
48+
if (localFileExisted) {
49+
nativeBinding = require('./zstd.darwin-x64.node');
50+
} else {
51+
nativeBinding = require('@mongodb-js/zstd-darwin-x64');
52+
}
53+
} catch (e) {
54+
loadError = e;
55+
}
56+
break;
57+
case 'arm64':
58+
localFileExisted = existsSync(join(__dirname, 'zstd.darwin-arm64.node'));
59+
try {
60+
if (localFileExisted) {
61+
nativeBinding = require('./zstd.darwin-arm64.node');
62+
} else {
63+
nativeBinding = require('@mongodb-js/zstd-darwin-arm64');
64+
}
65+
} catch (e) {
66+
loadError = e;
67+
}
68+
break;
69+
default:
70+
throw new Error(`Unsupported architecture on macOS: ${arch}`);
71+
}
72+
break;
73+
case 'linux':
74+
switch (arch) {
75+
case 'x64':
76+
if (isMusl()) {
77+
localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-musl.node'));
78+
try {
79+
if (localFileExisted) {
80+
nativeBinding = require('./zstd.linux-x64-musl.node');
81+
} else {
82+
nativeBinding = require('@mongodb-js/zstd-linux-x64-musl');
83+
}
84+
} catch (e) {
85+
loadError = e;
86+
}
87+
} else {
88+
localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-gnu.node'));
89+
try {
90+
if (localFileExisted) {
91+
nativeBinding = require('./zstd.linux-x64-gnu.node');
92+
} else {
93+
nativeBinding = require('@mongodb-js/zstd-linux-x64-gnu');
94+
}
95+
} catch (e) {
96+
loadError = e;
97+
}
98+
}
99+
break;
100+
case 'arm64':
101+
if (isMusl()) {
102+
localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-musl.node'));
103+
try {
104+
if (localFileExisted) {
105+
nativeBinding = require('./zstd.linux-arm64-musl.node');
106+
} else {
107+
nativeBinding = require('@mongodb-js/zstd-linux-arm64-musl');
108+
}
109+
} catch (e) {
110+
loadError = e;
111+
}
112+
} else {
113+
localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-gnu.node'));
114+
try {
115+
if (localFileExisted) {
116+
nativeBinding = require('./zstd.linux-arm64-gnu.node');
117+
} else {
118+
nativeBinding = require('@mongodb-js/zstd-linux-arm64-gnu');
119+
}
120+
} catch (e) {
121+
loadError = e;
122+
}
123+
}
124+
break;
125+
case 'arm':
126+
localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm-gnueabihf.node'));
127+
try {
128+
if (localFileExisted) {
129+
nativeBinding = require('./zstd.linux-arm-gnueabihf.node');
130+
} else {
131+
nativeBinding = require('@mongodb-js/zstd-linux-arm-gnueabihf');
132+
}
133+
} catch (e) {
134+
loadError = e;
135+
}
136+
break;
137+
default:
138+
throw new Error(`Unsupported architecture on Linux: ${arch}`);
139+
}
140+
break;
141+
default:
142+
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`);
143+
}
144+
145+
if (!nativeBinding) {
146+
if (loadError) {
147+
throw loadError;
148+
}
149+
throw new Error(`Failed to load native binding`);
150+
}
151+
152+
const { compress, decompress } = nativeBinding;
153+
154+
module.exports.compress = compress;
155+
module.exports.decompress = decompress;

Diff for: index.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33

44
/* auto-generated by NAPI-RS */
55

6-
export function compress(data: Buffer, level?: number | undefined | null): Promise<Buffer>
7-
export function decompress(data: Buffer): Promise<Buffer>
6+
export declare function compress(data: Buffer, level?: number | undefined | null): Promise<Buffer>
7+
export declare function decompress(data: Buffer): Promise<Buffer>

Diff for: index.js

+17-150
Original file line numberDiff line numberDiff line change
@@ -1,155 +1,22 @@
1-
const { existsSync, readFileSync } = require('fs');
2-
const { join } = require('path');
1+
// NB: If you update any type signatures to diverge from bindings itself, make
2+
// sure to update how index.d.ts is generated (napi build --dts ...)
33

4-
const { platform, arch } = process;
4+
const { compress: _compress, decompress: _decompress } = require('./bindings');
55

6-
let nativeBinding = null;
7-
let localFileExisted = false;
8-
let loadError = null;
6+
// Error objects created via napi don't have JS stacks; wrap them so .stack is present
7+
// https://github.com/nodejs/node/issues/25318#issuecomment-451068073
98

10-
function isMusl() {
11-
// For Node 10
12-
if (!process.report || typeof process.report.getReport !== 'function') {
13-
try {
14-
return readFileSync('/usr/bin/ldd', 'utf8').includes('musl');
15-
} catch (e) {
16-
return true;
17-
}
18-
} else {
19-
const { glibcVersionRuntime } = process.report.getReport().header;
20-
return !glibcVersionRuntime;
9+
exports.compress = async function compress(data) {
10+
try {
11+
return await _compress(data);
12+
} catch (e) {
13+
throw new Error(`zstd: ${e.message}`);
2114
}
22-
}
23-
24-
switch (platform) {
25-
case 'win32':
26-
switch (arch) {
27-
case 'x64':
28-
localFileExisted = existsSync(join(__dirname, 'zstd.win32-x64-msvc.node'));
29-
try {
30-
if (localFileExisted) {
31-
nativeBinding = require('./zstd.win32-x64-msvc.node');
32-
} else {
33-
nativeBinding = require('@mongodb-js/zstd-win32-x64-msvc');
34-
}
35-
} catch (e) {
36-
loadError = e;
37-
}
38-
break;
39-
default:
40-
throw new Error(`Unsupported architecture on Windows: ${arch}`);
41-
}
42-
break;
43-
case 'darwin':
44-
switch (arch) {
45-
case 'x64':
46-
localFileExisted = existsSync(join(__dirname, 'zstd.darwin-x64.node'));
47-
try {
48-
if (localFileExisted) {
49-
nativeBinding = require('./zstd.darwin-x64.node');
50-
} else {
51-
nativeBinding = require('@mongodb-js/zstd-darwin-x64');
52-
}
53-
} catch (e) {
54-
loadError = e;
55-
}
56-
break;
57-
case 'arm64':
58-
localFileExisted = existsSync(join(__dirname, 'zstd.darwin-arm64.node'));
59-
try {
60-
if (localFileExisted) {
61-
nativeBinding = require('./zstd.darwin-arm64.node');
62-
} else {
63-
nativeBinding = require('@mongodb-js/zstd-darwin-arm64');
64-
}
65-
} catch (e) {
66-
loadError = e;
67-
}
68-
break;
69-
default:
70-
throw new Error(`Unsupported architecture on macOS: ${arch}`);
71-
}
72-
break;
73-
case 'linux':
74-
switch (arch) {
75-
case 'x64':
76-
if (isMusl()) {
77-
localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-musl.node'));
78-
try {
79-
if (localFileExisted) {
80-
nativeBinding = require('./zstd.linux-x64-musl.node');
81-
} else {
82-
nativeBinding = require('@mongodb-js/zstd-linux-x64-musl');
83-
}
84-
} catch (e) {
85-
loadError = e;
86-
}
87-
} else {
88-
localFileExisted = existsSync(join(__dirname, 'zstd.linux-x64-gnu.node'));
89-
try {
90-
if (localFileExisted) {
91-
nativeBinding = require('./zstd.linux-x64-gnu.node');
92-
} else {
93-
nativeBinding = require('@mongodb-js/zstd-linux-x64-gnu');
94-
}
95-
} catch (e) {
96-
loadError = e;
97-
}
98-
}
99-
break;
100-
case 'arm64':
101-
if (isMusl()) {
102-
localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-musl.node'));
103-
try {
104-
if (localFileExisted) {
105-
nativeBinding = require('./zstd.linux-arm64-musl.node');
106-
} else {
107-
nativeBinding = require('@mongodb-js/zstd-linux-arm64-musl');
108-
}
109-
} catch (e) {
110-
loadError = e;
111-
}
112-
} else {
113-
localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm64-gnu.node'));
114-
try {
115-
if (localFileExisted) {
116-
nativeBinding = require('./zstd.linux-arm64-gnu.node');
117-
} else {
118-
nativeBinding = require('@mongodb-js/zstd-linux-arm64-gnu');
119-
}
120-
} catch (e) {
121-
loadError = e;
122-
}
123-
}
124-
break;
125-
case 'arm':
126-
localFileExisted = existsSync(join(__dirname, 'zstd.linux-arm-gnueabihf.node'));
127-
try {
128-
if (localFileExisted) {
129-
nativeBinding = require('./zstd.linux-arm-gnueabihf.node');
130-
} else {
131-
nativeBinding = require('@mongodb-js/zstd-linux-arm-gnueabihf');
132-
}
133-
} catch (e) {
134-
loadError = e;
135-
}
136-
break;
137-
default:
138-
throw new Error(`Unsupported architecture on Linux: ${arch}`);
139-
}
140-
break;
141-
default:
142-
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`);
143-
}
144-
145-
if (!nativeBinding) {
146-
if (loadError) {
147-
throw loadError;
15+
};
16+
exports.decompress = async function decompress(data) {
17+
try {
18+
return await _decompress(data);
19+
} catch (e) {
20+
throw new Error(`zstd: ${e.message}`);
14821
}
149-
throw new Error(`Failed to load native binding`);
150-
}
151-
152-
const { compress, decompress } = nativeBinding;
153-
154-
module.exports.compress = compress;
155-
module.exports.decompress = decompress;
22+
};

Diff for: package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
}
2121
},
2222
"files": [
23+
"bindings.js",
2324
"index.d.ts",
2425
"index.js"
2526
],
@@ -44,8 +45,8 @@
4445
},
4546
"scripts": {
4647
"artifacts": "napi artifacts",
47-
"build": "napi build --platform --release",
48-
"build:debug": "napi build --platform",
48+
"build": "napi build --js bindings.js --platform --release",
49+
"build:debug": "napi build --js bindings.js --platform",
4950
"format:js": "prettier --config ./package.json --write *.js",
5051
"format:rs": "cargo fmt",
5152
"prepublishOnly": "napi prepublish -t npm",

Diff for: test/index.test.js

+13
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,17 @@ describe('zstd', () => {
3737
});
3838
});
3939
});
40+
41+
describe('#decompress', () => {
42+
context('when decompressing invalid data', () => {
43+
it('includes a stack trace', async () => {
44+
try {
45+
await decompress(Buffer.from('invalid'));
46+
} catch (error) {
47+
expect(error.message).to.equal('zstd: Unknown frame descriptor');
48+
expect(error.stack).to.match(/at decompress/);
49+
}
50+
});
51+
});
52+
});
4053
});

0 commit comments

Comments
 (0)