opusdev/vector-similarity-api
1
1import { Binary, validateBinaryVector } from '../binary';2import type { BSONSymbol, DBRef, Document, MaxKey } from '../bson';3import type { Code } from '../code';4import * as constants from '../constants';5import type { DBRefLike } from '../db_ref';6import type { Decimal128 } from '../decimal128';7import type { Double } from '../double';8import { BSONError, BSONVersionError } from '../error';9import type { Int32 } from '../int_32';10import { Long } from '../long';11import type { MinKey } from '../min_key';12import type { ObjectId } from '../objectid';13import type { BSONRegExp } from '../regexp';14import { ByteUtils } from '../utils/byte_utils';15import { NumberUtils } from '../utils/number_utils';16import { isAnyArrayBuffer, isDate, isMap, isRegExp, isUint8Array } from './utils';17 18/** @public */19export interface SerializeOptions {20 /**21 * the serializer will check if keys are valid.22 * @defaultValue `false`23 */24 checkKeys?: boolean;25 /**26 * serialize the javascript functions27 * @defaultValue `false`28 */29 serializeFunctions?: boolean;30 /**31 * serialize will not emit undefined fields32 * note that the driver sets this to `false`33 * @defaultValue `true`34 */35 ignoreUndefined?: boolean;36 /** @internal Resize internal buffer */37 minInternalBufferSize?: number;38 /**39 * the index in the buffer where we wish to start serializing into40 * @defaultValue `0`41 */42 index?: number;43}44 45const regexp = /\x00/; // eslint-disable-line no-control-regex46const ignoreKeys = new Set(['$db', '$ref', '$id', '$clusterTime']);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 ascii51 * This will catch any errors in index as a string generation52 */53 54function serializeString(buffer: Uint8Array, key: string, value: string, index: number) {55 // Encode String type56 buffer[index++] = constants.BSON_DATA_STRING;57 // Number of written bytes58 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);59 // Encode the name60 index = index + numberOfWrittenBytes + 1;61 buffer[index - 1] = 0;62 // Write the string63 const size = ByteUtils.encodeUTF8Into(buffer, value, index + 4);64 // Write the size of the string to buffer65 NumberUtils.setInt32LE(buffer, index, size + 1);66 // Update index67 index = index + 4 + size;68 // Write zero69 buffer[index++] = 0;70 return index;71}72 73function serializeNumber(buffer: Uint8Array, key: string, value: number, index: number) {74 const isNegativeZero = Object.is(value, -0);75 76 const type =77 !isNegativeZero &&78 Number.isSafeInteger(value) &&79 value <= constants.BSON_INT32_MAX &&80 value >= constants.BSON_INT32_MIN81 ? constants.BSON_DATA_INT82 : constants.BSON_DATA_NUMBER;83 84 buffer[index++] = type;85 86 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);87 index = index + numberOfWrittenBytes;88 buffer[index++] = 0x00;89 90 if (type === constants.BSON_DATA_INT) {91 index += NumberUtils.setInt32LE(buffer, index, value);92 } else {93 index += NumberUtils.setFloat64LE(buffer, index, value);94 }95 96 return index;97}98 99function serializeBigInt(buffer: Uint8Array, key: string, value: bigint, index: number) {100 buffer[index++] = constants.BSON_DATA_LONG;101 // Number of written bytes102 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);103 // Encode the name104 index += numberOfWrittenBytes;105 buffer[index++] = 0;106 107 index += NumberUtils.setBigInt64LE(buffer, index, value);108 109 return index;110}111 112function serializeNull(buffer: Uint8Array, key: string, _: unknown, index: number) {113 // Set long type114 buffer[index++] = constants.BSON_DATA_NULL;115 116 // Number of written bytes117 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);118 119 // Encode the name120 index = index + numberOfWrittenBytes;121 buffer[index++] = 0;122 return index;123}124 125function serializeBoolean(buffer: Uint8Array, key: string, value: boolean, index: number) {126 // Write the type127 buffer[index++] = constants.BSON_DATA_BOOLEAN;128 // Number of written bytes129 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);130 // Encode the name131 index = index + numberOfWrittenBytes;132 buffer[index++] = 0;133 // Encode the boolean value134 buffer[index++] = value ? 1 : 0;135 return index;136}137 138function serializeDate(buffer: Uint8Array, key: string, value: Date, index: number) {139 // Write the type140 buffer[index++] = constants.BSON_DATA_DATE;141 // Number of written bytes142 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);143 // Encode the name144 index = index + numberOfWrittenBytes;145 buffer[index++] = 0;146 147 // Write the date148 const dateInMilis = Long.fromNumber(value.getTime());149 const lowBits = dateInMilis.getLowBits();150 const highBits = dateInMilis.getHighBits();151 // Encode low bits152 index += NumberUtils.setInt32LE(buffer, index, lowBits);153 // Encode high bits154 index += NumberUtils.setInt32LE(buffer, index, highBits);155 return index;156}157 158function serializeRegExp(buffer: Uint8Array, key: string, value: RegExp, index: number) {159 // Write the type160 buffer[index++] = constants.BSON_DATA_REGEXP;161 // Number of written bytes162 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);163 164 // Encode the name165 index = index + numberOfWrittenBytes;166 buffer[index++] = 0;167 if (value.source && value.source.match(regexp) != null) {168 throw new BSONError('value ' + value.source + ' must not contain null bytes');169 }170 // Adjust the index171 index = index + ByteUtils.encodeUTF8Into(buffer, value.source, index);172 // Write zero173 buffer[index++] = 0x00;174 // Write the parameters175 if (value.ignoreCase) buffer[index++] = 0x69; // i176 if (value.global) buffer[index++] = 0x73; // s177 if (value.multiline) buffer[index++] = 0x6d; // m178 179 // Add ending zero180 buffer[index++] = 0x00;181 return index;182}183 184function serializeBSONRegExp(buffer: Uint8Array, key: string, value: BSONRegExp, index: number) {185 // Write the type186 buffer[index++] = constants.BSON_DATA_REGEXP;187 // Number of written bytes188 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);189 // Encode the name190 index = index + numberOfWrittenBytes;191 buffer[index++] = 0;192 193 // Check the pattern for 0 bytes194 if (value.pattern.match(regexp) != null) {195 // The BSON spec doesn't allow keys with null bytes because keys are196 // null-terminated.197 throw new BSONError('pattern ' + value.pattern + ' must not contain null bytes');198 }199 200 // Adjust the index201 index = index + ByteUtils.encodeUTF8Into(buffer, value.pattern, index);202 // Write zero203 buffer[index++] = 0x00;204 // Write the options205 const sortedOptions = value.options.split('').sort().join('');206 index = index + ByteUtils.encodeUTF8Into(buffer, sortedOptions, index);207 // Add ending zero208 buffer[index++] = 0x00;209 return index;210}211 212function serializeMinMax(buffer: Uint8Array, key: string, value: MinKey | MaxKey, index: number) {213 // Write the type of either min or max key214 if (value === null) {215 buffer[index++] = constants.BSON_DATA_NULL;216 } else if (value._bsontype === 'MinKey') {217 buffer[index++] = constants.BSON_DATA_MIN_KEY;218 } else {219 buffer[index++] = constants.BSON_DATA_MAX_KEY;220 }221 222 // Number of written bytes223 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);224 // Encode the name225 index = index + numberOfWrittenBytes;226 buffer[index++] = 0;227 return index;228}229 230function serializeObjectId(buffer: Uint8Array, key: string, value: ObjectId, index: number) {231 // Write the type232 buffer[index++] = constants.BSON_DATA_OID;233 // Number of written bytes234 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);235 236 // Encode the name237 index = index + numberOfWrittenBytes;238 buffer[index++] = 0;239 240 index += value.serializeInto(buffer, index);241 242 // Adjust index243 return index;244}245 246function serializeBuffer(buffer: Uint8Array, key: string, value: Uint8Array, index: number) {247 // Write the type248 buffer[index++] = constants.BSON_DATA_BINARY;249 // Number of written bytes250 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);251 // Encode the name252 index = index + numberOfWrittenBytes;253 buffer[index++] = 0;254 // Get size of the buffer (current write point)255 const size = value.length;256 // Write the size of the string to buffer257 index += NumberUtils.setInt32LE(buffer, index, size);258 // Write the default subtype259 buffer[index++] = constants.BSON_BINARY_SUBTYPE_DEFAULT;260 // Copy the content form the binary field to the buffer261 if (size <= 16) {262 for (let i = 0; i < size; i++) buffer[index + i] = value[i];263 } else {264 buffer.set(value, index);265 }266 // Adjust the index267 index = index + size;268 return index;269}270 271function 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 type289 buffer[index++] = Array.isArray(value) ? constants.BSON_DATA_ARRAY : constants.BSON_DATA_OBJECT;290 // Number of written bytes291 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);292 // Encode the name293 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 path304 );305 306 path.delete(value);307 308 return endIndex;309}310 311function serializeDecimal128(buffer: Uint8Array, key: string, value: Decimal128, index: number) {312 buffer[index++] = constants.BSON_DATA_DECIMAL128;313 // Number of written bytes314 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);315 // Encode the name316 index = index + numberOfWrittenBytes;317 buffer[index++] = 0;318 // Write the data from the value319 for (let i = 0; i < 16; i++) buffer[index + i] = value.bytes[i];320 return index + 16;321}322 323function serializeLong(buffer: Uint8Array, key: string, value: Long, index: number) {324 // Write the type325 buffer[index++] =326 value._bsontype === 'Long' ? constants.BSON_DATA_LONG : constants.BSON_DATA_TIMESTAMP;327 // Number of written bytes328 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);329 // Encode the name330 index = index + numberOfWrittenBytes;331 buffer[index++] = 0;332 // Write the date333 const lowBits = value.getLowBits();334 const highBits = value.getHighBits();335 // Encode low bits336 index += NumberUtils.setInt32LE(buffer, index, lowBits);337 // Encode high bits338 index += NumberUtils.setInt32LE(buffer, index, highBits);339 return index;340}341 342function serializeInt32(buffer: Uint8Array, key: string, value: Int32 | number, index: number) {343 value = value.valueOf();344 // Set int type 32 bits or less345 buffer[index++] = constants.BSON_DATA_INT;346 // Number of written bytes347 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);348 // Encode the name349 index = index + numberOfWrittenBytes;350 buffer[index++] = 0;351 // Write the int value352 index += NumberUtils.setInt32LE(buffer, index, value);353 return index;354}355 356function serializeDouble(buffer: Uint8Array, key: string, value: Double, index: number) {357 // Encode as double358 buffer[index++] = constants.BSON_DATA_NUMBER;359 360 // Number of written bytes361 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);362 363 // Encode the name364 index = index + numberOfWrittenBytes;365 buffer[index++] = 0;366 367 // Write float368 index += NumberUtils.setFloat64LE(buffer, index, value.value);369 370 return index;371}372 373function serializeFunction(buffer: Uint8Array, key: string, value: Function, index: number) {374 buffer[index++] = constants.BSON_DATA_CODE;375 // Number of written bytes376 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);377 // Encode the name378 index = index + numberOfWrittenBytes;379 buffer[index++] = 0;380 // Function string381 const functionString = value.toString();382 383 // Write the string384 const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;385 // Write the size of the string to buffer386 NumberUtils.setInt32LE(buffer, index, size);387 // Update index388 index = index + 4 + size - 1;389 // Write zero390 buffer[index++] = 0;391 return index;392}393 394function 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 type407 buffer[index++] = constants.BSON_DATA_CODE_W_SCOPE;408 // Number of written bytes409 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);410 // Encode the name411 index = index + numberOfWrittenBytes;412 buffer[index++] = 0;413 414 // Starting index415 let startIndex = index;416 417 // Serialize the function418 // Get the function string419 const functionString = value.code;420 // Index adjustment421 index = index + 4;422 // Write string into buffer423 const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;424 // Write the size of the string to buffer425 NumberUtils.setInt32LE(buffer, index, codeSize);426 // Write end 0427 buffer[index + 4 + codeSize - 1] = 0;428 // Write the429 index = index + codeSize + 4;430 431 // Serialize the scope value432 const endIndex = serializeInto(433 buffer,434 value.scope,435 checkKeys,436 index,437 depth + 1,438 serializeFunctions,439 ignoreUndefined,440 path441 );442 index = endIndex - 1;443 444 // Writ the total445 const totalSize = endIndex - startIndex;446 447 // Write the total size of the object448 startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);449 // Write trailing zero450 buffer[index++] = 0;451 } else {452 buffer[index++] = constants.BSON_DATA_CODE;453 // Number of written bytes454 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);455 // Encode the name456 index = index + numberOfWrittenBytes;457 buffer[index++] = 0;458 // Function string459 const functionString = value.code.toString();460 // Write the string461 const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;462 // Write the size of the string to buffer463 NumberUtils.setInt32LE(buffer, index, size);464 // Update index465 index = index + 4 + size - 1;466 // Write zero467 buffer[index++] = 0;468 }469 470 return index;471}472 473function serializeBinary(buffer: Uint8Array, key: string, value: Binary, index: number) {474 // Write the type475 buffer[index++] = constants.BSON_DATA_BINARY;476 // Number of written bytes477 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);478 // Encode the name479 index = index + numberOfWrittenBytes;480 buffer[index++] = 0;481 // Extract the buffer482 const data = value.buffer;483 // Calculate size484 let size = value.position;485 // Add the deprecated 02 type 4 bytes of size to total486 if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) size = size + 4;487 // Write the size of the string to buffer488 index += NumberUtils.setInt32LE(buffer, index, size);489 // Write the subtype to the buffer490 buffer[index++] = value.sub_type;491 492 // If we have binary type 2 the 4 first bytes are the size493 if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {494 size = size - 4;495 index += NumberUtils.setInt32LE(buffer, index, size);496 }497 498 if (value.sub_type === Binary.SUBTYPE_VECTOR) {499 validateBinaryVector(value);500 }501 502 if (size <= 16) {503 for (let i = 0; i < size; i++) buffer[index + i] = data[i];504 } else {505 buffer.set(data, index);506 }507 // Adjust the index508 index = index + value.position;509 return index;510}511 512function serializeSymbol(buffer: Uint8Array, key: string, value: BSONSymbol, index: number) {513 // Write the type514 buffer[index++] = constants.BSON_DATA_SYMBOL;515 // Number of written bytes516 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);517 // Encode the name518 index = index + numberOfWrittenBytes;519 buffer[index++] = 0;520 // Write the string521 const size = ByteUtils.encodeUTF8Into(buffer, value.value, index + 4) + 1;522 // Write the size of the string to buffer523 NumberUtils.setInt32LE(buffer, index, size);524 // Update index525 index = index + 4 + size - 1;526 // Write zero527 buffer[index++] = 0;528 return index;529}530 531function serializeDBRef(532 buffer: Uint8Array,533 key: string,534 value: DBRef,535 index: number,536 depth: number,537 serializeFunctions: boolean,538 path: Set<Document>539) {540 // Write the type541 buffer[index++] = constants.BSON_DATA_OBJECT;542 // Number of written bytes543 const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);544 545 // Encode the name546 index = index + numberOfWrittenBytes;547 buffer[index++] = 0;548 549 let startIndex = index;550 let output: DBRefLike = {551 $ref: value.collection || value.namespace, // "namespace" was what library 1.x called "collection"552 $id: value.oid553 };554 555 if (value.db != null) {556 output.$db = value.db;557 }558 559 output = Object.assign(output, value.fields);560 const endIndex = serializeInto(561 buffer,562 output,563 false,564 index,565 depth + 1,566 serializeFunctions,567 true,568 path569 );570 571 // Calculate object size572 const size = endIndex - startIndex;573 // Write the size574 startIndex += NumberUtils.setInt32LE(buffer, index, size);575 // Set index576 return endIndex;577}578 579export function serializeInto(580 buffer: Uint8Array,581 object: Document,582 checkKeys: boolean,583 startingIndex: number,584 depth: number,585 serializeFunctions: boolean,586 ignoreUndefined: boolean,587 path: Set<Document> | null588): number {589 if (path == null) {590 // We are at the root input591 if (object == null) {592 // ONLY the root should turn into an empty document593 // BSON Empty document has a size of 5 (LE)594 buffer[0] = 0x05;595 buffer[1] = 0x00;596 buffer[2] = 0x00;597 buffer[3] = 0x00;598 // All documents end with null terminator599 buffer[4] = 0x00;600 return 5;601 }602 603 if (Array.isArray(object)) {604 throw new BSONError('serialize does not support an array as the root input');605 }606 if (typeof object !== 'object') {607 throw new BSONError('serialize does not support non-object as the root input');608 } else if ('_bsontype' in object && typeof object._bsontype === 'string') {609 throw new BSONError(`BSON types cannot be serialized as a document`);610 } else if (611 isDate(object) ||612 isRegExp(object) ||613 isUint8Array(object) ||614 isAnyArrayBuffer(object)615 ) {616 throw new BSONError(`date, regexp, typedarray, and arraybuffer cannot be BSON documents`);617 }618 619 path = new Set();620 }621 622 // Push the object to the path623 path.add(object);624 625 // Start place to serialize into626 let index = startingIndex + 4;627 628 // Special case isArray629 if (Array.isArray(object)) {630 // Get object keys631 for (let i = 0; i < object.length; i++) {632 const key = `${i}`;633 let value = object[i];634 635 // Is there an override value636 if (typeof value?.toBSON === 'function') {637 value = value.toBSON();638 }639 640 // Check the type of the value641 const type = typeof value;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 path673 );674 }675 } else if (type === 'object') {676 if (value[constants.BSON_VERSION_SYMBOL] !== constants.BSON_MAJOR_VERSION) {677 throw new BSONVersionError();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 path697 );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);715 }716 }717 } else if (object instanceof Map || isMap(object)) {718 const iterator = object.entries();719 let done = false;720 721 while (!done) {722 // Unpack the next entry723 const entry = iterator.next();724 done = !!entry.done;725 // Are we done, then skip and terminate726 if (done) continue;727 728 // Get the entry values729 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 value737 const type = typeof value;738 739 // Check the key and throw error if it's illegal740 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 are743 // 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 }753 }754 }755 756 if (value === undefined) {757 if (ignoreUndefined === false) index = serializeNull(buffer, key, value, index);758 } else if (value === null) {759 index = serializeNull(buffer, key, value, index);760 } else if (type === 'string') {761 index = serializeString(buffer, key, value, index);762 } else if (type === 'number') {763 index = serializeNumber(buffer, key, value, index);764 } else if (type === 'bigint') {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 path786 );787 }788 } else if (type === 'object') {789 if (value[constants.BSON_VERSION_SYMBOL] !== constants.BSON_MAJOR_VERSION) {790 throw new BSONVersionError();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 path810 );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);828 }829 }830 } else {831 if (typeof object?.toBSON === 'function') {832 // Provided a custom serialization method833 object = object.toBSON();834 if (object != null && typeof object !== 'object') {835 throw new BSONError('toBSON function did not return an object');836 }837 }838 839 // Iterate over all the keys840 for (const key of Object.keys(object)) {841 let value = object[key];842 // Is there an override value843 if (typeof value?.toBSON === 'function') {844 value = value.toBSON();845 }846 847 // Check the type of the value848 const type = typeof value;849 850 // Check the key and throw error if it's illegal851 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 are854 // 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 }864 }865 }866 867 if (value === undefined) {868 if (ignoreUndefined === false) index = serializeNull(buffer, key, value, index);869 } else if (value === null) {870 index = serializeNull(buffer, key, value, index);871 } else if (type === 'string') {872 index = serializeString(buffer, key, value, index);873 } else if (type === 'number') {874 index = serializeNumber(buffer, key, value, index);875 } else if (type === 'bigint') {876 index = serializeBigInt(buffer, key, value, index);877 } else if (type === 'boolean') {878 index = serializeBoolean(buffer, key, value, index);879 } else if (type === 'object' && value._bsontype == null) {880 if (value instanceof Date || isDate(value)) {881 index = serializeDate(buffer, key, value, index);882 } else if (value instanceof Uint8Array || isUint8Array(value)) {883 index = serializeBuffer(buffer, key, value, index);884 } else if (value instanceof RegExp || isRegExp(value)) {885 index = serializeRegExp(buffer, key, value, index);886 } else {887 index = serializeObject(888 buffer,889 key,890 value,891 index,892 checkKeys,893 depth,894 serializeFunctions,895 ignoreUndefined,896 path897 );898 }899 } else if (type === 'object') {900 if (value[constants.BSON_VERSION_SYMBOL] !== constants.BSON_MAJOR_VERSION) {901 throw new BSONVersionError();902 } else if (value._bsontype === 'ObjectId') {903 index = serializeObjectId(buffer, key, value, index);904 } else if (value._bsontype === 'Decimal128') {905 index = serializeDecimal128(buffer, key, value, index);906 } else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {907 index = serializeLong(buffer, key, value, index);908 } else if (value._bsontype === 'Double') {909 index = serializeDouble(buffer, key, value, index);910 } else if (value._bsontype === 'Code') {911 index = serializeCode(912 buffer,913 key,914 value,915 index,916 checkKeys,917 depth,918 serializeFunctions,919 ignoreUndefined,920 path921 );922 } else if (value._bsontype === 'Binary') {923 index = serializeBinary(buffer, key, value, index);924 } else if (value._bsontype === 'BSONSymbol') {925 index = serializeSymbol(buffer, key, value, index);926 } else if (value._bsontype === 'DBRef') {927 index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);928 } else if (value._bsontype === 'BSONRegExp') {929 index = serializeBSONRegExp(buffer, key, value, index);930 } else if (value._bsontype === 'Int32') {931 index = serializeInt32(buffer, key, value, 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)}`);936 }937 } else if (type === 'function' && serializeFunctions) {938 index = serializeFunction(buffer, key, value, index);939 }940 }941 }942 943 // Remove the path944 path.delete(object);945 946 // Final padding byte for object947 buffer[index++] = 0x00;948 949 // Final size950 const size = index - startingIndex;951 // Write the size of the object952 startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);953 return index;954}955 