From 8cdc54a4111f4e49a184d994f079fadbbbed9f86 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 16 Sep 2020 15:44:54 -0400 Subject: [PATCH 1/8] feat: :sparkles: Add support for serializing BigInt Given JavaScript's new BigInt type the serializer will attempt to fit the value in the smallest possible type. In the following order: Int32, Int64, Decimal128. Any values larger than Decimal128's maximum representable range will result in a thrown TypeError NODE-2529 --- src/long.ts | 15 +++++++++++ src/parser/serializer.ts | 57 ++++++++++++++++++++++++++++++++++++++- test/node/bigint_tests.js | 55 +++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 test/node/bigint_tests.js diff --git a/src/long.ts b/src/long.ts index 9a0d9959..c2230b15 100644 --- a/src/long.ts +++ b/src/long.ts @@ -187,6 +187,16 @@ export class Long { return Long.fromBits(value % TWO_PWR_32_DBL | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); } + /** + * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * @param value - The number in question + * @param unsigned - Whether unsigned or not, defaults to signed + * @returns The corresponding Long value + */ + static fromBigInt(value: bigint, unsigned?: boolean): Long { + return Long.fromString(value.toString(10), unsigned); + } + /** * Returns a Long representation of the given string, written using the specified radix. * @param str - The textual representation of the Long @@ -793,6 +803,11 @@ export class Long { return this.high * TWO_PWR_32_DBL + (this.low >>> 0); } + /** Converts the Long to a BigInt (arbitrary precision). */ + toBigInt(): bigint { + return BigInt(this.toString()); + } + /** * Converts this Long to its byte representation. * @param le - Whether little or big endian, defaults to big endian diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index 0bfdde6b..88ea88ef 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -4,7 +4,7 @@ import type { BSONSymbol, DBRef, Document, MaxKey } from '../bson'; import type { Code } from '../code'; import * as constants from '../constants'; import type { DBRefLike } from '../db_ref'; -import type { Decimal128 } from '../decimal128'; +import { Decimal128 } from '../decimal128'; import type { Double } from '../double'; import { ensureBuffer } from '../ensure_buffer'; import { isBSONType } from '../extended_json'; @@ -162,6 +162,55 @@ function serializeNumber( return index; } +function serializeBigInt( + buffer: Buffer, + key: string, + value: bigint, + index: number, + isArray?: boolean +) { + const typeIndicatorIndex = index; + index += 1; + const keyByteLength = !isArray + ? buffer.write(key, index, undefined, 'utf8') + : buffer.write(key, index, undefined, 'ascii'); + + index += keyByteLength + 1; + buffer[index - 1] = 0; + + if (BigInt(constants.BSON_INT32_MIN) <= value && BigInt(constants.BSON_INT32_MAX) >= value) { + // value fits in int 32 + buffer[typeIndicatorIndex] = constants.BSON_DATA_INT; + index = buffer.writeInt32LE(Number(value), index, false); + } else if ( + BigInt(constants.BSON_INT64_MIN) <= value && + value <= BigInt(constants.BSON_INT64_MAX) + ) { + // value fits in int 64 + buffer[typeIndicatorIndex] = constants.BSON_DATA_LONG; + const longVal = Long.fromBigInt(value); + buffer.set(longVal.toBytesLE(), index); + index += 8; + } else { + // value is very large, try decimal128 + try { + const dec128Value = Decimal128.fromString(value.toString(10)); + if (dec128Value.toString() !== value.toString(10)) { + // precision was lost! + throw new Error(); + } + buffer[typeIndicatorIndex] = constants.BSON_DATA_DECIMAL128; + buffer.set(dec128Value.bytes, index); + index += 16; + } catch (e) { + // We could stringify the number? some metadata has to be tracked to do that correctly, + // cross driver support even + throw new TypeError(`BigInt ${value} is outside of BSON number format ranges`); + } + } + return index; +} + function serializeNull(buffer: Buffer, key: string, _: unknown, index: number, isArray?: boolean) { // Set long type buffer[index++] = constants.BSON_DATA_NULL; @@ -807,6 +856,8 @@ export function serializeInto( index = serializeString(buffer, key, value, index, true); } else if (typeof value === 'number') { index = serializeNumber(buffer, key, value, index, true); + } else if (typeof value === 'bigint') { + index = serializeBigInt(buffer, key, value, index, true); } else if (typeof value === 'boolean') { index = serializeBoolean(buffer, key, value, index, true); } else if (value instanceof Date || isDate(value)) { @@ -913,6 +964,8 @@ export function serializeInto( index = serializeString(buffer, key, value, index); } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); + } else if (type === 'bigint') { + index = serializeBigInt(buffer, key, value, index); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { @@ -1015,6 +1068,8 @@ export function serializeInto( index = serializeString(buffer, key, value, index); } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); + } else if (type === 'bigint') { + index = serializeBigInt(buffer, key, value, index); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { diff --git a/test/node/bigint_tests.js b/test/node/bigint_tests.js new file mode 100644 index 00000000..5eb24106 --- /dev/null +++ b/test/node/bigint_tests.js @@ -0,0 +1,55 @@ +/* globals BigInt */ +'use strict'; + +const { Buffer } = require('buffer'); +const BSON = require('../register-bson'); + +describe('BSON BigInt Support', function () { + it('Should serialize an int that fits in int32', function () { + const testDoc = { b: BigInt(32) }; + const serializedDoc = BSON.serialize(testDoc); + + // prettier-ignore + const resultBuffer = Buffer.from([0x0C, 0x00, 0x00, 0x00, 0x10, 0x62, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00]); + + const resultDoc = BSON.deserialize(serializedDoc); + + expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); + expect(BigInt(resultDoc.b)).to.equal(testDoc.b); + }); + + it('Should serialize an int that fits in int64', function () { + const testDoc = { b: BigInt(0x1ffffffff) }; + const serializedDoc = BSON.serialize(testDoc); + + // prettier-ignore + const resultBuffer = Buffer.from([0x10, 0x00, 0x00, 0x00, 0x12, 0x62, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00]); + + const resultDoc = BSON.deserialize(serializedDoc); + + expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); + expect(BigInt(resultDoc.b)).to.equal(testDoc.b); + }); + + it('Should serialize an int that fits in decimal128', function () { + const testDoc = { b: BigInt('9223372036854776001') }; // int64 max + 1 + const serializedDoc = BSON.serialize(testDoc); + + // prettier-ignore + const resultBuffer = Buffer.from([0x18, 0x00, 0x00, 0x00, 0x13, 0x62, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0x00]); + + const resultDoc = BSON.deserialize(serializedDoc); + + expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); + expect(resultDoc.b._bsontype).to.equal('Decimal128'); + expect(BigInt(resultDoc.b.toString())).to.equal(testDoc.b); + }); + + it('Should throw if BigInt is too large to serialize', function () { + const testDoc = { + b: BigInt('9'.repeat(35)) + }; // decimal 128 can only encode 34 digits of precision + + expect(() => BSON.serialize(testDoc)).to.throw(); + }); +}); From 0bf1faa429ae664dbcc42950abd9bd84cdc678cb Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 17 Sep 2020 11:52:25 -0400 Subject: [PATCH 2/8] test: :bug: Skip BigInt tests on environments that don't have BigInt --- test/node/bigint_tests.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/node/bigint_tests.js b/test/node/bigint_tests.js index 5eb24106..a346c110 100644 --- a/test/node/bigint_tests.js +++ b/test/node/bigint_tests.js @@ -5,6 +5,13 @@ const { Buffer } = require('buffer'); const BSON = require('../register-bson'); describe('BSON BigInt Support', function () { + before(function () { + try { + BigInt(0); + } catch (_) { + this.skip('JS VM does not support BigInt'); + } + }); it('Should serialize an int that fits in int32', function () { const testDoc = { b: BigInt(32) }; const serializedDoc = BSON.serialize(testDoc); From 1519c339b1daef203d32d50ff949a9dea4fda067 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 17 Sep 2020 14:58:26 -0400 Subject: [PATCH 3/8] refactor: :fire: Throw an error when serializing BigInt In order to ensure users get consistent and expected behavoir BigInts should be manually translated to a type which can fit the precision of the value --- src/parser/serializer.ts | 2 ++ test/node/bigint_tests.js | 64 ++++++++++++++++++++++----------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index 88ea88ef..cfd9caeb 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -169,6 +169,8 @@ function serializeBigInt( index: number, isArray?: boolean ) { + throw new TypeError('Do not know how to serialize a BigInt'); + const typeIndicatorIndex = index; index += 1; const keyByteLength = !isArray diff --git a/test/node/bigint_tests.js b/test/node/bigint_tests.js index a346c110..2c87745a 100644 --- a/test/node/bigint_tests.js +++ b/test/node/bigint_tests.js @@ -1,7 +1,6 @@ /* globals BigInt */ 'use strict'; -const { Buffer } = require('buffer'); const BSON = require('../register-bson'); describe('BSON BigInt Support', function () { @@ -14,49 +13,58 @@ describe('BSON BigInt Support', function () { }); it('Should serialize an int that fits in int32', function () { const testDoc = { b: BigInt(32) }; - const serializedDoc = BSON.serialize(testDoc); + expect(() => BSON.serialize(testDoc)).to.throw( + TypeError, + 'Do not know how to serialize a BigInt' + ); - // prettier-ignore - const resultBuffer = Buffer.from([0x0C, 0x00, 0x00, 0x00, 0x10, 0x62, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00]); - - const resultDoc = BSON.deserialize(serializedDoc); - - expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); - expect(BigInt(resultDoc.b)).to.equal(testDoc.b); + // const serializedDoc = BSON.serialize(testDoc); + // // prettier-ignore + // const resultBuffer = Buffer.from([0x0C, 0x00, 0x00, 0x00, 0x10, 0x62, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00]); + // const resultDoc = BSON.deserialize(serializedDoc); + // expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); + // expect(BigInt(resultDoc.b)).to.equal(testDoc.b); }); it('Should serialize an int that fits in int64', function () { const testDoc = { b: BigInt(0x1ffffffff) }; - const serializedDoc = BSON.serialize(testDoc); - - // prettier-ignore - const resultBuffer = Buffer.from([0x10, 0x00, 0x00, 0x00, 0x12, 0x62, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00]); - - const resultDoc = BSON.deserialize(serializedDoc); + expect(() => BSON.serialize(testDoc)).to.throw( + TypeError, + 'Do not know how to serialize a BigInt' + ); - expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); - expect(BigInt(resultDoc.b)).to.equal(testDoc.b); + // const serializedDoc = BSON.serialize(testDoc); + // // prettier-ignore + // const resultBuffer = Buffer.from([0x10, 0x00, 0x00, 0x00, 0x12, 0x62, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00]); + // const resultDoc = BSON.deserialize(serializedDoc); + // expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); + // expect(BigInt(resultDoc.b)).to.equal(testDoc.b); }); it('Should serialize an int that fits in decimal128', function () { const testDoc = { b: BigInt('9223372036854776001') }; // int64 max + 1 - const serializedDoc = BSON.serialize(testDoc); + expect(() => BSON.serialize(testDoc)).to.throw( + TypeError, + 'Do not know how to serialize a BigInt' + ); - // prettier-ignore - const resultBuffer = Buffer.from([0x18, 0x00, 0x00, 0x00, 0x13, 0x62, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0x00]); - - const resultDoc = BSON.deserialize(serializedDoc); - - expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); - expect(resultDoc.b._bsontype).to.equal('Decimal128'); - expect(BigInt(resultDoc.b.toString())).to.equal(testDoc.b); + // const serializedDoc = BSON.serialize(testDoc); + // // prettier-ignore + // const resultBuffer = Buffer.from([0x18, 0x00, 0x00, 0x00, 0x13, 0x62, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0x00]); + // const resultDoc = BSON.deserialize(serializedDoc); + // expect(Array.from(serializedDoc)).to.have.members(Array.from(resultBuffer)); + // expect(resultDoc.b._bsontype).to.equal('Decimal128'); + // expect(BigInt(resultDoc.b.toString())).to.equal(testDoc.b); }); it('Should throw if BigInt is too large to serialize', function () { const testDoc = { b: BigInt('9'.repeat(35)) }; // decimal 128 can only encode 34 digits of precision - - expect(() => BSON.serialize(testDoc)).to.throw(); + expect(() => BSON.serialize(testDoc)).to.throw( + TypeError, + 'Do not know how to serialize a BigInt' + ); + // expect(() => BSON.serialize(testDoc)).to.throw(); }); }); From bbc59fa7c57921072d6f3873dadca31c89e01f66 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 17 Sep 2020 16:42:47 -0400 Subject: [PATCH 4/8] fix: :bug: Add catch for BigInt64Array and BigUInt64Array --- src/parser/serializer.ts | 4 ++-- src/parser/utils.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index cfd9caeb..910f5493 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -15,7 +15,7 @@ import { Map } from '../map'; import type { MinKey } from '../min_key'; import type { ObjectId } from '../objectid'; import type { BSONRegExp } from '../regexp'; -import { isDate, isUint8Array, normalizedFunctionString } from './utils'; +import { isBigInt64Array, isBigUInt64Array, isDate, normalizedFunctionString } from './utils'; export interface SerializeOptions { /** the serializer will check if keys are valid. */ @@ -966,7 +966,7 @@ export function serializeInto( index = serializeString(buffer, key, value, index); } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); - } else if (type === 'bigint') { + } else if (type === 'bigint' || isBigInt64Array(value) || isBigUInt64Array(value)) { index = serializeBigInt(buffer, key, value, index); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); diff --git a/src/parser/utils.ts b/src/parser/utils.ts index b7ee7aba..5a2c97ba 100644 --- a/src/parser/utils.ts +++ b/src/parser/utils.ts @@ -40,6 +40,24 @@ export function isUint8Array(value: unknown): value is Uint8Array { return Object.prototype.toString.call(value) === '[object Uint8Array]'; } +export function isBigInt64Array(value: unknown): value is BigInt64Array { + return Object.prototype.toString.call(value) === '[object BigInt64Array]'; +} + +export function isBigUInt64Array(value: unknown): value is BigUint64Array { + return Object.prototype.toString.call(value) === '[object BigUint64Array]'; +} + +/** Call to check if your environment has `Buffer` */ +export function haveBuffer(): boolean { + return typeof global !== 'undefined' && typeof global.Buffer !== 'undefined'; +} + +/** Callable in any environment to check if value is a Buffer */ +export function isBuffer(value: unknown): value is Buffer { + return haveBuffer() && Buffer.isBuffer(value); +} + // To ensure that 0.4 of node works correctly export function isDate(d: unknown): d is Date { return isObjectLike(d) && Object.prototype.toString.call(d) === '[object Date]'; From e86a3b6f1f32ca2fa2a830276000cd1326d74b0e Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 18 Sep 2020 11:55:43 -0400 Subject: [PATCH 5/8] remove dead bigint serializing code --- src/parser/serializer.ts | 57 +++------------------------------------- 1 file changed, 3 insertions(+), 54 deletions(-) diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index 910f5493..00021b71 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -162,57 +162,6 @@ function serializeNumber( return index; } -function serializeBigInt( - buffer: Buffer, - key: string, - value: bigint, - index: number, - isArray?: boolean -) { - throw new TypeError('Do not know how to serialize a BigInt'); - - const typeIndicatorIndex = index; - index += 1; - const keyByteLength = !isArray - ? buffer.write(key, index, undefined, 'utf8') - : buffer.write(key, index, undefined, 'ascii'); - - index += keyByteLength + 1; - buffer[index - 1] = 0; - - if (BigInt(constants.BSON_INT32_MIN) <= value && BigInt(constants.BSON_INT32_MAX) >= value) { - // value fits in int 32 - buffer[typeIndicatorIndex] = constants.BSON_DATA_INT; - index = buffer.writeInt32LE(Number(value), index, false); - } else if ( - BigInt(constants.BSON_INT64_MIN) <= value && - value <= BigInt(constants.BSON_INT64_MAX) - ) { - // value fits in int 64 - buffer[typeIndicatorIndex] = constants.BSON_DATA_LONG; - const longVal = Long.fromBigInt(value); - buffer.set(longVal.toBytesLE(), index); - index += 8; - } else { - // value is very large, try decimal128 - try { - const dec128Value = Decimal128.fromString(value.toString(10)); - if (dec128Value.toString() !== value.toString(10)) { - // precision was lost! - throw new Error(); - } - buffer[typeIndicatorIndex] = constants.BSON_DATA_DECIMAL128; - buffer.set(dec128Value.bytes, index); - index += 16; - } catch (e) { - // We could stringify the number? some metadata has to be tracked to do that correctly, - // cross driver support even - throw new TypeError(`BigInt ${value} is outside of BSON number format ranges`); - } - } - return index; -} - function serializeNull(buffer: Buffer, key: string, _: unknown, index: number, isArray?: boolean) { // Set long type buffer[index++] = constants.BSON_DATA_NULL; @@ -859,7 +808,7 @@ export function serializeInto( } else if (typeof value === 'number') { index = serializeNumber(buffer, key, value, index, true); } else if (typeof value === 'bigint') { - index = serializeBigInt(buffer, key, value, index, true); + throw new TypeError('Do not know how to serialize a BigInt, please use Decimal128'); } else if (typeof value === 'boolean') { index = serializeBoolean(buffer, key, value, index, true); } else if (value instanceof Date || isDate(value)) { @@ -967,7 +916,7 @@ export function serializeInto( } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); } else if (type === 'bigint' || isBigInt64Array(value) || isBigUInt64Array(value)) { - index = serializeBigInt(buffer, key, value, index); + throw new TypeError('Do not know how to serialize a BigInt, please use Decimal128'); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { @@ -1071,7 +1020,7 @@ export function serializeInto( } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); } else if (type === 'bigint') { - index = serializeBigInt(buffer, key, value, index); + throw new TypeError('Do not know how to serialize a BigInt, please use Decimal128'); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { From e119bab18b6aa941d04c2996dc540ea82934e7ad Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 18 Sep 2020 11:58:51 -0400 Subject: [PATCH 6/8] bigint test fixes --- src/parser/serializer.ts | 2 +- test/node/bigint_tests.js | 20 ++++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index 00021b71..87910157 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -4,7 +4,7 @@ import type { BSONSymbol, DBRef, Document, MaxKey } from '../bson'; import type { Code } from '../code'; import * as constants from '../constants'; import type { DBRefLike } from '../db_ref'; -import { Decimal128 } from '../decimal128'; +import type { Decimal128 } from '../decimal128'; import type { Double } from '../double'; import { ensureBuffer } from '../ensure_buffer'; import { isBSONType } from '../extended_json'; diff --git a/test/node/bigint_tests.js b/test/node/bigint_tests.js index 2c87745a..b8d17bd3 100644 --- a/test/node/bigint_tests.js +++ b/test/node/bigint_tests.js @@ -13,10 +13,7 @@ describe('BSON BigInt Support', function () { }); it('Should serialize an int that fits in int32', function () { const testDoc = { b: BigInt(32) }; - expect(() => BSON.serialize(testDoc)).to.throw( - TypeError, - 'Do not know how to serialize a BigInt' - ); + expect(() => BSON.serialize(testDoc)).to.throw(TypeError); // const serializedDoc = BSON.serialize(testDoc); // // prettier-ignore @@ -28,10 +25,7 @@ describe('BSON BigInt Support', function () { it('Should serialize an int that fits in int64', function () { const testDoc = { b: BigInt(0x1ffffffff) }; - expect(() => BSON.serialize(testDoc)).to.throw( - TypeError, - 'Do not know how to serialize a BigInt' - ); + expect(() => BSON.serialize(testDoc)).to.throw(TypeError); // const serializedDoc = BSON.serialize(testDoc); // // prettier-ignore @@ -43,10 +37,7 @@ describe('BSON BigInt Support', function () { it('Should serialize an int that fits in decimal128', function () { const testDoc = { b: BigInt('9223372036854776001') }; // int64 max + 1 - expect(() => BSON.serialize(testDoc)).to.throw( - TypeError, - 'Do not know how to serialize a BigInt' - ); + expect(() => BSON.serialize(testDoc)).to.throw(TypeError); // const serializedDoc = BSON.serialize(testDoc); // // prettier-ignore @@ -61,10 +52,7 @@ describe('BSON BigInt Support', function () { const testDoc = { b: BigInt('9'.repeat(35)) }; // decimal 128 can only encode 34 digits of precision - expect(() => BSON.serialize(testDoc)).to.throw( - TypeError, - 'Do not know how to serialize a BigInt' - ); + expect(() => BSON.serialize(testDoc)).to.throw(TypeError); // expect(() => BSON.serialize(testDoc)).to.throw(); }); }); From b8727ecd69b8a9b5bf896c09c24d2fedb2f852da Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 18 Sep 2020 17:09:06 -0400 Subject: [PATCH 7/8] fix: error message for bigint --- src/parser/serializer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index 87910157..eff35d2f 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -808,7 +808,7 @@ export function serializeInto( } else if (typeof value === 'number') { index = serializeNumber(buffer, key, value, index, true); } else if (typeof value === 'bigint') { - throw new TypeError('Do not know how to serialize a BigInt, please use Decimal128'); + throw new TypeError('Unsupported type BigInt, please use Decimal128'); } else if (typeof value === 'boolean') { index = serializeBoolean(buffer, key, value, index, true); } else if (value instanceof Date || isDate(value)) { @@ -916,7 +916,7 @@ export function serializeInto( } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); } else if (type === 'bigint' || isBigInt64Array(value) || isBigUInt64Array(value)) { - throw new TypeError('Do not know how to serialize a BigInt, please use Decimal128'); + throw new TypeError('Unsupported type BigInt, please use Decimal128'); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { @@ -1020,7 +1020,7 @@ export function serializeInto( } else if (type === 'number') { index = serializeNumber(buffer, key, value, index); } else if (type === 'bigint') { - throw new TypeError('Do not know how to serialize a BigInt, please use Decimal128'); + throw new TypeError('Unsupported type BigInt, please use Decimal128'); } else if (type === 'boolean') { index = serializeBoolean(buffer, key, value, index); } else if (value instanceof Date || isDate(value)) { From f6a51b2b296e9d07e48848efe712c4d41f9e32ba Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 18 Sep 2020 17:16:01 -0400 Subject: [PATCH 8/8] rebase types fixes --- src/long.ts | 4 ++-- src/parser/serializer.ts | 8 +++++++- src/parser/utils.ts | 2 ++ tsconfig.json | 4 +++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/long.ts b/src/long.ts index c2230b15..cd2ba6a4 100644 --- a/src/long.ts +++ b/src/long.ts @@ -1,6 +1,6 @@ -import type { Timestamp } from './timestamp'; import type { EJSONOptions } from './extended_json'; import { isObjectLike } from './parser/utils'; +import type { Timestamp } from './timestamp'; interface LongWASMHelpers { /** Gets the high bits of the last operation performed */ @@ -194,7 +194,7 @@ export class Long { * @returns The corresponding Long value */ static fromBigInt(value: bigint, unsigned?: boolean): Long { - return Long.fromString(value.toString(10), unsigned); + return Long.fromString(value.toString(), unsigned); } /** diff --git a/src/parser/serializer.ts b/src/parser/serializer.ts index eff35d2f..b3f7a7d0 100644 --- a/src/parser/serializer.ts +++ b/src/parser/serializer.ts @@ -15,7 +15,13 @@ import { Map } from '../map'; import type { MinKey } from '../min_key'; import type { ObjectId } from '../objectid'; import type { BSONRegExp } from '../regexp'; -import { isBigInt64Array, isBigUInt64Array, isDate, normalizedFunctionString } from './utils'; +import { + isBigInt64Array, + isBigUInt64Array, + isDate, + isUint8Array, + normalizedFunctionString +} from './utils'; export interface SerializeOptions { /** the serializer will check if keys are valid. */ diff --git a/src/parser/utils.ts b/src/parser/utils.ts index 5a2c97ba..00befef8 100644 --- a/src/parser/utils.ts +++ b/src/parser/utils.ts @@ -18,6 +18,8 @@ function insecureRandomBytes(size: number): Uint8Array { // eslint-disable-next-line @typescript-eslint/no-explicit-any declare let window: any; declare let require: Function; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +declare let global: any; export let randomBytes = insecureRandomBytes; if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) { diff --git a/tsconfig.json b/tsconfig.json index 020252b3..4752a1f9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,9 @@ "module": "commonjs", "moduleResolution": "node", "lib": [ - "ES2017" + "ES2017", + "ES2020.BigInt", + "ES2017.TypedArrays" ], "outDir": "lib", // We don't make use of tslib helpers