@sassoftware/vi-api
Version:
Types used in the SAS Visual Investigator API
84 lines (83 loc) • 3.73 kB
JavaScript
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;
}
}