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/lib/bson.cjs CHANGED
@@ -136,41 +136,20 @@ class BSONOffsetError extends BSONError {
136
136
  }
137
137
  }
138
138
 
139
- const FIRST_BIT = 0x80;
140
- const FIRST_TWO_BITS = 0xc0;
141
- const FIRST_THREE_BITS = 0xe0;
142
- const FIRST_FOUR_BITS = 0xf0;
143
- const FIRST_FIVE_BITS = 0xf8;
144
- const TWO_BIT_CHAR = 0xc0;
145
- const THREE_BIT_CHAR = 0xe0;
146
- const FOUR_BIT_CHAR = 0xf0;
147
- const CONTINUING_CHAR = 0x80;
148
- function validateUtf8(bytes, start, end) {
149
- let continuation = 0;
150
- for (let i = start; i < end; i += 1) {
151
- const byte = bytes[i];
152
- if (continuation) {
153
- if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
154
- return false;
155
- }
156
- continuation -= 1;
139
+ let TextDecoderFatal;
140
+ let TextDecoderNonFatal;
141
+ function parseUtf8(buffer, start, end, fatal) {
142
+ if (fatal) {
143
+ TextDecoderFatal ??= new TextDecoder('utf8', { fatal: true });
144
+ try {
145
+ return TextDecoderFatal.decode(buffer.subarray(start, end));
157
146
  }
158
- else if (byte & FIRST_BIT) {
159
- if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
160
- continuation = 1;
161
- }
162
- else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
163
- continuation = 2;
164
- }
165
- else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
166
- continuation = 3;
167
- }
168
- else {
169
- return false;
170
- }
147
+ catch (cause) {
148
+ throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
171
149
  }
172
150
  }
173
- return !continuation;
151
+ TextDecoderNonFatal ??= new TextDecoder('utf8', { fatal: false });
152
+ return TextDecoderNonFatal.decode(buffer.subarray(start, end));
174
153
  }
175
154
 
176
155
  function tryReadBasicLatin(uint8array, start, end) {
@@ -291,9 +270,7 @@ const nodeJsByteUtils = {
291
270
  if (fatal) {
292
271
  for (let i = 0; i < string.length; i++) {
293
272
  if (string.charCodeAt(i) === 0xfffd) {
294
- if (!validateUtf8(buffer, start, end)) {
295
- throw new BSONError('Invalid UTF-8 string in BSON document');
296
- }
273
+ parseUtf8(buffer, start, end, true);
297
274
  break;
298
275
  }
299
276
  }
@@ -417,15 +394,7 @@ const webByteUtils = {
417
394
  if (basicLatin != null) {
418
395
  return basicLatin;
419
396
  }
420
- if (fatal) {
421
- try {
422
- return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
423
- }
424
- catch (cause) {
425
- throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
426
- }
427
- }
428
- return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
397
+ return parseUtf8(uint8array, start, end, fatal);
429
398
  },
430
399
  utf8ByteLength(input) {
431
400
  return new TextEncoder().encode(input).byteLength;
@@ -838,6 +807,32 @@ class DBRef extends BSONValue {
838
807
  }
839
808
  }
840
809
 
810
+ function removeLeadingZerosAndExplicitPlus(str) {
811
+ if (str === '') {
812
+ return str;
813
+ }
814
+ let startIndex = 0;
815
+ const isNegative = str[startIndex] === '-';
816
+ const isExplicitlyPositive = str[startIndex] === '+';
817
+ if (isExplicitlyPositive || isNegative) {
818
+ startIndex += 1;
819
+ }
820
+ let foundInsignificantZero = false;
821
+ for (; startIndex < str.length && str[startIndex] === '0'; ++startIndex) {
822
+ foundInsignificantZero = true;
823
+ }
824
+ if (!foundInsignificantZero) {
825
+ return isExplicitlyPositive ? str.slice(1) : str;
826
+ }
827
+ return `${isNegative ? '-' : ''}${str.length === startIndex ? '0' : str.slice(startIndex)}`;
828
+ }
829
+ function validateStringCharacters(str, radix) {
830
+ radix = radix ?? 10;
831
+ const validCharacters = '0123456789abcdefghijklmnopqrstuvwxyz'.slice(0, radix);
832
+ const regex = new RegExp(`[^-+${validCharacters}]`, 'i');
833
+ return regex.test(str) ? false : str;
834
+ }
835
+
841
836
  let wasm = undefined;
842
837
  try {
843
838
  wasm = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 13, 2, 96, 0, 1, 127, 96, 4, 127, 127, 127, 127, 1, 127, 3, 7, 6, 0, 1, 1, 1, 1, 1, 6, 6, 1, 127, 1, 65, 0, 11, 7, 50, 6, 3, 109, 117, 108, 0, 1, 5, 100, 105, 118, 95, 115, 0, 2, 5, 100, 105, 118, 95, 117, 0, 3, 5, 114, 101, 109, 95, 115, 0, 4, 5, 114, 101, 109, 95, 117, 0, 5, 8, 103, 101, 116, 95, 104, 105, 103, 104, 0, 0, 10, 191, 1, 6, 4, 0, 35, 0, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 126, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 127, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 128, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 129, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 130, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11])), {}).exports;
