bson 6.2.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/bson.cjs CHANGED
@@ -97,8 +97,8 @@ class BSONError extends Error {
97
97
  get name() {
98
98
  return 'BSONError';
99
99
  }
100
- constructor(message) {
101
- super(message);
100
+ constructor(message, options) {
101
+ super(message, options);
102
102
  }
103
103
  static isBSONError(value) {
104
104
  return (value != null &&
@@ -127,6 +127,79 @@ class BSONRuntimeError extends BSONError {
127
127
  }
128
128
  }
129
129
 
130
+ const FIRST_BIT = 0x80;
131
+ const FIRST_TWO_BITS = 0xc0;
132
+ const FIRST_THREE_BITS = 0xe0;
133
+ const FIRST_FOUR_BITS = 0xf0;
134
+ const FIRST_FIVE_BITS = 0xf8;
135
+ const TWO_BIT_CHAR = 0xc0;
136
+ const THREE_BIT_CHAR = 0xe0;
137
+ const FOUR_BIT_CHAR = 0xf0;
138
+ const CONTINUING_CHAR = 0x80;
139
+ function validateUtf8(bytes, start, end) {
140
+ let continuation = 0;
141
+ for (let i = start; i < end; i += 1) {
142
+ const byte = bytes[i];
143
+ if (continuation) {
144
+ if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
145
+ return false;
146
+ }
147
+ continuation -= 1;
148
+ }
149
+ else if (byte & FIRST_BIT) {
150
+ if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
151
+ continuation = 1;
152
+ }
153
+ else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
154
+ continuation = 2;
155
+ }
156
+ else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
157
+ continuation = 3;
158
+ }
159
+ else {
160
+ return false;
161
+ }
162
+ }
163
+ }
164
+ return !continuation;
165
+ }
166
+
167
+ function tryLatin(uint8array, start, end) {
168
+ if (uint8array.length === 0) {
169
+ return '';
170
+ }
171
+ const stringByteLength = end - start;
172
+ if (stringByteLength === 0) {
173
+ return '';
174
+ }
175
+ if (stringByteLength > 20) {
176
+ return null;
177
+ }
178
+ if (stringByteLength === 1 && uint8array[start] < 128) {
179
+ return String.fromCharCode(uint8array[start]);
180
+ }
181
+ if (stringByteLength === 2 && uint8array[start] < 128 && uint8array[start + 1] < 128) {
182
+ return String.fromCharCode(uint8array[start]) + String.fromCharCode(uint8array[start + 1]);
183
+ }
184
+ if (stringByteLength === 3 &&
185
+ uint8array[start] < 128 &&
186
+ uint8array[start + 1] < 128 &&
187
+ uint8array[start + 2] < 128) {
188
+ return (String.fromCharCode(uint8array[start]) +
189
+ String.fromCharCode(uint8array[start + 1]) +
190
+ String.fromCharCode(uint8array[start + 2]));
191
+ }
192
+ const latinBytes = [];
193
+ for (let i = start; i < end; i++) {
194
+ const byte = uint8array[i];
195
+ if (byte > 127) {
196
+ return null;
197
+ }
198
+ latinBytes.push(byte);
199
+ }
200
+ return String.fromCharCode(...latinBytes);
201
+ }
202
+
130
203
  function nodejsMathRandomBytes(byteLength) {
131
204
  return nodeJsByteUtils.fromNumberArray(Array.from({ length: byteLength }, () => Math.floor(Math.random() * 256)));
132
205
  }
