bson 7.2.0 → 7.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/bson.d.ts +129 -5
- package/lib/bson.bundle.js +453 -453
- package/lib/bson.bundle.js.map +1 -1
- package/lib/bson.cjs +453 -453
- package/lib/bson.cjs.map +1 -1
- package/lib/bson.mjs +453 -453
- package/lib/bson.mjs.map +1 -1
- package/lib/bson.node.mjs +453 -453
- package/lib/bson.node.mjs.map +1 -1
- package/lib/bson.rn.cjs +454 -456
- package/lib/bson.rn.cjs.map +1 -1
- package/package.json +4 -4
- package/src/binary.ts +2 -2
- package/src/bson.ts +0 -2
- package/src/extended_json.ts +33 -12
- package/src/long.ts +1 -1
- package/src/objectid.ts +24 -10
- package/src/parser/calculate_size.ts +75 -68
- package/src/parser/deserializer.ts +227 -61
- package/src/parser/serializer.ts +272 -478
- package/src/timestamp.ts +158 -4
package/src/parser/serializer.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Binary, validateBinaryVector } from '../binary';
|
|
2
2
|
import type { BSONSymbol, DBRef, Document, MaxKey } from '../bson';
|
|
3
|
+
import { bsonType } from '../bson_value';
|
|
3
4
|
import type { Code } from '../code';
|
|
4
5
|
import * as constants from '../constants';
|
|
5
|
-
import type { DBRefLike } from '../db_ref';
|
|
6
6
|
import type { Decimal128 } from '../decimal128';
|
|
7
7
|
import type { Double } from '../double';
|
|
8
8
|
import { BSONError, BSONVersionError } from '../error';
|
|
@@ -45,12 +45,6 @@ export interface SerializeOptions {
|
|
|
45
45
|
const regexp = /\x00/; // eslint-disable-line no-control-regex
|
|
46
46
|
const ignoreKeys = new Set(['$db', '$ref', '$id', '$clusterTime']);
|
|
47
47
|
|
|
48
|
-
/*
|
|
49
|
-
* isArray indicates if we are writing to a BSON array (type 0x04)
|
|
50
|
-
* which forces the "key" which really an array index as a string to be written as ascii
|
|
51
|
-
* This will catch any errors in index as a string generation
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
48
|
function serializeString(buffer: Uint8Array, key: string, value: string, index: number) {
|
|
55
49
|
// Encode String type
|
|
56
50
|
buffer[index++] = constants.BSON_DATA_STRING;
|
|
@@ -213,7 +207,7 @@ function serializeMinMax(buffer: Uint8Array, key: string, value: MinKey | MaxKey
|
|
|
213
207
|
// Write the type of either min or max key
|
|
214
208
|
if (value === null) {
|
|
215
209
|
buffer[index++] = constants.BSON_DATA_NULL;
|
|
216
|
-
} else if (value
|
|
210
|
+
} else if (value[bsonType] === 'MinKey') {
|
|
217
211
|
buffer[index++] = constants.BSON_DATA_MIN_KEY;
|
|
218
212
|
} else {
|
|
219
213
|
buffer[index++] = constants.BSON_DATA_MAX_KEY;
|
|
@@ -268,46 +262,6 @@ function serializeBuffer(buffer: Uint8Array, key: string, value: Uint8Array, ind
|
|
|
268
262
|
return index;
|
|
269
263
|
}
|
|
270
264
|
|
|
271
|
-
function serializeObject(
|
|
272
|
-
buffer: Uint8Array,
|
|
273
|
-
key: string,
|
|
274
|
-
value: Document,
|
|
275
|
-
index: number,
|
|
276
|
-
checkKeys: boolean,
|
|
277
|
-
depth: number,
|
|
278
|
-
serializeFunctions: boolean,
|
|
279
|
-
ignoreUndefined: boolean,
|
|
280
|
-
path: Set<Document>
|
|
281
|
-
) {
|
|
282
|
-
if (path.has(value)) {
|
|
283
|
-
throw new BSONError('Cannot convert circular structure to BSON');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
path.add(value);
|
|
287
|
-
|
|
288
|
-
// Write the type
|
|
289
|
-
buffer[index++] = Array.isArray(value) ? constants.BSON_DATA_ARRAY : constants.BSON_DATA_OBJECT;
|
|
290
|
-
// Number of written bytes
|
|
291
|
-
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
292
|
-
// Encode the name
|
|
293
|
-
index = index + numberOfWrittenBytes;
|
|
294
|
-
buffer[index++] = 0;
|
|
295
|
-
const endIndex = serializeInto(
|
|
296
|
-
buffer,
|
|
297
|
-
value,
|
|
298
|
-
checkKeys,
|
|
299
|
-
index,
|
|
300
|
-
depth + 1,
|
|
301
|
-
serializeFunctions,
|
|
302
|
-
ignoreUndefined,
|
|
303
|
-
path
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
path.delete(value);
|
|
307
|
-
|
|
308
|
-
return endIndex;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
265
|
function serializeDecimal128(buffer: Uint8Array, key: string, value: Decimal128, index: number) {
|
|
312
266
|
buffer[index++] = constants.BSON_DATA_DECIMAL128;
|
|
313
267
|
// Number of written bytes
|
|
@@ -323,7 +277,7 @@ function serializeDecimal128(buffer: Uint8Array, key: string, value: Decimal128,
|
|
|
323
277
|
function serializeLong(buffer: Uint8Array, key: string, value: Long, index: number) {
|
|
324
278
|
// Write the type
|
|
325
279
|
buffer[index++] =
|
|
326
|
-
value
|
|
280
|
+
value[bsonType] === 'Long' ? constants.BSON_DATA_LONG : constants.BSON_DATA_TIMESTAMP;
|
|
327
281
|
// Number of written bytes
|
|
328
282
|
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
329
283
|
// Encode the name
|
|
@@ -391,85 +345,6 @@ function serializeFunction(buffer: Uint8Array, key: string, value: Function, ind
|
|
|
391
345
|
return index;
|
|
392
346
|
}
|
|
393
347
|
|
|
394
|
-
function serializeCode(
|
|
395
|
-
buffer: Uint8Array,
|
|
396
|
-
key: string,
|
|
397
|
-
value: Code,
|
|
398
|
-
index: number,
|
|
399
|
-
checkKeys = false,
|
|
400
|
-
depth = 0,
|
|
401
|
-
serializeFunctions = false,
|
|
402
|
-
ignoreUndefined = true,
|
|
403
|
-
path: Set<Document>
|
|
404
|
-
) {
|
|
405
|
-
if (value.scope && typeof value.scope === 'object') {
|
|
406
|
-
// Write the type
|
|
407
|
-
buffer[index++] = constants.BSON_DATA_CODE_W_SCOPE;
|
|
408
|
-
// Number of written bytes
|
|
409
|
-
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
410
|
-
// Encode the name
|
|
411
|
-
index = index + numberOfWrittenBytes;
|
|
412
|
-
buffer[index++] = 0;
|
|
413
|
-
|
|
414
|
-
// Starting index
|
|
415
|
-
let startIndex = index;
|
|
416
|
-
|
|
417
|
-
// Serialize the function
|
|
418
|
-
// Get the function string
|
|
419
|
-
const functionString = value.code;
|
|
420
|
-
// Index adjustment
|
|
421
|
-
index = index + 4;
|
|
422
|
-
// Write string into buffer
|
|
423
|
-
const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
424
|
-
// Write the size of the string to buffer
|
|
425
|
-
NumberUtils.setInt32LE(buffer, index, codeSize);
|
|
426
|
-
// Write end 0
|
|
427
|
-
buffer[index + 4 + codeSize - 1] = 0;
|
|
428
|
-
// Write the
|
|
429
|
-
index = index + codeSize + 4;
|
|
430
|
-
|
|
431
|
-
// Serialize the scope value
|
|
432
|
-
const endIndex = serializeInto(
|
|
433
|
-
buffer,
|
|
434
|
-
value.scope,
|
|
435
|
-
checkKeys,
|
|
436
|
-
index,
|
|
437
|
-
depth + 1,
|
|
438
|
-
serializeFunctions,
|
|
439
|
-
ignoreUndefined,
|
|
440
|
-
path
|
|
441
|
-
);
|
|
442
|
-
index = endIndex - 1;
|
|
443
|
-
|
|
444
|
-
// Writ the total
|
|
445
|
-
const totalSize = endIndex - startIndex;
|
|
446
|
-
|
|
447
|
-
// Write the total size of the object
|
|
448
|
-
startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
|
|
449
|
-
// Write trailing zero
|
|
450
|
-
buffer[index++] = 0;
|
|
451
|
-
} else {
|
|
452
|
-
buffer[index++] = constants.BSON_DATA_CODE;
|
|
453
|
-
// Number of written bytes
|
|
454
|
-
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
455
|
-
// Encode the name
|
|
456
|
-
index = index + numberOfWrittenBytes;
|
|
457
|
-
buffer[index++] = 0;
|
|
458
|
-
// Function string
|
|
459
|
-
const functionString = value.code.toString();
|
|
460
|
-
// Write the string
|
|
461
|
-
const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
462
|
-
// Write the size of the string to buffer
|
|
463
|
-
NumberUtils.setInt32LE(buffer, index, size);
|
|
464
|
-
// Update index
|
|
465
|
-
index = index + 4 + size - 1;
|
|
466
|
-
// Write zero
|
|
467
|
-
buffer[index++] = 0;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return index;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
348
|
function serializeBinary(buffer: Uint8Array, key: string, value: Binary, index: number) {
|
|
474
349
|
// Write the type
|
|
475
350
|
buffer[index++] = constants.BSON_DATA_BINARY;
|
|
@@ -528,52 +403,98 @@ function serializeSymbol(buffer: Uint8Array, key: string, value: BSONSymbol, ind
|
|
|
528
403
|
return index;
|
|
529
404
|
}
|
|
530
405
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
406
|
+
interface SerializationFrame {
|
|
407
|
+
/** Original object passed to this frame — used for cycle-detection (path.delete). */
|
|
408
|
+
sourceObject: Document;
|
|
409
|
+
/** True when serializing a BSON array; affects key format and type byte. */
|
|
410
|
+
isArray: boolean;
|
|
411
|
+
/** Buffer offset where this object's 4-byte size field will be written. */
|
|
412
|
+
objectSizeIndex: number;
|
|
413
|
+
/** Buffer offset for code-with-scope wrapper size, or null if not applicable. */
|
|
414
|
+
codeSizeIndex: number | null;
|
|
415
|
+
/**
|
|
416
|
+
* Object being iterated. For plain objects this may be the toBSON() result of
|
|
417
|
+
* sourceObject; for arrays and Maps it equals sourceObject.
|
|
418
|
+
*/
|
|
419
|
+
iterTarget: Document;
|
|
420
|
+
/**
|
|
421
|
+
* Pre-computed Object.keys() of iterTarget for plain objects.
|
|
422
|
+
* Null for arrays (use keyIndex as numeric index) and Maps (use mapIterator).
|
|
423
|
+
*/
|
|
424
|
+
keys: string[] | null;
|
|
425
|
+
/** Next index into keys[] for plain objects, or next array index for arrays. */
|
|
426
|
+
keyIndex: number;
|
|
427
|
+
/** Active iterator for Map objects; null for arrays and plain objects. */
|
|
428
|
+
mapIterator: IterableIterator<[unknown, unknown]> | null;
|
|
429
|
+
/** The enclosing frame, or null at the root. The stack is a linked list via this field. */
|
|
430
|
+
prev: SerializationFrame | null;
|
|
431
|
+
/** Whether to validate keys for this frame's document (may differ from caller, e.g. DBRef uses false). */
|
|
432
|
+
checkKeys: boolean;
|
|
433
|
+
/** Whether undefined values are skipped (may differ from caller, e.g. DBRef uses true). */
|
|
434
|
+
ignoreUndefined: boolean;
|
|
435
|
+
}
|
|
554
436
|
|
|
555
|
-
|
|
556
|
-
|
|
437
|
+
function makeFrame(
|
|
438
|
+
sourceObject: Document,
|
|
439
|
+
objectSizeIndex: number,
|
|
440
|
+
codeSizeIndex: number | null,
|
|
441
|
+
prev: SerializationFrame | null,
|
|
442
|
+
checkKeys: boolean,
|
|
443
|
+
ignoreUndefined: boolean
|
|
444
|
+
): SerializationFrame {
|
|
445
|
+
if (Array.isArray(sourceObject)) {
|
|
446
|
+
return {
|
|
447
|
+
sourceObject,
|
|
448
|
+
isArray: true,
|
|
449
|
+
objectSizeIndex,
|
|
450
|
+
codeSizeIndex,
|
|
451
|
+
iterTarget: sourceObject,
|
|
452
|
+
keys: null,
|
|
453
|
+
keyIndex: 0,
|
|
454
|
+
mapIterator: null,
|
|
455
|
+
prev,
|
|
456
|
+
checkKeys,
|
|
457
|
+
ignoreUndefined
|
|
458
|
+
};
|
|
557
459
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
//
|
|
574
|
-
|
|
575
|
-
//
|
|
576
|
-
|
|
460
|
+
if (sourceObject instanceof Map || isMap(sourceObject)) {
|
|
461
|
+
return {
|
|
462
|
+
sourceObject,
|
|
463
|
+
isArray: false,
|
|
464
|
+
objectSizeIndex,
|
|
465
|
+
codeSizeIndex,
|
|
466
|
+
iterTarget: sourceObject,
|
|
467
|
+
keys: null,
|
|
468
|
+
keyIndex: 0,
|
|
469
|
+
mapIterator: (sourceObject as Map<unknown, unknown>).entries(),
|
|
470
|
+
prev,
|
|
471
|
+
checkKeys,
|
|
472
|
+
ignoreUndefined
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
// Plain object: call toBSON() if defined to obtain the object to iterate.
|
|
476
|
+
let target: Document = sourceObject;
|
|
477
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
478
|
+
if (typeof (target as any)?.toBSON === 'function') {
|
|
479
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
480
|
+
target = (target as any).toBSON() as Document;
|
|
481
|
+
if (target != null && typeof target !== 'object') {
|
|
482
|
+
throw new BSONError('toBSON function did not return an object');
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
sourceObject,
|
|
487
|
+
isArray: false,
|
|
488
|
+
objectSizeIndex,
|
|
489
|
+
codeSizeIndex,
|
|
490
|
+
iterTarget: target,
|
|
491
|
+
keys: Object.keys(target as object),
|
|
492
|
+
keyIndex: 0,
|
|
493
|
+
mapIterator: null,
|
|
494
|
+
prev,
|
|
495
|
+
checkKeys,
|
|
496
|
+
ignoreUndefined
|
|
497
|
+
};
|
|
577
498
|
}
|
|
578
499
|
|
|
579
500
|
export function serializeInto(
|
|
@@ -581,7 +502,6 @@ export function serializeInto(
|
|
|
581
502
|
object: Document,
|
|
582
503
|
checkKeys: boolean,
|
|
583
504
|
startingIndex: number,
|
|
584
|
-
depth: number,
|
|
585
505
|
serializeFunctions: boolean,
|
|
586
506
|
ignoreUndefined: boolean,
|
|
587
507
|
path: Set<Document> | null
|
|
@@ -619,336 +539,210 @@ export function serializeInto(
|
|
|
619
539
|
path = new Set();
|
|
620
540
|
}
|
|
621
541
|
|
|
622
|
-
// Push the object to the path
|
|
623
542
|
path.add(object);
|
|
624
543
|
|
|
625
|
-
|
|
544
|
+
let currentFrame: SerializationFrame | null = makeFrame(
|
|
545
|
+
object,
|
|
546
|
+
startingIndex,
|
|
547
|
+
null,
|
|
548
|
+
null,
|
|
549
|
+
checkKeys,
|
|
550
|
+
ignoreUndefined
|
|
551
|
+
);
|
|
626
552
|
let index = startingIndex + 4;
|
|
627
553
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
if (value === undefined) {
|
|
644
|
-
index = serializeNull(buffer, key, value, index);
|
|
645
|
-
} else if (value === null) {
|
|
646
|
-
index = serializeNull(buffer, key, value, index);
|
|
647
|
-
} else if (type === 'string') {
|
|
648
|
-
index = serializeString(buffer, key, value, index);
|
|
649
|
-
} else if (type === 'number') {
|
|
650
|
-
index = serializeNumber(buffer, key, value, index);
|
|
651
|
-
} else if (type === 'bigint') {
|
|
652
|
-
index = serializeBigInt(buffer, key, value, index);
|
|
653
|
-
} else if (type === 'boolean') {
|
|
654
|
-
index = serializeBoolean(buffer, key, value, index);
|
|
655
|
-
} else if (type === 'object' && value._bsontype == null) {
|
|
656
|
-
if (value instanceof Date || isDate(value)) {
|
|
657
|
-
index = serializeDate(buffer, key, value, index);
|
|
658
|
-
} else if (value instanceof Uint8Array || isUint8Array(value)) {
|
|
659
|
-
index = serializeBuffer(buffer, key, value, index);
|
|
660
|
-
} else if (value instanceof RegExp || isRegExp(value)) {
|
|
661
|
-
index = serializeRegExp(buffer, key, value, index);
|
|
662
|
-
} else {
|
|
663
|
-
index = serializeObject(
|
|
664
|
-
buffer,
|
|
665
|
-
key,
|
|
666
|
-
value,
|
|
667
|
-
index,
|
|
668
|
-
checkKeys,
|
|
669
|
-
depth,
|
|
670
|
-
serializeFunctions,
|
|
671
|
-
ignoreUndefined,
|
|
672
|
-
path
|
|
673
|
-
);
|
|
554
|
+
while (currentFrame !== null) {
|
|
555
|
+
const frame: SerializationFrame = currentFrame;
|
|
556
|
+
|
|
557
|
+
// Advance to the next key-value pair, or finalize the frame if exhausted.
|
|
558
|
+
let key: string;
|
|
559
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
560
|
+
let value: any;
|
|
561
|
+
if (frame.mapIterator !== null) {
|
|
562
|
+
const next = frame.mapIterator.next();
|
|
563
|
+
if (next.done) {
|
|
564
|
+
buffer[index++] = 0x00;
|
|
565
|
+
NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
|
|
566
|
+
if (frame.codeSizeIndex !== null) {
|
|
567
|
+
NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
|
|
674
568
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
} else if (value._bsontype === 'ObjectId') {
|
|
679
|
-
index = serializeObjectId(buffer, key, value, index);
|
|
680
|
-
} else if (value._bsontype === 'Decimal128') {
|
|
681
|
-
index = serializeDecimal128(buffer, key, value, index);
|
|
682
|
-
} else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
|
|
683
|
-
index = serializeLong(buffer, key, value, index);
|
|
684
|
-
} else if (value._bsontype === 'Double') {
|
|
685
|
-
index = serializeDouble(buffer, key, value, index);
|
|
686
|
-
} else if (value._bsontype === 'Code') {
|
|
687
|
-
index = serializeCode(
|
|
688
|
-
buffer,
|
|
689
|
-
key,
|
|
690
|
-
value,
|
|
691
|
-
index,
|
|
692
|
-
checkKeys,
|
|
693
|
-
depth,
|
|
694
|
-
serializeFunctions,
|
|
695
|
-
ignoreUndefined,
|
|
696
|
-
path
|
|
697
|
-
);
|
|
698
|
-
} else if (value._bsontype === 'Binary') {
|
|
699
|
-
index = serializeBinary(buffer, key, value, index);
|
|
700
|
-
} else if (value._bsontype === 'BSONSymbol') {
|
|
701
|
-
index = serializeSymbol(buffer, key, value, index);
|
|
702
|
-
} else if (value._bsontype === 'DBRef') {
|
|
703
|
-
index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
|
|
704
|
-
} else if (value._bsontype === 'BSONRegExp') {
|
|
705
|
-
index = serializeBSONRegExp(buffer, key, value, index);
|
|
706
|
-
} else if (value._bsontype === 'Int32') {
|
|
707
|
-
index = serializeInt32(buffer, key, value, index);
|
|
708
|
-
} else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
709
|
-
index = serializeMinMax(buffer, key, value, index);
|
|
710
|
-
} else if (typeof value._bsontype !== 'undefined') {
|
|
711
|
-
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
712
|
-
}
|
|
713
|
-
} else if (type === 'function' && serializeFunctions) {
|
|
714
|
-
index = serializeFunction(buffer, key, value, index);
|
|
569
|
+
path.delete(frame.sourceObject);
|
|
570
|
+
currentFrame = frame.prev;
|
|
571
|
+
continue;
|
|
715
572
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
done = !!entry.done;
|
|
725
|
-
// Are we done, then skip and terminate
|
|
726
|
-
if (done) continue;
|
|
727
|
-
|
|
728
|
-
// Get the entry values
|
|
729
|
-
const key = entry.value ? entry.value[0] : undefined;
|
|
730
|
-
let value = entry.value ? entry.value[1] : undefined;
|
|
731
|
-
|
|
732
|
-
if (typeof value?.toBSON === 'function') {
|
|
733
|
-
value = value.toBSON();
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
// Check the type of the value
|
|
737
|
-
const type = typeof value;
|
|
738
|
-
|
|
739
|
-
// Check the key and throw error if it's illegal
|
|
740
|
-
if (typeof key === 'string' && !ignoreKeys.has(key)) {
|
|
741
|
-
if (key.match(regexp) != null) {
|
|
742
|
-
// The BSON spec doesn't allow keys with null bytes because keys are
|
|
743
|
-
// null-terminated.
|
|
744
|
-
throw new BSONError('key ' + key + ' must not contain null bytes');
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
if (checkKeys) {
|
|
748
|
-
if ('$' === key[0]) {
|
|
749
|
-
throw new BSONError('key ' + key + " must not start with '$'");
|
|
750
|
-
} else if (key.includes('.')) {
|
|
751
|
-
throw new BSONError('key ' + key + " must not contain '.'");
|
|
752
|
-
}
|
|
573
|
+
key = next.value[0] as string;
|
|
574
|
+
value = next.value[1];
|
|
575
|
+
} else if (frame.keys !== null) {
|
|
576
|
+
if (frame.keyIndex >= frame.keys.length) {
|
|
577
|
+
buffer[index++] = 0x00;
|
|
578
|
+
NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
|
|
579
|
+
if (frame.codeSizeIndex !== null) {
|
|
580
|
+
NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
|
|
753
581
|
}
|
|
582
|
+
path.delete(frame.sourceObject);
|
|
583
|
+
currentFrame = frame.prev;
|
|
584
|
+
continue;
|
|
754
585
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
index =
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
index = serializeBigInt(buffer, key, value, index);
|
|
766
|
-
} else if (type === 'boolean') {
|
|
767
|
-
index = serializeBoolean(buffer, key, value, index);
|
|
768
|
-
} else if (type === 'object' && value._bsontype == null) {
|
|
769
|
-
if (value instanceof Date || isDate(value)) {
|
|
770
|
-
index = serializeDate(buffer, key, value, index);
|
|
771
|
-
} else if (value instanceof Uint8Array || isUint8Array(value)) {
|
|
772
|
-
index = serializeBuffer(buffer, key, value, index);
|
|
773
|
-
} else if (value instanceof RegExp || isRegExp(value)) {
|
|
774
|
-
index = serializeRegExp(buffer, key, value, index);
|
|
775
|
-
} else {
|
|
776
|
-
index = serializeObject(
|
|
777
|
-
buffer,
|
|
778
|
-
key,
|
|
779
|
-
value,
|
|
780
|
-
index,
|
|
781
|
-
checkKeys,
|
|
782
|
-
depth,
|
|
783
|
-
serializeFunctions,
|
|
784
|
-
ignoreUndefined,
|
|
785
|
-
path
|
|
786
|
-
);
|
|
586
|
+
key = frame.keys[frame.keyIndex++];
|
|
587
|
+
value = (frame.iterTarget as Record<string, unknown>)[key];
|
|
588
|
+
} else {
|
|
589
|
+
// Array: use keyIndex as the numeric index.
|
|
590
|
+
const arr = frame.iterTarget as unknown[];
|
|
591
|
+
if (frame.keyIndex >= arr.length) {
|
|
592
|
+
buffer[index++] = 0x00;
|
|
593
|
+
NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
|
|
594
|
+
if (frame.codeSizeIndex !== null) {
|
|
595
|
+
NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
|
|
787
596
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
} else if (value._bsontype === 'ObjectId') {
|
|
792
|
-
index = serializeObjectId(buffer, key, value, index);
|
|
793
|
-
} else if (value._bsontype === 'Decimal128') {
|
|
794
|
-
index = serializeDecimal128(buffer, key, value, index);
|
|
795
|
-
} else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
|
|
796
|
-
index = serializeLong(buffer, key, value, index);
|
|
797
|
-
} else if (value._bsontype === 'Double') {
|
|
798
|
-
index = serializeDouble(buffer, key, value, index);
|
|
799
|
-
} else if (value._bsontype === 'Code') {
|
|
800
|
-
index = serializeCode(
|
|
801
|
-
buffer,
|
|
802
|
-
key,
|
|
803
|
-
value,
|
|
804
|
-
index,
|
|
805
|
-
checkKeys,
|
|
806
|
-
depth,
|
|
807
|
-
serializeFunctions,
|
|
808
|
-
ignoreUndefined,
|
|
809
|
-
path
|
|
810
|
-
);
|
|
811
|
-
} else if (value._bsontype === 'Binary') {
|
|
812
|
-
index = serializeBinary(buffer, key, value, index);
|
|
813
|
-
} else if (value._bsontype === 'BSONSymbol') {
|
|
814
|
-
index = serializeSymbol(buffer, key, value, index);
|
|
815
|
-
} else if (value._bsontype === 'DBRef') {
|
|
816
|
-
index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
|
|
817
|
-
} else if (value._bsontype === 'BSONRegExp') {
|
|
818
|
-
index = serializeBSONRegExp(buffer, key, value, index);
|
|
819
|
-
} else if (value._bsontype === 'Int32') {
|
|
820
|
-
index = serializeInt32(buffer, key, value, index);
|
|
821
|
-
} else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
822
|
-
index = serializeMinMax(buffer, key, value, index);
|
|
823
|
-
} else if (typeof value._bsontype !== 'undefined') {
|
|
824
|
-
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
825
|
-
}
|
|
826
|
-
} else if (type === 'function' && serializeFunctions) {
|
|
827
|
-
index = serializeFunction(buffer, key, value, index);
|
|
597
|
+
path.delete(frame.sourceObject);
|
|
598
|
+
currentFrame = frame.prev;
|
|
599
|
+
continue;
|
|
828
600
|
}
|
|
601
|
+
const i = frame.keyIndex++;
|
|
602
|
+
key = String(i);
|
|
603
|
+
value = arr[i];
|
|
829
604
|
}
|
|
830
|
-
|
|
831
|
-
if (typeof
|
|
832
|
-
|
|
833
|
-
object = object.toBSON();
|
|
834
|
-
if (object != null && typeof object !== 'object') {
|
|
835
|
-
throw new BSONError('toBSON function did not return an object');
|
|
836
|
-
}
|
|
605
|
+
|
|
606
|
+
if (typeof value?.toBSON === 'function') {
|
|
607
|
+
value = value.toBSON();
|
|
837
608
|
}
|
|
838
609
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
// Is there an override value
|
|
843
|
-
if (typeof value?.toBSON === 'function') {
|
|
844
|
-
value = value.toBSON();
|
|
610
|
+
if (!frame.isArray && typeof key === 'string' && !(key[0] === '$' && ignoreKeys.has(key))) {
|
|
611
|
+
if (regexp.test(key)) {
|
|
612
|
+
throw new BSONError('key ' + key + ' must not contain null bytes');
|
|
845
613
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
if (typeof key === 'string' && !ignoreKeys.has(key)) {
|
|
852
|
-
if (key.match(regexp) != null) {
|
|
853
|
-
// The BSON spec doesn't allow keys with null bytes because keys are
|
|
854
|
-
// null-terminated.
|
|
855
|
-
throw new BSONError('key ' + key + ' must not contain null bytes');
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
if (checkKeys) {
|
|
859
|
-
if ('$' === key[0]) {
|
|
860
|
-
throw new BSONError('key ' + key + " must not start with '$'");
|
|
861
|
-
} else if (key.includes('.')) {
|
|
862
|
-
throw new BSONError('key ' + key + " must not contain '.'");
|
|
863
|
-
}
|
|
614
|
+
if (frame.checkKeys) {
|
|
615
|
+
if ('$' === key[0]) {
|
|
616
|
+
throw new BSONError('key ' + key + " must not start with '$'");
|
|
617
|
+
} else if (key.includes('.')) {
|
|
618
|
+
throw new BSONError('key ' + key + " must not contain '.'");
|
|
864
619
|
}
|
|
865
620
|
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const type = typeof value;
|
|
866
624
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
} else if (value === null) {
|
|
625
|
+
if (value === undefined) {
|
|
626
|
+
if (frame.isArray || frame.ignoreUndefined === false) {
|
|
870
627
|
index = serializeNull(buffer, key, value, index);
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
checkKeys,
|
|
893
|
-
depth,
|
|
894
|
-
serializeFunctions,
|
|
895
|
-
ignoreUndefined,
|
|
896
|
-
path
|
|
897
|
-
);
|
|
628
|
+
}
|
|
629
|
+
} else if (value === null) {
|
|
630
|
+
index = serializeNull(buffer, key, value, index);
|
|
631
|
+
} else if (type === 'string') {
|
|
632
|
+
index = serializeString(buffer, key, value, index);
|
|
633
|
+
} else if (type === 'number') {
|
|
634
|
+
index = serializeNumber(buffer, key, value, index);
|
|
635
|
+
} else if (type === 'bigint') {
|
|
636
|
+
index = serializeBigInt(buffer, key, value, index);
|
|
637
|
+
} else if (type === 'boolean') {
|
|
638
|
+
index = serializeBoolean(buffer, key, value, index);
|
|
639
|
+
} else if (type === 'object' && value._bsontype == null) {
|
|
640
|
+
if (value instanceof Date || isDate(value)) {
|
|
641
|
+
index = serializeDate(buffer, key, value, index);
|
|
642
|
+
} else if (value instanceof Uint8Array || isUint8Array(value)) {
|
|
643
|
+
index = serializeBuffer(buffer, key, value, index);
|
|
644
|
+
} else if (value instanceof RegExp || isRegExp(value)) {
|
|
645
|
+
index = serializeRegExp(buffer, key, value, index);
|
|
646
|
+
} else {
|
|
647
|
+
if (path.has(value)) {
|
|
648
|
+
throw new BSONError('Cannot convert circular structure to BSON');
|
|
898
649
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
650
|
+
const nestedIsArray = Array.isArray(value);
|
|
651
|
+
buffer[index++] = nestedIsArray ? constants.BSON_DATA_ARRAY : constants.BSON_DATA_OBJECT;
|
|
652
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
653
|
+
buffer[index++] = 0x00;
|
|
654
|
+
const nestedStartIndex = index;
|
|
655
|
+
path.add(value);
|
|
656
|
+
currentFrame = makeFrame(
|
|
657
|
+
value,
|
|
658
|
+
nestedStartIndex,
|
|
659
|
+
null,
|
|
660
|
+
frame,
|
|
661
|
+
frame.checkKeys,
|
|
662
|
+
frame.ignoreUndefined
|
|
663
|
+
);
|
|
664
|
+
index += 4;
|
|
665
|
+
}
|
|
666
|
+
} else if (type === 'object') {
|
|
667
|
+
if (value[constants.BSON_VERSION_SYMBOL] !== constants.BSON_MAJOR_VERSION) {
|
|
668
|
+
throw new BSONVersionError();
|
|
669
|
+
}
|
|
670
|
+
const tag = value[bsonType];
|
|
671
|
+
if (tag === 'ObjectId') {
|
|
672
|
+
index = serializeObjectId(buffer, key, value, index);
|
|
673
|
+
} else if (tag === 'Decimal128') {
|
|
674
|
+
index = serializeDecimal128(buffer, key, value, index);
|
|
675
|
+
} else if (tag === 'Long' || tag === 'Timestamp') {
|
|
676
|
+
index = serializeLong(buffer, key, value, index);
|
|
677
|
+
} else if (tag === 'Double') {
|
|
678
|
+
index = serializeDouble(buffer, key, value, index);
|
|
679
|
+
} else if (tag === 'Code') {
|
|
680
|
+
const codeValue = value as Code;
|
|
681
|
+
if (codeValue.scope && typeof codeValue.scope === 'object') {
|
|
682
|
+
buffer[index++] = constants.BSON_DATA_CODE_W_SCOPE;
|
|
683
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
684
|
+
buffer[index++] = 0x00;
|
|
685
|
+
const codeTotalSizeIndex = index;
|
|
686
|
+
index += 4;
|
|
687
|
+
const functionString = codeValue.code;
|
|
688
|
+
const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
689
|
+
NumberUtils.setInt32LE(buffer, index, codeSize);
|
|
690
|
+
buffer[index + 4 + codeSize - 1] = 0;
|
|
691
|
+
index = index + codeSize + 4;
|
|
692
|
+
const scope = codeValue.scope;
|
|
693
|
+
if (path.has(scope)) {
|
|
694
|
+
throw new BSONError('Cannot convert circular structure to BSON');
|
|
695
|
+
}
|
|
696
|
+
path.add(scope);
|
|
697
|
+
currentFrame = makeFrame(
|
|
698
|
+
scope,
|
|
915
699
|
index,
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
ignoreUndefined
|
|
920
|
-
path
|
|
700
|
+
codeTotalSizeIndex,
|
|
701
|
+
frame,
|
|
702
|
+
frame.checkKeys,
|
|
703
|
+
frame.ignoreUndefined
|
|
921
704
|
);
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
index
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
index =
|
|
932
|
-
} else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
933
|
-
index = serializeMinMax(buffer, key, value, index);
|
|
934
|
-
} else if (typeof value._bsontype !== 'undefined') {
|
|
935
|
-
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
705
|
+
index += 4;
|
|
706
|
+
} else {
|
|
707
|
+
buffer[index++] = constants.BSON_DATA_CODE;
|
|
708
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
709
|
+
buffer[index++] = 0x00;
|
|
710
|
+
const functionString = codeValue.code.toString();
|
|
711
|
+
const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
712
|
+
NumberUtils.setInt32LE(buffer, index, size);
|
|
713
|
+
index = index + 4 + size - 1;
|
|
714
|
+
buffer[index++] = 0;
|
|
936
715
|
}
|
|
937
|
-
} else if (
|
|
938
|
-
index =
|
|
716
|
+
} else if (tag === 'Binary') {
|
|
717
|
+
index = serializeBinary(buffer, key, value, index);
|
|
718
|
+
} else if (tag === 'BSONSymbol') {
|
|
719
|
+
index = serializeSymbol(buffer, key, value, index);
|
|
720
|
+
} else if (tag === 'DBRef') {
|
|
721
|
+
const dbref = value as DBRef;
|
|
722
|
+
const orderedValues: Document = Object.assign(
|
|
723
|
+
{ $ref: dbref.collection, $id: dbref.oid },
|
|
724
|
+
dbref.db != null ? { $db: dbref.db } : null,
|
|
725
|
+
dbref.fields
|
|
726
|
+
);
|
|
727
|
+
buffer[index++] = constants.BSON_DATA_OBJECT;
|
|
728
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
729
|
+
buffer[index++] = 0x00;
|
|
730
|
+
path.add(orderedValues);
|
|
731
|
+
currentFrame = makeFrame(orderedValues, index, null, frame, false, true);
|
|
732
|
+
index += 4;
|
|
733
|
+
} else if (tag === 'BSONRegExp') {
|
|
734
|
+
index = serializeBSONRegExp(buffer, key, value, index);
|
|
735
|
+
} else if (tag === 'Int32') {
|
|
736
|
+
index = serializeInt32(buffer, key, value, index);
|
|
737
|
+
} else if (tag === 'MinKey' || tag === 'MaxKey') {
|
|
738
|
+
index = serializeMinMax(buffer, key, value, index);
|
|
739
|
+
} else if (typeof value._bsontype !== 'undefined') {
|
|
740
|
+
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
939
741
|
}
|
|
742
|
+
} else if (type === 'function' && serializeFunctions) {
|
|
743
|
+
index = serializeFunction(buffer, key, value, index);
|
|
940
744
|
}
|
|
941
745
|
}
|
|
942
746
|
|
|
943
|
-
// Remove the path
|
|
944
|
-
path.delete(object);
|
|
945
|
-
|
|
946
|
-
// Final padding byte for object
|
|
947
|
-
buffer[index++] = 0x00;
|
|
948
|
-
|
|
949
|
-
// Final size
|
|
950
|
-
const size = index - startingIndex;
|
|
951
|
-
// Write the size of the object
|
|
952
|
-
startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
|
|
953
747
|
return index;
|
|
954
748
|
}
|