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.mjs CHANGED
@@ -95,8 +95,8 @@ class BSONError extends Error {
95
95
  get name() {
96
96
  return 'BSONError';
97
97
  }
98
- constructor(message) {
99
- super(message);
98
+ constructor(message, options) {
99
+ super(message, options);
100
100
  }
101
101
  static isBSONError(value) {
102
102
  return (value != null &&
@@ -125,6 +125,79 @@ class BSONRuntimeError extends BSONError {
125
125
  }
126
126
  }
127
127
 
128
+ const FIRST_BIT = 0x80;
129
+ const FIRST_TWO_BITS = 0xc0;
130
+ const FIRST_THREE_BITS = 0xe0;
131
+ const FIRST_FOUR_BITS = 0xf0;
132
+ const FIRST_FIVE_BITS = 0xf8;
133
+ const TWO_BIT_CHAR = 0xc0;
134
+ const THREE_BIT_CHAR = 0xe0;
135
+ const FOUR_BIT_CHAR = 0xf0;
136
+ const CONTINUING_CHAR = 0x80;
137
+ function validateUtf8(bytes, start, end) {
138
+ let continuation = 0;
139
+ for (let i = start; i < end; i += 1) {
140
+ const byte = bytes[i];
141
+ if (continuation) {
142
+ if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
143
+ return false;
144
+ }
145
+ continuation -= 1;
146
+ }
147
+ else if (byte & FIRST_BIT) {
148
+ if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
149
+ continuation = 1;
150
+ }
151
+ else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
152
+ continuation = 2;
153
+ }
154
+ else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
155
+ continuation = 3;
156
+ }
157
+ else {
158
+ return false;
159
+ }
160
+ }
161
+ }
162
+ return !continuation;
163
+ }
164
+
165
+ function tryLatin(uint8array, start, end) {
166
+ if (uint8array.length === 0) {
167
+ return '';
168
+ }
169
+ const stringByteLength = end - start;
170
+ if (stringByteLength === 0) {
171
+ return '';
172
+ }
173
+ if (stringByteLength > 20) {
174
+ return null;
175
+ }
176
+ if (stringByteLength === 1 && uint8array[start] < 128) {
177
+ return String.fromCharCode(uint8array[start]);
178
+ }
179
+ if (stringByteLength === 2 && uint8array[start] < 128 && uint8array[start + 1] < 128) {
180
+ return String.fromCharCode(uint8array[start]) + String.fromCharCode(uint8array[start + 1]);
181
+ }
182
+ if (stringByteLength === 3 &&
183
+ uint8array[start] < 128 &&
184
+ uint8array[start + 1] < 128 &&
185
+ uint8array[start + 2] < 128) {
186
+ return (String.fromCharCode(uint8array[start]) +
187
+ String.fromCharCode(uint8array[start + 1]) +
188
+ String.fromCharCode(uint8array[start + 2]));
189
+ }
190
+ const latinBytes = [];
191
+ for (let i = start; i < end; i++) {
192
+ const byte = uint8array[i];
193
+ if (byte > 127) {
194
+ return null;
195
+ }
196
+ latinBytes.push(byte);
197
+ }
198
+ return String.fromCharCode(...latinBytes);
199
+ }
200
+
128
201
  function nodejsMathRandomBytes(byteLength) {
129
202
  return nodeJsByteUtils.fromNumberArray(Array.from({ length: byteLength }, () => Math.floor(Math.random() * 256)));
130
203
  }
