bson 4.2.3 → 4.5.0

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 (66) hide show
  1. package/bower.json +1 -1
  2. package/bson.d.ts +84 -9
  3. package/dist/bson.browser.esm.js +991 -2292
  4. package/dist/bson.browser.esm.js.map +1 -1
  5. package/dist/bson.browser.umd.js +1095 -2397
  6. package/dist/bson.browser.umd.js.map +1 -1
  7. package/dist/bson.bundle.js +1097 -2397
  8. package/dist/bson.bundle.js.map +1 -1
  9. package/dist/bson.esm.js +945 -2254
  10. package/dist/bson.esm.js.map +1 -1
  11. package/lib/binary.js +9 -1
  12. package/lib/binary.js.map +1 -1
  13. package/lib/bson.js +6 -2
  14. package/lib/bson.js.map +1 -1
  15. package/lib/db_ref.js +4 -1
  16. package/lib/db_ref.js.map +1 -1
  17. package/lib/decimal128.js +14 -51
  18. package/lib/decimal128.js.map +1 -1
  19. package/lib/ensure_buffer.js +2 -5
  20. package/lib/ensure_buffer.js.map +1 -1
  21. package/lib/extended_json.js +48 -9
  22. package/lib/extended_json.js.map +1 -1
  23. package/lib/long.js +18 -5
  24. package/lib/long.js.map +1 -1
  25. package/lib/map.js +3 -15
  26. package/lib/map.js.map +1 -1
  27. package/lib/objectid.js +9 -17
  28. package/lib/objectid.js.map +1 -1
  29. package/lib/parser/calculate_size.js +5 -6
  30. package/lib/parser/calculate_size.js.map +1 -1
  31. package/lib/parser/deserializer.js +61 -47
  32. package/lib/parser/deserializer.js.map +1 -1
  33. package/lib/parser/serializer.js +14 -13
  34. package/lib/parser/serializer.js.map +1 -1
  35. package/lib/parser/utils.js +44 -27
  36. package/lib/parser/utils.js.map +1 -1
  37. package/lib/regexp.js +1 -3
  38. package/lib/regexp.js.map +1 -1
  39. package/lib/timestamp.js +8 -2
  40. package/lib/timestamp.js.map +1 -1
  41. package/lib/utils/global.js +18 -0
  42. package/lib/utils/global.js.map +1 -0
  43. package/lib/uuid.js +173 -42
  44. package/lib/uuid.js.map +1 -1
  45. package/lib/uuid_utils.js +34 -0
  46. package/lib/uuid_utils.js.map +1 -0
  47. package/package.json +12 -9
  48. package/src/binary.ts +14 -2
  49. package/src/bson.ts +4 -1
  50. package/src/db_ref.ts +6 -1
  51. package/src/decimal128.ts +14 -52
  52. package/src/ensure_buffer.ts +7 -7
  53. package/src/extended_json.ts +64 -16
  54. package/src/long.ts +19 -7
  55. package/src/map.ts +5 -25
  56. package/src/objectid.ts +12 -19
  57. package/src/parser/calculate_size.ts +8 -11
  58. package/src/parser/deserializer.ts +68 -51
  59. package/src/parser/serializer.ts +13 -12
  60. package/src/parser/utils.ts +56 -18
  61. package/src/regexp.ts +2 -4
  62. package/src/timestamp.ts +15 -7
  63. package/src/utils/global.ts +22 -0
  64. package/src/uuid.ts +192 -40
  65. package/src/uuid_utils.ts +32 -0
  66. package/HISTORY.md +0 -481
@@ -1,7 +1,7 @@
1
1
  import { Binary } from './binary';
2
2
  import type { Document } from './bson';
3
3
  import { Code } from './code';
4
- import { DBRef } from './db_ref';
4
+ import { DBRef, isDBRefLike } from './db_ref';
5
5
  import { Decimal128 } from './decimal128';
6
6
  import { Double } from './double';
7
7
  import { Int32 } from './int_32';
@@ -9,7 +9,7 @@ import { Long } from './long';
9
9
  import { MaxKey } from './max_key';
10
10
  import { MinKey } from './min_key';
11
11
  import { ObjectId } from './objectid';
12
- import { isObjectLike } from './parser/utils';
12
+ import { isDate, isObjectLike, isRegExp } from './parser/utils';
13
13
  import { BSONRegExp } from './regexp';
