bson 7.1.1 → 7.3.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.
package/lib/bson.mjs CHANGED
@@ -259,6 +259,11 @@ const nodeJsByteUtils = {
259
259
  concat(list) {
260
260
  return Buffer.concat(list);
261
261
  },
262
+ copy(source, target, targetStart, sourceStart, sourceEnd) {
263
+ return nodeJsByteUtils
264
+ .toLocalBufferType(source)
265
+ .copy(target, targetStart ?? 0, sourceStart ?? 0, sourceEnd ?? source.length);
266
+ },
262
267
  equals(a, b) {
263
268
  return nodeJsByteUtils.toLocalBufferType(a).equals(b);
264
269
  },
@@ -403,6 +408,27 @@ const webByteUtils = {
403
408
  }
404
409
  return result;
405
410
  },
411
+ copy(source, target, targetStart, sourceStart, sourceEnd) {
412
+ if (sourceEnd !== undefined && sourceEnd < 0) {
413
+ throw new RangeError(`The value of "sourceEnd" is out of range. It must be >= 0. Received ${sourceEnd}`);
414
+ }
415
+ sourceEnd = sourceEnd ?? source.length;
416
+ if (sourceStart !== undefined && (sourceStart < 0 || sourceStart > sourceEnd)) {
417
+ throw new RangeError(`The value of "sourceStart" is out of range. It must be >= 0 and <= ${sourceEnd}. Received ${sourceStart}`);
418
+ }
419
+ sourceStart = sourceStart ?? 0;
420
+ if (targetStart !== undefined && targetStart < 0) {
421
+ throw new RangeError(`The value of "targetStart" is out of range. It must be >= 0. Received ${targetStart}`);
422
+ }
423
+ targetStart = targetStart ?? 0;
424
+ const srcSlice = source.subarray(sourceStart, sourceEnd);
425
+ const maxLen = Math.min(srcSlice.length, target.length - targetStart);
426
+ if (maxLen <= 0) {
427
+ return 0;
428
+ }
429
+ target.set(srcSlice.subarray(0, maxLen), targetStart);
430
+ return maxLen;
431
+ },
406
432
  equals(uint8Array, otherUint8Array) {
407
433
  if (uint8Array.byteLength !== otherUint8Array.byteLength) {
408
434
  return false;
@@ -670,7 +696,7 @@ class Binary extends BSONValue {
670
696
  !Array.isArray(buffer)) {
671
697
  throw new BSONError('Binary can only be constructed from Uint8Array or number[]');
672
698
  }
673
- this.sub_type = subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT;
699
+ this.sub_type = (subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT) & 0xff;
674
700
  if (buffer == null) {
675
701
  this.buffer = ByteUtils.allocate(Binary.BUFFER_SIZE);
676
702
  this.position = 0;
@@ -776,7 +802,7 @@ class Binary extends BSONValue {
776
802
  if (this.sub_type === Binary.SUBTYPE_UUID) {
777
803
  return new UUID(this.buffer.subarray(0, this.position));
778
804
  }
779
- throw new BSONError(`Binary sub_type "${this.sub_type}" is not supported for converting to UUID. Only "${Binary.SUBTYPE_UUID}" is currently supported.`);
805
+ throw new BSONError(`Binary sub_type "${this.sub_type}" (${typeof this.sub_type}) is not supported for converting to UUID. Only 0x${Binary.SUBTYPE_UUID.toString(16).padStart(2, '0')} is currently supported.`);
780
806
  }
781
807
  static createFromHexString(hex, subType) {
782
808
  return new Binary(ByteUtils.fromHex(hex), subType);
@@ -2565,13 +2591,24 @@ class MinKey extends BSONValue {
2565
2591
  }
2566
2592
  }
2567
2593
 
2568
- let PROCESS_UNIQUE = null;
2569
2594
  const __idCache = new WeakMap();
2570
2595
  class ObjectId extends BSONValue {
2571
2596
  get _bsontype() {
2572
2597
  return 'ObjectId';
2573
2598
  }
2574
- static index = Math.floor(Math.random() * 0xffffff);
2599
+ static index = 0;
2600
+ static PROCESS_UNIQUE = null;
2601
+ static resetState = () => {
2602
+ this.index = Math.floor(Math.random() * 0x1000000);
2603
+ this.PROCESS_UNIQUE = ByteUtils.randomBytes(5);
2604
+ };
2605
+ static {
2606
+ this.resetState();
2607
+ const { startupSnapshot } = globalThis?.process?.getBuiltinModule('v8') ?? {};
2608
+ if (startupSnapshot?.isBuildingSnapshot()) {
2609
+ startupSnapshot?.addDeserializeCallback(this.resetState);
2610
+ }
2611
+ }
2575
2612
  static cacheHexString;
2576
2613
  buffer;
2577
2614
  constructor(inputId) {
@@ -2648,7 +2685,7 @@ class ObjectId extends BSONValue {
2648
2685
  return hexString;
2649
2686
  }
2650
2687
  static getInc() {
2651
- return (ObjectId.index = (ObjectId.index + 1) % 0xffffff);
2688
+ return (ObjectId.index = (ObjectId.index + 1) % 0x1000000);
2652
2689
  }
2653
2690
  static generate(time) {
2654
2691
  if ('number' !== typeof time) {
@@ -2657,9 +2694,7 @@ class ObjectId extends BSONValue {
2657
2694
  const inc = ObjectId.getInc();
2658
2695
  const buffer = ByteUtils.allocateUnsafe(12);
2659
2696
  NumberUtils.setInt32BE(buffer, 0, time);
2660
- if (PROCESS_UNIQUE === null) {
2661
- PROCESS_UNIQUE = ByteUtils.randomBytes(5);
2662
- }
2697
+ const PROCESS_UNIQUE = this.PROCESS_UNIQUE;
2663
2698
  buffer[4] = PROCESS_UNIQUE[0];
2664
2699
  buffer[5] = PROCESS_UNIQUE[1];
2665
2700
  buffer[6] = PROCESS_UNIQUE[2];
@@ -2777,23 +2812,33 @@ class ObjectId extends BSONValue {
2777
2812
  }
2778
2813
 
2779
2814
  function internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined) {
2780
- let totalLength = 4 + 1;
2781
- if (Array.isArray(object)) {
2782
- for (let i = 0; i < object.length; i++) {
2783
- totalLength += calculateElement(i.toString(), object[i], serializeFunctions, true, ignoreUndefined);
2784
- }
2785
- }
2786
- else {
2787
- if (typeof object?.toBSON === 'function') {
2788
- object = object.toBSON();
2815
+ const objectStack = [
2816
+ { obj: object, ignoreUndefined: ignoreUndefined ?? false }
2817
+ ];
2818
+ let total = 0;
2819
+ while (objectStack.length > 0) {
2820
+ const { obj, ignoreUndefined: frameIgnoreUndefined } = objectStack.pop();
2821
+ total += 5;
2822
+ const isObjArray = Array.isArray(obj);
2823
+ let target = obj;
2824
+ if (!isObjArray && typeof obj?.toBSON === 'function') {
2825
+ target = obj.toBSON();
2826
+ }
2827
+ if (isObjArray) {
2828
+ const array = target;
2829
+ for (let i = 0; i < array.length; i++) {
2830
+ total += calculateElementSize(i.toString(), array[i], serializeFunctions, true, frameIgnoreUndefined, objectStack);
2831
+ }
2789
2832
  }
2790
- for (const key of Object.keys(object)) {
2791
- totalLength += calculateElement(key, object[key], serializeFunctions, false, ignoreUndefined);
2833
+ else {
2834
+ for (const key of Object.keys(target)) {
2835
+ total += calculateElementSize(key, target[key], serializeFunctions, false, frameIgnoreUndefined, objectStack);
2836
+ }
2792
2837
  }
2793
2838
  }
2794
- return totalLength;
2839
+ return total;
2795
2840
  }
2796
- function calculateElement(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false) {
2841
+ function calculateElementSize(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false, objectStack) {
2797
2842
  if (typeof value?.toBSON === 'function') {
2798
2843
  value = value.toBSON();
2799
2844
  }
@@ -2805,21 +2850,21 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2805
2850
  value >= JS_INT_MIN &&
2806
2851
  value <= JS_INT_MAX) {
2807
2852
  if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
2808
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (4 + 1);
2853
+ return ByteUtils.utf8ByteLength(name) + 1 + (4 + 1);
2809
2854
  }
2810
2855
  else {
2811
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2856
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2812
2857
  }
2813
2858
  }
2814
2859
  else {
2815
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2860
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2816
2861
  }
2817
2862
  case 'undefined':
2818
2863
  if (isArray || !ignoreUndefined)
2819
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2864
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2820
2865
  return 0;
2821
2866
  case 'boolean':
2822
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1);
2867
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 1);
2823
2868
  case 'object':
2824
2869
  if (value != null &&
2825
2870
  typeof value._bsontype === 'string' &&
@@ -2827,39 +2872,41 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2827
2872
  throw new BSONVersionError();
2828
2873
  }
2829
2874
  else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
2830
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2875
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2831
2876
  }
2832
2877
  else if (value._bsontype === 'ObjectId') {
2833
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1);
2878
+ return ByteUtils.utf8ByteLength(name) + 1 + (12 + 1);
2834
2879
  }
2835
2880
  else if (value instanceof Date || isDate(value)) {
2836
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2881
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2837
2882
  }
2838
2883
  else if (ArrayBuffer.isView(value) ||
2839
2884
  value instanceof ArrayBuffer ||
2840
2885
  isAnyArrayBuffer(value)) {
2841
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength);
2886
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 4 + 1) + value.byteLength;
2842
2887
  }
2843
2888
  else if (value._bsontype === 'Long' ||
2844
2889
  value._bsontype === 'Double' ||
2845
2890
  value._bsontype === 'Timestamp') {
2846
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2891
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2847
2892
  }
2848
2893
  else if (value._bsontype === 'Decimal128') {
2849
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1);
2894
+ return ByteUtils.utf8ByteLength(name) + 1 + (16 + 1);
2850
2895
  }
2851
2896
  else if (value._bsontype === 'Code') {
2852
2897
  if (value.scope != null && Object.keys(value.scope).length > 0) {
2853
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2898
+ objectStack.push({ obj: value.scope, ignoreUndefined });
2899
+ return (ByteUtils.utf8ByteLength(name) +
2900
+ 1 +
2854
2901
  1 +
2855
2902
  4 +
2856
2903
  4 +
2857
2904
  ByteUtils.utf8ByteLength(value.code.toString()) +
2858
- 1 +
2859
- internalCalculateObjectSize(value.scope, serializeFunctions, ignoreUndefined));
2905
+ 1);
2860
2906
  }
2861
2907
  else {
2862
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2908
+ return (ByteUtils.utf8ByteLength(name) +
2909
+ 1 +
2863
2910
  1 +
2864
2911
  4 +
2865
2912
  ByteUtils.utf8ByteLength(value.code.toString()) +
@@ -2869,19 +2916,14 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2869
2916
  else if (value._bsontype === 'Binary') {
2870
2917
  const binary = value;
2871
2918
  if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
2872
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2873
- (binary.position + 1 + 4 + 1 + 4));
2919
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1 + 4);
2874
2920
  }
2875
2921
  else {
2876
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1));
2922
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1);
2877
2923
  }
