bson 6.6.1 → 6.7.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/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "vendor"
15
15
  ],
16
16
  "types": "bson.d.ts",
17
- "version": "6.6.1",
17
+ "version": "6.7.1",
18
18
  "author": {
19
19
  "name": "The MongoDB NodeJS Team",
20
20
  "email": "dbx-node@mongodb.com"
@@ -27,40 +27,40 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@istanbuljs/nyc-config-typescript": "^1.0.2",
30
- "@microsoft/api-extractor": "^7.40.5",
31
- "@octokit/core": "^5.1.0",
30
+ "@microsoft/api-extractor": "^7.43.1",
31
+ "@octokit/core": "^6.1.2",
32
32
  "@rollup/plugin-node-resolve": "^15.2.3",
33
33
  "@rollup/plugin-typescript": "^11.1.6",
34
- "@types/chai": "^4.3.11",
34
+ "@types/chai": "^4.3.14",
35
35
  "@types/mocha": "^10.0.6",
36
- "@types/node": "^20.11.19",
36
+ "@types/node": "^20.12.7",
37
37
  "@types/sinon": "^17.0.3",
38
38
  "@types/sinon-chai": "^3.2.12",
39
- "@typescript-eslint/eslint-plugin": "^7.0.2",
40
- "@typescript-eslint/parser": "^7.0.2",
39
+ "@typescript-eslint/eslint-plugin": "^7.7.0",
40
+ "@typescript-eslint/parser": "^7.7.0",
41
41
  "benchmark": "^2.1.4",
42
- "chai": "^4.3.10",
42
+ "chai": "^4.4.1",
43
43
  "chalk": "^5.3.0",
44
44
  "dbx-js-tools": "github:mongodb-js/dbx-js-tools",
45
- "eslint": "^8.56.0",
45
+ "eslint": "^8.57.0",
46
46
  "eslint-config-prettier": "^9.1.0",
47
47
  "eslint-plugin-no-bigint-usage": "file:etc/eslint/no-bigint-usage",
48
48
  "eslint-plugin-prettier": "^5.1.3",
49
49
  "eslint-plugin-tsdoc": "^0.2.17",
50
- "magic-string": "^0.30.7",
51
- "mocha": "10.3.0",
50
+ "magic-string": "^0.30.10",
51
+ "mocha": "^10.4.0",
52
52
  "node-fetch": "^3.3.2",
53
53
  "nyc": "^15.1.0",
54
54
  "prettier": "^3.2.5",
55
- "rollup": "^4.12.0",
55
+ "rollup": "^4.14.3",
56
56
  "sinon": "^17.0.1",
57
57
  "sinon-chai": "^3.7.0",
58
58
  "source-map-support": "^0.5.21",
59
59
  "standard-version": "^9.5.0",
60
- "tar": "^6.2.0",
60
+ "tar": "^7.0.1",
61
61
  "ts-node": "^10.9.2",
62
- "tsd": "^0.30.5",
63
- "typescript": "^5.0.4",
62
+ "tsd": "^0.31.0",
63
+ "typescript": "5.3",
64
64
  "typescript-cached-transpile": "0.0.6",
65
65
  "uuid": "^9.0.1"
66
66
  },
package/src/constants.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** @internal */
2
- export const BSON_MAJOR_VERSION = 6 as const;
2
+ export const BSON_MAJOR_VERSION = 6;
3
3
 
4
4
  /** @internal */
5
5
  export const BSON_INT32_MAX = 0x7fffffff;
package/src/double.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BSONValue } from './bson_value';
2
+ import { BSONError } from './error';
2
3
  import type { EJSONOptions } from './extended_json';
3
4
  import { type InspectFn, defaultInspect } from './parser/utils';
4
5
 
@@ -32,6 +33,41 @@ export class Double extends BSONValue {
32
33
  this.value = +value;
33
34
  }
34
35
 
