UNPKG

odata-builder

Version:

odata builder for easier and typesafe usage

252 lines (241 loc) 12 kB
type PrevDepth$1<T extends number> = [never, 0, 1, 2, 3, 4, 5][T]; type StringFunctionDefinition<T> = { readonly type: 'concat'; readonly values: ReadonlyArray<string | FieldReference<T, string>>; } | { readonly type: 'contains'; readonly value: string | FieldReference<T, string>; } | { readonly type: 'endswith'; readonly value: string | FieldReference<T, string>; } | { readonly type: 'indexof'; readonly value: string | FieldReference<T, string>; } | { readonly type: 'length'; } | { readonly type: 'startswith'; readonly value: string | FieldReference<T, string>; } | { readonly type: 'substring'; readonly start: number | FieldReference<T, number>; readonly length?: number | FieldReference<T, number>; } | { readonly type: 'tolower'; } | { readonly type: 'toupper'; } | { readonly type: 'trim'; }; type ArithmeticOperator = 'add' | 'sub' | 'mul' | 'div' | 'mod'; type ArithmeticFunctionDefinition<T> = { readonly type: ArithmeticOperator; readonly operand: number | FieldReference<T, number>; } | { readonly type: 'round' | 'floor' | 'ceiling'; }; type DateFunctionDefinition<T> = { readonly type: 'now'; } | { readonly type: 'date'; readonly field: FieldReference<T, Date>; } | { readonly type: 'time'; readonly field: FieldReference<T, Date>; } | { readonly type: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'; }; type AnySupportedFunction<T> = StringFunctionDefinition<T> | ArithmeticFunctionDefinition<T> | DateFunctionDefinition<T>; type ODataDateStringLiteral = string & { __brand: 'ODataDateStringLiteral'; }; type ODataTimeStringLiteral = string & { __brand: 'ODataTimeStringLiteral'; }; type FunctionReturnType<F extends AnySupportedFunction<any>> = F extends { type: 'length' | 'indexof'; } ? number : F extends { type: 'contains' | 'startswith' | 'endswith'; } ? boolean : F extends { type: 'tolower' | 'toupper' | 'trim' | 'substring' | 'concat'; } ? string : F extends { type: 'round' | 'floor' | 'ceiling'; } ? number : F extends { type: ArithmeticOperator; } ? number : F extends { type: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'; } ? number : F extends { type: 'now'; } ? Date : F extends { type: 'date'; } ? ODataDateStringLiteral : F extends { type: 'time'; } ? ODataTimeStringLiteral : never; type GeneralFilterOperators = 'eq' | 'ne'; type ComparisonOperators = 'gt' | 'ge' | 'lt' | 'le'; type StringAsFunctionOperators = 'contains' | 'startswith' | 'endswith' | 'substringof' | 'indexof'; type StandardStringFilterOperators = GeneralFilterOperators | StringAsFunctionOperators; type ComparableFilterOperators = GeneralFilterOperators | ComparisonOperators; type BooleanFilterOperators = GeneralFilterOperators; type GuidFilterOperators = GeneralFilterOperators; type FilterOperators<V> = V extends ODataDateStringLiteral | ODataTimeStringLiteral ? ComparableFilterOperators : V extends Date ? ComparableFilterOperators : V extends number ? ComparableFilterOperators : V extends Guid ? GuidFilterOperators : V extends string ? StandardStringFilterOperators : V extends boolean ? BooleanFilterOperators : GeneralFilterOperators; interface BaseFilterCore<T, VFieldType> { readonly field: FilterFields<T, VFieldType>; readonly transform?: ReadonlyArray<PropertyTransform<VFieldType>>; } type StandardFilter<T, VFieldType> = BaseFilterCore<T, VFieldType> & ({ readonly function?: undefined; readonly operator: FilterOperators<VFieldType>; readonly value: VFieldType | null; readonly ignoreCase?: VFieldType extends string ? boolean : never; readonly removeQuotes?: VFieldType extends string | Guid ? boolean : never; } | (VFieldType extends string ? { readonly function: StringFunctionDefinition<T>; readonly operator: FilterOperators<FunctionReturnType<StringFunctionDefinition<T>>>; readonly value: FunctionReturnType<StringFunctionDefinition<T>> | null; readonly ignoreCase?: never; readonly removeQuotes?: FunctionReturnType<StringFunctionDefinition<T>> extends string ? boolean : never; } : never) | (VFieldType extends number ? { readonly function: ArithmeticFunctionDefinition<T>; readonly operator: FilterOperators<FunctionReturnType<ArithmeticFunctionDefinition<T>>>; readonly value: FunctionReturnType<ArithmeticFunctionDefinition<T>> | null; readonly ignoreCase?: never; readonly removeQuotes?: never; } : never) | (VFieldType extends Date ? { readonly function: DateFunctionDefinition<T>; readonly operator: FilterOperators<FunctionReturnType<DateFunctionDefinition<T>>>; readonly value: FunctionReturnType<DateFunctionDefinition<T>> | null; readonly ignoreCase?: never; readonly removeQuotes?: FunctionReturnType<DateFunctionDefinition<T>> extends string ? boolean : never; } : never)); type DirectBooleanODataFunctionFilter<T, VFieldType extends string> = BaseFilterCore<T, VFieldType> & { readonly function: Extract<StringFunctionDefinition<T>, { type: 'contains' | 'startswith' | 'endswith'; }>; readonly operator?: BooleanFilterOperators; readonly value?: boolean | null; readonly ignoreCase?: never; readonly removeQuotes?: never; }; type ComparisonFunctionFilter_Internal<T, VFieldType, F extends AnySupportedFunction<T>, FRT> = BaseFilterCore<T, VFieldType> & { readonly function: F; readonly operator: FilterOperators<FRT>; readonly value: (FRT extends ODataDateStringLiteral | ODataTimeStringLiteral ? string : FRT) | null; readonly ignoreCase?: never; readonly removeQuotes?: FRT extends ODataDateStringLiteral | ODataTimeStringLiteral | string ? boolean : never; }; type ComparisonFunctionFilterGenerator<T, VFieldType> = { [FuncKey in AnySupportedFunction<T>['type']]: FuncKey extends 'contains' | 'startswith' | 'endswith' ? never : ComparisonFunctionFilter_Internal<T, VFieldType, Extract<AnySupportedFunction<T>, { type: FuncKey; }>, FunctionReturnType<Extract<AnySupportedFunction<T>, { type: FuncKey; }>>>; }[AnySupportedFunction<T>['type']]; type StringQueryFilter<T> = StandardFilter<T, string> | DirectBooleanODataFunctionFilter<T, string> | Extract<ComparisonFunctionFilterGenerator<T, string>, { function: StringFunctionDefinition<T>; }>; type NumberQueryFilter<T> = StandardFilter<T, number> | Extract<ComparisonFunctionFilterGenerator<T, number>, { function: ArithmeticFunctionDefinition<T>; }>; type DateQueryFilter<T> = StandardFilter<T, Date> | Extract<ComparisonFunctionFilterGenerator<T, Date>, { function: DateFunctionDefinition<T>; }>; type GuidQueryFilter<T> = StandardFilter<T, Guid>; type BooleanQueryFilter<T> = StandardFilter<T, boolean>; type QueryFilter<T> = StringQueryFilter<T> | NumberQueryFilter<T> | DateQueryFilter<T> | GuidQueryFilter<T> | BooleanQueryFilter<T> | LambdaFilter<T>; type PropertyTransform<V> = V extends string ? StringTransform : V extends number ? NumberTransform : V extends Date ? DateTransform : V extends Guid ? GuidTransform : never; type LambdaFilter<T> = { readonly [K in ArrayFields<T>]: { readonly field: K; readonly lambdaOperator: 'any' | 'all'; readonly expression: QueryFilter<ArrayElement<T, K>> | CombinedFilter<ArrayElement<T, K>>; }; }[ArrayFields<T>]; type FieldReference<T, V extends string | number | Date | boolean> = { readonly fieldReference: FilterFields<T, V>; }; type ArrayFields<T> = { [K in keyof T]-?: NonNullable<T[K]> extends ReadonlyArray<unknown> ? K : never; }[keyof T]; type ArrayElement<T, K extends ArrayFields<T>> = NonNullable<T[K]> extends ReadonlyArray<infer U> ? U : never; type Join<K, P> = K extends string | number ? P extends string | number ? P extends '' ? `${K}` : K extends '' ? `${P}` : `${K}/${P}` : never : never; type Paths<T, VALUETYPE, D extends number = 5> = D extends 0 ? never : { [K in keyof T]-?: K extends string ? NonNullable<T[K]> extends VALUETYPE ? K : NonNullable<T[K]> extends ReadonlyArray<VALUETYPE> ? K : NonNullable<T[K]> extends ReadonlyArray<infer U> ? U extends object ? VALUETYPE extends U ? K : never : never : NonNullable<T[K]> extends object ? VALUETYPE extends NonNullable<T[K]> ? K : Join<K, Paths<NonNullable<T[K]>, VALUETYPE, PrevDepth$1<D>>> : never : never; }[keyof T]; type FilterFields<T, VALUETYPE, Depth extends number = 5> = Paths<T, VALUETYPE, Depth>; type LambdaFilterFields<T, VALUETYPE> = { [K in Extract<keyof T, string>]: NonNullable<T[K]> extends ReadonlyArray<infer U_ITEM> ? U_ITEM extends object ? FilterFields<U_ITEM, VALUETYPE> : never : never; }[Extract<keyof T, string>]; type StringTransform = 'tolower' | 'toupper' | 'trim'; type DateTransform = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'; type NumberTransform = 'round' | 'floor' | 'ceiling'; type GuidTransform = 'tolower'; interface CombinedFilter<T> { logic: 'and' | 'or'; filters: Array<QueryFilter<T> | CombinedFilter<T>>; } type ExpandFields<T, Depth extends number = 5> = Depth extends 0 ? never : { [K in Extract<keyof T, string>]: NonNullable<T[K]> extends object ? HasKeys<NonNullable<T[K]>> extends true ? K | (Depth extends 1 ? never : `${K}/${ExpandFields<NonNullable<T[K]>, PrevDepth<Depth>>}`) : never : never; }[Extract<keyof T, string>]; type Guid = string & { _type: Guid; }; type HasKeys<T> = [keyof T] extends [never] ? false : true; type PrevDepth<T extends number> = [ never, 0, 1, 2, 3, 4 ][T]; interface OrderByDescriptor<T> { field: OrderByFields<T>; orderDirection: 'asc' | 'desc'; } type OrderByFields<T, Depth extends number = 5> = [Depth] extends [never] ? never : { [K in Extract<keyof T, string>]-?: T[K] extends Record<string, unknown> ? K | (string extends OrderByFields<T[K], PrevDepth<Depth>> ? never : `${K}/${OrderByFields<T[K], PrevDepth<Depth>> & string}`) : K; }[Extract<keyof T, string>]; type SearchTerm = string & { readonly __brand: 'SearchTerm'; }; interface SearchPhrase { readonly phrase: string; } type SearchOperator = 'AND' | 'OR' | 'NOT'; interface SearchGroup { readonly expression: SearchExpression; } type SearchExpressionPart = SearchTerm | SearchPhrase | SearchOperator | SearchGroup; type SearchExpression = readonly SearchExpressionPart[]; declare class SearchExpressionBuilder { private readonly parts; constructor(parts?: SearchExpression); term(value: string): SearchExpressionBuilder; phrase(value: string): SearchExpressionBuilder; and(): SearchExpressionBuilder; or(): SearchExpressionBuilder; not(expressionBuilder: SearchExpressionBuilder): SearchExpressionBuilder; group(builder: SearchExpressionBuilder): SearchExpressionBuilder; build(): SearchExpression; toString(): string; equals(other: SearchExpressionBuilder): boolean; private stringifyPart; } declare class OdataQueryBuilder<T> { private queryComponents; top(topCount: number): this; skip(skipCount: number): this; select(...selectProps: ReadonlyArray<Extract<keyof Required<T>, string>>): this; filter(...filters: ReadonlyArray<CombinedFilter<Required<T>> | QueryFilter<Required<T>>>): this; expand(...expandFields: ReadonlyArray<ExpandFields<Required<T>>>): this; count(countEntities?: boolean): this; orderBy(...orderByInput: ReadonlyArray<OrderByDescriptor<Required<T>> | null | undefined>): this; search(searchExpression: string | SearchExpressionBuilder | null | undefined): this; toQuery(): string; private addComponent; } declare const isCombinedFilter: <T>(filters: unknown) => filters is CombinedFilter<T>; declare const isQueryFilter: <T>(filter: unknown) => filter is QueryFilter<T>; export { OdataQueryBuilder, SearchExpressionBuilder, isCombinedFilter, isQueryFilter }; export type { CombinedFilter, ExpandFields, FilterFields, FilterOperators, Guid, LambdaFilterFields, OrderByDescriptor, OrderByFields, QueryFilter, SearchExpression, SearchPhrase, SearchTerm };