@@ -926,25 +921,16 @@ class Long extends BSONValue {
926
921
  static fromBigInt(value, unsigned) {
927
922
  return Long.fromString(value.toString(), unsigned);
928
923
  }
929
- static fromString(str, unsigned, radix) {
924
+ static _fromString(str, unsigned, radix) {
930
925
  if (str.length === 0)
931
926
  throw new BSONError('empty string');
932
- if (str === 'NaN' || str === 'Infinity' || str === '+Infinity' || str === '-Infinity')
933
- return Long.ZERO;
934
- if (typeof unsigned === 'number') {
935
- (radix = unsigned), (unsigned = false);
936
- }
937
- else {
938
- unsigned = !!unsigned;
939
- }
940
- radix = radix || 10;
941
927
  if (radix < 2 || 36 < radix)
942
928
  throw new BSONError('radix');
943
929
  let p;
944
930
  if ((p = str.indexOf('-')) > 0)
945
931
  throw new BSONError('interior hyphen');
946
932
  else if (p === 0) {
947
- return Long.fromString(str.substring(1), unsigned, radix).neg();
933
+ return Long._fromString(str.substring(1), unsigned, radix).neg();
948
934
  }
949
935
  const radixToPower = Long.fromNumber(Math.pow(radix, 8));
950
936
  let result = Long.ZERO;
@@ -962,6 +948,45 @@ class Long extends BSONValue {
962
948
  result.unsigned = unsigned;
963
949
  return result;
964
950
  }
951
+ static fromStringStrict(str, unsignedOrRadix, radix) {
952
+ let unsigned = false;
953
+ if (typeof unsignedOrRadix === 'number') {
954
+ (radix = unsignedOrRadix), (unsignedOrRadix = false);
955
+ }
956
+ else {
957
+ unsigned = !!unsignedOrRadix;
958
+ }
959
+ radix ??= 10;
960
+ if (str.trim() !== str) {
961
+ throw new BSONError(`Input: '${str}' contains leading and/or trailing whitespace`);
962
+ }
963
+ if (!validateStringCharacters(str, radix)) {
964
+ throw new BSONError(`Input: '${str}' contains invalid characters for radix: ${radix}`);
965
+ }
966
+ const cleanedStr = removeLeadingZerosAndExplicitPlus(str);
967
+ const result = Long._fromString(cleanedStr, unsigned, radix);
968
+ if (result.toString(radix).toLowerCase() !== cleanedStr.toLowerCase()) {
969
+ throw new BSONError(`Input: ${str} is not representable as ${result.unsigned ? 'an unsigned' : 'a signed'} 64-bit Long ${radix != null ? `with radix: ${radix}` : ''}`);
970
+ }
971
+ return result;
972
+ }
973
+ static fromString(str, unsignedOrRadix, radix) {
974
+ let unsigned = false;
975
+ if (typeof unsignedOrRadix === 'number') {
976
+ (radix = unsignedOrRadix), (unsignedOrRadix = false);
977
+ }
978
+ else {
979
+ unsigned = !!unsignedOrRadix;
980
+ }
981
+ radix ??= 10;
982
+ if (str === 'NaN' && radix < 24) {
983
+ return Long.ZERO;
984
+ }
985
+ else if ((str === 'Infinity' || str === '+Infinity' || str === '-Infinity') && radix < 35) {
986
+ return Long.ZERO;
987
+ }
988
+ return Long._fromString(str, unsigned, radix);
989
+ }
965
990
  static fromBytes(bytes, unsigned, le) {
966
991
  return le ? Long.fromBytesLE(bytes, unsigned) : Long.fromBytesBE(bytes, unsigned);
967
992
  }
@@ -2039,6 +2064,28 @@ class Double extends BSONValue {
2039
2064
  }
2040
2065
  this.value = +value;
2041
2066
  }
2067
+ static fromString(value) {
2068
+ const coercedValue = Number(value);
2069
+ if (value === 'NaN')
2070
+ return new Double(NaN);
2071
+ if (value === 'Infinity')
2072
+ return new Double(Infinity);
2073
+ if (value === '-Infinity')
2074
+ return new Double(-Infinity);
2075
+ if (!Number.isFinite(coercedValue)) {
2076
+ throw new BSONError(`Input: ${value} is not representable as a Double`);
2077
+ }
2078
+ if (value.trim() !== value) {
2079
+ throw new BSONError(`Input: '${value}' contains whitespace`);
2080
+ }
2081
+ if (value === '') {
2082
+ throw new BSONError(`Input is an empty string`);
2083
+ }
2084
+ if (/[^-0-9.+eE]/.test(value)) {
2085
+ throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`);
2086
+ }
2087
+ return new Double(coercedValue);
2088
+ }
2042
2089
  valueOf() {
2043
2090
  return this.value;
2044
2091
  }
@@ -2080,6 +2127,23 @@ class Int32 extends BSONValue {
2080
2127
  }
2081
2128
  this.value = +value | 0;
2082
2129
  }
2130
+ static fromString(value) {
2131
+ const cleanedValue = removeLeadingZerosAndExplicitPlus(value);
2132
+ const coercedValue = Number(value);
2133
+ if (BSON_INT32_MAX < coercedValue) {
2134
+ throw new BSONError(`Input: '${value}' is larger than the maximum value for Int32`);
2135
+ }
2136
+ else if (BSON_INT32_MIN > coercedValue) {
2137
+ throw new BSONError(`Input: '${value}' is smaller than the minimum value for Int32`);
2138
+ }
2139
+ else if (!Number.isSafeInteger(coercedValue)) {
2140
+ throw new BSONError(`Input: '${value}' is not a safe integer`);
2141
+ }
2142
+ else if (coercedValue.toString() !== cleanedValue) {
2143
+ throw new BSONError(`Input: '${value}' is not a valid Int32 string`);
2144
+ }
2145
+ return new Int32(coercedValue);
2146
+ }
2083
2147
  valueOf() {
2084
2148
  return this.value;
2085
2149
  }
@@ -3173,12 +3237,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
3173
3237
  stringSize > buffer.length - index ||
3174
3238
  buffer[index + stringSize - 1] !== 0)
3175
3239
  throw new BSONError('bad string length in bson');
3176
- if (validation != null && validation.utf8) {
3177
- if (!validateUtf8(buffer, index, index + stringSize - 1)) {
3178
- throw new BSONError('Invalid UTF-8 string in BSON document');
3179
- }
3180
- }
3181
- const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, false);
3240
+ const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
3182
3241
  index = index + stringSize;
3183
3242
  const oidBuffer = ByteUtils.allocateUnsafe(12);
3184
3243
  for (let i = 0; i < 12; i++)