@@ -183,8 +256,23 @@ const nodeJsByteUtils = {
183
256
  fromUTF8(text) {
184
257
  return Buffer.from(text, 'utf8');
185
258
  },
186
- toUTF8(buffer, start, end) {
187
- return nodeJsByteUtils.toLocalBufferType(buffer).toString('utf8', start, end);
259
+ toUTF8(buffer, start, end, fatal) {
260
+ const basicLatin = end - start <= 20 ? tryLatin(buffer, start, end) : null;
261
+ if (basicLatin != null) {
262
+ return basicLatin;
263
+ }
264
+ const string = nodeJsByteUtils.toLocalBufferType(buffer).toString('utf8', start, end);
265
+ if (fatal) {
266
+ for (let i = 0; i < string.length; i++) {
267
+ if (string.charCodeAt(i) === 0xfffd) {
268
+ if (!validateUtf8(buffer, start, end)) {
269
+ throw new BSONError('Invalid UTF-8 string in BSON document');
270
+ }
271
+ break;
272
+ }
273
+ }
274
+ }
275
+ return string;
188
276
  },
189
277
  utf8ByteLength(input) {
190
278
  return Buffer.byteLength(input, 'utf8');
@@ -294,8 +382,20 @@ const webByteUtils = {
294
382
  fromUTF8(text) {
295
383
  return new TextEncoder().encode(text);
296
384
  },
297
- toUTF8(uint8array, start, end) {
298
- return new TextDecoder('utf8', { fatal: false }).decode(uint8array.slice(start, end));
385
+ toUTF8(uint8array, start, end, fatal) {
386
+ const basicLatin = end - start <= 20 ? tryLatin(uint8array, start, end) : null;
387
+ if (basicLatin != null) {
388
+ return basicLatin;
389
+ }
390
+ if (fatal) {
391
+ try {
392
+ return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
393
+ }
394
+ catch (cause) {
395
+ throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
396
+ }
397
+ }
398
+ return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
299
399
  },
300
400
  utf8ByteLength(input) {
301
401
  return webByteUtils.fromUTF8(input).byteLength;
@@ -416,8 +516,8 @@ class Binary extends BSONValue {
416
516
  if (encoding === 'base64')
417
517
  return ByteUtils.toBase64(this.buffer);
418
518
  if (encoding === 'utf8' || encoding === 'utf-8')
419
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength);
420
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength);
519
+ return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
520
+ return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
421
521
  }
422
522
  toExtendedJSON(options) {
423
523
  options = options || {};
@@ -2505,43 +2605,6 @@ class Timestamp extends LongWithoutOverridesClass {
2505
2605
  }
2506
2606
  Timestamp.MAX_VALUE = Long.MAX_UNSIGNED_VALUE;
2507
2607
 
2508
- const FIRST_BIT = 0x80;
2509
- const FIRST_TWO_BITS = 0xc0;
2510
- const FIRST_THREE_BITS = 0xe0;
2511
- const FIRST_FOUR_BITS = 0xf0;
2512
- const FIRST_FIVE_BITS = 0xf8;
2513
- const TWO_BIT_CHAR = 0xc0;
2514
- const THREE_BIT_CHAR = 0xe0;
2515
- const FOUR_BIT_CHAR = 0xf0;
2516
- const CONTINUING_CHAR = 0x80;
2517
- function validateUtf8(bytes, start, end) {
2518
- let continuation = 0;
2519
- for (let i = start; i < end; i += 1) {
2520
- const byte = bytes[i];
2521
- if (continuation) {
2522
- if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
2523
- return false;
2524
- }
2525
- continuation -= 1;
2526
- }
2527
- else if (byte & FIRST_BIT) {
2528
- if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
2529
- continuation = 1;
2530
- }
2531
- else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
2532
- continuation = 2;
2533
- }
2534
- else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
2535
- continuation = 3;
2536
- }
2537
- else {
2538
- return false;
2539
- }
2540
- }
2541
- }
2542
- return !continuation;
2543
- }
2544
-
2545
2608
  const JS_INT_MAX_LONG = Long.fromNumber(JS_INT_MAX);
2546
2609
  const JS_INT_MIN_LONG = Long.fromNumber(JS_INT_MIN);
2547
2610
  function internalDeserialize(buffer, options, isArray) {
@@ -2633,7 +2696,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2633
2696
  }
2634
2697
  if (i >= buffer.byteLength)
2635
2698
  throw new BSONError('Bad BSON Document: illegal CString');
2636
- const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i);
2699
+ const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
2637
2700
  let shouldValidateKey = true;
2638
2701
  if (globalUTFValidation || utf8KeysSet.has(name)) {
2639
2702
  shouldValidateKey = validationSetting;
@@ -2656,7 +2719,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2656
2719
  buffer[index + stringSize - 1] !== 0) {
2657
2720
  throw new BSONError('bad string length in bson');
2658
2721
  }
2659
- value = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
2722
+ value = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2660
2723
  index = index + stringSize;
2661
2724
  }
2662
2725
  else if (elementType === BSON_DATA_OID) {
@@ -2848,7 +2911,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2848
2911
  }
