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