UNPKG

@zennomi/mangadex-full-api

Version:

A MangaDex api based around the official API.

132 lines (117 loc) 5.11 kB
import { RelationshipSchema } from '../types/schema'; import IDObject from '../internal/IDObject'; // eslint-disable-next-line @typescript-eslint/ban-types type GettableClass<T> = Function & { get: (id: string) => Promise<T>; getMultiple?: (ids: string[]) => Promise<T[]> }; type PartialRelationshipSchema = Pick<RelationshipSchema, 'id' | 'type'> & Partial<Pick<RelationshipSchema, 'related' | 'attributes'>>; /** * Represents a relationship from one MangaDex object to another such as a manga, author, etc via its id. */ class Relationship<T> extends IDObject { /** * The MangaDex UUID of the object this relationship refers to */ id: string; /** * What type of object is this a relationship to */ type: string; /** * If the relationship is between two manga, how are they related? */ related?: RelationshipSchema['related']; /** * Cached relationships are created by using the 'includes' parameter on search requests or * other functions that support it. For every type included in the parameter, relationships of * that type will be replaced by the actual object. The object will still be represented * by a Relationship object, but the {@link resolve} method will return instantly with the cached data. * Each resulting object will have no further relationships of its own. */ cached = false; private cachedData?: object; private static typeMap: Record<string, GettableClass<unknown>> = {}; private static typeMapLocked = false; constructor(data: PartialRelationshipSchema) { super(); this.id = data.id; if (!(data.type in Relationship.typeMap)) throw `Unregistered relationship type: ${data.type}`; this.type = data.type; this.related = data.related; // Attempt to create cached object for reference expanded relationships if (data.attributes) { try { const classObj = Relationship.typeMap[this.type]; // Attempt to simulate a common schema object: const schemaObj = { attributes: data.attributes, id: this.id, type: this.type, relationships: [], }; this.cachedData = Reflect.construct(classObj, [schemaObj]); this.cached = true; } catch (err) { // console.log('Failed to create cache object'); // console.error(err); } } } /** * This will automatically fetch the associated object that this relationship refers to. * In other words, it wil call Manga.get(id), Chapter.get(id), etc with the information * stored in this relationship instance. If this relationship is cached, then the resulting * object will not have any relationships of its own. */ async resolve(): Promise<T> { if (this.cached) return this.cachedData as T; return Relationship.typeMap[this.type].get(this.id) as Promise<T>; } /** * This will {@link Relationship.resolve} an array of relationships, returning another array * in the same order. * @param relationshipArray - An array of relationships of the same type */ static async resolveAll<T>(relationshipArray: Relationship<T>[]): Promise<T[]> { if (relationshipArray.length === 0) return []; const classObj = Relationship.typeMap[relationshipArray[0].type] as GettableClass<T>; if (classObj !== undefined && classObj.getMultiple !== undefined) { return classObj.getMultiple(relationshipArray.map((i) => i.id)); } else { return await Promise.all(relationshipArray.map((elem) => elem.resolve())); } } /** * This will search through a relationship response from MangaDex and convert any * relationships of a specific type into relationship objects. * @internal */ static convertType<T2>(type: string, arr: RelationshipSchema[]): Relationship<T2>[] { return arr.filter((elem) => elem.type === type).map((elem) => new Relationship<T2>(elem)); } /** * This function is used to resolved circular references, and should only be used in base.ts. * Specifically, it pairs a relationship type to its associated class. * @internal */ static registerTypes(types: string[], classObj: GettableClass<unknown>) { if (Relationship.typeMapLocked) { throw Error(`Cannot add types ${types} because the Relationship type map has been locked.`); } types.forEach((type) => (Relationship.typeMap[type] = classObj)); } /** * Lock the type map so that no more types can be registered. * @internal */ static lockTypeMap() { Relationship.typeMapLocked = true; } /** * Returns an array of all registered Relationship types * @internal */ static getRegisteredTypes() { return Object.keys(Relationship.typeMap); } } export default Relationship;