UNPKG

@sassoftware/vi-api

Version:
84 lines (83 loc) 3.73 kB
import { ReplaySubject, throwError } from "rxjs"; import { take, timeoutWith } from "rxjs/operators"; import { APP_INIT_TIMEOUT_MS, ERR_INIT_TIMEOUT, SviInitEvent, ERR_EXISTING_APP_DECLARED, ERR_EXISTING_RESOURCE_BUNDLE_DECLARED } from "."; export class SviInitApi { constructor(globalI18nResources) { this.globalI18nResources = globalI18nResources; this.appReadyStates = new Map(); this.resourceReadyStates = new Map(); this.resourceServiceSubject = new ReplaySubject(); this.resourceService$ = this.resourceServiceSubject.pipe(take(1)); this.defaultTimeout = APP_INIT_TIMEOUT_MS; } declareApp(appName, i18nResourceBundleName) { if (this.isAppDeclared(appName)) { throw ERR_EXISTING_APP_DECLARED(appName); } this.initReadyState(this.appReadyStates, appName, true); if (!i18nResourceBundleName) { return; } if (this.isResourceBundleDeclared(i18nResourceBundleName)) { throw ERR_EXISTING_RESOURCE_BUNDLE_DECLARED(i18nResourceBundleName); } this.initReadyState(this.resourceReadyStates, i18nResourceBundleName, true); } async whenAppReady(appName, timeout) { return this.whenReady(this.appReadyStates, appName, timeout); } notifyAppReady(appName) { this.notifyReady(this.appReadyStates, appName); } registerResourceBundle(bundleName, resources) { // register bundle with the global i18nResources object which will be picked up by our resource services on construction. this.globalI18nResources[bundleName] = resources; this.notifyReady(this.resourceReadyStates, bundleName); // also register the bundle to be added when the resource service is ready. // this supports any consumers which may call this after init this.resourceService$.subscribe(resourceService => { resourceService.registerResourceBundle(bundleName, resources); }); } checkAllDeclaredAppsReady() { this.whenAllDeclaredReady(this.appReadyStates).then(() => this.notifyAppReady(SviInitEvent.AllDeclaredApps)); } checkAllDeclaredResourcesReady() { this.whenAllDeclaredReady(this.resourceReadyStates).then(() => this.notifyAppReady(SviInitEvent.AllDeclaredI18nResourceBundles)); } setResourceApi(resourceService) { this.resourceServiceSubject.next(resourceService); this.resourceServiceSubject.complete(); } isAppDeclared(appName) { return !!this.appReadyStates.get(appName)?.isDeclared; } isResourceBundleDeclared(bundleName) { return !!this.resourceReadyStates.get(bundleName)?.isDeclared; } notifyReady(map, name) { const readyState = this.initReadyState(map, name); readyState.subject.next(undefined); readyState.subject.complete(); } async whenReady(map, name, timeout) { const readySubject = this.initReadyState(map, name).subject; const timeoutMs = timeout ?? this.defaultTimeout; await readySubject.pipe(timeoutWith(timeoutMs, throwError(ERR_INIT_TIMEOUT(name, timeoutMs)))).toPromise(); } async whenAllDeclaredReady(map) { const toWaitFor = []; for (const [name, readyState] of map.entries()) { if (readyState.isDeclared) { toWaitFor.push(this.whenReady(map, name).catch(console.error)); } } await Promise.all(toWaitFor); } initReadyState(map, name, declare) { const state = map.get(name) ?? { subject: new ReplaySubject() }; state.isDeclared = declare ?? state.isDeclared; map.set(name, state); return state; } }