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