175 lines
4.7 KiB
TypeScript
175 lines
4.7 KiB
TypeScript
import type { BSONRegExp, BSONType } from "bson";
|
|
|
|
export type Document = {
|
|
[key: string]: any;
|
|
};
|
|
|
|
export type WithId<TSchema> = {
|
|
id: string;
|
|
} & TSchema;
|
|
|
|
export type Filter<TSchema> = {
|
|
[P in keyof TSchema]?: Condition<TSchema[P]>;
|
|
} & RootFilterOperators<TSchema> &
|
|
Record<string, any>;
|
|
|
|
export type UpdateFilter<TSchema> = {
|
|
$inc?: OnlyFieldsOfType<TSchema, number>;
|
|
$set?: MatchKeysAndValues<TSchema> | MatchKeysToFunctionValues<TSchema> | Record<string, any>;
|
|
$unset?: OnlyFieldsOfType<TSchema, any, "" | true | 1>;
|
|
$pull?: PullOperator<TSchema>;
|
|
$push?: PushOperator<TSchema>;
|
|
};
|
|
|
|
type RootFilterOperators<TSchema> = {
|
|
$and?: Filter<TSchema>[];
|
|
$nor?: Filter<TSchema>[];
|
|
$or?: Filter<TSchema>[];
|
|
$text?: {
|
|
$search: string;
|
|
$language?: string;
|
|
$caseSensitive?: boolean;
|
|
$diacriticSensitive?: boolean;
|
|
};
|
|
$where?: string | ((this: TSchema) => boolean);
|
|
$comment?: string | Document;
|
|
};
|
|
|
|
type Condition<T> = AlternativeType<T> | FilterOperators<AlternativeType<T>>;
|
|
|
|
type AlternativeType<T> = T extends ReadonlyArray<infer U> ? T | RegExpOrString<U> : RegExpOrString<T>;
|
|
|
|
type RegExpOrString<T> = T extends string ? BSONRegExp | RegExp | T : T;
|
|
|
|
type FilterOperators<TValue> = {
|
|
$eq?: TValue;
|
|
$gt?: TValue;
|
|
$gte?: TValue;
|
|
$in?: ReadonlyArray<TValue>;
|
|
$lt?: TValue;
|
|
$lte?: TValue;
|
|
$ne?: TValue;
|
|
$nin?: ReadonlyArray<TValue>;
|
|
$not?: TValue extends string ? FilterOperators<TValue> | RegExp : FilterOperators<TValue>;
|
|
/**
|
|
* When `true`, `$exists` matches the documents that contain the field,
|
|
* including documents where the field value is null.
|
|
*/
|
|
$exists?: boolean;
|
|
$type?: BSONType | BSONTypeAlias;
|
|
$expr?: Record<string, any>;
|
|
$jsonSchema?: Record<string, any>;
|
|
$mod?: TValue extends number ? [number, number] : never;
|
|
$regex?: TValue extends string ? RegExp | string : never;
|
|
$options?: TValue extends string ? string : never;
|
|
$geoIntersects?: {
|
|
$geometry: Document;
|
|
};
|
|
$geoWithin?: Document;
|
|
$near?: Document;
|
|
$nearSphere?: Document;
|
|
$maxDistance?: number;
|
|
$all?: ReadonlyArray<any>;
|
|
$elemMatch?: Document;
|
|
$size?: TValue extends ReadonlyArray<any> ? number : never;
|
|
$bitsAllClear?: BitwiseFilter;
|
|
$bitsAllSet?: BitwiseFilter;
|
|
$bitsAnyClear?: BitwiseFilter;
|
|
$bitsAnySet?: BitwiseFilter;
|
|
$rand?: Record<string, never>;
|
|
};
|
|
|
|
type BSONTypeAlias = keyof typeof BSONType;
|
|
|
|
type BitwiseFilter = number | ReadonlyArray<number>;
|
|
|
|
type OnlyFieldsOfType<TSchema, FieldType = any, AssignableType = FieldType> = IsAny<
|
|
TSchema[keyof TSchema],
|
|
Record<string, FieldType>,
|
|
AcceptedFields<TSchema, FieldType, AssignableType> &
|
|
NotAcceptedFields<TSchema, FieldType> &
|
|
Record<string, AssignableType>
|
|
>;
|
|
|
|
type MatchKeysAndValues<TSchema> = Readonly<Partial<TSchema>>;
|
|
|
|
type MatchKeysToFunctionValues<TSchema> = {
|
|
readonly [key in keyof TSchema]?: (this: TSchema, value: TSchema[key]) => TSchema[key];
|
|
};
|
|
|
|
type PullOperator<TSchema> = ({
|
|
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
|
|
| Partial<Flatten<TSchema[key]>>
|
|
| FilterOperations<Flatten<TSchema[key]>>;
|
|
} & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
readonly [key: string]: FilterOperators<any> | any;
|
|
};
|
|
|
|
type PushOperator<TSchema> = ({
|
|
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
|
|
| Flatten<TSchema[key]>
|
|
| ArrayOperator<Array<Flatten<TSchema[key]>>>;
|
|
} & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
readonly [key: string]: ArrayOperator<any> | any;
|
|
};
|
|
|
|
type KeysOfAType<TSchema, Type> = {
|
|
[key in keyof TSchema]: NonNullable<TSchema[key]> extends Type ? key : never;
|
|
}[keyof TSchema];
|
|
|
|
type AcceptedFields<TSchema, FieldType, AssignableType> = {
|
|
readonly [key in KeysOfAType<TSchema, FieldType>]?: AssignableType;
|
|
};
|
|
|
|
type NotAcceptedFields<TSchema, FieldType> = {
|
|
readonly [key in KeysOfOtherType<TSchema, FieldType>]?: never;
|
|
};
|
|
|
|
type Flatten<Type> = Type extends ReadonlyArray<infer Item> ? Item : Type;
|
|
|
|
type IsAny<Type, ResultIfAny, ResultIfNotAny> = true extends false & Type ? ResultIfAny : ResultIfNotAny;
|
|
|
|
type FilterOperations<T> = T extends Record<string, any>
|
|
? {
|
|
[key in keyof T]?: FilterOperators<T[key]>;
|
|
}
|
|
: FilterOperators<T>;
|
|
|
|
type ArrayOperator<Type> = {
|
|
$each?: Array<Flatten<Type>>;
|
|
$slice?: number;
|
|
$position?: number;
|
|
$sort?: Sort;
|
|
};
|
|
|
|
type Sort =
|
|
| string
|
|
| Exclude<
|
|
SortDirection,
|
|
{
|
|
$meta: string;
|
|
}
|
|
>
|
|
| string[]
|
|
| {
|
|
[key: string]: SortDirection;
|
|
}
|
|
| Map<string, SortDirection>
|
|
| [string, SortDirection][]
|
|
| [string, SortDirection];
|
|
|
|
type SortDirection =
|
|
| 1
|
|
| -1
|
|
| "asc"
|
|
| "desc"
|
|
| "ascending"
|
|
| "descending"
|
|
| {
|
|
$meta: string;
|
|
};
|
|
|
|
type KeysOfOtherType<TSchema, Type> = {
|
|
[key in keyof TSchema]: NonNullable<TSchema[key]> extends Type ? never : key;
|
|
}[keyof TSchema];
|