2878
2924
  }
2879
2925
  else if (value._bsontype === 'Symbol') {
2880
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2881
- ByteUtils.utf8ByteLength(value.value) +
2882
- 4 +
2883
- 1 +
2884
- 1);
2926
+ return (ByteUtils.utf8ByteLength(name) + 1 + ByteUtils.utf8ByteLength(value.value) + 4 + 1 + 1);
2885
2927
  }
2886
2928
  else if (value._bsontype === 'DBRef') {
2887
2929
  const ordered_values = Object.assign({
@@ -2891,12 +2933,12 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2891
2933
  if (value.db != null) {
2892
2934
  ordered_values['$db'] = value.db;
2893
2935
  }
2894
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2895
- 1 +
2896
- internalCalculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined));
2936
+ objectStack.push({ obj: ordered_values, ignoreUndefined: true });
2937
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2897
2938
  }
2898
2939
  else if (value instanceof RegExp || isRegExp(value)) {
2899
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2940
+ return (ByteUtils.utf8ByteLength(name) +
2941
+ 1 +
2900
2942
  1 +
2901
2943
  ByteUtils.utf8ByteLength(value.source) +
2902
2944
  1 +
@@ -2906,7 +2948,8 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2906
2948
  1);
2907
2949
  }
2908
2950
  else if (value._bsontype === 'BSONRegExp') {
2909
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2951
+ return (ByteUtils.utf8ByteLength(name) +
2952
+ 1 +
2910
2953
  1 +
2911
2954
  ByteUtils.utf8ByteLength(value.pattern) +
2912
2955
  1 +
@@ -2914,13 +2957,13 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2914
2957
  1);
2915
2958
  }
2916
2959
  else {
2917
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2918
- internalCalculateObjectSize(value, serializeFunctions, ignoreUndefined) +
2919
- 1);
2960
+ objectStack.push({ obj: value, ignoreUndefined });
2961
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2920
2962
  }
2921
2963
  case 'function':
