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
TypeScript
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 {};