Skip to content

Commit d9f0eaa

Browse files
nbbeekendurran
andauthored
feat(NODE-4892)!: error on bson types not from this version (#543)
Co-authored-by: Durran Jordan <[email protected]>
1 parent 946866d commit d9f0eaa

32 files changed

+512
-647
lines changed

docs/upgrade-to-v5.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,19 @@ try {
288288
throw error;
289289
}
290290
```
291+
292+
### Explicit cross version incompatibility
293+
294+
Starting with v5.0.0 of the BSON library instances of types from previous versions will throw an error when passed to the serializer.
295+
This is to ensure that types are always serialized correctly and that there is no unexpected silent BSON serialization mistakes that could occur when mixing versions.
296+
It's unexpected for any applications to have more than one version of the BSON library but with nested dependencies and re-exporting, this new error will illuminate those incorrect combinations.
297+
298+
```ts
299+
// npm install bson4@npm:bson@4
300+
// npm install bson5@npm:bson@5
301+
import { ObjectId } from 'bson4';
302+
import { serialize } from 'bson5';
303+
304+
serialize({ _id: new ObjectId() });
305+
// Uncaught BSONVersionError: Unsupported BSON version, bson types must be from bson 5.0 or later
306+
```

src/binary.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { EJSONOptions } from './extended_json';
44
import { BSONError } from './error';
55
import { BSON_BINARY_SUBTYPE_UUID_NEW } from './constants';
66
import { ByteUtils } from './utils/byte_utils';
7+
import { BSONValue } from './bson_value';
78

89
/** @public */
910
export type BinarySequence = Uint8Array | number[];
@@ -27,7 +28,7 @@ export interface BinaryExtended {
2728
* @public
2829
* @category BSONType
2930
*/
30-
export class Binary {
31+
export class Binary extends BSONValue {
3132
get _bsontype(): 'Binary' {
3233
return 'Binary';
3334
}
@@ -75,6 +76,7 @@ export class Binary {
7576
* @param subType - the option binary type.
7677
*/
7778
constructor(buffer?: string | BinarySequence, subType?: number) {
79+
super();
7880
if (
7981
!(buffer == null) &&
8082
!(typeof buffer === 'string') &&

src/bson.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ export {
4949
BSONRegExp,
5050
Decimal128
5151
};
52-
export { BSONError } from './error';
52+
export { BSONValue } from './bson_value';
53+
export { BSONError, BSONVersionError } from './error';
5354
export { BSONType } from './constants';
5455
export { EJSON } from './extended_json';
5556

src/bson_value.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { BSON_MAJOR_VERSION } from './constants';
2+
3+
/** @public */
4+
export abstract class BSONValue {
5+
/** @public */
6+
public abstract get _bsontype(): string;
7+
8+
/** @internal */
9+
get [Symbol.for('@@mdb.bson.version')](): typeof BSON_MAJOR_VERSION {
10+
return BSON_MAJOR_VERSION;
11+
}
12+
13+
/** @public */
14+
public abstract inspect(): string;
15+
16+
/** @internal */
17+
abstract toExtendedJSON(): unknown;
18+
}

src/code.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Document } from './bson';
2+
import { BSONValue } from './bson_value';
23

34
/** @public */
45
export interface CodeExtended {
@@ -11,7 +12,7 @@ export interface CodeExtended {
1112
* @public
1213
* @category BSONType
1314
*/
14-
export class Code {
15+
export class Code extends BSONValue {
1516
get _bsontype(): 'Code' {
1617
return 'Code';
1718
}
@@ -27,6 +28,7 @@ export class Code {
2728
* @param scope - an optional scope for the function.
2829
*/
2930
constructor(code: string | Function, scope?: Document | null) {
31+
super();
3032
this.code = code.toString();
3133
this.scope = scope ?? null;
3234
}

src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/** @internal */
2+
export const BSON_MAJOR_VERSION = 5 as const;
3+
14
/** @internal */
25
export const BSON_INT32_MAX = 0x7fffffff;
36
/** @internal */

src/db_ref.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Document } from './bson';
2+
import { BSONValue } from './bson_value';
23
import type { EJSONOptions } from './extended_json';
34
import type { ObjectId } from './objectid';
45

@@ -28,7 +29,7 @@ export function isDBRefLike(value: unknown): value is DBRefLike {
2829
* @public
2930
* @category BSONType
3031
*/
31-
export class DBRef {
32+
export class DBRef extends BSONValue {
3233
get _bsontype(): 'DBRef' {
3334
return 'DBRef';
3435
}
@@ -44,6 +45,7 @@ export class DBRef {
4445
* @param db - optional db name, if omitted the reference is local to the current db.
4546
*/
4647
constructor(collection: string, oid: ObjectId, db?: string, fields?: Document) {
48+
super();
4749
// check if namespace has been provided
4850
const parts = collection.split('.');
4951
if (parts.length === 2) {

src/decimal128.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { BSONValue } from './bson_value';
12
import { BSONError } from './error';
23
import { Long } from './long';
34
import { isUint8Array } from './parser/utils';
@@ -126,7 +127,7 @@ export interface Decimal128Extended {
126127
* @public
127128
* @category BSONType
128129
*/
129-
export class Decimal128 {
130+
export class Decimal128 extends BSONValue {
130131
get _bsontype(): 'Decimal128' {
131132
return 'Decimal128';
132133
}
@@ -138,6 +139,7 @@ export class Decimal128 {
138139
* or a string representation as returned by .toString()
139140
*/
140141
constructor(bytes: Uint8Array | string) {
142+
super();
141143
if (typeof bytes === 'string') {
142144
this.bytes = Decimal128.fromString(bytes).bytes;
143145
} else if (isUint8Array(bytes)) {

src/double.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { BSONValue } from './bson_value';
12
import type { EJSONOptions } from './extended_json';
23

34
/** @public */
@@ -10,7 +11,7 @@ export interface DoubleExtended {
1011
* @public
1112
* @category BSONType
1213
*/
13-
export class Double {
14+
export class Double extends BSONValue {
1415
get _bsontype(): 'Double' {
1516
return 'Double';
1617
}
@@ -22,6 +23,7 @@ export class Double {
2223
* @param value - the number we want to represent as a double.
2324
*/
2425
constructor(value: number) {
26+
super();
2527
if ((value as unknown) instanceof Number) {
2628
value = value.valueOf();
2729
}

src/error.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { BSON_MAJOR_VERSION } from './constants';
2+
13
/**
24
* @public
35
* `BSONError` objects are thrown when runtime errors occur.
@@ -43,3 +45,16 @@ export class BSONError extends Error {
4345
);
4446
}
4547
}
48+
49+
/** @public */
50+
export class BSONVersionError extends BSONError {
51+
get name(): 'BSONVersionError' {
52+
return 'BSONVersionError';
53+
}
54+
55+
constructor() {
56+
super(
57+
`Unsupported BSON version, bson types must be from bson ${BSON_MAJOR_VERSION}.0 or later`
58+
);
59+
}
60+
}

0 commit comments

Comments
 (0)