lincd
Version:
LINCD is a JavaScript library for building user interfaces with linked data (also known as 'structured data', or RDF)
472 lines (470 loc) • 25.1 kB
TypeScript
import { NamedNode, Node, Quad } from '../models.js';
import { NodeValuesSet } from '../collections/NodeValuesSet.js';
import { NodeSet } from '../collections/NodeSet.js';
import { QuadArray } from '../collections/QuadArray.js';
import { IShape } from '../interfaces/IShape.js';
import { ShapeSet } from '../collections/ShapeSet.js';
import { ICoreIterable } from '../interfaces/ICoreIterable.js';
import { SearchMap } from '../collections/SearchMap.js';
import { CoreSet } from '../collections/CoreSet.js';
import { QuadSet } from '../collections/QuadSet.js';
import type { NodeShape, PropertyShape } from './SHACL.js';
import { ShapeValuesSet } from '../collections/ShapeValuesSet.js';
import { GetQueryResponseType, PatchedQueryPromise, QResult, QShape, QueryBuildFn, QueryResponseToResultType, SelectQueryFactory } from '../queries/SelectQuery.js';
import { IQueryParser } from '../interfaces/IQueryParser.js';
import { AddId, NodeReferenceValue, UpdatePartial } from '../queries/QueryFactory.js';
import { ClassOf } from '../utils/Types.js';
import { CreateResponse } from '../queries/CreateQuery.js';
import { NodeId } from '../queries/MutationQuery.js';
import { DeleteResponse } from '../queries/DeleteQuery.js';
interface IClassConstruct {
prototype: any;
new (): any;
}
type AccessPropertiesShape<T extends Shape> = {
[P in keyof T]: PropertyShape;
};
type PropertyShapeMapFunction<T extends Shape, ResponseType> = (p: AccessPropertiesShape<T>) => ResponseType;
/**
* The base class of all classes that represent a rdfs:Class in the graph.
*
* This class helps form a bridge between the graph (RDF) world & the Object-Oriented typescript world.
* Each Shape class has a static type property pointing to the rdfs:Class that it represents.
* Each instance of a class that extends this Shape class points to a single node (NamedNode or Literal), that MUST have this rdfs:Class as its rdf:type in the graph.
*
* Classes that extend this class can thereby help simplify interactions with nodes that have a certain rdf:type by replacing low level property access (NamedNode.getAll(), getOne() etc) with high level methods that do not require knowledge of the underlying graph structure.
*
* @example
* An Example:
* ```tsx
* @linkedShape
* class Person extends Shape {
* static type = foaf.Person
* get friends() {
* return this.getAll(foaf.hasFriend)
* }
* }
*
* let personNode = NamedNode.getOrCreate();
* personNode.set(rdf.type,foaf.Person);
*
* //creates an instance of the class Person, which points to (represents) personResource.
* let person = new Person(personNode);
*
* //will log all the friends of the personResource (currently none)
* console.log(person.friends);
* ```
*/
export declare abstract class Shape implements IShape {
/**
* Points to the rdfs:Class that this typescript class represents. Each class extending Shape MUST define this explicitly.
The appointed NamedNode value must be a rdfs:Class ([value] rdf:type rdfs:Class in the graph)
@example
An example Shape class that states that all matching nodes must have `rdf:type foaf:Person`.
```tsx
import {foaf} from "./ontologies/foaf";
@linkedShape
export class Person extends Shape {
static targetClass:NamedNode = foaf.Person;
}
```
*/
static targetClass: NamedNode;
static queryParser: IQueryParser;
/**
* Tracks which types (named nodes) map to which Shapes
* @internal
*/
static typesToShapes: Map<NamedNode, CoreSet<IClassConstruct>>;
static shape: NodeShape;
protected static instancesLoaded: Map<NamedNode, {
promise: Promise<NodeSet<NamedNode>>;
done: boolean;
}>;
protected loadPromise: {
done: boolean;
promise: Promise<boolean>;
};
/**
* Creates a new instance of this class.
* If no node is given, a new NamedNode will be generated and it's rdf:type will be set.
* Only use this constructor directly if you want to create a new node as well.
* If you want to create an instance of an existing node, use `node.getAs(Class)` or `Class.getOf(node)`
* @param node
*/
constructor(node?: Node | any);
protected _node: Node;
/**
* Returns the node this instance represents.
*
* Since each node in RDF can have multiple types, each node can have multiple instances (multiple representations of itself reflecting the different things it 'is')
* But each instance always only represents a single node
*/
get node(): Node;
/**
* returns the rdf:Class that this type of instance represents.
*/
get nodeShape(): NodeShape;
/**
* Returns the NamedNode that this instance represents.
*
* Since each node in RDF can have multiple types, each node can have multiple instances (multiple representations of itself reflecting the different things it 'is')
* But each instance always only represents a single node
*
* NOTE: the node of an instance is NOT GUARANTEED to be a NamedNode. There are also instance of Literals.
* Therefore only use this method if you are certain that the instance you have represents a NamedNode.
* In that case this method - which works exactly the same as `.node` - simply tells the compiler that the return node is certainly a NamedNode.
*/
get namedNode(): NamedNode;
get value(): string;
get uri(): string;
get label(): string;
set label(val: string);
static create<ShapeType extends Shape, U extends UpdatePartial<ShapeType>>(this: {
new (node: Node): ShapeType;
queryParser: IQueryParser;
}, updateObjectOrFn?: U): Promise<CreateResponse<U>>;
static delete<ShapeType extends Shape, U extends UpdatePartial<ShapeType>>(this: {
new (node: Node): ShapeType;
queryParser: IQueryParser;
}, id: NodeId | NodeId[] | NodeReferenceValue[]): Promise<DeleteResponse>;
/**
* @internal
* @param shapeClass
* @param type
*/
static registerByType(shapeClass: typeof Shape, type?: NamedNode): void;
/**
* Get a the matching shape classes that have a targetClass equal to the given type node
* @internal
* @param type
* @param allowSuperClass
*/
static getClassesForType(type: NamedNode, allowSuperClass?: boolean): CoreSet<typeof Shape>;
static isValidNode(node: Node): boolean;
static query<S extends Shape, R = unknown>(this: {
new (node: Node): S;
targetClass: any;
}, subject: S | QShape<S> | QResult<S>, queryFn: QueryBuildFn<S, R>): SelectQueryFactory<S, R>;
static query<S extends Shape, R = unknown>(this: {
new (node: Node): S;
targetClass: any;
}, queryFn: QueryBuildFn<S, R>): SelectQueryFactory<S, R>;
/**
* Select properties of instances of this shape.
* Returns a single result if a single subject is provided, or an array of results if no subjects are provided.
* The select function (first or second argument) receives a proxy of the shape that allows you to virtually access any property you want up to any level of depth.
* @param selectFn
*/
static select<ShapeType extends Shape, S = unknown, ResultType = QueryResponseToResultType<S, ShapeType>[]>(this: {
new (node: Node): ShapeType;
queryParser: IQueryParser;
}, selectFn: QueryBuildFn<ShapeType, S>): Promise<ResultType> & PatchedQueryPromise<ResultType, ShapeType>;
static select<ShapeType extends Shape, S = unknown, ResultType = QueryResponseToResultType<GetQueryResponseType<SelectQueryFactory<ShapeType, S>>, ShapeType>[]>(this: {
new (node: Node): ShapeType;
queryParser: IQueryParser;
}): Promise<ResultType> & PatchedQueryPromise<ResultType, ShapeType>;
static select<ShapeType extends Shape, S = unknown, ResultType = QueryResponseToResultType<GetQueryResponseType<SelectQueryFactory<ShapeType, S>>, ShapeType>>(this: {
new (node: Node): ShapeType;
queryParser: IQueryParser;
}, subjects?: ShapeType | QResult<ShapeType>, selectFn?: QueryBuildFn<ShapeType, S>): Promise<ResultType> & PatchedQueryPromise<ResultType, ShapeType>;
static select<ShapeType extends Shape, S = unknown, ResultType = QueryResponseToResultType<GetQueryResponseType<SelectQueryFactory<ShapeType, S>>, ShapeType>[]>(this: {
new (node: Node): ShapeType;
queryParser: IQueryParser;
}, subjects?: ICoreIterable<ShapeType> | QResult<ShapeType>[], selectFn?: QueryBuildFn<ShapeType, S>): Promise<ResultType> & PatchedQueryPromise<ResultType, ShapeType>;
static update<ShapeType extends Shape, U extends UpdatePartial<ShapeType>>(this: {
new (node: Node): ShapeType;
queryParser: IQueryParser;
}, id: string | {
id: string;
} | {
uri: string;
} | QShape<ShapeType>, updateObjectOrFn?: U): Promise<AddId<U>>;
static mapPropertyShapes<ShapeType extends Shape, ResponseType = unknown>(this: {
new (node: Node): ShapeType;
targetClass: any;
}, mapFunction?: PropertyShapeMapFunction<ShapeType, ResponseType>): ResponseType;
static isInstanceOfTargetClass(node: Node): boolean;
static getInstanceByType<T extends IShape>(node: Node, ...shapes: {
new (): T;
targetClass: NamedNode;
getOf(node: Node): T;
}[]): T;
/**
* Searches instances with the given properties only from the local graph
* @param properties
* @param sanitized
*/
static searchLocal<T extends Shape>(this: {
new (node: Node): T;
targetClass: any;
}, properties: SearchMap, sanitized?: boolean): ShapeSet<T>;
/**
* Searches instances with given properties
* And if results are returned, it returns an instance of the first result, else null
* @param properties
*/
static findLocal<T extends Shape>(this: {
new (node: Node): T;
targetClass: any;
}, properties: SearchMap, sanitized?: boolean): T;
/**
* Finds all the instances whos rdf:type matches the targetClass of this shape
* Ignores if the nodes are valid instances of the shape
* Returns a set of shape instances.
* This is helpful when using partly loaded data
* @deprecated
*/
static getLocalInstancesByType<T extends Shape>(this: ShapeType<T>): ShapeSet<T>;
/**
* Finds all the instances whos rdf:type matches the targetClass of this shape
* Ignores if the nodes are valid instances of the shape
* Returns a set of shape instances.
* This is helpful when using partly loaded data
* @deprecated
*/
static getLocalInstanceNodesByType<T extends Shape>(this: ShapeType<T>): NodeSet;
/**
* @deprecated
* @param explicitInstancesOnly
*/
static getLocalInstances<T extends Shape>(this: ShapeType<T>, explicitInstancesOnly?: boolean): ShapeSet<T>;
/**
* @deprecated
*/
static getNumLocalInstances(): number;
/**
* @deprecated
* @param explicitInstancesOnly
*/
static getLocalInstanceNodes(explicitInstancesOnly?: boolean): NodeSet;
/**
* use new Shape(node) instead, where Shape can be any class that extends Shape
* @deprecated
* @param node
*/
static getOf<T extends Shape>(this: ShapeType<T>, node: Node): T;
/**
* Retrieves an existing node or creates a new (temporary) node and then sets the right rdf:type
* Then uses that node to return an instance of the Shape that you call this method from
* So it works just like NamedNode.getOrCreate() but creates an instance of the right shape straight away.
* Note that if the URI did not yet exist, it creates a temporary node, and hence only once you SAVE that node or shape
* Will it (and its properties) be stored in permanent storage.
*
* @param uri
* @param isTemporaryNodeIfNew
*/
static getFromURI<T extends Shape>(this: ShapeType<T>, uri: string, isTemporaryNodeIfNew?: boolean): T;
/**
* Generates a URI from the given prefixURI + optional unique parameters
* Then returns an instance of this shape with that URI, either from an existing or new node
* This method is intended to be extended by other shapes.
* The base implementation in Shape.ts will generate a unique URI if no uniqueParams are given, so extending methods may use super.getFromParams() when no params are given
* @param prefixURI
* @param uniqueParams
*/
static getFromParams<T extends Shape>(this: ShapeType<T>, prefixURI: string, ...uniqueParams: any[]): T;
static getSetOf<T extends Shape>(this: ShapeType<T>, nodes: NodeValuesSet, allowSubShapes?: boolean): ShapeValuesSet<T>;
static getSetOf<T extends Shape>(this: ShapeType<T>, nodes: ICoreIterable<Node>, allowSubShapes?: boolean): ShapeSet<T>;
private static ensureLinkedShape;
/**
* Get all values of a certain property as instances of a certain shape.
* The returned set of shape will automatically update when the property values change in the graph.
* @param property
* @param shapeClass
*/
getAllAs<T extends Shape>(property: NamedNode, shapeClass: typeof Shape, allowSubShapes?: boolean): ShapeValuesSet<T>;
/**
* If a value exists for the given property, this returns that value as an instance of the given shape
* If not, returns null
* @param property
* @param shape
*/
getOneAs<S extends Shape = Shape>(property: any, shape: typeof Shape, allowSubShapes?: boolean): S;
equals(other: any, checkShapeType?: boolean): boolean;
/**
* Makes sure that the node that this instance represents has the right rdf.type
* Also makes sure that this instance is destructed if the node is removed
* @internal
* @param node
*/
setupNode(node: Node): void;
/**
* Destructs the instance. Removes event listeners etc. Overwrite in each subclass of this class that uses custom event listeners
*/
destruct(): void;
validate(): boolean;
getOne(property: NamedNode): Node | null;
getAll(property: NamedNode): NodeValuesSet | undefined;
getAllExplicit(property: any): NodeSet;
getOneFromPath(...properties: NamedNode[]): Node | undefined;
getAllFromPath(...properties: NamedNode[]): NodeSet;
getOneInverse(property: NamedNode): NamedNode | null;
getAllInverse(property: NamedNode): NodeSet<NamedNode> | undefined;
set(property: NamedNode, value: Node): boolean;
setValue(property: NamedNode, value: string): boolean;
mset(property: NamedNode, values: ICoreIterable<Node>): boolean;
overwrite(property: NamedNode, value: Node): boolean;
moverwrite(property: NamedNode, values: ICoreIterable<Node>): boolean;
remove(): void;
/**
* @deprecated
*/
save(): Promise<void>;
unset(property: NamedNode, value: Node): boolean;
unsetAll(property: NamedNode): boolean;
has(property: NamedNode, value: Node): boolean;
hasValue(property: NamedNode, value: string): boolean;
hasExplicit(property: NamedNode, value: Node): boolean;
hasPath(properties: NamedNode[]): boolean;
hasPathTo(properties: NamedNode[], endPoint?: Node): boolean;
hasPathToSomeInSet(properties: NamedNode[], endPoints?: ICoreIterable<Node>): boolean;
/**
* Checks if the node has a value for this property that is the exact same object as the given value
* (as opposed to has() which also returns true for equivalent literal values in Literal objects)
* @param property
* @param value
* @returns {boolean}
*/
hasExact(property: NamedNode, value?: Node): boolean;
hasProperty(property: NamedNode): boolean;
hasInverse(property: NamedNode, value?: any): boolean;
hasInverseProperty(property: NamedNode): boolean;
getValue(property: NamedNode, language?: string): string | null;
getProperties(includeFromIncomingArcs?: boolean): NodeSet<NamedNode>;
getInverseProperties(): any;
getMultiple(properties: ICoreIterable<NamedNode>): NodeSet;
getMultipleInverse(properties: ICoreIterable<NamedNode>): NodeSet;
getDeep(property: NamedNode, maxDepth?: number): NodeSet;
getQuads(property: NamedNode, value?: Node): QuadSet;
getInverseQuads(property: NamedNode): QuadSet;
getAllInverseQuads(includeImplicit?: boolean): QuadArray;
getAllQuads(includeAsObject?: boolean, includeImplicit?: boolean): QuadArray;
/**
* Returns all quads related to this shape.
* Overwrite this method to automatically send over quads to the frontend when this shape is sent over
* This method is used internally by JSONWriter when sending a shape between environments by converting it to JSON & JSON-LD
* @param includeImplicit
*/
getDataQuads(includeImplicit?: boolean): Quad[];
/**
* Fires the given call back when ANY property of this node changes.
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChangeAny(callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Fires the given call back when this node become the value or is no longer the value of another node
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChangeAnyInverse(callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Fires the given call back when this node changes the values of the given property
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChange(property: NamedNode, callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Fires the given callback when this node become the value or is no longer the value of the given property of another node
* Example: if someGroup hasParticipant thisResource, and the group removes this node from its participants, it will trigger onChangeInverse for this node
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChangeInverse(property: any, callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Call this when you want to stop listening for onChangeAny events. Make sure to provide the exact same BOUND instance of a method to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChangeAny
* @param context the same context you supplied to onChangeAny
*/
removeOnChangeAny(callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Call this when you want to stop listening for onChangeAnyInverse events. Make sure to provide the exact same BOUND instance of a method to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChangeAnyInverse
* @param context the same context you supplied to onChangeAnyInverse
*/
removeOnChangeAnyInverse(callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Call this when you want to stop listening for onChange events. Make sure to provide the exact same BOUND instance of a method as callback to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChange
* @param context the same context you supplied to onChange
*/
removeOnChange(property: NamedNode, callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Call this when you want to stop listening for onChangeInverse events. Make sure to provide the exact same BOUND instance of a method as callback to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChangeInverse
* @param context the same context you supplied to onChangeInverse
*/
removeOnChangeInverse(property: any, callback: (quads?: QuadSet, property?: NamedNode) => void, context?: any): void;
/**
* Call this when you want to stop listening for onChangeAny events. Other then removeOnChangeAny you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChangeAny
* @param context the same context you supplied to onChangeAny
*/
clearOnChangeAny(context: any): void;
/**
* Call this when you want to stop listening for onChangeAnyInverse events. Other then removeOnChangeAnyInverse you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChangeAnyInverse
* @param context the same context you supplied to onChangeAnyInverse
*/
clearOnChangeAnyInverse(context: any): void;
/**
* Call this when you want to stop listening for onChange events. Other then removeOnChange you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChange
* @param context the same context you supplied to onChange
*/
clearOnChange(property: NamedNode, context?: any): void;
/**
* Call this when you want to stop listening for onChangeInverse events. Other then removeOnChangeInverse you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChangeInverse
* @param context the same context you supplied to onChangeAny
*/
clearOnChangeInverse(property: any, context: any): void;
/**
* Call this when you want to stop listening for onPredicateChange events
* @param context the same context you supplied to onPredicateChange
*/
clearOnPredicateChange(context: any): void;
/**
* Returns true if this instance has the given type as the value of rdf.type
* Syntactic sugar for this.has(rdf.type,type)
* @param type
*/
isa(type: NamedNode): boolean;
/**
* Other than NamedNode.promiseLoaded, a Shape will preload whatever data it requires to fulfill the constraints of the shape
* NOTE: loading is handled by the current StorageController, by default there is no StorageController
* @param {boolean} loadInverseProperties
* @returns {Promise<boolean>}
*/
promiseLoaded(loadInverseProperties?: boolean): Promise<boolean>;
/**
* Returns true if this instance has had its promiseLoaded function called and the loading has completed
* NOTE: will return false if the instance has never loaded, regardless of whether the namedNode it represents is already loaded, and even if this instance would not load anything else
*/
isLoaded(includingInverseProperties?: boolean): boolean;
reload(): Promise<boolean>;
load(loadInverseProperties?: boolean): Promise<boolean>;
toString(): string;
print(includeIncomingProperties?: boolean): string;
/**
* Returns a new cloned instance with the exact same quads
* The instance only exists locally (as it's not yet saved)
* @returns {T}
*/
clone(): this;
}
interface Constructor<M> {
new (...args: any[]): M;
}
export interface ShapeLike<M extends Shape> extends Constructor<M> {
targetClass: NamedNode;
getSetOf<M extends Shape>(this: ShapeLike<M>, nodes: ICoreIterable<Node>): ShapeSet<M>;
getFromURI<T extends Shape>(this: ShapeLike<T>, uri: string, isTemporaryNodeIfNew?: boolean): T;
getLocalInstanceNodes(explicitInstancesOnly?: boolean): NodeSet;
}
/**
* A class that represent the class of a shape.
*/
export type ShapeType<S extends Shape = Shape> = ClassOf<S> & typeof Shape;
export {};