From c95f4c11fbdfe7d55cb19198d68a17327d6d260d Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 15 Dec 2022 15:21:46 -0500 Subject: [PATCH 01/21] feat(NODE-4892)!: error on bson types not from this version --- src/binary.ts | 9 + src/code.ts | 4 + src/db_ref.ts | 4 + src/decimal128.ts | 4 + src/double.ts | 4 + src/extended_json.ts | 13 +- src/int_32.ts | 4 + src/long.ts | 4 + src/max_key.ts | 4 + src/min_key.ts | 4 + src/objectid.ts | 8 +- src/parser/calculate_size.ts | 2 +- src/parser/serializer.ts | 96 +++++----- src/regexp.ts | 13 ++ src/symbol.ts | 8 +- src/timestamp.ts | 4 + test/node/bson_older_versions_tests.js | 254 ------------------------- test/node/bson_test.js | 79 -------- test/node/bson_type_classes.test.ts | 68 +++++++ test/node/cross_compat.test.ts | 83 ++++++++ test/node/extended_json.test.ts | 208 +------------------- test/node/parser/serializer.test.ts | 8 +- test/node/type_identifier_tests.js | 9 +- test/types/bson.test-d.ts | 8 +- 24 files changed, 293 insertions(+), 609 deletions(-) delete mode 100644 test/node/bson_older_versions_tests.js create mode 100644 test/node/bson_type_classes.test.ts create mode 100644 test/node/cross_compat.test.ts diff --git a/src/binary.ts b/src/binary.ts index f87a94ec..3875601b 100644 --- a/src/binary.ts +++ b/src/binary.ts @@ -31,6 +31,10 @@ export class Binary { get _bsontype(): 'Binary' { return 'Binary'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } /** * Binary default subtype @@ -305,6 +309,11 @@ const UUID_BYTE_LENGTH = 16; * @public */ export class UUID extends Binary { + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } + static cacheHexString: boolean; /** UUID hexString cache @internal */ diff --git a/src/code.ts b/src/code.ts index 5f2f51a7..9c11dd4a 100644 --- a/src/code.ts +++ b/src/code.ts @@ -15,6 +15,10 @@ export class Code { get _bsontype(): 'Code' { return 'Code'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } code: string; diff --git a/src/db_ref.ts b/src/db_ref.ts index a8997890..d56b0edc 100644 --- a/src/db_ref.ts +++ b/src/db_ref.ts @@ -32,6 +32,10 @@ export class DBRef { get _bsontype(): 'DBRef' { return 'DBRef'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } collection!: string; oid!: ObjectId; diff --git a/src/decimal128.ts b/src/decimal128.ts index d705b4cc..0d20625a 100644 --- a/src/decimal128.ts +++ b/src/decimal128.ts @@ -130,6 +130,10 @@ export class Decimal128 { get _bsontype(): 'Decimal128' { return 'Decimal128'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } readonly bytes!: Uint8Array; diff --git a/src/double.ts b/src/double.ts index 7a4feb64..87ff8038 100644 --- a/src/double.ts +++ b/src/double.ts @@ -14,6 +14,10 @@ export class Double { get _bsontype(): 'Double' { return 'Double'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } value!: number; /** diff --git a/src/extended_json.ts b/src/extended_json.ts index f925630d..dcfef767 100644 --- a/src/extended_json.ts +++ b/src/extended_json.ts @@ -273,13 +273,9 @@ const BSON_TYPE_MAPPINGS = { ), MaxKey: () => new MaxKey(), MinKey: () => new MinKey(), - ObjectID: (o: ObjectId) => new ObjectId(o), - // The _bsontype for ObjectId is spelled with a capital "D", to the mapping above will be used (most of the time) - // specifically BSON versions 4.0.0 and 4.0.1 the _bsontype was changed to "ObjectId" so we keep this mapping to support - // those version of BSON ObjectId: (o: ObjectId) => new ObjectId(o), BSONRegExp: (o: BSONRegExp) => new BSONRegExp(o.pattern, o.options), - Symbol: (o: BSONSymbol) => new BSONSymbol(o.value), + BSONSymbol: (o: BSONSymbol) => new BSONSymbol(o.value), Timestamp: (o: Timestamp) => Timestamp.fromBits(o.low, o.high) } as const; @@ -310,6 +306,13 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) { } } return _doc; + } else if ( + doc != null && + typeof doc === 'object' && + typeof doc._bsontype === 'string' && + doc[Symbol.for('@@mdb.bson.version')] == null + ) { + throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); } else if (isBSONType(doc)) { // the "document" is really just a BSON type object // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/int_32.ts b/src/int_32.ts index 6160b718..dc7bbfa9 100644 --- a/src/int_32.ts +++ b/src/int_32.ts @@ -14,6 +14,10 @@ export class Int32 { get _bsontype(): 'Int32' { return 'Int32'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } value!: number; /** diff --git a/src/long.ts b/src/long.ts index 7d23aeea..cc8fa39c 100644 --- a/src/long.ts +++ b/src/long.ts @@ -102,6 +102,10 @@ export class Long { get _bsontype(): 'Long' { return 'Long'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } /** An indicator used to reliably determine if an object is a Long or not. */ get __isLong__(): boolean { diff --git a/src/max_key.ts b/src/max_key.ts index 5541a65a..f8ce78eb 100644 --- a/src/max_key.ts +++ b/src/max_key.ts @@ -12,6 +12,10 @@ export class MaxKey { get _bsontype(): 'MaxKey' { return 'MaxKey'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } /** @internal */ toExtendedJSON(): MaxKeyExtended { diff --git a/src/min_key.ts b/src/min_key.ts index 99181449..79d23e26 100644 --- a/src/min_key.ts +++ b/src/min_key.ts @@ -12,6 +12,10 @@ export class MinKey { get _bsontype(): 'MinKey' { return 'MinKey'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } /** @internal */ toExtendedJSON(): MinKeyExtended { diff --git a/src/objectid.ts b/src/objectid.ts index 6ec18a32..772e08e7 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -28,8 +28,12 @@ const kId = Symbol('id'); * @category BSONType */ export class ObjectId { - get _bsontype(): 'ObjectID' { - return 'ObjectID'; + get _bsontype(): 'ObjectId' { + return 'ObjectId'; + } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; } /** @internal */ diff --git a/src/parser/calculate_size.ts b/src/parser/calculate_size.ts index e96bf63e..73b6d898 100644 --- a/src/parser/calculate_size.ts +++ b/src/parser/calculate_size.ts @@ -79,7 +79,7 @@ function calculateElement( case 'object': if (value == null || value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1; - } else if (value['_bsontype'] === 'ObjectId' || value['_bsontype'] === 'ObjectID') { + } else if (value['_bsontype'] === 'ObjectId') { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1); } else if (value instanceof Date || isDate(value)) { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1); diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index b12bcedd..123983b3 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -684,13 +684,11 @@ export function serializeInto( index = serializeNull(buffer, key, value, index); } else if (value === null) { index = serializeNull(buffer, key, value, index); - } else if (value['_bsontype'] === 'ObjectId' || value['_bsontype'] === 'ObjectID') { - index = serializeObjectId(buffer, key, value, index); } else if (isUint8Array(value)) { index = serializeBuffer(buffer, key, value, index); } else if (value instanceof RegExp || isRegExp(value)) { index = serializeRegExp(buffer, key, value, index); - } else if (typeof value === 'object' && value['_bsontype'] == null) { + } else if (typeof value === 'object' && value._bsontype == null) { index = serializeObject( buffer, key, @@ -702,15 +700,19 @@ export function serializeInto( ignoreUndefined, path ); - } else if (value['_bsontype'] === 'Decimal128') { + } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) { + throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + } else if (value._bsontype === 'ObjectId') { + index = serializeObjectId(buffer, key, value, index); + } else if (value._bsontype === 'Decimal128') { index = serializeDecimal128(buffer, key, value, index); - } else if (value['_bsontype'] === 'Long' || value['_bsontype'] === 'Timestamp') { + } else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') { index = serializeLong(buffer, key, value, index); - } else if (value['_bsontype'] === 'Double') { + } else if (value._bsontype === 'Double') { index = serializeDouble(buffer, key, value, index); } else if (typeof value === 'function' && serializeFunctions) { index = serializeFunction(buffer, key, value, index); - } else if (value['_bsontype'] === 'Code') { + } else if (value._bsontype === 'Code') { index = serializeCode( buffer, key, @@ -722,20 +724,20 @@ export function serializeInto( ignoreUndefined, path ); - } else if (value['_bsontype'] === 'Binary') { + } else if (value._bsontype === 'Binary') { index = serializeBinary(buffer, key, value, index); - } else if (value['_bsontype'] === 'Symbol') { + } else if (value._bsontype === 'Symbol') { index = serializeSymbol(buffer, key, value, index); - } else if (value['_bsontype'] === 'DBRef') { + } else if (value._bsontype === 'DBRef') { index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path); - } else if (value['_bsontype'] === 'BSONRegExp') { + } else if (value._bsontype === 'BSONRegExp') { index = serializeBSONRegExp(buffer, key, value, index); - } else if (value['_bsontype'] === 'Int32') { + } else if (value._bsontype === 'Int32') { index = serializeInt32(buffer, key, value, index); - } else if (value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') { + } else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') { index = serializeMinMax(buffer, key, value, index); - } else if (typeof value['_bsontype'] !== 'undefined') { - throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value['_bsontype'])}`); + } else if (typeof value._bsontype !== 'undefined') { + throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`); } } } else if (object instanceof Map || isMap(object)) { @@ -785,13 +787,11 @@ export function serializeInto( index = serializeDate(buffer, key, value, index); } else if (value === null || (value === undefined && ignoreUndefined === false)) { index = serializeNull(buffer, key, value, index); - } else if (value['_bsontype'] === 'ObjectId' || value['_bsontype'] === 'ObjectID') { - index = serializeObjectId(buffer, key, value, index); } else if (isUint8Array(value)) { index = serializeBuffer(buffer, key, value, index); } else if (value instanceof RegExp || isRegExp(value)) { index = serializeRegExp(buffer, key, value, index); - } else if (type === 'object' && value['_bsontype'] == null) { + } else if (type === 'object' && value._bsontype == null) { index = serializeObject( buffer, key, @@ -803,13 +803,17 @@ export function serializeInto( ignoreUndefined, path ); - } else if (type === 'object' && value['_bsontype'] === 'Decimal128') { + } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) { + throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + } else if (value._bsontype === 'ObjectId') { + index = serializeObjectId(buffer, key, value, index); + } else if (type === 'object' && value._bsontype === 'Decimal128') { index = serializeDecimal128(buffer, key, value, index); - } else if (value['_bsontype'] === 'Long' || value['_bsontype'] === 'Timestamp') { + } else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') { index = serializeLong(buffer, key, value, index); - } else if (value['_bsontype'] === 'Double') { + } else if (value._bsontype === 'Double') { index = serializeDouble(buffer, key, value, index); - } else if (value['_bsontype'] === 'Code') { + } else if (value._bsontype === 'Code') { index = serializeCode( buffer, key, @@ -823,20 +827,20 @@ export function serializeInto( ); } else if (typeof value === 'function' && serializeFunctions) { index = serializeFunction(buffer, key, value, index); - } else if (value['_bsontype'] === 'Binary') { + } else if (value._bsontype === 'Binary') { index = serializeBinary(buffer, key, value, index); - } else if (value['_bsontype'] === 'Symbol') { + } else if (value._bsontype === 'Symbol') { index = serializeSymbol(buffer, key, value, index); - } else if (value['_bsontype'] === 'DBRef') { + } else if (value._bsontype === 'DBRef') { index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path); - } else if (value['_bsontype'] === 'BSONRegExp') { + } else if (value._bsontype === 'BSONRegExp') { index = serializeBSONRegExp(buffer, key, value, index); - } else if (value['_bsontype'] === 'Int32') { + } else if (value._bsontype === 'Int32') { index = serializeInt32(buffer, key, value, index); - } else if (value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') { + } else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') { index = serializeMinMax(buffer, key, value, index); - } else if (typeof value['_bsontype'] !== 'undefined') { - throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value['_bsontype'])}`); + } else if (typeof value._bsontype !== 'undefined') { + throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`); } } } else { @@ -890,13 +894,11 @@ export function serializeInto( if (ignoreUndefined === false) index = serializeNull(buffer, key, value, index); } else if (value === null) { index = serializeNull(buffer, key, value, index); - } else if (value['_bsontype'] === 'ObjectId' || value['_bsontype'] === 'ObjectID') { - index = serializeObjectId(buffer, key, value, index); } else if (isUint8Array(value)) { index = serializeBuffer(buffer, key, value, index); } else if (value instanceof RegExp || isRegExp(value)) { index = serializeRegExp(buffer, key, value, index); - } else if (type === 'object' && value['_bsontype'] == null) { + } else if (type === 'object' && value._bsontype == null) { index = serializeObject( buffer, key, @@ -908,13 +910,17 @@ export function serializeInto( ignoreUndefined, path ); - } else if (type === 'object' && value['_bsontype'] === 'Decimal128') { + } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) { + throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + } else if (value._bsontype === 'ObjectId') { + index = serializeObjectId(buffer, key, value, index); + } else if (type === 'object' && value._bsontype === 'Decimal128') { index = serializeDecimal128(buffer, key, value, index); - } else if (value['_bsontype'] === 'Long' || value['_bsontype'] === 'Timestamp') { + } else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') { index = serializeLong(buffer, key, value, index); - } else if (value['_bsontype'] === 'Double') { + } else if (value._bsontype === 'Double') { index = serializeDouble(buffer, key, value, index); - } else if (value['_bsontype'] === 'Code') { + } else if (value._bsontype === 'Code') { index = serializeCode( buffer, key, @@ -928,20 +934,20 @@ export function serializeInto( ); } else if (typeof value === 'function' && serializeFunctions) { index = serializeFunction(buffer, key, value, index); - } else if (value['_bsontype'] === 'Binary') { + } else if (value._bsontype === 'Binary') { index = serializeBinary(buffer, key, value, index); - } else if (value['_bsontype'] === 'Symbol') { + } else if (value._bsontype === 'Symbol') { index = serializeSymbol(buffer, key, value, index); - } else if (value['_bsontype'] === 'DBRef') { + } else if (value._bsontype === 'DBRef') { index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path); - } else if (value['_bsontype'] === 'BSONRegExp') { + } else if (value._bsontype === 'BSONRegExp') { index = serializeBSONRegExp(buffer, key, value, index); - } else if (value['_bsontype'] === 'Int32') { + } else if (value._bsontype === 'Int32') { index = serializeInt32(buffer, key, value, index); - } else if (value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') { + } else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') { index = serializeMinMax(buffer, key, value, index); - } else if (typeof value['_bsontype'] !== 'undefined') { - throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value['_bsontype'])}`); + } else if (typeof value._bsontype !== 'undefined') { + throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`); } } } diff --git a/src/regexp.ts b/src/regexp.ts index 5c9a475b..6996c16f 100644 --- a/src/regexp.ts +++ b/src/regexp.ts @@ -28,6 +28,10 @@ export class BSONRegExp { get _bsontype(): 'BSONRegExp' { return 'BSONRegExp'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } pattern!: string; options!: string; @@ -100,4 +104,13 @@ export class BSONRegExp { } throw new BSONTypeError(`Unexpected BSONRegExp EJSON object form: ${JSON.stringify(doc)}`); } + + /** @internal */ + [Symbol.for('nodejs.util.inspect.custom')](): string { + return this.inspect(); + } + + inspect(): string { + return `new BSONRegExp(${JSON.stringify(this.pattern)}, ${JSON.stringify(this.options)})`; + } } diff --git a/src/symbol.ts b/src/symbol.ts index a45554b4..feba31e5 100644 --- a/src/symbol.ts +++ b/src/symbol.ts @@ -9,8 +9,12 @@ export interface BSONSymbolExtended { * @category BSONType */ export class BSONSymbol { - get _bsontype(): 'Symbol' { - return 'Symbol'; + get _bsontype(): 'BSONSymbol' { + return 'BSONSymbol'; + } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; } value!: string; diff --git a/src/timestamp.ts b/src/timestamp.ts index 78d7fc4c..1a160828 100644 --- a/src/timestamp.ts +++ b/src/timestamp.ts @@ -32,6 +32,10 @@ export class Timestamp extends LongWithoutOverridesClass { get _bsontype(): 'Timestamp' { return 'Timestamp'; } + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): 5 { + return 5; + } static readonly MAX_VALUE = Long.MAX_UNSIGNED_VALUE; diff --git a/test/node/bson_older_versions_tests.js b/test/node/bson_older_versions_tests.js deleted file mode 100644 index 3f459269..00000000 --- a/test/node/bson_older_versions_tests.js +++ /dev/null @@ -1,254 +0,0 @@ -'use strict'; - -const currentNodeBSON = require('../register-bson'); -const vm = require('vm'); -const fs = require('fs'); -const rimraf = require('rimraf'); -const cp = require('child_process'); -const { __isWeb__ } = require('../register-bson'); - -// node-fetch is an es-module -let fetch; - -/* - * This file tests that previous versions of BSON - * serialize and deserialize correctly in the most recent version of BSON, - * and that the different distributions (browser, Node.js, etc.) of the - * most recent version are mutually compatible as well. - * - * This is an unusual situation to run into as users should be using one BSON lib version - * but it does arise with sub deps etc. and we wish to protect against unexpected behavior - * - * If backwards compatibility breaks there should be clear warnings/failures - * rather than empty or zero-ed values. - */ -const OLD_VERSIONS = ['v1.1.5', 'v1.1.4']; -const getZipUrl = ver => `https://github.com/mongodb/js-bson/archive/${ver}.zip`; -const getImportPath = ver => `../../bson-${ver}/js-bson-${ver.substring(1)}`; - -function downloadZip(version, done) { - // downloads a zip of previous BSON version - fetch(getZipUrl(version)) - .then(r => { - return r.arrayBuffer(); - }) - .then(r => { - fs.writeFileSync(`bson-${version}.zip`, new Uint8Array(r)); - try { - // unzips the code, right now these test won't handle versions written in TS - cp.execSync(`unzip bson-${version}.zip -d bson-${version}`); - } catch (err) { - return done(err); - } - done(); - }); -} - -// TODO(NODE-4843): These tests are failing not because of an error with BSON but the bundling changes -// Need to be accounted for. We can make the fixes in the ticket focused on this cross compat testing -describe.skip('Mutual version and distribution compatibility', function () { - before(function () { - if (__isWeb__) this.skip(); - }); - - before(async () => { - fetch = await import('node-fetch').then(mod => mod.default); - }); - - OLD_VERSIONS.forEach(version => { - before(function (done) { - this.timeout(30000); // Downloading may take a few seconds. - if (Number(process.version.split('.')[0].substring(1)) < 8) { - // WHATWG fetch doesn't download correctly prior to node 8 - // but we should be safe by testing on node 8 + - return done(); - } - if (fs.existsSync(`bson-${version}.zip`)) { - fs.unlinkSync(`bson-${version}.zip`); - rimraf(`./bson-${version}`, err => { - if (err) done(err); - - // download old versions - downloadZip(version, done); - }); - } else { - // download old versions - downloadZip(version, done); - } - }); - - after(function (done) { - try { - fs.unlinkSync(`bson-${version}.zip`); - } catch (e) { - // ignore - } - rimraf(`./bson-${version}`, err => { - if (err) done(err); - done(); - }); - }); - }); - - // Node.js requires an .mjs filename extension for loading ES modules. - before(() => { - try { - fs.writeFileSync( - './bson.browser.esm.mjs', - fs.readFileSync(__dirname + '/../../dist/bson.browser.esm.js') - ); - fs.writeFileSync('./bson.esm.mjs', fs.readFileSync(__dirname + '/../../dist/bson.esm.js')); - } catch (e) { - // bundling fails in CI on Windows, no idea why, hence also the - // process.platform !== 'win32' check below - } - }); - - after(() => { - try { - fs.unlinkSync('./bson.browser.esm.mjs'); - fs.unlinkSync('./bson.esm.mjs'); - } catch (e) { - // ignore - } - }); - - const variants = OLD_VERSIONS.map(version => ({ - name: `legacy ${version}`, - load: () => { - const bson = require(getImportPath(version)); - bson.serialize = bson.prototype.serialize; - bson.deserialize = bson.prototype.deserialize; - return Promise.resolve(bson); - }, - legacy: true - })).concat([ - { - name: 'Node.js lib/bson', - load: () => Promise.resolve(currentNodeBSON) - }, - { - name: 'Browser ESM', - // eval because import is a syntax error in earlier Node.js versions - // that are still supported in CI - load: () => eval(`import("${__dirname}/../../bson.browser.esm.mjs")`), - usesBufferPolyfill: true - }, - { - name: 'Browser UMD', - load: () => Promise.resolve(require('../../dist/bson.browser.umd.js')), - usesBufferPolyfill: true - }, - { - name: 'Generic bundle', - load: () => { - const source = fs.readFileSync(__dirname + '/../../dist/bson.bundle.js', 'utf8'); - return Promise.resolve(vm.runInNewContext(`${source}; BSON`, { global, process })); - }, - usesBufferPolyfill: true - }, - { - name: 'Node.js ESM', - load: () => eval(`import("${__dirname}/../../bson.esm.mjs")`) - } - ]); - - const makeObjects = bson => [ - new bson.ObjectId('5f16b8bebe434dc98cdfc9ca'), - new bson.DBRef('a', new bson.ObjectId('5f16b8bebe434dc98cdfc9cb'), 'db'), - new bson.MinKey(), - new bson.MaxKey(), - // TODO(NODE-4843): Timestamp has a necessary incompatibility, two number ctor throws now -- new bson.Timestamp(1, 100), - new bson.Timestamp(new bson.Long(1, 100, true)), - new bson.Code('abc'), - bson.Decimal128.fromString('1'), - bson.Long.fromString('1'), - new bson.Binary(Buffer.from('abcäbc🎉'), 128), - new Date('2021-05-04T15:49:33.000Z'), - /match/ - ]; - - for (const from of variants) { - for (const to of variants) { - describe(`serializing objects from ${from.name} using ${to.name}`, () => { - let fromObjects; - let fromBSON; - let toBSON; - - before(function () { - // Load the from/to BSON versions asynchronously because e.g. ESM - // requires asynchronous loading. - return Promise.resolve() - .then(() => { - return from.load(); - }) - .then(loaded => { - fromBSON = loaded; - return to.load(); - }) - .then(loaded => { - toBSON = loaded; - }) - .then( - () => { - if (from.usesBufferPolyfill || to.usesBufferPolyfill) { - // TODO(NODE-3555): The buffer polyfill does not correctly identify ArrayBuffers, will be fixed by removing - return this.skip(); - } - fromObjects = makeObjects(fromBSON); - }, - err => { - if (+process.version.slice(1).split('.')[0] >= 12) { - throw err; // On Node.js 12+, all loading is expected to work. - } else { - this.skip(); // Otherwise, e.g. ESM can't be loaded, so just skip. - } - } - ); - }); - - it('serializes in a compatible way', function () { - for (const object of fromObjects) { - // If the object in question uses Buffers in its serialization, and - // its Buffer was created using the polyfill, and we're serializing - // using a legacy version that uses buf.copy(), then that fails - // because the Buffer polyfill's typechecking is buggy, so we - // skip these cases. - // This is tracked as https://jira.mongodb.org/browse/NODE-2848 - // and would be addressed by https://github.com/feross/buffer/pull/285 - // if that is merged at some point. - if (from.usesBufferPolyfill || to.usesBufferPolyfill) { - // TODO(NODE-3555): The buffer polyfill does not correctly identify ArrayBuffers, will be fixed by removing - return this.skip(); - } - if ( - from.usesBufferPolyfill && - to.legacy && - ['ObjectId', 'Decimal128', 'DBRef', 'Binary'].includes(object.constructor.name) - ) { - continue; - } - - try { - // Check that both BSON versions serialize to equal Buffers - expect(toBSON.serialize({ object })).to.deep.equal(fromBSON.serialize({ object })); - if (!from.legacy) { - // Check that serializing using one version and deserializing using - // the other gives back the original object. - const cloned = fromBSON.deserialize(toBSON.serialize({ object })).object; - expect(fromBSON.EJSON.serialize(cloned)).to.deep.equal( - fromBSON.EJSON.serialize(object) - ); - } - } catch (err) { - // If something fails, note the object type in the error message - // for easier debugging. - err.message += ` (${object.constructor.name})`; - throw err; - } - } - }); - }); - } - } -}); diff --git a/test/node/bson_test.js b/test/node/bson_test.js index b71c4d6e..c3423045 100644 --- a/test/node/bson_test.js +++ b/test/node/bson_test.js @@ -1806,85 +1806,6 @@ describe('BSON', function () { done(); }); - it('should serialize ObjectIds from old bson versions', function () { - // In versions 4.0.0 and 4.0.1, we used _bsontype="ObjectId" which broke - // backwards compatibility with mongodb-core and other code. It was reverted - // back to "ObjectID" (capital D) in later library versions. - // The test below ensures that all three versions of Object ID work OK: - // 1. The current version's class - // 2. A simulation of the class from library 4.0.0 - // 3. The class currently in use by mongodb (not tested in browser where mongodb is unavailable) - - // test the old ObjectID class (in mongodb-core 3.1) because MongoDB drivers still return it - function getOldBSON() { - try { - // do a dynamic resolve to avoid exception when running browser tests - const file = require.resolve('mongodb-core'); - const oldModule = require(file).BSON; - const funcs = new oldModule.BSON(); - oldModule.serialize = funcs.serialize; - oldModule.deserialize = funcs.deserialize; - return oldModule; - } catch (e) { - return BSON; // if mongo is unavailable, e.g. browser tests, just re-use new BSON - } - } - - const OldBSON = getOldBSON(); - const OldObjectID = OldBSON === BSON ? BSON.ObjectId : OldBSON.ObjectID; - - // create a wrapper simulating the old ObjectId class from v4.0.0 - class ObjectIdv400 { - constructor() { - this.oid = new ObjectId(); - } - get id() { - return this.oid.id; - } - toString() { - return this.oid.toString(); - } - } - Object.defineProperty(ObjectIdv400.prototype, '_bsontype', { value: 'ObjectId' }); - - // Array - const array = [new ObjectIdv400(), new OldObjectID(), new ObjectId()]; - const deserializedArrayAsMap = BSON.deserialize( - BSON.serialize(Object.fromEntries(array.entries())) - ); - const deserializedArray = Object.keys(deserializedArrayAsMap).map( - x => deserializedArrayAsMap[x] - ); - expect(deserializedArray.map(x => x.toString())).to.eql(array.map(x => x.toString())); - - // Map - const map = new Map(); - map.set('oldBsonType', new ObjectIdv400()); - map.set('reallyOldBsonType', new OldObjectID()); - map.set('newBsonType', new ObjectId()); - const deserializedMapAsObject = BSON.deserialize(BSON.serialize(map), { relaxed: false }); - const deserializedMap = new Map( - Object.keys(deserializedMapAsObject).map(k => [k, deserializedMapAsObject[k]]) - ); - - map.forEach((value, key) => { - expect(deserializedMap.has(key)).to.be.true; - const deserializedMapValue = deserializedMap.get(key); - expect(deserializedMapValue.toString()).to.equal(value.toString()); - }); - - // Object - const record = { - oldBsonType: new ObjectIdv400(), - reallyOldBsonType: new OldObjectID(), - newBsonType: new ObjectId() - }; - const deserializedObject = BSON.deserialize(BSON.serialize(record)); - expect(deserializedObject).to.have.keys(['oldBsonType', 'reallyOldBsonType', 'newBsonType']); - expect(record.oldBsonType.toString()).to.equal(deserializedObject.oldBsonType.toString()); - expect(record.newBsonType.toString()).to.equal(deserializedObject.newBsonType.toString()); - }); - it('should throw if invalid BSON types are input to BSON serializer', function () { const oid = new ObjectId('111111111111111111111111'); const badBsonType = Object.assign({}, oid, { _bsontype: 'bogus' }); diff --git a/test/node/bson_type_classes.test.ts b/test/node/bson_type_classes.test.ts new file mode 100644 index 00000000..ffe8af4f --- /dev/null +++ b/test/node/bson_type_classes.test.ts @@ -0,0 +1,68 @@ +import { expect } from 'chai'; +import { + Binary, + BSONRegExp, + BSONSymbol, + Code, + DBRef, + Decimal128, + Double, + Int32, + Long, + MaxKey, + MinKey, + ObjectId, + Timestamp, + UUID +} from '../register-bson'; + +const BSONTypeClasses = [ + Binary, + Code, + DBRef, + Decimal128, + Double, + Int32, + Long, + MinKey, + MaxKey, + ObjectId, + BSONRegExp, + BSONSymbol, + Timestamp, + UUID +]; + +describe('BSON Type classes common interfaces', () => { + for (const TypeClass of BSONTypeClasses) { + describe(TypeClass.name, () => { + if (TypeClass.name !== 'UUID') { + it(`defines a _bsontype property equal to its name`, () => + expect(TypeClass.prototype).to.have.property('_bsontype', TypeClass.name)); + } else { + it(`UUID inherits _bsontype from Binary`, () => + expect(Object.getPrototypeOf(TypeClass.prototype)).to.have.property( + '_bsontype', + 'Binary' + )); + } + + it(`defines a Symbol.for('@@mdb.bson.version') property equal to 5`, () => + expect(TypeClass.prototype).to.have.property(Symbol.for('@@mdb.bson.version'), 5)); + + it(`defines a static fromExtendedJSON() method`, () => + expect(TypeClass).to.have.property('fromExtendedJSON').that.is.a('function')); + + it(`defines a toExtendedJSON() method`, () => + expect(TypeClass.prototype).to.have.property('toExtendedJSON').that.is.a('function')); + + it(`defines an inspect() method`, () => + expect(TypeClass.prototype).to.have.property('inspect').that.is.a('function')); + + it(`defines a [Symbol.for('nodejs.util.inspect.custom')]() method`, () => + expect(TypeClass.prototype) + .to.have.property(Symbol.for('nodejs.util.inspect.custom')) + .that.is.a('function')); + }); + } +}); diff --git a/test/node/cross_compat.test.ts b/test/node/cross_compat.test.ts new file mode 100644 index 00000000..88d9c103 --- /dev/null +++ b/test/node/cross_compat.test.ts @@ -0,0 +1,83 @@ +import { expect } from 'chai'; +import { + BSON, + EJSON, + Binary, + BSONRegExp, + BSONSymbol, + Code, + DBRef, + Decimal128, + Double, + Int32, + Long, + MaxKey, + MinKey, + ObjectId, + Timestamp, + UUID +} from '../register-bson'; + +const BSONTypeClasses = { + Binary: () => { + return new Binary(Buffer.alloc(3)); + }, + Code: () => { + return new Code('function () {}'); + }, + DBRef: () => { + return new DBRef('test', new ObjectId('00'.repeat(12))); + }, + Decimal128: () => { + return new Decimal128('1.28'); + }, + Double: () => { + return new Double(1.28); + }, + Int32: () => { + return new Int32(1); + }, + Long: () => { + return Long.fromNumber(1); + }, + MinKey: () => { + return new MinKey(); + }, + MaxKey: () => { + return new MaxKey(); + }, + ObjectId: () => { + return new ObjectId('00'.repeat(12)); + }, + BSONRegExp: () => { + return new BSONRegExp('abc', 'i'); + }, + BSONSymbol: () => { + return new BSONSymbol('abc'); + }, + Timestamp: () => { + return new Timestamp({ i: 0, t: 1 }); + }, + UUID: () => { + return new UUID('74e65f2f-6fdb-4c56-8785-bddb8ad79ea2'); + } +}; + +describe('Prevent previous major versions from working with BSON v5 serialize and stringify', function () { + for (const [typeName, typeMaker] of Object.entries(BSONTypeClasses)) { + it(`serialize throws if ${typeName} is missing a version symbol`, () => { + const type = typeMaker(); + Object.defineProperty(type, Symbol.for('@@mdb.bson.version'), { value: null }); // set an own property that overrides the getter + expect(() => BSON.serialize({ type })).to.throw(/Unsupported BSON version/); + expect(() => BSON.serialize({ a: [type] })).to.throw(/Unsupported BSON version/); + expect(() => BSON.serialize(new Map([['type', type]]))).to.throw(/Unsupported BSON version/); + }); + + it(`stringify throws if ${typeName} is missing a version symbol`, () => { + const type = typeMaker(); + Object.defineProperty(type, Symbol.for('@@mdb.bson.version'), { value: null }); // set an own property that overrides the getter + expect(() => EJSON.stringify({ type })).to.throw(/Unsupported BSON version/); + expect(() => EJSON.stringify({ a: [type] })).to.throw(/Unsupported BSON version/); + }); + } +}); diff --git a/test/node/extended_json.test.ts b/test/node/extended_json.test.ts index 4a372088..89cc8881 100644 --- a/test/node/extended_json.test.ts +++ b/test/node/extended_json.test.ts @@ -14,36 +14,11 @@ const Int32 = BSON.Int32; const Long = BSON.Long; const MaxKey = BSON.MaxKey; const MinKey = BSON.MinKey; -const ObjectID = BSON.ObjectId; const ObjectId = BSON.ObjectId; const BSONRegExp = BSON.BSONRegExp; const BSONSymbol = BSON.BSONSymbol; const Timestamp = BSON.Timestamp; -// Several tests in this file can test interop between current library versions and library version 1.1.0, because -// between 1.1.0 and 4.0.0 there was a significant rewrite. To minimize maintenance issues of a hard dependency on -// the old version, these interop tests are inactive by default. To activate, edit the check:test script in package.json: -// "check:test": "npm i --no-save --force bson@1.1.0 && mocha ./test/node && npm uninstall --no-save --force bson@1.1.0" -// -function getOldBSON() { - try { - // do a dynamic resolve to avoid exception when running browser tests - const file = require.resolve('bson'); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const oldModule = require(file).BSON; - const funcs = new oldModule.BSON(); - oldModule.serialize = funcs.serialize; - oldModule.deserialize = funcs.deserialize; - return oldModule; - } catch (e) { - return BSON; // if old bson lib is unavailable, e.g. browser tests, just re-use new BSON - } -} - -const OldBSON = getOldBSON(); -const OldObjectID = OldBSON === BSON ? BSON.ObjectId : OldBSON.ObjectID; -const usingOldBSON = OldBSON !== BSON; - describe('Extended JSON', function () { let doc = {}; @@ -66,8 +41,6 @@ describe('Extended JSON', function () { maxKey: new MaxKey(), minKey: new MinKey(), objectId: ObjectId.createFromHexString('111111111111111111111111'), - objectID: ObjectID.createFromHexString('111111111111111111111111'), - oldObjectID: OldObjectID.createFromHexString('111111111111111111111111'), regexp: new BSONRegExp('hello world', 'i'), symbol: new BSONSymbol('symbol'), timestamp: Timestamp.fromNumber(1000), @@ -81,7 +54,7 @@ describe('Extended JSON', function () { it('should correctly extend an existing mongodb module', function () { // TODO(NODE-4377): doubleNumberIntFit should be a double not a $numberLong const json = - '{"_id":{"$numberInt":"100"},"gh":{"$numberInt":"1"},"binary":{"$binary":{"base64":"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==","subType":"00"}},"date":{"$date":{"$numberLong":"1488372056737"}},"code":{"$code":"function() {}","$scope":{"a":{"$numberInt":"1"}}},"dbRef":{"$ref":"tests","$id":{"$numberInt":"1"},"$db":"test"},"decimal":{"$numberDecimal":"100"},"double":{"$numberDouble":"10.1"},"int32":{"$numberInt":"10"},"long":{"$numberLong":"200"},"maxKey":{"$maxKey":1},"minKey":{"$minKey":1},"objectId":{"$oid":"111111111111111111111111"},"objectID":{"$oid":"111111111111111111111111"},"oldObjectID":{"$oid":"111111111111111111111111"},"regexp":{"$regularExpression":{"pattern":"hello world","options":"i"}},"symbol":{"$symbol":"symbol"},"timestamp":{"$timestamp":{"t":0,"i":1000}},"int32Number":{"$numberInt":"300"},"doubleNumber":{"$numberDouble":"200.2"},"longNumberIntFit":{"$numberLong":"7036874417766400"},"doubleNumberIntFit":{"$numberLong":"19007199250000000"}}'; + '{"_id":{"$numberInt":"100"},"gh":{"$numberInt":"1"},"binary":{"$binary":{"base64":"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==","subType":"00"}},"date":{"$date":{"$numberLong":"1488372056737"}},"code":{"$code":"function() {}","$scope":{"a":{"$numberInt":"1"}}},"dbRef":{"$ref":"tests","$id":{"$numberInt":"1"},"$db":"test"},"decimal":{"$numberDecimal":"100"},"double":{"$numberDouble":"10.1"},"int32":{"$numberInt":"10"},"long":{"$numberLong":"200"},"maxKey":{"$maxKey":1},"minKey":{"$minKey":1},"objectId":{"$oid":"111111111111111111111111"},"regexp":{"$regularExpression":{"pattern":"hello world","options":"i"}},"symbol":{"$symbol":"symbol"},"timestamp":{"$timestamp":{"t":0,"i":1000}},"int32Number":{"$numberInt":"300"},"doubleNumber":{"$numberDouble":"200.2"},"longNumberIntFit":{"$numberLong":"7036874417766400"},"doubleNumberIntFit":{"$numberLong":"19007199250000000"}}'; expect(json).to.equal(EJSON.stringify(doc, null, 0, { relaxed: false })); }); @@ -128,10 +101,6 @@ describe('Extended JSON', function () { it('should correctly serialize bson types when they are values', function () { let serialized = EJSON.stringify(new ObjectId('591801a468f9e7024b6235ea'), { relaxed: false }); expect(serialized).to.equal('{"$oid":"591801a468f9e7024b6235ea"}'); - serialized = EJSON.stringify(new ObjectID('591801a468f9e7024b6235ea'), { relaxed: false }); - expect(serialized).to.equal('{"$oid":"591801a468f9e7024b6235ea"}'); - serialized = EJSON.stringify(new OldObjectID('591801a468f9e7024b6235ea'), { relaxed: false }); - expect(serialized).to.equal('{"$oid":"591801a468f9e7024b6235ea"}'); serialized = EJSON.stringify(new Int32(42), { relaxed: false }); expect(serialized).to.equal('{"$numberInt":"42"}'); @@ -142,20 +111,6 @@ describe('Extended JSON', function () { { relaxed: false } ); expect(serialized).to.equal('{"_id":{"$nin":[{"$oid":"591801a468f9e7024b6235ea"}]}}'); - serialized = EJSON.stringify( - { - _id: { $nin: [new ObjectID('591801a468f9e7024b6235ea')] } - }, - { relaxed: false } - ); - expect(serialized).to.equal('{"_id":{"$nin":[{"$oid":"591801a468f9e7024b6235ea"}]}}'); - serialized = EJSON.stringify( - { - _id: { $nin: [new OldObjectID('591801a468f9e7024b6235ea')] } - }, - { relaxed: false } - ); - expect(serialized).to.equal('{"_id":{"$nin":[{"$oid":"591801a468f9e7024b6235ea"}]}}'); serialized = EJSON.stringify(new Binary(new Uint8Array([1, 2, 3, 4, 5])), { relaxed: false }); expect(serialized).to.equal('{"$binary":{"base64":"AQIDBAU=","subType":"00"}}'); @@ -237,8 +192,6 @@ describe('Extended JSON', function () { maxKey: new MaxKey(), minKey: new MinKey(), objectId: ObjectId.createFromHexString('111111111111111111111111'), - objectID: ObjectID.createFromHexString('111111111111111111111111'), - oldObjectID: OldObjectID.createFromHexString('111111111111111111111111'), bsonRegExp: new BSONRegExp('hello world', 'i'), symbol: new BSONSymbol('symbol'), timestamp: new Timestamp(), @@ -258,8 +211,6 @@ describe('Extended JSON', function () { maxKey: { $maxKey: 1 }, minKey: { $minKey: 1 }, objectId: { $oid: '111111111111111111111111' }, - objectID: { $oid: '111111111111111111111111' }, - oldObjectID: { $oid: '111111111111111111111111' }, bsonRegExp: { $regularExpression: { pattern: 'hello world', options: 'i' } }, symbol: { $symbol: 'symbol' }, timestamp: { $timestamp: { t: 0, i: 0 } }, @@ -280,8 +231,6 @@ describe('Extended JSON', function () { maxKey: { $maxKey: 1 }, minKey: { $minKey: 1 }, objectId: { $oid: '111111111111111111111111' }, - objectID: { $oid: '111111111111111111111111' }, - oldObjectID: { $oid: '111111111111111111111111' }, bsonRegExp: { $regularExpression: { pattern: 'hello world', options: 'i' } }, symbol: { $symbol: 'symbol' }, timestamp: { $timestamp: { t: 0, i: 0 } } @@ -314,8 +263,6 @@ describe('Extended JSON', function () { expect(result.minKey).to.be.an.instanceOf(BSON.MinKey); // objectID expect(result.objectId.toString()).to.equal('111111111111111111111111'); - expect(result.objectID.toString()).to.equal('111111111111111111111111'); - expect(result.oldObjectID.toString()).to.equal('111111111111111111111111'); //bsonRegExp expect(result.bsonRegExp).to.be.an.instanceOf(BSON.BSONRegExp); expect(result.bsonRegExp.pattern).to.equal('hello world'); @@ -352,159 +299,6 @@ describe('Extended JSON', function () { expect(serialized).to.equal('{"a":10}'); }); - if (!usingOldBSON) { - it.skip('skipping 4.x/1.x interop tests', () => { - // ignore - }); - } else { - it('should interoperate 4.x with 1.x versions of this library', function () { - const buffer = Buffer.alloc(64); - for (let i = 0; i < buffer.length; i++) { - buffer[i] = i; - } - const [oldBsonObject, newBsonObject] = [OldBSON, BSON].map(bsonModule => { - const bsonTypes = { - binary: new bsonModule.Binary(buffer), - code: new bsonModule.Code('function() {}'), - dbRef: new bsonModule.DBRef('tests', new Int32(1), 'test'), - decimal128: bsonModule.Decimal128.fromString('9991223372036854775807'), - double: new bsonModule.Double(10.1), - int32: new bsonModule.Int32(10), - long: bsonModule.Long.fromString('1223372036854775807'), - maxKey: new bsonModule.MaxKey(), - // minKey: new bsonModule.MinKey(), // broken until #310 is fixed in 1.x - objectId: bsonModule.ObjectId.createFromHexString('111111111111111111111111'), - objectID: bsonModule.ObjectId.createFromHexString('111111111111111111111111'), - bsonRegExp: new bsonModule.BSONRegExp('hello world', 'i'), - symbol: bsonModule.BSONSymbol - ? new bsonModule.BSONSymbol('symbol') - : new bsonModule.Symbol('symbol'), - timestamp: new bsonModule.Timestamp() - }; - return bsonTypes; - }); - - const serializationOptions = {}; - const bsonBuffers = { - oldObjectOldSerializer: OldBSON.serialize(oldBsonObject, serializationOptions), - oldObjectNewSerializer: BSON.serialize(oldBsonObject, serializationOptions), - newObjectOldSerializer: OldBSON.serialize(newBsonObject, serializationOptions), - newObjectNewSerializer: BSON.serialize(newBsonObject, serializationOptions) - }; - - const expectedBufferBase64 = - 'VgEAAAViaW5hcnkAQAAAAAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/DWNvZGUADgAAAGZ1bmN0aW9uKCkge30AA2RiUmVmACwAAAACJHJlZgAGAAAAdGVzdHMAECRpZAABAAAAAiRkYgAFAAAAdGVzdAAAE2RlY2ltYWwxMjgA//837RjxE6AdAgAAAABAMAFkb3VibGUAMzMzMzMzJEAQaW50MzIACgAAABJsb25nAP//38RiSvoQf21heEtleQAHb2JqZWN0SWQAERERERERERERERERB29iamVjdElEABEREREREREREREREQtic29uUmVnRXhwAGhlbGxvIHdvcmxkAGkADnN5bWJvbAAHAAAAc3ltYm9sABF0aW1lc3RhbXAAAAAAAAAAAAAA'; - const expectedBuffer = Buffer.from(expectedBufferBase64, 'base64'); - - // Regardless of which library version created the objects, and which library version - // is being used to serialize the objects, validate that the correct BSON is returned. - expect(expectedBuffer).to.deep.equal(bsonBuffers.newObjectNewSerializer); - expect(expectedBuffer).to.deep.equal(bsonBuffers.newObjectOldSerializer); - expect(expectedBuffer).to.deep.equal(bsonBuffers.oldObjectNewSerializer); - expect(expectedBuffer).to.deep.equal(bsonBuffers.oldObjectOldSerializer); - - // Finally, validate that the BSON buffer above is correctly deserialized back to EJSON by the new library, - // regardless of which library version's deserializer is used. This is useful because the 1.x deserializer - // generates 1.x objects, while the 4.x serializer generates 4.x objects. The 4.x EJSON serializer should - // be able to handle both. - const deserializationOptions = { promoteValues: false }; - const deserialized = { - usingOldDeserializer: OldBSON.deserialize(expectedBuffer, deserializationOptions), - usingNewDeserializer: BSON.deserialize(expectedBuffer, deserializationOptions) - }; - // Apparently the Symbol BSON type was deprecated in V4. Symbols in BSON are deserialized as strings in V4 - // Therefore, for this type we know there will be a difference between the V1 library and the V4 library, - // so remove Symbol from the list of BSON types that are being compared. - // Browser tests currently don't handle BSON Symbol correctly, so only test this under Node where OldBSON !=== BSON module. - if (BSON !== OldBSON) { - expect(deserialized.usingOldDeserializer['symbol'].value).to.equal( - deserialized.usingNewDeserializer['symbol'] - ); - } - delete deserialized.usingOldDeserializer['symbol']; - delete deserialized.usingNewDeserializer['symbol']; - - const ejsonExpected = { - binary: { - $binary: { - base64: - 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==', - subType: '00' - } - }, - code: { $code: 'function() {}' }, - dbRef: { $ref: 'tests', $id: { $numberInt: '1' }, $db: 'test' }, - decimal128: { $numberDecimal: '9991223372036854775807' }, - double: { $numberDouble: '10.1' }, - int32: { $numberInt: '10' }, - long: { $numberLong: '1223372036854775807' }, - maxKey: { $maxKey: 1 }, - // minKey: { $minKey: 1 }, // broken until #310 is fixed in 1.x branch - objectId: { $oid: '111111111111111111111111' }, - objectID: { $oid: '111111111111111111111111' }, - bsonRegExp: { $regularExpression: { pattern: 'hello world', options: 'i' } }, - // symbol: { $symbol: 'symbol' }, // removed because this type is deprecated. See comment above. - timestamp: { $timestamp: { t: 0, i: 0 } } - }; - const ejsonSerializeOptions = { relaxed: false }; - const resultOld = EJSON.serialize(deserialized.usingOldDeserializer, ejsonSerializeOptions); - expect(resultOld).to.deep.equal(ejsonExpected); - const resultNew = EJSON.serialize(deserialized.usingNewDeserializer, ejsonSerializeOptions); - expect(resultNew).to.deep.equal(ejsonExpected); - }); - - // Must special-case the test for MinKey, because of #310. When #310 is fixed and is picked up - // by mongodb-core, then remove this test case and uncomment the MinKey checks in the test case above - it('should interop with MinKey 1.x and 4.x, except the case that #310 breaks', function () { - if (!usingOldBSON) { - it.skip('interop tests', () => { - // ignore - }); - return; - } - - const serializationOptions = {}; - const deserializationOptions = { promoteValues: false }; - - // when #310 is fixed and the fix makes it into mongodb-core. - const [oldMinKey, newMinKey] = [OldBSON, BSON].map(bsonModule => { - const bsonTypes = { - minKey: new bsonModule.MinKey() - }; - return bsonTypes; - }); - - const expectedBufferBase64MinKey = 'DQAAAP9taW5LZXkAAA=='; - const expectedBufferMinKey = Buffer.from(expectedBufferBase64MinKey, 'base64'); - - const bsonBuffersMinKey = { - oldObjectOldSerializer: OldBSON.serialize(oldMinKey, serializationOptions), - oldObjectNewSerializer: BSON.serialize(oldMinKey, serializationOptions), - newObjectOldSerializer: OldBSON.serialize(newMinKey, serializationOptions), - newObjectNewSerializer: BSON.serialize(newMinKey, serializationOptions) - }; - - expect(expectedBufferMinKey).to.deep.equal(bsonBuffersMinKey.newObjectNewSerializer); - // expect(expectedBufferMinKey).to.deep.equal(bsonBuffersMinKey.newObjectOldSerializer); // this is the case that's broken by #310 - expect(expectedBufferMinKey).to.deep.equal(bsonBuffersMinKey.oldObjectNewSerializer); - expect(expectedBufferMinKey).to.deep.equal(bsonBuffersMinKey.oldObjectOldSerializer); - - const ejsonExpected = { - minKey: { $minKey: 1 } - }; - - const deserialized = { - usingOldDeserializer: OldBSON.deserialize(expectedBufferMinKey, deserializationOptions), - usingNewDeserializer: BSON.deserialize(expectedBufferMinKey, deserializationOptions) - }; - const ejsonSerializeOptions = { relaxed: false }; - const resultOld = EJSON.serialize(deserialized.usingOldDeserializer, ejsonSerializeOptions); - expect(resultOld).to.deep.equal(ejsonExpected); - const resultNew = EJSON.serialize(deserialized.usingNewDeserializer, ejsonSerializeOptions); - expect(resultNew).to.deep.equal(ejsonExpected); - }); - } - it('should throw if invalid BSON types are input to EJSON serializer', function () { const oid = new ObjectId('111111111111111111111111'); const badBsonType = Object.assign({}, oid, { _bsontype: 'bogus' }); diff --git a/test/node/parser/serializer.test.ts b/test/node/parser/serializer.test.ts index 352fc538..26cfb282 100644 --- a/test/node/parser/serializer.test.ts +++ b/test/node/parser/serializer.test.ts @@ -44,9 +44,11 @@ describe('serialize()', () => { it('does not permit objects with a _bsontype string to be serialized at the root', () => { expect(() => BSON.serialize({ _bsontype: 'iLoveJavascript' })).to.throw(/BSON types cannot/); // a nested invalid _bsontype throws something different - expect(() => BSON.serialize({ a: { _bsontype: 'iLoveJavascript' } })).to.throw( - /invalid _bsontype/ - ); + expect(() => + BSON.serialize({ + a: { _bsontype: 'iLoveJavascript', [Symbol.for('@@mdb.bson.version')]: 5 } + }) + ).to.throw(/invalid _bsontype/); }); it('does permit objects with a _bsontype prop that is not a string', () => { diff --git a/test/node/type_identifier_tests.js b/test/node/type_identifier_tests.js index f23308d1..542bb921 100644 --- a/test/node/type_identifier_tests.js +++ b/test/node/type_identifier_tests.js @@ -18,12 +18,11 @@ const { } = require('../register-bson'); describe('_bsontype identifier', () => { - // The two out of the norm types: - it('should be equal to ObjectID for ObjectId', () => { - expect(ObjectId.prototype._bsontype).to.equal('ObjectID'); + it('should be equal to ObjectId for ObjectId', () => { + expect(ObjectId.prototype._bsontype).to.equal('ObjectId'); }); - it('should be equal to Symbol for BSONSymbol', () => { - expect(BSONSymbol.prototype._bsontype).to.equal('Symbol'); + it('should be equal to BSONSymbol for BSONSymbol', () => { + expect(BSONSymbol.prototype._bsontype).to.equal('BSONSymbol'); }); it('should be equal to Timestamp for Timestamp', () => { // TODO(NODE-2624): Make Timestamp hold its long value on a property rather than be a subclass diff --git a/test/types/bson.test-d.ts b/test/types/bson.test-d.ts index efa3e66d..448a97af 100644 --- a/test/types/bson.test-d.ts +++ b/test/types/bson.test-d.ts @@ -51,18 +51,14 @@ expectError(MinKey.prototype.toJSON); expectError(Long.prototype.toJSON); expectError(BSONRegExp.prototype.toJSON); -// ObjectID uses a capital "D", this does not relate to the class name, or export name, only the determination for serialization -expectType<'ObjectID'>(ObjectId.prototype._bsontype) -// BSONSymbol was renamed to not conflict with the global JS Symbol -// but its _bsontype is still 'Symbol' -expectType<'Symbol'>(BSONSymbol.prototype._bsontype) - // We hack TS to say that the prototype has _bsontype='Timestamp' // but it actually is _bsontype='Long', inside the Timestamp constructor // we override the property on the instance // TODO(NODE-2624): Make Timestamp hold its long value on a property rather than be a subclass expectType<'Timestamp'>(Timestamp.prototype._bsontype) +expectType<'ObjectId'>(ObjectId.prototype._bsontype) +expectType<'BSONSymbol'>(BSONSymbol.prototype._bsontype) expectType<'Binary'>(Binary.prototype._bsontype) expectType<'Code'>(Code.prototype._bsontype) expectType<'DBRef'>(DBRef.prototype._bsontype) From c0ce081051a335bec9fa65624b173e8a07fb408f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 19 Dec 2022 11:10:52 -0500 Subject: [PATCH 02/21] chore(release): 5.0.0-alpha.0 --- HISTORY.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b9d3c08b..19db1911 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,52 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [5.0.0-alpha.0](https://github.com/mongodb/js-bson/compare/v4.7.0...v5.0.0-alpha.0) (2022-12-16) + + +### ⚠ BREAKING CHANGES + +* **NODE-4892:** error on bson types not from this version +* **NODE-4713:** modernize bundling (#534) +* **NODE-1921:** validate serializer root input (#537) +* **NODE-4711:** remove evalFunctions option (#539) +* **NODE-4706:** validate Timestamp ctor argument (#536) +* **NODE-4710:** remove capital D ObjectID export (#528) +* **NODE-4862:** add BSONType enum and remove internal constants from export (#532) +* **NODE-4410:** only enumerate own properties (#527) +* **NODE-4850:** serialize negative zero to double (#529) +* **NODE-4704:** remove deprecated ObjectId methods (#525) +* **NODE-4461:** remove Decimal128 toObject transformer (#526) +* **NODE-4712:** remove unused Map polyfill (#523) +* **NODE-4440:** bump TS target version to es2020 (#520) +* **NODE-4802:** Refactor BSON to work with cross platform JS APIs (#518) +* **NODE-4435:** drop support for nodejs versions below 14 (#517) + +### Features + +* **NODE-4892:** error on bson types not from this version ([c95f4c1](https://github.com/mongodb/js-bson/commit/c95f4c11fbdfe7d55cb19198d68a17327d6d260d)) +* **NODE-4713:** modernize bundling ([#534](https://github.com/mongodb/js-bson/issues/534)) ([28ce4d5](https://github.com/mongodb/js-bson/commit/28ce4d584ce399d99c41c5f5fd022b6e16f7b913)) +* **NODE-1921:** validate serializer root input ([#537](https://github.com/mongodb/js-bson/issues/537)) ([95d5edf](https://github.com/mongodb/js-bson/commit/95d5edf5c4f8a1cd6c23deaac3a446959fde779e)) +* **NODE-4711:** remove evalFunctions option ([#539](https://github.com/mongodb/js-bson/issues/539)) ([0427eb5](https://github.com/mongodb/js-bson/commit/0427eb588073a07d9e88ddf6155d2c1891ae5ad1)) +* **NODE-4706:** validate Timestamp ctor argument ([#536](https://github.com/mongodb/js-bson/issues/536)) ([f90bcc3](https://github.com/mongodb/js-bson/commit/f90bcc3)) +* **NODE-4710:** remove capital D ObjectID export ([#528](https://github.com/mongodb/js-bson/issues/528)) ([8511225](https://github.com/mongodb/js-bson/commit/8511225)) +* **NODE-4862:** add BSONType enum and remove internal constants from export ([#532](https://github.com/mongodb/js-bson/issues/532)) ([196f9f8](https://github.com/mongodb/js-bson/commit/196f9f8)) +* **NODE-4410:** only enumerate own properties ([#527](https://github.com/mongodb/js-bson/issues/527)) ([5103e4d](https://github.com/mongodb/js-bson/commit/5103e4d)) +* **NODE-4850:** serialize negative zero to double ([#529](https://github.com/mongodb/js-bson/issues/529)) ([be74b30](https://github.com/mongodb/js-bson/commit/be74b30)) +* **NODE-4704:** remove deprecated ObjectId methods ([#525](https://github.com/mongodb/js-bson/issues/525)) ([f1cccf2](https://github.com/mongodb/js-bson/commit/f1cccf2)) +* **NODE-4461:** remove Decimal128 toObject transformer ([#526](https://github.com/mongodb/js-bson/issues/526)) ([14a7473](https://github.com/mongodb/js-bson/commit/14a7473)) +* **NODE-4712:** remove unused Map polyfill ([#523](https://github.com/mongodb/js-bson/issues/523)) ([1fb6dc6](https://github.com/mongodb/js-bson/commit/1fb6dc6)) +* **NODE-4440:** bump TS target version to es2020 ([#520](https://github.com/mongodb/js-bson/issues/520)) ([491d8b7](https://github.com/mongodb/js-bson/commit/491d8b7)) +* **NODE-4802:** Refactor BSON to work with cross platform JS APIs ([#518](https://github.com/mongodb/js-bson/issues/518)) ([3d3d0dc](https://github.com/mongodb/js-bson/commit/3d3d0dc)) +* **NODE-4435:** drop support for nodejs versions below 14 ([#517](https://github.com/mongodb/js-bson/issues/517)) ([027ffb7](https://github.com/mongodb/js-bson/commit/027ffb7)) + + +### Bug Fixes + +* **NODE-4464:** stringify and parse negative zero to and from $numberDouble: -0.0 ([#531](https://github.com/mongodb/js-bson/issues/531)) ([a469e91](https://github.com/mongodb/js-bson/commit/a469e91)) +* **NODE-4771:** serializeFunctions breaks function names outside of basic latin ([#538](https://github.com/mongodb/js-bson/issues/538)) ([35a9234](https://github.com/mongodb/js-bson/commit/35a92341c0860fb41cbd5761250c565154ce1353)) + + ## [4.7.0](https://github.com/mongodb/js-bson/compare/v4.6.5...v4.7.0) (2022-08-18) diff --git a/package-lock.json b/package-lock.json index 27ccbd3a..63d1d428 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bson", - "version": "4.7.0", + "version": "5.0.0-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bson", - "version": "4.7.0", + "version": "5.0.0-alpha.0", "license": "Apache-2.0", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 7a861a9e..f324bda2 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "etc/prepare.js" ], "types": "bson.d.ts", - "version": "4.7.0", + "version": "5.0.0-alpha.0", "author": { "name": "The MongoDB NodeJS Team", "email": "dbx-node@mongodb.com" From 5b38dd84027ac072c67fbaee6cf08e2a44be2980 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 19 Dec 2022 12:50:01 -0500 Subject: [PATCH 03/21] fix: BSONSymbol issue --- src/parser/serializer.ts | 6 +++--- test/node/symbol.test.ts | 44 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 test/node/symbol.test.ts diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index 123983b3..04115d49 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -726,7 +726,7 @@ export function serializeInto( ); } else if (value._bsontype === 'Binary') { index = serializeBinary(buffer, key, value, index); - } else if (value._bsontype === 'Symbol') { + } else if (value._bsontype === 'BSONSymbol') { index = serializeSymbol(buffer, key, value, index); } else if (value._bsontype === 'DBRef') { index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path); @@ -829,7 +829,7 @@ export function serializeInto( index = serializeFunction(buffer, key, value, index); } else if (value._bsontype === 'Binary') { index = serializeBinary(buffer, key, value, index); - } else if (value._bsontype === 'Symbol') { + } else if (value._bsontype === 'BSONSymbol') { index = serializeSymbol(buffer, key, value, index); } else if (value._bsontype === 'DBRef') { index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path); @@ -936,7 +936,7 @@ export function serializeInto( index = serializeFunction(buffer, key, value, index); } else if (value._bsontype === 'Binary') { index = serializeBinary(buffer, key, value, index); - } else if (value._bsontype === 'Symbol') { + } else if (value._bsontype === 'BSONSymbol') { index = serializeSymbol(buffer, key, value, index); } else if (value._bsontype === 'DBRef') { index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path); diff --git a/test/node/symbol.test.ts b/test/node/symbol.test.ts new file mode 100644 index 00000000..c63ad859 --- /dev/null +++ b/test/node/symbol.test.ts @@ -0,0 +1,44 @@ +import { expect } from 'chai'; +import { BSONSymbol, BSON } from '../register-bson'; +import { bufferFromHexArray } from './tools/utils'; + +describe('class BSONSymbol', () => { + it('get _bsontype returns BSONSymbol', () => { + const sym = new BSONSymbol('symbol'); + expect(sym).to.have.property('_bsontype', 'BSONSymbol'); + }); + + it('serializes to a bson symbol type', () => { + const bytes = bufferFromHexArray([ + '0E', // bson symbol + Buffer.from('sym\x00', 'utf8').toString('hex'), + '07000000', // 6 bytes + Buffer.from('symbol\x00').toString('hex') + ]); + + expect(BSON.serialize({ sym: new BSONSymbol('symbol') })).to.deep.equal(bytes); + }); + + it('deserializes to js string by default', () => { + const bytes = bufferFromHexArray([ + '0E', // bson symbol + Buffer.from('sym\x00', 'utf8').toString('hex'), + '07000000', // 6 bytes + Buffer.from('symbol\x00').toString('hex') + ]); + + expect(BSON.deserialize(bytes)).to.have.property('sym', 'symbol'); + }); + + it('deserializes to BSONSymbol if promoteValues is false', () => { + const bytes = bufferFromHexArray([ + '0E', // bson symbol + Buffer.from('sym\x00', 'utf8').toString('hex'), + '07000000', // 6 bytes + Buffer.from('symbol\x00').toString('hex') + ]); + + const result = BSON.deserialize(bytes, { promoteValues: false }); + expect(result).to.have.nested.property('sym._bsontype', 'BSONSymbol'); + }); +}); From 6557ebc1d5ee6c59c3cf07fd004c9be8789c529c Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 19 Dec 2022 12:54:03 -0500 Subject: [PATCH 04/21] fix: use common constant for versioning --- src/binary.ts | 10 +++++----- src/code.ts | 5 +++-- src/constants.ts | 5 +++++ src/db_ref.ts | 5 +++-- src/decimal128.ts | 5 +++-- src/double.ts | 5 +++-- src/int_32.ts | 5 +++-- src/long.ts | 5 +++-- src/max_key.ts | 6 ++++-- src/min_key.ts | 6 ++++-- src/objectid.ts | 5 +++-- src/regexp.ts | 5 +++-- src/symbol.ts | 6 ++++-- src/timestamp.ts | 5 +++-- 14 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/binary.ts b/src/binary.ts index 3875601b..72f2eeb5 100644 --- a/src/binary.ts +++ b/src/binary.ts @@ -2,7 +2,7 @@ import { bufferToUuidHexString, uuidHexStringToBuffer, uuidValidateString } from import { isUint8Array } from './parser/utils'; import type { EJSONOptions } from './extended_json'; import { BSONError, BSONTypeError } from './error'; -import { BSON_BINARY_SUBTYPE_UUID_NEW } from './constants'; +import { BSON_BINARY_SUBTYPE_UUID_NEW, BSON_MAJOR_VERSION } from './constants'; import { ByteUtils } from './utils/byte_utils'; /** @public */ @@ -32,8 +32,8 @@ export class Binary { return 'Binary'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } /** @@ -310,8 +310,8 @@ const UUID_BYTE_LENGTH = 16; */ export class UUID extends Binary { /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } static cacheHexString: boolean; diff --git a/src/code.ts b/src/code.ts index 9c11dd4a..4624a6bd 100644 --- a/src/code.ts +++ b/src/code.ts @@ -1,4 +1,5 @@ import type { Document } from './bson'; +import { BSON_MAJOR_VERSION } from './constants'; /** @public */ export interface CodeExtended { @@ -16,8 +17,8 @@ export class Code { return 'Code'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } code: string; diff --git a/src/constants.ts b/src/constants.ts index 17fd8a55..260213a7 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,8 @@ +/** @internal */ +export type BSON_MAJOR_VERSION = 5; +/** @internal */ +export const BSON_MAJOR_VERSION: BSON_MAJOR_VERSION = 5; + /** @internal */ export const BSON_INT32_MAX = 0x7fffffff; /** @internal */ diff --git a/src/db_ref.ts b/src/db_ref.ts index d56b0edc..2e3a4e7c 100644 --- a/src/db_ref.ts +++ b/src/db_ref.ts @@ -1,4 +1,5 @@ import type { Document } from './bson'; +import { BSON_MAJOR_VERSION } from './constants'; import type { EJSONOptions } from './extended_json'; import type { ObjectId } from './objectid'; @@ -33,8 +34,8 @@ export class DBRef { return 'DBRef'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } collection!: string; diff --git a/src/decimal128.ts b/src/decimal128.ts index 0d20625a..81384a24 100644 --- a/src/decimal128.ts +++ b/src/decimal128.ts @@ -1,3 +1,4 @@ +import { BSON_MAJOR_VERSION } from './constants'; import { BSONTypeError } from './error'; import { Long } from './long'; import { isUint8Array } from './parser/utils'; @@ -131,8 +132,8 @@ export class Decimal128 { return 'Decimal128'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } readonly bytes!: Uint8Array; diff --git a/src/double.ts b/src/double.ts index 87ff8038..379aebd7 100644 --- a/src/double.ts +++ b/src/double.ts @@ -1,3 +1,4 @@ +import { BSON_MAJOR_VERSION } from './constants'; import type { EJSONOptions } from './extended_json'; /** @public */ @@ -15,8 +16,8 @@ export class Double { return 'Double'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } value!: number; diff --git a/src/int_32.ts b/src/int_32.ts index dc7bbfa9..ca77771c 100644 --- a/src/int_32.ts +++ b/src/int_32.ts @@ -1,3 +1,4 @@ +import { BSON_MAJOR_VERSION } from './constants'; import type { EJSONOptions } from './extended_json'; /** @public */ @@ -15,8 +16,8 @@ export class Int32 { return 'Int32'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } value!: number; diff --git a/src/long.ts b/src/long.ts index cc8fa39c..77916fc5 100644 --- a/src/long.ts +++ b/src/long.ts @@ -1,3 +1,4 @@ +import { BSON_MAJOR_VERSION } from './constants'; import type { EJSONOptions } from './extended_json'; import type { Timestamp } from './timestamp'; @@ -103,8 +104,8 @@ export class Long { return 'Long'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } /** An indicator used to reliably determine if an object is a Long or not. */ diff --git a/src/max_key.ts b/src/max_key.ts index f8ce78eb..a2d20bcf 100644 --- a/src/max_key.ts +++ b/src/max_key.ts @@ -1,3 +1,5 @@ +import { BSON_MAJOR_VERSION } from './constants'; + /** @public */ export interface MaxKeyExtended { $maxKey: 1; @@ -13,8 +15,8 @@ export class MaxKey { return 'MaxKey'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } /** @internal */ diff --git a/src/min_key.ts b/src/min_key.ts index 79d23e26..ec9df3c4 100644 --- a/src/min_key.ts +++ b/src/min_key.ts @@ -1,3 +1,5 @@ +import { BSON_MAJOR_VERSION } from './constants'; + /** @public */ export interface MinKeyExtended { $minKey: 1; @@ -13,8 +15,8 @@ export class MinKey { return 'MinKey'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } /** @internal */ diff --git a/src/objectid.ts b/src/objectid.ts index 772e08e7..ac83f9dc 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -1,3 +1,4 @@ +import { BSON_MAJOR_VERSION } from './constants'; import { BSONTypeError } from './error'; import { isUint8Array } from './parser/utils'; import { BSONDataView, ByteUtils } from './utils/byte_utils'; @@ -32,8 +33,8 @@ export class ObjectId { return 'ObjectId'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } /** @internal */ diff --git a/src/regexp.ts b/src/regexp.ts index 6996c16f..bffb0bf9 100644 --- a/src/regexp.ts +++ b/src/regexp.ts @@ -1,3 +1,4 @@ +import { BSON_MAJOR_VERSION } from './constants'; import { BSONError, BSONTypeError } from './error'; import type { EJSONOptions } from './extended_json'; @@ -29,8 +30,8 @@ export class BSONRegExp { return 'BSONRegExp'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } pattern!: string; diff --git a/src/symbol.ts b/src/symbol.ts index feba31e5..1dffab3e 100644 --- a/src/symbol.ts +++ b/src/symbol.ts @@ -1,3 +1,5 @@ +import { BSON_MAJOR_VERSION } from './constants'; + /** @public */ export interface BSONSymbolExtended { $symbol: string; @@ -13,8 +15,8 @@ export class BSONSymbol { return 'BSONSymbol'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } value!: string; diff --git a/src/timestamp.ts b/src/timestamp.ts index 1a160828..bae2b08e 100644 --- a/src/timestamp.ts +++ b/src/timestamp.ts @@ -1,3 +1,4 @@ +import { BSON_MAJOR_VERSION } from './constants'; import { BSONError } from './error'; import type { Int32 } from './int_32'; import { Long } from './long'; @@ -33,8 +34,8 @@ export class Timestamp extends LongWithoutOverridesClass { return 'Timestamp'; } /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): 5 { - return 5; + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; } static readonly MAX_VALUE = Long.MAX_UNSIGNED_VALUE; From 75960866dcad817670351f70bfb911c2b3ee6e99 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 19 Dec 2022 13:06:07 -0500 Subject: [PATCH 05/21] chore(release): 5.0.0-alpha.1 --- HISTORY.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 19db1911..b9d7a065 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [5.0.0-alpha.1](https://github.com/mongodb/js-bson/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2022-12-19) + +### Bug Fixes + +* fix: BSONSymbol serialize not recognized issue ([5b38dd8](https://github.com/mongodb/js-bson/commit/5b38dd84027ac072c67fbaee6cf08e2a44be2980)) + + ## [5.0.0-alpha.0](https://github.com/mongodb/js-bson/compare/v4.7.0...v5.0.0-alpha.0) (2022-12-16) diff --git a/package-lock.json b/package-lock.json index 63d1d428..30a73be5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bson", - "version": "5.0.0-alpha.0", + "version": "5.0.0-alpha.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bson", - "version": "5.0.0-alpha.0", + "version": "5.0.0-alpha.1", "license": "Apache-2.0", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index f324bda2..615f9c07 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "etc/prepare.js" ], "types": "bson.d.ts", - "version": "5.0.0-alpha.0", + "version": "5.0.0-alpha.1", "author": { "name": "The MongoDB NodeJS Team", "email": "dbx-node@mongodb.com" From 02f26dda765ac95aab41c9e622caf4429a49ba83 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 5 Jan 2023 16:27:16 -0500 Subject: [PATCH 06/21] fix: calculate object size and test for error case --- src/extended_json.ts | 10 ++++++++-- src/parser/calculate_size.ts | 13 ++++++++++++- src/parser/serializer.ts | 15 ++++++++++++--- test/node/extended_json.test.ts | 8 ++++++++ test/node/parser/calculate_size.test.ts | 9 +++++++++ test/node/parser/serializer.test.ts | 9 +++++++++ 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/extended_json.ts b/src/extended_json.ts index 32ee886a..7c32e577 100644 --- a/src/extended_json.ts +++ b/src/extended_json.ts @@ -1,7 +1,13 @@ import { Binary } from './binary'; import type { Document } from './bson'; import { Code } from './code'; -import { BSON_INT32_MAX, BSON_INT32_MIN, BSON_INT64_MAX, BSON_INT64_MIN } from './constants'; +import { + BSON_INT32_MAX, + BSON_INT32_MIN, + BSON_INT64_MAX, + BSON_INT64_MIN, + BSON_MAJOR_VERSION +} from './constants'; import { DBRef, isDBRefLike } from './db_ref'; import { Decimal128 } from './decimal128'; import { Double } from './double'; @@ -310,7 +316,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) { doc != null && typeof doc === 'object' && typeof doc._bsontype === 'string' && - doc[Symbol.for('@@mdb.bson.version')] == null + doc[Symbol.for('@@mdb.bson.version')] !== BSON_MAJOR_VERSION ) { throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); } else if (isBSONType(doc)) { diff --git a/src/parser/calculate_size.ts b/src/parser/calculate_size.ts index 73b6d898..2794802f 100644 --- a/src/parser/calculate_size.ts +++ b/src/parser/calculate_size.ts @@ -1,5 +1,6 @@ import { Binary } from '../binary'; import type { Document } from '../bson'; +import { BSONError } from '../error'; import * as constants from '../constants'; import { ByteUtils } from '../utils/byte_utils'; import { isAnyArrayBuffer, isDate, isRegExp } from './utils'; @@ -77,7 +78,17 @@ function calculateElement( case 'boolean': return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1); case 'object': - if (value == null || value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') { + if ( + value != null && + typeof value._bsontype === 'string' && + value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION + ) { + throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + } else if ( + value == null || + value['_bsontype'] === 'MinKey' || + value['_bsontype'] === 'MaxKey' + ) { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1; } else if (value['_bsontype'] === 'ObjectId') { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1); diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index ba694df1..1581a003 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -706,7 +706,10 @@ export function serializeInto( ignoreUndefined, path ); - } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) { + } else if ( + typeof value === 'object' && + value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION + ) { throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); } else if (value._bsontype === 'ObjectId') { index = serializeObjectId(buffer, key, value, index); @@ -809,7 +812,10 @@ export function serializeInto( ignoreUndefined, path ); - } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) { + } else if ( + typeof value === 'object' && + value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION + ) { throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); } else if (value._bsontype === 'ObjectId') { index = serializeObjectId(buffer, key, value, index); @@ -916,7 +922,10 @@ export function serializeInto( ignoreUndefined, path ); - } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) { + } else if ( + typeof value === 'object' && + value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION + ) { throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); } else if (value._bsontype === 'ObjectId') { index = serializeObjectId(buffer, key, value, index); diff --git a/test/node/extended_json.test.ts b/test/node/extended_json.test.ts index f0840074..7ce4a4e3 100644 --- a/test/node/extended_json.test.ts +++ b/test/node/extended_json.test.ts @@ -550,4 +550,12 @@ describe('Extended JSON', function () { const result = JSON.parse(string); expect(result).to.deep.equal({ a: 1 }); }); + + it(`throws if Symbol.for('@@mdb.bson.version') is the wrong version in EJSON.stringify`, () => { + expect(() => + EJSON.stringify({ + a: { _bsontype: 'Int32', value: 2, [Symbol.for('@@mdb.bson.version')]: 1 } + }) + ).to.throw(BSONError, /Unsupported BSON version/i); + }); }); diff --git a/test/node/parser/calculate_size.test.ts b/test/node/parser/calculate_size.test.ts index 0afda66c..58f808ae 100644 --- a/test/node/parser/calculate_size.test.ts +++ b/test/node/parser/calculate_size.test.ts @@ -1,5 +1,6 @@ import * as BSON from '../../register-bson'; import { expect } from 'chai'; +import { BSONError } from '../../register-bson'; describe('calculateSize()', () => { it('should only enumerate own property keys from input objects', () => { @@ -7,4 +8,12 @@ describe('calculateSize()', () => { Object.setPrototypeOf(input, { b: 2 }); expect(BSON.calculateObjectSize(input)).to.equal(12); }); + + it(`throws if Symbol.for('@@mdb.bson.version') is the wrong version`, () => { + expect(() => + BSON.calculateObjectSize({ + a: { _bsontype: 'Int32', value: 2, [Symbol.for('@@mdb.bson.version')]: 1 } + }) + ).to.throw(BSONError, /Unsupported BSON version/i); + }); }); diff --git a/test/node/parser/serializer.test.ts b/test/node/parser/serializer.test.ts index 26cfb282..62aa4cb5 100644 --- a/test/node/parser/serializer.test.ts +++ b/test/node/parser/serializer.test.ts @@ -1,6 +1,7 @@ import * as BSON from '../../register-bson'; import { bufferFromHexArray } from '../tools/utils'; import { expect } from 'chai'; +import { BSONError } from '../../register-bson'; describe('serialize()', () => { it('should only enumerate own property keys from input objects', () => { @@ -90,5 +91,13 @@ describe('serialize()', () => { expect(() => BSON.serialize(Buffer.alloc(2))).to.throw(/cannot be BSON documents/); expect(() => BSON.serialize(new Uint8Array(3))).to.throw(/cannot be BSON documents/); }); + + it(`throws if Symbol.for('@@mdb.bson.version') is the wrong version`, () => { + expect(() => + BSON.serialize({ + a: { _bsontype: 'Int32', value: 2, [Symbol.for('@@mdb.bson.version')]: 1 } + }) + ).to.throw(BSONError, /Unsupported BSON version/i); + }); }); }); From ff249e9f38a520e5f27ab24ac0afdc9084368fcf Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 10 Jan 2023 16:37:06 -0500 Subject: [PATCH 07/21] chore(release): 5.0.0-alpha.2 --- HISTORY.md | 22 ++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b9d7a065..9b3f99ca 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [5.0.0-alpha.2](https://github.com/mongodb/js-bson/compare/v5.0.0-alpha.1...v5.0.0-alpha.2) (2023-01-10) + + +### ⚠ BREAKING CHANGES + +* **NODE-4890:** make all thrown errors into BSONErrors (#545) + +### Features + +* **NODE-4870:** Support BigInt serialization ([#541](https://github.com/mongodb/js-bson/issues/541)) ([e9e40a2](https://github.com/mongodb/js-bson/commit/e9e40a2cceb6392065a4f217d17fc4211aa1bbd7)) +* **NODE-4871:** Add support for int64 deserialization to BigInt ([#542](https://github.com/mongodb/js-bson/issues/542)) ([9ff60ba](https://github.com/mongodb/js-bson/commit/9ff60baedf2c3fcd5faf3760d4df03235531c608)) +* **NODE-4890:** make all thrown errors into BSONErrors ([#545](https://github.com/mongodb/js-bson/issues/545)) ([5b837a9](https://github.com/mongodb/js-bson/commit/5b837a9e5019016529a83700f3ba3065d5e53e80)) +* **NODE-4927:** exports in package.json for react native and document how to polyfill for BSON ([#550](https://github.com/mongodb/js-bson/issues/550)) ([3b4b61e](https://github.com/mongodb/js-bson/commit/3b4b61ed8dfbe7e7f8635e4584e1b17a91c1c4df)) + + +### Bug Fixes + +* calculate object size and test for error case ([02f26dd](https://github.com/mongodb/js-bson/commit/02f26dda765ac95aab41c9e622caf4429a49ba83)) +* **NODE-4887:** serializeInto does not check for the presence of a toBSON method for values in Map entries ([#555](https://github.com/mongodb/js-bson/issues/555)) ([ebc1c76](https://github.com/mongodb/js-bson/commit/ebc1c765276e83c9e8f073efdf41b27d4e5e7d7b)) +* **NODE-4905:** double precision accuracy in canonical EJSON ([#548](https://github.com/mongodb/js-bson/issues/548)) ([e0dbb17](https://github.com/mongodb/js-bson/commit/e0dbb17d0a13f3aca9879f08cba3573b2a84fd8d)) +* **NODE-4932:** remove .0 suffix from double extended json values ([#554](https://github.com/mongodb/js-bson/issues/554)) ([946866d](https://github.com/mongodb/js-bson/commit/946866d1a1b265f13c0e6c542f54b9786726fa85)) + ### [5.0.0-alpha.1](https://github.com/mongodb/js-bson/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2022-12-19) ### Bug Fixes diff --git a/package-lock.json b/package-lock.json index 30a73be5..069b1192 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bson", - "version": "5.0.0-alpha.1", + "version": "5.0.0-alpha.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bson", - "version": "5.0.0-alpha.1", + "version": "5.0.0-alpha.2", "license": "Apache-2.0", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 1503f62c..1cb06255 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "etc/prepare.js" ], "types": "bson.d.ts", - "version": "5.0.0-alpha.1", + "version": "5.0.0-alpha.2", "author": { "name": "The MongoDB NodeJS Team", "email": "dbx-node@mongodb.com" From 60bf3d405ecf004e5446f6d18115e5b96c128333 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 11 Jan 2023 18:09:43 -0500 Subject: [PATCH 08/21] feat: add BSONValue super class with version getter --- src/binary.ts | 8 +++---- src/bson.ts | 1 + src/bson_value.ts | 11 ++++++++++ src/code.ts | 9 +++----- src/db_ref.ts | 9 +++----- src/decimal128.ts | 9 +++----- src/double.ts | 9 +++----- src/int_32.ts | 9 +++----- src/long.ts | 9 +++----- src/max_key.ts | 8 ++----- src/min_key.ts | 8 ++----- src/objectid.ts | 9 +++----- src/regexp.ts | 9 +++----- src/symbol.ts | 9 +++----- src/timestamp.ts | 5 ----- test/node/bson_type_classes.test.ts | 34 ++++++++++++++++++++++++++++- test/node/exports.test.ts | 1 + 17 files changed, 80 insertions(+), 77 deletions(-) create mode 100644 src/bson_value.ts diff --git a/src/binary.ts b/src/binary.ts index 0b0113f8..0d00c891 100644 --- a/src/binary.ts +++ b/src/binary.ts @@ -4,6 +4,7 @@ import type { EJSONOptions } from './extended_json'; import { BSONError } from './error'; import { BSON_BINARY_SUBTYPE_UUID_NEW, BSON_MAJOR_VERSION } from './constants'; import { ByteUtils } from './utils/byte_utils'; +import { BSONValue } from './bson_value'; /** @public */ export type BinarySequence = Uint8Array | number[]; @@ -27,14 +28,10 @@ export interface BinaryExtended { * @public * @category BSONType */ -export class Binary { +export class Binary extends BSONValue { get _bsontype(): 'Binary' { return 'Binary'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } /** * Binary default subtype @@ -79,6 +76,7 @@ export class Binary { * @param subType - the option binary type. */ constructor(buffer?: string | BinarySequence, subType?: number) { + super(); if ( !(buffer == null) && !(typeof buffer === 'string') && diff --git a/src/bson.ts b/src/bson.ts index b0684731..574220d2 100644 --- a/src/bson.ts +++ b/src/bson.ts @@ -49,6 +49,7 @@ export { BSONRegExp, Decimal128 }; +export { BSONValue } from './bson_value'; export { BSONError } from './error'; export { BSONType } from './constants'; export { EJSON } from './extended_json'; diff --git a/src/bson_value.ts b/src/bson_value.ts new file mode 100644 index 00000000..2913312f --- /dev/null +++ b/src/bson_value.ts @@ -0,0 +1,11 @@ +import { BSON_MAJOR_VERSION } from './constants'; + +/** @public */ +export abstract class BSONValue { + abstract get _bsontype(): string; + + /** @internal */ + get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + return BSON_MAJOR_VERSION; + } +} diff --git a/src/code.ts b/src/code.ts index 4624a6bd..48889f56 100644 --- a/src/code.ts +++ b/src/code.ts @@ -1,5 +1,5 @@ import type { Document } from './bson'; -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; /** @public */ export interface CodeExtended { @@ -12,14 +12,10 @@ export interface CodeExtended { * @public * @category BSONType */ -export class Code { +export class Code extends BSONValue { get _bsontype(): 'Code' { return 'Code'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } code: string; @@ -32,6 +28,7 @@ export class Code { * @param scope - an optional scope for the function. */ constructor(code: string | Function, scope?: Document | null) { + super(); this.code = code.toString(); this.scope = scope ?? null; } diff --git a/src/db_ref.ts b/src/db_ref.ts index 2e3a4e7c..ebdcb61b 100644 --- a/src/db_ref.ts +++ b/src/db_ref.ts @@ -1,5 +1,5 @@ import type { Document } from './bson'; -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; import type { EJSONOptions } from './extended_json'; import type { ObjectId } from './objectid'; @@ -29,14 +29,10 @@ export function isDBRefLike(value: unknown): value is DBRefLike { * @public * @category BSONType */ -export class DBRef { +export class DBRef extends BSONValue { get _bsontype(): 'DBRef' { return 'DBRef'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } collection!: string; oid!: ObjectId; @@ -49,6 +45,7 @@ export class DBRef { * @param db - optional db name, if omitted the reference is local to the current db. */ constructor(collection: string, oid: ObjectId, db?: string, fields?: Document) { + super(); // check if namespace has been provided const parts = collection.split('.'); if (parts.length === 2) { diff --git a/src/decimal128.ts b/src/decimal128.ts index e2f097b6..55543a76 100644 --- a/src/decimal128.ts +++ b/src/decimal128.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; import { BSONError } from './error'; import { Long } from './long'; import { isUint8Array } from './parser/utils'; @@ -127,14 +127,10 @@ export interface Decimal128Extended { * @public * @category BSONType */ -export class Decimal128 { +export class Decimal128 extends BSONValue { get _bsontype(): 'Decimal128' { return 'Decimal128'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } readonly bytes!: Uint8Array; @@ -143,6 +139,7 @@ export class Decimal128 { * or a string representation as returned by .toString() */ constructor(bytes: Uint8Array | string) { + super(); if (typeof bytes === 'string') { this.bytes = Decimal128.fromString(bytes).bytes; } else if (isUint8Array(bytes)) { diff --git a/src/double.ts b/src/double.ts index b7b92994..45815360 100644 --- a/src/double.ts +++ b/src/double.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; import type { EJSONOptions } from './extended_json'; /** @public */ @@ -11,14 +11,10 @@ export interface DoubleExtended { * @public * @category BSONType */ -export class Double { +export class Double extends BSONValue { get _bsontype(): 'Double' { return 'Double'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } value!: number; /** @@ -27,6 +23,7 @@ export class Double { * @param value - the number we want to represent as a double. */ constructor(value: number) { + super(); if ((value as unknown) instanceof Number) { value = value.valueOf(); } diff --git a/src/int_32.ts b/src/int_32.ts index ca77771c..c74f3c82 100644 --- a/src/int_32.ts +++ b/src/int_32.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; import type { EJSONOptions } from './extended_json'; /** @public */ @@ -11,14 +11,10 @@ export interface Int32Extended { * @public * @category BSONType */ -export class Int32 { +export class Int32 extends BSONValue { get _bsontype(): 'Int32' { return 'Int32'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } value!: number; /** @@ -27,6 +23,7 @@ export class Int32 { * @param value - the number we want to represent as an int32. */ constructor(value: number | string) { + super(); if ((value as unknown) instanceof Number) { value = value.valueOf(); } diff --git a/src/long.ts b/src/long.ts index b02d9b22..0e6911e1 100644 --- a/src/long.ts +++ b/src/long.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; import { BSONError } from './error'; import type { EJSONOptions } from './extended_json'; import type { Timestamp } from './timestamp'; @@ -100,14 +100,10 @@ export interface LongExtended { * case would often result in infinite recursion. * Common constant values ZERO, ONE, NEG_ONE, etc. are found as static properties on this class. */ -export class Long { +export class Long extends BSONValue { get _bsontype(): 'Long' { return 'Long'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } /** An indicator used to reliably determine if an object is a Long or not. */ get __isLong__(): boolean { @@ -143,6 +139,7 @@ export class Long { * @param unsigned - Whether unsigned or not, defaults to signed */ constructor(low: number | bigint | string = 0, high?: number | boolean, unsigned?: boolean) { + super(); if (typeof low === 'bigint') { Object.assign(this, Long.fromBigInt(low, !!high)); } else if (typeof low === 'string') { diff --git a/src/max_key.ts b/src/max_key.ts index a2d20bcf..2894f58b 100644 --- a/src/max_key.ts +++ b/src/max_key.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; /** @public */ export interface MaxKeyExtended { @@ -10,14 +10,10 @@ export interface MaxKeyExtended { * @public * @category BSONType */ -export class MaxKey { +export class MaxKey extends BSONValue { get _bsontype(): 'MaxKey' { return 'MaxKey'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } /** @internal */ toExtendedJSON(): MaxKeyExtended { diff --git a/src/min_key.ts b/src/min_key.ts index ec9df3c4..f72cf26b 100644 --- a/src/min_key.ts +++ b/src/min_key.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; /** @public */ export interface MinKeyExtended { @@ -10,14 +10,10 @@ export interface MinKeyExtended { * @public * @category BSONType */ -export class MinKey { +export class MinKey extends BSONValue { get _bsontype(): 'MinKey' { return 'MinKey'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } /** @internal */ toExtendedJSON(): MinKeyExtended { diff --git a/src/objectid.ts b/src/objectid.ts index 012c1e19..a0358208 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; import { BSONError } from './error'; import { isUint8Array } from './parser/utils'; import { BSONDataView, ByteUtils } from './utils/byte_utils'; @@ -28,14 +28,10 @@ const kId = Symbol('id'); * @public * @category BSONType */ -export class ObjectId { +export class ObjectId extends BSONValue { get _bsontype(): 'ObjectId' { return 'ObjectId'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } /** @internal */ private static index = Math.floor(Math.random() * 0xffffff); @@ -53,6 +49,7 @@ export class ObjectId { * @param inputId - Can be a 24 character hex string, 12 byte binary Buffer, or a number. */ constructor(inputId?: string | number | ObjectId | ObjectIdLike | Uint8Array) { + super(); // workingId is set based on type of input and whether valid id exists for the input let workingId; if (typeof inputId === 'object' && inputId && 'id' in inputId) { diff --git a/src/regexp.ts b/src/regexp.ts index c40de2b0..0e7b279d 100644 --- a/src/regexp.ts +++ b/src/regexp.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; import { BSONError } from './error'; import type { EJSONOptions } from './extended_json'; @@ -25,14 +25,10 @@ export interface BSONRegExpExtended { * @public * @category BSONType */ -export class BSONRegExp { +export class BSONRegExp extends BSONValue { get _bsontype(): 'BSONRegExp' { return 'BSONRegExp'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } pattern!: string; options!: string; @@ -41,6 +37,7 @@ export class BSONRegExp { * @param options - The regular expression options */ constructor(pattern: string, options?: string) { + super(); this.pattern = pattern; this.options = alphabetize(options ?? ''); diff --git a/src/symbol.ts b/src/symbol.ts index 1dffab3e..9cce8060 100644 --- a/src/symbol.ts +++ b/src/symbol.ts @@ -1,4 +1,4 @@ -import { BSON_MAJOR_VERSION } from './constants'; +import { BSONValue } from './bson_value'; /** @public */ export interface BSONSymbolExtended { @@ -10,20 +10,17 @@ export interface BSONSymbolExtended { * @public * @category BSONType */ -export class BSONSymbol { +export class BSONSymbol extends BSONValue { get _bsontype(): 'BSONSymbol' { return 'BSONSymbol'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } value!: string; /** * @param value - the string representing the symbol. */ constructor(value: string) { + super(); this.value = value; } diff --git a/src/timestamp.ts b/src/timestamp.ts index bae2b08e..78d7fc4c 100644 --- a/src/timestamp.ts +++ b/src/timestamp.ts @@ -1,4 +1,3 @@ -import { BSON_MAJOR_VERSION } from './constants'; import { BSONError } from './error'; import type { Int32 } from './int_32'; import { Long } from './long'; @@ -33,10 +32,6 @@ export class Timestamp extends LongWithoutOverridesClass { get _bsontype(): 'Timestamp' { return 'Timestamp'; } - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } static readonly MAX_VALUE = Long.MAX_UNSIGNED_VALUE; diff --git a/test/node/bson_type_classes.test.ts b/test/node/bson_type_classes.test.ts index ffe8af4f..5c6c0947 100644 --- a/test/node/bson_type_classes.test.ts +++ b/test/node/bson_type_classes.test.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { __isWeb__ } from '../register-bson'; import { Binary, BSONRegExp, @@ -13,7 +14,8 @@ import { MinKey, ObjectId, Timestamp, - UUID + UUID, + BSONValue } from '../register-bson'; const BSONTypeClasses = [ @@ -33,7 +35,37 @@ const BSONTypeClasses = [ UUID ]; +const BSONTypeClassCtors = new Map BSONValue>([ + ['Binary', () => new Binary()], + ['Code', () => new Code('function () {}')], + ['DBRef', () => new DBRef('name', new ObjectId('00'.repeat(12)))], + ['Decimal128', () => new Decimal128('1.23')], + ['Double', () => new Double(1.23)], + ['Int32', () => new Int32(1)], + ['Long', () => new Long(1n)], + ['MinKey', () => new MinKey()], + ['MaxKey', () => new MaxKey()], + ['ObjectId', () => new ObjectId('00'.repeat(12))], + ['BSONRegExp', () => new BSONRegExp('abc', 'i')], + ['BSONSymbol', () => new BSONSymbol('name')], + ['Timestamp', () => new Timestamp({ t: 1, i: 2 })], + ['UUID', () => new UUID()] +]); + describe('BSON Type classes common interfaces', () => { + context('shared inheritance from BSONValue', () => { + before(function () { + if (__isWeb__) { + return this.currentTest?.skip(); + } + }); + for (const [name, creator] of BSONTypeClassCtors) { + it(`${name} inherits from BSONTypeClass`, () => { + expect(creator()).to.be.instanceOf(BSONValue); + }); + } + }); + for (const TypeClass of BSONTypeClasses) { describe(TypeClass.name, () => { if (TypeClass.name !== 'UUID') { diff --git a/test/node/exports.test.ts b/test/node/exports.test.ts index 004bc616..399af139 100644 --- a/test/node/exports.test.ts +++ b/test/node/exports.test.ts @@ -7,6 +7,7 @@ const EXPECTED_EXPORTS = [ '__isWeb__', 'BSONType', + 'BSONValue', 'EJSON', 'Code', 'BSONSymbol', From 40ed19e6a60fa303478450dbf34d791102b207b7 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 11 Jan 2023 18:16:46 -0500 Subject: [PATCH 09/21] test: name fix --- test/node/bson_type_classes.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node/bson_type_classes.test.ts b/test/node/bson_type_classes.test.ts index 5c6c0947..8d69d5b5 100644 --- a/test/node/bson_type_classes.test.ts +++ b/test/node/bson_type_classes.test.ts @@ -60,7 +60,7 @@ describe('BSON Type classes common interfaces', () => { } }); for (const [name, creator] of BSONTypeClassCtors) { - it(`${name} inherits from BSONTypeClass`, () => { + it(`${name} inherits from BSONValue`, () => { expect(creator()).to.be.instanceOf(BSONValue); }); } From 120d0261481727609308a1280803869ca226ef9b Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 11 Jan 2023 18:29:19 -0500 Subject: [PATCH 10/21] fix: rm getter from uuid --- src/binary.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/binary.ts b/src/binary.ts index 0d00c891..1477381c 100644 --- a/src/binary.ts +++ b/src/binary.ts @@ -307,11 +307,6 @@ const UUID_BYTE_LENGTH = 16; * @public */ export class UUID extends Binary { - /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { - return BSON_MAJOR_VERSION; - } - static cacheHexString: boolean; /** UUID hexString cache @internal */ From 3860c8c3c50ecf8a003b14549e9e75bd7ce45d58 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 11 Jan 2023 20:44:00 -0500 Subject: [PATCH 11/21] fix: lint --- src/binary.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/binary.ts b/src/binary.ts index 1477381c..f95de920 100644 --- a/src/binary.ts +++ b/src/binary.ts @@ -2,7 +2,7 @@ import { bufferToUuidHexString, uuidHexStringToBuffer, uuidValidateString } from import { isUint8Array } from './parser/utils'; import type { EJSONOptions } from './extended_json'; import { BSONError } from './error'; -import { BSON_BINARY_SUBTYPE_UUID_NEW, BSON_MAJOR_VERSION } from './constants'; +import { BSON_BINARY_SUBTYPE_UUID_NEW } from './constants'; import { ByteUtils } from './utils/byte_utils'; import { BSONValue } from './bson_value'; From b7c0e01e3c811809fdf6b126b43798023cebb756 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 11 Jan 2023 20:57:33 -0500 Subject: [PATCH 12/21] feat: add other common apis --- src/bson_value.ts | 3 +++ src/symbol.ts | 1 - test/types/bson.test-d.ts | 8 +++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/bson_value.ts b/src/bson_value.ts index 2913312f..e436f39d 100644 --- a/src/bson_value.ts +++ b/src/bson_value.ts @@ -8,4 +8,7 @@ export abstract class BSONValue { get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { return BSON_MAJOR_VERSION; } + + abstract inspect(): string; + abstract toExtendedJSON(): unknown; } diff --git a/src/symbol.ts b/src/symbol.ts index 9cce8060..081a52ad 100644 --- a/src/symbol.ts +++ b/src/symbol.ts @@ -33,7 +33,6 @@ export class BSONSymbol extends BSONValue { return this.value; } - /** @internal */ inspect(): string { return `new BSONSymbol("${this.value}")`; } diff --git a/test/types/bson.test-d.ts b/test/types/bson.test-d.ts index 448a97af..afa89b91 100644 --- a/test/types/bson.test-d.ts +++ b/test/types/bson.test-d.ts @@ -16,7 +16,8 @@ import { UUID, DBRefLike, Document, - Decimal128Extended + Decimal128Extended, + BSONValue } from '../../bson'; // import from generated bson.d.ts expectType<() => UUID>(Binary.prototype.toUUID); @@ -70,3 +71,8 @@ expectType<'MaxKey'>(MaxKey.prototype._bsontype) expectType<'MinKey'>(MinKey.prototype._bsontype) expectType<'BSONRegExp'>(BSONRegExp.prototype._bsontype) expectType<'Binary'>(UUID.prototype._bsontype) + +// Common BSONValue interface +declare const bsonValue: BSONValue; +expectType(bsonValue._bsontype); +expectType<() => string>(bsonValue.inspect); From 38874290d35c53c4d92ee7f70a5bc2da5f828dec Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 11 Jan 2023 20:59:03 -0500 Subject: [PATCH 13/21] fix: add access annotations --- src/bson_value.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bson_value.ts b/src/bson_value.ts index e436f39d..fdb5f82a 100644 --- a/src/bson_value.ts +++ b/src/bson_value.ts @@ -2,13 +2,17 @@ import { BSON_MAJOR_VERSION } from './constants'; /** @public */ export abstract class BSONValue { - abstract get _bsontype(): string; + /** @public */ + public abstract get _bsontype(): string; /** @internal */ get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { return BSON_MAJOR_VERSION; } - abstract inspect(): string; + /** @public */ + public abstract inspect(): string; + + /** @internal */ abstract toExtendedJSON(): unknown; } From 56e348df784d561c318f35fa90cb79ab995073db Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 12:19:31 -0500 Subject: [PATCH 14/21] fix: comments --- src/bson.ts | 2 +- src/error.ts | 11 +++++++++++ src/extended_json.ts | 4 ++-- src/parser/calculate_size.ts | 4 ++-- src/parser/serializer.ts | 8 ++++---- test/node/cross_compat.test.ts | 6 +++--- test/node/error.test.ts | 12 +++++++++++- test/node/exports.test.ts | 1 + 8 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/bson.ts b/src/bson.ts index 574220d2..8ec644bf 100644 --- a/src/bson.ts +++ b/src/bson.ts @@ -50,7 +50,7 @@ export { Decimal128 }; export { BSONValue } from './bson_value'; -export { BSONError } from './error'; +export { BSONError, BSONVersionError } from './error'; export { BSONType } from './constants'; export { EJSON } from './extended_json'; diff --git a/src/error.ts b/src/error.ts index 88e16b3e..e4e108ad 100644 --- a/src/error.ts +++ b/src/error.ts @@ -43,3 +43,14 @@ export class BSONError extends Error { ); } } + +/** @public */ +export class BSONVersionError extends BSONError { + get name(): 'BSONVersionError' { + return 'BSONVersionError'; + } + + constructor() { + super('Unsupported BSON version, bson types must be from bson 5.0 or later'); + } +} diff --git a/src/extended_json.ts b/src/extended_json.ts index 7c32e577..e3f07c61 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 } from './error'; +import { BSONError, BSONVersionError } from './error'; import { Int32 } from './int_32'; import { Long } from './long'; import { MaxKey } from './max_key'; @@ -318,7 +318,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) { typeof doc._bsontype === 'string' && doc[Symbol.for('@@mdb.bson.version')] !== BSON_MAJOR_VERSION ) { - throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + throw new BSONVersionError(); } else if (isBSONType(doc)) { // the "document" is really just a BSON type object // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/parser/calculate_size.ts b/src/parser/calculate_size.ts index 2794802f..4ac9fe42 100644 --- a/src/parser/calculate_size.ts +++ b/src/parser/calculate_size.ts @@ -1,6 +1,6 @@ import { Binary } from '../binary'; import type { Document } from '../bson'; -import { BSONError } from '../error'; +import { BSONVersionError } from '../error'; import * as constants from '../constants'; import { ByteUtils } from '../utils/byte_utils'; import { isAnyArrayBuffer, isDate, isRegExp } from './utils'; @@ -83,7 +83,7 @@ function calculateElement( typeof value._bsontype === 'string' && value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION ) { - throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + throw new BSONVersionError(); } else if ( value == null || value['_bsontype'] === 'MinKey' || diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index 7cfa9a2e..b59a1b77 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -5,7 +5,7 @@ import * as constants from '../constants'; import type { DBRefLike } from '../db_ref'; import type { Decimal128 } from '../decimal128'; import type { Double } from '../double'; -import { BSONError } from '../error'; +import { BSONError, BSONVersionError } from '../error'; import type { Int32 } from '../int_32'; import { Long } from '../long'; import type { MinKey } from '../min_key'; @@ -710,7 +710,7 @@ export function serializeInto( typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION ) { - throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + throw new BSONVersionError(); } else if (value._bsontype === 'ObjectId') { index = serializeObjectId(buffer, key, value, index); } else if (value._bsontype === 'Decimal128') { @@ -820,7 +820,7 @@ export function serializeInto( typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION ) { - throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + throw new BSONVersionError(); } else if (value._bsontype === 'ObjectId') { index = serializeObjectId(buffer, key, value, index); } else if (type === 'object' && value._bsontype === 'Decimal128') { @@ -930,7 +930,7 @@ export function serializeInto( typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION ) { - throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later'); + throw new BSONVersionError(); } else if (value._bsontype === 'ObjectId') { index = serializeObjectId(buffer, key, value, index); } else if (type === 'object' && value._bsontype === 'Decimal128') { diff --git a/test/node/cross_compat.test.ts b/test/node/cross_compat.test.ts index 88d9c103..b5abd03d 100644 --- a/test/node/cross_compat.test.ts +++ b/test/node/cross_compat.test.ts @@ -64,9 +64,9 @@ const BSONTypeClasses = { }; describe('Prevent previous major versions from working with BSON v5 serialize and stringify', function () { - for (const [typeName, typeMaker] of Object.entries(BSONTypeClasses)) { + for (const [typeName, typeFactory] of Object.entries(BSONTypeClasses)) { it(`serialize throws if ${typeName} is missing a version symbol`, () => { - const type = typeMaker(); + const type = typeFactory(); Object.defineProperty(type, Symbol.for('@@mdb.bson.version'), { value: null }); // set an own property that overrides the getter expect(() => BSON.serialize({ type })).to.throw(/Unsupported BSON version/); expect(() => BSON.serialize({ a: [type] })).to.throw(/Unsupported BSON version/); @@ -74,7 +74,7 @@ describe('Prevent previous major versions from working with BSON v5 serialize an }); it(`stringify throws if ${typeName} is missing a version symbol`, () => { - const type = typeMaker(); + const type = typeFactory(); Object.defineProperty(type, Symbol.for('@@mdb.bson.version'), { value: null }); // set an own property that overrides the getter expect(() => EJSON.stringify({ type })).to.throw(/Unsupported BSON version/); expect(() => EJSON.stringify({ a: [type] })).to.throw(/Unsupported BSON version/); diff --git a/test/node/error.test.ts b/test/node/error.test.ts index 126e7f2b..8e1e7289 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 } from '../register-bson'; +import { __isWeb__, BSONError, BSONVersionError } from '../register-bson'; const instanceOfChecksWork = !__isWeb__; @@ -82,4 +82,14 @@ describe('BSONError', function () { expect(bsonErr.name).equals('BSONError'); expect(bsonErr.message).equals('This is a BSONError message'); }); + + describe('class BSONVersionError', () => { + it('is a BSONError instance', () => { + expect(BSONError.isBSONError(new BSONVersionError())).to.be.true; + }); + + it('has a name property equal to "BSONVersionError"', () => { + expect(new BSONVersionError()).to.have.property('name', 'BSONVersionError'); + }); + }); }); diff --git a/test/node/exports.test.ts b/test/node/exports.test.ts index 399af139..d4d87f74 100644 --- a/test/node/exports.test.ts +++ b/test/node/exports.test.ts @@ -8,6 +8,7 @@ const EXPECTED_EXPORTS = [ 'BSONType', 'BSONValue', + 'BSONVersionError', 'EJSON', 'Code', 'BSONSymbol', From 11159bbe073c9dce037fd42e5810edc7032b2ca5 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 12:27:30 -0500 Subject: [PATCH 15/21] fix: revert history file --- HISTORY.md | 75 ------------------------------------------------------ 1 file changed, 75 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 9b3f99ca..b9d3c08b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,81 +2,6 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -## [5.0.0-alpha.2](https://github.com/mongodb/js-bson/compare/v5.0.0-alpha.1...v5.0.0-alpha.2) (2023-01-10) - - -### ⚠ BREAKING CHANGES - -* **NODE-4890:** make all thrown errors into BSONErrors (#545) - -### Features - -* **NODE-4870:** Support BigInt serialization ([#541](https://github.com/mongodb/js-bson/issues/541)) ([e9e40a2](https://github.com/mongodb/js-bson/commit/e9e40a2cceb6392065a4f217d17fc4211aa1bbd7)) -* **NODE-4871:** Add support for int64 deserialization to BigInt ([#542](https://github.com/mongodb/js-bson/issues/542)) ([9ff60ba](https://github.com/mongodb/js-bson/commit/9ff60baedf2c3fcd5faf3760d4df03235531c608)) -* **NODE-4890:** make all thrown errors into BSONErrors ([#545](https://github.com/mongodb/js-bson/issues/545)) ([5b837a9](https://github.com/mongodb/js-bson/commit/5b837a9e5019016529a83700f3ba3065d5e53e80)) -* **NODE-4927:** exports in package.json for react native and document how to polyfill for BSON ([#550](https://github.com/mongodb/js-bson/issues/550)) ([3b4b61e](https://github.com/mongodb/js-bson/commit/3b4b61ed8dfbe7e7f8635e4584e1b17a91c1c4df)) - - -### Bug Fixes - -* calculate object size and test for error case ([02f26dd](https://github.com/mongodb/js-bson/commit/02f26dda765ac95aab41c9e622caf4429a49ba83)) -* **NODE-4887:** serializeInto does not check for the presence of a toBSON method for values in Map entries ([#555](https://github.com/mongodb/js-bson/issues/555)) ([ebc1c76](https://github.com/mongodb/js-bson/commit/ebc1c765276e83c9e8f073efdf41b27d4e5e7d7b)) -* **NODE-4905:** double precision accuracy in canonical EJSON ([#548](https://github.com/mongodb/js-bson/issues/548)) ([e0dbb17](https://github.com/mongodb/js-bson/commit/e0dbb17d0a13f3aca9879f08cba3573b2a84fd8d)) -* **NODE-4932:** remove .0 suffix from double extended json values ([#554](https://github.com/mongodb/js-bson/issues/554)) ([946866d](https://github.com/mongodb/js-bson/commit/946866d1a1b265f13c0e6c542f54b9786726fa85)) - -### [5.0.0-alpha.1](https://github.com/mongodb/js-bson/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2022-12-19) - -### Bug Fixes - -* fix: BSONSymbol serialize not recognized issue ([5b38dd8](https://github.com/mongodb/js-bson/commit/5b38dd84027ac072c67fbaee6cf08e2a44be2980)) - - -## [5.0.0-alpha.0](https://github.com/mongodb/js-bson/compare/v4.7.0...v5.0.0-alpha.0) (2022-12-16) - - -### ⚠ BREAKING CHANGES - -* **NODE-4892:** error on bson types not from this version -* **NODE-4713:** modernize bundling (#534) -* **NODE-1921:** validate serializer root input (#537) -* **NODE-4711:** remove evalFunctions option (#539) -* **NODE-4706:** validate Timestamp ctor argument (#536) -* **NODE-4710:** remove capital D ObjectID export (#528) -* **NODE-4862:** add BSONType enum and remove internal constants from export (#532) -* **NODE-4410:** only enumerate own properties (#527) -* **NODE-4850:** serialize negative zero to double (#529) -* **NODE-4704:** remove deprecated ObjectId methods (#525) -* **NODE-4461:** remove Decimal128 toObject transformer (#526) -* **NODE-4712:** remove unused Map polyfill (#523) -* **NODE-4440:** bump TS target version to es2020 (#520) -* **NODE-4802:** Refactor BSON to work with cross platform JS APIs (#518) -* **NODE-4435:** drop support for nodejs versions below 14 (#517) - -### Features - -* **NODE-4892:** error on bson types not from this version ([c95f4c1](https://github.com/mongodb/js-bson/commit/c95f4c11fbdfe7d55cb19198d68a17327d6d260d)) -* **NODE-4713:** modernize bundling ([#534](https://github.com/mongodb/js-bson/issues/534)) ([28ce4d5](https://github.com/mongodb/js-bson/commit/28ce4d584ce399d99c41c5f5fd022b6e16f7b913)) -* **NODE-1921:** validate serializer root input ([#537](https://github.com/mongodb/js-bson/issues/537)) ([95d5edf](https://github.com/mongodb/js-bson/commit/95d5edf5c4f8a1cd6c23deaac3a446959fde779e)) -* **NODE-4711:** remove evalFunctions option ([#539](https://github.com/mongodb/js-bson/issues/539)) ([0427eb5](https://github.com/mongodb/js-bson/commit/0427eb588073a07d9e88ddf6155d2c1891ae5ad1)) -* **NODE-4706:** validate Timestamp ctor argument ([#536](https://github.com/mongodb/js-bson/issues/536)) ([f90bcc3](https://github.com/mongodb/js-bson/commit/f90bcc3)) -* **NODE-4710:** remove capital D ObjectID export ([#528](https://github.com/mongodb/js-bson/issues/528)) ([8511225](https://github.com/mongodb/js-bson/commit/8511225)) -* **NODE-4862:** add BSONType enum and remove internal constants from export ([#532](https://github.com/mongodb/js-bson/issues/532)) ([196f9f8](https://github.com/mongodb/js-bson/commit/196f9f8)) -* **NODE-4410:** only enumerate own properties ([#527](https://github.com/mongodb/js-bson/issues/527)) ([5103e4d](https://github.com/mongodb/js-bson/commit/5103e4d)) -* **NODE-4850:** serialize negative zero to double ([#529](https://github.com/mongodb/js-bson/issues/529)) ([be74b30](https://github.com/mongodb/js-bson/commit/be74b30)) -* **NODE-4704:** remove deprecated ObjectId methods ([#525](https://github.com/mongodb/js-bson/issues/525)) ([f1cccf2](https://github.com/mongodb/js-bson/commit/f1cccf2)) -* **NODE-4461:** remove Decimal128 toObject transformer ([#526](https://github.com/mongodb/js-bson/issues/526)) ([14a7473](https://github.com/mongodb/js-bson/commit/14a7473)) -* **NODE-4712:** remove unused Map polyfill ([#523](https://github.com/mongodb/js-bson/issues/523)) ([1fb6dc6](https://github.com/mongodb/js-bson/commit/1fb6dc6)) -* **NODE-4440:** bump TS target version to es2020 ([#520](https://github.com/mongodb/js-bson/issues/520)) ([491d8b7](https://github.com/mongodb/js-bson/commit/491d8b7)) -* **NODE-4802:** Refactor BSON to work with cross platform JS APIs ([#518](https://github.com/mongodb/js-bson/issues/518)) ([3d3d0dc](https://github.com/mongodb/js-bson/commit/3d3d0dc)) -* **NODE-4435:** drop support for nodejs versions below 14 ([#517](https://github.com/mongodb/js-bson/issues/517)) ([027ffb7](https://github.com/mongodb/js-bson/commit/027ffb7)) - - -### Bug Fixes - -* **NODE-4464:** stringify and parse negative zero to and from $numberDouble: -0.0 ([#531](https://github.com/mongodb/js-bson/issues/531)) ([a469e91](https://github.com/mongodb/js-bson/commit/a469e91)) -* **NODE-4771:** serializeFunctions breaks function names outside of basic latin ([#538](https://github.com/mongodb/js-bson/issues/538)) ([35a9234](https://github.com/mongodb/js-bson/commit/35a92341c0860fb41cbd5761250c565154ce1353)) - - ## [4.7.0](https://github.com/mongodb/js-bson/compare/v4.6.5...v4.7.0) (2022-08-18) From 06911d177e39f406b27e0d93cd6862fee3e576af Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 12:31:16 -0500 Subject: [PATCH 16/21] fix: lint --- src/parser/calculate_size.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/parser/calculate_size.ts b/src/parser/calculate_size.ts index 4ac9fe42..fd1e4a02 100644 --- a/src/parser/calculate_size.ts +++ b/src/parser/calculate_size.ts @@ -84,13 +84,9 @@ function calculateElement( value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION ) { throw new BSONVersionError(); - } else if ( - value == null || - value['_bsontype'] === 'MinKey' || - value['_bsontype'] === 'MaxKey' - ) { + } else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1; - } else if (value['_bsontype'] === 'ObjectId') { + } else if (value._bsontype === 'ObjectId') { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1); } else if (value instanceof Date || isDate(value)) { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1); @@ -103,14 +99,14 @@ function calculateElement( (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength ); } else if ( - value['_bsontype'] === 'Long' || - value['_bsontype'] === 'Double' || - value['_bsontype'] === 'Timestamp' + value._bsontype === 'Long' || + value._bsontype === 'Double' || + value._bsontype === 'Timestamp' ) { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1); - } else if (value['_bsontype'] === 'Decimal128') { + } else if (value._bsontype === 'Decimal128') { return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1); - } else if (value['_bsontype'] === 'Code') { + } else if (value._bsontype === 'Code') { // Calculate size depending on the availability of a scope if (value.scope != null && Object.keys(value.scope).length > 0) { return ( @@ -131,7 +127,7 @@ function calculateElement( 1 ); } - } else if (value['_bsontype'] === 'Binary') { + } else if (value._bsontype === 'Binary') { const binary: Binary = value; // Check what kind of subtype we have if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) { @@ -144,7 +140,7 @@ function calculateElement( (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1) ); } - } else if (value['_bsontype'] === 'Symbol') { + } else if (value._bsontype === 'Symbol') { return ( (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + ByteUtils.utf8ByteLength(value.value) + @@ -152,7 +148,7 @@ function calculateElement( 1 + 1 ); - } else if (value['_bsontype'] === 'DBRef') { + } else if (value._bsontype === 'DBRef') { // Set up correct object for serialization const ordered_values = Object.assign( { @@ -183,7 +179,7 @@ function calculateElement( (value.multiline ? 1 : 0) + 1 ); - } else if (value['_bsontype'] === 'BSONRegExp') { + } else if (value._bsontype === 'BSONRegExp') { return ( (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1 + From b11e13dd4bdc7157a641997ecda307198ce127fd Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 12:50:52 -0500 Subject: [PATCH 17/21] fix tests checking for BSONVersionError --- test/node/bson_test.js | 9 +++++---- test/node/extended_json.test.ts | 14 ++++++++------ test/node/parser/calculate_size.test.ts | 4 ++-- test/node/parser/serializer.test.ts | 4 ++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/test/node/bson_test.js b/test/node/bson_test.js index 8365d6f4..eea885d9 100644 --- a/test/node/bson_test.js +++ b/test/node/bson_test.js @@ -1808,7 +1808,8 @@ describe('BSON', function () { it('should throw if invalid BSON types are input to BSON serializer', function () { const oid = new ObjectId('111111111111111111111111'); - const badBsonType = Object.assign({}, oid, { _bsontype: 'bogus' }); + const badBsonType = new ObjectId('111111111111111111111111'); + Object.defineProperty(badBsonType, '_bsontype', { value: 'bogus' }); const badDoc = { bad: badBsonType }; const badArray = [oid, badDoc]; const badMap = new Map([ @@ -1817,9 +1818,9 @@ describe('BSON', function () { ['c', badArray] ]); - expect(() => BSON.serialize(badDoc)).to.throw(BSONError); - expect(() => BSON.serialize(badArray)).to.throw(BSONError); - expect(() => BSON.serialize(badMap)).to.throw(BSONError); + expect(() => BSON.serialize(badDoc)).to.throw(/invalid _bsontype/); + expect(() => BSON.serialize({ badArray })).to.throw(/invalid _bsontype/); + expect(() => BSON.serialize(badMap)).to.throw(/invalid _bsontype/); }); describe('Should support util.inspect for', function () { diff --git a/test/node/extended_json.test.ts b/test/node/extended_json.test.ts index 7ce4a4e3..cbaae0db 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 { BSONError } from '../../src'; +import { BSONVersionError } from '../../src'; // BSON types const Binary = BSON.Binary; @@ -300,14 +300,16 @@ describe('Extended JSON', function () { expect(serialized).to.equal('{"a":10}'); }); - it('should throw if invalid BSON types are input to EJSON serializer', function () { + it.skip('should throw if invalid BSON types are input to EJSON serializer', function () { + // TODO This doesn't throw for the reason you'd expect it to const oid = new ObjectId('111111111111111111111111'); - const badBsonType = Object.assign({}, oid, { _bsontype: 'bogus' }); + const badBsonType = new ObjectId('111111111111111111111111'); + Object.defineProperty(badBsonType, '_bsontype', { value: 'bogus' }); const badDoc = { bad: badBsonType }; const badArray = [oid, badDoc]; // const badMap = new Map([['a', badBsonType], ['b', badDoc], ['c', badArray]]); - expect(() => EJSON.serialize(badDoc)).to.throw(BSONError); - expect(() => EJSON.serialize(badArray)).to.throw(BSONError); + expect(() => EJSON.serialize(badDoc)).to.throw(/invalid _bsontype/); + expect(() => EJSON.serialize({ badArray })).to.throw(/invalid _bsontype/); // expect(() => EJSON.serialize(badMap)).to.throw(); // uncomment when EJSON supports ES6 Map }); @@ -556,6 +558,6 @@ describe('Extended JSON', function () { EJSON.stringify({ a: { _bsontype: 'Int32', value: 2, [Symbol.for('@@mdb.bson.version')]: 1 } }) - ).to.throw(BSONError, /Unsupported BSON version/i); + ).to.throw(BSONVersionError, /Unsupported BSON version/i); }); }); diff --git a/test/node/parser/calculate_size.test.ts b/test/node/parser/calculate_size.test.ts index 58f808ae..6f3f823e 100644 --- a/test/node/parser/calculate_size.test.ts +++ b/test/node/parser/calculate_size.test.ts @@ -1,6 +1,6 @@ import * as BSON from '../../register-bson'; import { expect } from 'chai'; -import { BSONError } from '../../register-bson'; +import { BSONVersionError } from '../../register-bson'; describe('calculateSize()', () => { it('should only enumerate own property keys from input objects', () => { @@ -14,6 +14,6 @@ describe('calculateSize()', () => { BSON.calculateObjectSize({ a: { _bsontype: 'Int32', value: 2, [Symbol.for('@@mdb.bson.version')]: 1 } }) - ).to.throw(BSONError, /Unsupported BSON version/i); + ).to.throw(BSONVersionError, /Unsupported BSON version/i); }); }); diff --git a/test/node/parser/serializer.test.ts b/test/node/parser/serializer.test.ts index 62aa4cb5..37349f4e 100644 --- a/test/node/parser/serializer.test.ts +++ b/test/node/parser/serializer.test.ts @@ -1,7 +1,7 @@ import * as BSON from '../../register-bson'; import { bufferFromHexArray } from '../tools/utils'; import { expect } from 'chai'; -import { BSONError } from '../../register-bson'; +import { BSONVersionError } from '../../register-bson'; describe('serialize()', () => { it('should only enumerate own property keys from input objects', () => { @@ -97,7 +97,7 @@ describe('serialize()', () => { BSON.serialize({ a: { _bsontype: 'Int32', value: 2, [Symbol.for('@@mdb.bson.version')]: 1 } }) - ).to.throw(BSONError, /Unsupported BSON version/i); + ).to.throw(BSONVersionError, /Unsupported BSON version/i); }); }); }); From ee1c50040880141622ca8dda1b7113e0d9513b4e Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 14:11:47 -0500 Subject: [PATCH 18/21] docs: migrate --- docs/upgrade-to-v5.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/upgrade-to-v5.md b/docs/upgrade-to-v5.md index 01be3b26..dca8b7e6 100644 --- a/docs/upgrade-to-v5.md +++ b/docs/upgrade-to-v5.md @@ -288,3 +288,9 @@ try { throw error; } ``` + +### Explicit cross version incompatibility + +Starting with v5.0.0 of the BSON library instances of types from previous versions will throw an error when passed to the serializer. +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. +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. From c3b4cc2bcc78e23c67fcf666568eae51b9fd2b01 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 14:23:47 -0500 Subject: [PATCH 19/21] docs: add jira ticket --- test/node/extended_json.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node/extended_json.test.ts b/test/node/extended_json.test.ts index cbaae0db..51e19fd7 100644 --- a/test/node/extended_json.test.ts +++ b/test/node/extended_json.test.ts @@ -301,7 +301,7 @@ describe('Extended JSON', function () { }); it.skip('should throw if invalid BSON types are input to EJSON serializer', function () { - // TODO This doesn't throw for the reason you'd expect it to + // TODO(NODE-4952): Increase EJSON strictness w.r.t _bsontype validation const oid = new ObjectId('111111111111111111111111'); const badBsonType = new ObjectId('111111111111111111111111'); Object.defineProperty(badBsonType, '_bsontype', { value: 'bogus' }); From 814f52fb5822012c9b5a983a057c0a17ff6ad7e7 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 18:23:28 -0500 Subject: [PATCH 20/21] address comments --- package-lock.json | 4 ++-- package.json | 2 +- src/bson_value.ts | 2 +- src/constants.ts | 4 +--- src/error.ts | 6 +++++- test/node/parser/calculate_size.test.ts | 8 ++++++++ test/node/parser/serializer.test.ts | 8 ++++++++ 7 files changed, 26 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 069b1192..27ccbd3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bson", - "version": "5.0.0-alpha.2", + "version": "4.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bson", - "version": "5.0.0-alpha.2", + "version": "4.7.0", "license": "Apache-2.0", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 1cb06255..b63fe190 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "etc/prepare.js" ], "types": "bson.d.ts", - "version": "5.0.0-alpha.2", + "version": "4.7.0", "author": { "name": "The MongoDB NodeJS Team", "email": "dbx-node@mongodb.com" diff --git a/src/bson_value.ts b/src/bson_value.ts index fdb5f82a..b36266e0 100644 --- a/src/bson_value.ts +++ b/src/bson_value.ts @@ -6,7 +6,7 @@ export abstract class BSONValue { public abstract get _bsontype(): string; /** @internal */ - get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION { + get [Symbol.for('@@mdb.bson.version')](): typeof BSON_MAJOR_VERSION { return BSON_MAJOR_VERSION; } diff --git a/src/constants.ts b/src/constants.ts index 260213a7..50a3d42e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,5 @@ /** @internal */ -export type BSON_MAJOR_VERSION = 5; -/** @internal */ -export const BSON_MAJOR_VERSION: BSON_MAJOR_VERSION = 5; +export const BSON_MAJOR_VERSION = 5 as const; /** @internal */ export const BSON_INT32_MAX = 0x7fffffff; diff --git a/src/error.ts b/src/error.ts index e4e108ad..b01cde53 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,3 +1,5 @@ +import { BSON_MAJOR_VERSION } from './constants'; + /** * @public * `BSONError` objects are thrown when runtime errors occur. @@ -51,6 +53,8 @@ export class BSONVersionError extends BSONError { } constructor() { - super('Unsupported BSON version, bson types must be from bson 5.0 or later'); + super( + `Unsupported BSON version, bson types must be from bson ${BSON_MAJOR_VERSION}.0 or later` + ); } } diff --git a/test/node/parser/calculate_size.test.ts b/test/node/parser/calculate_size.test.ts index 6f3f823e..3ebccd16 100644 --- a/test/node/parser/calculate_size.test.ts +++ b/test/node/parser/calculate_size.test.ts @@ -16,4 +16,12 @@ describe('calculateSize()', () => { }) ).to.throw(BSONVersionError, /Unsupported BSON version/i); }); + + it(`throws if Symbol.for('@@mdb.bson.version') is not defined`, () => { + expect(() => + BSON.calculateObjectSize({ + a: { _bsontype: 'Int32', value: 2 } + }) + ).to.throw(BSONVersionError, /Unsupported BSON version/i); + }); }); diff --git a/test/node/parser/serializer.test.ts b/test/node/parser/serializer.test.ts index 37349f4e..d09c11b1 100644 --- a/test/node/parser/serializer.test.ts +++ b/test/node/parser/serializer.test.ts @@ -99,5 +99,13 @@ describe('serialize()', () => { }) ).to.throw(BSONVersionError, /Unsupported BSON version/i); }); + + it(`throws if Symbol.for('@@mdb.bson.version') is not defined`, () => { + expect(() => + BSON.serialize({ + a: { _bsontype: 'Int32', value: 2 } + }) + ).to.throw(BSONVersionError, /Unsupported BSON version/i); + }); }); }); From 76148630f7fd069177943db454a40da0d696e32b Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Jan 2023 18:27:56 -0500 Subject: [PATCH 21/21] docs: update guide --- docs/upgrade-to-v5.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/upgrade-to-v5.md b/docs/upgrade-to-v5.md index dca8b7e6..6e133ecc 100644 --- a/docs/upgrade-to-v5.md +++ b/docs/upgrade-to-v5.md @@ -294,3 +294,13 @@ try { Starting with v5.0.0 of the BSON library instances of types from previous versions will throw an error when passed to the serializer. 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. 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. + +```ts +// npm install bson4@npm:bson@4 +// npm install bson5@npm:bson@5 +import { ObjectId } from 'bson4'; +import { serialize } from 'bson5'; + +serialize({ _id: new ObjectId() }); +// Uncaught BSONVersionError: Unsupported BSON version, bson types must be from bson 5.0 or later +```