Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion src/deps.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as zlib from 'zlib';

import { type Stream } from './cmap/connect';
import { MongoMissingDependencyError } from './error';
import type { Callback } from './utils';
Expand Down Expand Up @@ -60,15 +62,49 @@ type ZStandardLib = {

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

function getBuiltInZstdLibrary(): ZStandardLib | null {
if (typeof zlib.zstdCompress !== 'function' || typeof zlib.zstdDecompress !== 'function') {
return null;
}

return {
compress(buf: Uint8Array, level?: number): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
zlib.zstdCompress(
buf,
level == null ? {} : { params: { [zlib.constants.ZSTD_c_compressionLevel]: level } },
(error, result) => {
if (error) return reject(error);
resolve(result);
}
);
});
},
decompress(buf: Uint8Array): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
zlib.zstdDecompress(buf, (error, result) => {
if (error) return reject(error);
resolve(result);
});
});
}
};
}

export function getZstdLibrary(): ZStandardLib | { kModuleError: MongoMissingDependencyError } {
const builtInZstdLibrary = getBuiltInZstdLibrary();
if (builtInZstdLibrary != null) {
return builtInZstdLibrary;
}

let ZStandard: ZStandardLib | { kModuleError: MongoMissingDependencyError };
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
ZStandard = require('@mongodb-js/zstd');
} catch (error) {
ZStandard = makeErrorModule(
new MongoMissingDependencyError(
'Optional module `@mongodb-js/zstd` not found. Please install it to enable zstd compression',
'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',
{ cause: error, dependencyName: 'zstd' }
)
);
Expand Down
27 changes: 27 additions & 0 deletions test/unit/assorted/optional_require.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { existsSync } from 'fs';
import { resolve } from 'path';
import * as zlib from 'zlib';

import {
AuthContext,
Expand All @@ -16,6 +17,32 @@ function moduleExistsSync(moduleName) {
}

describe('optionalRequire', function () {
describe('Zstandard', function () {
it('supports built-in zstd when the addon is not installed', async function () {
const moduleName = '@mongodb-js/zstd';
if (moduleExistsSync(moduleName)) {
return this.skip();
}

const error = await compress(
{ zlibCompressionLevel: 0, agreedCompressor: 'zstd' },
Buffer.from('test', 'utf8')
).then(
() => null,
e => e
);

const hasBuiltInZstd =
typeof zlib.zstdCompress === 'function' && typeof zlib.zstdDecompress === 'function';

if (hasBuiltInZstd) {
expect(error).to.equal(null);
} else {
expect(error).to.be.instanceOf(MongoMissingDependencyError);
}
});
});

describe('Snappy', function () {
it('should error if not installed', async function () {
const moduleName = 'snappy';
Expand Down
5 changes: 2 additions & 3 deletions test/unit/cmap/wire_protocol/compression.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as zstd from '@mongodb-js/zstd';
import { expect } from 'chai';

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

it('compresses the data', async function () {
const data = await compress(options, buffer);
// decompress throws if the message is not zstd compresed
expect(await zstd.decompress(data)).to.deep.equal(buffer);
expect(data).to.not.deep.equal(buffer);
expect(await decompress(Compressor.zstd, data)).to.deep.equal(buffer);
});
});
});
Expand Down
Loading