forked from CIT/Vmeda.Online
		
	MOBILE-3977 core: Implement async instance pattern
This commit is contained in:
		
							parent
							
								
									a041471205
								
							
						
					
					
						commit
						7a2a8c3e98
					
				@ -13,7 +13,7 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
import { asyncInstance } from '@/core/utils/async-instance';
 | 
			
		||||
import { SQLiteDB, SQLiteDBRecordValues } from '@classes/sqlitedb';
 | 
			
		||||
import { CoreConfigProvider } from '@services/config';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
@ -34,7 +34,7 @@ export class CoreDatabaseTableProxy<
 | 
			
		||||
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
 | 
			
		||||
 | 
			
		||||
    protected config: CoreDatabaseConfiguration;
 | 
			
		||||
    protected target: CorePromisedValue<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>> = new CorePromisedValue();
 | 
			
		||||
    protected target = asyncInstance<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>>();
 | 
			
		||||
    protected environmentObserver?: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
@ -68,81 +68,63 @@ export class CoreDatabaseTableProxy<
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async all(conditions?: Partial<DBRecord>): Promise<DBRecord[]> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.all(conditions);
 | 
			
		||||
        return this.target.all(conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async find(conditions: Partial<DBRecord>): Promise<DBRecord> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.find(conditions);
 | 
			
		||||
        return this.target.find(conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async findByPrimaryKey(primaryKey: PrimaryKey): Promise<DBRecord> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.findByPrimaryKey(primaryKey);
 | 
			
		||||
        return this.target.findByPrimaryKey(primaryKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async reduce<T>(reducer: CoreDatabaseReducer<DBRecord, T>, conditions?: CoreDatabaseConditions<DBRecord>): Promise<T> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.reduce<T>(reducer, conditions);
 | 
			
		||||
        return this.target.reduce<T>(reducer, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async insert(record: DBRecord): Promise<void> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.insert(record);
 | 
			
		||||
        return this.target.insert(record);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async update(updates: Partial<DBRecord>, conditions?: Partial<DBRecord>): Promise<void> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.update(updates, conditions);
 | 
			
		||||
        return this.target.update(updates, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async updateWhere(updates: Partial<DBRecord>, conditions: CoreDatabaseConditions<DBRecord>): Promise<void> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.updateWhere(updates, conditions);
 | 
			
		||||
        return this.target.updateWhere(updates, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async delete(conditions?: Partial<DBRecord>): Promise<void> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.delete(conditions);
 | 
			
		||||
        return this.target.delete(conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async deleteByPrimaryKey(primaryKey: PrimaryKey): Promise<void> {
 | 
			
		||||
        const target = await this.target;
 | 
			
		||||
 | 
			
		||||
        return target.deleteByPrimaryKey(primaryKey);
 | 
			
		||||
        return this.target.deleteByPrimaryKey(primaryKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -174,18 +156,18 @@ export class CoreDatabaseTableProxy<
 | 
			
		||||
     * Update underlying target instance.
 | 
			
		||||
     */
 | 
			
		||||
    protected async updateTarget(): Promise<void> {
 | 
			
		||||
        const oldTarget = this.target.value;
 | 
			
		||||
        const oldTarget = this.target.instance;
 | 
			
		||||
        const newTarget = this.createTarget();
 | 
			
		||||
 | 
			
		||||
        if (oldTarget) {
 | 
			
		||||
            await oldTarget.destroy();
 | 
			
		||||
 | 
			
		||||
            this.target.reset();
 | 
			
		||||
            this.target.resetInstance();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await newTarget.initialize();
 | 
			
		||||
 | 
			
		||||
        this.target.resolve(newTarget);
 | 
			
		||||
        this.target.setInstance(newTarget);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,8 @@ import { Translate } from '@singletons';
 | 
			
		||||
import { CoreIonLoadingElement } from './ion-loading';
 | 
			
		||||
import { CoreLang } from '@services/lang';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { asyncInstance, AsyncInstance } from '../utils/async-instance';
 | 
			
		||||
import { CoreDatabaseTable } from './database/database-table';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * QR Code type enumeration.
 | 
			
		||||
@ -104,6 +106,7 @@ export class CoreSite {
 | 
			
		||||
    // Rest of variables.
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected db?: SQLiteDB;
 | 
			
		||||
    protected cacheTable: AsyncInstance<CoreDatabaseTable<CoreSiteWSCacheRecord>>;
 | 
			
		||||
    protected cleanUnicode = false;
 | 
			
		||||
    protected lastAutoLogin = 0;
 | 
			
		||||
    protected offlineDisabled = false;
 | 
			
		||||
@ -137,6 +140,7 @@ export class CoreSite {
 | 
			
		||||
    ) {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreSite');
 | 
			
		||||
        this.siteUrl = CoreUrlUtils.removeUrlParams(this.siteUrl); // Make sure the URL doesn't have params.
 | 
			
		||||
        this.cacheTable = asyncInstance(() => CoreSites.getCacheTable(this));
 | 
			
		||||
        this.setInfo(infos);
 | 
			
		||||
        this.calculateOfflineDisabled();
 | 
			
		||||
 | 
			
		||||
@ -926,15 +930,14 @@ export class CoreSite {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const id = this.getCacheId(method, data);
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
        let entry: CoreSiteWSCacheRecord | undefined;
 | 
			
		||||
 | 
			
		||||
        if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) {
 | 
			
		||||
            const entries = await cacheTable.all({ key: preSets.cacheKey });
 | 
			
		||||
            const entries = await this.cacheTable.all({ key: preSets.cacheKey });
 | 
			
		||||
 | 
			
		||||
            if (!entries.length) {
 | 
			
		||||
                // Cache key not found, get by params sent.
 | 
			
		||||
                entry = await cacheTable.findByPrimaryKey({ id });
 | 
			
		||||
                entry = await this.cacheTable.findByPrimaryKey({ id });
 | 
			
		||||
            } else {
 | 
			
		||||
                if (entries.length > 1) {
 | 
			
		||||
                    // More than one entry found. Search the one with same ID as this call.
 | 
			
		||||
@ -946,7 +949,7 @@ export class CoreSite {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            entry = await cacheTable.findByPrimaryKey({ id });
 | 
			
		||||
            entry = await this.cacheTable.findByPrimaryKey({ id });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (entry === undefined) {
 | 
			
		||||
@ -991,14 +994,13 @@ export class CoreSite {
 | 
			
		||||
     */
 | 
			
		||||
    async getComponentCacheSize(component: string, componentId?: number): Promise<number> {
 | 
			
		||||
        const params: Array<string | number> = [component];
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
        let extraClause = '';
 | 
			
		||||
        if (componentId !== undefined && componentId !== null) {
 | 
			
		||||
            params.push(componentId);
 | 
			
		||||
            extraClause = ' AND componentId = ?';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return cacheTable.reduce(
 | 
			
		||||
        return this.cacheTable.reduce(
 | 
			
		||||
            {
 | 
			
		||||
                sql: 'SUM(length(data))',
 | 
			
		||||
                js: (size, record) => size + record.data.length,
 | 
			
		||||
@ -1031,7 +1033,6 @@ export class CoreSite {
 | 
			
		||||
        // Since 3.7, the expiration time contains the time the entry is modified instead of the expiration time.
 | 
			
		||||
        // We decided to reuse this field to prevent modifying the database table.
 | 
			
		||||
        const id = this.getCacheId(method, data);
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
        const entry = {
 | 
			
		||||
            id,
 | 
			
		||||
            data: JSON.stringify(response),
 | 
			
		||||
@ -1049,7 +1050,7 @@ export class CoreSite {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await cacheTable.insert(entry);
 | 
			
		||||
        await this.cacheTable.insert(entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1064,12 +1065,11 @@ export class CoreSite {
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
    protected async deleteFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, allCacheKey?: boolean): Promise<void> {
 | 
			
		||||
        const id = this.getCacheId(method, data);
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
 | 
			
		||||
        if (allCacheKey) {
 | 
			
		||||
            await cacheTable.delete({ key: preSets.cacheKey });
 | 
			
		||||
            await this.cacheTable.delete({ key: preSets.cacheKey });
 | 
			
		||||
        } else {
 | 
			
		||||
            await cacheTable.deleteByPrimaryKey({ id });
 | 
			
		||||
            await this.cacheTable.deleteByPrimaryKey({ id });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1087,13 +1087,12 @@ export class CoreSite {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const params = { component };
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
 | 
			
		||||
        if (componentId) {
 | 
			
		||||
            params['componentId'] = componentId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await cacheTable.delete(params);
 | 
			
		||||
        await this.cacheTable.delete(params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
@ -1128,9 +1127,7 @@ export class CoreSite {
 | 
			
		||||
        this.logger.debug('Invalidate all the cache for site: ' + this.id);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
 | 
			
		||||
            await cacheTable.update({ expirationTime: 0 });
 | 
			
		||||
            await this.cacheTable.update({ expirationTime: 0 });
 | 
			
		||||
        } finally {
 | 
			
		||||
            CoreEvents.trigger(CoreEvents.WS_CACHE_INVALIDATED, {}, this.getId());
 | 
			
		||||
        }
 | 
			
		||||
@ -1149,9 +1146,7 @@ export class CoreSite {
 | 
			
		||||
 | 
			
		||||
        this.logger.debug('Invalidate cache for key: ' + key);
 | 
			
		||||
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
 | 
			
		||||
        await cacheTable.update({ expirationTime: 0 }, { key });
 | 
			
		||||
        await this.cacheTable.update({ expirationTime: 0 }, { key });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1185,9 +1180,7 @@ export class CoreSite {
 | 
			
		||||
 | 
			
		||||
        this.logger.debug('Invalidate cache for key starting with: ' + key);
 | 
			
		||||
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
 | 
			
		||||
        await cacheTable.updateWhere({ expirationTime: 0 }, {
 | 
			
		||||
        await this.cacheTable.updateWhere({ expirationTime: 0 }, {
 | 
			
		||||
            sql: 'key LIKE ?',
 | 
			
		||||
            sqlParams: [key],
 | 
			
		||||
            js: record => !!record.key?.startsWith(key),
 | 
			
		||||
@ -1266,9 +1259,7 @@ export class CoreSite {
 | 
			
		||||
     * @return Promise resolved with the total size of all data in the cache table (bytes)
 | 
			
		||||
     */
 | 
			
		||||
    async getCacheUsage(): Promise<number> {
 | 
			
		||||
        const cacheTable = await CoreSites.getCacheTable(this);
 | 
			
		||||
 | 
			
		||||
        return cacheTable.reduce({
 | 
			
		||||
        return this.cacheTable.reduce({
 | 
			
		||||
            sql: 'SUM(length(data))',
 | 
			
		||||
            js: (size, record) => size + record.data.length,
 | 
			
		||||
            jsInitialValue: 0,
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreConstants } from '../constants';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreDatabaseTable } from '@classes/database/database-table';
 | 
			
		||||
import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
import { asyncInstance } from '../utils/async-instance';
 | 
			
		||||
 | 
			
		||||
declare module '@singletons/events' {
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ export class CoreConfigProvider {
 | 
			
		||||
 | 
			
		||||
    static readonly ENVIRONMENT_UPDATED = 'environment_updated';
 | 
			
		||||
 | 
			
		||||
    protected table: CorePromisedValue<CoreDatabaseTable<ConfigDBEntry, 'name'>> = new CorePromisedValue();
 | 
			
		||||
    protected table = asyncInstance<CoreDatabaseTable<ConfigDBEntry, 'name'>>();
 | 
			
		||||
    protected defaultEnvironment?: EnvironmentConfig;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -67,7 +67,7 @@ export class CoreConfigProvider {
 | 
			
		||||
 | 
			
		||||
        await table.initialize();
 | 
			
		||||
 | 
			
		||||
        this.table.resolve(table);
 | 
			
		||||
        this.table.setInstance(table);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -77,9 +77,7 @@ export class CoreConfigProvider {
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async delete(name: string): Promise<void> {
 | 
			
		||||
        const table = await this.table;
 | 
			
		||||
 | 
			
		||||
        await table.deleteByPrimaryKey({ name });
 | 
			
		||||
        await this.table.deleteByPrimaryKey({ name });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -91,8 +89,7 @@ export class CoreConfigProvider {
 | 
			
		||||
     */
 | 
			
		||||
    async get<T>(name: string, defaultValue?: T): Promise<T> {
 | 
			
		||||
        try {
 | 
			
		||||
            const table = await this.table;
 | 
			
		||||
            const record = await table.findByPrimaryKey({ name });
 | 
			
		||||
            const record = await this.table.findByPrimaryKey({ name });
 | 
			
		||||
 | 
			
		||||
            return record.value;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
@ -112,9 +109,7 @@ export class CoreConfigProvider {
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async set(name: string, value: number | string): Promise<void> {
 | 
			
		||||
        const table = await this.table;
 | 
			
		||||
 | 
			
		||||
        await table.insert({ name, value });
 | 
			
		||||
        await this.table.insert({ name, value });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										122
									
								
								src/core/utils/async-instance.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/core/utils/async-instance.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,122 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a wrapper to hold an asynchronous instance.
 | 
			
		||||
 *
 | 
			
		||||
 * @param lazyConstructor Constructor to use the first time the instance is needed.
 | 
			
		||||
 * @returns Asynchronous instance wrapper.
 | 
			
		||||
 */
 | 
			
		||||
function createAsyncInstanceWrapper<T>(lazyConstructor?: () => T | Promise<T>): AsyncInstanceWrapper<T> {
 | 
			
		||||
    let promisedInstance: CorePromisedValue<T> | null = null;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        get instance() {
 | 
			
		||||
            return promisedInstance?.value ?? undefined;
 | 
			
		||||
        },
 | 
			
		||||
        async getInstance() {
 | 
			
		||||
            if (!promisedInstance) {
 | 
			
		||||
                promisedInstance = new CorePromisedValue();
 | 
			
		||||
 | 
			
		||||
                if (lazyConstructor) {
 | 
			
		||||
                    const instance = await lazyConstructor();
 | 
			
		||||
 | 
			
		||||
                    promisedInstance.resolve(instance);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return promisedInstance;
 | 
			
		||||
        },
 | 
			
		||||
        async getProperty(property) {
 | 
			
		||||
            const instance = await this.getInstance();
 | 
			
		||||
 | 
			
		||||
            return instance[property];
 | 
			
		||||
        },
 | 
			
		||||
        setInstance(instance) {
 | 
			
		||||
            if (!promisedInstance) {
 | 
			
		||||
                promisedInstance = new CorePromisedValue();
 | 
			
		||||
            } else if (promisedInstance.isSettled()) {
 | 
			
		||||
                promisedInstance.reset();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            promisedInstance.resolve(instance);
 | 
			
		||||
        },
 | 
			
		||||
        resetInstance() {
 | 
			
		||||
            if (!promisedInstance) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            promisedInstance.reset();
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Asynchronous instance wrapper.
 | 
			
		||||
 */
 | 
			
		||||
export interface AsyncInstanceWrapper<T> {
 | 
			
		||||
    instance?: T;
 | 
			
		||||
    getInstance(): Promise<T>;
 | 
			
		||||
    getProperty<P extends keyof T>(property: P): Promise<T[P]>;
 | 
			
		||||
    setInstance(instance: T): void;
 | 
			
		||||
    resetInstance(): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Asynchronous version of a method.
 | 
			
		||||
 */
 | 
			
		||||
export type AsyncMethod<T> =
 | 
			
		||||
    T extends (...args: infer Params) => infer Return
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
        ? T extends (...args: Params) => Promise<any>
 | 
			
		||||
            ? T
 | 
			
		||||
            : (...args: Params) => Promise<Return>
 | 
			
		||||
        : never;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Asynchronous instance.
 | 
			
		||||
 *
 | 
			
		||||
 * All methods are converted to their asynchronous version, and properties are available asynchronously using
 | 
			
		||||
 * the getProperty method.
 | 
			
		||||
 */
 | 
			
		||||
export type AsyncInstance<T> = AsyncInstanceWrapper<T> & {
 | 
			
		||||
    [k in keyof T]: AsyncMethod<T[k]>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create an asynchronous instance proxy, where all methods will be callable directly but will become asynchronous. If the
 | 
			
		||||
 * underlying instance hasn't been set, methods will be resolved once it is.
 | 
			
		||||
 *
 | 
			
		||||
 * @param lazyConstructor Constructor to use the first time the instance is needed.
 | 
			
		||||
 * @returns Asynchronous instance.
 | 
			
		||||
 */
 | 
			
		||||
export function asyncInstance<T>(lazyConstructor?: () => T | Promise<T>): AsyncInstance<T> {
 | 
			
		||||
    const wrapper = createAsyncInstanceWrapper<T>(lazyConstructor);
 | 
			
		||||
 | 
			
		||||
    return new Proxy(wrapper, {
 | 
			
		||||
        get: (target, property, receiver) => {
 | 
			
		||||
            if (property in target) {
 | 
			
		||||
                return Reflect.get(target, property, receiver);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return async (...args: unknown[]) => {
 | 
			
		||||
                const instance = await wrapper.getInstance();
 | 
			
		||||
 | 
			
		||||
                return instance[property](...args);
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
    }) as AsyncInstance<T>;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user