bson 7.1.1 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/bson.rn.cjs CHANGED
@@ -269,6 +269,11 @@ const nodeJsByteUtils = {
269
269
  concat(list) {
270
270
  return Buffer.concat(list);
271
271
  },
272
+ copy(source, target, targetStart, sourceStart, sourceEnd) {
273
+ return nodeJsByteUtils
274
+ .toLocalBufferType(source)
275
+ .copy(target, targetStart ?? 0, sourceStart ?? 0, sourceEnd ?? source.length);
276
+ },
272
277
  equals(a, b) {
273
278
  return nodeJsByteUtils.toLocalBufferType(a).equals(b);
274
279
  },
@@ -413,6 +418,27 @@ const webByteUtils = {
413
418
  }
414
419
  return result;
415
420
  },
421
+ copy(source, target, targetStart, sourceStart, sourceEnd) {
422
+ if (sourceEnd !== undefined && sourceEnd < 0) {
423
+ throw new RangeError(`The value of "sourceEnd" is out of range. It must be >= 0. Received ${sourceEnd}`);
424
+ }
425
+ sourceEnd = sourceEnd ?? source.length;
426
+ if (sourceStart !== undefined && (sourceStart < 0 || sourceStart > sourceEnd)) {
427
+ throw new RangeError(`The value of "sourceStart" is out of range. It must be >= 0 and <= ${sourceEnd}. Received ${sourceStart}`);
428
+ }
429
+ sourceStart = sourceStart ?? 0;
430
+ if (targetStart !== undefined && targetStart < 0) {
431
+ throw new RangeError(`The value of "targetStart" is out of range. It must be >= 0. Received ${targetStart}`);
432
+ }
433
+ targetStart = targetStart ?? 0;
434
+ const srcSlice = source.subarray(sourceStart, sourceEnd);
435
+ const maxLen = Math.min(srcSlice.length, target.length - targetStart);
436
+ if (maxLen <= 0) {
437
+ return 0;
438
+ }
439
+ target.set(srcSlice.subarray(0, maxLen), targetStart);
440
+ return maxLen;
441
+ },
416
442
  equals(uint8Array, otherUint8Array) {
417
443
  if (uint8Array.byteLength !== otherUint8Array.byteLength) {
418
444
  return false;
@@ -680,7 +706,7 @@ class Binary extends BSONValue {
680
706
  !Array.isArray(buffer)) {
681
707
  throw new BSONError('Binary can only be constructed from Uint8Array or number[]');
682
708
  }
683
- this.sub_type = subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT;
709
+ this.sub_type = (subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT) & 0xff;
684
710
  if (buffer == null) {
685
711
  this.buffer = ByteUtils.allocate(Binary.BUFFER_SIZE);
686
712
  this.position = 0;
@@ -786,7 +812,7 @@ class Binary extends BSONValue {
786
812
  if (this.sub_type === Binary.SUBTYPE_UUID) {
787
813
  return new UUID(this.buffer.subarray(0, this.position));
788
814
  }
789
- 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.`);
790
816
  }
791
817
  static createFromHexString(hex, subType) {
792
818
  return new Binary(ByteUtils.fromHex(hex), subType);
@@ -2575,13 +2601,24 @@ class MinKey extends BSONValue {
2575
2601
  }
2576
2602
  }
2577
2603
 
2578
- let PROCESS_UNIQUE = null;
2579
2604
  const __idCache = new WeakMap();
2580
2605
  class ObjectId extends BSONValue {
2581
2606
  get _bsontype() {
2582
2607
  return 'ObjectId';
2583
2608
  }
2584
- 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
+ }
2585
2622
  static cacheHexString;
2586
2623
  buffer;
2587
2624
  constructor(inputId) {
@@ -2658,7 +2695,7 @@ class ObjectId extends BSONValue {
2658
2695
  return hexString;
2659
2696
  }
2660
2697
  static getInc() {
2661
- return (ObjectId.index = (ObjectId.index + 1) % 0xffffff);
2698
+ return (ObjectId.index = (ObjectId.index + 1) % 0x1000000);
2662
2699
  }
2663
2700
  static generate(time) {
2664
2701
  if ('number' !== typeof time) {
@@ -2667,9 +2704,7 @@ class ObjectId extends BSONValue {
2667
2704
  const inc = ObjectId.getInc();
2668
2705
  const buffer = ByteUtils.allocateUnsafe(12);
2669
2706
  NumberUtils.setInt32BE(buffer, 0, time);
2670
- if (PROCESS_UNIQUE === null) {
2671
- PROCESS_UNIQUE = ByteUtils.randomBytes(5);
2672
- }
2707
+ const PROCESS_UNIQUE = this.PROCESS_UNIQUE;
2673
2708
  buffer[4] = PROCESS_UNIQUE[0];
2674
2709
  buffer[5] = PROCESS_UNIQUE[1];
2675
2710
  buffer[6] = PROCESS_UNIQUE[2];
@@ -2787,23 +2822,33 @@ class ObjectId extends BSONValue {
2787
2822
  }
2788
2823
 
2789
2824
  function internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined) {
2790
- let totalLength = 4 + 1;
2791
- if (Array.isArray(object)) {
2792
- for (let i = 0; i < object.length; i++) {
2793
- totalLength += calculateElement(i.toString(), object[i], serializeFunctions, true, ignoreUndefined);
2794
- }
2795
- }
2796
- else {
2797
- if (typeof object?.toBSON === 'function') {
2798
- 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
+ }
2799
2842
  }
2800
- for (const key of Object.keys(object)) {
2801
- 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
+ }
2802
2847
  }
2803
2848
  }
2804
- return totalLength;
2849
+ return total;
2805
2850
  }
2806
- function calculateElement(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false) {
2851
+ function calculateElementSize(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false, objectStack) {
2807
2852
  if (typeof value?.toBSON === 'function') {
2808
2853
  value = value.toBSON();
2809
2854
  }
@@ -2815,21 +2860,21 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2815
2860
  value >= JS_INT_MIN &&
2816
2861
  value <= JS_INT_MAX) {
2817
2862
  if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
2818
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (4 + 1);
2863
+ return ByteUtils.utf8ByteLength(name) + 1 + (4 + 1);
2819
2864
  }
2820
2865
  else {
2821
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2866
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2822
2867
  }
2823
2868
  }
2824
2869
  else {
2825
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2870
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2826
2871
  }
2827
2872
  case 'undefined':
2828
2873
  if (isArray || !ignoreUndefined)
2829
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2874
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2830
2875
  return 0;
2831
2876
  case 'boolean':
2832
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1);
2877
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 1);
2833
2878
  case 'object':
2834
2879
  if (value != null &&
2835
2880
  typeof value._bsontype === 'string' &&
@@ -2837,39 +2882,41 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2837
2882
  throw new BSONVersionError();
2838
2883
  }
2839
2884
  else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
2840
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
2885
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2841
2886
  }
2842
2887
  else if (value._bsontype === 'ObjectId') {
2843
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1);
2888
+ return ByteUtils.utf8ByteLength(name) + 1 + (12 + 1);
2844
2889
  }
2845
2890
  else if (value instanceof Date || isDate(value)) {
2846
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2891
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2847
2892
  }
2848
2893
  else if (ArrayBuffer.isView(value) ||
2849
2894
  value instanceof ArrayBuffer ||
2850
2895
  isAnyArrayBuffer(value)) {
2851
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength);
2896
+ return ByteUtils.utf8ByteLength(name) + 1 + (1 + 4 + 1) + value.byteLength;
2852
2897
  }
2853
2898
  else if (value._bsontype === 'Long' ||
2854
2899
  value._bsontype === 'Double' ||
2855
2900
  value._bsontype === 'Timestamp') {
2856
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2901
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2857
2902
  }
2858
2903
  else if (value._bsontype === 'Decimal128') {
2859
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1);
2904
+ return ByteUtils.utf8ByteLength(name) + 1 + (16 + 1);
2860
2905
  }
2861
2906
  else if (value._bsontype === 'Code') {
2862
2907
  if (value.scope != null && Object.keys(value.scope).length > 0) {
2863
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2908
+ objectStack.push({ obj: value.scope, ignoreUndefined });
2909
+ return (ByteUtils.utf8ByteLength(name) +
2910
+ 1 +
2864
2911
  1 +
2865
2912
  4 +
2866
2913
  4 +
2867
2914
  ByteUtils.utf8ByteLength(value.code.toString()) +
2868
- 1 +
2869
- internalCalculateObjectSize(value.scope, serializeFunctions, ignoreUndefined));
2915
+ 1);
2870
2916
  }
2871
2917
  else {
2872
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2918
+ return (ByteUtils.utf8ByteLength(name) +
2919
+ 1 +
2873
2920
  1 +
2874
2921
  4 +
2875
2922
  ByteUtils.utf8ByteLength(value.code.toString()) +
@@ -2879,19 +2926,14 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2879
2926
  else if (value._bsontype === 'Binary') {
2880
2927
  const binary = value;
2881
2928
  if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
2882
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2883
- (binary.position + 1 + 4 + 1 + 4));
2929
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1 + 4);
2884
2930
  }
2885
2931
  else {
2886
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1));
2932
+ return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1);
2887
2933
  }
2888
2934
  }
2889
2935
  else if (value._bsontype === 'Symbol') {
2890
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2891
- ByteUtils.utf8ByteLength(value.value) +
2892
- 4 +
2893
- 1 +
2894
- 1);
2936
+ return (ByteUtils.utf8ByteLength(name) + 1 + ByteUtils.utf8ByteLength(value.value) + 4 + 1 + 1);
2895
2937
  }
2896
2938
  else if (value._bsontype === 'DBRef') {
2897
2939
  const ordered_values = Object.assign({
@@ -2901,12 +2943,12 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2901
2943
  if (value.db != null) {
2902
2944
  ordered_values['$db'] = value.db;
2903
2945
  }
2904
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2905
- 1 +
2906
- internalCalculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined));
2946
+ objectStack.push({ obj: ordered_values, ignoreUndefined: true });
2947
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2907
2948
  }
2908
2949
  else if (value instanceof RegExp || isRegExp(value)) {
2909
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2950
+ return (ByteUtils.utf8ByteLength(name) +
2951
+ 1 +
2910
2952
  1 +
2911
2953
  ByteUtils.utf8ByteLength(value.source) +
2912
2954
  1 +
@@ -2916,7 +2958,8 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2916
2958
  1);
2917
2959
  }
2918
2960
  else if (value._bsontype === 'BSONRegExp') {
2919
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2961
+ return (ByteUtils.utf8ByteLength(name) +
2962
+ 1 +
2920
2963
  1 +
2921
2964
  ByteUtils.utf8ByteLength(value.pattern) +
2922
2965
  1 +
@@ -2924,13 +2967,13 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2924
2967
  1);
2925
2968
  }
2926
2969
  else {
2927
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2928
- internalCalculateObjectSize(value, serializeFunctions, ignoreUndefined) +
2929
- 1);
2970
+ objectStack.push({ obj: value, ignoreUndefined });
2971
+ return ByteUtils.utf8ByteLength(name) + 1 + 1;
2930
2972
  }
2931
2973
  case 'function':
2932
2974
  if (serializeFunctions) {
2933
- return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
2975
+ return (ByteUtils.utf8ByteLength(name) +
2976
+ 1 +
2934
2977
  1 +
2935
2978
  4 +
2936
2979
  ByteUtils.utf8ByteLength(value.toString()) +
@@ -2938,13 +2981,12 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
2938
2981
  }
2939
2982
  return 0;
2940
2983
  case 'bigint':
2941
- return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
2984
+ return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
2942
2985
  case 'symbol':
2943
2986
  return 0;
2944
2987
  default:
2945
2988
  throw new BSONError(`Unrecognized JS type: ${typeof value}`);
2946
2989
  }
2947
- return 0;
2948
2990
  }
2949
2991
 
2950
2992
  function alphabetize(str) {
@@ -3155,7 +3197,28 @@ function internalDeserialize(buffer, options, isArray) {
3155
3197
  return deserializeObject(buffer, index, options, isArray);
3156
3198
  }
3157
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
+ }
3158
3220
  function deserializeObject(buffer, index, options, isArray = false) {
3221
+ options = { ...options };
3159
3222
  const fieldsAsRaw = options['fieldsAsRaw'] == null ? null : options['fieldsAsRaw'];
3160
3223
  const raw = options['raw'] == null ? false : options['raw'];
3161
3224
  const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
@@ -3206,32 +3269,87 @@ function deserializeObject(buffer, index, options, isArray = false) {
3206
3269
  index += 4;
3207
3270
  if (size < 5 || size > buffer.length)
3208
3271
  throw new BSONError('corrupt bson message');
3209
- const object = isArray ? [] : {};
3272
+ const rootObject = isArray ? [] : {};
3210
3273
  let arrayIndex = 0;
3211
- const done = false;
3212
3274
  let isPossibleDBRef = isArray ? false : null;
3213
- while (!done) {
3275
+ let currentFrame = null;
3276
+ let currentDest = rootObject;
3277
+ let currentIsArray = isArray;
3278
+ while (true) {
3214
3279
  const elementType = buffer[index++];
3215
- if (elementType === 0)
3216
- 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
+ }
3217
3322
  let i = index;
3218
3323
  while (buffer[i] !== 0x00 && i < buffer.length) {
3219
3324
  i++;
3220
3325
  }
3221
3326
  if (i >= buffer.byteLength)
3222
3327
  throw new BSONError('Bad BSON Document: illegal CString');
3223
- const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
3224
- let shouldValidateKey = true;
3225
- 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)) {
3226
3338
  shouldValidateKey = validationSetting;
3227
3339
  }
3228
3340
  else {
3229
3341
  shouldValidateKey = !validationSetting;
3230
3342
  }
3231
- 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] === '$') {
3232
3349
  isPossibleDBRef = allowedDBRefKeys.test(name);
3233
3350
  }
3234
3351
  let value;
3352
+ let isDeferredValue = false;
3235
3353
  index = i + 1;
3236
3354
  if (elementType === BSON_DATA_STRING) {
3237
3355
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3277,39 +3395,58 @@ function deserializeObject(buffer, index, options, isArray = false) {
3277
3395
  value = buffer[index++] === 1;
3278
3396
  }
3279
3397
  else if (elementType === BSON_DATA_OBJECT) {
3280
- const _index = index;
3281
3398
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3282
- if (objectSize <= 0 || objectSize > buffer.length - index)
3399
+ if (objectSize < 5 || objectSize > buffer.length - index)
3283
3400
  throw new BSONError('bad embedded document length in bson');
3284
- if (raw) {
3401
+ if (raw || (currentFrame?.raw ?? false)) {
3285
3402
  value = buffer.subarray(index, index + objectSize);
3403
+ index = index + objectSize;
3286
3404
  }
3287
3405
  else {
3288
- let objectOptions = options;
3289
- if (!globalUTFValidation) {
3290
- objectOptions = { ...options, validation: { utf8: shouldValidateKey } };
3291
- }
3292
- 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;
3293
3424
  }
3294
- index = index + objectSize;
3295
3425
  }
3296
3426
  else if (elementType === BSON_DATA_ARRAY) {
3297
- const _index = index;
3298
3427
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3299
- let arrayOptions = options;
3428
+ if (objectSize < 5 || objectSize > buffer.length - index)
3429
+ throw new BSONError('bad embedded array length in bson');
3300
3430
  const stopIndex = index + objectSize;
3301
- if (fieldsAsRaw && fieldsAsRaw[name]) {
3302
- arrayOptions = { ...options, raw: true };
3303
- }
3304
- if (!globalUTFValidation) {
3305
- arrayOptions = { ...arrayOptions, validation: { utf8: shouldValidateKey } };
3306
- }
3307
- value = deserializeObject(buffer, _index, arrayOptions, true);
3308
- index = index + objectSize;
3309
- if (buffer[index - 1] !== 0)
3310
- throw new BSONError('invalid array terminator byte');
3311
- if (index !== stopIndex)
3312
- 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;
3313
3450
  }
3314
3451
  else if (elementType === BSON_DATA_UNDEFINED) {
3315
3452
  value = undefined;
@@ -3481,15 +3618,32 @@ function deserializeObject(buffer, index, options, isArray = false) {
3481
3618
  index = index + stringSize;
3482
3619
  const _index = index;
3483
3620
  const objectSize = NumberUtils.getInt32LE(buffer, index);
3484
- const scopeObject = deserializeObject(buffer, _index, options, false);
3485
- index = index + objectSize;
3621
+ if (objectSize < 5 || objectSize > buffer.length - index)
3622
+ throw new BSONError('bad scope document size in code_w_scope');
3486
3623
  if (totalSize < 4 + 4 + objectSize + stringSize) {
3487
3624
  throw new BSONError('code_w_scope total size is too short, truncating scope');
3488
3625
  }
3489
3626
  if (totalSize > 4 + 4 + objectSize + stringSize) {
3490
3627
  throw new BSONError('code_w_scope total size is too long, clips outer document');
3491
3628
  }
3492
- 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;
3493
3647
  }
3494
3648
  else if (elementType === BSON_DATA_DBPOINTER) {
3495
3649
  const stringSize = NumberUtils.getInt32LE(buffer, index);
@@ -3510,18 +3664,14 @@ function deserializeObject(buffer, index, options, isArray = false) {
3510
3664
  else {
3511
3665
  throw new BSONError(`Detected unknown BSON type ${elementType.toString(16)} for fieldname "${name}"`);
3512
3666
  }
3513
- if (name === '__proto__') {
3514
- Object.defineProperty(object, name, {
3515
- value,
3516
- writable: true,
3517
- enumerable: true,
3518
- configurable: true
3519
- });
3520
- }
3521
- else {
3522
- object[name] = value;
3667
+ if (!isDeferredValue) {
3668
+ assignValue(currentDest, name, value);
3523
3669
  }
3524
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;
3525
3675
  if (size !== index - startIndex) {
3526
3676
  if (isArray)
3527
3677
  throw new BSONError('corrupt array bson');
@@ -3529,14 +3679,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
3529
3679
  }
3530
3680
  if (!isPossibleDBRef)
3531
3681
  return object;
3532
- if (isDBRefLike(object)) {
3533
- const copy = Object.assign({}, object);
3534
- delete copy.$ref;
3535
- delete copy.$id;
3536
- delete copy.$db;
3537
- return new DBRef(object.$ref, object.$id, object.$db, copy);
3538
- }
3539
- return object;
3682
+ return toPotentialDbRef(object);
3540
3683
  }
3541
3684
 
3542
3685
  const regexp = /\x00/;
@@ -3645,7 +3788,7 @@ function serializeMinMax(buffer, key, value, index) {
3645
3788
  if (value === null) {
3646
3789
  buffer[index++] = BSON_DATA_NULL;
3647
3790
  }
3648
- else if (value._bsontype === 'MinKey') {
3791
+ else if (value[bsonType] === 'MinKey') {
3649
3792
  buffer[index++] = BSON_DATA_MIN_KEY;
3650
3793
  }
3651
3794
  else {
@@ -3682,19 +3825,6 @@ function serializeBuffer(buffer, key, value, index) {
3682
3825
  index = index + size;
3683
3826
  return index;
3684
3827
  }
3685
- function serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path) {
3686
- if (path.has(value)) {
3687
- throw new BSONError('Cannot convert circular structure to BSON');
3688
- }
3689
- path.add(value);
3690
- buffer[index++] = Array.isArray(value) ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
3691
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3692
- index = index + numberOfWrittenBytes;
3693
- buffer[index++] = 0;
3694
- const endIndex = serializeInto(buffer, value, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3695
- path.delete(value);
3696
- return endIndex;
3697
- }
3698
3828
  function serializeDecimal128(buffer, key, value, index) {
3699
3829
  buffer[index++] = BSON_DATA_DECIMAL128;
3700
3830
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3706,7 +3836,7 @@ function serializeDecimal128(buffer, key, value, index) {
3706
3836
  }
3707
3837
  function serializeLong(buffer, key, value, index) {
3708
3838
  buffer[index++] =
3709
- value._bsontype === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3839
+ value[bsonType] === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
3710
3840
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3711
3841
  index = index + numberOfWrittenBytes;
3712
3842
  buffer[index++] = 0;
@@ -3745,38 +3875,6 @@ function serializeFunction(buffer, key, value, index) {
3745
3875
  buffer[index++] = 0;
3746
3876
  return index;
3747
3877
  }
3748
- function serializeCode(buffer, key, value, index, checkKeys = false, depth = 0, serializeFunctions = false, ignoreUndefined = true, path) {
3749
- if (value.scope && typeof value.scope === 'object') {
3750
- buffer[index++] = BSON_DATA_CODE_W_SCOPE;
3751
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3752
- index = index + numberOfWrittenBytes;
3753
- buffer[index++] = 0;
3754
- let startIndex = index;
3755
- const functionString = value.code;
3756
- index = index + 4;
3757
- const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3758
- NumberUtils.setInt32LE(buffer, index, codeSize);
3759
- buffer[index + 4 + codeSize - 1] = 0;
3760
- index = index + codeSize + 4;
3761
- const endIndex = serializeInto(buffer, value.scope, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
3762
- index = endIndex - 1;
3763
- const totalSize = endIndex - startIndex;
3764
- startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
3765
- buffer[index++] = 0;
3766
- }
3767
- else {
3768
- buffer[index++] = BSON_DATA_CODE;
3769
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3770
- index = index + numberOfWrittenBytes;
3771
- buffer[index++] = 0;
3772
- const functionString = value.code.toString();
3773
- const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
3774
- NumberUtils.setInt32LE(buffer, index, size);
3775
- index = index + 4 + size - 1;
3776
- buffer[index++] = 0;
3777
- }
3778
- return index;
3779
- }
3780
3878
  function serializeBinary(buffer, key, value, index) {
3781
3879
  buffer[index++] = BSON_DATA_BINARY;
3782
3880
  const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
@@ -3816,26 +3914,59 @@ function serializeSymbol(buffer, key, value, index) {
3816
3914
  buffer[index++] = 0;
3817
3915
  return index;
3818
3916
  }
3819
- function serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path) {
3820
- buffer[index++] = BSON_DATA_OBJECT;
3821
- const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
3822
- index = index + numberOfWrittenBytes;
3823
- buffer[index++] = 0;
3824
- let startIndex = index;
3825
- let output = {
3826
- $ref: value.collection || value.namespace,
3827
- $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
3828
3967
  };
3829
- if (value.db != null) {
3830
- output.$db = value.db;
3831
- }
3832
- output = Object.assign(output, value.fields);
3833
- const endIndex = serializeInto(buffer, output, false, index, depth + 1, serializeFunctions, true, path);
3834
- const size = endIndex - startIndex;
3835
- startIndex += NumberUtils.setInt32LE(buffer, index, size);
3836
- return endIndex;
3837
3968
  }
3838
- function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializeFunctions, ignoreUndefined, path) {
3969
+ function serializeInto(buffer, object, checkKeys, startingIndex, serializeFunctions, ignoreUndefined, path) {
3839
3970
  if (path == null) {
3840
3971
  if (object == null) {
3841
3972
  buffer[0] = 0x05;
@@ -3863,308 +3994,200 @@ function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializ
3863
3994
  path = new Set();
3864
3995
  }
3865
3996
  path.add(object);
3997
+ let currentFrame = makeFrame(object, startingIndex, null, null, checkKeys, ignoreUndefined);
3866
3998
  let index = startingIndex + 4;
3867
- if (Array.isArray(object)) {
3868
- for (let i = 0; i < object.length; i++) {
3869
- const key = `${i}`;
3870
- let value = object[i];
3871
- if (typeof value?.toBSON === 'function') {
3872
- value = value.toBSON();
3873
- }
3874
- const type = typeof value;
3875
- if (value === undefined) {
3876
- index = serializeNull(buffer, key, value, index);
3877
- }
3878
- else if (value === null) {
3879
- index = serializeNull(buffer, key, value, index);
3880
- }
3881
- else if (type === 'string') {
3882
- index = serializeString(buffer, key, value, index);
3883
- }
3884
- else if (type === 'number') {
3885
- index = serializeNumber(buffer, key, value, index);
3886
- }
3887
- else if (type === 'bigint') {
3888
- index = serializeBigInt(buffer, key, value, index);
3889
- }
3890
- else if (type === 'boolean') {
3891
- index = serializeBoolean(buffer, key, value, index);
3892
- }
3893
- else if (type === 'object' && value._bsontype == null) {
3894
- if (value instanceof Date || isDate(value)) {
3895
- index = serializeDate(buffer, key, value, index);
3896
- }
3897
- else if (value instanceof Uint8Array || isUint8Array(value)) {
3898
- index = serializeBuffer(buffer, key, value, index);
3899
- }
3900
- else if (value instanceof RegExp || isRegExp(value)) {
3901
- index = serializeRegExp(buffer, key, value, index);
3902
- }
3903
- else {
3904
- 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);
3905
4010
  }
4011
+ path.delete(frame.sourceObject);
4012
+ currentFrame = frame.prev;
4013
+ continue;
3906
4014
  }
3907
- else if (type === 'object') {
3908
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
3909
- throw new BSONVersionError();
3910
- }
3911
- else if (value._bsontype === 'ObjectId') {
3912
- index = serializeObjectId(buffer, key, value, index);
3913
- }
3914
- else if (value._bsontype === 'Decimal128') {
3915
- index = serializeDecimal128(buffer, key, value, index);
3916
- }
3917
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
3918
- index = serializeLong(buffer, key, value, index);
3919
- }
3920
- else if (value._bsontype === 'Double') {
3921
- index = serializeDouble(buffer, key, value, index);
3922
- }
3923
- else if (value._bsontype === 'Code') {
3924
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
3925
- }
3926
- else if (value._bsontype === 'Binary') {
3927
- index = serializeBinary(buffer, key, value, index);
3928
- }
3929
- else if (value._bsontype === 'BSONSymbol') {
3930
- index = serializeSymbol(buffer, key, value, index);
3931
- }
3932
- else if (value._bsontype === 'DBRef') {
3933
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
3934
- }
3935
- else if (value._bsontype === 'BSONRegExp') {
3936
- index = serializeBSONRegExp(buffer, key, value, index);
3937
- }
3938
- else if (value._bsontype === 'Int32') {
3939
- index = serializeInt32(buffer, key, value, index);
3940
- }
3941
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
3942
- index = serializeMinMax(buffer, key, value, index);
3943
- }
3944
- else if (typeof value._bsontype !== 'undefined') {
3945
- 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);
3946
4024
  }
4025
+ path.delete(frame.sourceObject);
4026
+ currentFrame = frame.prev;
4027
+ continue;
3947
4028
  }
3948
- else if (type === 'function' && serializeFunctions) {
3949
- index = serializeFunction(buffer, key, value, index);
3950
- }
4029
+ key = frame.keys[frame.keyIndex++];
4030
+ value = frame.iterTarget[key];
3951
4031
  }
3952
- }
3953
- else if (object instanceof Map || isMap(object)) {
3954
- const iterator = object.entries();
3955
- let done = false;
3956
- while (!done) {
3957
- const entry = iterator.next();
3958
- done = !!entry.done;
3959
- 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;
3960
4042
  continue;
3961
- const key = entry.value ? entry.value[0] : undefined;
3962
- let value = entry.value ? entry.value[1] : undefined;
3963
- if (typeof value?.toBSON === 'function') {
3964
- value = value.toBSON();
3965
- }
3966
- const type = typeof value;
3967
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
3968
- if (key.match(regexp) != null) {
3969
- 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 '$'");
3970
4058
  }
3971
- if (checkKeys) {
3972
- if ('$' === key[0]) {
3973
- throw new BSONError('key ' + key + " must not start with '$'");
3974
- }
3975
- else if (key.includes('.')) {
3976
- throw new BSONError('key ' + key + " must not contain '.'");
3977
- }
4059
+ else if (key.includes('.')) {
4060
+ throw new BSONError('key ' + key + " must not contain '.'");
3978
4061
  }
3979
4062
  }
3980
- if (value === undefined) {
3981
- if (ignoreUndefined === false)
3982
- index = serializeNull(buffer, key, value, index);
3983
- }
3984
- else if (value === null) {
4063
+ }
4064
+ const type = typeof value;
4065
+ if (value === undefined) {
4066
+ if (frame.isArray || frame.ignoreUndefined === false) {
3985
4067
  index = serializeNull(buffer, key, value, index);
3986
4068
  }
3987
- else if (type === 'string') {
3988
- index = serializeString(buffer, key, value, index);
3989
- }
3990
- else if (type === 'number') {
3991
- index = serializeNumber(buffer, key, value, index);
3992
- }
3993
- else if (type === 'bigint') {
3994
- 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);
3995
4088
  }
3996
- else if (type === 'boolean') {
3997
- index = serializeBoolean(buffer, key, value, index);
4089
+ else if (value instanceof Uint8Array || isUint8Array(value)) {
4090
+ index = serializeBuffer(buffer, key, value, index);
3998
4091
  }
3999
- else if (type === 'object' && value._bsontype == null) {
4000
- if (value instanceof Date || isDate(value)) {
4001
- index = serializeDate(buffer, key, value, index);
4002
- }
4003
- else if (value instanceof Uint8Array || isUint8Array(value)) {
4004
- index = serializeBuffer(buffer, key, value, index);
4005
- }
4006
- else if (value instanceof RegExp || isRegExp(value)) {
4007
- index = serializeRegExp(buffer, key, value, index);
4008
- }
4009
- else {
4010
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4011
- }
4092
+ else if (value instanceof RegExp || isRegExp(value)) {
4093
+ index = serializeRegExp(buffer, key, value, index);
4012
4094
  }
4013
- else if (type === 'object') {
4014
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4015
- throw new BSONVersionError();
4016
- }
4017
- else if (value._bsontype === 'ObjectId') {
4018
- index = serializeObjectId(buffer, key, value, index);
4019
- }
4020
- else if (value._bsontype === 'Decimal128') {
4021
- index = serializeDecimal128(buffer, key, value, index);
4022
- }
4023
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4024
- index = serializeLong(buffer, key, value, index);
4025
- }
4026
- else if (value._bsontype === 'Double') {
4027
- index = serializeDouble(buffer, key, value, index);
4028
- }
4029
- else if (value._bsontype === 'Code') {
4030
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4031
- }
4032
- else if (value._bsontype === 'Binary') {
4033
- index = serializeBinary(buffer, key, value, index);
4034
- }
4035
- else if (value._bsontype === 'BSONSymbol') {
4036
- index = serializeSymbol(buffer, key, value, index);
4037
- }
4038
- else if (value._bsontype === 'DBRef') {
4039
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4040
- }
4041
- else if (value._bsontype === 'BSONRegExp') {
4042
- index = serializeBSONRegExp(buffer, key, value, index);
4043
- }
4044
- else if (value._bsontype === 'Int32') {
4045
- index = serializeInt32(buffer, key, value, index);
4046
- }
4047
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4048
- index = serializeMinMax(buffer, key, value, index);
4049
- }
4050
- else if (typeof value._bsontype !== 'undefined') {
4051
- 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');
4052
4098
  }
4053
- }
4054
- else if (type === 'function' && serializeFunctions) {
4055
- index = serializeFunction(buffer, key, value, index);
4056
- }
4057
- }
4058
- }
4059
- else {
4060
- if (typeof object?.toBSON === 'function') {
4061
- object = object.toBSON();
4062
- if (object != null && typeof object !== 'object') {
4063
- 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;
4064
4107
  }
4065
4108
  }
4066
- for (const key of Object.keys(object)) {
4067
- let value = object[key];
4068
- if (typeof value?.toBSON === 'function') {
4069
- value = value.toBSON();
4109
+ else if (type === 'object') {
4110
+ if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4111
+ throw new BSONVersionError();
4070
4112
  }
4071
- const type = typeof value;
4072
- if (typeof key === 'string' && !ignoreKeys.has(key)) {
4073
- if (key.match(regexp) != null) {
4074
- throw new BSONError('key ' + key + ' must not contain null bytes');
4075
- }
4076
- if (checkKeys) {
4077
- if ('$' === key[0]) {
4078
- throw new BSONError('key ' + key + " must not start with '$'");
4079
- }
4080
- else if (key.includes('.')) {
4081
- 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');
4082
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;
4083
4156
  }
4084
4157
  }
4085
- if (value === undefined) {
4086
- if (ignoreUndefined === false)
4087
- index = serializeNull(buffer, key, value, index);
4088
- }
4089
- else if (value === null) {
4090
- index = serializeNull(buffer, key, value, index);
4091
- }
4092
- else if (type === 'string') {
4093
- index = serializeString(buffer, key, value, index);
4158
+ else if (tag === 'Binary') {
4159
+ index = serializeBinary(buffer, key, value, index);
4094
4160
  }
4095
- else if (type === 'number') {
4096
- index = serializeNumber(buffer, key, value, index);
4161
+ else if (tag === 'BSONSymbol') {
4162
+ index = serializeSymbol(buffer, key, value, index);
4097
4163
  }
4098
- else if (type === 'bigint') {
4099
- 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;
4100
4173
  }
4101
- else if (type === 'boolean') {
4102
- index = serializeBoolean(buffer, key, value, index);
4174
+ else if (tag === 'BSONRegExp') {
4175
+ index = serializeBSONRegExp(buffer, key, value, index);
4103
4176
  }
4104
- else if (type === 'object' && value._bsontype == null) {
4105
- if (value instanceof Date || isDate(value)) {
4106
- index = serializeDate(buffer, key, value, index);
4107
- }
4108
- else if (value instanceof Uint8Array || isUint8Array(value)) {
4109
- index = serializeBuffer(buffer, key, value, index);
4110
- }
4111
- else if (value instanceof RegExp || isRegExp(value)) {
4112
- index = serializeRegExp(buffer, key, value, index);
4113
- }
4114
- else {
4115
- index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4116
- }
4177
+ else if (tag === 'Int32') {
4178
+ index = serializeInt32(buffer, key, value, index);
4117
4179
  }
4118
- else if (type === 'object') {
4119
- if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
4120
- throw new BSONVersionError();
4121
- }
4122
- else if (value._bsontype === 'ObjectId') {
4123
- index = serializeObjectId(buffer, key, value, index);
4124
- }
4125
- else if (value._bsontype === 'Decimal128') {
4126
- index = serializeDecimal128(buffer, key, value, index);
4127
- }
4128
- else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
4129
- index = serializeLong(buffer, key, value, index);
4130
- }
4131
- else if (value._bsontype === 'Double') {
4132
- index = serializeDouble(buffer, key, value, index);
4133
- }
4134
- else if (value._bsontype === 'Code') {
4135
- index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
4136
- }
4137
- else if (value._bsontype === 'Binary') {
4138
- index = serializeBinary(buffer, key, value, index);
4139
- }
4140
- else if (value._bsontype === 'BSONSymbol') {
4141
- index = serializeSymbol(buffer, key, value, index);
4142
- }
4143
- else if (value._bsontype === 'DBRef') {
4144
- index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
4145
- }
4146
- else if (value._bsontype === 'BSONRegExp') {
4147
- index = serializeBSONRegExp(buffer, key, value, index);
4148
- }
4149
- else if (value._bsontype === 'Int32') {
4150
- index = serializeInt32(buffer, key, value, index);
4151
- }
4152
- else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
4153
- index = serializeMinMax(buffer, key, value, index);
4154
- }
4155
- else if (typeof value._bsontype !== 'undefined') {
4156
- throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4157
- }
4180
+ else if (tag === 'MinKey' || tag === 'MaxKey') {
4181
+ index = serializeMinMax(buffer, key, value, index);
4158
4182
  }
4159
- else if (type === 'function' && serializeFunctions) {
4160
- index = serializeFunction(buffer, key, value, index);
4183
+ else if (typeof value._bsontype !== 'undefined') {
4184
+ throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
4161
4185
  }
4162
4186
  }
4187
+ else if (type === 'function' && serializeFunctions) {
4188
+ index = serializeFunction(buffer, key, value, index);
4189
+ }
4163
4190
  }
4164
- path.delete(object);
4165
- buffer[index++] = 0x00;
4166
- const size = index - startingIndex;
4167
- startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
4168
4191
  return index;
4169
4192
  }
4170
4193
 
@@ -4320,7 +4343,7 @@ function serializeValue(value, options) {
4320
4343
  if (Array.isArray(value))
4321
4344
  return serializeArray(value, options);
4322
4345
  if (value === undefined)
4323
- return null;
4346
+ return options.ignoreUndefined ? undefined : null;
4324
4347
  if (value instanceof Date || isDate(value)) {
4325
4348
  const dateNum = value.getTime(), inRange = dateNum > -1 && dateNum < 253402318800000;
4326
4349
  if (options.legacy) {
@@ -4447,21 +4470,22 @@ function parse(text, options) {
4447
4470
  return deserializeValue(value, ejsonOptions);
4448
4471
  });
4449
4472
  }
4450
- function stringify(value, replacer, space, options) {
4451
- if (space != null && typeof space === 'object') {
4452
- options = space;
4453
- space = 0;
4473
+ function stringify(value, replacerOrOptions, spaceOrOptions, options) {
4474
+ if (spaceOrOptions != null && typeof spaceOrOptions === 'object') {
4475
+ options = spaceOrOptions;
4476
+ spaceOrOptions = undefined;
4454
4477
  }
4455
- if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
4456
- options = replacer;
4457
- replacer = undefined;
4458
- space = 0;
4478
+ if (replacerOrOptions != null &&
4479
+ typeof replacerOrOptions === 'object' &&
4480
+ !Array.isArray(replacerOrOptions)) {
4481
+ options = replacerOrOptions;
4482
+ replacerOrOptions = undefined;
4459
4483
  }
4460
4484
  const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
4461
4485
  seenObjects: [{ propertyName: '(root)', obj: null }]
4462
4486
  });
4463
4487
  const doc = serializeValue(value, serializeOptions);
4464
- return JSON.stringify(doc, replacer, space);
4488
+ return JSON.stringify(doc, replacerOrOptions, spaceOrOptions);
4465
4489
  }
4466
4490
  function EJSONserialize(value, options) {
4467
4491
  options = options || {};
@@ -4623,7 +4647,7 @@ function serialize(object, options = {}) {
4623
4647
  if (buffer.length < minInternalBufferSize) {
4624
4648
  buffer = ByteUtils.allocate(minInternalBufferSize);
4625
4649
  }
4626
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4650
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4627
4651
  const finishedBuffer = ByteUtils.allocateUnsafe(serializationIndex);
4628
4652
  finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
4629
4653
  return finishedBuffer;
@@ -4633,7 +4657,7 @@ function serializeWithBufferAndIndex(object, finalBuffer, options = {}) {
4633
4657
  const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
4634
4658
  const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
4635
4659
  const startIndex = typeof options.index === 'number' ? options.index : 0;
4636
- const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
4660
+ const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
4637
4661
  finalBuffer.set(buffer.subarray(0, serializationIndex), startIndex);
4638
4662
  return startIndex + serializationIndex - 1;
4639
4663
  }