36
+ /**
37
+ * Attempt to create an double type from string.
38
+ *
39
+ * This method will throw a BSONError on any string input that is not representable as a IEEE-754 64-bit double.
40
+ * Notably, this method will also throw on the following string formats:
41
+ * - Strings in non-decimal and non-exponential formats (binary, hex, or octal digits)
42
+ * - Strings with characters other than numeric, floating point, or leading sign characters (Note: 'Infinity', '-Infinity', and 'NaN' input strings are still allowed)
43
+ * - Strings with leading and/or trailing whitespace
44
+ *
45
+ * Strings with leading zeros, however, are also allowed
46
+ *
47
+ * @param value - the string we want to represent as a double.
48
+ */
49
+ static fromString(value: string): Double {
50
+ const coercedValue = Number(value);
51
+
52
+ if (value === 'NaN') return new Double(NaN);
53
+ if (value === 'Infinity') return new Double(Infinity);
54
+ if (value === '-Infinity') return new Double(-Infinity);
55
+
56
+ if (!Number.isFinite(coercedValue)) {
57
+ throw new BSONError(`Input: ${value} is not representable as a Double`);
58
+ }
59
+ if (value.trim() !== value) {
60
+ throw new BSONError(`Input: '${value}' contains whitespace`);
61
+ }
62
+ if (value === '') {
63
+ throw new BSONError(`Input is an empty string`);
64
+ }
65
+ if (/[^-0-9.+eE]/.test(value)) {
66
+ throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`);
67
+ }
68
+ return new Double(coercedValue);
69
+ }
70
+
35
71
  /**
36
72
  * Access the number value.
37
73
  *
package/src/int_32.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { BSONValue } from './bson_value';
2
+ import { BSON_INT32_MAX, BSON_INT32_MIN } from './constants';
3
+ import { BSONError } from './error';
2
4
  import type { EJSONOptions } from './extended_json';
3
5
  import { type InspectFn, defaultInspect } from './parser/utils';
6
+ import { removeLeadingZerosAndExplicitPlus } from './utils/string_utils';
4
7
 
5
8
  /** @public */
6
9
  export interface Int32Extended {
@@ -32,6 +35,37 @@ export class Int32 extends BSONValue {
32
35
  this.value = +value | 0;
33
36
  }
34
37
 
38
+ /**
39
+ * Attempt to create an Int32 type from string.
40
+ *
41
+ * This method will throw a BSONError on any string input that is not representable as an Int32.
42
+ * Notably, this method will also throw on the following string formats:
43
+ * - Strings in non-decimal formats (exponent notation, binary, hex, or octal digits)
44
+ * - Strings non-numeric and non-leading sign characters (ex: '2.0', '24,000')
45
+ * - Strings with leading and/or trailing whitespace
46
+ *
47
+ * Strings with leading zeros, however, are allowed.
48
+ *
49
+ * @param value - the string we want to represent as an int32.
50
+ */
51
+ static fromString(value: string): Int32 {
52
+ const cleanedValue = removeLeadingZerosAndExplicitPlus(value);
53
+
54
+ const coercedValue = Number(value);
55
+
56
+ if (BSON_INT32_MAX < coercedValue) {
57
+ throw new BSONError(`Input: '${value}' is larger than the maximum value for Int32`);
58
+ } else if (BSON_INT32_MIN > coercedValue) {
59
+ throw new BSONError(`Input: '${value}' is smaller than the minimum value for Int32`);
60
+ } else if (!Number.isSafeInteger(coercedValue)) {
61
+ throw new BSONError(`Input: '${value}' is not a safe integer`);
62
+ } else if (coercedValue.toString() !== cleanedValue) {
63
+ // catch all case
64
+ throw new BSONError(`Input: '${value}' is not a valid Int32 string`);
65
+ }
66
+ return new Int32(coercedValue);
67
+ }
68
+
35
69
  /**
36
70
  * Access the number value.
37
71
  *
package/src/long.ts CHANGED
@@ -3,6 +3,7 @@ import { BSONError } from './error';
3
3
  import type { EJSONOptions } from './extended_json';
4
4
  import { type InspectFn, defaultInspect } from './parser/utils';
5
5
  import type { Timestamp } from './timestamp';
6
+ import * as StringUtils from './utils/string_utils';
6
7
 
7
8
  interface LongWASMHelpers {
8
9
  /** Gets the high bits of the last operation performed */
@@ -246,29 +247,24 @@ export class Long extends BSONValue {
246
247
  }
247
248
 
248
249
  /**
250
+ * @internal
249
251
  * Returns a Long representation of the given string, written using the specified radix.
252
+ * Throws an error if `throwsError` is set to true and any of the following conditions are true:
253
+ * - the string contains invalid characters for the given radix
254
+ * - the string contains whitespace
250
255
  * @param str - The textual representation of the Long
251
256
  * @param unsigned - Whether unsigned or not, defaults to signed
252
257
  * @param radix - The radix in which the text is written (2-36), defaults to 10
253
258
  * @returns The corresponding Long value
254
259
  */
255
- static fromString(str: string, unsigned?: boolean, radix?: number): Long {
260
+ private static _fromString(str: string, unsigned: boolean, radix: number): Long {
256
261
  if (str.length === 0) throw new BSONError('empty string');
257
- if (str === 'NaN' || str === 'Infinity' || str === '+Infinity' || str === '-Infinity')
258
- return Long.ZERO;
259
- if (typeof unsigned === 'number') {
260
- // For goog.math.long compatibility
261
- (radix = unsigned), (unsigned = false);
262
- } else {
263
- unsigned = !!unsigned;
264
- }
265
- radix = radix || 10;
266
262
  if (radix < 2 || 36 < radix) throw new BSONError('radix');
267
263
 
268
264
  let p;
269
265
  if ((p = str.indexOf('-')) > 0) throw new BSONError('interior hyphen');
270
266
  else if (p === 0) {
271
- return Long.fromString(str.substring(1), unsigned, radix).neg();
267
+ return Long._fromString(str.substring(1), unsigned, radix).neg();
272
268
  }
273
269
 
274
270
  // Do several (8) digits each time through the loop, so as to
@@ -291,6 +287,167 @@ export class Long extends BSONValue {
291
287
  return result;
292
288
  }
293
289
 
290
+ /**
291
+ * Returns a signed Long representation of the given string, written using radix 10.
292
+ * Will throw an error if the given text is not exactly representable as a Long.
293
+ * Throws an error if any of the following conditions are true:
294
+ * - the string contains invalid characters for the radix 10
295
+ * - the string contains whitespace
296
+ * - the value the string represents is too large or too small to be a Long
297
+ * Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
298
+ * @param str - The textual representation of the Long
299
+ * @returns The corresponding Long value
300
+ */
301
+ static fromStringStrict(str: string): Long;
302
+ /**
303
+ * Returns a Long representation of the given string, written using the radix 10.
304
+ * Will throw an error if the given parameters are not exactly representable as a Long.
305
+ * Throws an error if any of the following conditions are true:
306
+ * - the string contains invalid characters for the given radix
307
+ * - the string contains whitespace
308
+ * - the value the string represents is too large or too small to be a Long
309
+ * Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
310
+ * @param str - The textual representation of the Long
311
+ * @param unsigned - Whether unsigned or not, defaults to signed
312
+ * @returns The corresponding Long value
313
+ */
314
+ static fromStringStrict(str: string, unsigned?: boolean): Long;
315
+ /**
316
+ * Returns a signed Long representation of the given string, written using the specified radix.
317
+ * Will throw an error if the given parameters are not exactly representable as a Long.
318
+ * Throws an error if any of the following conditions are true:
319
+ * - the string contains invalid characters for the given radix
320
+ * - the string contains whitespace
321
+ * - the value the string represents is too large or too small to be a Long
322
+ * Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
323
+ * @param str - The textual representation of the Long
324
+ * @param radix - The radix in which the text is written (2-36), defaults to 10
325
+ * @returns The corresponding Long value
326
+ */
327
+ static fromStringStrict(str: string, radix?: boolean): Long;
328
+ /**
329
+ * Returns a Long representation of the given string, written using the specified radix.
330
+ * Will throw an error if the given parameters are not exactly representable as a Long.
331
+ * Throws an error if any of the following conditions are true:
332
+ * - the string contains invalid characters for the given radix
333
+ * - the string contains whitespace
334
+ * - the value the string represents is too large or too small to be a Long
335
+ * Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
336
+ * @param str - The textual representation of the Long
337
+ * @param unsigned - Whether unsigned or not, defaults to signed
338
+ * @param radix - The radix in which the text is written (2-36), defaults to 10
339
+ * @returns The corresponding Long value
340
+ */
341
+ static fromStringStrict(str: string, unsigned?: boolean, radix?: number): Long;
342
+ static fromStringStrict(str: string, unsignedOrRadix?: boolean | number, radix?: number): Long {
343
+ let unsigned = false;
344
+ if (typeof unsignedOrRadix === 'number') {
345
+ // For goog.math.long compatibility
346
+ (radix = unsignedOrRadix), (unsignedOrRadix = false);
347
+ } else {
348
+ unsigned = !!unsignedOrRadix;
349
+ }
350
+ radix ??= 10;
351
+
352
+ if (str.trim() !== str) {
353
+ throw new BSONError(`Input: '${str}' contains leading and/or trailing whitespace`);
354
+ }
355
+ if (!StringUtils.validateStringCharacters(str, radix)) {
356
+ throw new BSONError(`Input: '${str}' contains invalid characters for radix: ${radix}`);
357
+ }
358
+
359
+ // remove leading zeros (for later string comparison and to make math faster)
360
+ const cleanedStr = StringUtils.removeLeadingZerosAndExplicitPlus(str);
361
+
362
+ // check roundtrip result
363
+ const result = Long._fromString(cleanedStr, unsigned, radix);
364
+ if (result.toString(radix).toLowerCase() !== cleanedStr.toLowerCase()) {
365
+ throw new BSONError(
366
+ `Input: ${str} is not representable as ${result.unsigned ? 'an unsigned' : 'a signed'} 64-bit Long ${radix != null ? `with radix: ${radix}` : ''}`
367
+ );
368
+ }
369
+ return result;
370
+ }
371
+
372
+ /**
373
+ * Returns a signed Long representation of the given string, written using radix 10.
374
+ *
375
+ * If the input string is empty, this function will throw a BSONError.
376
+ *
377
+ * If input string does not have valid signed 64-bit Long representation, this method will return a coerced value:
378
+ * - inputs that overflow 64-bit signed long will be coerced to Long.MAX_VALUE and Long.MIN_VALUE respectively
379
+ * - 'NaN' or '+/-Infinity' are coerced to Long.ZERO
380
+ * - other invalid characters sequences have variable behavior
381
+ *
382
+ * @param str - The textual representation of the Long
383
+ * @returns The corresponding Long value
384
+ */
385
+ static fromString(str: string): Long;
386
+ /**
387
+ * Returns a signed Long representation of the given string, written using the provided radix.
388
+ *
389
+ * If the input string is empty or a provided radix is not within (2-36), this function will throw a BSONError.
390
+ *
391
+ * If input parameters do not have valid signed 64-bit Long representation, this method will return a coerced value:
392
+ * - inputs that overflow 64-bit signed long will be coerced to Long.MAX_VALUE and Long.MIN_VALUE respectively
393
+ * - if the radix is less than 24, 'NaN' is coerced to Long.ZERO
394
+ * - if the radix is less than 35, '+/-Infinity' inputs are coerced to Long.ZERO
395
+ * - other invalid characters sequences have variable behavior
396
+ * @param str - The textual representation of the Long
397
+ * @param radix - The radix in which the text is written (2-36), defaults to 10
398
+ * @returns The corresponding Long value
399
+ */
400
+ static fromString(str: string, radix?: number): Long;
401
+ /**
402
+ * Returns a Long representation of the given string, written using radix 10.
403
+ *
404
+ * If the input string is empty, this function will throw a BSONError.
405
+ *
406
+ * If input parameters do not have a valid 64-bit Long representation, this method will return a coerced value:
407
+ * - inputs that overflow 64-bit long will be coerced to max or min (if signed) values
408
+ * - if the radix is less than 24, 'NaN' is coerced to Long.ZERO
409
+ * - if the radix is less than 35, '+/-Infinity' inputs are coerced to Long.ZERO
410
+ * - other invalid characters sequences have variable behavior
411
+ * @param str - The textual representation of the Long
412
+ * @param unsigned - Whether unsigned or not, defaults to signed
413
+ * @returns The corresponding Long value
414
+ */
415
+ static fromString(str: string, unsigned?: boolean): Long;
416
+ /**
417
+ * Returns a Long representation of the given string, written using the specified radix.
418
+ *
419
+ * If the input string is empty or a provided radix is not within (2-36), this function will throw a BSONError.
420
+ *
421
+ * If input parameters do not have a valid 64-bit Long representation, this method will return a coerced value:
422
+ * - inputs that overflow 64-bit long will be coerced to max or min (if signed) values
423
+ * - if the radix is less than 24, 'NaN' is coerced to Long.ZERO
424
+ * - if the radix is less than 35, '+/-Infinity' inputs are coerced to Long.ZERO
425
+ * - other invalid characters sequences have variable behavior
426
+ * @param str - The textual representation of the Long
427
+ * @param unsigned - Whether unsigned or not, defaults to signed
428
+ * @param radix - The radix in which the text is written (2-36), defaults to 10
429
+ * @returns The corresponding Long value
430
+ */
431
+ static fromString(str: string, unsigned?: boolean, radix?: number): Long;
432
+ static fromString(str: string, unsignedOrRadix?: boolean | number, radix?: number): Long {
433
+ let unsigned = false;
434
+ if (typeof unsignedOrRadix === 'number') {
435
+ // For goog.math.long compatibility
436
+ (radix = unsignedOrRadix), (unsignedOrRadix = false);
437
+ } else {
438
+ unsigned = !!unsignedOrRadix;
439
+ }
440
+ radix ??= 10;
441
+ if (str === 'NaN' && radix < 24) {
442
+ // radix does not support n, so coerce to zero
443
+ return Long.ZERO;
444
+ } else if ((str === 'Infinity' || str === '+Infinity' || str === '-Infinity') && radix < 35) {
445
+ // radix does not support y, so coerce to zero
446
+ return Long.ZERO;
447
+ }
448
+ return Long._fromString(str, unsigned, radix);
449
+ }
450
+
294
451
  /**
295
452
  * Creates a Long from its byte representation.
296
453
  * @param bytes - Byte representation
@@ -0,0 +1,35 @@
1
+ import { BSONError } from './error';
2
+
3
+ type TextDecoder = {
4
+ readonly encoding: string;
5
+ readonly fatal: boolean;
6
+ readonly ignoreBOM: boolean;
7
+ decode(input?: Uint8Array): string;
8
+ };
9
+ type TextDecoderConstructor = {
10
+ new (label: 'utf8', options: { fatal: boolean; ignoreBOM?: boolean }): TextDecoder;
11
+ };
12
+
13
+ // parse utf8 globals
14
+ declare const TextDecoder: TextDecoderConstructor;
15
+ let TextDecoderFatal: TextDecoder;
16
+ let TextDecoderNonFatal: TextDecoder;
17
+
18
+ /**
19
+ * Determines if the passed in bytes are valid utf8
20
+ * @param bytes - An array of 8-bit bytes. Must be indexable and have length property
21
+ * @param start - The index to start validating
22
+ * @param end - The index to end validating
23
+ */
24
+ export function parseUtf8(buffer: Uint8Array, start: number, end: number, fatal: boolean): string {
25
+ if (fatal) {
26
+ TextDecoderFatal ??= new TextDecoder('utf8', { fatal: true });
27
+ try {
28
+ return TextDecoderFatal.decode(buffer.subarray(start, end));
29
+ } catch (cause) {
30
+ throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
31
+ }
32
+ }
33
+ TextDecoderNonFatal ??= new TextDecoder('utf8', { fatal: false });
34
+ return TextDecoderNonFatal.decode(buffer.subarray(start, end));
35
+ }
@@ -16,7 +16,6 @@ import { BSONSymbol } from '../symbol';
16
16
  import { Timestamp } from '../timestamp';
17
17
  import { ByteUtils } from '../utils/byte_utils';
18
18
  import { NumberUtils } from '../utils/number_utils';
19
- import { validateUtf8 } from '../validate_utf8';
20
19
 
21
20
  /** @public */
22
21
  export interface DeserializeOptions {
@@ -604,12 +603,7 @@ function deserializeObject(
604
603
  )
605
604
  throw new BSONError('bad string length in bson');
606
605
  // Namespace
607
- if (validation != null && validation.utf8) {
608
- if (!validateUtf8(buffer, index, index + stringSize - 1)) {
609
- throw new BSONError('Invalid UTF-8 string in BSON document');
610
- }
611
- }
612
- const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, false);
606
+ const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
613
607
  // Update parse index position
614
608
  index = index + stringSize;
615
609
 
@@ -1,5 +1,5 @@
1
1
  import { BSONError } from '../error';
2
- import { validateUtf8 } from '../validate_utf8';
2
+ import { parseUtf8 } from '../parse_utf8';
3
3
  import { tryReadBasicLatin, tryWriteBasicLatin } from './latin';
4
4
 
5
5
  type NodeJsEncoding = 'base64' | 'hex' | 'utf8' | 'binary';
@@ -136,12 +136,9 @@ export const nodeJsByteUtils = {
136
136
 
137
137
  const string = nodeJsByteUtils.toLocalBufferType(buffer).toString('utf8', start, end);
138
138
  if (fatal) {
139
- // TODO(NODE-4930): Insufficiently strict BSON UTF8 validation
140
139
  for (let i = 0; i < string.length; i++) {
141
140
  if (string.charCodeAt(i) === 0xfffd) {
142
- if (!validateUtf8(buffer, start, end)) {
143
- throw new BSONError('Invalid UTF-8 string in BSON document');
144
- }
141
+ parseUtf8(buffer, start, end, true);
145
142
  break;
146
143
  }
147
144
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @internal
3
+ * Removes leading zeros and explicit plus from textual representation of a number.
4
+ */
5
+ export function removeLeadingZerosAndExplicitPlus(str: string): string {
6
+ if (str === '') {
7
+ return str;
8
+ }
9
+
10
+ let startIndex = 0;
11
+
12
+ const isNegative = str[startIndex] === '-';
13
+ const isExplicitlyPositive = str[startIndex] === '+';
14
+
15
+ if (isExplicitlyPositive || isNegative) {
16
+ startIndex += 1;
17
+ }
18
+
19
+ let foundInsignificantZero = false;
20
+
21
+ for (; startIndex < str.length && str[startIndex] === '0'; ++startIndex) {
22
+ foundInsignificantZero = true;
23
+ }
24
+
25
+ if (!foundInsignificantZero) {
26
+ return isExplicitlyPositive ? str.slice(1) : str;
27
+ }
28
+
29
+ return `${isNegative ? '-' : ''}${str.length === startIndex ? '0' : str.slice(startIndex)}`;
30
+ }
31
+
32
+ /**
33
+ * @internal
34
+ * Returns false for an string that contains invalid characters for its radix, else returns the original string.
35
+ * @param str - The textual representation of the Long
36
+ * @param radix - The radix in which the text is written (2-36), defaults to 10
37
+ */
38
+ export function validateStringCharacters(str: string, radix?: number): false | string {
39
+ radix = radix ?? 10;
40
+ const validCharacters = '0123456789abcdefghijklmnopqrstuvwxyz'.slice(0, radix);
41
+ // regex is case insensitive and checks that each character within the string is one of the validCharacters
42
+ const regex = new RegExp(`[^-+${validCharacters}]`, 'i');
43
+ return regex.test(str) ? false : str;
44
+ }
@@ -1,5 +1,6 @@
1
1
  import { BSONError } from '../error';
2
2
  import { tryReadBasicLatin } from './latin';
3
+ import { parseUtf8 } from '../parse_utf8';
3
4
 
4
5
  type TextDecoder = {
5
6
  readonly encoding: string;
@@ -179,14 +180,7 @@ export const webByteUtils = {
179
180
  return basicLatin;
180
181
  }
181
182
 
182
- if (fatal) {
183
- try {
184
- return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
185
- } catch (cause) {
186
- throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
187
- }
188
- }
189
- return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
183
+ return parseUtf8(uint8array, start, end, fatal);
190
184
  },
191
185
 
192
186
  utf8ByteLength(input: string): number {
@@ -1,47 +0,0 @@
1
- const FIRST_BIT = 0x80;
2
- const FIRST_TWO_BITS = 0xc0;
3
- const FIRST_THREE_BITS = 0xe0;
4
- const FIRST_FOUR_BITS = 0xf0;
5
- const FIRST_FIVE_BITS = 0xf8;
6
-
7
- const TWO_BIT_CHAR = 0xc0;
8
- const THREE_BIT_CHAR = 0xe0;
9
- const FOUR_BIT_CHAR = 0xf0;
10
- const CONTINUING_CHAR = 0x80;
11
-
12
- /**
13
- * Determines if the passed in bytes are valid utf8
14
- * @param bytes - An array of 8-bit bytes. Must be indexable and have length property
15
- * @param start - The index to start validating
16
- * @param end - The index to end validating
17
- */
18
- export function validateUtf8(
19
- bytes: { [index: number]: number },
20
- start: number,
21
- end: number
22
- ): boolean {
23
- let continuation = 0;
24
-
25
- for (let i = start; i < end; i += 1) {
26
- const byte = bytes[i];
27
-
28
- if (continuation) {
29
- if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
30
- return false;
31
- }
32
- continuation -= 1;
33
- } else if (byte & FIRST_BIT) {
34
- if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
35
- continuation = 1;
36
- } else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
37
- continuation = 2;
38
- } else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
39
- continuation = 3;
40
- } else {
41
- return false;
42
- }
43
- }
44
- }
45
-
46
- return !continuation;
47
- }