bson 6.8.0 → 6.10.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/bson.d.ts +119 -2
- package/lib/bson.bundle.js +542 -363
- package/lib/bson.bundle.js.map +1 -1
- package/lib/bson.cjs +542 -363
- package/lib/bson.cjs.map +1 -1
- package/lib/bson.mjs +542 -363
- package/lib/bson.mjs.map +1 -1
- package/lib/bson.rn.cjs +546 -365
- package/lib/bson.rn.cjs.map +1 -1
- package/package.json +21 -20
- package/src/binary.ts +265 -9
- package/src/bson_value.ts +2 -1
- package/src/constants.ts +3 -0
- package/src/decimal128.ts +1 -1
- package/src/extended_json.ts +3 -2
- package/src/long.ts +0 -1
- package/src/objectid.ts +25 -4
- package/src/parser/calculate_size.ts +1 -1
- package/src/parser/deserializer.ts +17 -45
- package/src/parser/serializer.ts +201 -189
- package/src/parser/utils.ts +43 -9
- package/src/timestamp.ts +19 -3
- package/src/utils/byte_utils.ts +2 -0
- package/src/utils/node_byte_utils.ts +7 -2
- package/src/utils/number_utils.ts +4 -0
- package/src/utils/web_byte_utils.ts +21 -2
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.10.0",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "The MongoDB NodeJS Team",
|
|
20
20
|
"email": "dbx-node@mongodb.com"
|
|
@@ -27,41 +27,41 @@
|
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
30
|
-
"@microsoft/api-extractor": "^7.
|
|
30
|
+
"@microsoft/api-extractor": "^7.47.5",
|
|
31
31
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
32
32
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
33
|
-
"@types/chai": "^4.3.
|
|
34
|
-
"@types/mocha": "^10.0.
|
|
35
|
-
"@types/node": "^
|
|
33
|
+
"@types/chai": "^4.3.17",
|
|
34
|
+
"@types/mocha": "^10.0.7",
|
|
35
|
+
"@types/node": "^22.1.0",
|
|
36
36
|
"@types/sinon": "^17.0.3",
|
|
37
37
|
"@types/sinon-chai": "^3.2.12",
|
|
38
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
39
|
-
"@typescript-eslint/parser": "^
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
|
39
|
+
"@typescript-eslint/parser": "^8.0.1",
|
|
40
40
|
"benchmark": "^2.1.4",
|
|
41
41
|
"chai": "^4.4.1",
|
|
42
42
|
"chalk": "^5.3.0",
|
|
43
43
|
"dbx-js-tools": "github:mongodb-js/dbx-js-tools",
|
|
44
|
-
"eslint": "^8.
|
|
44
|
+
"eslint": "^9.8.0",
|
|
45
45
|
"eslint-config-prettier": "^9.1.0",
|
|
46
46
|
"eslint-plugin-no-bigint-usage": "file:etc/eslint/no-bigint-usage",
|
|
47
|
-
"eslint-plugin-prettier": "^5.1
|
|
48
|
-
"eslint-plugin-tsdoc": "^0.
|
|
49
|
-
"magic-string": "^0.30.
|
|
50
|
-
"mocha": "^10.
|
|
47
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
48
|
+
"eslint-plugin-tsdoc": "^0.3.0",
|
|
49
|
+
"magic-string": "^0.30.11",
|
|
50
|
+
"mocha": "^10.7.0",
|
|
51
51
|
"node-fetch": "^3.3.2",
|
|
52
52
|
"nyc": "^15.1.0",
|
|
53
|
-
"prettier": "^3.
|
|
54
|
-
"rollup": "^4.
|
|
55
|
-
"sinon": "^
|
|
53
|
+
"prettier": "^3.3.3",
|
|
54
|
+
"rollup": "^4.20.0",
|
|
55
|
+
"sinon": "^18.0.0",
|
|
56
56
|
"sinon-chai": "^3.7.0",
|
|
57
57
|
"source-map-support": "^0.5.21",
|
|
58
58
|
"standard-version": "^9.5.0",
|
|
59
|
-
"tar": "^7.
|
|
59
|
+
"tar": "^7.4.3",
|
|
60
60
|
"ts-node": "^10.9.2",
|
|
61
|
-
"tsd": "^0.31.
|
|
62
|
-
"typescript": "5.
|
|
61
|
+
"tsd": "^0.31.1",
|
|
62
|
+
"typescript": "^5.5.4",
|
|
63
63
|
"typescript-cached-transpile": "0.0.6",
|
|
64
|
-
"uuid": "^
|
|
64
|
+
"uuid": "^10.0.0"
|
|
65
65
|
},
|
|
66
66
|
"tsd": {
|
|
67
67
|
"directory": "test/types",
|
|
@@ -105,12 +105,13 @@
|
|
|
105
105
|
"check:web-no-bigint": "WEB=true NO_BIGINT=true mocha test/node",
|
|
106
106
|
"check:granular-bench": "npm run build:bench && node ./test/bench/etc/run_granular_benchmarks.js",
|
|
107
107
|
"check:spec-bench": "npm run build:bench && node ./test/bench/lib/spec/bsonBench.js",
|
|
108
|
+
"check:custom-bench": "npm run build && node ./test/bench/custom/main.mjs",
|
|
108
109
|
"build:bench": "cd test/bench && npx tsc",
|
|
109
110
|
"build:ts": "node ./node_modules/typescript/bin/tsc",
|
|
110
111
|
"build:dts": "npm run build:ts && api-extractor run --typescript-compiler-folder node_modules/typescript --local && node etc/clean_definition_files.cjs",
|
|
111
112
|
"build:bundle": "rollup -c rollup.config.mjs",
|
|
112
113
|
"build": "npm run build:dts && npm run build:bundle",
|
|
113
|
-
"check:lint": "eslint -v && eslint --ext '.js,.ts' --max-warnings=0 src test && npm run build:dts && npm run check:tsd",
|
|
114
|
+
"check:lint": "ESLINT_USE_FLAT_CONFIG=false eslint -v && ESLINT_USE_FLAT_CONFIG=false eslint --ext '.js,.ts' --max-warnings=0 src test && npm run build:dts && npm run check:tsd",
|
|
114
115
|
"format": "eslint --ext '.js,.ts' src test --fix",
|
|
115
116
|
"check:coverage": "nyc --check-coverage npm run check:node",
|
|
116
117
|
"prepare": "node etc/prepare.js",
|
package/src/binary.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { BSONError } from './error';
|
|
|
4
4
|
import { BSON_BINARY_SUBTYPE_UUID_NEW } from './constants';
|
|
5
5
|
import { ByteUtils } from './utils/byte_utils';
|
|
6
6
|
import { BSONValue } from './bson_value';
|
|
7
|
+
import { NumberUtils } from './utils/number_utils';
|
|
7
8
|
|
|
8
9
|
/** @public */
|
|
9
10
|
export type BinarySequence = Uint8Array | number[];
|
|
@@ -58,12 +59,60 @@ export class Binary extends BSONValue {
|
|
|
58
59
|
static readonly SUBTYPE_COLUMN = 7;
|
|
59
60
|
/** Sensitive BSON type */
|
|
60
61
|
static readonly SUBTYPE_SENSITIVE = 8;
|
|
62
|
+
/** Vector BSON type */
|
|
63
|
+
static readonly SUBTYPE_VECTOR = 9;
|
|
61
64
|
/** User BSON type */
|
|
62
65
|
static readonly SUBTYPE_USER_DEFINED = 128;
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
/** datatype of a Binary Vector (subtype: 9) */
|
|
68
|
+
static readonly VECTOR_TYPE = Object.freeze({
|
|
69
|
+
Int8: 0x03,
|
|
70
|
+
Float32: 0x27,
|
|
71
|
+
PackedBit: 0x10
|
|
72
|
+
} as const);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The bytes of the Binary value.
|
|
76
|
+
*
|
|
77
|
+
* The format of a Binary value in BSON is defined as:
|
|
78
|
+
* ```txt
|
|
79
|
+
* binary ::= int32 subtype (byte*)
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* This `buffer` is the "(byte*)" segment.
|
|
83
|
+
*
|
|
84
|
+
* Unless the value is subtype 2, then deserialize will read the first 4 bytes as an int32 and set this to the remaining bytes.
|
|
85
|
+
*
|
|
86
|
+
* ```txt
|
|
87
|
+
* binary ::= int32 unsigned_byte(2) int32 (byte*)
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @see https://bsonspec.org/spec.html
|
|
91
|
+
*/
|
|
92
|
+
public buffer: Uint8Array;
|
|
93
|
+
/**
|
|
94
|
+
* The binary subtype.
|
|
95
|
+
*
|
|
96
|
+
* Current defined values are:
|
|
97
|
+
*
|
|
98
|
+
* - `unsigned_byte(0)` Generic binary subtype
|
|
99
|
+
* - `unsigned_byte(1)` Function
|
|
100
|
+
* - `unsigned_byte(2)` Binary (Deprecated)
|
|
101
|
+
* - `unsigned_byte(3)` UUID (Deprecated)
|
|
102
|
+
* - `unsigned_byte(4)` UUID
|
|
103
|
+
* - `unsigned_byte(5)` MD5
|
|
104
|
+
* - `unsigned_byte(6)` Encrypted BSON value
|
|
105
|
+
* - `unsigned_byte(7)` Compressed BSON column
|
|
106
|
+
* - `unsigned_byte(8)` Sensitive
|
|
107
|
+
* - `unsigned_byte(9)` Vector
|
|
108
|
+
* - `unsigned_byte(128)` - `unsigned_byte(255)` User defined
|
|
109
|
+
*/
|
|
110
|
+
public sub_type: number;
|
|
111
|
+
/**
|
|
112
|
+
* The Binary's `buffer` can be larger than the Binary's content.
|
|
113
|
+
* This property is used to determine where the content ends in the buffer.
|
|
114
|
+
*/
|
|
115
|
+
public position: number;
|
|
67
116
|
|
|
68
117
|
/**
|
|
69
118
|
* Create a new Binary instance.
|
|
@@ -160,16 +209,15 @@ export class Binary extends BSONValue {
|
|
|
160
209
|
}
|
|
161
210
|
|
|
162
211
|
/**
|
|
163
|
-
*
|
|
212
|
+
* Returns a view of **length** bytes starting at **position**.
|
|
164
213
|
*
|
|
165
214
|
* @param position - read from the given position in the Binary.
|
|
166
215
|
* @param length - the number of bytes to read.
|
|
167
216
|
*/
|
|
168
|
-
read(position: number, length: number):
|
|
217
|
+
read(position: number, length: number): Uint8Array {
|
|
169
218
|
length = length && length > 0 ? length : this.position;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return this.buffer.slice(position, position + length);
|
|
219
|
+
const end = position + length;
|
|
220
|
+
return this.buffer.subarray(position, end > this.position ? this.position : end);
|
|
173
221
|
}
|
|
174
222
|
|
|
175
223
|
/** returns a view of the binary value as a Uint8Array */
|
|
@@ -200,6 +248,11 @@ export class Binary extends BSONValue {
|
|
|
200
248
|
/** @internal */
|
|
201
249
|
toExtendedJSON(options?: EJSONOptions): BinaryExtendedLegacy | BinaryExtended {
|
|
202
250
|
options = options || {};
|
|
251
|
+
|
|
252
|
+
if (this.sub_type === Binary.SUBTYPE_VECTOR) {
|
|
253
|
+
validateBinaryVector(this);
|
|
254
|
+
}
|
|
255
|
+
|
|
203
256
|
const base64String = ByteUtils.toBase64(this.buffer);
|
|
204
257
|
|
|
205
258
|
const subType = Number(this.sub_type).toString(16);
|
|
@@ -219,7 +272,7 @@ export class Binary extends BSONValue {
|
|
|
219
272
|
|
|
220
273
|
toUUID(): UUID {
|
|
221
274
|
if (this.sub_type === Binary.SUBTYPE_UUID) {
|
|
222
|
-
return new UUID(this.buffer.
|
|
275
|
+
return new UUID(this.buffer.subarray(0, this.position));
|
|
223
276
|
}
|
|
224
277
|
|
|
225
278
|
throw new BSONError(
|
|
@@ -272,6 +325,209 @@ export class Binary extends BSONValue {
|
|
|
272
325
|
const subTypeArg = inspect(this.sub_type, options);
|
|
273
326
|
return `Binary.createFromBase64(${base64Arg}, ${subTypeArg})`;
|
|
274
327
|
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* If this Binary represents a Int8 Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.Int8`),
|
|
331
|
+
* returns a copy of the bytes in a new Int8Array.
|
|
332
|
+
*
|
|
333
|
+
* If the Binary is not a Vector, or the datatype is not Int8, an error is thrown.
|
|
334
|
+
*/
|
|
335
|
+
public toInt8Array(): Int8Array {
|
|
336
|
+
if (this.sub_type !== Binary.SUBTYPE_VECTOR) {
|
|
337
|
+
throw new BSONError('Binary sub_type is not Vector');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (this.buffer[0] !== Binary.VECTOR_TYPE.Int8) {
|
|
341
|
+
throw new BSONError('Binary datatype field is not Int8');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return new Int8Array(
|
|
345
|
+
this.buffer.buffer.slice(this.buffer.byteOffset + 2, this.buffer.byteOffset + this.position)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* If this Binary represents a Float32 Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.Float32`),
|
|
351
|
+
* returns a copy of the bytes in a new Float32Array.
|
|
352
|
+
*
|
|
353
|
+
* If the Binary is not a Vector, or the datatype is not Float32, an error is thrown.
|
|
354
|
+
*/
|
|
355
|
+
public toFloat32Array(): Float32Array {
|
|
356
|
+
if (this.sub_type !== Binary.SUBTYPE_VECTOR) {
|
|
357
|
+
throw new BSONError('Binary sub_type is not Vector');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (this.buffer[0] !== Binary.VECTOR_TYPE.Float32) {
|
|
361
|
+
throw new BSONError('Binary datatype field is not Float32');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const floatBytes = new Uint8Array(
|
|
365
|
+
this.buffer.buffer.slice(this.buffer.byteOffset + 2, this.buffer.byteOffset + this.position)
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
if (NumberUtils.isBigEndian) ByteUtils.swap32(floatBytes);
|
|
369
|
+
|
|
370
|
+
return new Float32Array(floatBytes.buffer);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* If this Binary represents packed bit Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.PackedBit`),
|
|
375
|
+
* returns a copy of the bytes that are packed bits.
|
|
376
|
+
*
|
|
377
|
+
* Use `toBits` to get the unpacked bits.
|
|
378
|
+
*
|
|
379
|
+
* If the Binary is not a Vector, or the datatype is not PackedBit, an error is thrown.
|
|
380
|
+
*/
|
|
381
|
+
public toPackedBits(): Uint8Array {
|
|
382
|
+
if (this.sub_type !== Binary.SUBTYPE_VECTOR) {
|
|
383
|
+
throw new BSONError('Binary sub_type is not Vector');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (this.buffer[0] !== Binary.VECTOR_TYPE.PackedBit) {
|
|
387
|
+
throw new BSONError('Binary datatype field is not packed bit');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return new Uint8Array(
|
|
391
|
+
this.buffer.buffer.slice(this.buffer.byteOffset + 2, this.buffer.byteOffset + this.position)
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* If this Binary represents a Packed bit Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.PackedBit`),
|
|
397
|
+
* returns a copy of the bit unpacked into a new Int8Array.
|
|
398
|
+
*
|
|
399
|
+
* Use `toPackedBits` to get the bits still in packed form.
|
|
400
|
+
*
|
|
401
|
+
* If the Binary is not a Vector, or the datatype is not PackedBit, an error is thrown.
|
|
402
|
+
*/
|
|
403
|
+
public toBits(): Int8Array {
|
|
404
|
+
if (this.sub_type !== Binary.SUBTYPE_VECTOR) {
|
|
405
|
+
throw new BSONError('Binary sub_type is not Vector');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (this.buffer[0] !== Binary.VECTOR_TYPE.PackedBit) {
|
|
409
|
+
throw new BSONError('Binary datatype field is not packed bit');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const byteCount = this.length() - 2;
|
|
413
|
+
const bitCount = byteCount * 8 - this.buffer[1];
|
|
414
|
+
const bits = new Int8Array(bitCount);
|
|
415
|
+
|
|
416
|
+
for (let bitOffset = 0; bitOffset < bits.length; bitOffset++) {
|
|
417
|
+
const byteOffset = (bitOffset / 8) | 0;
|
|
418
|
+
const byte = this.buffer[byteOffset + 2];
|
|
419
|
+
const shift = 7 - (bitOffset % 8);
|
|
420
|
+
const bit = (byte >> shift) & 1;
|
|
421
|
+
bits[bitOffset] = bit;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return bits;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Constructs a Binary representing an Int8 Vector.
|
|
429
|
+
* @param array - The array to store as a view on the Binary class
|
|
430
|
+
*/
|
|
431
|
+
public static fromInt8Array(array: Int8Array): Binary {
|
|
432
|
+
const buffer = ByteUtils.allocate(array.byteLength + 2);
|
|
433
|
+
buffer[0] = Binary.VECTOR_TYPE.Int8;
|
|
434
|
+
buffer[1] = 0;
|
|
435
|
+
const intBytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
|
436
|
+
buffer.set(intBytes, 2);
|
|
437
|
+
return new this(buffer, this.SUBTYPE_VECTOR);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** Constructs a Binary representing an Float32 Vector. */
|
|
441
|
+
public static fromFloat32Array(array: Float32Array): Binary {
|
|
442
|
+
const binaryBytes = ByteUtils.allocate(array.byteLength + 2);
|
|
443
|
+
binaryBytes[0] = Binary.VECTOR_TYPE.Float32;
|
|
444
|
+
binaryBytes[1] = 0;
|
|
445
|
+
|
|
446
|
+
const floatBytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
|
447
|
+
binaryBytes.set(floatBytes, 2);
|
|
448
|
+
|
|
449
|
+
if (NumberUtils.isBigEndian) ByteUtils.swap32(new Uint8Array(binaryBytes.buffer, 2));
|
|
450
|
+
|
|
451
|
+
return new this(binaryBytes, this.SUBTYPE_VECTOR);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Constructs a Binary representing a packed bit Vector.
|
|
456
|
+
*
|
|
457
|
+
* Use `fromBits` to pack an array of 1s and 0s.
|
|
458
|
+
*/
|
|
459
|
+
public static fromPackedBits(array: Uint8Array, padding = 0): Binary {
|
|
460
|
+
const buffer = ByteUtils.allocate(array.byteLength + 2);
|
|
461
|
+
buffer[0] = Binary.VECTOR_TYPE.PackedBit;
|
|
462
|
+
buffer[1] = padding;
|
|
463
|
+
buffer.set(array, 2);
|
|
464
|
+
return new this(buffer, this.SUBTYPE_VECTOR);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Constructs a Binary representing an Packed Bit Vector.
|
|
469
|
+
* @param array - The array of 1s and 0s to pack into the Binary instance
|
|
470
|
+
*/
|
|
471
|
+
public static fromBits(bits: ArrayLike<number>): Binary {
|
|
472
|
+
const byteLength = (bits.length + 7) >>> 3; // ceil(bits.length / 8)
|
|
473
|
+
const bytes = new Uint8Array(byteLength + 2);
|
|
474
|
+
bytes[0] = Binary.VECTOR_TYPE.PackedBit;
|
|
475
|
+
|
|
476
|
+
const remainder = bits.length % 8;
|
|
477
|
+
bytes[1] = remainder === 0 ? 0 : 8 - remainder;
|
|
478
|
+
|
|
479
|
+
for (let bitOffset = 0; bitOffset < bits.length; bitOffset++) {
|
|
480
|
+
const byteOffset = bitOffset >>> 3; // floor(bitOffset / 8)
|
|
481
|
+
const bit = bits[bitOffset];
|
|
482
|
+
|
|
483
|
+
if (bit !== 0 && bit !== 1) {
|
|
484
|
+
throw new BSONError(
|
|
485
|
+
`Invalid bit value at ${bitOffset}: must be 0 or 1, found ${bits[bitOffset]}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (bit === 0) continue;
|
|
490
|
+
|
|
491
|
+
const shift = 7 - (bitOffset % 8);
|
|
492
|
+
bytes[byteOffset + 2] |= bit << shift;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return new this(bytes, Binary.SUBTYPE_VECTOR);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export function validateBinaryVector(vector: Binary): void {
|
|
500
|
+
if (vector.sub_type !== Binary.SUBTYPE_VECTOR) return;
|
|
501
|
+
|
|
502
|
+
const size = vector.position;
|
|
503
|
+
|
|
504
|
+
// NOTE: Validation is only applied to **KNOWN** vector types
|
|
505
|
+
// If a new datatype is introduced, a future version of the library will need to add validation
|
|
506
|
+
const datatype = vector.buffer[0];
|
|
507
|
+
|
|
508
|
+
// NOTE: We do not enable noUncheckedIndexedAccess so TS believes this is always number
|
|
509
|
+
// a Binary vector may be empty, in which case the padding is undefined
|
|
510
|
+
// this possible value is tolerable for our validation checks
|
|
511
|
+
const padding: number | undefined = vector.buffer[1];
|
|
512
|
+
|
|
513
|
+
if (
|
|
514
|
+
(datatype === Binary.VECTOR_TYPE.Float32 || datatype === Binary.VECTOR_TYPE.Int8) &&
|
|
515
|
+
padding !== 0
|
|
516
|
+
) {
|
|
517
|
+
throw new BSONError('Invalid Vector: padding must be zero for int8 and float32 vectors');
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (datatype === Binary.VECTOR_TYPE.PackedBit && padding !== 0 && size === 2) {
|
|
521
|
+
throw new BSONError(
|
|
522
|
+
'Invalid Vector: padding must be zero for packed bit vectors that are empty'
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (datatype === Binary.VECTOR_TYPE.PackedBit && padding > 7) {
|
|
527
|
+
throw new BSONError(
|
|
528
|
+
`Invalid Vector: padding must be a value between 0 and 7. found: ${padding}`
|
|
529
|
+
);
|
|
530
|
+
}
|
|
275
531
|
}
|
|
276
532
|
|
|
277
533
|
/** @public */
|
package/src/bson_value.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BSON_MAJOR_VERSION } from './constants';
|
|
2
2
|
import { type InspectFn } from './parser/utils';
|
|
3
|
+
import { BSON_VERSION_SYMBOL } from './constants';
|
|
3
4
|
|
|
4
5
|
/** @public */
|
|
5
6
|
export abstract class BSONValue {
|
|
@@ -7,7 +8,7 @@ export abstract class BSONValue {
|
|
|
7
8
|
public abstract get _bsontype(): string;
|
|
8
9
|
|
|
9
10
|
/** @internal */
|
|
10
|
-
get [
|
|
11
|
+
get [BSON_VERSION_SYMBOL](): typeof BSON_MAJOR_VERSION {
|
|
11
12
|
return BSON_MAJOR_VERSION;
|
|
12
13
|
}
|
|
13
14
|
|
package/src/constants.ts
CHANGED
package/src/decimal128.ts
CHANGED
|
@@ -142,7 +142,7 @@ export class Decimal128 extends BSONValue {
|
|
|
142
142
|
super();
|
|
143
143
|
if (typeof bytes === 'string') {
|
|
144
144
|
this.bytes = Decimal128.fromString(bytes).bytes;
|
|
145
|
-
} else if (isUint8Array(bytes)) {
|
|
145
|
+
} else if (bytes instanceof Uint8Array || isUint8Array(bytes)) {
|
|
146
146
|
if (bytes.byteLength !== 16) {
|
|
147
147
|
throw new BSONError('Decimal128 must take a Buffer of 16 bytes');
|
|
148
148
|
}
|
package/src/extended_json.ts
CHANGED
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
BSON_INT32_MIN,
|
|
7
7
|
BSON_INT64_MAX,
|
|
8
8
|
BSON_INT64_MIN,
|
|
9
|
-
BSON_MAJOR_VERSION
|
|
9
|
+
BSON_MAJOR_VERSION,
|
|
10
|
+
BSON_VERSION_SYMBOL
|
|
10
11
|
} from './constants';
|
|
11
12
|
import { DBRef, isDBRefLike } from './db_ref';
|
|
12
13
|
import { Decimal128 } from './decimal128';
|
|
@@ -358,7 +359,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
|
358
359
|
doc != null &&
|
|
359
360
|
typeof doc === 'object' &&
|
|
360
361
|
typeof doc._bsontype === 'string' &&
|
|
361
|
-
doc[
|
|
362
|
+
doc[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION
|
|
362
363
|
) {
|
|
363
364
|
throw new BSONVersionError();
|
|
364
365
|
} else if (isBSONType(doc)) {
|
package/src/long.ts
CHANGED
|
@@ -1163,7 +1163,6 @@ export class Long extends BSONValue {
|
|
|
1163
1163
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1164
1164
|
let rem: Long = this;
|
|
1165
1165
|
let result = '';
|
|
1166
|
-
// eslint-disable-next-line no-constant-condition
|
|
1167
1166
|
while (true) {
|
|
1168
1167
|
const remDiv = rem.div(radixToPower);
|
|
1169
1168
|
const intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0;
|
package/src/objectid.ts
CHANGED
|
@@ -4,9 +4,6 @@ import { type InspectFn, defaultInspect } from './parser/utils';
|
|
|
4
4
|
import { ByteUtils } from './utils/byte_utils';
|
|
5
5
|
import { NumberUtils } from './utils/number_utils';
|
|
6
6
|
|
|
7
|
-
// Regular expression that checks for hex value
|
|
8
|
-
const checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$');
|
|
9
|
-
|
|
10
7
|
// Unique sequence for the current process (initialized on first use)
|
|
11
8
|
let PROCESS_UNIQUE: Uint8Array | null = null;
|
|
12
9
|
|
|
@@ -112,7 +109,7 @@ export class ObjectId extends BSONValue {
|
|
|
112
109
|
// If intstanceof matches we can escape calling ensure buffer in Node.js environments
|
|
113
110
|
this.buffer = ByteUtils.toLocalBufferType(workingId);
|
|
114
111
|
} else if (typeof workingId === 'string') {
|
|
115
|
-
if (
|
|
112
|
+
if (ObjectId.validateHexString(workingId)) {
|
|
116
113
|
this.buffer = ByteUtils.fromHex(workingId);
|
|
117
114
|
} else {
|
|
118
115
|
throw new BSONError(
|
|
@@ -143,6 +140,29 @@ export class ObjectId extends BSONValue {
|
|
|
143
140
|
}
|
|
144
141
|
}
|
|
145
142
|
|
|
143
|
+
/**
|
|
144
|
+
* @internal
|
|
145
|
+
* Validates the input string is a valid hex representation of an ObjectId.
|
|
146
|
+
*/
|
|
147
|
+
private static validateHexString(string: string): boolean {
|
|
148
|
+
if (string?.length !== 24) return false;
|
|
149
|
+
for (let i = 0; i < 24; i++) {
|
|
150
|
+
const char = string.charCodeAt(i);
|
|
151
|
+
if (
|
|
152
|
+
// Check for ASCII 0-9
|
|
153
|
+
(char >= 48 && char <= 57) ||
|
|
154
|
+
// Check for ASCII a-f
|
|
155
|
+
(char >= 97 && char <= 102) ||
|
|
156
|
+
// Check for ASCII A-F
|
|
157
|
+
(char >= 65 && char <= 70)
|
|
158
|
+
) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
146
166
|
/** Returns the ObjectId id as a 24 lowercase character hex string representation */
|
|
147
167
|
toHexString(): string {
|
|
148
168
|
if (ObjectId.cacheHexString && this.__id) {
|
|
@@ -329,6 +349,7 @@ export class ObjectId extends BSONValue {
|
|
|
329
349
|
*/
|
|
330
350
|
static isValid(id: string | number | ObjectId | ObjectIdLike | Uint8Array): boolean {
|
|
331
351
|
if (id == null) return false;
|
|
352
|
+
if (typeof id === 'string') return ObjectId.validateHexString(id);
|
|
332
353
|
|
|
333
354
|
try {
|
|
334
355
|
new ObjectId(id);
|
|
@@ -81,7 +81,7 @@ function calculateElement(
|
|
|
81
81
|
if (
|
|
82
82
|
value != null &&
|
|
83
83
|
typeof value._bsontype === 'string' &&
|
|
84
|
-
value[
|
|
84
|
+
value[constants.BSON_VERSION_SYMBOL] !== constants.BSON_MAJOR_VERSION
|
|
85
85
|
) {
|
|
86
86
|
throw new BSONVersionError();
|
|
87
87
|
} else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
@@ -296,7 +296,7 @@ function deserializeObject(
|
|
|
296
296
|
|
|
297
297
|
// We have a raw value
|
|
298
298
|
if (raw) {
|
|
299
|
-
value = buffer.
|
|
299
|
+
value = buffer.subarray(index, index + objectSize);
|
|
300
300
|
} else {
|
|
301
301
|
let objectOptions = options;
|
|
302
302
|
if (!globalUTFValidation) {
|
|
@@ -374,52 +374,24 @@ function deserializeObject(
|
|
|
374
374
|
if (binarySize > buffer.byteLength)
|
|
375
375
|
throw new BSONError('Binary type size larger than document size');
|
|
376
376
|
|
|
377
|
-
//
|
|
378
|
-
if (
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
|
|
389
|
-
}
|
|
377
|
+
// If we have subtype 2 skip the 4 bytes for the size
|
|
378
|
+
if (subType === Binary.SUBTYPE_BYTE_ARRAY) {
|
|
379
|
+
binarySize = NumberUtils.getInt32LE(buffer, index);
|
|
380
|
+
index += 4;
|
|
381
|
+
if (binarySize < 0)
|
|
382
|
+
throw new BSONError('Negative binary type element size found for subtype 0x02');
|
|
383
|
+
if (binarySize > totalBinarySize - 4)
|
|
384
|
+
throw new BSONError('Binary type with subtype 0x02 contains too long binary size');
|
|
385
|
+
if (binarySize < totalBinarySize - 4)
|
|
386
|
+
throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
|
|
387
|
+
}
|
|
390
388
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
} else {
|
|
394
|
-
value = new Binary(buffer.slice(index, index + binarySize), subType);
|
|
395
|
-
if (subType === constants.BSON_BINARY_SUBTYPE_UUID_NEW && UUID.isValid(value)) {
|
|
396
|
-
value = value.toUUID();
|
|
397
|
-
}
|
|
398
|
-
}
|
|
389
|
+
if (promoteBuffers && promoteValues) {
|
|
390
|
+
value = ByteUtils.toLocalBufferType(buffer.subarray(index, index + binarySize));
|
|
399
391
|
} else {
|
|
400
|
-
|
|
401
|
-
if (subType ===
|
|
402
|
-
|
|
403
|
-
index += 4;
|
|
404
|
-
if (binarySize < 0)
|
|
405
|
-
throw new BSONError('Negative binary type element size found for subtype 0x02');
|
|
406
|
-
if (binarySize > totalBinarySize - 4)
|
|
407
|
-
throw new BSONError('Binary type with subtype 0x02 contains too long binary size');
|
|
408
|
-
if (binarySize < totalBinarySize - 4)
|
|
409
|
-
throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (promoteBuffers && promoteValues) {
|
|
413
|
-
value = ByteUtils.allocateUnsafe(binarySize);
|
|
414
|
-
// Copy the data
|
|
415
|
-
for (i = 0; i < binarySize; i++) {
|
|
416
|
-
value[i] = buffer[index + i];
|
|
417
|
-
}
|
|
418
|
-
} else {
|
|
419
|
-
value = new Binary(buffer.slice(index, index + binarySize), subType);
|
|
420
|
-
if (subType === constants.BSON_BINARY_SUBTYPE_UUID_NEW && UUID.isValid(value)) {
|
|
421
|
-
value = value.toUUID();
|
|
422
|
-
}
|
|
392
|
+
value = new Binary(buffer.subarray(index, index + binarySize), subType);
|
|
393
|
+
if (subType === constants.BSON_BINARY_SUBTYPE_UUID_NEW && UUID.isValid(value)) {
|
|
394
|
+
value = value.toUUID();
|
|
423
395
|
}
|
|
424
396
|
}
|
|
425
397
|
|