2849
2912
  if (i >= buffer.length)
2850
2913
  throw new BSONError('Bad BSON Document: illegal CString');
2851
- const source = ByteUtils.toUTF8(buffer, index, i);
2914
+ const source = ByteUtils.toUTF8(buffer, index, i, false);
2852
2915
  index = i + 1;
2853
2916
  i = index;
2854
2917
  while (buffer[i] !== 0x00 && i < buffer.length) {
@@ -2856,7 +2919,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2856
2919
  }
2857
2920
  if (i >= buffer.length)
2858
2921
  throw new BSONError('Bad BSON Document: illegal CString');
2859
- const regExpOptions = ByteUtils.toUTF8(buffer, index, i);
2922
+ const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
2860
2923
  index = i + 1;
2861
2924
  const optionsArray = new Array(regExpOptions.length);
2862
2925
  for (i = 0; i < regExpOptions.length; i++) {
@@ -2881,7 +2944,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2881
2944
  }
2882
2945
  if (i >= buffer.length)
2883
2946
  throw new BSONError('Bad BSON Document: illegal CString');
2884
- const source = ByteUtils.toUTF8(buffer, index, i);
2947
+ const source = ByteUtils.toUTF8(buffer, index, i, false);
2885
2948
  index = i + 1;
2886
2949
  i = index;
2887
2950
  while (buffer[i] !== 0x00 && i < buffer.length) {
@@ -2889,7 +2952,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2889
2952
  }
2890
2953
  if (i >= buffer.length)
2891
2954
  throw new BSONError('Bad BSON Document: illegal CString');
2892
- const regExpOptions = ByteUtils.toUTF8(buffer, index, i);
2955
+ const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
2893
2956
  index = i + 1;
2894
2957
  value = new BSONRegExp(source, regExpOptions);
2895
2958
  }
@@ -2903,7 +2966,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2903
2966
  buffer[index + stringSize - 1] !== 0) {
2904
2967
  throw new BSONError('bad string length in bson');
2905
2968
  }
2906
- const symbol = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
2969
+ const symbol = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2907
2970
  value = promoteValues ? symbol : new BSONSymbol(symbol);
2908
2971
  index = index + stringSize;
2909
2972
  }
@@ -2934,7 +2997,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2934
2997
  buffer[index + stringSize - 1] !== 0) {
2935
2998
  throw new BSONError('bad string length in bson');
2936
2999
  }
2937
- const functionString = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
3000
+ const functionString = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2938
3001
  value = new Code(functionString);
2939
3002
  index = index + stringSize;
2940
3003
  }
@@ -2955,7 +3018,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2955
3018
  buffer[index + stringSize - 1] !== 0) {
2956
3019
  throw new BSONError('bad string length in bson');
2957
3020
  }
2958
- const functionString = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
3021
+ const functionString = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
2959
3022
  index = index + stringSize;
2960
3023
  const _index = index;
2961
3024
  const objectSize = buffer[index] |
@@ -2986,7 +3049,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
2986
3049
  throw new BSONError('Invalid UTF-8 string in BSON document');
2987
3050
  }
2988
3051
  }
2989
- const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1);
3052
+ const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, false);
2990
3053
  index = index + stringSize;
2991
3054
  const oidBuffer = ByteUtils.allocate(12);
2992
3055
  oidBuffer.set(buffer.subarray(index, index + 12), 0);
@@ -3025,20 +3088,6 @@ function deserializeObject(buffer, index, options, isArray = false) {
3025
3088
  }
3026
3089
  return object;
3027
3090
  }
3028
- function getValidatedString(buffer, start, end, shouldValidateUtf8) {
3029
- const value = ByteUtils.toUTF8(buffer, start, end);
3030
- if (shouldValidateUtf8) {
3031
- for (let i = 0; i < value.length; i++) {
3032
- if (value.charCodeAt(i) === 0xfffd) {
3033
- if (!validateUtf8(buffer, start, end)) {
3034
- throw new BSONError('Invalid UTF-8 string in BSON document');
3035
- }
3036
- break;
3037
- }
3038
- }
3039
- }
3040
- return value;
3041
- }
3042
3091
 
3043
3092
  const regexp = /\x00/;
3044
3093
  const ignoreKeys = new Set(['$db', '$ref', '$id', '$clusterTime']);