CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
serializer.ts955 linesDownload Raw Back to parser
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