@solid/community-server
Version:
Community Solid Server: an open and modular implementation of the Solid specifications
179 lines (178 loc) • 9.53 kB
TypeScript
import { INDEX_ID_KEY } from './IndexedStorage';
import type { CreateTypeObject, IndexedQuery, IndexedStorage, IndexTypeCollection, StringKey, TypeObject, ValueType } from './IndexedStorage';
import type { KeyValueStorage } from './KeyValueStorage';
/**
* Key used to link to child objects in a {@link WrappedIndexedStorage}.
*/
export type VirtualKey<TChild> = TChild extends string ? `**${TChild}**` : never;
/**
* Object stored in the wrapped {@link KeyValueStorage} in a {@link WrappedIndexedStorage}.
*/
export type VirtualObject = {
[]: Record<string, VirtualObject>;
[]: unknown;
[]: string;
};
/**
* A parent/child relation description in a {@link WrappedIndexedStorage}.
*/
export type IndexRelation<TTypes> = {
parent: {
key: VirtualKey<string>;
type: StringKey<TTypes>;
};
child: {
key: string;
type: StringKey<TTypes>;
};
};
/**
* An {@link IndexedStorage} that makes use of 2 {@link KeyValueStorage}s to implement the interface.
* Due to being limited by key/value storages, there are some restrictions on the allowed type definitions:
*
* * There needs to be exactly 1 type with no references to other types.
* * All other types need to have exactly 1 reference to another type.
* * Types can't reference each other to create a cycle of references.
*
* All of the above to create a tree-like structure of references.
* Such a tree is then stored in one of the storages.
* The other storage is used to store all indexes that are used to find the correct tree object when solving queries.
*/
export declare class WrappedIndexedStorage<T extends IndexTypeCollection<T>> implements IndexedStorage<T> {
protected readonly logger: import("global-logger-factory").Logger<unknown>;
private readonly valueStorage;
private readonly indexStorage;
/**
* For every type, the keys on which an index tracks the values and which root object they are contained in.
* All types for which a `defineType` call was made will have a key in this object.
* For all types that are not the root, there will always be an index on their ID value.
*/
private readonly indexes;
/**
* Keeps track of type validation.
* If true the defined types create a valid structure that can be used.
*/
private validDefinition;
/**
* The variable in which the root type is stored.
* A separate getter is used to always return the value
* so the potential `undefined` does not have to be taken into account.
*/
private rootTypeVar;
/**
* All parent/child relations between all types in the storage,
* including the keys in both types that are used to reference each other.
*/
private readonly relations;
constructor(valueStorage: KeyValueStorage<string, VirtualObject>, indexStorage: KeyValueStorage<string, string[]>);
defineType<TType extends StringKey<T>>(type: TType, description: T[TType]): Promise<void>;
createIndex<TType extends StringKey<T>>(type: TType, key: StringKey<T[TType]>): Promise<void>;
has<TType extends StringKey<T>>(type: TType, id: string): Promise<boolean>;
get<TType extends StringKey<T>>(type: TType, id: string): Promise<TypeObject<T[TType]> | undefined>;
create<TType extends StringKey<T>>(type: TType, value: CreateTypeObject<T[TType]>): Promise<TypeObject<T[TType]>>;
set<TType extends StringKey<T>>(type: TType, value: TypeObject<T[TType]>): Promise<void>;
setField<TType extends StringKey<T>, TKey extends StringKey<T[TType]>>(type: TType, id: string, key: TKey, value: ValueType<T[TType][TKey]>): Promise<void>;
delete<TType extends StringKey<T>>(type: TType, id: string): Promise<void>;
findIds<TType extends StringKey<T>>(type: TType, query: IndexedQuery<T, TType>): Promise<string[]>;
find<TType extends StringKey<T>>(type: TType, query: IndexedQuery<T, TType>): Promise<(TypeObject<T[TType]>)[]>;
entries<TType extends StringKey<T>>(type: TType): AsyncIterableIterator<TypeObject<T[TType]>>;
/**
* Converts a {@link VirtualObject} into a {@link TypeObject}.
* To be used when outputting results.
*/
protected toTypeObject<TType extends StringKey<T>>(type: TType, obj: VirtualObject): TypeObject<T[TType]>;
/**
* The root type for this storage.
* Use this instead of rootTypeVar to prevent having to check for `undefined`.
* This value will always be defined if the type definitions have been validated.
*/
protected get rootType(): string;
/**
* Finds the root object that contains the requested type/id combination.
*/
protected getRoot<TType extends StringKey<T>>(type: TType, id: string): Promise<VirtualObject | undefined>;
/**
* Returns the sequence of virtual keys that need to be accessed to reach the given type, starting from the root.
*/
protected getPathToType(type: string): VirtualKey<string>[];
/**
* Finds all records that can be found in the given object by following the given path of virtual keys.
*/
protected getPathRecords(obj: VirtualObject, path: VirtualKey<string>[]): Record<string, VirtualObject>[];
/**
* Finds all objects in the provided object that can be found by following the provided path of virtual keys.
*/
protected getChildObjects(obj: VirtualObject, path: VirtualKey<string>[]): VirtualObject[];
/**
* Finds the record in the given object that contains the given type/id combination.
* This function assumes it was already verified through an index that this object contains the given combination.
*/
protected getContainingRecord<TType extends StringKey<T>>(rootObj: VirtualObject, type: TType, id: string): Record<string, VirtualObject>;
/**
* Replaces an object of the given type.
* The identifier in the value is used to determine which object.
*/
protected updateValue<TType extends StringKey<T>>(type: TType, value: TypeObject<T[TType]>, replace: true): Promise<void>;
/**
* Replaces part of an object of the given type with the given partial value.
* The identifier in the value is used to determine which object.
*/
protected updateValue<TType extends StringKey<T>>(type: TType, partial: Partial<TypeObject<T[TType]>> & {
[INDEX_ID_KEY]: string;
}, replace: false): Promise<void>;
/**
* Returns all relations where the given type is the parent.
*/
protected getChildRelations<TType extends StringKey<T>>(type: TType): IndexRelation<T>[];
/**
* Returns the relation where the given type is the child.
* Will return `undefined` for the root type as that one doesn't have a parent.
*/
protected getParentRelation<TType extends StringKey<T>>(type: TType): IndexRelation<T> | undefined;
/**
* Makes sure the defined types fulfill all the requirements necessary for types on this storage.
* Will throw an error if this is not the case.
* This should be called before doing any data interactions.
* Stores success in a variable so future calls are instantaneous.
*/
protected validateDefinition(type: string): void;
/**
* Finds the IDs of all root objects that contain objects of the given type matching the given query
* by making use of the indexes applicable to the keys in the query.
* This function only looks at the keys in the query with primitive values,
* object values in the query referencing parent objects are not considered.
* Similarly, only indexes are used, keys without index are also ignored.
*
* If an array of root IDs is provided as input,
* the result will be an intersection of this array and the found identifiers.
*
* If the result is an empty array, it means that there is no valid identifier matching the query,
* while an `undefined` result means there is no index matching any of the query keys,
* so a result can't be determined.
*/
protected findIndexedRoots<TType extends StringKey<T>>(type: TType, match: IndexedQuery<T, TType>, rootIds?: string[]): Promise<string[] | undefined>;
/**
* Finds all objects of the given type matching the query.
* The `rootIds` array can be used to restrict the IDs of root objects to look at,
* which is relevant for the recursive calls the function does.
*
* Will throw an error if there is no index that can be used to solve the query.
*/
protected solveQuery<TType extends StringKey<T>>(type: TType, query: IndexedQuery<T, TType>, rootIds?: string[]): Promise<VirtualObject[]>;
/**
* Generate the key used to store the index in the index storage.
*/
protected getIndexKey(type: string, key: string, value: string | number): string;
/**
* Update all indexes for an object of the given type, and all its children.
*/
protected updateDeepTypeIndex<TType extends StringKey<T>>(type: TType, rootId: string, oldObj: VirtualObject, newObj?: VirtualObject): Promise<void>;
/**
* Updates all indexes for an object of the given type.
*/
protected updateTypeIndex<TType extends StringKey<T>>(type: TType, rootId: string, oldObj?: VirtualObject, newObj?: VirtualObject): Promise<void>;
/**
* Updates the index for a specific key of an object of the given type.
*/
protected updateKeyIndex(type: string, key: string, value: string, rootId: string, add: boolean): Promise<void>;
}