bson 5.0.0-alpha.1 → 5.0.0-alpha.3
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 +85 -93
- package/bson.d.ts +50 -22
- package/lib/bson.bundle.js +242 -204
- package/lib/bson.bundle.js.map +1 -1
- package/lib/bson.cjs +242 -204
- package/lib/bson.cjs.map +1 -1
- package/lib/bson.mjs +241 -204
- package/lib/bson.mjs.map +1 -1
- package/package.json +4 -4
- package/src/binary.ts +11 -18
- package/src/bson.ts +2 -1
- package/src/bson_value.ts +18 -0
- package/src/code.ts +3 -6
- package/src/constants.ts +1 -3
- package/src/db_ref.ts +3 -6
- package/src/decimal128.ts +10 -13
- package/src/double.ts +9 -20
- package/src/error.ts +47 -8
- package/src/extended_json.ts +36 -10
- package/src/int_32.ts +3 -6
- package/src/long.ts +37 -14
- package/src/max_key.ts +2 -6
- package/src/min_key.ts +2 -6
- package/src/objectid.ts +9 -14
- package/src/parser/calculate_size.ts +18 -11
- package/src/parser/deserializer.ts +24 -9
- package/src/parser/serializer.ts +53 -34
- package/src/regexp.ts +5 -8
- package/src/symbol.ts +3 -7
- package/src/timestamp.ts +0 -5
- package/src/uuid_utils.ts +2 -2
package/src/long.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BSONValue } from './bson_value';
|
|
2
|
+
import { BSONError } from './error';
|
|
2
3
|
import type { EJSONOptions } from './extended_json';
|
|
3
4
|
import type { Timestamp } from './timestamp';
|
|
4
5
|
|
|
@@ -75,6 +76,10 @@ const INT_CACHE: { [key: number]: Long } = {};
|
|
|
75
76
|
/** A cache of the Long representations of small unsigned integer values. */
|
|
76
77
|
const UINT_CACHE: { [key: number]: Long } = {};
|
|
77
78
|
|
|
79
|
+
const MAX_INT64_STRING_LENGTH = 20;
|
|
80
|
+
|
|
81
|
+
const DECIMAL_REG_EX = /^(\+?0|(\+|-)?[1-9][0-9]*)$/;
|
|
82
|
+
|
|
78
83
|
/** @public */
|
|
79
84
|
export interface LongExtended {
|
|
80
85
|
$numberLong: string;
|
|
@@ -99,14 +104,10 @@ export interface LongExtended {
|
|
|
99
104
|
* case would often result in infinite recursion.
|
|
100
105
|
* Common constant values ZERO, ONE, NEG_ONE, etc. are found as static properties on this class.
|
|
101
106
|
*/
|
|
102
|
-
export class Long {
|
|
107
|
+
export class Long extends BSONValue {
|
|
103
108
|
get _bsontype(): 'Long' {
|
|
104
109
|
return 'Long';
|
|
105
110
|
}
|
|
106
|
-
/** @internal */
|
|
107
|
-
get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
|
|
108
|
-
return BSON_MAJOR_VERSION;
|
|
109
|
-
}
|
|
110
111
|
|
|
111
112
|
/** An indicator used to reliably determine if an object is a Long or not. */
|
|
112
113
|
get __isLong__(): boolean {
|
|
@@ -142,6 +143,7 @@ export class Long {
|
|
|
142
143
|
* @param unsigned - Whether unsigned or not, defaults to signed
|
|
143
144
|
*/
|
|
144
145
|
constructor(low: number | bigint | string = 0, high?: number | boolean, unsigned?: boolean) {
|
|
146
|
+
super();
|
|
145
147
|
if (typeof low === 'bigint') {
|
|
146
148
|
Object.assign(this, Long.fromBigInt(low, !!high));
|
|
147
149
|
} else if (typeof low === 'string') {
|
|
@@ -250,7 +252,7 @@ export class Long {
|
|
|
250
252
|
* @returns The corresponding Long value
|
|
251
253
|
*/
|
|
252
254
|
static fromString(str: string, unsigned?: boolean, radix?: number): Long {
|
|
253
|
-
if (str.length === 0) throw
|
|
255
|
+
if (str.length === 0) throw new BSONError('empty string');
|
|
254
256
|
if (str === 'NaN' || str === 'Infinity' || str === '+Infinity' || str === '-Infinity')
|
|
255
257
|
return Long.ZERO;
|
|
256
258
|
if (typeof unsigned === 'number') {
|
|
@@ -260,10 +262,10 @@ export class Long {
|
|
|
260
262
|
unsigned = !!unsigned;
|
|
261
263
|
}
|
|
262
264
|
radix = radix || 10;
|
|
263
|
-
if (radix < 2 || 36 < radix) throw
|
|
265
|
+
if (radix < 2 || 36 < radix) throw new BSONError('radix');
|
|
264
266
|
|
|
265
267
|
let p;
|
|
266
|
-
if ((p = str.indexOf('-')) > 0) throw
|
|
268
|
+
if ((p = str.indexOf('-')) > 0) throw new BSONError('interior hyphen');
|
|
267
269
|
else if (p === 0) {
|
|
268
270
|
return Long.fromString(str.substring(1), unsigned, radix).neg();
|
|
269
271
|
}
|
|
@@ -431,7 +433,7 @@ export class Long {
|
|
|
431
433
|
*/
|
|
432
434
|
divide(divisor: string | number | Long | Timestamp): Long {
|
|
433
435
|
if (!Long.isLong(divisor)) divisor = Long.fromValue(divisor);
|
|
434
|
-
if (divisor.isZero()) throw
|
|
436
|
+
if (divisor.isZero()) throw new BSONError('division by zero');
|
|
435
437
|
|
|
436
438
|
// use wasm support if present
|
|
437
439
|
if (wasm) {
|
|
@@ -959,7 +961,7 @@ export class Long {
|
|
|
959
961
|
*/
|
|
960
962
|
toString(radix?: number): string {
|
|
961
963
|
radix = radix || 10;
|
|
962
|
-
if (radix < 2 || 36 < radix) throw
|
|
964
|
+
if (radix < 2 || 36 < radix) throw new BSONError('radix');
|
|
963
965
|
if (this.isZero()) return '0';
|
|
964
966
|
if (this.isNegative()) {
|
|
965
967
|
// Unsigned Longs are never negative
|
|
@@ -1025,9 +1027,30 @@ export class Long {
|
|
|
1025
1027
|
if (options && options.relaxed) return this.toNumber();
|
|
1026
1028
|
return { $numberLong: this.toString() };
|
|
1027
1029
|
}
|
|
1028
|
-
static fromExtendedJSON(
|
|
1029
|
-
|
|
1030
|
-
|
|
1030
|
+
static fromExtendedJSON(
|
|
1031
|
+
doc: { $numberLong: string },
|
|
1032
|
+
options?: EJSONOptions
|
|
1033
|
+
): number | Long | bigint {
|
|
1034
|
+
const { useBigInt64 = false, relaxed = true } = { ...options };
|
|
1035
|
+
|
|
1036
|
+
if (doc.$numberLong.length > MAX_INT64_STRING_LENGTH) {
|
|
1037
|
+
throw new BSONError('$numberLong string is too long');
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
if (!DECIMAL_REG_EX.test(doc.$numberLong)) {
|
|
1041
|
+
throw new BSONError(`$numberLong string "${doc.$numberLong}" is in an invalid format`);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (useBigInt64) {
|
|
1045
|
+
const bigIntResult = BigInt(doc.$numberLong);
|
|
1046
|
+
return BigInt.asIntN(64, bigIntResult);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const longResult = Long.fromString(doc.$numberLong);
|
|
1050
|
+
if (relaxed) {
|
|
1051
|
+
return longResult.toNumber();
|
|
1052
|
+
}
|
|
1053
|
+
return longResult;
|
|
1031
1054
|
}
|
|
1032
1055
|
|
|
1033
1056
|
/** @internal */
|
package/src/max_key.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BSONValue } from './bson_value';
|
|
2
2
|
|
|
3
3
|
/** @public */
|
|
4
4
|
export interface MaxKeyExtended {
|
|
@@ -10,14 +10,10 @@ export interface MaxKeyExtended {
|
|
|
10
10
|
* @public
|
|
11
11
|
* @category BSONType
|
|
12
12
|
*/
|
|
13
|
-
export class MaxKey {
|
|
13
|
+
export class MaxKey extends BSONValue {
|
|
14
14
|
get _bsontype(): 'MaxKey' {
|
|
15
15
|
return 'MaxKey';
|
|
16
16
|
}
|
|
17
|
-
/** @internal */
|
|
18
|
-
get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
|
|
19
|
-
return BSON_MAJOR_VERSION;
|
|
20
|
-
}
|
|
21
17
|
|
|
22
18
|
/** @internal */
|
|
23
19
|
toExtendedJSON(): MaxKeyExtended {
|
package/src/min_key.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BSONValue } from './bson_value';
|
|
2
2
|
|
|
3
3
|
/** @public */
|
|
4
4
|
export interface MinKeyExtended {
|
|
@@ -10,14 +10,10 @@ export interface MinKeyExtended {
|
|
|
10
10
|
* @public
|
|
11
11
|
* @category BSONType
|
|
12
12
|
*/
|
|
13
|
-
export class MinKey {
|
|
13
|
+
export class MinKey extends BSONValue {
|
|
14
14
|
get _bsontype(): 'MinKey' {
|
|
15
15
|
return 'MinKey';
|
|
16
16
|
}
|
|
17
|
-
/** @internal */
|
|
18
|
-
get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
|
|
19
|
-
return BSON_MAJOR_VERSION;
|
|
20
|
-
}
|
|
21
17
|
|
|
22
18
|
/** @internal */
|
|
23
19
|
toExtendedJSON(): MinKeyExtended {
|
package/src/objectid.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { BSONValue } from './bson_value';
|
|
2
|
+
import { BSONError } from './error';
|
|
3
3
|
import { isUint8Array } from './parser/utils';
|
|
4
4
|
import { BSONDataView, ByteUtils } from './utils/byte_utils';
|
|
5
5
|
|
|
@@ -28,14 +28,10 @@ const kId = Symbol('id');
|
|
|
28
28
|
* @public
|
|
29
29
|
* @category BSONType
|
|
30
30
|
*/
|
|
31
|
-
export class ObjectId {
|
|
31
|
+
export class ObjectId extends BSONValue {
|
|
32
32
|
get _bsontype(): 'ObjectId' {
|
|
33
33
|
return 'ObjectId';
|
|
34
34
|
}
|
|
35
|
-
/** @internal */
|
|
36
|
-
get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
|
|
37
|
-
return BSON_MAJOR_VERSION;
|
|
38
|
-
}
|
|
39
35
|
|
|
40
36
|
/** @internal */
|
|
41
37
|
private static index = Math.floor(Math.random() * 0xffffff);
|
|
@@ -53,13 +49,12 @@ export class ObjectId {
|
|
|
53
49
|
* @param inputId - Can be a 24 character hex string, 12 byte binary Buffer, or a number.
|
|
54
50
|
*/
|
|
55
51
|
constructor(inputId?: string | number | ObjectId | ObjectIdLike | Uint8Array) {
|
|
52
|
+
super();
|
|
56
53
|
// workingId is set based on type of input and whether valid id exists for the input
|
|
57
54
|
let workingId;
|
|
58
55
|
if (typeof inputId === 'object' && inputId && 'id' in inputId) {
|
|
59
56
|
if (typeof inputId.id !== 'string' && !ArrayBuffer.isView(inputId.id)) {
|
|
60
|
-
throw new
|
|
61
|
-
'Argument passed in must have an id that is of type string or Buffer'
|
|
62
|
-
);
|
|
57
|
+
throw new BSONError('Argument passed in must have an id that is of type string or Buffer');
|
|
63
58
|
}
|
|
64
59
|
if ('toHexString' in inputId && typeof inputId.toHexString === 'function') {
|
|
65
60
|
workingId = ByteUtils.fromHex(inputId.toHexString());
|
|
@@ -85,17 +80,17 @@ export class ObjectId {
|
|
|
85
80
|
if (bytes.byteLength === 12) {
|
|
86
81
|
this[kId] = bytes;
|
|
87
82
|
} else {
|
|
88
|
-
throw new
|
|
83
|
+
throw new BSONError('Argument passed in must be a string of 12 bytes');
|
|
89
84
|
}
|
|
90
85
|
} else if (workingId.length === 24 && checkForHexRegExp.test(workingId)) {
|
|
91
86
|
this[kId] = ByteUtils.fromHex(workingId);
|
|
92
87
|
} else {
|
|
93
|
-
throw new
|
|
88
|
+
throw new BSONError(
|
|
94
89
|
'Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer'
|
|
95
90
|
);
|
|
96
91
|
}
|
|
97
92
|
} else {
|
|
98
|
-
throw new
|
|
93
|
+
throw new BSONError('Argument passed in does not match the accepted types');
|
|
99
94
|
}
|
|
100
95
|
// If we are caching the hex string
|
|
101
96
|
if (ObjectId.cacheHexString) {
|
|
@@ -271,7 +266,7 @@ export class ObjectId {
|
|
|
271
266
|
static createFromHexString(hexString: string): ObjectId {
|
|
272
267
|
// Throw an error if it's not a valid setup
|
|
273
268
|
if (typeof hexString === 'undefined' || (hexString != null && hexString.length !== 24)) {
|
|
274
|
-
throw new
|
|
269
|
+
throw new BSONError(
|
|
275
270
|
'Argument passed in must be a single String of 12 bytes or a string of 24 hex characters'
|
|
276
271
|
);
|
|
277
272
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Binary } from '../binary';
|
|
2
2
|
import type { Document } from '../bson';
|
|
3
|
+
import { BSONVersionError } from '../error';
|
|
3
4
|
import * as constants from '../constants';
|
|
4
5
|
import { ByteUtils } from '../utils/byte_utils';
|
|
5
6
|
import { isAnyArrayBuffer, isDate, isRegExp } from './utils';
|
|
@@ -77,9 +78,15 @@ function calculateElement(
|
|
|
77
78
|
case 'boolean':
|
|
78
79
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1);
|
|
79
80
|
case 'object':
|
|
80
|
-
if (
|
|
81
|
+
if (
|
|
82
|
+
value != null &&
|
|
83
|
+
typeof value._bsontype === 'string' &&
|
|
84
|
+
value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
|
|
85
|
+
) {
|
|
86
|
+
throw new BSONVersionError();
|
|
87
|
+
} else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
81
88
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
|
|
82
|
-
} else if (value
|
|
89
|
+
} else if (value._bsontype === 'ObjectId') {
|
|
83
90
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1);
|
|
84
91
|
} else if (value instanceof Date || isDate(value)) {
|
|
85
92
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
|
|
@@ -92,14 +99,14 @@ function calculateElement(
|
|
|
92
99
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength
|
|
93
100
|
);
|
|
94
101
|
} else if (
|
|
95
|
-
value
|
|
96
|
-
value
|
|
97
|
-
value
|
|
102
|
+
value._bsontype === 'Long' ||
|
|
103
|
+
value._bsontype === 'Double' ||
|
|
104
|
+
value._bsontype === 'Timestamp'
|
|
98
105
|
) {
|
|
99
106
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
|
|
100
|
-
} else if (value
|
|
107
|
+
} else if (value._bsontype === 'Decimal128') {
|
|
101
108
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1);
|
|
102
|
-
} else if (value
|
|
109
|
+
} else if (value._bsontype === 'Code') {
|
|
103
110
|
// Calculate size depending on the availability of a scope
|
|
104
111
|
if (value.scope != null && Object.keys(value.scope).length > 0) {
|
|
105
112
|
return (
|
|
@@ -120,7 +127,7 @@ function calculateElement(
|
|
|
120
127
|
1
|
|
121
128
|
);
|
|
122
129
|
}
|
|
123
|
-
} else if (value
|
|
130
|
+
} else if (value._bsontype === 'Binary') {
|
|
124
131
|
const binary: Binary = value;
|
|
125
132
|
// Check what kind of subtype we have
|
|
126
133
|
if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
|
|
@@ -133,7 +140,7 @@ function calculateElement(
|
|
|
133
140
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1)
|
|
134
141
|
);
|
|
135
142
|
}
|
|
136
|
-
} else if (value
|
|
143
|
+
} else if (value._bsontype === 'Symbol') {
|
|
137
144
|
return (
|
|
138
145
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
|
|
139
146
|
ByteUtils.utf8ByteLength(value.value) +
|
|
@@ -141,7 +148,7 @@ function calculateElement(
|
|
|
141
148
|
1 +
|
|
142
149
|
1
|
|
143
150
|
);
|
|
144
|
-
} else if (value
|
|
151
|
+
} else if (value._bsontype === 'DBRef') {
|
|
145
152
|
// Set up correct object for serialization
|
|
146
153
|
const ordered_values = Object.assign(
|
|
147
154
|
{
|
|
@@ -172,7 +179,7 @@ function calculateElement(
|
|
|
172
179
|
(value.multiline ? 1 : 0) +
|
|
173
180
|
1
|
|
174
181
|
);
|
|
175
|
-
} else if (value
|
|
182
|
+
} else if (value._bsontype === 'BSONRegExp') {
|
|
176
183
|
return (
|
|
177
184
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
|
|
178
185
|
1 +
|
|
@@ -14,12 +14,14 @@ import { ObjectId } from '../objectid';
|
|
|
14
14
|
import { BSONRegExp } from '../regexp';
|
|
15
15
|
import { BSONSymbol } from '../symbol';
|
|
16
16
|
import { Timestamp } from '../timestamp';
|
|
17
|
-
import { ByteUtils } from '../utils/byte_utils';
|
|
17
|
+
import { BSONDataView, ByteUtils } from '../utils/byte_utils';
|
|
18
18
|
import { validateUtf8 } from '../validate_utf8';
|
|
19
19
|
|
|
20
20
|
/** @public */
|
|
21
21
|
export interface DeserializeOptions {
|
|
22
|
-
/** when deserializing a Long will
|
|
22
|
+
/** when deserializing a Long will return as a BigInt. */
|
|
23
|
+
useBigInt64?: boolean;
|
|
24
|
+
/** when deserializing a Long will fit it into a Number if it's smaller than 53 bits. */
|
|
23
25
|
promoteLongs?: boolean;
|
|
24
26
|
/** when deserializing a Binary will return it as a node.js Buffer instance. */
|
|
25
27
|
promoteBuffers?: boolean;
|
|
@@ -29,7 +31,7 @@ export interface DeserializeOptions {
|
|
|
29
31
|
fieldsAsRaw?: Document;
|
|
30
32
|
/** return BSON regular expressions as BSONRegExp instances. */
|
|
31
33
|
bsonRegExp?: boolean;
|
|
32
|
-
/** allows the buffer to be larger than the parsed BSON object */
|
|
34
|
+
/** allows the buffer to be larger than the parsed BSON object. */
|
|
33
35
|
allowObjectSmallerThanBufferSize?: boolean;
|
|
34
36
|
/** Offset into buffer to begin reading document from */
|
|
35
37
|
index?: number;
|
|
@@ -96,7 +98,7 @@ export function internalDeserialize(
|
|
|
96
98
|
);
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
// Start
|
|
101
|
+
// Start deserialization
|
|
100
102
|
return deserializeObject(buffer, index, options, isArray);
|
|
101
103
|
}
|
|
102
104
|
|
|
@@ -117,9 +119,18 @@ function deserializeObject(
|
|
|
117
119
|
const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
|
|
118
120
|
|
|
119
121
|
// Controls the promotion of values vs wrapper classes
|
|
120
|
-
const promoteBuffers = options
|
|
121
|
-
const promoteLongs = options
|
|
122
|
-
const promoteValues = options
|
|
122
|
+
const promoteBuffers = options.promoteBuffers ?? false;
|
|
123
|
+
const promoteLongs = options.promoteLongs ?? true;
|
|
124
|
+
const promoteValues = options.promoteValues ?? true;
|
|
125
|
+
const useBigInt64 = options.useBigInt64 ?? false;
|
|
126
|
+
|
|
127
|
+
if (useBigInt64 && !promoteValues) {
|
|
128
|
+
throw new BSONError('Must either request bigint or Long for int64 deserialization');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (useBigInt64 && !promoteLongs) {
|
|
132
|
+
throw new BSONError('Must either request bigint or Long for int64 deserialization');
|
|
133
|
+
}
|
|
123
134
|
|
|
124
135
|
// Ensures default validation option if none given
|
|
125
136
|
const validation = options.validation == null ? { utf8: true } : options.validation;
|
|
@@ -323,6 +334,8 @@ function deserializeObject(
|
|
|
323
334
|
value = null;
|
|
324
335
|
} else if (elementType === constants.BSON_DATA_LONG) {
|
|
325
336
|
// Unpack the low and high bits
|
|
337
|
+
const dataview = BSONDataView.fromUint8Array(buffer.subarray(index, index + 8));
|
|
338
|
+
|
|
326
339
|
const lowBits =
|
|
327
340
|
buffer[index++] |
|
|
328
341
|
(buffer[index++] << 8) |
|
|
@@ -334,8 +347,10 @@ function deserializeObject(
|
|
|
334
347
|
(buffer[index++] << 16) |
|
|
335
348
|
(buffer[index++] << 24);
|
|
336
349
|
const long = new Long(lowBits, highBits);
|
|
337
|
-
|
|
338
|
-
|
|
350
|
+
if (useBigInt64) {
|
|
351
|
+
value = dataview.getBigInt64(0, true);
|
|
352
|
+
} else if (promoteLongs && promoteValues === true) {
|
|
353
|
+
// Promote the long if possible
|
|
339
354
|
value =
|
|
340
355
|
long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
|
|
341
356
|
? long.toNumber()
|
package/src/parser/serializer.ts
CHANGED
|
@@ -5,22 +5,14 @@ import * as constants from '../constants';
|
|
|
5
5
|
import type { DBRefLike } from '../db_ref';
|
|
6
6
|
import type { Decimal128 } from '../decimal128';
|
|
7
7
|
import type { Double } from '../double';
|
|
8
|
-
import { BSONError,
|
|
8
|
+
import { BSONError, BSONVersionError } from '../error';
|
|
9
9
|
import type { Int32 } from '../int_32';
|
|
10
10
|
import { Long } from '../long';
|
|
11
11
|
import type { MinKey } from '../min_key';
|
|
12
12
|
import type { ObjectId } from '../objectid';
|
|
13
13
|
import type { BSONRegExp } from '../regexp';
|
|
14
14
|
import { ByteUtils } from '../utils/byte_utils';
|
|
15
|
-
import {
|
|
16
|
-
isAnyArrayBuffer,
|
|
17
|
-
isBigInt64Array,
|
|
18
|
-
isBigUInt64Array,
|
|
19
|
-
isDate,
|
|
20
|
-
isMap,
|
|
21
|
-
isRegExp,
|
|
22
|
-
isUint8Array
|
|
23
|
-
} from './utils';
|
|
15
|
+
import { isAnyArrayBuffer, isDate, isMap, isRegExp, isUint8Array } from './utils';
|
|
24
16
|
|
|
25
17
|
/** @public */
|
|
26
18
|
export interface SerializeOptions {
|
|
@@ -103,6 +95,20 @@ function serializeNumber(buffer: Uint8Array, key: string, value: number, index:
|
|
|
103
95
|
return index;
|
|
104
96
|
}
|
|
105
97
|
|
|
98
|
+
function serializeBigInt(buffer: Uint8Array, key: string, value: bigint, index: number) {
|
|
99
|
+
buffer[index++] = constants.BSON_DATA_LONG;
|
|
100
|
+
// Number of written bytes
|
|
101
|
+
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
102
|
+
// Encode the name
|
|
103
|
+
index += numberOfWrittenBytes;
|
|
104
|
+
buffer[index++] = 0;
|
|
105
|
+
NUMBER_SPACE.setBigInt64(0, value, true);
|
|
106
|
+
// Write BigInt value
|
|
107
|
+
buffer.set(EIGHT_BYTE_VIEW_ON_NUMBER, index);
|
|
108
|
+
index += EIGHT_BYTE_VIEW_ON_NUMBER.byteLength;
|
|
109
|
+
return index;
|
|
110
|
+
}
|
|
111
|
+
|
|
106
112
|
function serializeNull(buffer: Uint8Array, key: string, _: unknown, index: number) {
|
|
107
113
|
// Set long type
|
|
108
114
|
buffer[index++] = constants.BSON_DATA_NULL;
|
|
@@ -165,7 +171,7 @@ function serializeRegExp(buffer: Uint8Array, key: string, value: RegExp, index:
|
|
|
165
171
|
index = index + numberOfWrittenBytes;
|
|
166
172
|
buffer[index++] = 0;
|
|
167
173
|
if (value.source && value.source.match(regexp) != null) {
|
|
168
|
-
throw
|
|
174
|
+
throw new BSONError('value ' + value.source + ' must not contain null bytes');
|
|
169
175
|
}
|
|
170
176
|
// Adjust the index
|
|
171
177
|
index = index + ByteUtils.encodeUTF8Into(buffer, value.source, index);
|
|
@@ -194,7 +200,7 @@ function serializeBSONRegExp(buffer: Uint8Array, key: string, value: BSONRegExp,
|
|
|
194
200
|
if (value.pattern.match(regexp) != null) {
|
|
195
201
|
// The BSON spec doesn't allow keys with null bytes because keys are
|
|
196
202
|
// null-terminated.
|
|
197
|
-
throw
|
|
203
|
+
throw new BSONError('pattern ' + value.pattern + ' must not contain null bytes');
|
|
198
204
|
}
|
|
199
205
|
|
|
200
206
|
// Adjust the index
|
|
@@ -241,7 +247,7 @@ function serializeObjectId(buffer: Uint8Array, key: string, value: ObjectId, ind
|
|
|
241
247
|
if (isUint8Array(value.id)) {
|
|
242
248
|
buffer.set(value.id.subarray(0, 12), index);
|
|
243
249
|
} else {
|
|
244
|
-
throw new
|
|
250
|
+
throw new BSONError('object [' + JSON.stringify(value) + '] is not a valid ObjectId');
|
|
245
251
|
}
|
|
246
252
|
|
|
247
253
|
// Adjust index
|
|
@@ -675,7 +681,7 @@ export function serializeInto(
|
|
|
675
681
|
} else if (typeof value === 'number') {
|
|
676
682
|
index = serializeNumber(buffer, key, value, index);
|
|
677
683
|
} else if (typeof value === 'bigint') {
|
|
678
|
-
|
|
684
|
+
index = serializeBigInt(buffer, key, value, index);
|
|
679
685
|
} else if (typeof value === 'boolean') {
|
|
680
686
|
index = serializeBoolean(buffer, key, value, index);
|
|
681
687
|
} else if (value instanceof Date || isDate(value)) {
|
|
@@ -700,8 +706,11 @@ export function serializeInto(
|
|
|
700
706
|
ignoreUndefined,
|
|
701
707
|
path
|
|
702
708
|
);
|
|
703
|
-
} else if (
|
|
704
|
-
|
|
709
|
+
} else if (
|
|
710
|
+
typeof value === 'object' &&
|
|
711
|
+
value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
|
|
712
|
+
) {
|
|
713
|
+
throw new BSONVersionError();
|
|
705
714
|
} else if (value._bsontype === 'ObjectId') {
|
|
706
715
|
index = serializeObjectId(buffer, key, value, index);
|
|
707
716
|
} else if (value._bsontype === 'Decimal128') {
|
|
@@ -737,7 +746,7 @@ export function serializeInto(
|
|
|
737
746
|
} else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
738
747
|
index = serializeMinMax(buffer, key, value, index);
|
|
739
748
|
} else if (typeof value._bsontype !== 'undefined') {
|
|
740
|
-
throw new
|
|
749
|
+
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
741
750
|
}
|
|
742
751
|
}
|
|
743
752
|
} else if (object instanceof Map || isMap(object)) {
|
|
@@ -753,7 +762,11 @@ export function serializeInto(
|
|
|
753
762
|
|
|
754
763
|
// Get the entry values
|
|
755
764
|
const key = entry.value[0];
|
|
756
|
-
|
|
765
|
+
let value = entry.value[1];
|
|
766
|
+
|
|
767
|
+
if (typeof value?.toBSON === 'function') {
|
|
768
|
+
value = value.toBSON();
|
|
769
|
+
}
|
|
757
770
|
|
|
758
771
|
// Check the type of the value
|
|
759
772
|
const type = typeof value;
|
|
@@ -763,14 +776,14 @@ export function serializeInto(
|
|
|
763
776
|
if (key.match(regexp) != null) {
|
|
764
777
|
// The BSON spec doesn't allow keys with null bytes because keys are
|
|
765
778
|
// null-terminated.
|
|
766
|
-
throw
|
|
779
|
+
throw new BSONError('key ' + key + ' must not contain null bytes');
|
|
767
780
|
}
|
|
768
781
|
|
|
769
782
|
if (checkKeys) {
|
|
770
783
|
if ('$' === key[0]) {
|
|
771
|
-
throw
|
|
784
|
+
throw new BSONError('key ' + key + " must not start with '$'");
|
|
772
785
|
} else if (~key.indexOf('.')) {
|
|
773
|
-
throw
|
|
786
|
+
throw new BSONError('key ' + key + " must not contain '.'");
|
|
774
787
|
}
|
|
775
788
|
}
|
|
776
789
|
}
|
|
@@ -779,8 +792,8 @@ export function serializeInto(
|
|
|
779
792
|
index = serializeString(buffer, key, value, index);
|
|
780
793
|
} else if (type === 'number') {
|
|
781
794
|
index = serializeNumber(buffer, key, value, index);
|
|
782
|
-
} else if (type === 'bigint'
|
|
783
|
-
|
|
795
|
+
} else if (type === 'bigint') {
|
|
796
|
+
index = serializeBigInt(buffer, key, value, index);
|
|
784
797
|
} else if (type === 'boolean') {
|
|
785
798
|
index = serializeBoolean(buffer, key, value, index);
|
|
786
799
|
} else if (value instanceof Date || isDate(value)) {
|
|
@@ -803,8 +816,11 @@ export function serializeInto(
|
|
|
803
816
|
ignoreUndefined,
|
|
804
817
|
path
|
|
805
818
|
);
|
|
806
|
-
} else if (
|
|
807
|
-
|
|
819
|
+
} else if (
|
|
820
|
+
typeof value === 'object' &&
|
|
821
|
+
value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
|
|
822
|
+
) {
|
|
823
|
+
throw new BSONVersionError();
|
|
808
824
|
} else if (value._bsontype === 'ObjectId') {
|
|
809
825
|
index = serializeObjectId(buffer, key, value, index);
|
|
810
826
|
} else if (type === 'object' && value._bsontype === 'Decimal128') {
|
|
@@ -840,7 +856,7 @@ export function serializeInto(
|
|
|
840
856
|
} else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
841
857
|
index = serializeMinMax(buffer, key, value, index);
|
|
842
858
|
} else if (typeof value._bsontype !== 'undefined') {
|
|
843
|
-
throw new
|
|
859
|
+
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
844
860
|
}
|
|
845
861
|
}
|
|
846
862
|
} else {
|
|
@@ -848,7 +864,7 @@ export function serializeInto(
|
|
|
848
864
|
// Provided a custom serialization method
|
|
849
865
|
object = object.toBSON();
|
|
850
866
|
if (object != null && typeof object !== 'object') {
|
|
851
|
-
throw new
|
|
867
|
+
throw new BSONError('toBSON function did not return an object');
|
|
852
868
|
}
|
|
853
869
|
}
|
|
854
870
|
|
|
@@ -868,14 +884,14 @@ export function serializeInto(
|
|
|
868
884
|
if (key.match(regexp) != null) {
|
|
869
885
|
// The BSON spec doesn't allow keys with null bytes because keys are
|
|
870
886
|
// null-terminated.
|
|
871
|
-
throw
|
|
887
|
+
throw new BSONError('key ' + key + ' must not contain null bytes');
|
|
872
888
|
}
|
|
873
889
|
|
|
874
890
|
if (checkKeys) {
|
|
875
891
|
if ('$' === key[0]) {
|
|
876
|
-
throw
|
|
892
|
+
throw new BSONError('key ' + key + " must not start with '$'");
|
|
877
893
|
} else if (~key.indexOf('.')) {
|
|
878
|
-
throw
|
|
894
|
+
throw new BSONError('key ' + key + " must not contain '.'");
|
|
879
895
|
}
|
|
880
896
|
}
|
|
881
897
|
}
|
|
@@ -885,7 +901,7 @@ export function serializeInto(
|
|
|
885
901
|
} else if (type === 'number') {
|
|
886
902
|
index = serializeNumber(buffer, key, value, index);
|
|
887
903
|
} else if (type === 'bigint') {
|
|
888
|
-
|
|
904
|
+
index = serializeBigInt(buffer, key, value, index);
|
|
889
905
|
} else if (type === 'boolean') {
|
|
890
906
|
index = serializeBoolean(buffer, key, value, index);
|
|
891
907
|
} else if (value instanceof Date || isDate(value)) {
|
|
@@ -910,8 +926,11 @@ export function serializeInto(
|
|
|
910
926
|
ignoreUndefined,
|
|
911
927
|
path
|
|
912
928
|
);
|
|
913
|
-
} else if (
|
|
914
|
-
|
|
929
|
+
} else if (
|
|
930
|
+
typeof value === 'object' &&
|
|
931
|
+
value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION
|
|
932
|
+
) {
|
|
933
|
+
throw new BSONVersionError();
|
|
915
934
|
} else if (value._bsontype === 'ObjectId') {
|
|
916
935
|
index = serializeObjectId(buffer, key, value, index);
|
|
917
936
|
} else if (type === 'object' && value._bsontype === 'Decimal128') {
|
|
@@ -947,7 +966,7 @@ export function serializeInto(
|
|
|
947
966
|
} else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
948
967
|
index = serializeMinMax(buffer, key, value, index);
|
|
949
968
|
} else if (typeof value._bsontype !== 'undefined') {
|
|
950
|
-
throw new
|
|
969
|
+
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
951
970
|
}
|
|
952
971
|
}
|
|
953
972
|
}
|
package/src/regexp.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BSONError
|
|
1
|
+
import { BSONValue } from './bson_value';
|
|
2
|
+
import { BSONError } from './error';
|
|
3
3
|
import type { EJSONOptions } from './extended_json';
|
|
4
4
|
|
|
5
5
|
function alphabetize(str: string): string {
|
|
@@ -25,14 +25,10 @@ export interface BSONRegExpExtended {
|
|
|
25
25
|
* @public
|
|
26
26
|
* @category BSONType
|
|
27
27
|
*/
|
|
28
|
-
export class BSONRegExp {
|
|
28
|
+
export class BSONRegExp extends BSONValue {
|
|
29
29
|
get _bsontype(): 'BSONRegExp' {
|
|
30
30
|
return 'BSONRegExp';
|
|
31
31
|
}
|
|
32
|
-
/** @internal */
|
|
33
|
-
get [Symbol.for('@@mdb.bson.version')](): BSON_MAJOR_VERSION {
|
|
34
|
-
return BSON_MAJOR_VERSION;
|
|
35
|
-
}
|
|
36
32
|
|
|
37
33
|
pattern!: string;
|
|
38
34
|
options!: string;
|
|
@@ -41,6 +37,7 @@ export class BSONRegExp {
|
|
|
41
37
|
* @param options - The regular expression options
|
|
42
38
|
*/
|
|
43
39
|
constructor(pattern: string, options?: string) {
|
|
40
|
+
super();
|
|
44
41
|
this.pattern = pattern;
|
|
45
42
|
this.options = alphabetize(options ?? '');
|
|
46
43
|
|
|
@@ -103,7 +100,7 @@ export class BSONRegExp {
|
|
|
103
100
|
BSONRegExp.parseOptions(doc.$regularExpression.options)
|
|
104
101
|
);
|
|
105
102
|
}
|
|
106
|
-
throw new
|
|
103
|
+
throw new BSONError(`Unexpected BSONRegExp EJSON object form: ${JSON.stringify(doc)}`);
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
/** @internal */
|