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.rn.cjs CHANGED
@@ -706,7 +706,7 @@ class Binary extends BSONValue {
706
706
  !Array.isArray(buffer)) {
707
707
  throw new BSONError('Binary can only be constructed from Uint8Array or number[]');
708
708
  }
709
- this.sub_type = subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT;
709
+ this.sub_type = (subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT) & 0xff;
710
710
  if (buffer == null) {
711
711
  this.buffer = ByteUtils.allocate(Binary.BUFFER_SIZE);
712
712
  this.position = 0;
@@ -812,7 +812,7 @@ class Binary extends BSONValue {
812
812
  if (this.sub_type === Binary.SUBTYPE_UUID) {
813
813
  return new UUID(this.buffer.subarray(0, this.position));
814
814
  }
815
- throw new BSONError(`Binary sub_type "${this.sub_type}" is not supported for converting to UUID. Only "${Binary.SUBTYPE_UUID}" is currently supported.`);
815
+ 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.`);
816
816
  }
817
817
  static createFromHexString(hex, subType) {
818
818
  return new Binary(ByteUtils.fromHex(hex), subType);
@@ -1324,7 +1324,7 @@ class Long extends BSONValue {
1324
1324
  }
1325
1325
  if (value < 0)
1326
1326
  return Long.fromNumber(-value, unsigned).neg();
1327
- return Long.fromBits(value % TWO_PWR_32_DBL | 0, (value / TWO_PWR_32_DBL) | 0, unsigned);
1327
+ return Long.fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned);
1328
1328
  }
1329
1329
  static fromBigInt(value, unsigned) {
1330
1330
  const FROM_BIGINT_BIT_MASK = 0xffffffffn;
@@ -2601,13 +2601,24 @@ class MinKey extends BSONValue {
2601
2601
  }
2602
2602
  }
2603
2603
 
2604
- let PROCESS_UNIQUE = null;
2605
2604
  const __idCache = new WeakMap();
2606
2605
  class ObjectId extends BSONValue {
2607
2606
  get _bsontype() {
2608
2607
  return 'ObjectId';
2609
2608
  }
2610
- static index = Math.floor(Math.random() * 0xffffff);
2609
+ static index = 0;
2610
+ static PROCESS_UNIQUE = null;
2611
+ static resetState = () => {
2612
+ this.index = Math.floor(Math.random() * 0x1000000);
2613
+ this.PROCESS_UNIQUE = ByteUtils.randomBytes(5);
2614
+ };
2615
+ static {
2616
+ this.resetState();
2617
+ const { startupSnapshot } = globalThis?.process?.getBuiltinModule?.('v8') ?? {};
2618
+ if (startupSnapshot?.isBuildingSnapshot?.()) {
2619
+ startupSnapshot?.addDeserializeCallback?.(this.resetState);
2620
+ }
2621
+ }
2611
2622
  static cacheHexString;
2612
2623
  buffer;
2613
2624
  constructor(inputId) {
@@ -2684,7 +2695,7 @@ class ObjectId extends BSONValue {
2684
2695
  return hexString;
2685
2696
  }
2686
2697
  static getInc() {
2687
- return (ObjectId.index = (ObjectId.index + 1) % 0xffffff);
2698
+ return (ObjectId.index = (ObjectId.index + 1) % 0x1000000);
2688
2699
  }
2689
2700
  static generate(time) {
2690
2701
  if ('number' !== typeof time) {
@@ -2693,9 +2704,7 @@ class ObjectId extends BSONValue {
2693
2704
  const inc = ObjectId.getInc();
2694
2705
  const buffer = ByteUtils.allocateUnsafe(12);
2695
2706
  NumberUtils.setInt32BE(buffer, 0, time);
2696
- if (PROCESS_UNIQUE === null) {
2697
- PROCESS_UNIQUE = ByteUtils.randomBytes(5);
2698
- }
2707
+ const PROCESS_UNIQUE = this.PROCESS_UNIQUE;
2699
2708
  buffer[4] = PROCESS_UNIQUE[0];
2700
2709
  buffer[5] = PROCESS_UNIQUE[1];
2701
2710
  buffer[6] = PROCESS_UNIQUE[2];
@@ -2813,23 +2822,33 @@ class ObjectId extends BSONValue {
2813
2822
  }
2814
2823
 
2815
2824
  function internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined) {
2816
- let totalLength = 4 + 1;
2817
- if (Array.isArray(object)) {
2818
- for (let i = 0; i < object.length; i++) {
2819
- totalLength += calculateElement(i.toString(), object[i], serializeFunctions, true, ignoreUndefined);
2820
- }
2821
- }
2822
- else {
2823
- if (typeof object?.toBSON === 'function') {
2824
- object = object.toBSON();
2825
+ const objectStack = [
2826
+ { obj: object, ignoreUndefined: ignoreUndefined ?? false }
2827
+ ];
2828
+ let total = 0;
2829
+ while (objectStack.length > 0) {
2830
+ const { obj, ignoreUndefined: frameIgnoreUndefined } = objectStack.pop();
2831
+ total += 5;
2832
+ const isObjArray = Array.isArray(obj);
2833
+ let target = obj;
2834
+ if (!isObjArray && typeof obj?.toBSON === 'function') {
2835
+ target = obj.toBSON();
2836
+ }
2837
+ if (isObjArray) {
2838
+ const array = target;
2839
+ for (let i = 0; i < array.length; i++) {
2840
+ total += calculateElementSize(i.toString(), array[i], serializeFunctions, true, frameIgnoreUndefined, objectStack);
2841
+ }
2825
2842
  }
2826
- for (const key of Object.keys(object)) {
2827
- totalLength += calculateElement(key, object[key], serializeFunctions, false, ignoreUndefined);
2843
+ else {
2844
+ for (const key of Object.keys(target)) {
2845
+ total += calculateElementSize(key, target[key], serializeFunctions, false, frameIgnoreUndefined, objectStack);
2846
+ }
2828
2847
  }
2829
2848
  }
2830
- return totalLength;
2849
+ return total;
2831
2850
  }
2832
- function calculateElement(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false) {
2851
+ function calculateElementSize(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false, objectStack) {
2833
2852
  if (typeof value?.toBSON === 'function') {
2834
2853
  value = value.toBSON();
2835
2854
  }
@@ -2841,21 +2860,21 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2841
2860
  value >= JS_INT_MIN &&
2842
2861
  value <= JS_INT_MAX) {
2843
2862
  if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
2844
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (4 + 1);
2863
+ return ByteUtils.utf8ByteLength(name) + 1 + (4 + 1);
2845
2864
  }
2846
2865
  else {
2847
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2866
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2848
2867
  }
2849
2868
  }
2850
2869
  else {
2851
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2870
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2852
2871
  }
2853
2872
  case 'undefined':
2854
2873
  if (isArray || !ignoreUndefined)
2855
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2874
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2856
2875
  return 0;
2857
2876
  case 'boolean':
2858
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1);
2877
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 1);
2859
2878
  case 'object':
2860
2879
  if (value != null &&
2861
2880
  typeof value._bsontype === 'string' &&
@@ -2863,39 +2882,41 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2863
2882
  throw new BSONVersionError();
2864
2883
  }
2865
2884
  else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
2866
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2885
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2867
2886
  }
2868
2887
  else if (value._bsontype === 'ObjectId') {
2869
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1);
2888
+ return ByteUtils.utf8ByteLength(name) + 1 + (12 + 1);
2870
2889
  }
2871
2890
  else if (value instanceof Date || isDate(value)) {
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 (ArrayBuffer.isView(value) ||
2875
2894
  value instanceof ArrayBuffer ||
2876
2895
  isAnyArrayBuffer(value)) {
2877
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength);
2896
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 4 + 1) + value.byteLength;
2878
2897
  }
2879
2898
  else if (value._bsontype === 'Long' ||
2880
2899
  value._bsontype === 'Double' ||
2881
2900
  value._bsontype === 'Timestamp') {
2882
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2901
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2883
2902
  }
2884
2903
  else if (value._bsontype === 'Decimal128') {
2885
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1);
2904
+ return ByteUtils.utf8ByteLength(name) + 1 + (16 + 1);
2886
2905
  }
2887
2906
  else if (value._bsontype === 'Code') {
2888
2907
  if (value.scope != null && Object.keys(value.scope).length > 0) {
2889
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2908
+ objectStack.push({ obj: value.scope, ignoreUndefined });
2909
+ return (ByteUtils.utf8ByteLength(name) +
2910
+ 1 +
2890
2911
  1 +
2891
2912
  4 +
2892
2913
  4 +
2893
2914
  ByteUtils.utf8ByteLength(value.code.toString()) +
2894
- 1 +
2895
- internalCalculateObjectSize(value.scope, serializeFunctions, ignoreUndefined));
2915
+ 1);
2896
2916
  }
2897
2917
  else {
2898
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2918
+ return (ByteUtils.utf8ByteLength(name) +
2919
+ 1 +
2899
2920
  1 +
2900
2921
  4 +
2901
2922
  ByteUtils.utf8ByteLength(value.code.toString()) +
@@ -2905,19 +2926,14 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2905
2926
  else if (value._bsontype === 'Binary') {
2906
2927
  const binary = value;
2907
2928
  if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
2908
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2909
- (binary.position + 1 + 4 + 1 + 4));
2929
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1 + 4);
2910
2930
  }
2911
2931
  else {
2912
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1));
2932
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1);
2913
2933
  }
2914
2934
  }
2915
2935
  else if (value._bsontype === 'Symbol') {
2916
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2917
- ByteUtils.utf8ByteLength(value.value) +
2918
- 4 +
2919
- 1 +
2920
- 1);
2936
+ return (ByteUtils.utf8ByteLength(name) + 1 + ByteUtils.utf8ByteLength(value.value) + 4 + 1 + 1);
2921
2937
  }
2922
2938
  else if (value._bsontype === 'DBRef') {
2923
2939
  const ordered_values = Object.assign({
@@ -2927,12 +2943,12 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2927
2943
  if (value.db != null) {
2928
2944
  ordered_values['$db'] = value.db;
2929
2945
  }
2930
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2931
- 1 +
2932
- internalCalculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined));
2946
+ objectStack.push({ obj: ordered_values, ignoreUndefined: true });
2947
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2933
2948
  }
2934
2949
  else if (value instanceof RegExp || isRegExp(value)) {
2935
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2950
+ return (ByteUtils.utf8ByteLength(name) +
2951
+ 1 +
2936
2952
  1 +
2937
2953
  ByteUtils.utf8ByteLength(value.source) +
2938
2954
  1 +
@@ -2942,7 +2958,8 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2942
2958
  1);
2943
2959
  }
2944
2960
  else if (value._bsontype === 'BSONRegExp') {
2945
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2961
+ return (ByteUtils.utf8ByteLength(name) +
2962
+ 1 +
2946
2963
  1 +
2947
2964
  ByteUtils.utf8ByteLength(value.pattern) +
2948
2965
  1 +
@@ -2950,13 +2967,13 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2950
2967
  1);
2951
2968
  }
2952
2969
  else {
2953
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2954
- internalCalculateObjectSize(value, serializeFunctions, ignoreUndefined) +
2955
- 1);
2970
+ objectStack.push({ obj: value, ignoreUndefined });
2971
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2956
2972
  }
2957
2973
  case 'function':
2958
2974
  if (serializeFunctions) {
2959
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2975
+ return (ByteUtils.utf8ByteLength(name) +
2976
+ 1 +
2960
2977
  1 +
2961
2978
  4 +
2962
2979
  ByteUtils.utf8ByteLength(value.toString()) +
@@ -2964,13 +2981,12 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2964
2981
  }
2965
2982
  return 0;
2966
2983
  case 'bigint':
2967
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2984
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2968
2985
  case 'symbol':
2969
2986
  return 0;
2970
2987
  default:
2971
2988
  throw new BSONError(`Unrecognized JS type: ${typeof value}`);
2972
2989
  }
2973
- return 0;
2974
2990
  }
2975
2991
 
2976
2992
  function alphabetize(str) {
@@ -3181,7 +3197,28 @@ function internalDeserialize(buffer, options, isArray) {
3181
3197
  return deserializeObject(buffer, index, options, isArray);
3182
3198
  }
3183
3199
  const allowedDBRefKeys = /^\$ref$|^\$id$|^\$db$/;
3200
+ function assignValue(dest, name, value) {
3201
+ if (name === '__proto__') {
3202
+ Object.defineProperty(dest, name, {
3203
+ value,
3204
+ writable: true,
3205
+ enumerable: true,
3206
+ configurable: true
3207
+ });
3208
+ }
3209
+ else {
3210
+ dest[name] = value;
3211
+ }
3212
+ }
3213
+ function toPotentialDbRef(doc) {
3214
+ if (isDBRefLike(doc)) {
3215
+ const { $ref, $id, $db, ...fields } = doc;
3216
+ return new DBRef($ref, $id, $db, fields);
3217
+ }
3218
+ return doc;
3219
+ }
3184
3220
  function deserializeObject(buffer, index, options, isArray = false) {
3221
+ options = { ...options };
3185
3222
  const fieldsAsRaw = options['fieldsAsRaw'] == null ? null : options['fieldsAsRaw'];
3186
3223
  const raw = options['raw'] == null ? false : options['raw'];
3187
3224
  const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
@@ -3232,32 +3269,87 @@ function deserializeObject(buffer, index, options, isArray = false) {
3232
3269
  index += 4;
3233
3270
  if (size < 5 || size > buffer.length)
3234
3271
  throw new BSONError('corrupt bson message');
3235
- const object = isArray ? [] : {};
3272
+ const rootObject = isArray ? [] : {};
3236
3273
  let arrayIndex = 0;
3237
- const done = false;
3238
3274
  let isPossibleDBRef = isArray ? false : null;
3239
- while (!done) {
3275
+ let currentFrame = null;
3276
+ let currentDest = rootObject;
3277
+ let currentIsArray = isArray;
3278
+ while (true) {
3240
3279
  const elementType = buffer[index++];
3241
- if (elementType === 0)
3242
- break;
3280
+ if (elementType === 0) {
3281
+ if (currentFrame) {
3282
+ if (index === currentFrame.lastIndex) {
3283
+ const completedFrame = currentFrame;
3284
+ currentFrame = completedFrame.prev;
3285
+ if (currentFrame === null) {
3286
+ currentDest = rootObject;
3287
+ currentIsArray = isArray;
3288
+ }
3289
+ else {
3290
+ currentDest = currentFrame.holdingDocument;
3291
+ currentIsArray = currentFrame.isArray;
3292
+ }
3293
+ let result = completedFrame.holdingDocument;
3294
+ switch (completedFrame.elementType) {
3295
+ case BSON_DATA_OBJECT:
3296
+ if (completedFrame.isPossibleDBRef) {
3297
+ result = toPotentialDbRef(result);
3298
+ }
3299
+ break;
3300
+ case BSON_DATA_ARRAY:
3301
+ break;
3302
+ case BSON_DATA_CODE_W_SCOPE:
3303
+ result = new Code(completedFrame.functionString, completedFrame.holdingDocument);
3304
+ break;
3305
+ default:
3306
+ throw new BSONError('Unexpected element type in frame stack');
3307
+ }
3308
+ assignValue(currentDest, completedFrame.propertyName, result);
3309
+ continue;
3310
+ }
3311
+ else {
3312
+ if (currentFrame.elementType === BSON_DATA_ARRAY) {
3313
+ throw new BSONError('corrupted array bson');
3314
+ }
3315
+ throw new BSONError('Bad BSON Document: object not properly terminated');
3316
+ }
3317
+ }
3318
+ else {
3319
+ break;
3320
+ }
3321
+ }
3243
3322
  let i = index;
3244
3323
  while (buffer[i] !== 0x00 && i < buffer.length) {
3245
3324
  i++;
3246
3325
  }
3247
3326
  if (i >= buffer.byteLength)
3248
3327
  throw new BSONError('Bad BSON Document: illegal CString');
3249
- const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
3250
- let shouldValidateKey = true;
3251
- if (globalUTFValidation || utf8KeysSet?.has(name)) {
3328
+ const name = currentIsArray
3329
+ ? currentFrame !== null
3330
+ ? currentFrame.arrayIndex++
3331
+ : arrayIndex++
3332
+ : ByteUtils.toUTF8(buffer, index, i, false);
3333
+ let shouldValidateKey;
3334
+ if (currentFrame !== null) {
3335
+ shouldValidateKey = currentFrame.validationSetting;
3336
+ }
3337
+ else if (globalUTFValidation || utf8KeysSet?.has(name)) {
3252
3338
  shouldValidateKey = validationSetting;
3253
3339
  }
3254
3340
  else {
3255
3341
  shouldValidateKey = !validationSetting;
3256
3342
  }
3257
- if (isPossibleDBRef !== false && name[0] === '$') {
3343
+ if (currentFrame !== null) {
3344
+ if (currentFrame.isPossibleDBRef !== false && typeof name === 'string' && name[0] === '$') {
3345
+ currentFrame.isPossibleDBRef = allowedDBRefKeys.test(name);
3346
+ }
3347
+ }
3348
+ else if (isPossibleDBRef !== false && name[0] === '$') {
3258
3349
  isPossibleDBRef = allowedDBRefKeys.test(name);
3259
3350
  }
3260
3351
  let value;
3352
+ let isDeferredValue = false;
3261
3353
  index = i + 1;
3262
3354
  if (elementType === BSON_DATA_STRING) {
3263
3355
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3303,39 +3395,58 @@ function deserializeObject(buffer, index, options, isArray = false) {
3303
3395
  value = buffer[index++] === 1;
3304
3396
  }
3305
3397
  else if (elementType === BSON_DATA_OBJECT) {
3306
- const _index = index;
3307
3398
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3308
- if (objectSize <= 0 || objectSize > buffer.length - index)
3399
+ if (objectSize < 5 || objectSize > buffer.length - index)
3309
3400
  throw new BSONError('bad embedded document length in bson');
3310
- if (raw) {
3401
+ if (raw || (currentFrame?.raw ?? false)) {
3311
3402
  value = buffer.subarray(index, index + objectSize);
3403
+ index = index + objectSize;
3312
3404
  }
3313
3405
  else {
3314
- let objectOptions = options;
3315
- if (!globalUTFValidation) {
3316
- objectOptions = { ...options, validation: { utf8: shouldValidateKey } };
3317
- }
3318
- value = deserializeObject(buffer, _index, objectOptions, false);
3406
+ isDeferredValue = true;
3407
+ const objectFrame = {
3408
+ holdingDocument: {},
3409
+ elementType: BSON_DATA_OBJECT,
3410
+ propertyName: name,
3411
+ functionString: null,
3412
+ lastIndex: index + objectSize,
3413
+ isArray: false,
3414
+ arrayIndex: 0,
3415
+ raw: false,
3416
+ isPossibleDBRef: null,
3417
+ validationSetting: shouldValidateKey,
3418
+ prev: currentFrame
3419
+ };
3420
+ currentFrame = objectFrame;
3421
+ currentDest = objectFrame.holdingDocument;
3422
+ currentIsArray = false;
3423
+ index = index + 4;
3319
3424
  }
3320
- index = index + objectSize;
3321
3425
  }
3322
3426
  else if (elementType === BSON_DATA_ARRAY) {
3323
- const _index = index;
3324
3427
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3325
- let arrayOptions = options;
3428
+ if (objectSize < 5 || objectSize > buffer.length - index)
3429
+ throw new BSONError('bad embedded array length in bson');
3326
3430
  const stopIndex = index + objectSize;
3327
- if (fieldsAsRaw && fieldsAsRaw[name]) {
3328
- arrayOptions = { ...options, raw: true };
3329
- }
3330
- if (!globalUTFValidation) {
3331
- arrayOptions = { ...arrayOptions, validation: { utf8: shouldValidateKey } };
3332
- }
3333
- value = deserializeObject(buffer, _index, arrayOptions, true);
3334
- index = index + objectSize;
3335
- if (buffer[index - 1] !== 0)
3336
- throw new BSONError('invalid array terminator byte');
3337
- if (index !== stopIndex)
3338
- throw new BSONError('corrupted array bson');
3431
+ const arrayRaw = !!(fieldsAsRaw && fieldsAsRaw[name]) || (currentFrame?.raw ?? false);
3432
+ isDeferredValue = true;
3433
+ const arrayFrame = {
3434
+ holdingDocument: [],
3435
+ elementType: BSON_DATA_ARRAY,
3436
+ propertyName: name,
3437
+ functionString: null,
3438
+ lastIndex: stopIndex,
3439
+ isArray: true,
3440
+ arrayIndex: 0,
3441
+ raw: arrayRaw,
3442
+ isPossibleDBRef: false,
3443
+ validationSetting: shouldValidateKey,
3444
+ prev: currentFrame
3445
+ };
3446
+ currentFrame = arrayFrame;
3447
+ currentDest = arrayFrame.holdingDocument;
3448
+ currentIsArray = true;
3449
+ index = index + 4;
3339
3450
  }
3340
3451
  else if (elementType === BSON_DATA_UNDEFINED) {
3341
3452
  value = undefined;
@@ -3507,15 +3618,32 @@ function deserializeObject(buffer, index, options, isArray = false) {
3507
3618
  index = index + stringSize;
3508
3619
  const _index = index;
3509
3620
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3510
- const scopeObject = deserializeObject(buffer, _index, options, false);
3511
- index = index + objectSize;
3621
+ if (objectSize < 5 || objectSize > buffer.length - index)
3622
+ throw new BSONError('bad scope document size in code_w_scope');
3512
3623
  if (totalSize < 4 + 4 + objectSize + stringSize) {
3513
3624
  throw new BSONError('code_w_scope total size is too short, truncating scope');
3514
3625
  }
3515
3626
  if (totalSize > 4 + 4 + objectSize + stringSize) {
3516
3627
  throw new BSONError('code_w_scope total size is too long, clips outer document');
3517
3628
  }
3518
- value = new Code(functionString, scopeObject);
3629
+ isDeferredValue = true;
3630
+ const scopeFrame = {
3631
+ holdingDocument: {},
3632
+ elementType: BSON_DATA_CODE_W_SCOPE,
3633
+ propertyName: name,
3634
+ functionString: functionString,
3635
+ lastIndex: _index + objectSize,
3636
+ isArray: false,
3637
+ arrayIndex: 0,
3638
+ raw: false,
3639
+ isPossibleDBRef: null,
3640
+ validationSetting: shouldValidateKey,
3641
+ prev: currentFrame
3642
+ };
3643
+ currentFrame = scopeFrame;
3644
+ currentDest = scopeFrame.holdingDocument;
3645
+ currentIsArray = false;
3646
+ index = index + 4;
3519
3647
  }
3520
3648
  else if (elementType === BSON_DATA_DBPOINTER) {
3521
3649
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3536,18 +3664,14 @@ function deserializeObject(buffer, index, options, isArray = false) {
3536
3664
  else {
3537
3665
  throw new BSONError(`Detected unknown BSON type ${elementType.toString(16)} for fieldname "${name}"`);
3538
3666
  }
3539
- if (name === '__proto__') {
3540
- Object.defineProperty(object, name, {
3541
- value,
3542
- writable: true,
3543
- enumerable: true,
3544
- configurable: true
3545
- });
3546
- }
3547
- else {
3548
- object[name] = value;
3667
+ if (!isDeferredValue) {
3668
+ assignValue(currentDest, name, value);
3549
3669
  }
3550
3670
  }
3671
+ if (currentFrame !== null) {
3672
+ throw new BSONError('corrupted bson, more objects expected based on the current document size');
3673
+ }
3674
+ const object = rootObject;
3551
3675
  if (size !== index - startIndex) {
3552
3676
  if (isArray)
3553
3677
  throw new BSONError('corrupt array bson');
@@ -3555,14 +3679,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
3555
3679
  }
3556
3680
  if (!isPossibleDBRef)
3557
3681
  return object;
3558
- if (isDBRefLike(object)) {
3559
- const copy = Object.assign({}, object);
3560
- delete copy.$ref;
3561
- delete copy.$id;
3562
- delete copy.$db;
3563
- return new DBRef(object.$ref, object.$id, object.$db, copy);
3564
- }
3565
- return object;
3682
+ return toPotentialDbRef(object);
3566
3683
  }
3567
3684
 
3568
3685
  const regexp = /\x00/;
@@ -3671,7 +3788,7 @@ function serializeMinMax(buffer, key, value, index) {
3671
3788
  if (value === null) {
3672
3789
  buffer[index++] = BSON_DATA_NULL;
3673
3790
  }
3674
- else if (value._bsontype === 'MinKey') {
3791
+ else if (value[bsonType] === 'MinKey') {
3675
3792
  buffer[index++] = BSON_DATA_MIN_KEY;
3676
3793
  }
3677
3794
  else {
@@ -3708,19 +3825,6 @@ function serializeBuffer(buffer, key, value, index) {
3708
3825
  index = index + size;
3709
3826
  return index;
3710
3827
  }
3711
- function serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path) {
3712
- if (path.has(value)) {
3713
- throw new BSONError('Cannot convert circular structure to BSON');
3714
- }
3715
- path.add(value);
3716
- buffer[index++] = Array.isArray(value) ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
3717
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3718
- index = index + numberOfWrittenBytes;
3719
- buffer[index++] = 0;
3720
- const endIndex = serializeInto(buffer, value, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3721
- path.delete(value);
3722
- return endIndex;
3723
- }
3724
3828
  function serializeDecimal128(buffer, key, value, index) {
3725
3829
  buffer[index++] = BSON_DATA_DECIMAL128;
3726
3830
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3732,7 +3836,7 @@ function serializeDecimal128(buffer, key, value, index) {
3732
3836
  }
3733
3837
  function serializeLong(buffer, key, value, index) {
3734
3838
  buffer[index++] =
3735
- value._bsontype === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3839
+ value[bsonType] === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3736
3840
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3737
3841
  index = index + numberOfWrittenBytes;
3738
3842
  buffer[index++] = 0;
@@ -3771,38 +3875,6 @@ function serializeFunction(buffer, key, value, index) {
3771
3875
  buffer[index++] = 0;
3772
3876
  return index;
3773
3877
  }
3774
- function serializeCode(buffer, key, value, index, checkKeys = false, depth = 0, serializeFunctions = false, ignoreUndefined = true, path) {
3775
- if (value.scope && typeof value.scope === 'object') {
3776
- buffer[index++] = BSON_DATA_CODE_W_SCOPE;
3777
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3778
- index = index + numberOfWrittenBytes;
3779
- buffer[index++] = 0;
3780
- let startIndex = index;
3781
- const functionString = value.code;
3782
- index = index + 4;
3783
- const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3784
- NumberUtils.setInt32LE(buffer, index, codeSize);
3785
- buffer[index + 4 + codeSize - 1] = 0;
3786
- index = index + codeSize + 4;
3787
- const endIndex = serializeInto(buffer, value.scope, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3788
- index = endIndex - 1;
3789
- const totalSize = endIndex - startIndex;
3790
- startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
3791
- buffer[index++] = 0;
3792
- }
3793
- else {
3794
- buffer[index++] = BSON_DATA_CODE;
3795
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3796
- index = index + numberOfWrittenBytes;
3797
- buffer[index++] = 0;
3798
- const functionString = value.code.toString();
3799
- const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3800
- NumberUtils.setInt32LE(buffer, index, size);
3801
- index = index + 4 + size - 1;
3802
- buffer[index++] = 0;
3803
- }
3804
- return index;
3805
- }
3806
3878
  function serializeBinary(buffer, key, value, index) {
3807
3879
  buffer[index++] = BSON_DATA_BINARY;
3808
3880
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3842,26 +3914,59 @@ function serializeSymbol(buffer, key, value, index) {
3842
3914
  buffer[index++] = 0;
3843
3915
  return index;
3844
3916
  }
3845
- function serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path) {
3846
- buffer[index++] = BSON_DATA_OBJECT;
3847
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3848
- index = index + numberOfWrittenBytes;
3849
- buffer[index++] = 0;
3850
- let startIndex = index;
3851
- let output = {
3852
- $ref: value.collection || value.namespace,
3853
- $id: value.oid
3917
+ function makeFrame(sourceObject, objectSizeIndex, codeSizeIndex, prev, checkKeys, ignoreUndefined) {
3918
+ if (Array.isArray(sourceObject)) {
3919
+ return {
3920
+ sourceObject,
3921
+ isArray: true,
3922
+ objectSizeIndex,
3923
+ codeSizeIndex,
3924
+ iterTarget: sourceObject,
3925
+ keys: null,
3926
+ keyIndex: 0,
3927
+ mapIterator: null,
3928
+ prev,
3929
+ checkKeys,
3930
+ ignoreUndefined
3931
+ };
3932
+ }
3933
+ if (sourceObject instanceof Map || isMap(sourceObject)) {
3934
+ return {
3935
+ sourceObject,
3936
+ isArray: false,
3937
+ objectSizeIndex,
3938
+ codeSizeIndex,
3939
+ iterTarget: sourceObject,
3940
+ keys: null,
3941
+ keyIndex: 0,
3942
+ mapIterator: sourceObject.entries(),
3943
+ prev,
3944
+ checkKeys,
3945
+ ignoreUndefined
3946
+ };
3947
+ }
3948
+ let target = sourceObject;
3949
+ if (typeof target?.toBSON === 'function') {
3950
+ target = target.toBSON();
3951
+ if (target != null && typeof target !== 'object') {
3952
+ throw new BSONError('toBSON function did not return an object');
3953
+ }
3954
+ }
3955
+ return {
3956
+ sourceObject,
3957
+ isArray: false,
3958
+ objectSizeIndex,
3959
+ codeSizeIndex,
3960
+ iterTarget: target,
3961
+ keys: Object.keys(target),
3962
+ keyIndex: 0,
3963
+ mapIterator: null,
3964
+ prev,
3965
+ checkKeys,
3966
+ ignoreUndefined
3854
3967
  };
3855
- if (value.db != null) {
3856
- output.$db = value.db;
3857
- }
3858
- output = Object.assign(output, value.fields);
3859
- const endIndex = serializeInto(buffer, output, false, index, depth + 1, serializeFunctions, true, path);
3860
- const size = endIndex - startIndex;
3861
- startIndex += NumberUtils.setInt32LE(buffer, index, size);
3862
- return endIndex;
3863
3968
  }
3864
- function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializeFunctions, ignoreUndefined, path) {
3969
+ function serializeInto(buffer, object, checkKeys, startingIndex, serializeFunctions, ignoreUndefined, path) {
3865
3970
  if (path == null) {
3866
3971
  if (object == null) {
3867
3972
  buffer[0] = 0x05;
@@ -3889,308 +3994,200 @@ function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializ
3889
3994
  path = new Set();
3890
3995
  }
3891
3996
  path.add(object);
3997
+ let currentFrame = makeFrame(object, startingIndex, null, null, checkKeys, ignoreUndefined);
3892
3998
  let index = startingIndex + 4;
3893
- if (Array.isArray(object)) {
3894
- for (let i = 0; i < object.length; i++) {
3895
- const key = `${i}`;
3896
- let value = object[i];
3897
- if (typeof value?.toBSON === 'function') {
3898
- value = value.toBSON();
3899
- }
3900
- const type = typeof value;
3901
- if (value === undefined) {
3902
- index = serializeNull(buffer, key, value, index);
3903
- }
3904
- else if (value === null) {
3905
- index = serializeNull(buffer, key, value, index);
3906
- }
3907
- else if (type === 'string') {
3908
- index = serializeString(buffer, key, value, index);
3909
- }
3910
- else if (type === 'number') {
3911
- index = serializeNumber(buffer, key, value, index);
3912
- }
3913
- else if (type === 'bigint') {
3914
- index = serializeBigInt(buffer, key, value, index);
3915
- }
3916
- else if (type === 'boolean') {
3917
- index = serializeBoolean(buffer, key, value, index);
3918
- }
3919
- else if (type === 'object' && value._bsontype == null) {
3920
- if (value instanceof Date || isDate(value)) {
3921
- index = serializeDate(buffer, key, value, index);
3922
- }
3923
- else if (value instanceof Uint8Array || isUint8Array(value)) {
3924
- index = serializeBuffer(buffer, key, value, index);
3925
- }
3926
- else if (value instanceof RegExp || isRegExp(value)) {
3927
- index = serializeRegExp(buffer, key, value, index);
3928
- }
3929
- else {
3930
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
3999
+ while (currentFrame !== null) {
4000
+ const frame = currentFrame;
4001
+ let key;
4002
+ let value;
4003
+ if (frame.mapIterator !== null) {
4004
+ const next = frame.mapIterator.next();
4005
+ if (next.done) {
4006
+ buffer[index++] = 0x00;
4007
+ NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
4008
+ if (frame.codeSizeIndex !== null) {
4009
+ NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
3931
4010
  }
4011
+ path.delete(frame.sourceObject);
4012
+ currentFrame = frame.prev;
4013
+ continue;
3932
4014
  }
3933
- else if (type === 'object') {
3934
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
3935
- throw new BSONVersionError();
3936
- }
3937
- else if (value._bsontype === 'ObjectId') {
3938
- index = serializeObjectId(buffer, key, value, index);
3939
- }
3940
- else if (value._bsontype === 'Decimal128') {
3941
- index = serializeDecimal128(buffer, key, value, index);
3942
- }
3943
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
3944
- index = serializeLong(buffer, key, value, index);
3945
- }
3946
- else if (value._bsontype === 'Double') {
3947
- index = serializeDouble(buffer, key, value, index);
3948
- }
3949
- else if (value._bsontype === 'Code') {
3950
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
3951
- }
3952
- else if (value._bsontype === 'Binary') {
3953
- index = serializeBinary(buffer, key, value, index);
3954
- }
3955
- else if (value._bsontype === 'BSONSymbol') {
3956
- index = serializeSymbol(buffer, key, value, index);
3957
- }
3958
- else if (value._bsontype === 'DBRef') {
3959
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
3960
- }
3961
- else if (value._bsontype === 'BSONRegExp') {
3962
- index = serializeBSONRegExp(buffer, key, value, index);
3963
- }
3964
- else if (value._bsontype === 'Int32') {
3965
- index = serializeInt32(buffer, key, value, index);
3966
- }
3967
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
3968
- index = serializeMinMax(buffer, key, value, index);
3969
- }
3970
- else if (typeof value._bsontype !== 'undefined') {
3971
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4015
+ key = next.value[0];
4016
+ value = next.value[1];
4017
+ }
4018
+ else if (frame.keys !== null) {
4019
+ if (frame.keyIndex >= frame.keys.length) {
4020
+ buffer[index++] = 0x00;
4021
+ NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
4022
+ if (frame.codeSizeIndex !== null) {
4023
+ NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
3972
4024
  }
4025
+ path.delete(frame.sourceObject);
4026
+ currentFrame = frame.prev;
4027
+ continue;
3973
4028
  }
3974
- else if (type === 'function' && serializeFunctions) {
3975
- index = serializeFunction(buffer, key, value, index);
3976
- }
4029
+ key = frame.keys[frame.keyIndex++];
4030
+ value = frame.iterTarget[key];
3977
4031
  }
3978
- }
3979
- else if (object instanceof Map || isMap(object)) {
3980
- const iterator = object.entries();
3981
- let done = false;
3982
- while (!done) {
3983
- const entry = iterator.next();
3984
- done = !!entry.done;
3985
- if (done)
4032
+ else {
4033
+ const arr = frame.iterTarget;
4034
+ if (frame.keyIndex >= arr.length) {
4035
+ buffer[index++] = 0x00;
4036
+ NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
4037
+ if (frame.codeSizeIndex !== null) {
4038
+ NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
4039
+ }
4040
+ path.delete(frame.sourceObject);
4041
+ currentFrame = frame.prev;
3986
4042
  continue;
3987
- const key = entry.value ? entry.value[0] : undefined;
3988
- let value = entry.value ? entry.value[1] : undefined;
3989
- if (typeof value?.toBSON === 'function') {
3990
- value = value.toBSON();
3991
- }
3992
- const type = typeof value;
3993
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
3994
- if (key.match(regexp) != null) {
3995
- throw new BSONError('key ' + key + ' must not contain null bytes');
4043
+ }
4044
+ const i = frame.keyIndex++;
4045
+ key = String(i);
4046
+ value = arr[i];
4047
+ }
4048
+ if (typeof value?.toBSON === 'function') {
4049
+ value = value.toBSON();
4050
+ }
4051
+ if (!frame.isArray && typeof key === 'string' && !(key[0] === '$' && ignoreKeys.has(key))) {
4052
+ if (regexp.test(key)) {
4053
+ throw new BSONError('key ' + key + ' must not contain null bytes');
4054
+ }
4055
+ if (frame.checkKeys) {
4056
+ if ('$' === key[0]) {
4057
+ throw new BSONError('key ' + key + " must not start with '$'");
3996
4058
  }
3997
- if (checkKeys) {
3998
- if ('$' === key[0]) {
3999
- throw new BSONError('key ' + key + " must not start with '$'");
4000
- }
4001
- else if (key.includes('.')) {
4002
- throw new BSONError('key ' + key + " must not contain '.'");
4003
- }
4059
+ else if (key.includes('.')) {
4060
+ throw new BSONError('key ' + key + " must not contain '.'");
4004
4061
  }
4005
4062
  }
4006
- if (value === undefined) {
4007
- if (ignoreUndefined === false)
4008
- index = serializeNull(buffer, key, value, index);
4009
- }
4010
- else if (value === null) {
4063
+ }
4064
+ const type = typeof value;
4065
+ if (value === undefined) {
4066
+ if (frame.isArray || frame.ignoreUndefined === false) {
4011
4067
  index = serializeNull(buffer, key, value, index);
4012
4068
  }
4013
- else if (type === 'string') {
4014
- index = serializeString(buffer, key, value, index);
4015
- }
4016
- else if (type === 'number') {
4017
- index = serializeNumber(buffer, key, value, index);
4018
- }
4019
- else if (type === 'bigint') {
4020
- index = serializeBigInt(buffer, key, value, index);
4069
+ }
4070
+ else if (value === null) {
4071
+ index = serializeNull(buffer, key, value, index);
4072
+ }
4073
+ else if (type === 'string') {
4074
+ index = serializeString(buffer, key, value, index);
4075
+ }
4076
+ else if (type === 'number') {
4077
+ index = serializeNumber(buffer, key, value, index);
4078
+ }
4079
+ else if (type === 'bigint') {
4080
+ index = serializeBigInt(buffer, key, value, index);
4081
+ }
4082
+ else if (type === 'boolean') {
4083
+ index = serializeBoolean(buffer, key, value, index);
4084
+ }
4085
+ else if (type === 'object' && value._bsontype == null) {
4086
+ if (value instanceof Date || isDate(value)) {
4087
+ index = serializeDate(buffer, key, value, index);
4021
4088
  }
4022
- else if (type === 'boolean') {
4023
- index = serializeBoolean(buffer, key, value, index);
4089
+ else if (value instanceof Uint8Array || isUint8Array(value)) {
4090
+ index = serializeBuffer(buffer, key, value, index);
4024
4091
  }
4025
- else if (type === 'object' && value._bsontype == null) {
4026
- if (value instanceof Date || isDate(value)) {
4027
- index = serializeDate(buffer, key, value, index);
4028
- }
4029
- else if (value instanceof Uint8Array || isUint8Array(value)) {
4030
- index = serializeBuffer(buffer, key, value, index);
4031
- }
4032
- else if (value instanceof RegExp || isRegExp(value)) {
4033
- index = serializeRegExp(buffer, key, value, index);
4034
- }
4035
- else {
4036
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4037
- }
4092
+ else if (value instanceof RegExp || isRegExp(value)) {
4093
+ index = serializeRegExp(buffer, key, value, index);
4038
4094
  }
4039
- else if (type === 'object') {
4040
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4041
- throw new BSONVersionError();
4042
- }
4043
- else if (value._bsontype === 'ObjectId') {
4044
- index = serializeObjectId(buffer, key, value, index);
4045
- }
4046
- else if (value._bsontype === 'Decimal128') {
4047
- index = serializeDecimal128(buffer, key, value, index);
4048
- }
4049
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4050
- index = serializeLong(buffer, key, value, index);
4051
- }
4052
- else if (value._bsontype === 'Double') {
4053
- index = serializeDouble(buffer, key, value, index);
4054
- }
4055
- else if (value._bsontype === 'Code') {
4056
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4057
- }
4058
- else if (value._bsontype === 'Binary') {
4059
- index = serializeBinary(buffer, key, value, index);
4060
- }
4061
- else if (value._bsontype === 'BSONSymbol') {
4062
- index = serializeSymbol(buffer, key, value, index);
4063
- }
4064
- else if (value._bsontype === 'DBRef') {
4065
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4066
- }
4067
- else if (value._bsontype === 'BSONRegExp') {
4068
- index = serializeBSONRegExp(buffer, key, value, index);
4069
- }
4070
- else if (value._bsontype === 'Int32') {
4071
- index = serializeInt32(buffer, key, value, index);
4072
- }
4073
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4074
- index = serializeMinMax(buffer, key, value, index);
4075
- }
4076
- else if (typeof value._bsontype !== 'undefined') {
4077
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4095
+ else {
4096
+ if (path.has(value)) {
4097
+ throw new BSONError('Cannot convert circular structure to BSON');
4078
4098
  }
4079
- }
4080
- else if (type === 'function' && serializeFunctions) {
4081
- index = serializeFunction(buffer, key, value, index);
4082
- }
4083
- }
4084
- }
4085
- else {
4086
- if (typeof object?.toBSON === 'function') {
4087
- object = object.toBSON();
4088
- if (object != null && typeof object !== 'object') {
4089
- throw new BSONError('toBSON function did not return an object');
4099
+ const nestedIsArray = Array.isArray(value);
4100
+ buffer[index++] = nestedIsArray ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
4101
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4102
+ buffer[index++] = 0x00;
4103
+ const nestedStartIndex = index;
4104
+ path.add(value);
4105
+ currentFrame = makeFrame(value, nestedStartIndex, null, frame, frame.checkKeys, frame.ignoreUndefined);
4106
+ index += 4;
4090
4107
  }
4091
4108
  }
4092
- for (const key of Object.keys(object)) {
4093
- let value = object[key];
4094
- if (typeof value?.toBSON === 'function') {
4095
- value = value.toBSON();
4109
+ else if (type === 'object') {
4110
+ if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4111
+ throw new BSONVersionError();
4096
4112
  }
4097
- const type = typeof value;
4098
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
4099
- if (key.match(regexp) != null) {
4100
- throw new BSONError('key ' + key + ' must not contain null bytes');
4101
- }
4102
- if (checkKeys) {
4103
- if ('$' === key[0]) {
4104
- throw new BSONError('key ' + key + " must not start with '$'");
4105
- }
4106
- else if (key.includes('.')) {
4107
- throw new BSONError('key ' + key + " must not contain '.'");
4113
+ const tag = value[bsonType];
4114
+ if (tag === 'ObjectId') {
4115
+ index = serializeObjectId(buffer, key, value, index);
4116
+ }
4117
+ else if (tag === 'Decimal128') {
4118
+ index = serializeDecimal128(buffer, key, value, index);
4119
+ }
4120
+ else if (tag === 'Long' || tag === 'Timestamp') {
4121
+ index = serializeLong(buffer, key, value, index);
4122
+ }
4123
+ else if (tag === 'Double') {
4124
+ index = serializeDouble(buffer, key, value, index);
4125
+ }
4126
+ else if (tag === 'Code') {
4127
+ const codeValue = value;
4128
+ if (codeValue.scope && typeof codeValue.scope === 'object') {
4129
+ buffer[index++] = BSON_DATA_CODE_W_SCOPE;
4130
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4131
+ buffer[index++] = 0x00;
4132
+ const codeTotalSizeIndex = index;
4133
+ index += 4;
4134
+ const functionString = codeValue.code;
4135
+ const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
4136
+ NumberUtils.setInt32LE(buffer, index, codeSize);
4137
+ buffer[index + 4 + codeSize - 1] = 0;
4138
+ index = index + codeSize + 4;
4139
+ const scope = codeValue.scope;
4140
+ if (path.has(scope)) {
4141
+ throw new BSONError('Cannot convert circular structure to BSON');
4108
4142
  }
4143
+ path.add(scope);
4144
+ currentFrame = makeFrame(scope, index, codeTotalSizeIndex, frame, frame.checkKeys, frame.ignoreUndefined);
4145
+ index += 4;
4146
+ }
4147
+ else {
4148
+ buffer[index++] = BSON_DATA_CODE;
4149
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4150
+ buffer[index++] = 0x00;
4151
+ const functionString = codeValue.code.toString();
4152
+ const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
4153
+ NumberUtils.setInt32LE(buffer, index, size);
4154
+ index = index + 4 + size - 1;
4155
+ buffer[index++] = 0;
4109
4156
  }
4110
4157
  }
4111
- if (value === undefined) {
4112
- if (ignoreUndefined === false)
4113
- index = serializeNull(buffer, key, value, index);
4114
- }
4115
- else if (value === null) {
4116
- index = serializeNull(buffer, key, value, index);
4117
- }
4118
- else if (type === 'string') {
4119
- index = serializeString(buffer, key, value, index);
4158
+ else if (tag === 'Binary') {
4159
+ index = serializeBinary(buffer, key, value, index);
4120
4160
  }
4121
- else if (type === 'number') {
4122
- index = serializeNumber(buffer, key, value, index);
4161
+ else if (tag === 'BSONSymbol') {
4162
+ index = serializeSymbol(buffer, key, value, index);
4123
4163
  }
4124
- else if (type === 'bigint') {
4125
- index = serializeBigInt(buffer, key, value, index);
4164
+ else if (tag === 'DBRef') {
4165
+ const dbref = value;
4166
+ const orderedValues = Object.assign({ $ref: dbref.collection, $id: dbref.oid }, dbref.db != null ? { $db: dbref.db } : null, dbref.fields);
4167
+ buffer[index++] = BSON_DATA_OBJECT;
4168
+ index += ByteUtils.encodeUTF8Into(buffer, key, index);
4169
+ buffer[index++] = 0x00;
4170
+ path.add(orderedValues);
4171
+ currentFrame = makeFrame(orderedValues, index, null, frame, false, true);
4172
+ index += 4;
4126
4173
  }
4127
- else if (type === 'boolean') {
4128
- index = serializeBoolean(buffer, key, value, index);
4174
+ else if (tag === 'BSONRegExp') {
4175
+ index = serializeBSONRegExp(buffer, key, value, index);
4129
4176
  }
4130
- else if (type === 'object' && value._bsontype == null) {
4131
- if (value instanceof Date || isDate(value)) {
4132
- index = serializeDate(buffer, key, value, index);
4133
- }
4134
- else if (value instanceof Uint8Array || isUint8Array(value)) {
4135
- index = serializeBuffer(buffer, key, value, index);
4136
- }
4137
- else if (value instanceof RegExp || isRegExp(value)) {
4138
- index = serializeRegExp(buffer, key, value, index);
4139
- }
4140
- else {
4141
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4142
- }
4177
+ else if (tag === 'Int32') {
4178
+ index = serializeInt32(buffer, key, value, index);
4143
4179
  }
4144
- else if (type === 'object') {
4145
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4146
- throw new BSONVersionError();
4147
- }
4148
- else if (value._bsontype === 'ObjectId') {
4149
- index = serializeObjectId(buffer, key, value, index);
4150
- }
4151
- else if (value._bsontype === 'Decimal128') {
4152
- index = serializeDecimal128(buffer, key, value, index);
4153
- }
4154
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4155
- index = serializeLong(buffer, key, value, index);
4156
- }
4157
- else if (value._bsontype === 'Double') {
4158
- index = serializeDouble(buffer, key, value, index);
4159
- }
4160
- else if (value._bsontype === 'Code') {
4161
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4162
- }
4163
- else if (value._bsontype === 'Binary') {
4164
- index = serializeBinary(buffer, key, value, index);
4165
- }
4166
- else if (value._bsontype === 'BSONSymbol') {
4167
- index = serializeSymbol(buffer, key, value, index);
4168
- }
4169
- else if (value._bsontype === 'DBRef') {
4170
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4171
- }
4172
- else if (value._bsontype === 'BSONRegExp') {
4173
- index = serializeBSONRegExp(buffer, key, value, index);
4174
- }
4175
- else if (value._bsontype === 'Int32') {
4176
- index = serializeInt32(buffer, key, value, index);
4177
- }
4178
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4179
- index = serializeMinMax(buffer, key, value, index);
4180
- }
4181
- else if (typeof value._bsontype !== 'undefined') {
4182
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4183
- }
4180
+ else if (tag === 'MinKey' || tag === 'MaxKey') {
4181
+ index = serializeMinMax(buffer, key, value, index);
4184
4182
  }
4185
- else if (type === 'function' && serializeFunctions) {
4186
- index = serializeFunction(buffer, key, value, index);
4183
+ else if (typeof value._bsontype !== 'undefined') {
4184
+ throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4187
4185
  }
4188
4186
  }
4187
+ else if (type === 'function' && serializeFunctions) {
4188
+ index = serializeFunction(buffer, key, value, index);
4189
+ }
4189
4190
  }
4190
- path.delete(object);
4191
- buffer[index++] = 0x00;
4192
- const size = index - startingIndex;
4193
- startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
4194
4191
  return index;
4195
4192
  }
4196
4193
 
@@ -4473,21 +4470,22 @@ function parse(text, options) {
4473
4470
  return deserializeValue(value, ejsonOptions);
4474
4471
  });
4475
4472
  }
4476
- function stringify(value, replacer, space, options) {
4477
- if (space != null && typeof space === 'object') {
4478
- options = space;
4479
- space = 0;
4473
+ function stringify(value, replacerOrOptions, spaceOrOptions, options) {
4474
+ if (spaceOrOptions != null && typeof spaceOrOptions === 'object') {
4475
+ options = spaceOrOptions;
4476
+ spaceOrOptions = undefined;
4480
4477
  }
4481
- if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
4482
- options = replacer;
4483
- replacer = undefined;
4484
- space = 0;
4478
+ if (replacerOrOptions != null &&
4479
+ typeof replacerOrOptions === 'object' &&
4480
+ !Array.isArray(replacerOrOptions)) {
4481
+ options = replacerOrOptions;
4482
+ replacerOrOptions = undefined;
4485
4483
  }
4486
4484
  const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
4487
4485
  seenObjects: [{ propertyName: '(root)', obj: null }]
4488
4486
  });
4489
4487
  const doc = serializeValue(value, serializeOptions);
4490
- return JSON.stringify(doc, replacer, space);
4488
+ return JSON.stringify(doc, replacerOrOptions, spaceOrOptions);
4491
4489
  }
4492
4490
  function EJSONserialize(value, options) {
4493
4491
  options = options || {};
@@ -4649,7 +4647,7 @@ function serialize(object, options = {}) {
4649
4647
  if (buffer.length < minInternalBufferSize) {
4650
4648
  buffer = ByteUtils.allocate(minInternalBufferSize);
4651
4649
  }
4652
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4650
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4653
4651
  const finishedBuffer = ByteUtils.allocateUnsafe(serializationIndex);
4654
4652
  finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
4655
4653
  return finishedBuffer;
@@ -4659,7 +4657,7 @@ function serializeWithBufferAndIndex(object, finalBuffer, options = {}) {
4659
4657
  const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
4660
4658
  const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
4661
4659
  const startIndex = typeof options.index === 'number' ? options.index : 0;
4662
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4660
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4663
4661
  finalBuffer.set(buffer.subarray(0, serializationIndex), startIndex);
4664
4662
  return startIndex + serializationIndex - 1;
4665
4663
  }