bson 4.7.0 → 5.0.0-alpha.1

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.
Files changed (98) hide show
  1. package/bson.d.ts +207 -259
  2. package/lib/bson.bundle.js +4034 -0
  3. package/lib/bson.bundle.js.map +1 -0
  4. package/lib/bson.cjs +4029 -0
  5. package/lib/bson.cjs.map +1 -0
  6. package/lib/bson.mjs +4003 -0
  7. package/lib/bson.mjs.map +1 -0
  8. package/package.json +49 -62
  9. package/src/binary.ts +64 -53
  10. package/src/bson.ts +27 -108
  11. package/src/code.ts +25 -14
  12. package/src/constants.ts +33 -0
  13. package/src/db_ref.ts +14 -8
  14. package/src/decimal128.ts +32 -25
  15. package/src/double.ts +8 -5
  16. package/src/error.ts +0 -2
  17. package/src/extended_json.ts +148 -148
  18. package/src/index.ts +19 -0
  19. package/src/int_32.ts +8 -5
  20. package/src/long.ts +17 -16
  21. package/src/max_key.ts +8 -6
  22. package/src/min_key.ts +8 -6
  23. package/src/objectid.ts +42 -74
  24. package/src/parser/calculate_size.ts +39 -63
  25. package/src/parser/deserializer.ts +41 -112
  26. package/src/parser/serializer.ts +234 -341
  27. package/src/parser/utils.ts +1 -99
  28. package/src/regexp.ts +17 -5
  29. package/src/symbol.ts +9 -5
  30. package/src/timestamp.ts +63 -27
  31. package/src/utils/byte_utils.ts +61 -0
  32. package/src/utils/node_byte_utils.ts +141 -0
  33. package/src/utils/web_byte_utils.ts +190 -0
  34. package/src/uuid_utils.ts +15 -15
  35. package/bower.json +0 -26
  36. package/dist/bson.browser.esm.js +0 -7470
  37. package/dist/bson.browser.esm.js.map +0 -1
  38. package/dist/bson.browser.umd.js +0 -7537
  39. package/dist/bson.browser.umd.js.map +0 -1
  40. package/dist/bson.bundle.js +0 -7536
  41. package/dist/bson.bundle.js.map +0 -1
  42. package/dist/bson.esm.js +0 -5436
  43. package/dist/bson.esm.js.map +0 -1
  44. package/lib/binary.js +0 -426
  45. package/lib/binary.js.map +0 -1
  46. package/lib/bson.js +0 -251
  47. package/lib/bson.js.map +0 -1
  48. package/lib/code.js +0 -46
  49. package/lib/code.js.map +0 -1
  50. package/lib/constants.js +0 -82
  51. package/lib/constants.js.map +0 -1
  52. package/lib/db_ref.js +0 -97
  53. package/lib/db_ref.js.map +0 -1
  54. package/lib/decimal128.js +0 -669
  55. package/lib/decimal128.js.map +0 -1
  56. package/lib/double.js +0 -76
  57. package/lib/double.js.map +0 -1
  58. package/lib/ensure_buffer.js +0 -25
  59. package/lib/ensure_buffer.js.map +0 -1
  60. package/lib/error.js +0 -55
  61. package/lib/error.js.map +0 -1
  62. package/lib/extended_json.js +0 -390
  63. package/lib/extended_json.js.map +0 -1
  64. package/lib/int_32.js +0 -58
  65. package/lib/int_32.js.map +0 -1
  66. package/lib/long.js +0 -900
  67. package/lib/long.js.map +0 -1
  68. package/lib/map.js +0 -123
  69. package/lib/map.js.map +0 -1
  70. package/lib/max_key.js +0 -33
  71. package/lib/max_key.js.map +0 -1
  72. package/lib/min_key.js +0 -33
  73. package/lib/min_key.js.map +0 -1
  74. package/lib/objectid.js +0 -299
  75. package/lib/objectid.js.map +0 -1
  76. package/lib/parser/calculate_size.js +0 -194
  77. package/lib/parser/calculate_size.js.map +0 -1
  78. package/lib/parser/deserializer.js +0 -665
  79. package/lib/parser/deserializer.js.map +0 -1
  80. package/lib/parser/serializer.js +0 -867
  81. package/lib/parser/serializer.js.map +0 -1
  82. package/lib/parser/utils.js +0 -115
  83. package/lib/parser/utils.js.map +0 -1
  84. package/lib/regexp.js +0 -74
  85. package/lib/regexp.js.map +0 -1
  86. package/lib/symbol.js +0 -48
  87. package/lib/symbol.js.map +0 -1
  88. package/lib/timestamp.js +0 -102
  89. package/lib/timestamp.js.map +0 -1
  90. package/lib/utils/global.js +0 -18
  91. package/lib/utils/global.js.map +0 -1
  92. package/lib/uuid_utils.js +0 -35
  93. package/lib/uuid_utils.js.map +0 -1
  94. package/lib/validate_utf8.js +0 -47
  95. package/lib/validate_utf8.js.map +0 -1
  96. package/src/ensure_buffer.ts +0 -27
  97. package/src/map.ts +0 -119
  98. package/src/utils/global.ts +0 -22
