import type { BSONType, ObjectIdLike } from 'bson'; import { EventEmitter } from 'events'; import type { Binary, BSONRegExp, Decimal128, Document, Double, Int32, Long, ObjectId, Timestamp } from './bson'; import type { MongoLoggableComponent, MongoLogger } from './mongo_logger'; import type { Sort } from './sort'; /** @internal */ export type TODO_NODE_3286 = any; /** Given an object shaped type, return the type of the _id field or default to ObjectId @public */ export type InferIdType = TSchema extends { _id: infer IdType } ? // user has defined a type for _id Record extends IdType ? never // explicitly forbid empty objects as the type of _id : IdType : TSchema extends { _id?: infer IdType } ? // optional _id defined - return ObjectId | IdType unknown extends IdType ? ObjectId // infer the _id type as ObjectId if the type of _id is unknown : IdType : ObjectId; // user has not defined _id on schema /** Add an _id field to an object shaped type @public */ export type WithId = EnhancedOmit & { _id: InferIdType }; /** * Add an optional _id field to an object shaped type * @public */ export type OptionalId = EnhancedOmit & { _id?: InferIdType }; /** * Adds an optional _id field to an object shaped type, unless the _id field is required on that type. * In the case _id is required, this method continues to require_id. * * @public * * @privateRemarks * `ObjectId extends TSchema['_id']` is a confusing ordering at first glance. Rather than ask * `TSchema['_id'] extends ObjectId` which translated to "Is the _id property ObjectId?" * we instead ask "Does ObjectId look like (have the same shape) as the _id?" */ export type OptionalUnlessRequiredId = TSchema extends { _id: any } ? TSchema : OptionalId; /** TypeScript Omit (Exclude to be specific) does not work for objects with an "any" indexed type, and breaks discriminated unions @public */ export type EnhancedOmit = string extends keyof TRecordOrUnion ? TRecordOrUnion // TRecordOrUnion has indexed type e.g. { _id: string; [k: string]: any; } or it is "any" : TRecordOrUnion extends any ? Pick> // discriminated unions : never; /** Remove the _id field from an object shaped type @public */ export type WithoutId = Omit; /** A MongoDB filter can be some portion of the schema or a set of operators @public */ export type Filter = { [P in keyof WithId]?: Condition[P]>; } & RootFilterOperators>; /** @public */ export type Condition = AlternativeType | FilterOperators>; /** * It is possible to search using alternative types in mongodb e.g. * string types can be searched using a regex in mongo * array types can be searched using their element type * @public */ export type AlternativeType = T extends ReadonlyArray ? T | RegExpOrString : RegExpOrString; /** @public */ export type RegExpOrString = T extends string ? BSONRegExp | RegExp | T : T; /** @public */ export interface RootFilterOperators extends Document { $and?: Filter[]; $nor?: Filter[]; $or?: Filter[]; $text?: { $search: string; $language?: string; $caseSensitive?: boolean; $diacriticSensitive?: boolean; }; $where?: string | ((this: TSchema) => boolean); $comment?: string | Document; } /** * @public * A type that extends Document but forbids anything that "looks like" an object id. */ export type NonObjectIdLikeDocument = { [key in keyof ObjectIdLike]?: never; } & Document; /** @public */ export interface FilterOperators extends NonObjectIdLikeDocument { // Comparison $eq?: TValue; $gt?: TValue; $gte?: TValue; $in?: ReadonlyArray; $lt?: TValue; $lte?: TValue; $ne?: TValue; $nin?: ReadonlyArray; // Logical $not?: TValue extends string ? FilterOperators | RegExp : FilterOperators; // Element /** * When `true`, `$exists` matches the documents that contain the field, * including documents where the field value is null. */ $exists?: boolean; $type?: BSONType | BSONTypeAlias; // Evaluation $expr?: Record; $jsonSchema?: Record; $mod?: TValue extends number ? [number, number] : never; $regex?: TValue extends string ? RegExp | BSONRegExp | string : never; $options?: TValue extends string ? string : never; // Geospatial $geoIntersects?: { $geometry: Document }; $geoWithin?: Document; $near?: Document; $nearSphere?: Document; $maxDistance?: number; // Array $all?: ReadonlyArray; $elemMatch?: Document; $size?: TValue extends ReadonlyArray ? number : never; // Bitwise $bitsAllClear?: BitwiseFilter; $bitsAllSet?: BitwiseFilter; $bitsAnyClear?: BitwiseFilter; $bitsAnySet?: BitwiseFilter; $rand?: Record; } /** @public */ export type BitwiseFilter = | number /** numeric bit mask */ | Binary /** BinData bit mask */ | ReadonlyArray; /** `[ , , ... ]` */ /** @public */ export type BSONTypeAlias = keyof typeof BSONType; /** @public */ export type IsAny = true extends false & Type ? ResultIfAny : ResultIfNotAny; /** @public */ export type Flatten = Type extends ReadonlyArray ? Item : Type; /** @public */ export type ArrayElement = Type extends ReadonlyArray ? Item : never; /** @public */ export type SchemaMember = { [P in keyof T]?: V } | { [key: string]: V }; /** @public */ export type IntegerType = number | Int32 | Long | bigint; /** @public */ export type NumericType = IntegerType | Decimal128 | Double; /** @public */ export type FilterOperations = T extends Record ? { [key in keyof T]?: FilterOperators } : FilterOperators; /** @public */ export type KeysOfAType = { [key in keyof TSchema]: NonNullable extends Type ? key : never; }[keyof TSchema]; /** @public */ export type KeysOfOtherType = { [key in keyof TSchema]: NonNullable extends Type ? never : key; }[keyof TSchema]; /** @public */ export type AcceptedFields = { readonly [key in KeysOfAType]?: AssignableType; }; /** It avoids using fields with not acceptable types @public */ export type NotAcceptedFields = { readonly [key in KeysOfOtherType]?: never; }; /** @public */ export type OnlyFieldsOfType = IsAny< TSchema[keyof TSchema], Record, AcceptedFields & NotAcceptedFields & Record >; /** @public */ export type MatchKeysAndValues = Readonly> & Record; /** @public */ export type AddToSetOperators = { $each?: Array>; }; /** @public */ export type ArrayOperator = { $each?: Array>; $slice?: number; $position?: number; $sort?: Sort; }; /** @public */ export type SetFields = ({ readonly [key in KeysOfAType | undefined>]?: | OptionalId> | AddToSetOperators>>>; } & NotAcceptedFields | undefined>) & { readonly [key: string]: AddToSetOperators | any; }; /** @public */ export type PushOperator = ({ readonly [key in KeysOfAType>]?: | Flatten | ArrayOperator>>; } & NotAcceptedFields>) & { readonly [key: string]: ArrayOperator | any; }; /** @public */ export type PullOperator = ({ readonly [key in KeysOfAType>]?: | Partial> | FilterOperations>; } & NotAcceptedFields>) & { readonly [key: string]: FilterOperators | any; }; /** @public */ export type PullAllOperator = ({ readonly [key in KeysOfAType>]?: TSchema[key]; } & NotAcceptedFields>) & { readonly [key: string]: ReadonlyArray; }; /** @public */ export type UpdateFilter = { $currentDate?: OnlyFieldsOfType< TSchema, Date | Timestamp, true | { $type: 'date' | 'timestamp' } >; $inc?: OnlyFieldsOfType; $min?: MatchKeysAndValues; $max?: MatchKeysAndValues; $mul?: OnlyFieldsOfType; $rename?: Record; $set?: MatchKeysAndValues; $setOnInsert?: MatchKeysAndValues; $unset?: OnlyFieldsOfType; $addToSet?: SetFields; $pop?: OnlyFieldsOfType, 1 | -1>; $pull?: PullOperator; $push?: PushOperator; $pullAll?: PullAllOperator; $bit?: OnlyFieldsOfType< TSchema, NumericType | undefined, { and: IntegerType } | { or: IntegerType } | { xor: IntegerType } >; } & Document; /** @public */ export type Nullable = AnyType | null | undefined; /** @public */ export type OneOrMore = T | ReadonlyArray; /** @public */ export type GenericListener = (...args: any[]) => void; /** * Event description type * @public */ export type EventsDescription = Record; /** @public */ export type CommonEvents = 'newListener' | 'removeListener'; /** * Typescript type safe event emitter * @public */ export declare interface TypedEventEmitter extends EventEmitter { addListener(event: EventKey, listener: Events[EventKey]): this; addListener( event: CommonEvents, listener: (eventName: string | symbol, listener: GenericListener) => void ): this; addListener(event: string | symbol, listener: GenericListener): this; on(event: EventKey, listener: Events[EventKey]): this; on( event: CommonEvents, listener: (eventName: string | symbol, listener: GenericListener) => void ): this; on(event: string | symbol, listener: GenericListener): this; once(event: EventKey, listener: Events[EventKey]): this; once( event: CommonEvents, listener: (eventName: string | symbol, listener: GenericListener) => void ): this; once(event: string | symbol, listener: GenericListener): this; removeListener(event: EventKey, listener: Events[EventKey]): this; removeListener( event: CommonEvents, listener: (eventName: string | symbol, listener: GenericListener) => void ): this; removeListener(event: string | symbol, listener: GenericListener): this; off(event: EventKey, listener: Events[EventKey]): this; off( event: CommonEvents, listener: (eventName: string | symbol, listener: GenericListener) => void ): this; off(event: string | symbol, listener: GenericListener): this; removeAllListeners( event?: EventKey | CommonEvents | symbol | string ): this; listeners( event: EventKey | CommonEvents | symbol | string ): Events[EventKey][]; rawListeners( event: EventKey | CommonEvents | symbol | string ): Events[EventKey][]; emit( event: EventKey | symbol, ...args: Parameters ): boolean; listenerCount( type: EventKey | CommonEvents | symbol | string ): number; prependListener(event: EventKey, listener: Events[EventKey]): this; prependListener( event: CommonEvents, listener: (eventName: string | symbol, listener: GenericListener) => void ): this; prependListener(event: string | symbol, listener: GenericListener): this; prependOnceListener( event: EventKey, listener: Events[EventKey] ): this; prependOnceListener( event: CommonEvents, listener: (eventName: string | symbol, listener: GenericListener) => void ): this; prependOnceListener(event: string | symbol, listener: GenericListener): this; eventNames(): string[]; getMaxListeners(): number; setMaxListeners(n: number): this; } /** * Typescript type safe event emitter * @public */ export class TypedEventEmitter extends EventEmitter { /** @internal */ protected mongoLogger?: MongoLogger; /** @internal */ protected component?: MongoLoggableComponent; /** @internal */ protected emitAndLog( event: EventKey | symbol, ...args: Parameters ): void { this.emit(event, ...args); if (this.component) this.mongoLogger?.debug(this.component, args[0]); } } /** @public */ export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> {} /** * Helper types for dot-notation filter attributes */ /** @public */ export type Join = T extends [] ? '' : T extends [string | number] ? `${T[0]}` : T extends [string | number, ...infer R] ? `${T[0]}${D}${Join}` : string; /** @public */ export type PropertyType = string extends Property ? unknown : Property extends keyof Type ? Type[Property] : Property extends `${number}` ? Type extends ReadonlyArray ? ArrayType : unknown : Property extends `${infer Key}.${infer Rest}` ? Key extends `${number}` ? Type extends ReadonlyArray ? PropertyType : unknown : Key extends keyof Type ? Type[Key] extends Map ? MapType : PropertyType : unknown : unknown; /** * @public * returns tuple of strings (keys to be joined on '.') that represent every path into a schema * https://www.mongodb.com/docs/manual/tutorial/query-embedded-documents/ * * @remarks * Through testing we determined that a depth of 8 is safe for the typescript compiler * and provides reasonable compilation times. This number is otherwise not special and * should be changed if issues are found with this level of checking. Beyond this * depth any helpers that make use of NestedPaths should devolve to not asserting any * type safety on the input. */ export type NestedPaths = Depth['length'] extends 8 ? [] : Type extends | string | number | bigint | boolean | Date | RegExp | Buffer | Uint8Array | ((...args: any[]) => any) | { _bsontype: string } ? [] : Type extends ReadonlyArray ? [] | [number, ...NestedPaths] : Type extends Map ? [string] : Type extends object ? { [Key in Extract]: Type[Key] extends Type // type of value extends the parent ? [Key] : // for a recursive union type, the child will never extend the parent type. // but the parent will still extend the child Type extends Type[Key] ? [Key] : Type[Key] extends ReadonlyArray // handling recursive types with arrays ? Type extends ArrayType // is the type of the parent the same as the type of the array? ? [Key] // yes, it's a recursive array type : // for unions, the child type extends the parent ArrayType extends Type ? [Key] // we have a recursive array union : // child is an array, but it's not a recursive array [Key, ...NestedPaths] : // child is not structured the same as the parent [Key, ...NestedPaths] | [Key]; }[Extract] : []; /** * @public * returns keys (strings) for every path into a schema with a value of type * https://www.mongodb.com/docs/manual/tutorial/query-embedded-documents/ */ export type NestedPathsOfType = KeysOfAType< { [Property in Join, '.'>]: PropertyType; }, Type >; /** * @public * @experimental */ export type StrictFilter = | Partial | ({ [Property in Join, []>, '.'>]?: Condition< PropertyType, Property> >; } & RootFilterOperators>); /** * @public * @experimental */ export type StrictUpdateFilter = { $currentDate?: OnlyFieldsOfType< TSchema, Date | Timestamp, true | { $type: 'date' | 'timestamp' } >; $inc?: OnlyFieldsOfType; $min?: StrictMatchKeysAndValues; $max?: StrictMatchKeysAndValues; $mul?: OnlyFieldsOfType; $rename?: Record; $set?: StrictMatchKeysAndValues; $setOnInsert?: StrictMatchKeysAndValues; $unset?: OnlyFieldsOfType; $addToSet?: SetFields; $pop?: OnlyFieldsOfType, 1 | -1>; $pull?: PullOperator; $push?: PushOperator; $pullAll?: PullAllOperator; $bit?: OnlyFieldsOfType< TSchema, NumericType | undefined, { and: IntegerType } | { or: IntegerType } | { xor: IntegerType } >; } & Document; /** * @public * @experimental */ export type StrictMatchKeysAndValues = Readonly< { [Property in Join, '.'>]?: PropertyType; } & { [Property in `${NestedPathsOfType}.$${`[${string}]` | ''}`]?: ArrayElement< PropertyType >; } & { [Property in `${NestedPathsOfType[]>}.$${ | `[${string}]` | ''}.${string}`]?: any; // Could be further narrowed } & Document >;