From f9f06ef9d02279cc22f089743ddcffb78f9380ae Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 13 Feb 2023 15:18:13 -0500 Subject: [PATCH 1/7] NODE(5056): Fix EJSON.parse bug --- src/bson.ts | 2 +- src/error.ts | 11 +++++++++++ src/extended_json.ts | 6 +++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/bson.ts b/src/bson.ts index 8ec644bf..962e69e0 100644 --- a/src/bson.ts +++ b/src/bson.ts @@ -50,7 +50,7 @@ export { Decimal128 }; export { BSONValue } from './bson_value'; -export { BSONError, BSONVersionError } from './error'; +export { BSONError, BSONVersionError, BSONRuntimeError } from './error'; export { BSONType } from './constants'; export { EJSON } from './extended_json'; diff --git a/src/error.ts b/src/error.ts index b01cde53..5ef407ca 100644 --- a/src/error.ts +++ b/src/error.ts @@ -58,3 +58,14 @@ export class BSONVersionError extends BSONError { ); } } + +/** @public */ +export class BSONRuntimeError extends BSONError { + get name(): 'BSONRuntimeError' { + return 'BSONRuntimeError'; + } + + constructor(message: string) { + super(message); + } +} diff --git a/src/extended_json.ts b/src/extended_json.ts index d0b60131..3448fb6f 100644 --- a/src/extended_json.ts +++ b/src/extended_json.ts @@ -11,7 +11,7 @@ import { import { DBRef, isDBRefLike } from './db_ref'; import { Decimal128 } from './decimal128'; import { Double } from './double'; -import { BSONError, BSONVersionError } from './error'; +import { BSONError, BSONRuntimeError, BSONVersionError } from './error'; import { Int32 } from './int_32'; import { Long } from './long'; import { MaxKey } from './max_key'; @@ -125,10 +125,14 @@ function deserializeValue(value: any, options: EJSONOptions = {}) { if (options.legacy) { if (typeof d === 'number') date.setTime(d); else if (typeof d === 'string') date.setTime(Date.parse(d)); + else if (typeof d === 'bigint') date.setTime(Number(d)); + else throw new BSONRuntimeError('Unrecognized type for EJSON date'); } else { if (typeof d === 'string') date.setTime(Date.parse(d)); else if (Long.isLong(d)) date.setTime(d.toNumber()); else if (typeof d === 'number' && options.relaxed) date.setTime(d); + else if (typeof d === 'bigint') date.setTime(Number(d)); + else throw new BSONRuntimeError('Unrecognized type for EJSON date'); } return date; } From 39aaf8b0ad94654782bd50c2b60a3690b2a59306 Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 13 Feb 2023 15:23:29 -0500 Subject: [PATCH 2/7] test(NODE-5056): add tests --- test/node/extended_json.test.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/node/extended_json.test.ts b/test/node/extended_json.test.ts index 51e19fd7..c1f14f2d 100644 --- a/test/node/extended_json.test.ts +++ b/test/node/extended_json.test.ts @@ -2,7 +2,7 @@ import * as BSON from '../register-bson'; const EJSON = BSON.EJSON; import * as vm from 'node:vm'; import { expect } from 'chai'; -import { BSONVersionError } from '../../src'; +import { BSONVersionError, BSONRuntimeError } from '../../src'; // BSON types const Binary = BSON.Binary; @@ -440,6 +440,29 @@ describe('Extended JSON', function () { expect(bson).to.deep.equal(doc); }); }); + + context('when using useBigInt64=true', function () { + it('parses $date.$numberLong with millis since epoch', function () { + if (BSON.__noBigInt__) { + this.skip(); + } + const date = new Date(1676315495987); + const doc = { field: date }; + const stringified = EJSON.stringify(doc, { relaxed: false }); + const parsedDoc = EJSON.parse(stringified, { useBigInt64: true, relaxed: false }); + expect(parsedDoc).to.deep.equal(doc); + }); + }); + + context('when deserializing object with invalid $date key', function () { + it('throws a BSONRuntimeError', function () { + const doc = { field: { $date: new ArrayBuffer(10) } }; + const s = EJSON.stringify(doc, { relaxed: false }); + expect(() => { + EJSON.parse(s, { relaxed: false }); + }).to.throw(BSONRuntimeError, /Unrecognized type/i); + }); + }); }); context('when deserializing regex', function () { From 2ca06bef871df401d61afd7610fd363dc8d511f3 Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 13 Feb 2023 15:48:37 -0500 Subject: [PATCH 3/7] test(NODE-5056): Add new error class to expected exports --- test/node/exports.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/node/exports.test.ts b/test/node/exports.test.ts index ac258c20..2f800729 100644 --- a/test/node/exports.test.ts +++ b/test/node/exports.test.ts @@ -26,6 +26,7 @@ const EXPECTED_EXPORTS = [ 'BSONRegExp', 'Decimal128', 'BSONError', + 'BSONRuntimeError', 'setInternalBufferSize', 'serialize', 'serializeWithBufferAndIndex', From 78aea12d35c9675adfdf2815f9b38949000849a0 Mon Sep 17 00:00:00 2001 From: Warren James Date: Wed, 15 Feb 2023 15:04:34 -0500 Subject: [PATCH 4/7] test(NODE-5056): Add new tests for new error class --- test/node/error.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/node/error.test.ts b/test/node/error.test.ts index 8e1e7289..ee854368 100644 --- a/test/node/error.test.ts +++ b/test/node/error.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { loadESModuleBSON } from '../load_bson'; -import { __isWeb__, BSONError, BSONVersionError } from '../register-bson'; +import { __isWeb__, BSONError, BSONVersionError, BSONRuntimeError } from '../register-bson'; const instanceOfChecksWork = !__isWeb__; @@ -92,4 +92,14 @@ describe('BSONError', function () { expect(new BSONVersionError()).to.have.property('name', 'BSONVersionError'); }); }); + + describe('class BSONRuntimeError', function () { + it('is a BSONError instance', function () { + expect(BSONError.isBSONError(new BSONRuntimeError('Oopsie'))).to.be.true; + }); + + it('has a name property equal to "BSONRuntimeError"', function () { + expect(new BSONRuntimeError('Woops!')).to.have.property('name', 'BSONRuntimeError'); + }); + }); }); From 11879822a89fc0307881fb98c29dd2a7274e8cae Mon Sep 17 00:00:00 2001 From: Warren James Date: Wed, 15 Feb 2023 15:15:00 -0500 Subject: [PATCH 5/7] fix(NODE-5056): Update error messages and docs --- src/error.ts | 16 ++++++++++++++-- src/extended_json.ts | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/error.ts b/src/error.ts index 5ef407ca..76f90871 100644 --- a/src/error.ts +++ b/src/error.ts @@ -2,6 +2,8 @@ import { BSON_MAJOR_VERSION } from './constants'; /** * @public + * @category Error + * * `BSONError` objects are thrown when runtime errors occur. */ export class BSONError extends Error { @@ -46,7 +48,10 @@ export class BSONError extends Error { } } -/** @public */ +/** + * @public + * @category Error + */ export class BSONVersionError extends BSONError { get name(): 'BSONVersionError' { return 'BSONVersionError'; @@ -59,7 +64,14 @@ export class BSONVersionError extends BSONError { } } -/** @public */ +/** + * @public + * @category Error + * + * An error generated when BSON functions encounter an unexpected input + * or reaches an unexpected/invalid internal state + * + */ export class BSONRuntimeError extends BSONError { get name(): 'BSONRuntimeError' { return 'BSONRuntimeError'; diff --git a/src/extended_json.ts b/src/extended_json.ts index 3448fb6f..fd1f0a30 100644 --- a/src/extended_json.ts +++ b/src/extended_json.ts @@ -126,13 +126,13 @@ function deserializeValue(value: any, options: EJSONOptions = {}) { if (typeof d === 'number') date.setTime(d); else if (typeof d === 'string') date.setTime(Date.parse(d)); else if (typeof d === 'bigint') date.setTime(Number(d)); - else throw new BSONRuntimeError('Unrecognized type for EJSON date'); + else throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`); } else { if (typeof d === 'string') date.setTime(Date.parse(d)); else if (Long.isLong(d)) date.setTime(d.toNumber()); else if (typeof d === 'number' && options.relaxed) date.setTime(d); else if (typeof d === 'bigint') date.setTime(Number(d)); - else throw new BSONRuntimeError('Unrecognized type for EJSON date'); + else throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`); } return date; } From 7599a985541576a2d0aebfc781650ebb5613ee7d Mon Sep 17 00:00:00 2001 From: Warren James Date: Wed, 15 Feb 2023 16:47:10 -0500 Subject: [PATCH 6/7] docs(NODE-5056): update inline docs for BSONError --- src/error.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/error.ts b/src/error.ts index 76f90871..b3c5da86 100644 --- a/src/error.ts +++ b/src/error.ts @@ -4,7 +4,9 @@ import { BSON_MAJOR_VERSION } from './constants'; * @public * @category Error * - * `BSONError` objects are thrown when runtime errors occur. + * `BSONError` objects are thrown when BSON ecounters an error. + * + * This is the parent calss for all the other errors thrown in this library. */ export class BSONError extends Error { /** From d52c3dd23e24911846850d0456fd9418e0a3b501 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 15 Feb 2023 17:53:51 -0500 Subject: [PATCH 7/7] fix typo --- src/error.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.ts b/src/error.ts index b3c5da86..f98f0fb7 100644 --- a/src/error.ts +++ b/src/error.ts @@ -6,7 +6,7 @@ import { BSON_MAJOR_VERSION } from './constants'; * * `BSONError` objects are thrown when BSON ecounters an error. * - * This is the parent calss for all the other errors thrown in this library. + * This is the parent class for all the other errors thrown by this library. */ export class BSONError extends Error { /**