UNPKG

@solid/community-server

Version:

Community Solid Server: an open and modular implementation of the Solid specifications

179 lines (178 loc) 9.53 kB
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 = { [key: VirtualKey<string>]: Record<string, VirtualObject>; [key: string]: unknown; [INDEX_ID_KEY]: 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>; }