bson 7.2.0 → 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
@@ -698,7 +698,7 @@ class Binary extends BSONValue {
698
698
  !Array.isArray(buffer)) {
699
699
  throw new BSONError('Binary can only be constructed from Uint8Array or number[]');
700
700
  }
701
- this.sub_type = subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT;
701
+ this.sub_type = (subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT) & 0xff;
702
702
  if (buffer == null) {
703
703
  this.buffer = ByteUtils.allocate(Binary.BUFFER_SIZE);
704
704
  this.position = 0;
@@ -804,7 +804,7 @@ class Binary extends BSONValue {
804
804
  if (this.sub_type === Binary.SUBTYPE_UUID) {
805
805
  return new UUID(this.buffer.subarray(0, this.position));
806
806
  }
807
- 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.`);
808
808
  }
809
809
  static createFromHexString(hex, subType) {
810
810
  return new Binary(ByteUtils.fromHex(hex), subType);
@@ -2593,13 +2593,24 @@ class MinKey extends BSONValue {
2593
2593
  }
2594
2594
  }
2595
2595
 
2596
- let PROCESS_UNIQUE = null;
2597
2596
  const __idCache = new WeakMap();
2598
2597
  class ObjectId extends BSONValue {
2599
2598
  get _bsontype() {
2600
2599
  return 'ObjectId';
2601
2600
  }
2602
- 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
+ }
2603
2614
  static cacheHexString;
2604
2615
  buffer;
2605
2616
  constructor(inputId) {
@@ -2676,7 +2687,7 @@ class ObjectId extends BSONValue {
2676
2687
  return hexString;
2677
2688
  }
2678
2689
  static getInc() {
2679
- return (ObjectId.index = (ObjectId.index + 1) % 0xffffff);
2690
+ return (ObjectId.index = (ObjectId.index + 1) % 0x1000000);
2680
2691
  }
2681
2692
  static generate(time) {
2682
2693
  if ('number' !== typeof time) {
@@ -2685,9 +2696,7 @@ class ObjectId extends BSONValue {
2685
2696
  const inc = ObjectId.getInc();
2686
2697
  const buffer = ByteUtils.allocateUnsafe(12);
2687
2698
  NumberUtils.setInt32BE(buffer, 0, time);
2688
- if (PROCESS_UNIQUE === null) {
2689
- PROCESS_UNIQUE = ByteUtils.randomBytes(5);
2690
- }
2699
+ const PROCESS_UNIQUE = this.PROCESS_UNIQUE;
2691
2700
  buffer[4] = PROCESS_UNIQUE[0];
2692
2701
  buffer[5] = PROCESS_UNIQUE[1];
2693
2702
  buffer[6] = PROCESS_UNIQUE[2];
@@ -2805,23 +2814,33 @@ class ObjectId extends BSONValue {
2805
2814
  }
2806
2815
 
2807
2816
  function internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined) {
2808
- let totalLength = 4 + 1;
2809
- if (Array.isArray(object)) {
2810
- for (let i = 0; i < object.length; i++) {
2811
- totalLength += calculateElement(i.toString(), object[i], serializeFunctions, true, ignoreUndefined);
2812
- }
2813
- }
2814
- else {
2815
- if (typeof object?.toBSON === 'function') {
2816
- 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
+ }
2817
2834
  }
2818
- for (const key of Object.keys(object)) {
2819
- 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
+ }
2820
2839
  }
2821
2840
  }
2822
- return totalLength;
2841
+ return total;
2823
2842
  }
2824
- function calculateElement(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false) {
2843
+ function calculateElementSize(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false, objectStack) {
2825
2844
  if (typeof value?.toBSON === 'function') {
2826
2845
  value = value.toBSON();
2827
2846
  }
@@ -2833,21 +2852,21 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2833
2852
  value >= JS_INT_MIN &&
2834
2853
  value <= JS_INT_MAX) {
2835
2854
  if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
2836
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (4 + 1);
2855
+ return ByteUtils.utf8ByteLength(name) + 1 + (4 + 1);
2837
2856
  }
2838
2857
  else {
2839
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2858
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2840
2859
  }
2841
2860
  }
2842
2861
  else {
2843
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2862
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2844
2863
  }
2845
2864
  case 'undefined':
2846
2865
  if (isArray || !ignoreUndefined)
2847
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2866
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2848
2867
  return 0;
2849
2868
  case 'boolean':
2850
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1);
2869
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 1);
2851
2870
  case 'object':
2852
2871
  if (value != null &&
2853
2872
  typeof value._bsontype === 'string' &&
@@ -2855,39 +2874,41 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2855
2874
  throw new BSONVersionError();
2856
2875
  }
2857
2876
  else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
2858
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2877
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2859
2878
  }
2860
2879
  else if (value._bsontype === 'ObjectId') {
2861
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1);
2880
+ return ByteUtils.utf8ByteLength(name) + 1 + (12 + 1);
2862
2881
  }
2863
2882
  else if (value instanceof Date || isDate(value)) {
2864
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2883
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2865
2884
  }
2866
2885
  else if (ArrayBuffer.isView(value) ||
2867
2886
  value instanceof ArrayBuffer ||
2868
2887
  isAnyArrayBuffer(value)) {
2869
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength);
2888
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 4 + 1) + value.byteLength;
2870
2889
  }
2871
2890
  else if (value._bsontype === 'Long' ||
2872
2891
  value._bsontype === 'Double' ||
2873
2892
  value._bsontype === 'Timestamp') {
2874
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2893
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2875
2894
  }
2876
2895
  else if (value._bsontype === 'Decimal128') {
2877
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1);
2896
+ return ByteUtils.utf8ByteLength(name) + 1 + (16 + 1);
2878
2897
  }
2879
2898
  else if (value._bsontype === 'Code') {
2880
2899
  if (value.scope != null && Object.keys(value.scope).length > 0) {
2881
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2900
+ objectStack.push({ obj: value.scope, ignoreUndefined });
2901
+ return (ByteUtils.utf8ByteLength(name) +
2902
+ 1 +
2882
2903
  1 +
2883
2904
  4 +
2884
2905
  4 +
2885
2906
  ByteUtils.utf8ByteLength(value.code.toString()) +
2886
- 1 +
2887
- internalCalculateObjectSize(value.scope, serializeFunctions, ignoreUndefined));
2907
+ 1);
2888
2908
  }
2889
2909
  else {
2890
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2910
+ return (ByteUtils.utf8ByteLength(name) +
2911
+ 1 +
2891
2912
  1 +
2892
2913
  4 +
2893
2914
  ByteUtils.utf8ByteLength(value.code.toString()) +
@@ -2897,19 +2918,14 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2897
2918
  else if (value._bsontype === 'Binary') {
2898
2919
  const binary = value;
2899
2920
  if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
2900
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2901
- (binary.position + 1 + 4 + 1 + 4));
2921
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1 + 4);
2902
2922
  }
2903
2923
  else {
2904
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1));
2924
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1);
2905
2925
  }
2906
2926
  }
2907
2927
  else if (value._bsontype === 'Symbol') {
2908
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2909
- ByteUtils.utf8ByteLength(value.value) +
2910
- 4 +
2911
- 1 +
2912
- 1);
2928
+ return (ByteUtils.utf8ByteLength(name) + 1 + ByteUtils.utf8ByteLength(value.value) + 4 + 1 + 1);
2913
2929
  }
2914
2930
  else if (value._bsontype === 'DBRef') {
2915
2931
  const ordered_values = Object.assign({
@@ -2919,12 +2935,12 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2919
2935
  if (value.db != null) {
2920
2936
  ordered_values['$db'] = value.db;
2921
2937
  }
2922
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2923
- 1 +
2924
- internalCalculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined));
2938
+ objectStack.push({ obj: ordered_values, ignoreUndefined: true });
2939
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2925
2940
  }
2926
2941
  else if (value instanceof RegExp || isRegExp(value)) {
2927
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2942
+ return (ByteUtils.utf8ByteLength(name) +
2943
+ 1 +
2928
2944
  1 +
2929
2945
  ByteUtils.utf8ByteLength(value.source) +
2930
2946
  1 +
@@ -2934,7 +2950,8 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2934
2950
  1);
2935
2951
  }
2936
2952
  else if (value._bsontype === 'BSONRegExp') {
2937
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2953
+ return (ByteUtils.utf8ByteLength(name) +
2954
+ 1 +
2938
2955
  1 +
2939
2956
  ByteUtils.utf8ByteLength(value.pattern) +
2940
2957
  1 +
@@ -2942,13 +2959,13 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2942
2959
  1);
2943
2960
  }
2944
2961
  else {
2945
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2946
- internalCalculateObjectSize(value, serializeFunctions, ignoreUndefined) +
2947
- 1);
2962
+ objectStack.push({ obj: value, ignoreUndefined });
2963
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2948
2964
  }
2949
2965
  case 'function':
2950
2966
  if (serializeFunctions) {
2951
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2967
+ return (ByteUtils.utf8ByteLength(name) +
2968
+ 1 +
2952
2969
  1 +
2953
2970
  4 +
2954
2971
  ByteUtils.utf8ByteLength(value.toString()) +
@@ -2956,7 +2973,7 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2956
2973
  }
2957
2974
  return 0;
2958
2975
  case 'bigint':
2959
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2976
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2960
2977
  case 'symbol':
2961
2978
  return 0;
2962
2979
  default:
@@ -3172,7 +3189,28 @@ function internalDeserialize(buffer, options, isArray) {
3172
3189
  return deserializeObject(buffer, index, options, isArray);
3173
3190
  }
3174
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
+ }
3175
3212
  function deserializeObject(buffer, index, options, isArray = false) {
3213
+ options = { ...options };
3176
3214
  const fieldsAsRaw = options['fieldsAsRaw'] == null ? null : options['fieldsAsRaw'];
3177
3215
  const raw = options['raw'] == null ? false : options['raw'];
3178
3216
  const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
@@ -3223,31 +3261,87 @@ function deserializeObject(buffer, index, options, isArray = false) {
3223
3261
  index += 4;
3224
3262
  if (size < 5 || size > buffer.length)
3225
3263
  throw new BSONError('corrupt bson message');
3226
- const object = isArray ? [] : {};
3264
+ const rootObject = isArray ? [] : {};
3227
3265
  let arrayIndex = 0;
3228
3266
  let isPossibleDBRef = isArray ? false : null;
3267
+ let currentFrame = null;
3268
+ let currentDest = rootObject;
3269
+ let currentIsArray = isArray;
3229
3270
  while (true) {
3230
3271
  const elementType = buffer[index++];
3231
- if (elementType === 0)
3232
- 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
+ }
3233
3314
  let i = index;
3234
3315
  while (buffer[i] !== 0x00 && i < buffer.length) {
3235
3316
  i++;
3236
3317
  }
3237
3318
  if (i >= buffer.byteLength)
3238
3319
  throw new BSONError('Bad BSON Document: illegal CString');
3239
- const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
3240
- let shouldValidateKey = true;
3241
- 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)) {
3242
3330
  shouldValidateKey = validationSetting;
3243
3331
  }
3244
3332
  else {
3245
3333
  shouldValidateKey = !validationSetting;
3246
3334
  }
3247
- 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] === '$') {
3248
3341
  isPossibleDBRef = allowedDBRefKeys.test(name);
3249
3342
  }
3250
3343
  let value;
3344
+ let isDeferredValue = false;
3251
3345
  index = i + 1;
3252
3346
  if (elementType === BSON_DATA_STRING) {
3253
3347
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3293,39 +3387,58 @@ function deserializeObject(buffer, index, options, isArray = false) {
3293
3387
  value = buffer[index++] === 1;
3294
3388
  }
3295
3389
  else if (elementType === BSON_DATA_OBJECT) {
3296
- const _index = index;
3297
3390
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3298
- if (objectSize <= 0 || objectSize > buffer.length - index)
3391
+ if (objectSize < 5 || objectSize > buffer.length - index)
3299
3392
  throw new BSONError('bad embedded document length in bson');
3300
- if (raw) {
3393
+ if (raw || (currentFrame?.raw ?? false)) {
3301
3394
  value = buffer.subarray(index, index + objectSize);
3395
+ index = index + objectSize;
3302
3396
  }
3303
3397
  else {
3304
- let objectOptions = options;
3305
- if (!globalUTFValidation) {
3306
- objectOptions = { ...options, validation: { utf8: shouldValidateKey } };
3307
- }
3308
- 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;
3309
3416
  }
3310
- index = index + objectSize;
3311
3417
  }
3312
3418
  else if (elementType === BSON_DATA_ARRAY) {
3313
- const _index = index;
3314
3419
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3315
- let arrayOptions = options;
3420
+ if (objectSize < 5 || objectSize > buffer.length - index)
3421
+ throw new BSONError('bad embedded array length in bson');
3316
3422
  const stopIndex = index + objectSize;
3317
- if (fieldsAsRaw && fieldsAsRaw[name]) {
3318
- arrayOptions = { ...options, raw: true };
3319
- }
3320
- if (!globalUTFValidation) {
3321
- arrayOptions = { ...arrayOptions, validation: { utf8: shouldValidateKey } };
3322
- }
3323
- value = deserializeObject(buffer, _index, arrayOptions, true);
3324
- index = index + objectSize;
3325
- if (buffer[index - 1] !== 0)
3326
- throw new BSONError('invalid array terminator byte');
3327
- if (index !== stopIndex)
3328
- 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;
3329
3442
  }
3330
3443
  else if (elementType === BSON_DATA_UNDEFINED) {
3331
3444
  value = undefined;
@@ -3497,15 +3610,32 @@ function deserializeObject(buffer, index, options, isArray = false) {
3497
3610
  index = index + stringSize;
3498
3611
  const _index = index;
3499
3612
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3500
- const scopeObject = deserializeObject(buffer, _index, options, false);
3501
- index = index + objectSize;
3613
+ if (objectSize < 5 || objectSize > buffer.length - index)
3614
+ throw new BSONError('bad scope document size in code_w_scope');
3502
3615
  if (totalSize < 4 + 4 + objectSize + stringSize) {
3503
3616
  throw new BSONError('code_w_scope total size is too short, truncating scope');
3504
3617
  }
3505
3618
  if (totalSize > 4 + 4 + objectSize + stringSize) {
3506
3619
  throw new BSONError('code_w_scope total size is too long, clips outer document');
3507
3620
  }
3508
- 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;
3509
3639
  }
3510
3640
  else if (elementType === BSON_DATA_DBPOINTER) {
3511
3641
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3526,18 +3656,14 @@ function deserializeObject(buffer, index, options, isArray = false) {
3526
3656
  else {
3527
3657
  throw new BSONError(`Detected unknown BSON type ${elementType.toString(16)} for fieldname "${name}"`);
3528
3658
  }
3529
- if (name === '__proto__') {
3530
- Object.defineProperty(object, name, {
3531
- value,
3532
- writable: true,
3533
- enumerable: true,
3534
- configurable: true
3535
- });
3536
- }
3537
- else {
3538
- object[name] = value;
3659
+ if (!isDeferredValue) {
3660
+ assignValue(currentDest, name, value);
3539
3661
  }
3540
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;
3541
3667
  if (size !== index - startIndex) {
3542
3668
  if (isArray)
3543
3669
  throw new BSONError('corrupt array bson');
@@ -3545,14 +3671,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
3545
3671
  }
3546
3672
  if (!isPossibleDBRef)
3547
3673
  return object;
3548
- if (isDBRefLike(object)) {
3549
- const copy = Object.assign({}, object);
3550
- delete copy.$ref;
3551
- delete copy.$id;
3552
- delete copy.$db;
3553
- return new DBRef(object.$ref, object.$id, object.$db, copy);
3554
- }
3555
- return object;
3674
+ return toPotentialDbRef(object);
3556
3675
  }
3557
3676
 
3558
3677
  const regexp = /\x00/;
@@ -3661,7 +3780,7 @@ function serializeMinMax(buffer, key, value, index) {
3661
3780
  if (value === null) {
3662
3781
  buffer[index++] = BSON_DATA_NULL;
3663
3782
  }
3664
- else if (value._bsontype === 'MinKey') {
3783
+ else if (value[bsonType] === 'MinKey') {
3665
3784
  buffer[index++] = BSON_DATA_MIN_KEY;
3666
3785
  }
3667
3786
  else {
@@ -3698,19 +3817,6 @@ function serializeBuffer(buffer, key, value, index) {
3698
3817
  index = index + size;
3699
3818
  return index;
3700
3819
  }
3701
- function serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path) {
3702
- if (path.has(value)) {
3703
- throw new BSONError('Cannot convert circular structure to BSON');
3704
- }
3705
- path.add(value);
3706
- buffer[index++] = Array.isArray(value) ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
3707
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3708
- index = index + numberOfWrittenBytes;
3709
- buffer[index++] = 0;
3710
- const endIndex = serializeInto(buffer, value, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3711
- path.delete(value);
3712
- return endIndex;
3713
- }
3714
3820
  function serializeDecimal128(buffer, key, value, index) {
3715
3821
  buffer[index++] = BSON_DATA_DECIMAL128;
3716
3822
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3722,7 +3828,7 @@ function serializeDecimal128(buffer, key, value, index) {
3722
3828
  }
3723
3829
  function serializeLong(buffer, key, value, index) {
3724
3830
  buffer[index++] =
3725
- value._bsontype === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3831
+ value[bsonType] === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3726
3832
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3727
3833
  index = index + numberOfWrittenBytes;
3728
3834
  buffer[index++] = 0;
@@ -3761,38 +3867,6 @@ function serializeFunction(buffer, key, value, index) {
3761
3867
  buffer[index++] = 0;
3762
3868
  return index;
3763
3869
  }
3764
- function serializeCode(buffer, key, value, index, checkKeys = false, depth = 0, serializeFunctions = false, ignoreUndefined = true, path) {
3765
- if (value.scope && typeof value.scope === 'object') {
3766
- buffer[index++] = BSON_DATA_CODE_W_SCOPE;
3767
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3768
- index = index + numberOfWrittenBytes;
3769
- buffer[index++] = 0;
3770
- let startIndex = index;
3771
- const functionString = value.code;
3772
- index = index + 4;
3773
- const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3774
- NumberUtils.setInt32LE(buffer, index, codeSize);
3775
- buffer[index + 4 + codeSize - 1] = 0;
3776
- index = index + codeSize + 4;
3777
- const endIndex = serializeInto(buffer, value.scope, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3778
- index = endIndex - 1;
3779
- const totalSize = endIndex - startIndex;
3780
- startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
3781
- buffer[index++] = 0;
3782
- }
3783
- else {
3784
- buffer[index++] = BSON_DATA_CODE;
3785
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3786
- index = index + numberOfWrittenBytes;
3787
- buffer[index++] = 0;
3788
- const functionString = value.code.toString();
3789
- const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3790
- NumberUtils.setInt32LE(buffer, index, size);
3791
- index = index + 4 + size - 1;
3792
- buffer[index++] = 0;
3793
- }
3794
- return index;
3795
- }
3796
3870
  function serializeBinary(buffer, key, value, index) {
3797
3871
  buffer[index++] = BSON_DATA_BINARY;
3798
3872
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3832,26 +3906,59 @@ function serializeSymbol(buffer, key, value, index) {
3832
3906
  buffer[index++] = 0;
3833
3907
  return index;
3834
3908
  }
3835
- function serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path) {
3836
- buffer[index++] = BSON_DATA_OBJECT;
3837
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3838
- index = index + numberOfWrittenBytes;
3839
- buffer[index++] = 0;
3840
- let startIndex = index;
3841
- let output = {
3842
- $ref: value.collection || value.namespace,
3843
- $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
3844
3959
  };
3845
- if (value.db != null) {
3846
- output.$db = value.db;
3847
- }
3848
- output = Object.assign(output, value.fields);
3849
- const endIndex = serializeInto(buffer, output, false, index, depth + 1, serializeFunctions, true, path);
3850
- const size = endIndex - startIndex;
3851
- startIndex += NumberUtils.setInt32LE(buffer, index, size);
3852
- return endIndex;
3853
3960
  }
3854
- function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializeFunctions, ignoreUndefined, path) {
3961
+ function serializeInto(buffer, object, checkKeys, startingIndex, serializeFunctions, ignoreUndefined, path) {
3855
3962
  if (path == null) {
3856
3963
  if (object == null) {
3857
3964
  buffer[0] = 0x05;
@@ -3879,308 +3986,200 @@ function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializ
3879
3986
  path = new Set();
3880
3987
  }
3881
3988
  path.add(object);
3989
+ let currentFrame = makeFrame(object, startingIndex, null, null, checkKeys, ignoreUndefined);
3882
3990
  let index = startingIndex + 4;
3883
- if (Array.isArray(object)) {
3884
- for (let i = 0; i < object.length; i++) {
3885
- const key = `${i}`;
3886
- let value = object[i];
3887
- if (typeof value?.toBSON === 'function') {
3888
- value = value.toBSON();
3889
- }
3890
- const type = typeof value;
3891
- if (value === undefined) {
3892
- index = serializeNull(buffer, key, value, index);
3893
- }
3894
- else if (value === null) {
3895
- index = serializeNull(buffer, key, value, index);
3896
- }
3897
- else if (type === 'string') {
3898
- index = serializeString(buffer, key, value, index);
3899
- }
3900
- else if (type === 'number') {
3901
- index = serializeNumber(buffer, key, value, index);
3902
- }
3903
- else if (type === 'bigint') {
3904
- index = serializeBigInt(buffer, key, value, index);
3905
- }
3906
- else if (type === 'boolean') {
3907
- index = serializeBoolean(buffer, key, value, index);
3908
- }
3909
- else if (type === 'object' && value._bsontype == null) {
3910
- if (value instanceof Date || isDate(value)) {
3911
- index = serializeDate(buffer, key, value, index);
3912
- }
3913
- else if (value instanceof Uint8Array || isUint8Array(value)) {
3914
- index = serializeBuffer(buffer, key, value, index);
3915
- }
3916
- else if (value instanceof RegExp || isRegExp(value)) {
3917
- index = serializeRegExp(buffer, key, value, index);
3918
- }
3919
- else {
3920
- 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);
3921
4002
  }
4003
+ path.delete(frame.sourceObject);
4004
+ currentFrame = frame.prev;
4005
+ continue;
3922
4006
  }
3923
- else if (type === 'object') {
3924
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
3925
- throw new BSONVersionError();
3926
- }
3927
- else if (value._bsontype === 'ObjectId') {
3928
- index = serializeObjectId(buffer, key, value, index);
3929
- }
3930
- else if (value._bsontype === 'Decimal128') {
3931
- index = serializeDecimal128(buffer, key, value, index);
3932
- }
3933
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
3934
- index = serializeLong(buffer, key, value, index);
3935
- }
3936
- else if (value._bsontype === 'Double') {
3937
- index = serializeDouble(buffer, key, value, index);
3938
- }
3939
- else if (value._bsontype === 'Code') {
3940
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
3941
- }
3942
- else if (value._bsontype === 'Binary') {
3943
- index = serializeBinary(buffer, key, value, index);
3944
- }
3945
- else if (value._bsontype === 'BSONSymbol') {
3946
- index = serializeSymbol(buffer, key, value, index);
3947
- }
3948
- else if (value._bsontype === 'DBRef') {
3949
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
3950
- }
3951
- else if (value._bsontype === 'BSONRegExp') {
3952
- index = serializeBSONRegExp(buffer, key, value, index);
3953
- }
3954
- else if (value._bsontype === 'Int32') {
3955
- index = serializeInt32(buffer, key, value, index);
3956
- }
3957
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
3958
- index = serializeMinMax(buffer, key, value, index);
3959
- }
3960
- else if (typeof value._bsontype !== 'undefined') {
3961
- 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);
3962
4016
  }
4017
+ path.delete(frame.sourceObject);
4018
+ currentFrame = frame.prev;
4019
+ continue;
3963
4020
  }
3964
- else if (type === 'function' && serializeFunctions) {
3965
- index = serializeFunction(buffer, key, value, index);
3966
- }
4021
+ key = frame.keys[frame.keyIndex++];
4022
+ value = frame.iterTarget[key];
3967
4023
  }
3968
- }
3969
- else if (object instanceof Map || isMap(object)) {
3970
- const iterator = object.entries();
3971
- let done = false;
3972
- while (!done) {
3973
- const entry = iterator.next();
3974
- done = !!entry.done;
3975
- 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;
3976
4034
  continue;
3977
- const key = entry.value ? entry.value[0] : undefined;
3978
- let value = entry.value ? entry.value[1] : undefined;
3979
- if (typeof value?.toBSON === 'function') {
3980
- value = value.toBSON();
3981
- }
3982
- const type = typeof value;
3983
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
3984
- if (key.match(regexp) != null) {
3985
- 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 '$'");
3986
4050
  }
3987
- if (checkKeys) {
3988
- if ('$' === key[0]) {
3989
- throw new BSONError('key ' + key + " must not start with '$'");
3990
- }
3991
- else if (key.includes('.')) {
3992
- throw new BSONError('key ' + key + " must not contain '.'");
3993
- }
4051
+ else if (key.includes('.')) {
4052
+ throw new BSONError('key ' + key + " must not contain '.'");
3994
4053
  }
3995
4054
  }
3996
- if (value === undefined) {
3997
- if (ignoreUndefined === false)
3998
- index = serializeNull(buffer, key, value, index);
3999
- }
4000
- else if (value === null) {
4055
+ }
4056
+ const type = typeof value;
4057
+ if (value === undefined) {
4058
+ if (frame.isArray || frame.ignoreUndefined === false) {
4001
4059
  index = serializeNull(buffer, key, value, index);
4002
4060
  }
4003
- else if (type === 'string') {
4004
- index = serializeString(buffer, key, value, index);
4005
- }
4006
- else if (type === 'number') {
4007
- index = serializeNumber(buffer, key, value, index);
4008
- }
4009
- else if (type === 'bigint') {
4010
- 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);
4011
4080
  }
4012
- else if (type === 'boolean') {
4013
- index = serializeBoolean(buffer, key, value, index);
4081
+ else if (value instanceof Uint8Array || isUint8Array(value)) {
4082
+ index = serializeBuffer(buffer, key, value, index);
4014
4083
  }
4015
- else if (type === 'object' && value._bsontype == null) {
4016
- if (value instanceof Date || isDate(value)) {
4017
- index = serializeDate(buffer, key, value, index);
4018
- }
4019
- else if (value instanceof Uint8Array || isUint8Array(value)) {
4020
- index = serializeBuffer(buffer, key, value, index);
4021
- }
4022
- else if (value instanceof RegExp || isRegExp(value)) {
4023
- index = serializeRegExp(buffer, key, value, index);
4024
- }
4025
- else {
4026
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4027
- }
4084
+ else if (value instanceof RegExp || isRegExp(value)) {
4085
+ index = serializeRegExp(buffer, key, value, index);
4028
4086
  }
4029
- else if (type === 'object') {
4030
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4031
- throw new BSONVersionError();
4032
- }
4033
- else if (value._bsontype === 'ObjectId') {
4034
- index = serializeObjectId(buffer, key, value, index);
4035
- }
4036
- else if (value._bsontype === 'Decimal128') {
4037
- index = serializeDecimal128(buffer, key, value, index);
4038
- }
4039
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4040
- index = serializeLong(buffer, key, value, index);
4041
- }
4042
- else if (value._bsontype === 'Double') {
4043
- index = serializeDouble(buffer, key, value, index);
4044
- }
4045
- else if (value._bsontype === 'Code') {
4046
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4047
- }
4048
- else if (value._bsontype === 'Binary') {
4049
- index = serializeBinary(buffer, key, value, index);
4050
- }
4051
- else if (value._bsontype === 'BSONSymbol') {
4052
- index = serializeSymbol(buffer, key, value, index);
4053
- }
4054
- else if (value._bsontype === 'DBRef') {
4055
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4056
- }
4057
- else if (value._bsontype === 'BSONRegExp') {
4058
- index = serializeBSONRegExp(buffer, key, value, index);
4059
- }
4060
- else if (value._bsontype === 'Int32') {
4061
- index = serializeInt32(buffer, key, value, index);
4062
- }
4063
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4064
- index = serializeMinMax(buffer, key, value, index);
4065
- }
4066
- else if (typeof value._bsontype !== 'undefined') {
4067
- 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');
4068
4090
  }
4069
- }
4070
- else if (type === 'function' && serializeFunctions) {
4071
- index = serializeFunction(buffer, key, value, index);
4072
- }
4073
- }
4074
- }
4075
- else {
4076
- if (typeof object?.toBSON === 'function') {
4077
- object = object.toBSON();
4078
- if (object != null && typeof object !== 'object') {
4079
- 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;
4080
4099
  }
4081
4100
  }
4082
- for (const key of Object.keys(object)) {
4083
- let value = object[key];
4084
- if (typeof value?.toBSON === 'function') {
4085
- value = value.toBSON();
4101
+ else if (type === 'object') {
4102
+ if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4103
+ throw new BSONVersionError();
4086
4104
  }
4087
- const type = typeof value;
4088
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
4089
- if (key.match(regexp) != null) {
4090
- throw new BSONError('key ' + key + ' must not contain null bytes');
4091
- }
4092
- if (checkKeys) {
4093
- if ('$' === key[0]) {
4094
- throw new BSONError('key ' + key + " must not start with '$'");
4095
- }
4096
- else if (key.includes('.')) {
4097
- 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');
4098
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;
4099
4148
  }
4100
4149
  }
4101
- if (value === undefined) {
4102
- if (ignoreUndefined === false)
4103
- index = serializeNull(buffer, key, value, index);
4104
- }
4105
- else if (value === null) {
4106
- index = serializeNull(buffer, key, value, index);
4107
- }
4108
- else if (type === 'string') {
4109
- index = serializeString(buffer, key, value, index);
4150
+ else if (tag === 'Binary') {
4151
+ index = serializeBinary(buffer, key, value, index);
4110
4152
  }
4111
- else if (type === 'number') {
4112
- index = serializeNumber(buffer, key, value, index);
4153
+ else if (tag === 'BSONSymbol') {
4154
+ index = serializeSymbol(buffer, key, value, index);
4113
4155
  }
4114
- else if (type === 'bigint') {
4115
- 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;
4116
4165
  }
4117
- else if (type === 'boolean') {
4118
- index = serializeBoolean(buffer, key, value, index);
4166
+ else if (tag === 'BSONRegExp') {
4167
+ index = serializeBSONRegExp(buffer, key, value, index);
4119
4168
  }
4120
- else if (type === 'object' && value._bsontype == null) {
4121
- if (value instanceof Date || isDate(value)) {
4122
- index = serializeDate(buffer, key, value, index);
4123
- }
4124
- else if (value instanceof Uint8Array || isUint8Array(value)) {
4125
- index = serializeBuffer(buffer, key, value, index);
4126
- }
4127
- else if (value instanceof RegExp || isRegExp(value)) {
4128
- index = serializeRegExp(buffer, key, value, index);
4129
- }
4130
- else {
4131
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4132
- }
4169
+ else if (tag === 'Int32') {
4170
+ index = serializeInt32(buffer, key, value, index);
4133
4171
  }
4134
- else if (type === 'object') {
4135
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4136
- throw new BSONVersionError();
4137
- }
4138
- else if (value._bsontype === 'ObjectId') {
4139
- index = serializeObjectId(buffer, key, value, index);
4140
- }
4141
- else if (value._bsontype === 'Decimal128') {
4142
- index = serializeDecimal128(buffer, key, value, index);
4143
- }
4144
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4145
- index = serializeLong(buffer, key, value, index);
4146
- }
4147
- else if (value._bsontype === 'Double') {
4148
- index = serializeDouble(buffer, key, value, index);
4149
- }
4150
- else if (value._bsontype === 'Code') {
4151
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4152
- }
4153
- else if (value._bsontype === 'Binary') {
4154
- index = serializeBinary(buffer, key, value, index);
4155
- }
4156
- else if (value._bsontype === 'BSONSymbol') {
4157
- index = serializeSymbol(buffer, key, value, index);
4158
- }
4159
- else if (value._bsontype === 'DBRef') {
4160
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4161
- }
4162
- else if (value._bsontype === 'BSONRegExp') {
4163
- index = serializeBSONRegExp(buffer, key, value, index);
4164
- }
4165
- else if (value._bsontype === 'Int32') {
4166
- index = serializeInt32(buffer, key, value, index);
4167
- }
4168
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4169
- index = serializeMinMax(buffer, key, value, index);
4170
- }
4171
- else if (typeof value._bsontype !== 'undefined') {
4172
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4173
- }
4172
+ else if (tag === 'MinKey' || tag === 'MaxKey') {
4173
+ index = serializeMinMax(buffer, key, value, index);
4174
4174
  }
4175
- else if (type === 'function' && serializeFunctions) {
4176
- index = serializeFunction(buffer, key, value, index);
4175
+ else if (typeof value._bsontype !== 'undefined') {
4176
+ throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4177
4177
  }
4178
4178
  }
4179
+ else if (type === 'function' && serializeFunctions) {
4180
+ index = serializeFunction(buffer, key, value, index);
4181
+ }
4179
4182
  }
4180
- path.delete(object);
4181
- buffer[index++] = 0x00;
4182
- const size = index - startingIndex;
4183
- startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
4184
4183
  return index;
4185
4184
  }
4186
4185
 
@@ -4463,21 +4462,22 @@ function parse(text, options) {
4463
4462
  return deserializeValue(value, ejsonOptions);
4464
4463
  });
4465
4464
  }
4466
- function stringify(value, replacer, space, options) {
4467
- if (space != null && typeof space === 'object') {
4468
- options = space;
4469
- space = 0;
4465
+ function stringify(value, replacerOrOptions, spaceOrOptions, options) {
4466
+ if (spaceOrOptions != null && typeof spaceOrOptions === 'object') {
4467
+ options = spaceOrOptions;
4468
+ spaceOrOptions = undefined;
4470
4469
  }
4471
- if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
4472
- options = replacer;
4473
- replacer = undefined;
4474
- space = 0;
4470
+ if (replacerOrOptions != null &&
4471
+ typeof replacerOrOptions === 'object' &&
4472
+ !Array.isArray(replacerOrOptions)) {
4473
+ options = replacerOrOptions;
4474
+ replacerOrOptions = undefined;
4475
4475
  }
4476
4476
  const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
4477
4477
  seenObjects: [{ propertyName: '(root)', obj: null }]
4478
4478
  });
4479
4479
  const doc = serializeValue(value, serializeOptions);
4480
- return JSON.stringify(doc, replacer, space);
4480
+ return JSON.stringify(doc, replacerOrOptions, spaceOrOptions);
4481
4481
  }
4482
4482
  function EJSONserialize(value, options) {
4483
4483
  options = options || {};
@@ -4639,7 +4639,7 @@ function serialize(object, options = {}) {
4639
4639
  if (buffer.length < minInternalBufferSize) {
4640
4640
  buffer = ByteUtils.allocate(minInternalBufferSize);
4641
4641
  }
4642
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4642
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4643
4643
  const finishedBuffer = ByteUtils.allocateUnsafe(serializationIndex);
4644
4644
  finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
4645
4645
  return finishedBuffer;
@@ -4649,7 +4649,7 @@ function serializeWithBufferAndIndex(object, finalBuffer, options = {}) {
4649
4649
  const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
4650
4650
  const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
4651
4651
  const startIndex = typeof options.index === 'number' ? options.index : 0;
4652
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4652
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4653
4653
  finalBuffer.set(buffer.subarray(0, serializationIndex), startIndex);
4654
4654
  return startIndex + serializationIndex - 1;
4655
4655
  }