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