bson 5.0.0-alpha.1 → 5.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/long.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { BSON_MAJOR_VERSION } from './constants';
1
+ import { BSONValue } from './bson_value';
2
+ import { BSONError } from './error';
2
3
  import type { EJSONOptions } from './extended_json';
3
4
  import type { Timestamp } from './timestamp';
4
5
 
@@ -75,6 +76,10 @@ const INT_CACHE: { [key: number]: Long } = {};
75
76
  /** A cache of the Long representations of small unsigned integer values. */
76
77
  const UINT_CACHE: { [key: number]: Long } = {};
77
78
 
79
+ const MAX_INT64_STRING_LENGTH = 20;
80
+
81
+ const DECIMAL_REG_EX = /^(\+?0|(\+|-)?[1-9][0-9]*)$/;
82
+
78
83
  /** @public */
79
84
  export interface LongExtended {
80
85
  $numberLong: string;
@@ -99,14 +104,10 @@ export interface LongExtended {
99
104
  * case would often result in infinite recursion.
100
105
  * Common constant values ZERO, ONE, NEG_ONE, etc. are found as static properties on this class.
101
106
  */
102
- export class Long {
107
+ export class Long extends BSONValue {
103
108
  get _bsontype(): 'Long' {
104
109
  return 'Long';
105
110
  }
106
- /** @internal */
107
- get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
108
- return BSON_MAJOR_VERSION;
109
- }
110
111
 
111
112
  /** An indicator used to reliably determine if an object is a Long or not. */
112
113
  get __isLong__(): boolean {
@@ -142,6 +143,7 @@ export class Long {
142
143
  * @param unsigned - Whether unsigned or not, defaults to signed
143
144
  */
144
145
  constructor(low: number | bigint | string = 0, high?: number | boolean, unsigned?: boolean) {
146
+ super();
145
147
  if (typeof low === 'bigint') {
146
148
  Object.assign(this, Long.fromBigInt(low, !!high));
147
149
  } else if (typeof low === 'string') {
@@ -250,7 +252,7 @@ export class Long {
250
252
  * @returns The corresponding Long value
251
253
  */
252
254
  static fromString(str: string, unsigned?: boolean, radix?: number): Long {
253
- if (str.length === 0) throw Error('empty string');
255
+ if (str.length === 0) throw new BSONError('empty string');
254
256
  if (str === 'NaN' || str === 'Infinity' || str === '+Infinity' || str === '-Infinity')
255
257
  return Long.ZERO;
256
258
  if (typeof unsigned === 'number') {
@@ -260,10 +262,10 @@ export class Long {
260
262
  unsigned = !!unsigned;
261
263
  }
262
264
  radix = radix || 10;
263
- if (radix < 2 || 36 < radix) throw RangeError('radix');
265
+ if (radix < 2 || 36 < radix) throw new BSONError('radix');
264
266
 
265
267
  let p;
266
- if ((p = str.indexOf('-')) > 0) throw Error('interior hyphen');
268
+ if ((p = str.indexOf('-')) > 0) throw new BSONError('interior hyphen');
267
269
  else if (p === 0) {
268
270
  return Long.fromString(str.substring(1), unsigned, radix).neg();
269
271
  }
@@ -431,7 +433,7 @@ export class Long {
431
433
  */
432
434
  divide(divisor: string | number | Long | Timestamp): Long {
433
435
  if (!Long.isLong(divisor)) divisor = Long.fromValue(divisor);
434
- if (divisor.isZero()) throw Error('division by zero');
436
+ if (divisor.isZero()) throw new BSONError('division by zero');
435
437
 
436
438
  // use wasm support if present
437
439
  if (wasm) {
@@ -959,7 +961,7 @@ export class Long {
959
961
  */
960
962
  toString(radix?: number): string {
961
963
  radix = radix || 10;
962
- if (radix < 2 || 36 < radix) throw RangeError('radix');
964
+ if (radix < 2 || 36 < radix) throw new BSONError('radix');
963
965
  if (this.isZero()) return '0';
964
966
  if (this.isNegative()) {
965
967
  // Unsigned Longs are never negative
@@ -1025,9 +1027,30 @@ export class Long {
1025
1027
  if (options && options.relaxed) return this.toNumber();
1026
1028
  return { $numberLong: this.toString() };
1027
1029
  }
1028
- static fromExtendedJSON(doc: { $numberLong: string }, options?: EJSONOptions): number | Long {
1029
- const result = Long.fromString(doc.$numberLong);
1030
- return options && options.relaxed ? result.toNumber() : result;
1030
+ static fromExtendedJSON(
1031
+ doc: { $numberLong: string },
1032
+ options?: EJSONOptions
1033
+ ): number | Long | bigint {
1034
+ const { useBigInt64 = false, relaxed = true } = { ...options };
1035
+
1036
+ if (doc.$numberLong.length > MAX_INT64_STRING_LENGTH) {
1037
+ throw new BSONError('$numberLong string is too long');
1038
+ }
1039
+
1040
+ if (!DECIMAL_REG_EX.test(doc.$numberLong)) {
1041
+ throw new BSONError(`$numberLong string "${doc.$numberLong}" is in an invalid format`);
1042
+ }
1043
+
1044
+ if (useBigInt64) {
1045
+ const bigIntResult = BigInt(doc.$numberLong);
1046
+ return BigInt.asIntN(64, bigIntResult);
1047
+ }
1048
+
1049
+ const longResult = Long.fromString(doc.$numberLong);
1050
+ if (relaxed) {
1051
+ return longResult.toNumber();
1052
+ }
1053
+ return longResult;
1031
1054
  }
1032
1055
 
1033
1056
  /** @internal */
package/src/max_key.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BSON_MAJOR_VERSION } from './constants';
1
+ import { BSONValue } from './bson_value';
2
2
 
3
3
  /** @public */
4
4
  export interface MaxKeyExtended {
@@ -10,14 +10,10 @@ export interface MaxKeyExtended {
10
10
  * @public
11
11
  * @category BSONType
12
12
  */
13
- export class MaxKey {
13
+ export class MaxKey extends BSONValue {
14
14
  get _bsontype(): 'MaxKey' {
15
15
  return 'MaxKey';
16
16
  }
17
- /** @internal */
18
- get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
19
- return BSON_MAJOR_VERSION;
20
- }
21
17
 
22
18
  /** @internal */
23
19
  toExtendedJSON(): MaxKeyExtended {
package/src/min_key.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BSON_MAJOR_VERSION } from './constants';
1
+ import { BSONValue } from './bson_value';
2
2
 
3
3
  /** @public */
4
4
  export interface MinKeyExtended {
@@ -10,14 +10,10 @@ export interface MinKeyExtended {
10
10
  * @public
11
11
  * @category BSONType
12
12
  */
13
- export class MinKey {
13
+ export class MinKey extends BSONValue {
14
14
  get _bsontype(): 'MinKey' {
15
15
  return 'MinKey';
16
16
  }
17
- /** @internal */
18
- get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
19
- return BSON_MAJOR_VERSION;
20
- }
21
17
 
22
18
  /** @internal */
23
19
  toExtendedJSON(): MinKeyExtended {
package/src/objectid.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { BSON_MAJOR_VERSION } from './constants';
2
- import { BSONTypeError } from './error';
1
+ import { BSONValue } from './bson_value';
2
+ import { BSONError } from './error';
3
3
  import { isUint8Array } from './parser/utils';
4
4
  import { BSONDataView, ByteUtils } from './utils/byte_utils';
5
5
 
@@ -28,14 +28,10 @@ const kId = Symbol('id');
28
28
  * @public
29
29
  * @category BSONType
30
30
  */
31
- export class ObjectId {
31
+ export class ObjectId extends BSONValue {
32
32
  get _bsontype(): 'ObjectId' {
33
33
  return 'ObjectId';
34
34
  }
35
- /** @internal */
36
- get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
37
- return BSON_MAJOR_VERSION;
38
- }
39
35
 
40
36
  /** @internal */
41
37
  private static index = Math.floor(Math.random() * 0xffffff);
@@ -53,13 +49,12 @@ export class ObjectId {
53
49
  * @param inputId - Can be a 24 character hex string, 12 byte binary Buffer, or a number.
54
50
  */
55
51
  constructor(inputId?: string | number | ObjectId | ObjectIdLike | Uint8Array) {
52
+ super();
56
53
  // workingId is set based on type of input and whether valid id exists for the input
57
54
  let workingId;
58
55
  if (typeof inputId === 'object' && inputId && 'id' in inputId) {
59
56
  if (typeof inputId.id !== 'string' && !ArrayBuffer.isView(inputId.id)) {
60
- throw new BSONTypeError(
61
- 'Argument passed in must have an id that is of type string or Buffer'
62
- );
57
+ throw new BSONError('Argument passed in must have an id that is of type string or Buffer');
63
58
  }
64
59
  if ('toHexString' in inputId && typeof inputId.toHexString === 'function') {
65
60
  workingId = ByteUtils.fromHex(inputId.toHexString());
@@ -85,17 +80,17 @@ export class ObjectId {
85
80
  if (bytes.byteLength === 12) {
86
81
  this[kId] = bytes;
87
82
  } else {
88
- throw new BSONTypeError('Argument passed in must be a string of 12 bytes');
83
+ throw new BSONError('Argument passed in must be a string of 12 bytes');
89
84
  }
90
85
  } else if (workingId.length === 24 && checkForHexRegExp.test(workingId)) {
91
86
  this[kId] = ByteUtils.fromHex(workingId);
92
87
  } else {
93
- throw new BSONTypeError(
88
+ throw new BSONError(
94
89
  'Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer'
95
90
  );
96
91
  }
97
92
  } else {
98
- throw new BSONTypeError('Argument passed in does not match the accepted types');
93
+ throw new BSONError('Argument passed in does not match the accepted types');
99
94
  }
100
95
  // If we are caching the hex string
101
96
  if (ObjectId.cacheHexString) {
@@ -271,7 +266,7 @@ export class ObjectId {
271
266
  static createFromHexString(hexString: string): ObjectId {
272
267
  // Throw an error if it's not a valid setup
273
268
  if (typeof hexString === 'undefined' || (hexString != null && hexString.length !== 24)) {
274
- throw new BSONTypeError(
269
+ throw new BSONError(
275
270
  'Argument passed in must be a single String of 12 bytes or a string of 24 hex characters'
276
271
  );
277
272
  }
@@ -1,5 +1,6 @@
1
1
  import { Binary } from '../binary';
2
2
  import type { Document } from '../bson';
3
+ import { BSONVersionError } from '../error';
3
4
  import * as constants from '../constants';
4
5
  import { ByteUtils } from '../utils/byte_utils';
5
6
  import { isAnyArrayBuffer, isDate, isRegExp } from './utils';
@@ -77,9 +78,15 @@ function calculateElement(
77
78
  case 'boolean':
78
79
  return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1);
79
80
  case 'object':
80
- if (value == null || value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') {
81
+ if (
82
+ value != null &&
83
+ typeof value._bsontype === 'string' &&
84
+ value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
85
+ ) {
86
+ throw new BSONVersionError();
87
+ } else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
81
88
  return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
82
- } else if (value['_bsontype'] === 'ObjectId') {
89
+ } else if (value._bsontype === 'ObjectId') {
83
90
  return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1);
84
91
  } else if (value instanceof Date || isDate(value)) {
85
92
  return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
@@ -92,14 +99,14 @@ function calculateElement(
92
99
  (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength
93
100
  );
94
101
  } else if (
95
- value['_bsontype'] === 'Long' ||
96
- value['_bsontype'] === 'Double' ||
97
- value['_bsontype'] === 'Timestamp'
102
+ value._bsontype === 'Long' ||
103
+ value._bsontype === 'Double' ||
104
+ value._bsontype === 'Timestamp'
98
105
  ) {
99
106
  return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
100
- } else if (value['_bsontype'] === 'Decimal128') {
107
+ } else if (value._bsontype === 'Decimal128') {
101
108
  return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1);
102
- } else if (value['_bsontype'] === 'Code') {
109
+ } else if (value._bsontype === 'Code') {
103
110
  // Calculate size depending on the availability of a scope
104
111
  if (value.scope != null && Object.keys(value.scope).length > 0) {
105
112
  return (
@@ -120,7 +127,7 @@ function calculateElement(
120
127
  1
121
128
  );
122
129
  }
123
- } else if (value['_bsontype'] === 'Binary') {
130
+ } else if (value._bsontype === 'Binary') {
124
131
  const binary: Binary = value;
125
132
  // Check what kind of subtype we have
126
133
  if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
@@ -133,7 +140,7 @@ function calculateElement(
133
140
  (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1)
134
141
  );
135
142
  }
136
- } else if (value['_bsontype'] === 'Symbol') {
143
+ } else if (value._bsontype === 'Symbol') {
137
144
  return (
138
145
  (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
139
146
  ByteUtils.utf8ByteLength(value.value) +
@@ -141,7 +148,7 @@ function calculateElement(
141
148
  1 +
142
149
  1
143
150
  );
144
- } else if (value['_bsontype'] === 'DBRef') {
151
+ } else if (value._bsontype === 'DBRef') {
145
152
  // Set up correct object for serialization
146
153
  const ordered_values = Object.assign(
147
154
  {
@@ -172,7 +179,7 @@ function calculateElement(
172
179
  (value.multiline ? 1 : 0) +
173
180
  1
174
181
  );
175
- } else if (value['_bsontype'] === 'BSONRegExp') {
182
+ } else if (value._bsontype === 'BSONRegExp') {
176
183
  return (
177
184
  (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
178
185
  1 +
@@ -14,12 +14,14 @@ import { ObjectId } from '../objectid';
14
14
  import { BSONRegExp } from '../regexp';
15
15
  import { BSONSymbol } from '../symbol';
16
16
  import { Timestamp } from '../timestamp';
17
- import { ByteUtils } from '../utils/byte_utils';
17
+ import { BSONDataView, ByteUtils } from '../utils/byte_utils';
18
18
  import { validateUtf8 } from '../validate_utf8';
19
19
 
20
20
  /** @public */
21
21
  export interface DeserializeOptions {
22
- /** when deserializing a Long will fit it into a Number if it's smaller than 53 bits */
22
+ /** when deserializing a Long will return as a BigInt. */
23
+ useBigInt64?: boolean;
24
+ /** when deserializing a Long will fit it into a Number if it's smaller than 53 bits. */
23
25
  promoteLongs?: boolean;
24
26
  /** when deserializing a Binary will return it as a node.js Buffer instance. */
25
27
  promoteBuffers?: boolean;
@@ -29,7 +31,7 @@ export interface DeserializeOptions {
29
31
  fieldsAsRaw?: Document;
30
32
  /** return BSON regular expressions as BSONRegExp instances. */
31
33
  bsonRegExp?: boolean;
32
- /** allows the buffer to be larger than the parsed BSON object */
34
+ /** allows the buffer to be larger than the parsed BSON object. */
33
35
  allowObjectSmallerThanBufferSize?: boolean;
34
36
  /** Offset into buffer to begin reading document from */
35
37
  index?: number;
@@ -96,7 +98,7 @@ export function internalDeserialize(
96
98
  );
97
99
  }
98
100
 
99
- // Start deserializtion
101
+ // Start deserialization
100
102
  return deserializeObject(buffer, index, options, isArray);
101
103
  }
102
104
 
@@ -117,9 +119,18 @@ function deserializeObject(
117
119
  const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
118
120
 
119
121
  // Controls the promotion of values vs wrapper classes
120
- const promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers'];
121
- const promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
122
- const promoteValues = options['promoteValues'] == null ? true : options['promoteValues'];
122
+ const promoteBuffers = options.promoteBuffers ?? false;
123
+ const promoteLongs = options.promoteLongs ?? true;
124
+ const promoteValues = options.promoteValues ?? true;
125
+ const useBigInt64 = options.useBigInt64 ?? false;
126
+
127
+ if (useBigInt64 && !promoteValues) {
128
+ throw new BSONError('Must either request bigint or Long for int64 deserialization');
129
+ }
130
+
131
+ if (useBigInt64 && !promoteLongs) {
132
+ throw new BSONError('Must either request bigint or Long for int64 deserialization');
133
+ }
123
134
 
124
135
  // Ensures default validation option if none given
125
136
  const validation = options.validation == null ? { utf8: true } : options.validation;
@@ -323,6 +334,8 @@ function deserializeObject(
323
334
  value = null;
324
335
  } else if (elementType === constants.BSON_DATA_LONG) {
325
336
  // Unpack the low and high bits
337
+ const dataview = BSONDataView.fromUint8Array(buffer.subarray(index, index + 8));
338
+
326
339
  const lowBits =
327
340
  buffer[index++] |
328
341
  (buffer[index++] << 8) |
@@ -334,8 +347,10 @@ function deserializeObject(
334
347
  (buffer[index++] << 16) |
335
348
  (buffer[index++] << 24);
336
349
  const long = new Long(lowBits, highBits);
337
- // Promote the long if possible
338
- if (promoteLongs && promoteValues === true) {
350
+ if (useBigInt64) {
351
+ value = dataview.getBigInt64(0, true);
352
+ } else if (promoteLongs && promoteValues === true) {
353
+ // Promote the long if possible
339
354
  value =
340
355
  long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
341
356
  ? long.toNumber()
@@ -5,22 +5,14 @@ import * as constants from '../constants';
5
5
  import type { DBRefLike } from '../db_ref';
6
6
  import type { Decimal128 } from '../decimal128';
7
7
  import type { Double } from '../double';
8
- import { BSONError, BSONTypeError } from '../error';
8
+ import { BSONError, BSONVersionError } from '../error';
9
9
  import type { Int32 } from '../int_32';
10
10
  import { Long } from '../long';
11
11
  import type { MinKey } from '../min_key';
12
12
  import type { ObjectId } from '../objectid';
13
13
  import type { BSONRegExp } from '../regexp';
14
14
  import { ByteUtils } from '../utils/byte_utils';
15
- import {
16
- isAnyArrayBuffer,
17
- isBigInt64Array,
18
- isBigUInt64Array,
19
- isDate,
20
- isMap,
21
- isRegExp,
22
- isUint8Array
23
- } from './utils';
15
+ import { isAnyArrayBuffer, isDate, isMap, isRegExp, isUint8Array } from './utils';
24
16
 
25
17
  /** @public */
26
18
  export interface SerializeOptions {
@@ -103,6 +95,20 @@ function serializeNumber(buffer: Uint8Array, key: string, value: number, index:
103
95
  return index;
104
96
  }
105
97
 
98
+ function serializeBigInt(buffer: Uint8Array, key: string, value: bigint, index: number) {
99
+ buffer[index++] = constants.BSON_DATA_LONG;
100
+ // Number of written bytes
101
+ const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
102
+ // Encode the name
103
+ index += numberOfWrittenBytes;
104
+ buffer[index++] = 0;
105
+ NUMBER_SPACE.setBigInt64(0, value, true);
106
+ // Write BigInt value
107
+ buffer.set(EIGHT_BYTE_VIEW_ON_NUMBER, index);
108
+ index += EIGHT_BYTE_VIEW_ON_NUMBER.byteLength;
109
+ return index;
110
+ }
111
+
106
112
  function serializeNull(buffer: Uint8Array, key: string, _: unknown, index: number) {
107
113
  // Set long type
108
114
  buffer[index++] = constants.BSON_DATA_NULL;
@@ -165,7 +171,7 @@ function serializeRegExp(buffer: Uint8Array, key: string, value: RegExp, index:
165
171
  index = index + numberOfWrittenBytes;
166
172
  buffer[index++] = 0;
167
173
  if (value.source && value.source.match(regexp) != null) {
168
- throw Error('value ' + value.source + ' must not contain null bytes');
174
+ throw new BSONError('value ' + value.source + ' must not contain null bytes');
169
175
  }
170
176
  // Adjust the index
171
177
  index = index + ByteUtils.encodeUTF8Into(buffer, value.source, index);
@@ -194,7 +200,7 @@ function serializeBSONRegExp(buffer: Uint8Array, key: string, value: BSONRegExp,
194
200
  if (value.pattern.match(regexp) != null) {
195
201
  // The BSON spec doesn't allow keys with null bytes because keys are
196
202
  // null-terminated.
197
- throw Error('pattern ' + value.pattern + ' must not contain null bytes');
203
+ throw new BSONError('pattern ' + value.pattern + ' must not contain null bytes');
198
204
  }
199
205
 
200
206
  // Adjust the index
@@ -241,7 +247,7 @@ function serializeObjectId(buffer: Uint8Array, key: string, value: ObjectId, ind
241
247
  if (isUint8Array(value.id)) {
242
248
  buffer.set(value.id.subarray(0, 12), index);
243
249
  } else {
244
- throw new BSONTypeError('object [' + JSON.stringify(value) + '] is not a valid ObjectId');
250
+ throw new BSONError('object [' + JSON.stringify(value) + '] is not a valid ObjectId');
245
251
  }
246
252
 
247
253
  // Adjust index
@@ -675,7 +681,7 @@ export function serializeInto(
675
681
  } else if (typeof value === 'number') {
676
682
  index = serializeNumber(buffer, key, value, index);
677
683
  } else if (typeof value === 'bigint') {
678
- throw new BSONTypeError('Unsupported type BigInt, please use Decimal128');
684
+ index = serializeBigInt(buffer, key, value, index);
679
685
  } else if (typeof value === 'boolean') {
680
686
  index = serializeBoolean(buffer, key, value, index);
681
687
  } else if (value instanceof Date || isDate(value)) {
@@ -700,8 +706,11 @@ export function serializeInto(
700
706
  ignoreUndefined,
701
707
  path
702
708
  );
703
- } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) {
704
- throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later');
709
+ } else if (
710
+ typeof value === 'object' &&
711
+ value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
712
+ ) {
713
+ throw new BSONVersionError();
705
714
  } else if (value._bsontype === 'ObjectId') {
706
715
  index = serializeObjectId(buffer, key, value, index);
707
716
  } else if (value._bsontype === 'Decimal128') {
@@ -737,7 +746,7 @@ export function serializeInto(
737
746
  } else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
738
747
  index = serializeMinMax(buffer, key, value, index);
739
748
  } else if (typeof value._bsontype !== 'undefined') {
740
- throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
749
+ throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
741
750
  }
742
751
  }
743
752
  } else if (object instanceof Map || isMap(object)) {
@@ -753,7 +762,11 @@ export function serializeInto(
753
762
 
754
763
  // Get the entry values
755
764
  const key = entry.value[0];
756
- const value = entry.value[1];
765
+ let value = entry.value[1];
766
+
767
+ if (typeof value?.toBSON === 'function') {
768
+ value = value.toBSON();
769
+ }
757
770
 
758
771
  // Check the type of the value
759
772
  const type = typeof value;
@@ -763,14 +776,14 @@ export function serializeInto(
763
776
  if (key.match(regexp) != null) {
764
777
  // The BSON spec doesn't allow keys with null bytes because keys are
765
778
  // null-terminated.
766
- throw Error('key ' + key + ' must not contain null bytes');
779
+ throw new BSONError('key ' + key + ' must not contain null bytes');
767
780
  }
768
781
 
769
782
  if (checkKeys) {
770
783
  if ('$' === key[0]) {
771
- throw Error('key ' + key + " must not start with '$'");
784
+ throw new BSONError('key ' + key + " must not start with '$'");
772
785
  } else if (~key.indexOf('.')) {
773
- throw Error('key ' + key + " must not contain '.'");
786
+ throw new BSONError('key ' + key + " must not contain '.'");
774
787
  }
775
788
  }
776
789
  }
@@ -779,8 +792,8 @@ export function serializeInto(
779
792
  index = serializeString(buffer, key, value, index);
780
793
  } else if (type === 'number') {
781
794
  index = serializeNumber(buffer, key, value, index);
782
- } else if (type === 'bigint' || isBigInt64Array(value) || isBigUInt64Array(value)) {
783
- throw new BSONTypeError('Unsupported type BigInt, please use Decimal128');
795
+ } else if (type === 'bigint') {
796
+ index = serializeBigInt(buffer, key, value, index);
784
797
  } else if (type === 'boolean') {
785
798
  index = serializeBoolean(buffer, key, value, index);
786
799
  } else if (value instanceof Date || isDate(value)) {
@@ -803,8 +816,11 @@ export function serializeInto(
803
816
  ignoreUndefined,
804
817
  path
805
818
  );
806
- } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) {
807
- throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later');
819
+ } else if (
820
+ typeof value === 'object' &&
821
+ value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
822
+ ) {
823
+ throw new BSONVersionError();
808
824
  } else if (value._bsontype === 'ObjectId') {
809
825
  index = serializeObjectId(buffer, key, value, index);
810
826
  } else if (type === 'object' && value._bsontype === 'Decimal128') {
@@ -840,7 +856,7 @@ export function serializeInto(
840
856
  } else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
841
857
  index = serializeMinMax(buffer, key, value, index);
842
858
  } else if (typeof value._bsontype !== 'undefined') {
843
- throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
859
+ throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
844
860
  }
845
861
  }
846
862
  } else {
@@ -848,7 +864,7 @@ export function serializeInto(
848
864
  // Provided a custom serialization method
849
865
  object = object.toBSON();
850
866
  if (object != null && typeof object !== 'object') {
851
- throw new BSONTypeError('toBSON function did not return an object');
867
+ throw new BSONError('toBSON function did not return an object');
852
868
  }
853
869
  }
854
870
 
@@ -868,14 +884,14 @@ export function serializeInto(
868
884
  if (key.match(regexp) != null) {
869
885
  // The BSON spec doesn't allow keys with null bytes because keys are
870
886
  // null-terminated.
871
- throw Error('key ' + key + ' must not contain null bytes');
887
+ throw new BSONError('key ' + key + ' must not contain null bytes');
872
888
  }
873
889
 
874
890
  if (checkKeys) {
875
891
  if ('$' === key[0]) {
876
- throw Error('key ' + key + " must not start with '$'");
892
+ throw new BSONError('key ' + key + " must not start with '$'");
877
893
  } else if (~key.indexOf('.')) {
878
- throw Error('key ' + key + " must not contain '.'");
894
+ throw new BSONError('key ' + key + " must not contain '.'");
879
895
  }
880
896
  }
881
897
  }
@@ -885,7 +901,7 @@ export function serializeInto(
885
901
  } else if (type === 'number') {
886
902
  index = serializeNumber(buffer, key, value, index);
887
903
  } else if (type === 'bigint') {
888
- throw new BSONTypeError('Unsupported type BigInt, please use Decimal128');
904
+ index = serializeBigInt(buffer, key, value, index);
889
905
  } else if (type === 'boolean') {
890
906
  index = serializeBoolean(buffer, key, value, index);
891
907
  } else if (value instanceof Date || isDate(value)) {
@@ -910,8 +926,11 @@ export function serializeInto(
910
926
  ignoreUndefined,
911
927
  path
912
928
  );
913
- } else if (typeof value === 'object' && value[Symbol.for('@@mdb.bson.version')] == null) {
914
- throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later');
929
+ } else if (
930
+ typeof value === 'object' &&
931
+ value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
932
+ ) {
933
+ throw new BSONVersionError();
915
934
  } else if (value._bsontype === 'ObjectId') {
916
935
  index = serializeObjectId(buffer, key, value, index);
917
936
  } else if (type === 'object' && value._bsontype === 'Decimal128') {
@@ -947,7 +966,7 @@ export function serializeInto(
947
966
  } else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
948
967
  index = serializeMinMax(buffer, key, value, index);
949
968
  } else if (typeof value._bsontype !== 'undefined') {
950
- throw new BSONTypeError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
969
+ throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
951
970
  }
952
971
  }
953
972
  }
package/src/regexp.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { BSON_MAJOR_VERSION } from './constants';
2
- import { BSONError, BSONTypeError } from './error';
1
+ import { BSONValue } from './bson_value';
2
+ import { BSONError } from './error';
3
3
  import type { EJSONOptions } from './extended_json';
4
4
 
5
5
  function alphabetize(str: string): string {
@@ -25,14 +25,10 @@ export interface BSONRegExpExtended {
25
25
  * @public
26
26
  * @category BSONType
27
27
  */
28
- export class BSONRegExp {
28
+ export class BSONRegExp extends BSONValue {
29
29
  get _bsontype(): 'BSONRegExp' {
30
30
  return 'BSONRegExp';
31
31
  }
32
- /** @internal */
33
- get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
34
- return BSON_MAJOR_VERSION;
35
- }
36
32
 
37
33
  pattern!: string;
38
34
  options!: string;
@@ -41,6 +37,7 @@ export class BSONRegExp {
41
37
  * @param options - The regular expression options
42
38
  */
43
39
  constructor(pattern: string, options?: string) {
40
+ super();
44
41
  this.pattern = pattern;
45
42
  this.options = alphabetize(options ?? '');
46
43
 
@@ -103,7 +100,7 @@ export class BSONRegExp {
103
100
  BSONRegExp.parseOptions(doc.$regularExpression.options)
104
101
  );
105
102
  }
106
- throw new BSONTypeError(`Unexpected BSONRegExp EJSON object form: ${JSON.stringify(doc)}`);
103
+ throw new BSONError(`Unexpected BSONRegExp EJSON object form: ${JSON.stringify(doc)}`);
107
104
  }
108
105
 
109
106
  /** @internal */