bson 7.2.0 → 7.3.1

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