2922
2964
  if (serializeFunctions) {
2923
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2965
+ return (ByteUtils.utf8ByteLength(name) +
2966
+ 1 +
2924
2967
  1 +
2925
2968
  4 +
2926
2969
  ByteUtils.utf8ByteLength(value.toString()) +
@@ -2928,7 +2971,7 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2928
2971
  }
2929
2972
  return 0;
2930
2973
  case 'bigint':
2931
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2974
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2932
2975
  case 'symbol':
2933
2976
  return 0;
2934
2977
  default:
@@ -3144,7 +3187,28 @@ function internalDeserialize(buffer, options, isArray) {
3144
3187
  return deserializeObject(buffer, index, options, isArray);
3145
3188
  }
3146
3189
  const allowedDBRefKeys = /^\$ref$|^\$id$|^\$db$/;
3190
+ function assignValue(dest, name, value) {
3191
+ if (name === '__proto__') {
3192
+ Object.defineProperty(dest, name, {
3193
+ value,
3194
+ writable: true,
3195
+ enumerable: true,
3196
+ configurable: true
3197
+ });
3198
+ }
3199
+ else {
3200
+ dest[name] = value;
3201
+ }
3202
+ }
3203
+ function toPotentialDbRef(doc) {
3204
+ if (isDBRefLike(doc)) {
3205
+ const { $ref, $id, $db, ...fields } = doc;
3206
+ return new DBRef($ref, $id, $db, fields);
3207
+ }
3208
+ return doc;
3209
+ }
3147
3210
  function deserializeObject(buffer, index, options, isArray = false) {
3211
+ options = { ...options };
3148
3212
  const fieldsAsRaw = options['fieldsAsRaw'] == null ? null : options['fieldsAsRaw'];
3149
3213
  const raw = options['raw'] == null ? false : options['raw'];
3150
3214
  const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
@@ -3195,31 +3259,87 @@ function deserializeObject(buffer, index, options, isArray = false) {
3195
3259
  index += 4;
3196
3260
  if (size < 5 || size > buffer.length)
3197
3261
  throw new BSONError('corrupt bson message');
3198
- const object = isArray ? [] : {};
3262
+ const rootObject = isArray ? [] : {};
3199
3263
  let arrayIndex = 0;
3200
3264
  let isPossibleDBRef = isArray ? false : null;
3265
+ let currentFrame = null;
3266
+ let currentDest = rootObject;
3267
+ let currentIsArray = isArray;
3201
3268
  while (true) {
3202
3269
  const elementType = buffer[index++];
3203
- if (elementType === 0)
3204
- break;
3270
+ if (elementType === 0) {
3271
+ if (currentFrame) {
3272
+ if (index === currentFrame.lastIndex) {
3273
+ const completedFrame = currentFrame;
3274
+ currentFrame = completedFrame.prev;
3275
+ if (currentFrame === null) {
3276
+ currentDest = rootObject;
3277
+ currentIsArray = isArray;
3278
+ }
3279
+ else {
3280
+ currentDest = currentFrame.holdingDocument;
3281
+ currentIsArray = currentFrame.isArray;
3282
+ }
3283
+ let result = completedFrame.holdingDocument;
3284
+ switch (completedFrame.elementType) {
3285
+ case BSON_DATA_OBJECT:
3286
+ if (completedFrame.isPossibleDBRef) {
3287
+ result = toPotentialDbRef(result);
3288
+ }
3289
+ break;
3290
+ case BSON_DATA_ARRAY:
3291
+ break;
3292
+ case BSON_DATA_CODE_W_SCOPE:
3293
+ result = new Code(completedFrame.functionString, completedFrame.holdingDocument);
3294
+ break;
3295
+ default:
3296
+ throw new BSONError('Unexpected element type in frame stack');
3297
+ }
3298
+ assignValue(currentDest, completedFrame.propertyName, result);
3299
+ continue;
3300
+ }
3301
+ else {
3302
+ if (currentFrame.elementType === BSON_DATA_ARRAY) {
3303
+ throw new BSONError('corrupted array bson');
3304
+ }
3305
+ throw new BSONError('Bad BSON Document: object not properly terminated');
3306
+ }
3307
+ }
3308
+ else {
3309
+ break;
3310
+ }
3311
+ }
3205
3312
  let i = index;
3206
3313
  while (buffer[i] !== 0x00 && i < buffer.length) {
3207
3314
  i++;
3208
3315
  }
3209
3316
  if (i >= buffer.byteLength)
3210
3317
  throw new BSONError('Bad BSON Document: illegal CString');
3211
- const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
3212
- let shouldValidateKey = true;
3213
- if (globalUTFValidation || utf8KeysSet?.has(name)) {
3318
+ const name = currentIsArray
3319
+ ? currentFrame !== null
3320
+ ? currentFrame.arrayIndex++
3321
+ : arrayIndex++
3322
+ : ByteUtils.toUTF8(buffer, index, i, false);
3323
+ let shouldValidateKey;
3324
+ if (currentFrame !== null) {
3325
+ shouldValidateKey = currentFrame.validationSetting;
3326
+ }
3327
+ else if (globalUTFValidation || utf8KeysSet?.has(name)) {
3214
3328
  shouldValidateKey = validationSetting;
3215
3329
  }
3216
3330
  else {
3217
3331
  shouldValidateKey = !validationSetting;
3218
3332
  }
3219
- if (isPossibleDBRef !== false && name[0] === '$') {
3333
+ if (currentFrame !== null) {
3334
+ if (currentFrame.isPossibleDBRef !== false && typeof name === 'string' && name[0] === '$') {
3335
+ currentFrame.isPossibleDBRef = allowedDBRefKeys.test(name);
3336
+ }
3337
+ }
3338
+ else if (isPossibleDBRef !== false && name[0] === '$') {
3220
3339
  isPossibleDBRef = allowedDBRefKeys.test(name);
3221
3340
  }
3222
3341
  let value;
3342
+ let isDeferredValue = false;
3223
3343
  index = i + 1;
3224
3344
  if (elementType === BSON_DATA_STRING) {
3225
3345
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3265,39 +3385,58 @@ function deserializeObject(buffer, index, options, isArray = false) {
3265
3385
  value = buffer[index++] === 1;
3266
3386
  }
3267
3387
  else if (elementType === BSON_DATA_OBJECT) {
3268
- const _index = index;
3269
3388
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3270
- if (objectSize <= 0 || objectSize > buffer.length - index)
3389
+ if (objectSize < 5 || objectSize > buffer.length - index)
3271
3390
  throw new BSONError('bad embedded document length in bson');
3272
- if (raw) {
3391
+ if (raw || (currentFrame?.raw ?? false)) {
3273
3392
  value = buffer.subarray(index, index + objectSize);
3393
+ index = index + objectSize;
3274
3394
  }
3275
3395
  else {
3276
- let objectOptions = options;
3277
- if (!globalUTFValidation) {
3278
- objectOptions = { ...options, validation: { utf8: shouldValidateKey } };
3279
- }
3280
- value = deserializeObject(buffer, _index, objectOptions, false);
3396
+ isDeferredValue = true;
3397
+ const objectFrame = {
3398
+ holdingDocument: {},
3399
+ elementType: BSON_DATA_OBJECT,
3400
+ propertyName: name,
3401
+ functionString: null,
3402
+ lastIndex: index + objectSize,
3403
+ isArray: false,
3404
+ arrayIndex: 0,
3405
+ raw: false,
3406
+ isPossibleDBRef: null,
3407
+ validationSetting: shouldValidateKey,
3408
+ prev: currentFrame
3409
+ };
3410
+ currentFrame = objectFrame;
3411
+ currentDest = objectFrame.holdingDocument;
3412
+ currentIsArray = false;
3413
+ index = index + 4;
3281
3414
  }
3282
- index = index + objectSize;
3283
3415
  }
3284
3416
  else if (elementType === BSON_DATA_ARRAY) {
3285
- const _index = index;
3286
3417
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3287
- let arrayOptions = options;
3418
+ if (objectSize < 5 || objectSize > buffer.length - index)
3419
+ throw new BSONError('bad embedded array length in bson');
3288
3420
  const stopIndex = index + objectSize;
3289
- if (fieldsAsRaw && fieldsAsRaw[name]) {
3290
- arrayOptions = { ...options, raw: true };
3291
- }
3292
- if (!globalUTFValidation) {
3293
- arrayOptions = { ...arrayOptions, validation: { utf8: shouldValidateKey } };
3294
- }
3295
- value = deserializeObject(buffer, _index, arrayOptions, true);
3296
- index = index + objectSize;
3297
- if (buffer[index - 1] !== 0)
3298
- throw new BSONError('invalid array terminator byte');
3299
- if (index !== stopIndex)
3300
- throw new BSONError('corrupted array bson');
3421
+ const arrayRaw = !!(fieldsAsRaw && fieldsAsRaw[name]) || (currentFrame?.raw ?? false);
3422
+ isDeferredValue = true;
3423
+ const arrayFrame = {
3424
+ holdingDocument: [],
3425
+ elementType: BSON_DATA_ARRAY,
3426
+ propertyName: name,
3427
+ functionString: null,
3428
+ lastIndex: stopIndex,
3429
+ isArray: true,
3430
+ arrayIndex: 0,
3431
+ raw: arrayRaw,
3432
+ isPossibleDBRef: false,
3433
+ validationSetting: shouldValidateKey,
3434
+ prev: currentFrame
3435
+ };
3436
+ currentFrame = arrayFrame;
3437
+ currentDest = arrayFrame.holdingDocument;
3438
+ currentIsArray = true;
3439
+ index = index + 4;
3301
3440
  }
3302
3441
  else if (elementType === BSON_DATA_UNDEFINED) {
3303
3442
  value = undefined;
@@ -3469,15 +3608,32 @@ function deserializeObject(buffer, index, options, isArray = false) {
3469
3608
  index = index + stringSize;
3470
3609
  const _index = index;
3471
3610
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3472
- const scopeObject = deserializeObject(buffer, _index, options, false);
3473
- index = index + objectSize;
3611
+ if (objectSize < 5 || objectSize > buffer.length - index)
3612
+ throw new BSONError('bad scope document size in code_w_scope');
3474
3613
  if (totalSize < 4 + 4 + objectSize + stringSize) {
3475
3614
  throw new BSONError('code_w_scope total size is too short, truncating scope');
3476
3615
  }
3477
3616
  if (totalSize > 4 + 4 + objectSize + stringSize) {
3478
3617
  throw new BSONError('code_w_scope total size is too long, clips outer document');
3479
3618
  }
3480
- value = new Code(functionString, scopeObject);
3619
+ isDeferredValue = true;
3620
+ const scopeFrame = {
3621
+ holdingDocument: {},
3622
+ elementType: BSON_DATA_CODE_W_SCOPE,
3623
+ propertyName: name,
3624
+ functionString: functionString,
3625
+ lastIndex: _index + objectSize,
3626
+ isArray: false,
3627
+ arrayIndex: 0,
3628
+ raw: false,
3629
+ isPossibleDBRef: null,
3630
+ validationSetting: shouldValidateKey,
3631
+ prev: currentFrame
3632
+ };
3633
+ currentFrame = scopeFrame;
3634
+ currentDest = scopeFrame.holdingDocument;
3635
+ currentIsArray = false;
3636
+ index = index + 4;
3481
3637
  }
3482
3638
  else if (elementType === BSON_DATA_DBPOINTER) {
3483
3639
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3498,18 +3654,14 @@ function deserializeObject(buffer, index, options, isArray = false) {
3498
3654
  else {
3499
3655
  throw new BSONError(`Detected unknown BSON type ${elementType.toString(16)} for fieldname "${name}"`);
3500
3656
  }
3501
- if (name === '__proto__') {
3502
- Object.defineProperty(object, name, {
3503
- value,
3504
- writable: true,
3505
- enumerable: true,
3506
- configurable: true
3507
- });
3508
- }
3509
- else {
3510
- object[name] = value;
3657
+ if (!isDeferredValue) {
3658
+ assignValue(currentDest, name, value);
3511
3659
  }
3512
3660
  }
3661
+ if (currentFrame !== null) {
3662
+ throw new BSONError('corrupted bson, more objects expected based on the current document size');
3663
+ }
3664
+ const object = rootObject;
3513
3665
  if (size !== index - startIndex) {
3514
3666
  if (isArray)
3515
3667
  throw new BSONError('corrupt array bson');
@@ -3517,14 +3669,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
3517
3669
  }
3518
3670
  if (!isPossibleDBRef)
3519
3671
  return object;
3520
- if (isDBRefLike(object)) {
3521
- const copy = Object.assign({}, object);
3522
- delete copy.$ref;
3523
- delete copy.$id;
3524
- delete copy.$db;
3525
- return new DBRef(object.$ref, object.$id, object.$db, copy);
3526
- }
3527
- return object;
3672
+ return toPotentialDbRef(object);
3528
3673
  }
3529
3674
 
3530
3675
  const regexp = /\x00/;
@@ -3633,7 +3778,7 @@ function serializeMinMax(buffer, key, value, index) {
3633
3778
  if (value === null) {
3634
3779
  buffer[index++] = BSON_DATA_NULL;
3635
3780
  }
3636
- else if (value._bsontype === 'MinKey') {
3781
+ else if (value[bsonType] === 'MinKey') {
3637
3782
  buffer[index++] = BSON_DATA_MIN_KEY;
3638
3783
  }
3639
3784
  else {
@@ -3670,19 +3815,6 @@ function serializeBuffer(buffer, key, value, index) {
3670
3815
  index = index + size;
3671
3816
  return index;
3672
3817
  }
3673
- function serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path) {
3674
- if (path.has(value)) {
3675
- throw new BSONError('Cannot convert circular structure to BSON');
3676
- }
3677
- path.add(value);
3678
- buffer[index++] = Array.isArray(value) ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
3679
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3680
- index = index + numberOfWrittenBytes;
3681
- buffer[index++] = 0;
3682
- const endIndex = serializeInto(buffer, value, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3683
- path.delete(value);
3684
- return endIndex;
3685
- }
3686
3818
  function serializeDecimal128(buffer, key, value, index) {
3687
3819
  buffer[index++] = BSON_DATA_DECIMAL128;
3688
3820
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3694,7 +3826,7 @@ function serializeDecimal128(buffer, key, value, index) {
3694
3826
  }
3695
3827
  function serializeLong(buffer, key, value, index) {
3696
3828
  buffer[index++] =
3697
- value._bsontype === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3829
+ value[bsonType] === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3698
3830
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3699
3831
  index = index + numberOfWrittenBytes;
3700
3832
  buffer[index++] = 0;
@@ -3733,38 +3865,6 @@ function serializeFunction(buffer, key, value, index) {
3733
3865
  buffer[index++] = 0;
3734
3866
  return index;
3735
3867
  }
3736
- function serializeCode(buffer, key, value, index, checkKeys = false, depth = 0, serializeFunctions = false, ignoreUndefined = true, path) {
3737
- if (value.scope && typeof value.scope === 'object') {
3738
- buffer[index++] = BSON_DATA_CODE_W_SCOPE;
3739
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3740
- index = index + numberOfWrittenBytes;
3741
- buffer[index++] = 0;
3742
- let startIndex = index;
3743
- const functionString = value.code;
3744
- index = index + 4;
3745
- const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3746
- NumberUtils.setInt32LE(buffer, index, codeSize);
3747
- buffer[index + 4 + codeSize - 1] = 0;
3748
- index = index + codeSize + 4;
3749
- const endIndex = serializeInto(buffer, value.scope, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3750
- index = endIndex - 1;
3751
- const totalSize = endIndex - startIndex;
3752
- startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
3753
- buffer[index++] = 0;
3754
- }
3755
- else {
3756
- buffer[index++] = BSON_DATA_CODE;
3757
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3758
- index = index + numberOfWrittenBytes;
3759
- buffer[index++] = 0;
3760
- const functionString = value.code.toString();
3761
- const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3762
- NumberUtils.setInt32LE(buffer, index, size);
3763
- index = index + 4 + size - 1;
3764
- buffer[index++] = 0;
3765
- }
3766
- return index;
3767
- }
3768
3868
  function serializeBinary(buffer, key, value, index) {
3769
3869
  buffer[index++] = BSON_DATA_BINARY;
3770
3870
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3804,26 +3904,59 @@ function serializeSymbol(buffer, key, value, index) {
3804
3904
  buffer[index++] = 0;
3805
3905
  return index;
3806
3906
  }
3807
- function serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path) {
3808
- buffer[index++] = BSON_DATA_OBJECT;
3809
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3810
- index = index + numberOfWrittenBytes;
3811
- buffer[index++] = 0;
3812
- let startIndex = index;
3813
- let output = {
3814
- $ref: value.collection || value.namespace,
3815
- $id: value.oid
3907
+ function makeFrame(sourceObject, objectSizeIndex, codeSizeIndex, prev, checkKeys, ignoreUndefined) {
3908
+ if (Array.isArray(sourceObject)) {
3909
+ return {
3910
+ sourceObject,
3911
+ isArray: true,
3912
+ objectSizeIndex,
3913
+ codeSizeIndex,
3914
+ iterTarget: sourceObject,
3915
+ keys: null,
3916
+ keyIndex: 0,
3917
+ mapIterator: null,
3918
+ prev,
3919
+ checkKeys,
3920
+ ignoreUndefined
3921
+ };
3922
+ }
3923
+ if (sourceObject instanceof Map || isMap(sourceObject)) {
3924
+ return {
3925
+ sourceObject,
3926
+ isArray: false,
3927
+ objectSizeIndex,
3928
+ codeSizeIndex,
3929
+ iterTarget: sourceObject,
3930
+ keys: null,
3931
+ keyIndex: 0,
3932
+ mapIterator: sourceObject.entries(),
3933
+ prev,
3934
+ checkKeys,
3935
+ ignoreUndefined
3936
+ };
3937
+ }
3938
+ let target = sourceObject;
3939
+ if (typeof target?.toBSON === 'function') {
3940
+ target = target.toBSON();
3941
+ if (target != null && typeof target !== 'object') {
3942
+ throw new BSONError('toBSON function did not return an object');
3943
+ }
3944
+ }
3945
+ return {
3946
+ sourceObject,
3947
+ isArray: false,
3948
+ objectSizeIndex,
3949
+ codeSizeIndex,
3950
+ iterTarget: target,
3951
+ keys: Object.keys(target),
3952
+ keyIndex: 0,
3953
+ mapIterator: null,
3954
+ prev,
3955
+ checkKeys,
3956
+ ignoreUndefined
3816
3957
  };
3817
- if (value.db != null) {
3818
- output.$db = value.db;
3819
- }
3820
- output = Object.assign(output, value.fields);
3821
- const endIndex = serializeInto(buffer, output, false, index, depth + 1, serializeFunctions, true, path);
3822
- const size = endIndex - startIndex;
3823
- startIndex += NumberUtils.setInt32LE(buffer, index, size);
3824
- return endIndex;
3825
3958
  }
3826
- function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializeFunctions, ignoreUndefined, path) {
3959
+ function serializeInto(buffer, object, checkKeys, startingIndex, serializeFunctions, ignoreUndefined, path) {
3827
3960
  if (path == null) {
3828
3961
  if (object == null) {
3829
3962
  buffer[0] = 0x05;
@@ -3851,308 +3984,200 @@ function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializ
3851
3984
  path = new Set();
3852
3985
  }
3853
3986
  path.add(object);
3987
+ let currentFrame = makeFrame(object, startingIndex, null, null, checkKeys, ignoreUndefined);
3854
3988
  let index = startingIndex + 4;
3855
- if (Array.isArray(object)) {
3856
- for (let i = 0; i < object.length; i++) {
3857
- const key = `${i}`;
3858
- let value = object[i];
3859
- if (typeof value?.toBSON === 'function') {
3860
- value = value.toBSON();
3861
- }
3862
- const type = typeof value;
3863
- if (value === undefined) {
3864
- index = serializeNull(buffer, key, value, index);
3865
- }
3866
- else if (value === null) {
3867
- index = serializeNull(buffer, key, value, index);
3868
- }
3869
- else if (type === 'string') {
3870
- index = serializeString(buffer, key, value, index);
3871
- }
3872
- else if (type === 'number') {
3873
- index = serializeNumber(buffer, key, value, index);
3874
- }
3875
- else if (type === 'bigint') {
3876
- index = serializeBigInt(buffer, key, value, index);
3877
- }
3878
- else if (type === 'boolean') {
3879
- index = serializeBoolean(buffer, key, value, index);
3880
- }
3881
- else if (type === 'object' && value._bsontype == null) {
3882
- if (value instanceof Date || isDate(value)) {
3883
- index = serializeDate(buffer, key, value, index);
3884
- }
3885
- else if (value instanceof Uint8Array || isUint8Array(value)) {
3886
- index = serializeBuffer(buffer, key, value, index);
3887
- }
3888
- else if (value instanceof RegExp || isRegExp(value)) {
3889
- index = serializeRegExp(buffer, key, value, index);
3890
- }
3891
- else {
3892
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
3989
+ while (currentFrame !== null) {
3990
+ const frame = currentFrame;
3991
+ let key;
3992
+ let value;
3993
+ if (frame.mapIterator !== null) {
3994
+ const next = frame.mapIterator.next();
3995
+ if (next.done) {
3996
+ buffer[index++] = 0x00;
3997
+ NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
3998
+ if (frame.codeSizeIndex !== null) {
3999
+ NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
3893
4000
  }
4001
+ path.delete(frame.sourceObject);
4002
+ currentFrame = frame.prev;
4003
+ continue;
3894
4004
  }
3895
- else if (type === 'object') {
3896
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
3897
- throw new BSONVersionError();
3898
- }
3899
- else if (value._bsontype === 'ObjectId') {
3900
- index = serializeObjectId(buffer, key, value, index);
3901
- }
3902
- else if (value._bsontype === 'Decimal128') {
3903
- index = serializeDecimal128(buffer, key, value, index);
3904
- }
3905
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
3906
- index = serializeLong(buffer, key, value, index);
3907
- }
3908
- else if (value._bsontype === 'Double') {
3909
- index = serializeDouble(buffer, key, value, index);
3910
- }
3911
- else if (value._bsontype === 'Code') {
3912
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
3913
- }
3914
- else if (value._bsontype === 'Binary') {
3915
- index = serializeBinary(buffer, key, value, index);
3916
- }
3917
- else if (value._bsontype === 'BSONSymbol') {
3918
- index = serializeSymbol(buffer, key, value, index);
3919
- }
3920
- else if (value._bsontype === 'DBRef') {
3921
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
3922
- }
3923
- else if (value._bsontype === 'BSONRegExp') {
3924
- index = serializeBSONRegExp(buffer, key, value, index);
3925
- }
3926
- else if (value._bsontype === 'Int32') {
3927
- index = serializeInt32(buffer, key, value, index);
3928
- }
3929
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
3930
- index = serializeMinMax(buffer, key, value, index);
3931
- }
3932
- else if (typeof value._bsontype !== 'undefined') {
3933
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4005
+ key = next.value[0];
4006
+ value = next.value[1];
4007
+ }
4008
+ else if (frame.keys !== null) {
4009
+ if (frame.keyIndex >= frame.keys.length) {
4010
+ buffer[index++] = 0x00;
4011
+ NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
4012
+ if (frame.codeSizeIndex !== null) {
4013
+ NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
3934
4014
  }
4015
+ path.delete(frame.sourceObject);
4016
+ currentFrame = frame.prev;
4017
+ continue;
3935
4018
  }
3936
- else if (type === 'function' && serializeFunctions) {
3937
- index = serializeFunction(buffer, key, value, index);
3938
- }
4019
+ key = frame.keys[frame.keyIndex++];
4020
+ value = frame.iterTarget[key];
3939
4021
  }
3940
- }
3941
- else if (object instanceof Map || isMap(object)) {
3942
- const iterator = object.entries();
3943
- let done = false;
3944
- while (!done) {
3945
- const entry = iterator.next();
3946
- done = !!entry.done;
3947
- if (done)
4022
+ else {
4023
+ const arr = frame.iterTarget;
4024
+ if (frame.keyIndex >= arr.length) {
4025
+ buffer[index++] = 0x00;
4026
+ NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
4027
+ if (frame.codeSizeIndex !== null) {
4028
+ NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
4029
+ }
4030
+ path.delete(frame.sourceObject);
4031
+ currentFrame = frame.prev;
3948
4032
  continue;
3949
- const key = entry.value ? entry.value[0] : undefined;
3950
- let value = entry.value ? entry.value[1] : undefined;
3951
- if (typeof value?.toBSON === 'function') {
3952
- value = value.toBSON();
3953
- }
3954
- const type = typeof value;
3955
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
3956
- if (key.match(regexp) != null) {
3957
- throw new BSONError('key ' + key + ' must not contain null bytes');
4033
+ }
4034
+ const i = frame.keyIndex++;
4035
+ key = String(i);
4036
+ value = arr[i];
4037
+ }
4038
+ if (typeof value?.toBSON === 'function') {
4039
+ value = value.toBSON();
4040
+ }
4041
+ if (!frame.isArray && typeof key === 'string' && !(key[0] === '$' && ignoreKeys.has(key))) {
4042
+ if (regexp.test(key)) {
4043
+ throw new BSONError('key ' + key + ' must not contain null bytes');
4044
+ }
4045
+ if (frame.checkKeys) {
4046
+ if ('$' === key[0]) {
4047
+ throw new BSONError('key ' + key + " must not start with '$'");
3958
4048
  }
3959
- if (checkKeys) {
3960
- if ('$' === key[0]) {
3961
- throw new BSONError('key ' + key + " must not start with '$'");
3962
- }
3963
- else if (key.includes('.')) {
3964
- throw new BSONError('key ' + key + " must not contain '.'");
3965
- }
4049
+ else if (key.includes('.')) {
4050
+ throw new BSONError('key ' + key + " must not contain '.'");
3966
4051
  }
3967
4052
  }
3968
- if (value === undefined) {
3969
- if (ignoreUndefined === false)
3970
- index = serializeNull(buffer, key, value, index);
3971
- }
3972
- else if (value === null) {
4053
+ }
4054
+ const type = typeof value;
4055
+ if (value === undefined) {
4056
+ if (frame.isArray || frame.ignoreUndefined === false) {
3973
4057
  index = serializeNull(buffer, key, value, index);
3974
4058
  }
3975
- else if (type === 'string') {
3976
- index = serializeString(buffer, key, value, index);
3977
- }
3978
- else if (type === 'number') {
3979
- index = serializeNumber(buffer, key, value, index);
3980
- }
3981
- else if (type === 'bigint') {
3982
- index = serializeBigInt(buffer, key, value, index);
4059
+ }
4060
+ else if (value === null) {
4061
+ index = serializeNull(buffer, key, value, index);
4062
+ }
4063
+ else if (type === 'string') {
4064
+ index = serializeString(buffer, key, value, index);
4065
+ }
4066
+ else if (type === 'number') {
4067
+ index = serializeNumber(buffer, key, value, index);
4068
+ }
4069
+ else if (type === 'bigint') {
4070
+ index = serializeBigInt(buffer, key, value, index);
4071
+ }
4072
+ else if (type === 'boolean') {
4073
+ index = serializeBoolean(buffer, key, value, index);
4074
+ }
4075
+ else if (type === 'object' && value._bsontype == null) {
4076
+ if (value instanceof Date || isDate(value)) {
4077
+ index = serializeDate(buffer, key, value, index);
3983
4078
  }
3984
- else if (type === 'boolean') {
3985
- index = serializeBoolean(buffer, key, value, index);
4079
+ else if (value instanceof Uint8Array || isUint8Array(value)) {
4080
+ index = serializeBuffer(buffer, key, value, index);
3986
4081
  }
3987
- else if (type === 'object' && value._bsontype == null) {
3988
- if (value instanceof Date || isDate(value)) {
3989
- index = serializeDate(buffer, key, value, index);
3990
- }
3991
- else if (value instanceof Uint8Array || isUint8Array(value)) {
3992
- index = serializeBuffer(buffer, key, value, index);
3993
- }
3994
- else if (value instanceof RegExp || isRegExp(value)) {
3995
- index = serializeRegExp(buffer, key, value, index);
3996
- }
3997
- else {
3998
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
3999
- }
4082
+ else if (value instanceof RegExp || isRegExp(value)) {
4083
+ index = serializeRegExp(buffer, key, value, index);
4000
4084
  }
4001
- else if (type === 'object') {
4002
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4003
- throw new BSONVersionError();
4004
- }
4005
- else if (value._bsontype === 'ObjectId') {
4006
- index = serializeObjectId(buffer, key, value, index);
4007
- }
4008
- else if (value._bsontype === 'Decimal128') {
4009
- index = serializeDecimal128(buffer, key, value, index);
4010
- }
4011
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4012
- index = serializeLong(buffer, key, value, index);
4013
- }
4014
- else if (value._bsontype === 'Double') {
4015
- index = serializeDouble(buffer, key, value, index);
4016
- }
4017
- else if (value._bsontype === 'Code') {
4018
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4019
- }
4020
- else if (value._bsontype === 'Binary') {
4021
- index = serializeBinary(buffer, key, value, index);
4022
- }
4023
- else if (value._bsontype === 'BSONSymbol') {
4024
- index = serializeSymbol(buffer, key, value, index);
4025
- }
4026
- else if (value._bsontype === 'DBRef') {
4027
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4028
- }
4029
- else if (value._bsontype === 'BSONRegExp') {
4030
- index = serializeBSONRegExp(buffer, key, value, index);
4031
- }
4032
- else if (value._bsontype === 'Int32') {
4033
- index = serializeInt32(buffer, key, value, index);
4034
- }
4035
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4036
- index = serializeMinMax(buffer, key, value, index);
4037
- }
4038
- else if (typeof value._bsontype !== 'undefined') {
4039
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4085
+ else {
4086
+ if (path.has(value)) {
4087
+ throw new BSONError('Cannot convert circular structure to BSON');
4040
4088
  }
4041
- }
4042
- else if (type === 'function' && serializeFunctions) {
4043
- index = serializeFunction(buffer, key, value, index);
4044
- }
4045
- }
4046
- }
4047
- else {
4048
- if (typeof object?.toBSON === 'function') {
4049
- object = object.toBSON();
4050
- if (object != null && typeof object !== 'object') {
4051
- throw new BSONError('toBSON function did not return an object');
4089
+ const nestedIsArray = Array.isArray(value);
4090
+ buffer[index++] = nestedIsArray ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
4091
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4092
+ buffer[index++] = 0x00;
4093
+ const nestedStartIndex = index;
4094
+ path.add(value);
4095
+ currentFrame = makeFrame(value, nestedStartIndex, null, frame, frame.checkKeys, frame.ignoreUndefined);
4096
+ index += 4;
4052
4097
  }
4053
4098
  }
4054
- for (const key of Object.keys(object)) {
4055
- let value = object[key];
4056
- if (typeof value?.toBSON === 'function') {
4057
- value = value.toBSON();
4099
+ else if (type === 'object') {
4100
+ if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4101
+ throw new BSONVersionError();
4058
4102
  }
4059
- const type = typeof value;
4060
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
4061
- if (key.match(regexp) != null) {
4062
- throw new BSONError('key ' + key + ' must not contain null bytes');
4063
- }
4064
- if (checkKeys) {
4065
- if ('$' === key[0]) {
4066
- throw new BSONError('key ' + key + " must not start with '$'");
4067
- }
4068
- else if (key.includes('.')) {
4069
- throw new BSONError('key ' + key + " must not contain '.'");
4103
+ const tag = value[bsonType];
4104
+ if (tag === 'ObjectId') {
4105
+ index = serializeObjectId(buffer, key, value, index);
4106
+ }
4107
+ else if (tag === 'Decimal128') {
4108
+ index = serializeDecimal128(buffer, key, value, index);
4109
+ }
4110
+ else if (tag === 'Long' || tag === 'Timestamp') {
4111
+ index = serializeLong(buffer, key, value, index);
4112
+ }
4113
+ else if (tag === 'Double') {
4114
+ index = serializeDouble(buffer, key, value, index);
4115
+ }
4116
+ else if (tag === 'Code') {
4117
+ const codeValue = value;
4118
+ if (codeValue.scope && typeof codeValue.scope === 'object') {
4119
+ buffer[index++] = BSON_DATA_CODE_W_SCOPE;
4120
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4121
+ buffer[index++] = 0x00;
4122
+ const codeTotalSizeIndex = index;
4123
+ index += 4;
4124
+ const functionString = codeValue.code;
4125
+ const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
4126
+ NumberUtils.setInt32LE(buffer, index, codeSize);
4127
+ buffer[index + 4 + codeSize - 1] = 0;
4128
+ index = index + codeSize + 4;
4129
+ const scope = codeValue.scope;
4130
+ if (path.has(scope)) {
4131
+ throw new BSONError('Cannot convert circular structure to BSON');
4070
4132
  }
4133
+ path.add(scope);
4134
+ currentFrame = makeFrame(scope, index, codeTotalSizeIndex, frame, frame.checkKeys, frame.ignoreUndefined);
4135
+ index += 4;
4136
+ }
4137
+ else {
4138
+ buffer[index++] = BSON_DATA_CODE;
4139
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4140
+ buffer[index++] = 0x00;
4141
+ const functionString = codeValue.code.toString();
4142
+ const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
4143
+ NumberUtils.setInt32LE(buffer, index, size);
4144
+ index = index + 4 + size - 1;
4145
+ buffer[index++] = 0;
4071
4146
  }
4072
4147
  }
4073
- if (value === undefined) {
4074
- if (ignoreUndefined === false)
4075
- index = serializeNull(buffer, key, value, index);
4076
- }
4077
- else if (value === null) {
4078
- index = serializeNull(buffer, key, value, index);
4079
- }
4080
- else if (type === 'string') {
4081
- index = serializeString(buffer, key, value, index);
4148
+ else if (tag === 'Binary') {
4149
+ index = serializeBinary(buffer, key, value, index);
4082
4150
  }
4083
- else if (type === 'number') {
4084
- index = serializeNumber(buffer, key, value, index);
4151
+ else if (tag === 'BSONSymbol') {
4152
+ index = serializeSymbol(buffer, key, value, index);
4085
4153
  }
4086
- else if (type === 'bigint') {
4087
- index = serializeBigInt(buffer, key, value, index);
4154
+ else if (tag === 'DBRef') {
4155
+ const dbref = value;
4156
+ const orderedValues = Object.assign({ $ref: dbref.collection, $id: dbref.oid }, dbref.db != null ? { $db: dbref.db } : null, dbref.fields);
4157
+ buffer[index++] = BSON_DATA_OBJECT;
4158
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4159
+ buffer[index++] = 0x00;
4160
+ path.add(orderedValues);
4161
+ currentFrame = makeFrame(orderedValues, index, null, frame, false, true);
4162
+ index += 4;
4088
4163
  }
4089
- else if (type === 'boolean') {
4090
- index = serializeBoolean(buffer, key, value, index);
4164
+ else if (tag === 'BSONRegExp') {
4165
+ index = serializeBSONRegExp(buffer, key, value, index);
4091
4166
  }
4092
- else if (type === 'object' && value._bsontype == null) {
4093
- if (value instanceof Date || isDate(value)) {
4094
- index = serializeDate(buffer, key, value, index);
4095
- }
4096
- else if (value instanceof Uint8Array || isUint8Array(value)) {
4097
- index = serializeBuffer(buffer, key, value, index);
4098
- }
4099
- else if (value instanceof RegExp || isRegExp(value)) {
4100
- index = serializeRegExp(buffer, key, value, index);
4101
- }
4102
- else {
4103
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4104
- }
4167
+ else if (tag === 'Int32') {
4168
+ index = serializeInt32(buffer, key, value, index);
4105
4169
  }
4106
- else if (type === 'object') {
4107
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4108
- throw new BSONVersionError();
4109
- }
4110
- else if (value._bsontype === 'ObjectId') {
4111
- index = serializeObjectId(buffer, key, value, index);
4112
- }
4113
- else if (value._bsontype === 'Decimal128') {
4114
- index = serializeDecimal128(buffer, key, value, index);
4115
- }
4116
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4117
- index = serializeLong(buffer, key, value, index);
4118
- }
4119
- else if (value._bsontype === 'Double') {
4120
- index = serializeDouble(buffer, key, value, index);
4121
- }
4122
- else if (value._bsontype === 'Code') {
4123
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4124
- }
4125
- else if (value._bsontype === 'Binary') {
4126
- index = serializeBinary(buffer, key, value, index);
4127
- }
4128
- else if (value._bsontype === 'BSONSymbol') {
4129
- index = serializeSymbol(buffer, key, value, index);
4130
- }
4131
- else if (value._bsontype === 'DBRef') {
4132
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4133
- }
4134
- else if (value._bsontype === 'BSONRegExp') {
4135
- index = serializeBSONRegExp(buffer, key, value, index);
4136
- }
4137
- else if (value._bsontype === 'Int32') {
4138
- index = serializeInt32(buffer, key, value, index);
4139
- }
4140
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4141
- index = serializeMinMax(buffer, key, value, index);
4142
- }
4143
- else if (typeof value._bsontype !== 'undefined') {
4144
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4145
- }
4170
+ else if (tag === 'MinKey' || tag === 'MaxKey') {
4171
+ index = serializeMinMax(buffer, key, value, index);
4146
4172
  }
4147
- else if (type === 'function' && serializeFunctions) {
4148
- index = serializeFunction(buffer, key, value, index);
4173
+ else if (typeof value._bsontype !== 'undefined') {
4174
+ throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4149
4175
  }
4150
4176
  }
4177
+ else if (type === 'function' && serializeFunctions) {
4178
+ index = serializeFunction(buffer, key, value, index);
4179
+ }
4151
4180
  }
4152
- path.delete(object);
4153
- buffer[index++] = 0x00;
4154
- const size = index - startingIndex;
4155
- startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
4156
4181
  return index;
4157
4182
  }
4158
4183
 
@@ -4308,7 +4333,7 @@ function serializeValue(value, options) {
4308
4333
  if (Array.isArray(value))
4309
4334
  return serializeArray(value, options);
4310
4335
  if (value === undefined)
4311
- return null;
4336
+ return options.ignoreUndefined ? undefined : null;
4312
4337
  if (value instanceof Date || isDate(value)) {
4313
4338
  const dateNum = value.getTime(), inRange = dateNum > -1 && dateNum < 253402318800000;
4314
4339
  if (options.legacy) {
@@ -4435,21 +4460,22 @@ function parse(text, options) {
4435
4460
  return deserializeValue(value, ejsonOptions);
4436
4461
  });
4437
4462
  }
4438
- function stringify(value, replacer, space, options) {
4439
- if (space != null && typeof space === 'object') {
4440
- options = space;
4441
- space = 0;
4463
+ function stringify(value, replacerOrOptions, spaceOrOptions, options) {
4464
+ if (spaceOrOptions != null && typeof spaceOrOptions === 'object') {
4465
+ options = spaceOrOptions;
4466
+ spaceOrOptions = undefined;
4442
4467
  }
4443
- if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
4444
- options = replacer;
4445
- replacer = undefined;
4446
- space = 0;
4468
+ if (replacerOrOptions != null &&
4469
+ typeof replacerOrOptions === 'object' &&
4470
+ !Array.isArray(replacerOrOptions)) {
4471
+ options = replacerOrOptions;
4472
+ replacerOrOptions = undefined;
4447
4473
  }
4448
4474
  const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
4449
4475
  seenObjects: [{ propertyName: '(root)', obj: null }]
4450
4476
  });
4451
4477
  const doc = serializeValue(value, serializeOptions);
4452
- return JSON.stringify(doc, replacer, space);
4478
+ return JSON.stringify(doc, replacerOrOptions, spaceOrOptions);
4453
4479
  }
4454
4480
  function EJSONserialize(value, options) {
4455
4481
  options = options || {};
@@ -4611,7 +4637,7 @@ function serialize(object, options = {}) {
4611
4637
  if (buffer.length < minInternalBufferSize) {
4612
4638
  buffer = ByteUtils.allocate(minInternalBufferSize);
4613
4639
  }
4614
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4640
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4615
4641
  const finishedBuffer = ByteUtils.allocateUnsafe(serializationIndex);
4616
4642
  finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
4617
4643
  return finishedBuffer;
@@ -4621,7 +4647,7 @@ function serializeWithBufferAndIndex(object, finalBuffer, options = {}) {
4621
4647
  const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
4622
4648
  const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
4623
4649
  const startIndex = typeof options.index === 'number' ? options.index : 0;
4624
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4650
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4625
4651
  finalBuffer.set(buffer.subarray(0, serializationIndex), startIndex);
4626
4652
  return startIndex + serializationIndex - 1;
4627
4653
  }