Skip to content

Commit b850868

Browse files
authored
feat(NODE-4867)!: adopt BSON v5 (#3490)
1 parent 410ef30 commit b850868

20 files changed

+360
-431
lines changed

etc/notes/CHANGES_5.0.0.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ The following is a detailed collection of the changes in the major v5 release of
1818

1919
### Dot Notation Typescript Support Removed By Default
2020

21-
**NOTE** This is a **Typescript compile-time only** change. Dot notation in filters sent to MongoDB will still work the same.
21+
**NOTE** This is a **Typescript compile-time only** change. Dot notation in filters sent to MongoDB will still work the same.
2222

2323
Version 4.3.0 introduced Typescript support for dot notation in filter predicates. For example:
2424

package-lock.json

+39-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"email": "[email protected]"
2626
},
2727
"dependencies": {
28-
"bson": "^4.7.0",
28+
"bson": "^5.0.0-alpha.3",
2929
"mongodb-connection-string-url": "^2.6.0",
3030
"socks": "^2.7.1"
3131
},

src/bson.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import type { DeserializeOptions, SerializeOptions } from 'bson';
22

33
export {
44
Binary,
5+
BSON,
56
BSONRegExp,
67
BSONSymbol,
8+
BSONType,
79
calculateObjectSize,
810
Code,
911
DBRef,
@@ -13,21 +15,13 @@ export {
1315
Double,
1416
Int32,
1517
Long,
16-
Map,
1718
MaxKey,
1819
MinKey,
1920
ObjectId,
2021
serialize,
2122
Timestamp
2223
} from 'bson';
2324

24-
// TODO(NODE-4867): fix with bson v5
25-
/** @internal */
26-
// eslint-disable-next-line @typescript-eslint/no-var-requires
27-
const BSON = require('bson');
28-
29-
export { BSON };
30-
3125
/**
3226
* BSON Serialization options.
3327
* @public
@@ -42,6 +36,7 @@ export interface BSONSerializeOptions
4236
| 'allowObjectSmallerThanBufferSize'
4337
| 'index'
4438
| 'validation'
39+
| 'useBigInt64'
4540
> {
4641
/**
4742
* Enabling the raw option will return a [Node.js Buffer](https://nodejs.org/api/buffer.html)

src/cmap/auth/mongodb_aws.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
MongoMissingCredentialsError,
1212
MongoRuntimeError
1313
} from '../../error';
14-
import { Callback, maxWireVersion, ns } from '../../utils';
14+
import { ByteUtils, Callback, maxWireVersion, ns } from '../../utils';
1515
import { AuthContext, AuthProvider } from './auth_provider';
1616
import { MongoCredentials } from './mongo_credentials';
1717
import { AuthMechanism } from './providers';
@@ -108,7 +108,8 @@ export class MongoDBAWS extends AuthProvider {
108108
return;
109109
}
110110

111-
if (serverNonce.compare(nonce, 0, nonce.length, 0, nonce.length) !== 0) {
111+
// TODO(NODE-4990)
112+
if (!ByteUtils.equals(serverNonce.subarray(0, nonce.byteLength), nonce)) {
112113
// TODO(NODE-3483)
113114
callback(new MongoRuntimeError('Server nonce does not begin with client nonce'));
114115
return;
@@ -130,7 +131,7 @@ export class MongoDBAWS extends AuthProvider {
130131
headers: {
131132
'Content-Type': 'application/x-www-form-urlencoded',
132133
'Content-Length': body.length,
133-
'X-MongoDB-Server-Nonce': serverNonce.toString('base64'),
134+
'X-MongoDB-Server-Nonce': ByteUtils.toBase64(serverNonce),
134135
'X-MongoDB-GS2-CB-Flag': 'n'
135136
},
136137
path: '/',

src/cmap/commands.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export class Query {
136136
}
137137

138138
// Uses a single allocated buffer for the process, avoiding multiple memory allocations
139-
toBin(): Buffer[] {
139+
toBin(): Uint8Array[] {
140140
const buffers = [];
141141
let projection = null;
142142

@@ -550,7 +550,7 @@ export class Msg {
550550
return buffers;
551551
}
552552

553-
makeDocumentSegment(buffers: Buffer[], document: Document): number {
553+
makeDocumentSegment(buffers: Uint8Array[], document: Document): number {
554554
const payloadTypeBuffer = Buffer.alloc(1);
555555
payloadTypeBuffer[0] = 0;
556556

@@ -561,7 +561,7 @@ export class Msg {
561561
return payloadTypeBuffer.length + documentBuffer.length;
562562
}
563563

564-
serializeBson(document: Document): Buffer {
564+
serializeBson(document: Document): Uint8Array {
565565
return BSON.serialize(document, {
566566
checkKeys: this.checkKeys,
567567
serializeFunctions: this.serializeFunctions,

src/index.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ import { MongoClient } from './mongo_client';
1717
import { CancellationToken } from './mongo_types';
1818
import { ClientSession } from './sessions';
1919

20-
/** @internal */
20+
/** @public */
2121
export { BSON } from './bson';
2222
export {
2323
Binary,
2424
BSONRegExp,
2525
BSONSymbol,
26+
BSONType,
2627
Code,
2728
DBRef,
2829
Decimal128,
2930
Double,
3031
Int32,
3132
Long,
32-
Map,
3333
MaxKey,
3434
MinKey,
3535
ObjectId,
@@ -103,7 +103,6 @@ export { MongoErrorLabel } from './error';
103103
export { ExplainVerbosity } from './explain';
104104
export { LoggerLevel } from './logger';
105105
export { ServerApiVersion } from './mongo_client';
106-
export { BSONType } from './mongo_types';
107106
export { ReturnDocument } from './operations/find_and_modify';
108107
export { ProfilingLevel } from './operations/set_profiling_level';
109108
export { ReadConcernLevel } from './read_concern';

src/mongo_types.ts

+1-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ObjectIdLike } from 'bson';
1+
import type { BSONType, ObjectIdLike } from 'bson';
22
import { EventEmitter } from 'events';
33

44
import type {
@@ -158,33 +158,6 @@ export type BitwiseFilter =
158158
| Binary /** BinData bit mask */
159159
| ReadonlyArray<number>; /** `[ <position1>, <position2>, ... ]` */
160160

161-
/** @public */
162-
export const BSONType = Object.freeze({
163-
double: 1,
164-
string: 2,
165-
object: 3,
166-
array: 4,
167-
binData: 5,
168-
undefined: 6,
169-
objectId: 7,
170-
bool: 8,
171-
date: 9,
172-
null: 10,
173-
regex: 11,
174-
dbPointer: 12,
175-
javascript: 13,
176-
symbol: 14,
177-
javascriptWithScope: 15,
178-
int: 16,
179-
timestamp: 17,
180-
long: 18,
181-
decimal: 19,
182-
minKey: -1,
183-
maxKey: 127
184-
} as const);
185-
186-
/** @public */
187-
export type BSONType = typeof BSONType[keyof typeof BSONType];
188161
/** @public */
189162
export type BSONTypeAlias = keyof typeof BSONType;
190163

src/sessions.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { ReadPreference } from './read_preference';
3030
import { _advanceClusterTime, ClusterTime, TopologyType } from './sdam/common';
3131
import { isTransactionCommand, Transaction, TransactionOptions, TxnState } from './transactions';
3232
import {
33+
ByteUtils,
3334
calculateDurationInMs,
3435
Callback,
3536
commandSupportsReadConcern,
@@ -347,7 +348,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
347348
return false;
348349
}
349350

350-
return this.id.id.buffer.equals(session.id.id.buffer);
351+
return ByteUtils.equals(this.id.id.buffer, session.id.id.buffer);
351352
}
352353

353354
/**

src/utils.ts

+21-3
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,28 @@ import { W, WriteConcern, WriteConcernOptions } from './write_concern';
3737
*/
3838
export type Callback<T = any> = (error?: AnyError, result?: T) => void;
3939

40-
export const MAX_JS_INT = Number.MAX_SAFE_INTEGER + 1;
41-
4240
export type AnyOptions = Document;
4341

42+
export const ByteUtils = {
43+
toLocalBufferType(this: void, buffer: Buffer | Uint8Array): Buffer {
44+
return Buffer.isBuffer(buffer)
45+
? buffer
46+
: Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
47+
},
48+
49+
equals(this: void, seqA: Uint8Array, seqB: Uint8Array) {
50+
return ByteUtils.toLocalBufferType(seqA).equals(seqB);
51+
},
52+
53+
compare(this: void, seqA: Uint8Array, seqB: Uint8Array) {
54+
return ByteUtils.toLocalBufferType(seqA).compare(seqB);
55+
},
56+
57+
toBase64(this: void, uint8array: Uint8Array) {
58+
return ByteUtils.toLocalBufferType(uint8array).toString('base64');
59+
}
60+
};
61+
4462
/**
4563
* Throws if collectionName is not a valid mongodb collection namespace.
4664
* @internal
@@ -1401,7 +1419,7 @@ export function compareObjectId(oid1?: ObjectId | null, oid2?: ObjectId | null):
14011419
return 1;
14021420
}
14031421

1404-
return oid1.id.compare(oid2.id);
1422+
return ByteUtils.compare(oid1.id, oid2.id);
14051423
}
14061424

14071425
export function parseInteger(value: unknown): number | null {

test/integration/change-streams/change_streams.prose.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ describe('Change Stream prose tests', function () {
429429

430430
// Helpers
431431
timestamp() {
432-
return new Timestamp(this._timestampCounter++, Date.now());
432+
return new Timestamp({ i: this._timestampCounter++, t: this._timestampCounter });
433433
}
434434

435435
applyOpTime(obj) {

test/integration/client-side-encryption/client_side_encryption.spec.test.ts

-8
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@ const skippedAuthTests = [
3535
'unset works with an encrypted field',
3636
'updateOne with deterministic encryption',
3737
'updateMany with deterministic encryption',
38-
'type=date',
39-
'type=regex',
40-
'type=timestamp',
41-
'type=javascript',
42-
'type=binData',
43-
'type=int',
44-
'type=objectId',
45-
'type=symbol',
4638
'replaceOne with encryption',
4739
'Insert with encryption on a missing key',
4840
'A local schema should override',

0 commit comments

Comments
 (0)