14
14
  import { BSONSymbol } from './symbol';
15
15
  import { Timestamp } from './timestamp';
@@ -120,7 +120,7 @@ function deserializeValue(value: any, options: EJSON.Options = {}) {
120
120
  return Code.fromExtendedJSON(value);
121
121
  }
122
122
 
123
- if (value.$ref != null || value.$dbPointer != null) {
123
+ if (isDBRefLike(value) || value.$dbPointer) {
124
124
  const v = value.$ref ? value : value.$dbPointer;
125
125
 
126
126
  // we run into this in a "degenerate EJSON" case (with $id and $ref order flipped)
@@ -140,9 +140,20 @@ function deserializeValue(value: any, options: EJSON.Options = {}) {
140
140
  return value;
141
141
  }
142
142
 
143
+ type EJSONSerializeOptions = EJSON.Options & {
144
+ seenObjects: { obj: unknown; propertyName: string }[];
145
+ };
146
+
143
147
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
- function serializeArray(array: any[], options: EJSON.Options): any[] {
145
- return array.map((v: unknown) => serializeValue(v, options));
148
+ function serializeArray(array: any[], options: EJSONSerializeOptions): any[] {
149
+ return array.map((v: unknown, index: number) => {
150
+ options.seenObjects.push({ propertyName: `index ${index}`, obj: null });
151
+ try {
152
+ return serializeValue(v, options);
153
+ } finally {
154
+ options.seenObjects.pop();
155
+ }
156
+ });
146
157
  }
147
158
 
148
159
  function getISOString(date: Date) {
@@ -152,12 +163,42 @@ function getISOString(date: Date) {
152
163
  }
153
164
 
154
165
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
- function serializeValue(value: any, options: EJSON.Options): any {
166
+ function serializeValue(value: any, options: EJSONSerializeOptions): any {
167
+ if ((typeof value === 'object' || typeof value === 'function') && value !== null) {
168
+ const index = options.seenObjects.findIndex(entry => entry.obj === value);
169
+ if (index !== -1) {
170
+ const props = options.seenObjects.map(entry => entry.propertyName);
171
+ const leadingPart = props
172
+ .slice(0, index)
173
+ .map(prop => `${prop} -> `)
174
+ .join('');
175
+ const alreadySeen = props[index];
176
+ const circularPart =
177
+ ' -> ' +
178
+ props
179
+ .slice(index + 1, props.length - 1)
180
+ .map(prop => `${prop} -> `)
181
+ .join('');
182
+ const current = props[props.length - 1];
183
+ const leadingSpace = ' '.repeat(leadingPart.length + alreadySeen.length / 2);
184
+ const dashes = '-'.repeat(
185
+ circularPart.length + (alreadySeen.length + current.length) / 2 - 1
186
+ );
187
+
188
+ throw new TypeError(
189
+ 'Converting circular structure to EJSON:\n' +
190
+ ` ${leadingPart}${alreadySeen}${circularPart}${current}\n` +
191
+ ` ${leadingSpace}\\${dashes}/`
192
+ );
193
+ }
194
+ options.seenObjects[options.seenObjects.length - 1].obj = value;
195
+ }
196
+
156
197
  if (Array.isArray(value)) return serializeArray(value, options);
157
198
 
158
199
  if (value === undefined) return null;
159
200
 
160
- if (value instanceof Date) {
201
+ if (value instanceof Date || isDate(value)) {
161
202
  const dateNum = value.getTime(),
162
203
  // is it in year range 1970-9999?
163
204
  inRange = dateNum > -1 && dateNum < 253402318800000;
@@ -172,7 +213,7 @@ function serializeValue(value: any, options: EJSON.Options): any {
172
213
  : { $date: { $numberLong: value.getTime().toString() } };
173
214
  }
174
215
 
175
- if (typeof value === 'number' && !options.relaxed) {
216
+ if (typeof value === 'number' && (!options.relaxed || !isFinite(value))) {
176
217
  // it's an integer
177
218
  if (Math.floor(value) === value) {
178
219
  const int32Range = value >= BSON_INT32_MIN && value <= BSON_INT32_MAX,
@@ -185,7 +226,7 @@ function serializeValue(value: any, options: EJSON.Options): any {
185
226
  return { $numberDouble: value.toString() };
186
227
  }
187
228
 
188
- if (value instanceof RegExp) {
229
+ if (value instanceof RegExp || isRegExp(value)) {
189
230
  let flags = value.flags;
190
231
  if (flags === undefined) {
191
232
  const match = value.toString().match(/[gimuy]*$/);
@@ -232,7 +273,7 @@ const BSON_TYPE_MAPPINGS = {
232
273
  } as const;
233
274
 
234
275
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
235
- function serializeDocument(doc: any, options: EJSON.Options) {
276
+ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
236
277
  if (doc == null || typeof doc !== 'object') throw new Error('not an object instance');
237
278
 
238
279
  const bsontype: BSONType['_bsontype'] = doc._bsontype;
@@ -240,7 +281,12 @@ function serializeDocument(doc: any, options: EJSON.Options) {
240
281
  // It's a regular object. Recursively serialize its property values.
241
282
  const _doc: Document = {};
242
283
  for (const name in doc) {
243
- _doc[name] = serializeValue(doc[name], options);
284
+ options.seenObjects.push({ propertyName: name, obj: null });
285
+ try {
286
+ _doc[name] = serializeValue(doc[name], options);
287
+ } finally {
288
+ options.seenObjects.pop();
289
+ }
244
290
  }
245
291
  return _doc;
246
292
  } else if (isBSONType(doc)) {
@@ -264,10 +310,10 @@ function serializeDocument(doc: any, options: EJSON.Options) {
264
310
  outDoc = new Code(outDoc.code, serializeValue(outDoc.scope, options));
265
311
  } else if (bsontype === 'DBRef' && outDoc.oid) {
266
312
  outDoc = new DBRef(
267
- outDoc.collection,
313
+ serializeValue(outDoc.collection, options),
268
314
  serializeValue(outDoc.oid, options),
269
- outDoc.db,
270
- outDoc.fields
315
+ serializeValue(outDoc.db, options),
316
+ serializeValue(outDoc.fields, options)
271
317
  );
272
318
  }
273
319
 
@@ -365,9 +411,11 @@ export namespace EJSON {
365
411
  replacer = undefined;
366
412
  space = 0;
367
413
  }
368
- options = Object.assign({}, { relaxed: true, legacy: false }, options);
414
+ const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
415
+ seenObjects: [{ propertyName: '(root)', obj: null }]
416
+ });
369
417
 
370
- const doc = serializeValue(value, options);
418
+ const doc = serializeValue(value, serializeOptions);
371
419
  return JSON.stringify(doc, replacer as Parameters<JSON['stringify']>[1], space);
372
420
  }
373
421
 
package/src/long.ts CHANGED
@@ -27,13 +27,13 @@ let wasm: LongWASMHelpers | undefined = undefined;
27
27
  declare const WebAssembly: any;
28
28
 
29
29
  try {
30
- wasm = (new WebAssembly.Instance(
30
+ wasm = new WebAssembly.Instance(
31
31
  new WebAssembly.Module(
32
32
  // prettier-ignore
33
33
  new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 13, 2, 96, 0, 1, 127, 96, 4, 127, 127, 127, 127, 1, 127, 3, 7, 6, 0, 1, 1, 1, 1, 1, 6, 6, 1, 127, 1, 65, 0, 11, 7, 50, 6, 3, 109, 117, 108, 0, 1, 5, 100, 105, 118, 95, 115, 0, 2, 5, 100, 105, 118, 95, 117, 0, 3, 5, 114, 101, 109, 95, 115, 0, 4, 5, 114, 101, 109, 95, 117, 0, 5, 8, 103, 101, 116, 95, 104, 105, 103, 104, 0, 0, 10, 191, 1, 6, 4, 0, 35, 0, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 126, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 127, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 128, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 129, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 130, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11])
34
34
  ),
35
35
  {}
36
- ).exports as unknown) as LongWASMHelpers;
36
+ ).exports as unknown as LongWASMHelpers;
37
37
  } catch {
38
38
  // no wasm support
39
39
  }
@@ -97,16 +97,28 @@ export class Long {
97
97
  /**
98
98
  * Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers.
99
99
  * See the from* functions below for more convenient ways of constructing Longs.
100
+ *
101
+ * Acceptable signatures are:
102
+ * - Long(low, high, unsigned?)
103
+ * - Long(bigint, unsigned?)
104
+ * - Long(string, unsigned?)
105
+ *
100
106
  * @param low - The low (signed) 32 bits of the long
101
107
  * @param high - The high (signed) 32 bits of the long
102
108
  * @param unsigned - Whether unsigned or not, defaults to signed
103
109
  */
104
- constructor(low = 0, high = 0, unsigned?: boolean) {
110
+ constructor(low: number | bigint | string = 0, high?: number | boolean, unsigned?: boolean) {
105
111
  if (!(this instanceof Long)) return new Long(low, high, unsigned);
106
112
 
107
- this.low = low | 0;
108
- this.high = high | 0;
109
- this.unsigned = !!unsigned;
113
+ if (typeof low === 'bigint') {
114
+ Object.assign(this, Long.fromBigInt(low, !!high));
115
+ } else if (typeof low === 'string') {
116
+ Object.assign(this, Long.fromString(low, !!high));
117
+ } else {
118
+ this.low = low | 0;
119
+ this.high = (high as number) | 0;
120
+ this.unsigned = !!unsigned;
121
+ }
110
122
 
111
123
  Object.defineProperty(this, '__isLong__', {
112
124
  value: true,
@@ -994,7 +1006,7 @@ export class Long {
994
1006
  }
995
1007
 
996
1008
  inspect(): string {
997
- return `new Long("${this.toString()}")`;
1009
+ return `new Long("${this.toString()}"${this.unsigned ? ', true' : ''})`;
998
1010
  }
999
1011
  }
1000
1012
 
package/src/map.ts CHANGED
@@ -1,37 +1,17 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  // We have an ES6 Map available, return the native instance
3
3
 
4
- /* We do not want to have to include DOM types just for this check */
5
- declare const window: unknown;
6
- declare const self: unknown;
7
- declare const global: unknown;
4
+ import { getGlobal } from './utils/global';
8
5
 
9
6
  /** @public */
10
7
  let bsonMap: MapConstructor;
11
8
 
12
- const check = function (potentialGlobal: any) {
13
- // eslint-disable-next-line eqeqeq
14
- return potentialGlobal && potentialGlobal.Math == Math && potentialGlobal;
15
- };
16
-
17
- // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
18
- function getGlobal() {
19
- // eslint-disable-next-line no-undef
20
- return (
21
- check(typeof globalThis === 'object' && globalThis) ||
22
- check(typeof window === 'object' && window) ||
23
- check(typeof self === 'object' && self) ||
24
- check(typeof global === 'object' && global) ||
25
- Function('return this')()
26
- );
27
- }
28
-
29
- const bsonGlobal = getGlobal();
30
- if (Object.prototype.hasOwnProperty.call(bsonGlobal, 'Map')) {
9
+ const bsonGlobal = getGlobal<{ Map?: MapConstructor }>();
10
+ if (bsonGlobal.Map) {
31
11
  bsonMap = bsonGlobal.Map;
32
12
  } else {
33
13
  // We will return a polyfill
34
- bsonMap = (class Map {
14
+ bsonMap = class Map {
35
15
  private _keys: string[];
36
16
  private _values: Record<string, any>;
37
17
  constructor(array: [string, any][] = []) {
@@ -133,7 +113,7 @@ if (Object.prototype.hasOwnProperty.call(bsonGlobal, 'Map')) {
133
113
  get size() {
134
114
  return this._keys.length;
135
115
  }
136
- } as unknown) as MapConstructor;
116
+ } as unknown as MapConstructor;
137
117
  }
138
118
 
139
119
  export { bsonMap as Map };
package/src/objectid.ts CHANGED
@@ -1,24 +1,12 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import { ensureBuffer } from './ensure_buffer';
3
- import { deprecate, randomBytes } from './parser/utils';
4
-
5
- // constants
6
- const PROCESS_UNIQUE = randomBytes(5);
3
+ import { deprecate, isUint8Array, randomBytes } from './parser/utils';
7
4
 
8
5
  // Regular expression that checks for hex value
9
6
  const checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$');
10
7
 
11
- // Precomputed hex table enables speedy hex string conversion
12
- const hexTable: string[] = [];
13
- for (let i = 0; i < 256; i++) {
14
- hexTable[i] = (i <= 15 ? '0' : '') + i.toString(16);
15
- }
16
-
17
- // Lookup tables
18
- const decodeLookup: number[] = [];
19
- let i = 0;
20
- while (i < 10) decodeLookup[0x30 + i] = i++;
21
- while (i < 16) decodeLookup[0x41 - 10 + i] = decodeLookup[0x61 - 10 + i] = i++;
8
+ // Unique sequence for the current process (initialized on first use)
9
+ let PROCESS_UNIQUE: Uint8Array | null = null;
22
10
 
23
11
  /** @public */
24
12
  export interface ObjectIdLike {
@@ -176,6 +164,11 @@ export class ObjectId {
176
164
  // 4-byte timestamp
177
165
  buffer.writeUInt32BE(time, 0);
178
166
 
167
+ // set PROCESS_UNIQUE if yet not initialized
168
+ if (PROCESS_UNIQUE === null) {
169
+ PROCESS_UNIQUE = randomBytes(5);
170
+ }
171
+
179
172
  // 5-byte process unique
180
173
  buffer[4] = PROCESS_UNIQUE[0];
181
174
  buffer[5] = PROCESS_UNIQUE[1];
@@ -229,9 +222,9 @@ export class ObjectId {
229
222
  typeof otherId === 'string' &&
230
223
  ObjectId.isValid(otherId) &&
231
224
  otherId.length === 12 &&
232
- this.id instanceof Buffer
225
+ isUint8Array(this.id)
233
226
  ) {
234
- return otherId === this.id.toString('binary');
227
+ return otherId === Buffer.prototype.toString.call(this.id, 'latin1');
235
228
  }
236
229
 
237
230
  if (typeof otherId === 'string' && ObjectId.isValid(otherId) && otherId.length === 24) {
@@ -300,7 +293,7 @@ export class ObjectId {
300
293
  *
301
294
  * @param id - ObjectId instance to validate.
302
295
  */
303
- static isValid(id: number | string | ObjectId | Buffer | ObjectIdLike): boolean {
296
+ static isValid(id: number | string | ObjectId | Uint8Array | ObjectIdLike): boolean {
304
297
  if (id == null) return false;
305
298
 
306
299
  if (typeof id === 'number') {
@@ -315,7 +308,7 @@ export class ObjectId {
315
308
  return true;
316
309
  }
317
310
 
318
- if (id instanceof Buffer && id.length === 12) {
311
+ if (isUint8Array(id) && id.length === 12) {
319
312
  return true;
320
313
  }
321
314
 
@@ -2,7 +2,7 @@ import { Buffer } from 'buffer';
2
2
  import { Binary } from '../binary';
3
3
  import type { Document } from '../bson';
4
4
  import * as constants from '../constants';
5
- import { isDate, normalizedFunctionString } from './utils';
5
+ import { isAnyArrayBuffer, isDate, isRegExp, normalizedFunctionString } from './utils';
6
6
 
7
7
  export function calculateObjectSize(
8
8
  object: Document,
@@ -83,7 +83,11 @@ function calculateElement(
83
83
  return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (12 + 1);
84
84
  } else if (value instanceof Date || isDate(value)) {
85
85
  return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (8 + 1);
86
- } else if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
86
+ } else if (
87
+ ArrayBuffer.isView(value) ||
88
+ value instanceof ArrayBuffer ||
89
+ isAnyArrayBuffer(value)
90
+ ) {
87
91
  return (
88
92
  (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (1 + 4 + 1) + value.byteLength
89
93
  );
@@ -156,10 +160,7 @@ function calculateElement(
156
160
  1 +
157
161
  calculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined)
158
162
  );
159
- } else if (
160
- value instanceof RegExp ||
161
- Object.prototype.toString.call(value) === '[object RegExp]'
162
- ) {
163
+ } else if (value instanceof RegExp || isRegExp(value)) {
163
164
  return (
164
165
  (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
165
166
  1 +
@@ -188,11 +189,7 @@ function calculateElement(
188
189
  }
189
190
  case 'function':
190
191
  // WTF for 0.4.X where typeof /someregexp/ === 'function'
191
- if (
192
- value instanceof RegExp ||
193
- Object.prototype.toString.call(value) === '[object RegExp]' ||
194
- String.call(value) === '[object RegExp]'
195
- ) {
192
+ if (value instanceof RegExp || isRegExp(value) || String.call(value) === '[object RegExp]') {
196
193
  return (
197
194
  (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
198
195
  1 +