Skip to content

Commit d5088af

Browse files
authored
fix(NODE-5056): EJSON.parse date handling when useBigInt64=true (#562)
1 parent 6ef1000 commit d5088af

File tree

6 files changed

+69
-6
lines changed

6 files changed

+69
-6
lines changed

src/bson.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export {
5050
Decimal128
5151
};
5252
export { BSONValue } from './bson_value';
53-
export { BSONError, BSONVersionError } from './error';
53+
export { BSONError, BSONVersionError, BSONRuntimeError } from './error';
5454
export { BSONType } from './constants';
5555
export { EJSON } from './extended_json';
5656

src/error.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { BSON_MAJOR_VERSION } from './constants';
22

33
/**
44
* @public
5-
* `BSONError` objects are thrown when runtime errors occur.
5+
* @category Error
6+
*
7+
* `BSONError` objects are thrown when BSON ecounters an error.
8+
*
9+
* This is the parent class for all the other errors thrown by this library.
610
*/
711
export class BSONError extends Error {
812
/**
@@ -46,7 +50,10 @@ export class BSONError extends Error {
4650
}
4751
}
4852

49-
/** @public */
53+
/**
54+
* @public
55+
* @category Error
56+
*/
5057
export class BSONVersionError extends BSONError {
5158
get name(): 'BSONVersionError' {
5259
return 'BSONVersionError';
@@ -58,3 +65,21 @@ export class BSONVersionError extends BSONError {
5865
);
5966
}
6067
}
68+
69+
/**
70+
* @public
71+
* @category Error
72+
*
73+
* An error generated when BSON functions encounter an unexpected input
74+
* or reaches an unexpected/invalid internal state
75+
*
76+
*/
77+
export class BSONRuntimeError extends BSONError {
78+
get name(): 'BSONRuntimeError' {
79+
return 'BSONRuntimeError';
80+
}
81+
82+
constructor(message: string) {
83+
super(message);
84+
}
85+
}

src/extended_json.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import { DBRef, isDBRefLike } from './db_ref';
1212
import { Decimal128 } from './decimal128';
1313
import { Double } from './double';
14-
import { BSONError, BSONVersionError } from './error';
14+
import { BSONError, BSONRuntimeError, BSONVersionError } from './error';
1515
import { Int32 } from './int_32';
1616
import { Long } from './long';
1717
import { MaxKey } from './max_key';
@@ -125,10 +125,14 @@ function deserializeValue(value: any, options: EJSONOptions = {}) {
125125
if (options.legacy) {
126126
if (typeof d === 'number') date.setTime(d);
127127
else if (typeof d === 'string') date.setTime(Date.parse(d));
128+
else if (typeof d === 'bigint') date.setTime(Number(d));
129+
else throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`);
128130
} else {
129131
if (typeof d === 'string') date.setTime(Date.parse(d));
130132
else if (Long.isLong(d)) date.setTime(d.toNumber());
131133
else if (typeof d === 'number' && options.relaxed) date.setTime(d);
134+
else if (typeof d === 'bigint') date.setTime(Number(d));
135+
else throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`);
132136
}
133137
return date;
134138
}

test/node/error.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { loadESModuleBSON } from '../load_bson';
33

4-
import { __isWeb__, BSONError, BSONVersionError } from '../register-bson';
4+
import { __isWeb__, BSONError, BSONVersionError, BSONRuntimeError } from '../register-bson';
55

66
const instanceOfChecksWork = !__isWeb__;
77

@@ -92,4 +92,14 @@ describe('BSONError', function () {
9292
expect(new BSONVersionError()).to.have.property('name', 'BSONVersionError');
9393
});
9494
});
95+
96+
describe('class BSONRuntimeError', function () {
97+
it('is a BSONError instance', function () {
98+
expect(BSONError.isBSONError(new BSONRuntimeError('Oopsie'))).to.be.true;
99+
});
100+
101+
it('has a name property equal to "BSONRuntimeError"', function () {
102+
expect(new BSONRuntimeError('Woops!')).to.have.property('name', 'BSONRuntimeError');
103+
});
104+
});
95105
});

test/node/exports.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const EXPECTED_EXPORTS = [
2626
'BSONRegExp',
2727
'Decimal128',
2828
'BSONError',
29+
'BSONRuntimeError',
2930
'setInternalBufferSize',
3031
'serialize',
3132
'serializeWithBufferAndIndex',

test/node/extended_json.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as BSON from '../register-bson';
22
const EJSON = BSON.EJSON;
33
import * as vm from 'node:vm';
44
import { expect } from 'chai';
5-
import { BSONVersionError } from '../../src';
5+
import { BSONVersionError, BSONRuntimeError } from '../../src';
66

77
// BSON types
88
const Binary = BSON.Binary;
@@ -440,6 +440,29 @@ describe('Extended JSON', function () {
440440
expect(bson).to.deep.equal(doc);
441441
});
442442
});
443+
444+
context('when using useBigInt64=true', function () {
445+
it('parses $date.$numberLong with millis since epoch', function () {
446+
if (BSON.__noBigInt__) {
447+
this.skip();
448+
}
449+
const date = new Date(1676315495987);
450+
const doc = { field: date };
451+
const stringified = EJSON.stringify(doc, { relaxed: false });
452+
const parsedDoc = EJSON.parse(stringified, { useBigInt64: true, relaxed: false });
453+
expect(parsedDoc).to.deep.equal(doc);
454+
});
455+
});
456+
457+
context('when deserializing object with invalid $date key', function () {
458+
it('throws a BSONRuntimeError', function () {
459+
const doc = { field: { $date: new ArrayBuffer(10) } };
460+
const s = EJSON.stringify(doc, { relaxed: false });
461+
expect(() => {
462+
EJSON.parse(s, { relaxed: false });
463+
}).to.throw(BSONRuntimeError, /Unrecognized type/i);
464+
});
465+
});
443466
});
444467

445468
context('when deserializing regex', function () {

0 commit comments

Comments
 (0)