@@ -185,8 +258,23 @@ const nodeJsByteUtils = {
185
258
  fromUTF8(text) {
186
259
  return Buffer.from(text, 'utf8');
187
260
  },
188
- toUTF8(buffer, start, end) {
189
- return nodeJsByteUtils.toLocalBufferType(buffer).toString('utf8', start, end);
261
+ toUTF8(buffer, start, end, fatal) {
262
+ const basicLatin = end - start <= 20 ? tryLatin(buffer, start, end) : null;
263
+ if (basicLatin != null) {
264
+ return basicLatin;
265
+ }
266
+ const string = nodeJsByteUtils.toLocalBufferType(buffer).toString('utf8', start, end);
267
+ if (fatal) {
268
+ for (let i = 0; i < string.length; i++) {
269
+ if (string.charCodeAt(i) === 0xfffd) {
270
+ if (!validateUtf8(buffer, start, end)) {
271
+ throw new BSONError('Invalid UTF-8 string in BSON document');
272
+ }
273
+ break;
274
+ }
275
+ }
276
+ }
277
+ return string;
190
278
  },
191
279
  utf8ByteLength(input) {
192
280
  return Buffer.byteLength(input, 'utf8');
@@ -296,8 +384,20 @@ const webByteUtils = {
296
384
  fromUTF8(text) {
297
385
  return new TextEncoder().encode(text);
298
386
  },
299
- toUTF8(uint8array, start, end) {
300
- return new TextDecoder('utf8', { fatal: false }).decode(uint8array.slice(start, end));
387
+ toUTF8(uint8array, start, end, fatal) {
388
+ const basicLatin = end - start <= 20 ? tryLatin(uint8array, start, end) : null;
389
+ if (basicLatin != null) {
390
+ return basicLatin;
391
+ }
392
+ if (fatal) {
393
+ try {
394
+ return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
395
+ }
396
+ catch (cause) {
397
+ throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
398
+ }
399
+ }
400
+ return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
301
401
  },
302
402
  utf8ByteLength(input) {
303
403
  return webByteUtils.fromUTF8(input).byteLength;
@@ -418,8 +518,8 @@ class Binary extends BSONValue {
418
518
  if (encoding === 'base64')
419
519
  return ByteUtils.toBase64(this.buffer);
420
520
  if (encoding === 'utf8' || encoding === 'utf-8')
421
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength);
422
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength);
521
+ return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
522
+ return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
423
523
  }
424
524
  toExtendedJSON(options) {
425
525
  options = options || {};
@@ -2507,43 +2607,6 @@ class Timestamp extends LongWithoutOverridesClass {
2507
2607
  }
2508
2608
  Timestamp.MAX_VALUE = Long.MAX_UNSIGNED_VALUE;
2509
2609
 
2510
- const FIRST_BIT = 0x80;
2511
- const FIRST_TWO_BITS = 0xc0;
2512
- const FIRST_THREE_BITS = 0xe0;
2513
- const FIRST_FOUR_BITS = 0xf0;
2514
- const FIRST_FIVE_BITS = 0xf8;
2515
- const TWO_BIT_CHAR = 0xc0;
2516
- const THREE_BIT_CHAR = 0xe0;
2517
- const FOUR_BIT_CHAR = 0xf0;
2518
- const CONTINUING_CHAR = 0x80;
2519
- function validateUtf8(bytes, start, end) {
2520
- let continuation = 0;
2521
- for (let i = start; i < end; i += 1) {
2522
- const byte = bytes[i];
2523
- if (continuation) {
2524
- if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
2525
- return false;
2526
- }
2527
- continuation -= 1;
2528
- }
2529
- else if (byte & FIRST_BIT) {
2530
- if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
2531
- continuation = 1;
2532
- }
2533
- else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
2534
- continuation = 2;
2535
- }
2536
- else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
2537
- continuation = 3;
2538
- }
2539
- else {
2540
- return false;
2541
- }
2542
- }
2543
- }
2544
- return !continuation;
2545
- }
2546
-
2547
2610
  const JS_INT_MAX_LONG = Long.fromNumber(JS_INT_MAX);
2548
2611
  const JS_INT_MIN_LONG = Long.fromNumber(JS_INT_MIN);
2549
2612
  function internalDeserialize(buffer, options, isArray) {
@@ -2635,7 +2698,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2635
2698
  }
2636
2699
  if (i >= buffer.byteLength)
2637
2700
  throw new BSONError('Bad BSON Document: illegal CString');
2638
- const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i);
2701
+ const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
2639
2702
  let shouldValidateKey = true;
2640
2703
  if (globalUTFValidation || utf8KeysSet.has(name)) {
2641
2704
  shouldValidateKey = validationSetting;
@@ -2658,7 +2721,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2658
2721
  buffer[index + stringSize - 1] !== 0) {
2659
2722
  throw new BSONError('bad string length in bson');
2660
2723
  }
2661
- value = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
2724
+ value = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2662
2725
  index = index + stringSize;
2663
2726
  }
2664
2727
  else if (elementType === BSON_DATA_OID) {
@@ -2850,7 +2913,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2850
2913
  }
