UNPKG

lincd

Version:

LINCD is a JavaScript library for building user interfaces with linked data (also known as 'structured data', or RDF)

419 lines (418 loc) 24.3 kB
import { Shape, ShapeType } from '../shapes/Shape.js'; import { TestNode } from '../utils/TraceShape.js'; import { PropertyShape } from '../shapes/SHACL.js'; import { ShapeSet } from '../collections/ShapeSet.js'; import { CoreSet } from '../collections/CoreSet.js'; import { LinkedComponent, LinkedSetComponent } from '../utils/LinkedComponent.js'; import { CoreMap } from '../collections/CoreMap.js'; import { NodeReferenceValue, Prettify, QueryFactory, ShapeReferenceValue } from './QueryFactory.js'; import { NamedNode } from '../models.js'; /** * ################################### * #### TYPES FOR QUERY BUILDING #### * ################################### */ export type JSPrimitive = JSNonNullPrimitive | null | undefined; export type JSNonNullPrimitive = string | number | boolean | Date; export type SingleResult<ResultType> = ResultType extends Array<infer R> ? R : ResultType extends Set<infer R> ? R : ResultType; /** * All the possible types that a regular get/set method of a Shape can return */ export type AccessorReturnValue = Shape | ShapeSet | JSPrimitive | TestNode; export type WhereClause<S extends Shape | AccessorReturnValue> = Evaluation | ((s: ToQueryBuilderObject<S>) => Evaluation); export type QueryBuildFn<T extends Shape, ResponseType> = (p: ToQueryBuilderObject<T>, q: SelectQueryFactory<T>) => ResponseType; export type QueryWrapperObject<ShapeType extends Shape = any> = { [key: string]: SelectQueryFactory<ShapeType>; }; export type CustomQueryObject = { [key: string]: QueryPath; }; export type SelectPath = QueryPath[] | CustomQueryObject; export type SortByPath = { paths: QueryPath[]; direction: 'ASC' | 'DESC'; }; /** * A LinkedQuery is used to build a query, when complete it can be turned into a LinkedQueryObject * that is used to send across the network as it can be serialized to JSON * @todo add | UpdateQuery and others */ export interface LinkedQuery { type: string; } export type SubQueryPaths = SelectPath; /** * A QueryPath is an array of QuerySteps, representing the path of properties that were requested to reach a certain value */ export type QueryPath = (QueryStep | SubQueryPaths)[] | WherePath; /** * A plain JS object that represents a LinkedQuery created by a Shape.select(...) call * It can be sent across the network. * @see LinkedQuery */ export interface SelectQuery<S extends Shape = Shape, ResultType = any> extends LinkedQuery { select: SelectPath; where?: WherePath; sortBy?: SortByPath; subject?: S | QResult<S>; limit?: number; offset?: number; shape?: ShapeType<S>; singleResult?: boolean; } /** * Much like a querypath, except it can only contain QuerySteps */ export type QueryPropertyPath = QueryStep[]; /** * A QueryStep is a single step in a query path * It contains the property that was requested, and optionally a where clause */ export type QueryStep = PropertyQueryStep | SizeStep | CustomQueryObject | ShapeReferenceValue; export type SizeStep = { count: QueryPropertyPath; label?: string; }; export type PropertyQueryStep = { property: PropertyShape; where?: WherePath; }; export type WhereAndOr = { firstPath: WherePath; andOr: AndOrQueryToken[]; }; /** * A WhereQuery is a (sub)query that is used to filter down the results of its parent query * Hence it extends LinkedQuery and can do anything a normal query can */ export type AndOrQueryToken = { and?: WherePath; or?: WherePath; }; export declare enum WhereMethods { EQUALS = "=", SOME = "some", EVERY = "every" } /** * Maps all the return types of get/set methods of a Shape and maps their return types to QueryBuilderObjects */ export type QueryShapeProps<T extends Shape, Source, Property extends string | number | symbol = any> = { [P in keyof T]: ToQueryBuilderObject<T[P], QShape<T, Source, Property>, P>; }; /** * This type states that the ShapeSet has access to the same methods as the shape of all the items in the set * (this is enabled with the QueryShapeSet.proxifyShapeSet method) * Each value of the shape is converted to a QueryBuilderObject */ export type QueryShapeSetProps<SourceShapeSet, Shape> = { [P in keyof Shape]: ToQueryBuilderObject<Shape[P], SourceShapeSet, P>; }; /** * ShapeSets are converted to QueryShapeSets, but also inherit all the properties of the shape that each item in the set has (with converted result types) */ export type QShapeSet<ShapeSetType extends Shape, Source = null, Property extends string | number | symbol = null> = QueryShapeSet<ShapeSetType, Source, Property> & QueryShapeSetProps<QueryShapeSet<ShapeSetType, Source, Property>, ShapeSetType>; /** * Shapes are converted to QueryShapes, but also inherit all the properties of the shape (with converted result types) */ export type QShape<T extends Shape, Source = any, Property extends string | number | symbol = any> = QueryShape<T, Source, Property> & QueryShapeProps<T, Source, Property>; export type ToQueryBuilderObject<T, Source = null, Property extends string | number | symbol = ''> = T extends ShapeSet<infer ShapeSetType> ? QShapeSet<ShapeSetType, Source, Property> : T extends Shape ? QShape<T, Source, Property> : T extends string | number | Date | boolean ? ToQueryPrimitive<T, Source, Property> : T extends Array<infer AT> ? AT extends Date | string | number ? QueryPrimitiveSet<ToQueryPrimitive<AT, Source, Property>> : AT extends boolean ? QueryBoolean : AT[] : T extends NamedNode ? QShape<Shape, Source, Property> : QueryBuilderObject<T, Source, Property>; export type ToQueryPrimitive<T extends string | number | Date | boolean, Source, Property extends string | number | symbol = ''> = T extends string ? QueryString<Source, Property> : T extends number ? QueryNumber<Source, Property> : T extends Date ? QueryDate<Source, Property> : T extends boolean ? QueryBoolean<Source, Property> : never; export type WherePath = WhereEvaluationPath | WhereAndOr; export type WhereEvaluationPath = { path: QueryPropertyPath; method: WhereMethods; args: QueryArg[]; }; /** * An argument can be a direct reference to a node, a js primitive (boolean,number), a path to resolve (like from a query context variables) * Or a wherePath in the case of some() or every() (e.g. x.where(x.friends.some(f => f.age > 18) -> the argument is a wherePath) */ export type QueryArg = NodeReferenceValue | JSNonNullPrimitive | ArgPath | WherePath; export type ArgPath = { path: QueryPropertyPath; subject: ShapeReferenceValue; }; export type ComponentQueryPath = (QueryStep | SubQueryPaths)[] | WherePath; /** * ################################### * #### QUERY RESULT TYPES #### * ################################### */ export type NodeResultMap = CoreMap<string, QResult<any, any>>; export type QResult<ShapeType extends Shape = Shape, Object = {}> = Object & { id: string; }; export type QueryProps<Q extends SelectQueryFactory<any>> = Q extends SelectQueryFactory<infer ShapeType, infer ResponseType> ? QueryResponseToResultType<ResponseType, ShapeType> : never; export type QueryControllerProps = { query?: QueryController; }; export type QueryController = { nextPage: () => void; previousPage: () => void; setLimit: (limit: number) => void; setPage: (page: number) => void; }; export type PatchedQueryPromise<ResultType, ShapeType extends Shape> = { where(validation: WhereClause<ShapeType>): PatchedQueryPromise<ResultType, ShapeType>; limit(lim: number): PatchedQueryPromise<ResultType, ShapeType>; sortBy(sortParam: any, direction?: 'ASC' | 'DESC'): PatchedQueryPromise<ResultType, ShapeType>; one(): PatchedQueryPromise<SingleResult<ResultType>, ShapeType>; } & Promise<ResultType>; export type GetCustomObjectKeys<T> = T extends QueryWrapperObject ? { [P in keyof T]: T[P] extends SelectQueryFactory<any> ? ToQueryResultSet<T[P]> : never; } : []; export type QueryIndividualResultType<T extends SelectQueryFactory<any>> = T extends SelectQueryFactory<infer ShapeType, infer ResponseType> ? QueryResponseToResultType<ResponseType, ShapeType> : null; export type ToQueryResultSet<T> = T extends SelectQueryFactory<infer ShapeType, infer ResponseType> ? QueryResponseToResultType<ResponseType, ShapeType>[] : null; /** * MAIN ENTRY to convert the response of a query into a result object */ export type QueryResponseToResultType<T, QShapeType extends Shape = null, HasName = false> = T extends QueryBuilderObject ? GetQueryObjectResultType<T, {}, false, HasName> : T extends SelectQueryFactory<any, infer Response, infer Source> ? GetNestedQueryResultType<Response, Source> : T extends Array<infer Type> ? UnionToIntersection<QueryResponseToResultType<Type>> : T extends Evaluation ? boolean : T extends Object ? QResult<QShapeType, Prettify<ObjectToPlainResult<T>>> : never; /** * Turns a QueryBuilderObject into a plain JS object * @param QV the query value type * @param SubProperties to add extra properties into the result object (used to merge arrays into objects for example) * @param SourceOverwrite if the source of the query value should be overwritten */ export type GetQueryObjectResultType<QV, SubProperties = {}, PrimitiveArray = false, HasName = false> = QV extends SetSize<infer Source> ? SetSizeToQueryResult<Source, HasName> : QV extends QueryPrimitive<infer Primitive, infer Source, infer Property> ? CreateQResult<Source, PrimitiveArray extends true ? Primitive[] : Primitive, Property, {}, HasName> : QV extends QueryShape<infer ShapeType, infer Source, infer Property> ? CreateQResult<Source, ShapeType, Property, SubProperties, HasName> : QV extends BoundComponent<infer Source, infer ShapeType, infer ComponentResultType> ? GetQueryObjectResultType<Source, SubProperties & ComponentResultType, PrimitiveArray, HasName> : QV extends QueryShapeSet<infer ShapeType, infer Source, infer Property> ? CreateShapeSetQResult<ShapeType, Source, Property, SubProperties, HasName> : QV extends QueryPrimitiveSet<infer QPrim extends QueryPrimitive<any>> ? GetQueryObjectResultType<QPrim, null, null, true> : QV extends Array<infer Type> ? UnionToIntersection<QueryResponseToResultType<Type>> : QV extends QueryBoolean<any, any> ? 'bool' : never; export type GetShapesResultTypeWithSource<Source> = QueryResponseToResultType<Source>; export type SetSizeToQueryResult<Source, HasName = false> = Source extends QueryShapeSet<infer ShapeType, infer ParentSource, infer SourceProperty> ? HasName extends false ? CreateQResult<ParentSource, number, SourceProperty> : number : number; /** * If the source is an object (it extends shape) * then the result is a plain JS Object, with Property as its key, with type Value */ export type CreateQResult<Source, Value = undefined, Property extends string | number | symbol = '', SubProperties = {}, HasName = false> = Source extends QueryShape<infer SourceShapeType, infer ParentSource, infer SourceProperty> ? ParentSource extends null ? HasName extends true ? Value : Value extends null ? QResult<SourceShapeType, { [P in Property]: CreateQResult<Value, Value>; } & SubProperties> : QResult<SourceShapeType, { [P in Property]: CreateQResult<Value, Value, '', SubProperties>; }> : CreateQResult<ParentSource, QResult<SourceShapeType, { [P in Property]: CreateQResult<Value, Value>; } & SubProperties>, SourceProperty, {}, HasName> : Source extends QueryShapeSet<infer ShapeType, infer ParentSource, infer SourceProperty> ? CreateQResult<ParentSource, QResult<ShapeType, { [P in Property]: CreateQResult<Value, Value, null, SubProperties>; }>[], SourceProperty, {}, HasName> : Value extends Shape ? QResult<Value, SubProperties> : NormaliseBoolean<Value>; type NormaliseBoolean<T> = [T] extends [boolean] ? boolean : T; export type CreateShapeSetQResult<ShapeType = undefined, Source = undefined, Property extends string | number | symbol = '', SubProperties = {}, HasName = false> = Source extends QueryShape<infer SourceShapeType, infer ParentSource> ? [ HasName, ParentSource ] extends [true, null] ? CreateQResult<Source, null, null>[] : QResult<SourceShapeType, { [P in Property]: CreateQResult<Source, null, null, SubProperties>[]; }> : Source extends QueryShapeSet<infer ShapeType, infer ParentSource, infer SourceProperty> ? CreateQResult<ParentSource, QResult<ShapeType, { [P in Property]: CreateQResult<ShapeType>[]; }>[], SourceProperty, {}, HasName> : CreateQResult<ShapeType>; /** * Ignores the source and property, and returns the converted value */ export type ObjectToPlainResult<T> = { [P in keyof T]: QueryResponseToResultType<T[P], null, true>; }; export type GetSource<Source, Overwrite> = Overwrite extends null ? Source : Overwrite; type GetNestedQueryResultType<Response, Source> = Source extends QueryBuilderObject ? GetQueryObjectResultType<Source, QueryResponseToResultType<Response>> : QueryResponseToResultType<Response>[]; type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never; export type GetQueryResponseType<Q> = Q extends SelectQueryFactory<any, infer ResponseType> ? ResponseType : Q; export type GetQueryShapeType<Q> = Q extends SelectQueryFactory<infer ShapeType, infer ResponseType> ? ShapeType : never; export type QueryResponseToEndValues<T> = T extends SetSize ? number[] : T extends SelectQueryFactory<any, infer Response> ? QueryResponseToEndValues<Response>[] : T extends QueryShapeSet<infer ShapeType> ? ShapeSet<ShapeType> : T extends QueryShape<infer ShapeType> ? ShapeType : T extends QueryString ? string[] : T extends Array<infer ArrType> ? Array<QueryResponseToEndValues<ArrType>> : T extends Evaluation ? boolean[] : T; /** * ################################### * #### QUERY BUILDING CLASSES #### * ################################### */ export declare class QueryBuilderObject<OriginalValue = any, Source = any, Property extends string | number | symbol = any> { property?: PropertyShape; subject?: QueryShape<any> | QueryShapeSet<any> | QueryPrimitiveSet; wherePath?: WherePath; protected originalValue?: OriginalValue; protected source: Source; protected prop: Property; constructor(property?: PropertyShape, subject?: QueryShape<any> | QueryShapeSet<any> | QueryPrimitiveSet); /** * Converts an original value into a query value * @param originalValue * @param requestedPropertyShape the property shape that is connected to the get accessor that returned the original value */ static convertOriginal(originalValue: AccessorReturnValue, property: PropertyShape, subject: QueryShape<any> | QueryShapeSet<any> | QueryShape<any>): QueryBuilderObject; /** * Create a Query Builder Object based on the requested PropertyShape */ static generatePathValue(property: PropertyShape, subject: QueryShape<any> | QueryShapeSet<any> | QueryShape<any>): QueryBuilderObject; static getOriginalSource(endValue: ShapeSet<Shape> | Shape[] | QueryPrimitiveSet): ShapeSet; static getOriginalSource(endValue: Shape): Shape; static getOriginalSource(endValue: QueryString): Shape | string; static getOriginalSource(endValue: string[] | QueryBuilderObject): Shape | ShapeSet; getOriginalValue(): OriginalValue; getPropertyStep(): QueryStep; preloadFor<ShapeType extends Shape, CompQueryRes>(component: LinkedComponent<any, ShapeType, CompQueryRes> | LinkedSetComponent<any, ShapeType, CompQueryRes>): BoundComponent<this, ShapeType, CompQueryRes>; limit(lim: number): void; /** * Returns the path of properties that were requested to reach this value */ getPropertyPath(currentPath?: QueryPropertyPath): QueryPropertyPath; } export declare class QueryShapeSet<S extends Shape = Shape, Source = any, Property extends string | number | symbol = any> extends QueryBuilderObject<ShapeSet<S>, Source, Property> { queryShapes: CoreSet<QueryShape>; private proxy; constructor(_originalValue?: ShapeSet<S>, property?: PropertyShape, subject?: QueryShape<any> | QueryShapeSet<any>); static create<S extends Shape = Shape>(originalValue: ShapeSet<S>, property: PropertyShape, subject: QueryShape<any> | QueryShapeSet<any>): any; static proxifyShapeSet<T extends Shape = Shape>(queryShapeSet: QueryShapeSet<T>): any; as<ShapeClass extends typeof Shape>(shape: ShapeClass): QShapeSet<InstanceType<ShapeClass>, Source, Property>; add(item: any): void; concat(other: QueryShapeSet): QueryShapeSet; filter(filterFn: any): QueryShapeSet; setSource(val: boolean): void; getOriginalValue(): ShapeSet<S>; callPropertyShapeAccessor(propertyShape: PropertyShape): QueryShapeSet | QueryPrimitiveSet; size(): SetSize<this>; where(validation: WhereClause<S>): this; select<QF = unknown>(subQueryFn: QueryBuildFn<S, QF>): SelectQueryFactory<S, QF, QueryShapeSet<S, Source, Property>>; some(validation: WhereClause<S>): SetEvaluation; every(validation: WhereClause<S>): SetEvaluation; private someOrEvery; } export declare class QueryShape<S extends Shape = Shape, Source = any, Property extends string | number | symbol = any> extends QueryBuilderObject<S, Source, Property> { originalValue: S; isSource: boolean; private proxy; constructor(originalValue: S, property?: PropertyShape, subject?: QueryShape<any> | QueryShapeSet<any>); get id(): any; static create(original: Shape, property?: PropertyShape, subject?: QueryShape<any> | QueryShapeSet<any>): any; private static proxifyQueryShape; as<ShapeClass extends typeof Shape>(shape: ShapeClass): QShape<InstanceType<ShapeClass>, Source, Property>; equals(otherValue: NodeReferenceValue | QShape<any>): Evaluation; select<QF = unknown>(subQueryFn: QueryBuildFn<S, QF>): SelectQueryFactory<S, QF, QueryShape<S, Source, Property>>; } export declare class BoundComponent<Source extends QueryBuilderObject, ShapeType extends Shape, CompQueryResult = any> extends QueryBuilderObject { originalValue: LinkedComponent<any, ShapeType, CompQueryResult> | LinkedSetComponent<any, ShapeType, CompQueryResult>; source: Source; constructor(originalValue: LinkedComponent<any, ShapeType, CompQueryResult> | LinkedSetComponent<any, ShapeType, CompQueryResult>, source: Source); getParentQueryFactory(): SelectQueryFactory<any>; getPropertyPath(): QueryPropertyPath; } export declare class Evaluation { value: QueryBuilderObject | QueryPrimitiveSet; method: WhereMethods; args: QueryArg[]; private _andOr; constructor(value: QueryBuilderObject | QueryPrimitiveSet, method: WhereMethods, args: QueryArg[]); getPropertyPath(): WherePath; processArgs(): QueryArg[]; getWherePath(): WherePath; and(subQuery: WhereClause<any>): this; or(subQuery: WhereClause<any>): this; } declare class SetEvaluation extends Evaluation { } /** * The class that is used for when JS primitives are converted to a QueryValue * This is extended by QueryString, QueryNumber, QueryBoolean, etc */ export declare abstract class QueryPrimitive<T, Source = any, Property extends string | number | symbol = any> extends QueryBuilderObject<T, Source, Property> { originalValue?: T; property?: PropertyShape; subject?: QueryShape<any> | QueryShapeSet<any> | QueryPrimitiveSet; constructor(originalValue?: T, property?: PropertyShape, subject?: QueryShape<any> | QueryShapeSet<any> | QueryPrimitiveSet); equals(otherValue: JSPrimitive | QueryBuilderObject): Evaluation; where(validation: WhereClause<string>): this; } export declare class QueryString<Source = any, Property extends string | number | symbol = ''> extends QueryPrimitive<string, Source, Property> { } export declare class QueryDate<Source = any, Property extends string | number | symbol = any> extends QueryPrimitive<Date, Source, Property> { } export declare class QueryNumber<Source = any, Property extends string | number | symbol = any> extends QueryPrimitive<number, Source, Property> { } export declare class QueryBoolean<Source = any, Property extends string | number | symbol = any> extends QueryPrimitive<boolean, Source, Property> { } export declare class QueryPrimitiveSet<QPrimitive extends QueryPrimitive<any> = null> extends QueryBuilderObject<any, any, any> { originalValue?: JSNonNullPrimitive[]; property?: PropertyShape; subject?: QueryShapeSet<any> | QueryShape<any>; contents: CoreSet<QPrimitive>; constructor(originalValue?: JSNonNullPrimitive[], property?: PropertyShape, subject?: QueryShapeSet<any> | QueryShape<any>, items?: any); add(item: any): void; values(): QPrimitive[]; createNew(...args: any[]): this; equals(other: any): Evaluation; getPropertyStep(): QueryStep; getPropertyPath(): QueryPropertyPath; size(): SetSize<this>; } export declare var onQueriesReady: (callback: any) => void; export declare class SelectQueryFactory<S extends Shape, ResponseType = any, Source = any> extends QueryFactory { shape: ShapeType<S>; private queryBuildFn?; subject?: S | ShapeSet<S> | QResult<S>; /** * The returned value when the query was initially run. * Will likely be an array or object or query values that can be used to trace back which methods/accessors were used in the query. * @private */ traceResponse: ResponseType; sortResponse: any; sortDirection: string; parentQueryPath: QueryPath; singleResult: boolean; private limit; private offset; private wherePath; private initPromise; debugStack: string; constructor(shape: ShapeType<S>, queryBuildFn?: QueryBuildFn<S, ResponseType>, subject?: S | ShapeSet<S> | QResult<S>); setLimit(limit: number): void; getLimit(): number; setOffset(offset: number): void; getOffset(): number; setSubject(subject: any): this; where(validation: WhereClause<S>): this; exec(): Promise<QueryResponseToResultType<ResponseType>[]>; /** * Turns the LinkedQuery into a SelectQuery, which is a plain JS object that can be serialized to JSON */ getQueryObject(): SelectQuery<S>; getSubject(): { id: string; } | S | ShapeSet<S>; /** * Returns an array of query paths * A single query can request multiple things in multiple "query paths" (For example this is using 2 paths: Shape.select(p => [p.name, p.friends.name])) * Each query path is returned as array of the property paths requested, with potential where clauses (together called a QueryStep) */ getQueryPaths(response?: ResponseType): CustomQueryObject | QueryPath[]; isValidSetResult(qResults: QResult<any>[]): boolean; isValidResult(qResult: QResult<any>): boolean; clone(): SelectQueryFactory<S, ResponseType, any>; /** * Makes a clone of the query template, sets the subject and executes the query * @param subject */ execFor(subject: any): Promise<QueryResponseToResultType<ResponseType, null, false>[]>; patchResultPromise<ResultType>(p: Promise<ResultType>): PatchedQueryPromise<any, S>; sortBy<R>(sortFn: QueryBuildFn<S, R>, direction: any): this; private init; private initialized; /** * Returns the dummy shape instance who's properties can be accessed freely inside a queryBuildFn * It is used to trace the properties that are accessed in the queryBuildFn * @private */ private getQueryShape; private getSortByPath; private isValidQueryPathsResult; private isValidQueryPathResult; private isValidQueryStepResult; private isValidCustomObjectResult; } export declare class SetSize<Source = null> extends QueryNumber<Source> { subject: QueryShapeSet | QueryShape | QueryPrimitiveSet; countable?: QueryBuilderObject; label?: string; constructor(subject: QueryShapeSet | QueryShape | QueryPrimitiveSet, countable?: QueryBuilderObject, label?: string); as(label: string): this; getPropertyPath(): QueryPropertyPath; } /** * A sub query that is used to filter results * i.e p.friends.where(f => //LinkedWhereQuery here) */ export declare class LinkedWhereQuery<S extends Shape, ResponseType = any> extends SelectQueryFactory<S, ResponseType> { getResponse(): Evaluation; getWherePath(): WherePath; } export {};