opusdev/vector-similarity-api
1
1import { BSONValue } from './bson_value';2import { BSONError } from './error';3import type { EJSONOptions } from './extended_json';4import { type InspectFn, defaultInspect } from './parser/utils';5 6/** @public */7export interface DoubleExtended {8 $numberDouble: string;9}10 11/**12 * A class representation of the BSON Double type.13 * @public14 * @category BSONType15 */16export class Double extends BSONValue {17 get _bsontype(): 'Double' {18 return 'Double';19 }20 21 value!: number;22 /**23 * Create a Double type24 *25 * @param value - the number we want to represent as a double.26 */27 constructor(value: number) {28 super();29 if ((value as unknown) instanceof Number) {30 value = value.valueOf();31 }32 33 this.value = +value;34 }35 36 /**37 * Attempt to create an double type from string.38 *39 * This method will throw a BSONError on any string input that is not representable as a IEEE-754 64-bit double.40 * Notably, this method will also throw on the following string formats:41 * - Strings in non-decimal and non-exponential formats (binary, hex, or octal digits)42 * - Strings with characters other than numeric, floating point, or leading sign characters (Note: 'Infinity', '-Infinity', and 'NaN' input strings are still allowed)43 * - Strings with leading and/or trailing whitespace44 *45 * Strings with leading zeros, however, are also allowed46 *47 * @param value - the string we want to represent as a double.48 */49 static fromString(value: string): Double {50 const coercedValue = Number(value);51 52 if (value === 'NaN') return new Double(NaN);53 if (value === 'Infinity') return new Double(Infinity);54 if (value === '-Infinity') return new Double(-Infinity);55 56 if (!Number.isFinite(coercedValue)) {57 throw new BSONError(`Input: ${value} is not representable as a Double`);58 }59 if (value.trim() !== value) {60 throw new BSONError(`Input: '${value}' contains whitespace`);61 }62 if (value === '') {63 throw new BSONError(`Input is an empty string`);64 }65 if (/[^-0-9.+eE]/.test(value)) {66 throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`);67 }68 return new Double(coercedValue);69 }70 71 /**72 * Access the number value.73 *74 * @returns returns the wrapped double number.75 */76 valueOf(): number {77 return this.value;78 }79 80 toJSON(): number {81 return this.value;82 }83 84 toString(radix?: number): string {85 return this.value.toString(radix);86 }87 88 /** @internal */89 toExtendedJSON(options?: EJSONOptions): number | DoubleExtended {90 if (options && (options.legacy || (options.relaxed && isFinite(this.value)))) {91 return this.value;92 }93 94 if (Object.is(Math.sign(this.value), -0)) {95 // NOTE: JavaScript has +0 and -0, apparently to model limit calculations. If a user96 // explicitly provided `-0` then we need to ensure the sign makes it into the output97 return { $numberDouble: '-0.0' };98 }99 100 return {101 $numberDouble: Number.isInteger(this.value) ? this.value.toFixed(1) : this.value.toString()102 };103 }104 105 /** @internal */106 static fromExtendedJSON(doc: DoubleExtended, options?: EJSONOptions): number | Double {107 const doubleValue = parseFloat(doc.$numberDouble);108 return options && options.relaxed ? doubleValue : new Double(doubleValue);109 }110 111 inspect(depth?: number, options?: unknown, inspect?: InspectFn): string {112 inspect ??= defaultInspect;113 return `new Double(${inspect(this.value, options)})`;114 }115}116 