Skip to content

Commit 6cd6fc8

Browse files
committed
feat(NODE-7097): adopt built-in zstd support
1 parent 275afa5 commit 6cd6fc8

3 files changed

Lines changed: 66 additions & 4 deletions

File tree

src/deps.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as zlib from 'zlib';
2+
13
import { type Stream } from './cmap/connect';
24
import { MongoMissingDependencyError } from './error';
35
import type { Callback } from './utils';
@@ -60,15 +62,49 @@ type ZStandardLib = {
6062

6163
export type ZStandard = ZStandardLib | { kModuleError: MongoMissingDependencyError };
6264

65+
function getBuiltInZstdLibrary(): ZStandardLib | null {
66+
if (typeof zlib.zstdCompress !== 'function' || typeof zlib.zstdDecompress !== 'function') {
67+
return null;
68+
}
69+
70+
return {
71+
compress(buf: Uint8Array, level?: number): Promise<Uint8Array> {
72+
return new Promise((resolve, reject) => {
73+
zlib.zstdCompress(
74+
buf,
75+
level == null ? {} : { params: { [zlib.constants.ZSTD_c_compressionLevel]: level } },
76+
(error, result) => {
77+
if (error) return reject(error);
78+
resolve(result);
79+
}
80+
);
81+
});
82+
},
83+
decompress(buf: Uint8Array): Promise<Uint8Array> {
84+
return new Promise((resolve, reject) => {
85+
zlib.zstdDecompress(buf, (error, result) => {
86+
if (error) return reject(error);
87+
resolve(result);
88+
});
89+
});
90+
}
91+
};
92+
}
93+
6394
export function getZstdLibrary(): ZStandardLib | { kModuleError: MongoMissingDependencyError } {
95+
const builtInZstdLibrary = getBuiltInZstdLibrary();
96+
if (builtInZstdLibrary != null) {
97+
return builtInZstdLibrary;
98+
}
99+
64100
let ZStandard: ZStandardLib | { kModuleError: MongoMissingDependencyError };
65101
try {
66102
// eslint-disable-next-line @typescript-eslint/no-require-imports
67103
ZStandard = require('@mongodb-js/zstd');
68104
} catch (error) {
69105
ZStandard = makeErrorModule(
70106
new MongoMissingDependencyError(
71-
'Optional module `@mongodb-js/zstd` not found. Please install it to enable zstd compression',
107+
'Built-in zstd support is unavailable and optional module `@mongodb-js/zstd` not found. Please use Node.js 22.15.0+ or install `@mongodb-js/zstd` to enable zstd compression',
72108
{ cause: error, dependencyName: 'zstd' }
73109
)
74110
);

test/unit/assorted/optional_require.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { expect } from 'chai';
22
import { existsSync } from 'fs';
33
import { resolve } from 'path';
4+
import * as zlib from 'zlib';
45

56
import {
67
AuthContext,
@@ -16,6 +17,32 @@ function moduleExistsSync(moduleName) {
1617
}
1718

1819
describe('optionalRequire', function () {
20+
describe('Zstandard', function () {
21+
it('supports built-in zstd when the addon is not installed', async function () {
22+
const moduleName = '@mongodb-js/zstd';
23+
if (moduleExistsSync(moduleName)) {
24+
return this.skip();
25+
}
26+
27+
const error = await compress(
28+
{ zlibCompressionLevel: 0, agreedCompressor: 'zstd' },
29+
Buffer.from('test', 'utf8')
30+
).then(
31+
() => null,
32+
e => e
33+
);
34+
35+
const hasBuiltInZstd =
36+
typeof zlib.zstdCompress === 'function' && typeof zlib.zstdDecompress === 'function';
37+
38+
if (hasBuiltInZstd) {
39+
expect(error).to.equal(null);
40+
} else {
41+
expect(error).to.be.instanceOf(MongoMissingDependencyError);
42+
}
43+
});
44+
});
45+
1946
describe('Snappy', function () {
2047
it('should error if not installed', async function () {
2148
const moduleName = 'snappy';

test/unit/cmap/wire_protocol/compression.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as zstd from '@mongodb-js/zstd';
21
import { expect } from 'chai';
32

43
import { compress, Compressor, decompress } from '../../../mongodb';
@@ -13,8 +12,8 @@ describe('compression', function () {
1312

1413
it('compresses the data', async function () {
1514
const data = await compress(options, buffer);
16-
// decompress throws if the message is not zstd compresed
17-
expect(await zstd.decompress(data)).to.deep.equal(buffer);
15+
expect(data).to.not.deep.equal(buffer);
16+
expect(await decompress(Compressor.zstd, data)).to.deep.equal(buffer);
1817
});
1918
});
2019
});

0 commit comments

Comments
 (0)