package/src/db_ref.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Document } from './bson';
2
+ import { BSON_MAJOR_VERSION } from './constants';
2
3
  import type { EJSONOptions } from './extended_json';
3
4
  import type { ObjectId } from './objectid';
4
- import { isObjectLike } from './parser/utils';
5
5
 
6
6
  /** @public */
7
7
  export interface DBRefLike {
@@ -13,10 +13,14 @@ export interface DBRefLike {
13
13
  /** @internal */
14
14
  export function isDBRefLike(value: unknown): value is DBRefLike {
15
15
  return (
16
- isObjectLike(value) &&
16
+ value != null &&
17
+ typeof value === 'object' &&
18
+ '$id' in value &&
17
19
  value.$id != null &&
20
+ '$ref' in value &&
18
21
  typeof value.$ref === 'string' &&
19
- (value.$db == null || typeof value.$db === 'string')
22
+ // If '$db' is defined it MUST be a string, otherwise it should be absent
23
+ (!('$db' in value) || ('$db' in value && typeof value.$db === 'string'))
20
24
  );
21
25
  }
22
26
 
@@ -26,7 +30,13 @@ export function isDBRefLike(value: unknown): value is DBRefLike {
26
30
  * @category BSONType
27
31
  */
28
32
  export class DBRef {
29
- _bsontype!: 'DBRef';
33
+ get _bsontype(): 'DBRef' {
34
+ return 'DBRef';
35
+ }
36
+ /** @internal */
37
+ get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
38
+ return BSON_MAJOR_VERSION;
39
+ }
30
40
 
31
41
  collection!: string;
32
42
  oid!: ObjectId;
@@ -39,8 +49,6 @@ export class DBRef {
39
49
  * @param db - optional db name, if omitted the reference is local to the current db.
40
50
  */
41
51
  constructor(collection: string, oid: ObjectId, db?: string, fields?: Document) {
42
- if (!(this instanceof DBRef)) return new DBRef(collection, oid, db, fields);
43
-
44
52
  // check if namespace has been provided
45
53
  const parts = collection.split('.');
46
54
  if (parts.length === 2) {
@@ -120,5 +128,3 @@ export class DBRef {
120
128
  })`;
121
129
  }
122
130
  }
123
-
124
- Object.defineProperty(DBRef.prototype, '_bsontype', { value: 'DBRef' });
package/src/decimal128.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { Buffer } from 'buffer';
1
+ import { BSON_MAJOR_VERSION } from './constants';
2
2
  import { BSONTypeError } from './error';
3
3
  import { Long } from './long';
4
4
  import { isUint8Array } from './parser/utils';
5
+ import { ByteUtils } from './utils/byte_utils';
5
6
 
6
7
  const PARSE_STRING_REGEXP = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/;
7
8
  const PARSE_INF_REGEXP = /^(\+|-)?(Infinity|inf)$/i;
@@ -13,16 +14,22 @@ const EXPONENT_BIAS = 6176;
13
14
  const MAX_DIGITS = 34;
14
15
 
15
16
  // Nan value bits as 32 bit values (due to lack of longs)
16
- const NAN_BUFFER = [
17
- 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
18
- ].reverse();
17
+ const NAN_BUFFER = ByteUtils.fromNumberArray(
18
+ [
19
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
20
+ ].reverse()
21
+ );
19
22
  // Infinity value bits 32 bit values (due to lack of longs)
20
- const INF_NEGATIVE_BUFFER = [
21
- 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
22
- ].reverse();
23
- const INF_POSITIVE_BUFFER = [
24
- 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
25
- ].reverse();
23
+ const INF_NEGATIVE_BUFFER = ByteUtils.fromNumberArray(
24
+ [
25
+ 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
26
+ ].reverse()
27
+ );
28
+ const INF_POSITIVE_BUFFER = ByteUtils.fromNumberArray(
29
+ [
30
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
31
+ ].reverse()
32
+ );
26
33
 
27
34
  const EXPONENT_REGEX = /^([-+])?(\d+)?$/;
28
35
 
@@ -121,17 +128,21 @@ export interface Decimal128Extended {
121
128
  * @category BSONType
122
129
  */
123
130
  export class Decimal128 {
124
- _bsontype!: 'Decimal128';
131
+ get _bsontype(): 'Decimal128' {
132
+ return 'Decimal128';
133
+ }
134
+ /** @internal */
135
+ get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
136
+ return BSON_MAJOR_VERSION;
137
+ }
125
138
 
126
- readonly bytes!: Buffer;
139
+ readonly bytes!: Uint8Array;
127
140
 
128
141
  /**
129
142
  * @param bytes - a buffer containing the raw Decimal128 bytes in little endian order,
130
143
  * or a string representation as returned by .toString()
131
144
  */
132
- constructor(bytes: Buffer | string) {
133
- if (!(this instanceof Decimal128)) return new Decimal128(bytes);
134
-
145
+ constructor(bytes: Uint8Array | string) {
135
146
  if (typeof bytes === 'string') {
136
147
  this.bytes = Decimal128.fromString(bytes).bytes;
137
148
  } else if (isUint8Array(bytes)) {
@@ -239,9 +250,9 @@ export class Decimal128 {
239
250
  // Check if user passed Infinity or NaN
240
251
  if (!isDigit(representation[index]) && representation[index] !== '.') {
241
252
  if (representation[index] === 'i' || representation[index] === 'I') {
242
- return new Decimal128(Buffer.from(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER));
253
+ return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);
243
254
  } else if (representation[index] === 'N') {
244
- return new Decimal128(Buffer.from(NAN_BUFFER));
255
+ return new Decimal128(NAN_BUFFER);
245
256
  }
246
257
  }
247
258
 
@@ -285,7 +296,7 @@ export class Decimal128 {
285
296
  const match = representation.substr(++index).match(EXPONENT_REGEX);
286
297
 
287
298
  // No digits read
288
- if (!match || !match[2]) return new Decimal128(Buffer.from(NAN_BUFFER));
299
+ if (!match || !match[2]) return new Decimal128(NAN_BUFFER);
289
300
 
290
301
  // Get exponent
291
302
  exponent = parseInt(match[0], 10);
@@ -295,7 +306,7 @@ export class Decimal128 {
295
306
  }
296
307
 
297
308
  // Return not a number
298
- if (representation[index]) return new Decimal128(Buffer.from(NAN_BUFFER));
309
+ if (representation[index]) return new Decimal128(NAN_BUFFER);
299
310
 
300
311
  // Done reading input
301
312
  // Find first non-zero digit in digits
@@ -423,9 +434,7 @@ export class Decimal128 {
423
434
  exponent = exponent + 1;
424
435
  digits[dIdx] = 1;
425
436
  } else {
426
- return new Decimal128(
427
- Buffer.from(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER)
428
- );
437
+ return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);
429
438
  }
430
439
  }
431
440
  }
@@ -503,7 +512,7 @@ export class Decimal128 {
503
512
  }
504
513
 
505
514
  // Encode into a buffer
506
- const buffer = Buffer.alloc(16);
515
+ const buffer = ByteUtils.allocate(16);
507
516
  index = 0;
508
517
 
509
518
  // Encode the low 64 bits of the decimal
@@ -769,5 +778,3 @@ export class Decimal128 {
769
778
  return `new Decimal128("${this.toString()}")`;
770
779
  }
771
780
  }
772
-
773
- Object.defineProperty(Decimal128.prototype, '_bsontype', { value: 'Decimal128' });
package/src/double.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { BSON_MAJOR_VERSION } from './constants';
1
2
  import type { EJSONOptions } from './extended_json';
2
3
 
3
4
  /** @public */
@@ -11,7 +12,13 @@ export interface DoubleExtended {
11
12
  * @category BSONType
12
13
  */
13
14
  export class Double {
14
- _bsontype!: 'Double';
15
+ get _bsontype(): 'Double' {
16
+ return 'Double';
17
+ }
18
+ /** @internal */
19
+ get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
20
+ return BSON_MAJOR_VERSION;
21
+ }
15
22
 
16
23
  value!: number;
17
24
  /**
@@ -20,8 +27,6 @@ export class Double {
20
27
  * @param value - the number we want to represent as a double.
21
28
  */
22
29
  constructor(value: number) {
23
- if (!(this instanceof Double)) return new Double(value);
24
-
25
30
  if ((value as unknown) instanceof Number) {
26
31
  value = value.valueOf();
27
32
  }
@@ -87,5 +92,3 @@ export class Double {
87
92
  return `new Double(${eJSON.$numberDouble})`;
88
93
  }
89
94
  }
90
-
91
- Object.defineProperty(Double.prototype, '_bsontype', { value: 'Double' });
package/src/error.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  export class BSONError extends Error {
3
3
  constructor(message: string) {
4
4
  super(message);
5
- Object.setPrototypeOf(this, BSONError.prototype);
6
5
  }
7
6
 
8
7
  get name(): string {
@@ -14,7 +13,6 @@ export class BSONError extends Error {
14
13
  export class BSONTypeError extends TypeError {
15
14
  constructor(message: string) {
16
15
  super(message);
17
- Object.setPrototypeOf(this, BSONTypeError.prototype);
18
16
  }
19
17
 
20
18
  get name(): string {
@@ -1,6 +1,7 @@
1
1
  import { Binary } from './binary';
2
2
  import type { Document } from './bson';
3
3
  import { Code } from './code';
4
+ import { BSON_INT32_MAX, BSON_INT32_MIN, BSON_INT64_MAX, BSON_INT64_MIN } from './constants';
4
5
  import { DBRef, isDBRefLike } from './db_ref';
5
6
  import { Decimal128 } from './decimal128';
6
7
  import { Double } from './double';
@@ -10,13 +11,18 @@ import { Long } from './long';
10
11
  import { MaxKey } from './max_key';
11
12
  import { MinKey } from './min_key';
12
13
  import { ObjectId } from './objectid';
13
- import { isDate, isObjectLike, isRegExp } from './parser/utils';
14
+ import { isDate, isRegExp } from './parser/utils';
14
15
  import { BSONRegExp } from './regexp';
15
16
  import { BSONSymbol } from './symbol';
16
17
  import { Timestamp } from './timestamp';
17
18
 
18
19
  /** @public */
19
- export type EJSONOptions = EJSON.Options;
20
+ export type EJSONOptions = {
21
+ /** Output using the Extended JSON v1 spec */
22
+ legacy?: boolean;
23
+ /** Enable Extended JSON's `relaxed` mode, which attempts to return native JS types where possible, rather than BSON types */
24
+ relaxed?: boolean;
25
+ };
20
26
 
21
27
  /** @internal */
22
28
  type BSONType =
@@ -34,20 +40,15 @@ type BSONType =
34
40
  | BSONSymbol
35
41
  | Timestamp;
36
42
 
37
- export function isBSONType(value: unknown): value is BSONType {
43
+ function isBSONType(value: unknown): value is BSONType {
38
44
  return (
39
- isObjectLike(value) && Reflect.has(value, '_bsontype') && typeof value._bsontype === 'string'
45
+ value != null &&
46
+ typeof value === 'object' &&
47
+ '_bsontype' in value &&
48
+ typeof value._bsontype === 'string'
40
49
  );
41
50
  }
42
51
 
43
- // INT32 boundaries
44
- const BSON_INT32_MAX = 0x7fffffff;
45
- const BSON_INT32_MIN = -0x80000000;
46
- // INT64 boundaries
47
- // const BSON_INT64_MAX = 0x7fffffffffffffff; // TODO(NODE-4377): This number cannot be precisely represented in JS
48
- const BSON_INT64_MAX = 0x8000000000000000;
49
- const BSON_INT64_MIN = -0x8000000000000000;
50
-
51
52
  // all the types where we don't need to do any special processing and can just pass the EJSON
52
53
  //straight to type.fromExtendedJSON
53
54
  const keysToCodecs = {
@@ -67,17 +68,21 @@ const keysToCodecs = {
67
68
  } as const;
68
69
 
69
70
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
- function deserializeValue(value: any, options: EJSON.Options = {}) {
71
+ function deserializeValue(value: any, options: EJSONOptions = {}) {
71
72
  if (typeof value === 'number') {
72
73
  if (options.relaxed || options.legacy) {
73
74
  return value;
74
75
  }
75
76
 
76
- // if it's an integer, should interpret as smallest BSON integer
77
- // that can represent it exactly. (if out of range, interpret as double.)
78
- if (Math.floor(value) === value) {
79
- if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) return new Int32(value);
80
- if (value >= BSON_INT64_MIN && value <= BSON_INT64_MAX) return Long.fromNumber(value);
77
+ if (Number.isInteger(value) && !Object.is(value, -0)) {
78
+ // interpret as being of the smallest BSON integer type that can represent the number exactly
79
+ if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
80
+ return new Int32(value);
81
+ }
82
+ if (value >= BSON_INT64_MIN && value <= BSON_INT64_MAX) {
83
+ // TODO(NODE-4377): EJSON js number handling diverges from BSON
84
+ return Long.fromNumber(value);
85
+ }
81
86
  }
82
87
 
83
88
  // If the number is a non-integer or out of integer range, should interpret as BSON Double.
@@ -142,7 +147,7 @@ function deserializeValue(value: any, options: EJSON.Options = {}) {
142
147
  return value;
143
148
  }
144
149
 
145
- type EJSONSerializeOptions = EJSON.Options & {
150
+ type EJSONSerializeOptions = EJSONOptions & {
146
151
  seenObjects: { obj: unknown; propertyName: string }[];
147
152
  };
148
153
 
@@ -216,16 +221,17 @@ function serializeValue(value: any, options: EJSONSerializeOptions): any {
216
221
  }
217
222
 
218
223
  if (typeof value === 'number' && (!options.relaxed || !isFinite(value))) {
219
- // it's an integer
220
- if (Math.floor(value) === value) {
221
- const int32Range = value >= BSON_INT32_MIN && value <= BSON_INT32_MAX,
222
- int64Range = value >= BSON_INT64_MIN && value <= BSON_INT64_MAX;
223
-
224
+ if (Number.isInteger(value) && !Object.is(value, -0)) {
224
225
  // interpret as being of the smallest BSON integer type that can represent the number exactly
225
- if (int32Range) return { $numberInt: value.toString() };
226
- if (int64Range) return { $numberLong: value.toString() };
226
+ if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
227
+ return { $numberInt: value.toString() };
228
+ }
229
+ if (value >= BSON_INT64_MIN && value <= BSON_INT64_MAX) {
230
+ // TODO(NODE-4377): EJSON js number handling diverges from BSON
231
+ return { $numberLong: value.toString() };
232
+ }
227
233
  }
228
- return { $numberDouble: value.toString() };
234
+ return { $numberDouble: Object.is(value, -0) ? '-0.0' : value.toString() };
229
235
  }
230
236
 
231
237
  if (value instanceof RegExp || isRegExp(value)) {
@@ -267,10 +273,9 @@ const BSON_TYPE_MAPPINGS = {
267
273
  ),
268
274
  MaxKey: () => new MaxKey(),
269
275
  MinKey: () => new MinKey(),
270
- ObjectID: (o: ObjectId) => new ObjectId(o),
271
- ObjectId: (o: ObjectId) => new ObjectId(o), // support 4.0.0/4.0.1 before _bsontype was reverted back to ObjectID
276
+ ObjectId: (o: ObjectId) => new ObjectId(o),
272
277
  BSONRegExp: (o: BSONRegExp) => new BSONRegExp(o.pattern, o.options),
273
- Symbol: (o: BSONSymbol) => new BSONSymbol(o.value),
278
+ BSONSymbol: (o: BSONSymbol) => new BSONSymbol(o.value),
274
279
  Timestamp: (o: Timestamp) => Timestamp.fromBits(o.low, o.high)
275
280
  } as const;
276
281
 
@@ -282,7 +287,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
282
287
  if (typeof bsontype === 'undefined') {
283
288
  // It's a regular object. Recursively serialize its property values.
284
289
  const _doc: Document = {};
285
- for (const name in doc) {
290
+ for (const name of Object.keys(doc)) {
286
291
  options.seenObjects.push({ propertyName: name, obj: null });
287
292
  try {
288
293
  const value = serializeValue(doc[name], options);
@@ -301,6 +306,13 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
301
306
  }
302
307
  }
303
308
  return _doc;
309
+ } else if (
310
+ doc != null &&
311
+ typeof doc === 'object' &&
312
+ typeof doc._bsontype === 'string' &&
313
+ doc[Symbol.for('@@mdb.bson.version')] == null
314
+ ) {
315
+ throw new BSONError('Unsupported BSON version, bson types must be from bson 5.0 or later');
304
316
  } else if (isBSONType(doc)) {
305
317
  // the "document" is really just a BSON type object
306
318
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -336,127 +348,115 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
336
348
  }
337
349
 
338
350
  /**
339
- * EJSON parse / stringify API
340
- * @public
351
+ * Parse an Extended JSON string, constructing the JavaScript value or object described by that
352
+ * string.
353
+ *
354
+ * @example
355
+ * ```js
356
+ * const { EJSON } = require('bson');
357
+ * const text = '{ "int32": { "$numberInt": "10" } }';
358
+ *
359
+ * // prints { int32: { [String: '10'] _bsontype: 'Int32', value: '10' } }
360
+ * console.log(EJSON.parse(text, { relaxed: false }));
361
+ *
362
+ * // prints { int32: 10 }
363
+ * console.log(EJSON.parse(text));
364
+ * ```
341
365
  */
342
- // the namespace here is used to emulate `export * as EJSON from '...'`
343
- // which as of now (sept 2020) api-extractor does not support
344
- // eslint-disable-next-line @typescript-eslint/no-namespace
345
- export namespace EJSON {
346
- export interface Options {
347
- /** Output using the Extended JSON v1 spec */
348
- legacy?: boolean;
349
- /** Enable Extended JSON's `relaxed` mode, which attempts to return native JS types where possible, rather than BSON types */
350
- relaxed?: boolean;
351
- /**
352
- * Disable Extended JSON's `relaxed` mode, which attempts to return BSON types where possible, rather than native JS types
353
- * @deprecated Please use the relaxed property instead
354
- */
355
- strict?: boolean;
356
- }
357
-
358
- /**
359
- * Parse an Extended JSON string, constructing the JavaScript value or object described by that
360
- * string.
361
- *
362
- * @example
363
- * ```js
364
- * const { EJSON } = require('bson');
365
- * const text = '{ "int32": { "$numberInt": "10" } }';
366
- *
367
- * // prints { int32: { [String: '10'] _bsontype: 'Int32', value: '10' } }
368
- * console.log(EJSON.parse(text, { relaxed: false }));
369
- *
370
- * // prints { int32: 10 }
371
- * console.log(EJSON.parse(text));
372
- * ```
373
- */
374
- export function parse(text: string, options?: EJSON.Options): SerializableTypes {
375
- const finalOptions = Object.assign({}, { relaxed: true, legacy: false }, options);
376
-
377
- // relaxed implies not strict
378
- if (typeof finalOptions.relaxed === 'boolean') finalOptions.strict = !finalOptions.relaxed;
379
- if (typeof finalOptions.strict === 'boolean') finalOptions.relaxed = !finalOptions.strict;
380
-
381
- return JSON.parse(text, (key, value) => {
382
- if (key.indexOf('\x00') !== -1) {
383
- throw new BSONError(
384
- `BSON Document field names cannot contain null bytes, found: ${JSON.stringify(key)}`
385
- );
386
- }
387
- return deserializeValue(value, finalOptions);
388
- });
389
- }
390
-
391
- export type JSONPrimitive = string | number | boolean | null;
392
- export type SerializableTypes = Document | Array<JSONPrimitive | Document> | JSONPrimitive;
393
-
394
- /**
395
- * Converts a BSON document to an Extended JSON string, optionally replacing values if a replacer
396
- * function is specified or optionally including only the specified properties if a replacer array
397
- * is specified.
398
- *
399
- * @param value - The value to convert to extended JSON
400
- * @param replacer - A function that alters the behavior of the stringification process, or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of the value object to be included in the JSON string. If this value is null or not provided, all properties of the object are included in the resulting JSON string
401
- * @param space - A String or Number object that's used to insert white space into the output JSON string for readability purposes.
402
- * @param options - Optional settings
403
- *
404
- * @example
405
- * ```js
406
- * const { EJSON } = require('bson');
407
- * const Int32 = require('mongodb').Int32;
408
- * const doc = { int32: new Int32(10) };
409
- *
410
- * // prints '{"int32":{"$numberInt":"10"}}'
411
- * console.log(EJSON.stringify(doc, { relaxed: false }));
412
- *
413
- * // prints '{"int32":10}'
414
- * console.log(EJSON.stringify(doc));
415
- * ```
416
- */
417
- export function stringify(
418
- value: SerializableTypes,
419
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
420
- replacer?: (number | string)[] | ((this: any, key: string, value: any) => any) | EJSON.Options,
421
- space?: string | number,
422
- options?: EJSON.Options
423
- ): string {
424
- if (space != null && typeof space === 'object') {
425
- options = space;
426
- space = 0;
427
- }
428
- if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
429
- options = replacer;
430
- replacer = undefined;
431
- space = 0;
366
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
367
+ function parse(text: string, options?: EJSONOptions): any {
368
+ return JSON.parse(text, (key, value) => {
369
+ if (key.indexOf('\x00') !== -1) {
370
+ throw new BSONError(
371
+ `BSON Document field names cannot contain null bytes, found: ${JSON.stringify(key)}`
372
+ );
432
373
  }
433
- const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
434
- seenObjects: [{ propertyName: '(root)', obj: null }]
435
- });
374
+ return deserializeValue(value, { relaxed: true, legacy: false, ...options });
375
+ });
376
+ }
436
377
 
437
- const doc = serializeValue(value, serializeOptions);
438
- return JSON.stringify(doc, replacer as Parameters<JSON['stringify']>[1], space);
378
+ /**
379
+ * Converts a BSON document to an Extended JSON string, optionally replacing values if a replacer
380
+ * function is specified or optionally including only the specified properties if a replacer array
381
+ * is specified.
382
+ *
383
+ * @param value - The value to convert to extended JSON
384
+ * @param replacer - A function that alters the behavior of the stringification process, or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of the value object to be included in the JSON string. If this value is null or not provided, all properties of the object are included in the resulting JSON string
385
+ * @param space - A String or Number object that's used to insert white space into the output JSON string for readability purposes.
386
+ * @param options - Optional settings
387
+ *
388
+ * @example
389
+ * ```js
390
+ * const { EJSON } = require('bson');
391
+ * const Int32 = require('mongodb').Int32;
392
+ * const doc = { int32: new Int32(10) };
393
+ *
394
+ * // prints '{"int32":{"$numberInt":"10"}}'
395
+ * console.log(EJSON.stringify(doc, { relaxed: false }));
396
+ *
397
+ * // prints '{"int32":10}'
398
+ * console.log(EJSON.stringify(doc));
399
+ * ```
400
+ */
401
+ function stringify(
402
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
403
+ value: any,
404
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
405
+ replacer?: (number | string)[] | ((this: any, key: string, value: any) => any) | EJSONOptions,
406
+ space?: string | number,
407
+ options?: EJSONOptions
408
+ ): string {
409
+ if (space != null && typeof space === 'object') {
410
+ options = space;
411
+ space = 0;
439
412
  }
440
-
441
- /**
442
- * Serializes an object to an Extended JSON string, and reparse it as a JavaScript object.
443
- *
444
- * @param value - The object to serialize
445
- * @param options - Optional settings passed to the `stringify` function
446
- */
447
- export function serialize(value: SerializableTypes, options?: EJSON.Options): Document {
448
- options = options || {};
449
- return JSON.parse(stringify(value, options));
413
+ if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
414
+ options = replacer;
415
+ replacer = undefined;
416
+ space = 0;
450
417
  }
418
+ const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
419
+ seenObjects: [{ propertyName: '(root)', obj: null }]
420
+ });
451
421
 
452
- /**
453
- * Deserializes an Extended JSON object into a plain JavaScript object with native/BSON types
454
- *
455
- * @param ejson - The Extended JSON object to deserialize
456
- * @param options - Optional settings passed to the parse method
457
- */
458
- export function deserialize(ejson: Document, options?: EJSON.Options): SerializableTypes {
459
- options = options || {};
460
- return parse(JSON.stringify(ejson), options);
461
- }
422
+ const doc = serializeValue(value, serializeOptions);
423
+ return JSON.stringify(doc, replacer as Parameters<JSON['stringify']>[1], space);
462
424
  }
425
+
426
+ /**
427
+ * Serializes an object to an Extended JSON string, and reparse it as a JavaScript object.
428
+ *
429
+ * @param value - The object to serialize
430
+ * @param options - Optional settings passed to the `stringify` function
431
+ */
432
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
433
+ function EJSONserialize(value: any, options?: EJSONOptions): Document {
434
+ options = options || {};
435
+ return JSON.parse(stringify(value, options));
436
+ }
437
+
438
+ /**
439
+ * Deserializes an Extended JSON object into a plain JavaScript object with native/BSON types
440
+ *
441
+ * @param ejson - The Extended JSON object to deserialize
442
+ * @param options - Optional settings passed to the parse method
443
+ */
444
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
445
+ function EJSONdeserialize(ejson: Document, options?: EJSONOptions): any {
446
+ options = options || {};
447
+ return parse(JSON.stringify(ejson), options);
448
+ }
449
+
450
+ /** @public */
451
+ const EJSON: {
452
+ parse: typeof parse;
453
+ stringify: typeof stringify;
454
+ serialize: typeof EJSONserialize;
455
+ deserialize: typeof EJSONdeserialize;
456
+ } = Object.create(null);
457
+ EJSON.parse = parse;
458
+ EJSON.stringify = stringify;
459
+ EJSON.serialize = EJSONserialize;
460
+ EJSON.deserialize = EJSONdeserialize;
461
+ Object.freeze(EJSON);
462
+ export { EJSON };
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ import * as BSON from './bson';
2
+
3
+ // Export all named properties from BSON to support
4
+ // import { ObjectId, serialize } from 'bson';
5
+ // const { ObjectId, serialize } = require('bson');
6
+ export * from './bson';
7
+
8
+ // Export BSON as a namespace to support:
9
+ // import { BSON } from 'bson';
10
+ // const { BSON } = require('bson');
11
+ export { BSON };
12
+
13
+ // BSON does **NOT** have a default export
14
+
15
+ // The following will crash in es module environments
16
+ // import BSON from 'bson';
17
+
18
+ // The following will work as expected, BSON as a namespace of all the APIs (BSON.ObjectId, BSON.serialize)
19
+ // const BSON = require('bson');
package/src/int_32.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { BSON_MAJOR_VERSION } from './constants';
1
2
  import type { EJSONOptions } from './extended_json';
2
3
 
3
4
  /** @public */
@@ -11,7 +12,13 @@ export interface Int32Extended {
11
12
  * @category BSONType
12
13
  */
13
14
  export class Int32 {
14
- _bsontype!: 'Int32';
15
+ get _bsontype(): 'Int32' {
16
+ return 'Int32';
17
+ }
18
+ /** @internal */
19
+ get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
20
+ return BSON_MAJOR_VERSION;
21
+ }
15
22
 
16
23
  value!: number;
17
24
  /**
@@ -20,8 +27,6 @@ export class Int32 {
20
27
  * @param value - the number we want to represent as an int32.
21
28
  */
22
29
  constructor(value: number | string) {
23
- if (!(this instanceof Int32)) return new Int32(value);
24
-
25
30
  if ((value as unknown) instanceof Number) {
26
31
  value = value.valueOf();
27
32
  }
@@ -66,5 +71,3 @@ export class Int32 {
66
71
  return `new Int32(${this.valueOf()})`;
67
72
  }
68
73
  }
69
-
70
- Object.defineProperty(Int32.prototype, '_bsontype', { value: 'Int32' });