2851
2914
  if (i >= buffer.length)
2852
2915
  throw new BSONError('Bad BSON Document: illegal CString');
2853
- const source = ByteUtils.toUTF8(buffer, index, i);
2916
+ const source = ByteUtils.toUTF8(buffer, index, i, false);
2854
2917
  index = i + 1;
2855
2918
  i = index;
2856
2919
  while (buffer[i] !== 0x00 && i < buffer.length) {
@@ -2858,7 +2921,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2858
2921
  }
2859
2922
  if (i >= buffer.length)
2860
2923
  throw new BSONError('Bad BSON Document: illegal CString');
2861
- const regExpOptions = ByteUtils.toUTF8(buffer, index, i);
2924
+ const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
2862
2925
  index = i + 1;
2863
2926
  const optionsArray = new Array(regExpOptions.length);
2864
2927
  for (i = 0; i < regExpOptions.length; i++) {
@@ -2883,7 +2946,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2883
2946
  }
2884
2947
  if (i >= buffer.length)
2885
2948
  throw new BSONError('Bad BSON Document: illegal CString');
2886
- const source = ByteUtils.toUTF8(buffer, index, i);
2949
+ const source = ByteUtils.toUTF8(buffer, index, i, false);
2887
2950
  index = i + 1;
2888
2951
  i = index;
2889
2952
  while (buffer[i] !== 0x00 && i < buffer.length) {
@@ -2891,7 +2954,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2891
2954
  }
2892
2955
  if (i >= buffer.length)
2893
2956
  throw new BSONError('Bad BSON Document: illegal CString');
2894
- const regExpOptions = ByteUtils.toUTF8(buffer, index, i);
2957
+ const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
2895
2958
  index = i + 1;
2896
2959
  value = new BSONRegExp(source, regExpOptions);
2897
2960
  }
@@ -2905,7 +2968,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2905
2968
  buffer[index + stringSize - 1] !== 0) {
2906
2969
  throw new BSONError('bad string length in bson');
2907
2970
  }
2908
- const symbol = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
2971
+ const symbol = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2909
2972
  value = promoteValues ? symbol : new BSONSymbol(symbol);
2910
2973
  index = index + stringSize;
2911
2974
  }
@@ -2936,7 +2999,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2936
2999
  buffer[index + stringSize - 1] !== 0) {
2937
3000
  throw new BSONError('bad string length in bson');
2938
3001
  }
2939
- const functionString = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
3002
+ const functionString = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2940
3003
  value = new Code(functionString);
2941
3004
  index = index + stringSize;
2942
3005
  }
@@ -2957,7 +3020,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2957
3020
  buffer[index + stringSize - 1] !== 0) {
2958
3021
  throw new BSONError('bad string length in bson');
2959
3022
  }
2960
- const functionString = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
3023
+ const functionString = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2961
3024
  index = index + stringSize;
2962
3025
  const _index = index;
2963
3026
  const objectSize = buffer[index] |
@@ -2988,7 +3051,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2988
3051
  throw new BSONError('Invalid UTF-8 string in BSON document');
2989
3052
  }
2990
3053
  }
2991
- const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1);
3054
+ const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, false);
2992
3055
  index = index + stringSize;
2993
3056
  const oidBuffer = ByteUtils.allocate(12);
2994
3057
  oidBuffer.set(buffer.subarray(index, index + 12), 0);
@@ -3027,20 +3090,6 @@ function deserializeObject(buffer, index, options, isArray = false) {
3027
3090
  }
3028
3091
  return object;
3029
3092
  }
3030
- function getValidatedString(buffer, start, end, shouldValidateUtf8) {
3031
- const value = ByteUtils.toUTF8(buffer, start, end);
3032
- if (shouldValidateUtf8) {
3033
- for (let i = 0; i < value.length; i++) {
3034
- if (value.charCodeAt(i) === 0xfffd) {
3035
- if (!validateUtf8(buffer, start, end)) {
3036
- throw new BSONError('Invalid UTF-8 string in BSON document');
3037
- }
3038
- break;
3039
- }
3040
- }
3041
- }
3042
- return value;
3043
- }
3044
3093
 
3045
3094
  const regexp = /\x00/;
3046
3095
  const ignoreKeys = new Set(['$db', '$ref', '$id', '$clusterTime']);