bson 6.6.0 → 6.8.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/README.md +27 -0
- package/bson.d.ts +148 -7
- package/lib/bson.bundle.js +134 -74
- package/lib/bson.bundle.js.map +1 -1
- package/lib/bson.cjs +134 -74
- package/lib/bson.cjs.map +1 -1
- package/lib/bson.mjs +134 -74
- package/lib/bson.mjs.map +1 -1
- package/lib/bson.rn.cjs +136 -75
- package/lib/bson.rn.cjs.map +1 -1
- package/package.json +14 -15
- package/src/constants.ts +1 -1
- package/src/double.ts +36 -0
- package/src/int_32.ts +34 -0
- package/src/long.ts +211 -31
- package/src/parse_utf8.ts +35 -0
- package/src/parser/deserializer.ts +1 -7
- package/src/utils/node_byte_utils.ts +2 -5
- package/src/utils/string_utils.ts +44 -0
- package/src/utils/web_byte_utils.ts +2 -8
- package/src/validate_utf8.ts +0 -47
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"vendor"
|
|
15
15
|
],
|
|
16
16
|
"types": "bson.d.ts",
|
|
17
|
-
"version": "6.
|
|
17
|
+
"version": "6.8.0",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "The MongoDB NodeJS Team",
|
|
20
20
|
"email": "dbx-node@mongodb.com"
|
|
@@ -27,40 +27,39 @@
|
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
30
|
-
"@microsoft/api-extractor": "^7.
|
|
31
|
-
"@octokit/core": "^5.1.0",
|
|
30
|
+
"@microsoft/api-extractor": "^7.43.1",
|
|
32
31
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
33
32
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
34
|
-
"@types/chai": "^4.3.
|
|
33
|
+
"@types/chai": "^4.3.14",
|
|
35
34
|
"@types/mocha": "^10.0.6",
|
|
36
|
-
"@types/node": "^20.
|
|
35
|
+
"@types/node": "^20.12.7",
|
|
37
36
|
"@types/sinon": "^17.0.3",
|
|
38
37
|
"@types/sinon-chai": "^3.2.12",
|
|
39
|
-
"@typescript-eslint/eslint-plugin": "^7.0
|
|
40
|
-
"@typescript-eslint/parser": "^7.0
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
39
|
+
"@typescript-eslint/parser": "^7.7.0",
|
|
41
40
|
"benchmark": "^2.1.4",
|
|
42
|
-
"chai": "^4.
|
|
41
|
+
"chai": "^4.4.1",
|
|
43
42
|
"chalk": "^5.3.0",
|
|
44
43
|
"dbx-js-tools": "github:mongodb-js/dbx-js-tools",
|
|
45
|
-
"eslint": "^8.
|
|
44
|
+
"eslint": "^8.57.0",
|
|
46
45
|
"eslint-config-prettier": "^9.1.0",
|
|
47
46
|
"eslint-plugin-no-bigint-usage": "file:etc/eslint/no-bigint-usage",
|
|
48
47
|
"eslint-plugin-prettier": "^5.1.3",
|
|
49
48
|
"eslint-plugin-tsdoc": "^0.2.17",
|
|
50
|
-
"magic-string": "^0.30.
|
|
51
|
-
"mocha": "10.
|
|
49
|
+
"magic-string": "^0.30.10",
|
|
50
|
+
"mocha": "^10.4.0",
|
|
52
51
|
"node-fetch": "^3.3.2",
|
|
53
52
|
"nyc": "^15.1.0",
|
|
54
53
|
"prettier": "^3.2.5",
|
|
55
|
-
"rollup": "^4.
|
|
54
|
+
"rollup": "^4.14.3",
|
|
56
55
|
"sinon": "^17.0.1",
|
|
57
56
|
"sinon-chai": "^3.7.0",
|
|
58
57
|
"source-map-support": "^0.5.21",
|
|
59
58
|
"standard-version": "^9.5.0",
|
|
60
|
-
"tar": "^
|
|
59
|
+
"tar": "^7.0.1",
|
|
61
60
|
"ts-node": "^10.9.2",
|
|
62
|
-
"tsd": "^0.
|
|
63
|
-
"typescript": "
|
|
61
|
+
"tsd": "^0.31.0",
|
|
62
|
+
"typescript": "5.3",
|
|
64
63
|
"typescript-cached-transpile": "0.0.6",
|
|
65
64
|
"uuid": "^9.0.1"
|
|
66
65
|
},
|
package/src/constants.ts
CHANGED
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 */
|
|
@@ -118,42 +119,57 @@ export class Long extends BSONValue {
|
|
|
118
119
|
/**
|
|
119
120
|
* The high 32 bits as a signed value.
|
|
120
121
|
*/
|
|
121
|
-
high
|
|
122
|
+
high: number;
|
|
122
123
|
|
|
123
124
|
/**
|
|
124
125
|
* The low 32 bits as a signed value.
|
|
125
126
|
*/
|
|
126
|
-
low
|
|
127
|
+
low: number;
|
|
127
128
|
|
|
128
129
|
/**
|
|
129
130
|
* Whether unsigned or not.
|
|
130
131
|
*/
|
|
131
|
-
unsigned
|
|
132
|
+
unsigned: boolean;
|
|
132
133
|
|
|
133
134
|
/**
|
|
134
135
|
* Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers.
|
|
135
|
-
* See the from* functions below for more convenient ways of constructing Longs.
|
|
136
|
-
*
|
|
137
|
-
* Acceptable signatures are:
|
|
138
|
-
* - Long(low, high, unsigned?)
|
|
139
|
-
* - Long(bigint, unsigned?)
|
|
140
|
-
* - Long(string, unsigned?)
|
|
141
136
|
*
|
|
142
137
|
* @param low - The low (signed) 32 bits of the long
|
|
143
138
|
* @param high - The high (signed) 32 bits of the long
|
|
144
139
|
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
145
140
|
*/
|
|
146
|
-
constructor(low: number
|
|
141
|
+
constructor(low: number, high?: number, unsigned?: boolean);
|
|
142
|
+
/**
|
|
143
|
+
* Constructs a 64 bit two's-complement integer, given a bigint representation.
|
|
144
|
+
*
|
|
145
|
+
* @param value - BigInt representation of the long value
|
|
146
|
+
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
147
|
+
*/
|
|
148
|
+
constructor(value: bigint, unsigned?: boolean);
|
|
149
|
+
/**
|
|
150
|
+
* Constructs a 64 bit two's-complement integer, given a string representation.
|
|
151
|
+
*
|
|
152
|
+
* @param value - String representation of the long value
|
|
153
|
+
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
154
|
+
*/
|
|
155
|
+
constructor(value: string, unsigned?: boolean);
|
|
156
|
+
constructor(
|
|
157
|
+
lowOrValue: number | bigint | string = 0,
|
|
158
|
+
highOrUnsigned?: number | boolean,
|
|
159
|
+
unsigned?: boolean
|
|
160
|
+
) {
|
|
147
161
|
super();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
const unsignedBool = typeof highOrUnsigned === 'boolean' ? highOrUnsigned : Boolean(unsigned);
|
|
163
|
+
const high = typeof highOrUnsigned === 'number' ? highOrUnsigned : 0;
|
|
164
|
+
const res =
|
|
165
|
+
typeof lowOrValue === 'string'
|
|
166
|
+
? Long.fromString(lowOrValue, unsignedBool)
|
|
167
|
+
: typeof lowOrValue === 'bigint'
|
|
168
|
+
? Long.fromBigInt(lowOrValue, unsignedBool)
|
|
169
|
+
: { low: lowOrValue | 0, high: high | 0, unsigned: unsignedBool };
|
|
170
|
+
this.low = res.low;
|
|
171
|
+
this.high = res.high;
|
|
172
|
+
this.unsigned = res.unsigned;
|
|
157
173
|
}
|
|
158
174
|
|
|
159
175
|
static TWO_PWR_24 = Long.fromInt(TWO_PWR_24_DBL);
|
|
@@ -242,33 +258,36 @@ export class Long extends BSONValue {
|
|
|
242
258
|
* @returns The corresponding Long value
|
|
243
259
|
*/
|
|
244
260
|
static fromBigInt(value: bigint, unsigned?: boolean): Long {
|
|
245
|
-
|
|
261
|
+
// eslint-disable-next-line no-restricted-globals
|
|
262
|
+
const FROM_BIGINT_BIT_MASK = BigInt(0xffffffff);
|
|
263
|
+
// eslint-disable-next-line no-restricted-globals
|
|
264
|
+
const FROM_BIGINT_BIT_SHIFT = BigInt(32);
|
|
265
|
+
return new Long(
|
|
266
|
+
Number(value & FROM_BIGINT_BIT_MASK),
|
|
267
|
+
Number((value >> FROM_BIGINT_BIT_SHIFT) & FROM_BIGINT_BIT_MASK),
|
|
268
|
+
unsigned
|
|
269
|
+
);
|
|
246
270
|
}
|
|
247
271
|
|
|
248
272
|
/**
|
|
273
|
+
* @internal
|
|
249
274
|
* Returns a Long representation of the given string, written using the specified radix.
|
|
275
|
+
* Throws an error if `throwsError` is set to true and any of the following conditions are true:
|
|
276
|
+
* - the string contains invalid characters for the given radix
|
|
277
|
+
* - the string contains whitespace
|
|
250
278
|
* @param str - The textual representation of the Long
|
|
251
279
|
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
252
280
|
* @param radix - The radix in which the text is written (2-36), defaults to 10
|
|
253
281
|
* @returns The corresponding Long value
|
|
254
282
|
*/
|
|
255
|
-
static
|
|
283
|
+
private static _fromString(str: string, unsigned: boolean, radix: number): Long {
|
|
256
284
|
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
285
|
if (radix < 2 || 36 < radix) throw new BSONError('radix');
|
|
267
286
|
|
|
268
287
|
let p;
|
|
269
288
|
if ((p = str.indexOf('-')) > 0) throw new BSONError('interior hyphen');
|
|
270
289
|
else if (p === 0) {
|
|
271
|
-
return Long.
|
|
290
|
+
return Long._fromString(str.substring(1), unsigned, radix).neg();
|
|
272
291
|
}
|
|
273
292
|
|
|
274
293
|
// Do several (8) digits each time through the loop, so as to
|
|
@@ -291,6 +310,167 @@ export class Long extends BSONValue {
|
|
|
291
310
|
return result;
|
|
292
311
|
}
|
|
293
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Returns a signed Long representation of the given string, written using radix 10.
|
|
315
|
+
* Will throw an error if the given text is not exactly representable as a Long.
|
|
316
|
+
* Throws an error if any of the following conditions are true:
|
|
317
|
+
* - the string contains invalid characters for the radix 10
|
|
318
|
+
* - the string contains whitespace
|
|
319
|
+
* - the value the string represents is too large or too small to be a Long
|
|
320
|
+
* Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
|
|
321
|
+
* @param str - The textual representation of the Long
|
|
322
|
+
* @returns The corresponding Long value
|
|
323
|
+
*/
|
|
324
|
+
static fromStringStrict(str: string): Long;
|
|
325
|
+
/**
|
|
326
|
+
* Returns a Long representation of the given string, written using the radix 10.
|
|
327
|
+
* Will throw an error if the given parameters are not exactly representable as a Long.
|
|
328
|
+
* Throws an error if any of the following conditions are true:
|
|
329
|
+
* - the string contains invalid characters for the given radix
|
|
330
|
+
* - the string contains whitespace
|
|
331
|
+
* - the value the string represents is too large or too small to be a Long
|
|
332
|
+
* Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
|
|
333
|
+
* @param str - The textual representation of the Long
|
|
334
|
+
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
335
|
+
* @returns The corresponding Long value
|
|
336
|
+
*/
|
|
337
|
+
static fromStringStrict(str: string, unsigned?: boolean): Long;
|
|
338
|
+
/**
|
|
339
|
+
* Returns a signed Long representation of the given string, written using the specified radix.
|
|
340
|
+
* Will throw an error if the given parameters are not exactly representable as a Long.
|
|
341
|
+
* Throws an error if any of the following conditions are true:
|
|
342
|
+
* - the string contains invalid characters for the given radix
|
|
343
|
+
* - the string contains whitespace
|
|
344
|
+
* - the value the string represents is too large or too small to be a Long
|
|
345
|
+
* Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
|
|
346
|
+
* @param str - The textual representation of the Long
|
|
347
|
+
* @param radix - The radix in which the text is written (2-36), defaults to 10
|
|
348
|
+
* @returns The corresponding Long value
|
|
349
|
+
*/
|
|
350
|
+
static fromStringStrict(str: string, radix?: boolean): Long;
|
|
351
|
+
/**
|
|
352
|
+
* Returns a Long representation of the given string, written using the specified radix.
|
|
353
|
+
* Will throw an error if the given parameters are not exactly representable as a Long.
|
|
354
|
+
* Throws an error if any of the following conditions are true:
|
|
355
|
+
* - the string contains invalid characters for the given radix
|
|
356
|
+
* - the string contains whitespace
|
|
357
|
+
* - the value the string represents is too large or too small to be a Long
|
|
358
|
+
* Unlike Long.fromString, this method does not coerce '+/-Infinity' and 'NaN' to Long.Zero
|
|
359
|
+
* @param str - The textual representation of the Long
|
|
360
|
+
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
361
|
+
* @param radix - The radix in which the text is written (2-36), defaults to 10
|
|
362
|
+
* @returns The corresponding Long value
|
|
363
|
+
*/
|
|
364
|
+
static fromStringStrict(str: string, unsigned?: boolean, radix?: number): Long;
|
|
365
|
+
static fromStringStrict(str: string, unsignedOrRadix?: boolean | number, radix?: number): Long {
|
|
366
|
+
let unsigned = false;
|
|
367
|
+
if (typeof unsignedOrRadix === 'number') {
|
|
368
|
+
// For goog.math.long compatibility
|
|
369
|
+
(radix = unsignedOrRadix), (unsignedOrRadix = false);
|
|
370
|
+
} else {
|
|
371
|
+
unsigned = !!unsignedOrRadix;
|
|
372
|
+
}
|
|
373
|
+
radix ??= 10;
|
|
374
|
+
|
|
375
|
+
if (str.trim() !== str) {
|
|
376
|
+
throw new BSONError(`Input: '${str}' contains leading and/or trailing whitespace`);
|
|
377
|
+
}
|
|
378
|
+
if (!StringUtils.validateStringCharacters(str, radix)) {
|
|
379
|
+
throw new BSONError(`Input: '${str}' contains invalid characters for radix: ${radix}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// remove leading zeros (for later string comparison and to make math faster)
|
|
383
|
+
const cleanedStr = StringUtils.removeLeadingZerosAndExplicitPlus(str);
|
|
384
|
+
|
|
385
|
+
// check roundtrip result
|
|
386
|
+
const result = Long._fromString(cleanedStr, unsigned, radix);
|
|
387
|
+
if (result.toString(radix).toLowerCase() !== cleanedStr.toLowerCase()) {
|
|
388
|
+
throw new BSONError(
|
|
389
|
+
`Input: ${str} is not representable as ${result.unsigned ? 'an unsigned' : 'a signed'} 64-bit Long ${radix != null ? `with radix: ${radix}` : ''}`
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Returns a signed Long representation of the given string, written using radix 10.
|
|
397
|
+
*
|
|
398
|
+
* If the input string is empty, this function will throw a BSONError.
|
|
399
|
+
*
|
|
400
|
+
* If input string does not have valid signed 64-bit Long representation, this method will return a coerced value:
|
|
401
|
+
* - inputs that overflow 64-bit signed long will be coerced to Long.MAX_VALUE and Long.MIN_VALUE respectively
|
|
402
|
+
* - 'NaN' or '+/-Infinity' are coerced to Long.ZERO
|
|
403
|
+
* - other invalid characters sequences have variable behavior
|
|
404
|
+
*
|
|
405
|
+
* @param str - The textual representation of the Long
|
|
406
|
+
* @returns The corresponding Long value
|
|
407
|
+
*/
|
|
408
|
+
static fromString(str: string): Long;
|
|
409
|
+
/**
|
|
410
|
+
* Returns a signed Long representation of the given string, written using the provided radix.
|
|
411
|
+
*
|
|
412
|
+
* If the input string is empty or a provided radix is not within (2-36), this function will throw a BSONError.
|
|
413
|
+
*
|
|
414
|
+
* If input parameters do not have valid signed 64-bit Long representation, this method will return a coerced value:
|
|
415
|
+
* - inputs that overflow 64-bit signed long will be coerced to Long.MAX_VALUE and Long.MIN_VALUE respectively
|
|
416
|
+
* - if the radix is less than 24, 'NaN' is coerced to Long.ZERO
|
|
417
|
+
* - if the radix is less than 35, '+/-Infinity' inputs are coerced to Long.ZERO
|
|
418
|
+
* - other invalid characters sequences have variable behavior
|
|
419
|
+
* @param str - The textual representation of the Long
|
|
420
|
+
* @param radix - The radix in which the text is written (2-36), defaults to 10
|
|
421
|
+
* @returns The corresponding Long value
|
|
422
|
+
*/
|
|
423
|
+
static fromString(str: string, radix?: number): Long;
|
|
424
|
+
/**
|
|
425
|
+
* Returns a Long representation of the given string, written using radix 10.
|
|
426
|
+
*
|
|
427
|
+
* If the input string is empty, this function will throw a BSONError.
|
|
428
|
+
*
|
|
429
|
+
* If input parameters do not have a valid 64-bit Long representation, this method will return a coerced value:
|
|
430
|
+
* - inputs that overflow 64-bit long will be coerced to max or min (if signed) values
|
|
431
|
+
* - if the radix is less than 24, 'NaN' is coerced to Long.ZERO
|
|
432
|
+
* - if the radix is less than 35, '+/-Infinity' inputs are coerced to Long.ZERO
|
|
433
|
+
* - other invalid characters sequences have variable behavior
|
|
434
|
+
* @param str - The textual representation of the Long
|
|
435
|
+
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
436
|
+
* @returns The corresponding Long value
|
|
437
|
+
*/
|
|
438
|
+
static fromString(str: string, unsigned?: boolean): Long;
|
|
439
|
+
/**
|
|
440
|
+
* Returns a Long representation of the given string, written using the specified radix.
|
|
441
|
+
*
|
|
442
|
+
* If the input string is empty or a provided radix is not within (2-36), this function will throw a BSONError.
|
|
443
|
+
*
|
|
444
|
+
* If input parameters do not have a valid 64-bit Long representation, this method will return a coerced value:
|
|
445
|
+
* - inputs that overflow 64-bit long will be coerced to max or min (if signed) values
|
|
446
|
+
* - if the radix is less than 24, 'NaN' is coerced to Long.ZERO
|
|
447
|
+
* - if the radix is less than 35, '+/-Infinity' inputs are coerced to Long.ZERO
|
|
448
|
+
* - other invalid characters sequences have variable behavior
|
|
449
|
+
* @param str - The textual representation of the Long
|
|
450
|
+
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
451
|
+
* @param radix - The radix in which the text is written (2-36), defaults to 10
|
|
452
|
+
* @returns The corresponding Long value
|
|
453
|
+
*/
|
|
454
|
+
static fromString(str: string, unsigned?: boolean, radix?: number): Long;
|
|
455
|
+
static fromString(str: string, unsignedOrRadix?: boolean | number, radix?: number): Long {
|
|
456
|
+
let unsigned = false;
|
|
457
|
+
if (typeof unsignedOrRadix === 'number') {
|
|
458
|
+
// For goog.math.long compatibility
|
|
459
|
+
(radix = unsignedOrRadix), (unsignedOrRadix = false);
|
|
460
|
+
} else {
|
|
461
|
+
unsigned = !!unsignedOrRadix;
|
|
462
|
+
}
|
|
463
|
+
radix ??= 10;
|
|
464
|
+
if (str === 'NaN' && radix < 24) {
|
|
465
|
+
// radix does not support n, so coerce to zero
|
|
466
|
+
return Long.ZERO;
|
|
467
|
+
} else if ((str === 'Infinity' || str === '+Infinity' || str === '-Infinity') && radix < 35) {
|
|
468
|
+
// radix does not support y, so coerce to zero
|
|
469
|
+
return Long.ZERO;
|
|
470
|
+
}
|
|
471
|
+
return Long._fromString(str, unsigned, radix);
|
|
472
|
+
}
|
|
473
|
+
|
|
294
474
|
/**
|
|
295
475
|
* Creates a Long from its byte representation.
|
|
296
476
|
* @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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
package/src/validate_utf8.ts
DELETED
|
@@ -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
|
-
}
|