bson 6.5.0 → 6.7.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
@@ -128,47 +128,26 @@ class BSONOffsetError extends BSONError {
128
128
  get name() {
129
129
  return 'BSONOffsetError';
130
130
  }
131
- constructor(message, offset) {
132
- super(`${message}. offset: ${offset}`);
131
+ constructor(message, offset, options) {
132
+ super(`${message}. offset: ${offset}`, options);
133
133
  this.offset = offset;
134
134
  }
135
135
  }
136
136
 
137
- const FIRST_BIT = 0x80;
138
- const FIRST_TWO_BITS = 0xc0;
139
- const FIRST_THREE_BITS = 0xe0;
140
- const FIRST_FOUR_BITS = 0xf0;
141
- const FIRST_FIVE_BITS = 0xf8;
142
- const TWO_BIT_CHAR = 0xc0;
143
- const THREE_BIT_CHAR = 0xe0;
144
- const FOUR_BIT_CHAR = 0xf0;
145
- const CONTINUING_CHAR = 0x80;
146
- function validateUtf8(bytes, start, end) {
147
- let continuation = 0;
148
- for (let i = start; i < end; i += 1) {
149
- const byte = bytes[i];
150
- if (continuation) {
151
- if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
152
- return false;
153
- }
154
- continuation -= 1;
137
+ let TextDecoderFatal;
138
+ let TextDecoderNonFatal;
139
+ function parseUtf8(buffer, start, end, fatal) {
140
+ if (fatal) {
141
+ TextDecoderFatal ??= new TextDecoder('utf8', { fatal: true });
142
+ try {
143
+ return TextDecoderFatal.decode(buffer.subarray(start, end));
155
144
  }
156
- else if (byte & FIRST_BIT) {
157
- if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
158
- continuation = 1;
159
- }
160
- else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
161
- continuation = 2;
162
- }
163
- else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
164
- continuation = 3;
165
- }
166
- else {
167
- return false;
168
- }
145
+ catch (cause) {
146
+ throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
169
147
  }
170
148
  }
171
- return !continuation;
149
+ TextDecoderNonFatal ??= new TextDecoder('utf8', { fatal: false });
150
+ return TextDecoderNonFatal.decode(buffer.subarray(start, end));
172
151
  }
173
152
 
174
153
  function tryReadBasicLatin(uint8array, start, end) {
@@ -289,9 +268,7 @@ const nodeJsByteUtils = {
289
268
  if (fatal) {
290
269
  for (let i = 0; i < string.length; i++) {
291
270
  if (string.charCodeAt(i) === 0xfffd) {
292
- if (!validateUtf8(buffer, start, end)) {
293
- throw new BSONError('Invalid UTF-8 string in BSON document');
294
- }
271
+ parseUtf8(buffer, start, end, true);
295
272
  break;
296
273
  }
297
274
  }
@@ -415,15 +392,7 @@ const webByteUtils = {
415
392
  if (basicLatin != null) {
416
393
  return basicLatin;
417
394
  }
418
- if (fatal) {
419
- try {
420
- return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
421
- }
422
- catch (cause) {
423
- throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
424
- }
425
- }
426
- return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
395
+ return parseUtf8(uint8array, start, end, fatal);
427
396
  },
428
397
  utf8ByteLength(input) {
429
398
  return new TextEncoder().encode(input).byteLength;
@@ -531,16 +500,16 @@ class Binary extends BSONValue {
531
500
  return this.position;
532
501
  }
533
502
  toJSON() {
534
- return ByteUtils.toBase64(this.buffer);
503
+ return ByteUtils.toBase64(this.buffer.subarray(0, this.position));
535
504
  }
536
505
  toString(encoding) {
537
506
  if (encoding === 'hex')
538
- return ByteUtils.toHex(this.buffer);
507
+ return ByteUtils.toHex(this.buffer.subarray(0, this.position));
539
508
  if (encoding === 'base64')
540
- return ByteUtils.toBase64(this.buffer);
509
+ return ByteUtils.toBase64(this.buffer.subarray(0, this.position));
541
510
  if (encoding === 'utf8' || encoding === 'utf-8')
542
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
543
- return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
511
+ return ByteUtils.toUTF8(this.buffer, 0, this.position, false);
512
+ return ByteUtils.toUTF8(this.buffer, 0, this.position, false);
544
513
  }
545
514
  toExtendedJSON(options) {
546
515
  options = options || {};
@@ -836,6 +805,32 @@ class DBRef extends BSONValue {
836
805
  }
837
806
  }
838
807
 
808
+ function removeLeadingZerosAndExplicitPlus(str) {
809
+ if (str === '') {
810
+ return str;
811
+ }
812
+ let startIndex = 0;
813
+ const isNegative = str[startIndex] === '-';
814
+ const isExplicitlyPositive = str[startIndex] === '+';
815
+ if (isExplicitlyPositive || isNegative) {
816
+ startIndex += 1;
817
+ }
818
+ let foundInsignificantZero = false;
819
+ for (; startIndex < str.length && str[startIndex] === '0'; ++startIndex) {
820
+ foundInsignificantZero = true;
821
+ }
822
+ if (!foundInsignificantZero) {
823
+ return isExplicitlyPositive ? str.slice(1) : str;
824
+ }
825
+ return `${isNegative ? '-' : ''}${str.length === startIndex ? '0' : str.slice(startIndex)}`;
826
+ }
827
+ function validateStringCharacters(str, radix) {
828
+ radix = radix ?? 10;
829
+ const validCharacters = '0123456789abcdefghijklmnopqrstuvwxyz'.slice(0, radix);
830
+ const regex = new RegExp(`[^-+${validCharacters}]`, 'i');
831
+ return regex.test(str) ? false : str;
832
+ }
833
+
839
834
  let wasm = undefined;
840
835
  try {
841
836
  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;
@@ -924,25 +919,16 @@ class Long extends BSONValue {
924
919
  static fromBigInt(value, unsigned) {
925
920
  return Long.fromString(value.toString(), unsigned);
926
921
  }
927
- static fromString(str, unsigned, radix) {
922
+ static _fromString(str, unsigned, radix) {
928
923
  if (str.length === 0)
929
924
  throw new BSONError('empty string');
930
- if (str === 'NaN' || str === 'Infinity' || str === '+Infinity' || str === '-Infinity')
931
- return Long.ZERO;
932
- if (typeof unsigned === 'number') {
933
- (radix = unsigned), (unsigned = false);
934
- }
935
- else {
936
- unsigned = !!unsigned;
937
- }
938
- radix = radix || 10;
939
925
  if (radix < 2 || 36 < radix)
940
926
  throw new BSONError('radix');
941
927
  let p;
942
928
  if ((p = str.indexOf('-')) > 0)
943
929
  throw new BSONError('interior hyphen');
944
930
  else if (p === 0) {
945
- return Long.fromString(str.substring(1), unsigned, radix).neg();
931
+ return Long._fromString(str.substring(1), unsigned, radix).neg();
946
932
  }
947
933
  const radixToPower = Long.fromNumber(Math.pow(radix, 8));
948
934
  let result = Long.ZERO;
@@ -960,6 +946,45 @@ class Long extends BSONValue {
960
946
  result.unsigned = unsigned;
961
947
  return result;
962
948
  }
949
+ static fromStringStrict(str, unsignedOrRadix, radix) {
950
+ let unsigned = false;
951
+ if (typeof unsignedOrRadix === 'number') {
952
+ (radix = unsignedOrRadix), (unsignedOrRadix = false);
953
+ }
954
+ else {
955
+ unsigned = !!unsignedOrRadix;
956
+ }
957
+ radix ??= 10;
958
+ if (str.trim() !== str) {
959
+ throw new BSONError(`Input: '${str}' contains leading and/or trailing whitespace`);
960
+ }
961
+ if (!validateStringCharacters(str, radix)) {
962
+ throw new BSONError(`Input: '${str}' contains invalid characters for radix: ${radix}`);
963
+ }
964
+ const cleanedStr = removeLeadingZerosAndExplicitPlus(str);
965
+ const result = Long._fromString(cleanedStr, unsigned, radix);
966
+ if (result.toString(radix).toLowerCase() !== cleanedStr.toLowerCase()) {
967
+ throw new BSONError(`Input: ${str} is not representable as ${result.unsigned ? 'an unsigned' : 'a signed'} 64-bit Long ${radix != null ? `with radix: ${radix}` : ''}`);
968
+ }
969
+ return result;
970
+ }
971
+ static fromString(str, unsignedOrRadix, radix) {
972
+ let unsigned = false;
973
+ if (typeof unsignedOrRadix === 'number') {
974
+ (radix = unsignedOrRadix), (unsignedOrRadix = false);
975
+ }
976
+ else {
977
+ unsigned = !!unsignedOrRadix;
978
+ }
979
+ radix ??= 10;
980
+ if (str === 'NaN' && radix < 24) {
981
+ return Long.ZERO;
982
+ }
983
+ else if ((str === 'Infinity' || str === '+Infinity' || str === '-Infinity') && radix < 35) {
984
+ return Long.ZERO;
985
+ }
986
+ return Long._fromString(str, unsigned, radix);
987
+ }
963
988
  static fromBytes(bytes, unsigned, le) {
964
989
  return le ? Long.fromBytesLE(bytes, unsigned) : Long.fromBytesBE(bytes, unsigned);
965
990
  }
@@ -2037,6 +2062,28 @@ class Double extends BSONValue {
2037
2062
  }
2038
2063
  this.value = +value;
2039
2064
  }
2065
+ static fromString(value) {
2066
+ const coercedValue = Number(value);
2067
+ if (value === 'NaN')
2068
+ return new Double(NaN);
2069
+ if (value === 'Infinity')
2070
+ return new Double(Infinity);
2071
+ if (value === '-Infinity')
2072
+ return new Double(-Infinity);
2073
+ if (!Number.isFinite(coercedValue)) {
2074
+ throw new BSONError(`Input: ${value} is not representable as a Double`);
2075
+ }
2076
+ if (value.trim() !== value) {
2077
+ throw new BSONError(`Input: '${value}' contains whitespace`);
2078
+ }
2079
+ if (value === '') {
2080
+ throw new BSONError(`Input is an empty string`);
2081
+ }
2082
+ if (/[^-0-9.+eE]/.test(value)) {
2083
+ throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`);
2084
+ }
2085
+ return new Double(coercedValue);
2086
+ }
2040
2087
  valueOf() {
2041
2088
  return this.value;
2042
2089
  }
@@ -2078,6 +2125,23 @@ class Int32 extends BSONValue {
2078
2125
  }
2079
2126
  this.value = +value | 0;
2080
2127
  }
2128
+ static fromString(value) {
2129
+ const cleanedValue = removeLeadingZerosAndExplicitPlus(value);
2130
+ const coercedValue = Number(value);
2131
+ if (BSON_INT32_MAX < coercedValue) {
2132
+ throw new BSONError(`Input: '${value}' is larger than the maximum value for Int32`);
2133
+ }
2134
+ else if (BSON_INT32_MIN > coercedValue) {
2135
+ throw new BSONError(`Input: '${value}' is smaller than the minimum value for Int32`);
2136
+ }
2137
+ else if (!Number.isSafeInteger(coercedValue)) {
2138
+ throw new BSONError(`Input: '${value}' is not a safe integer`);
2139
+ }
2140
+ else if (coercedValue.toString() !== cleanedValue) {
2141
+ throw new BSONError(`Input: '${value}' is not a valid Int32 string`);
2142
+ }
2143
+ return new Int32(coercedValue);
2144
+ }
2081
2145
  valueOf() {
2082
2146
  return this.value;
2083
2147
  }
@@ -2136,6 +2200,15 @@ const FLOAT_BYTES = new Uint8Array(FLOAT.buffer, 0, 8);
2136
2200
  FLOAT[0] = -1;
2137
2201
  const isBigEndian = FLOAT_BYTES[7] === 0;
2138
2202
  const NumberUtils = {
2203
+ getNonnegativeInt32LE(source, offset) {
2204
+ if (source[offset + 3] > 127) {
2205
+ throw new RangeError(`Size cannot be negative at offset: ${offset}`);
2206
+ }
2207
+ return (source[offset] |
2208
+ (source[offset + 1] << 8) |
2209
+ (source[offset + 2] << 16) |
2210
+ (source[offset + 3] << 24));
2211
+ },
2139
2212
  getInt32LE(source, offset) {
2140
2213
  return (source[offset] |
2141
2214
  (source[offset + 1] << 8) |
@@ -3156,12 +3229,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
3156
3229
  stringSize > buffer.length - index ||
3157
3230
  buffer[index + stringSize - 1] !== 0)
3158
3231
  throw new BSONError('bad string length in bson');
3159
- if (validation != null && validation.utf8) {
3160
- if (!validateUtf8(buffer, index, index + stringSize - 1)) {
3161
- throw new BSONError('Invalid UTF-8 string in BSON document');
3162
- }
3163
- }
3164
- const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, false);
3232
+ const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
3165
3233
  index = index + stringSize;
3166
3234
  const oidBuffer = ByteUtils.allocateUnsafe(12);
3167
3235
  for (let i = 0; i < 12; i++)
@@ -4125,13 +4193,12 @@ EJSON.deserialize = EJSONdeserialize;
4125
4193
  Object.freeze(EJSON);
4126
4194
 
4127
4195
  function getSize(source, offset) {
4128
- if (source[offset + 3] > 127) {
4129
- throw new BSONOffsetError('BSON size cannot be negative', offset);
4196
+ try {
4197
+ return NumberUtils.getNonnegativeInt32LE(source, offset);
4198
+ }
4199
+ catch (cause) {
4200
+ throw new BSONOffsetError('BSON size cannot be negative', offset, { cause });
4130
4201
  }
4131
- return (source[offset] |
4132
- (source[offset + 1] << 8) |
4133
- (source[offset + 2] << 16) |
4134
- (source[offset + 3] << 24));
4135
4202
  }
4136
4203
  function findNull(bytes, offset) {
4137
4204
  let nullTerminatorOffset = offset;
@@ -4143,6 +4210,7 @@ function findNull(bytes, offset) {
4143
4210
  return nullTerminatorOffset;
4144
4211
  }
4145
4212
  function parseToElements(bytes, startOffset = 0) {
4213
+ startOffset ??= 0;
4146
4214
  if (bytes.length < 5) {
4147
4215
  throw new BSONOffsetError(`Input must be at least 5 bytes, got ${bytes.length} bytes`, startOffset);
4148
4216
  }
@@ -4168,7 +4236,10 @@ function parseToElements(bytes, startOffset = 0) {
4168
4236
  const nameLength = findNull(bytes, offset) - nameOffset;
4169
4237
  offset += nameLength + 1;
4170
4238
  let length;
4171
- if (type === 1 || type === 18 || type === 9 || type === 17) {
4239
+ if (type === 1 ||
4240
+ type === 18 ||
4241
+ type === 9 ||
4242
+ type === 17) {
4172
4243
  length = 8;
4173
4244
  }
4174
4245
  else if (type === 16) {
@@ -4183,13 +4254,18 @@ function parseToElements(bytes, startOffset = 0) {
4183
4254
  else if (type === 8) {
4184
4255
  length = 1;
4185
4256
  }
4186
- else if (type === 10 || type === 6 || type === 127 || type === 255) {
4257
+ else if (type === 10 ||
4258
+ type === 6 ||
4259
+ type === 127 ||
4260
+ type === 255) {
4187
4261
  length = 0;
4188
4262
  }
4189
4263
  else if (type === 11) {
4190
4264
  length = findNull(bytes, findNull(bytes, offset) + 1) + 1 - offset;
4191
4265
  }
4192
- else if (type === 3 || type === 4 || type === 15) {
4266
+ else if (type === 3 ||
4267
+ type === 4 ||
4268
+ type === 15) {
4193
4269
  length = getSize(bytes, offset);
4194
4270
  }
4195
4271
  else if (type === 2 ||
@@ -4219,7 +4295,8 @@ function parseToElements(bytes, startOffset = 0) {
4219
4295
 
4220
4296
  const onDemand = Object.create(null);
4221
4297
  onDemand.parseToElements = parseToElements;
4222
- onDemand.BSONOffsetError = BSONOffsetError;
4298
+ onDemand.ByteUtils = ByteUtils;
4299
+ onDemand.NumberUtils = NumberUtils;
4223
4300
  Object.freeze(onDemand);
4224
4301
 
4225
4302
  const MAXSIZE = 1024 * 1024 * 17;
@@ -4276,6 +4353,7 @@ function deserializeStream(data, startIndex, numberOfDocuments, documents, docSt
4276
4353
  var bson = /*#__PURE__*/Object.freeze({
4277
4354
  __proto__: null,
4278
4355
  BSONError: BSONError,
4356
+ BSONOffsetError: BSONOffsetError,
4279
4357
  BSONRegExp: BSONRegExp,
4280
4358
  BSONRuntimeError: BSONRuntimeError,
4281
4359
  BSONSymbol: BSONSymbol,
@@ -4304,5 +4382,5 @@ var bson = /*#__PURE__*/Object.freeze({
4304
4382
  setInternalBufferSize: setInternalBufferSize
4305
4383
  });
4306
4384
 
4307
- export { bson as BSON, BSONError, BSONRegExp, BSONRuntimeError, BSONSymbol, BSONType, BSONValue, BSONVersionError, Binary, Code, DBRef, Decimal128, Double, EJSON, Int32, Long, MaxKey, MinKey, ObjectId, Timestamp, UUID, calculateObjectSize, deserialize, deserializeStream, onDemand, serialize, serializeWithBufferAndIndex, setInternalBufferSize };
4385
+ export { bson as BSON, BSONError, BSONOffsetError, BSONRegExp, BSONRuntimeError, BSONSymbol, BSONType, BSONValue, BSONVersionError, Binary, Code, DBRef, Decimal128, Double, EJSON, Int32, Long, MaxKey, MinKey, ObjectId, Timestamp, UUID, calculateObjectSize, deserialize, deserializeStream, onDemand, serialize, serializeWithBufferAndIndex, setInternalBufferSize };
4308
4386
  //# sourceMappingURL=bson.mjs.map