-
Matiss Janis Aboltins authoredMatiss Janis Aboltins authored
app.ts 1.81 KiB
// @ts-strict-ignore
import mitt from 'mitt';
import { captureException } from '../platform/exceptions';
// This is a simple helper abstraction for defining methods exposed to
// the client. It doesn't do much, but checks for naming conflicts and
// makes it cleaner to combine methods. We call a group of related
// methods an "app".
class App<Handlers> {
events;
handlers: Handlers;
services;
unlistenServices;
constructor() {
this.handlers = {} as Handlers;
this.services = [];
this.events = mitt();
this.unlistenServices = [];
}
method<Name extends string & keyof Handlers>(
name: Name,
func: Handlers[Name],
) {
if (this.handlers[name] != null) {
throw new Error(
'Conflicting method name, names must be globally unique: ' + name,
);
}
this.handlers[name] = func;
}
service(func) {
this.services.push(func);
}
combine(...apps) {
for (const app of apps) {
Object.keys(app.handlers).forEach(name => {
this.method(name as string & keyof Handlers, app.handlers[name]);
});
app.services.forEach(service => {
this.service(service);
});
for (const [name, listeners] of app.events.all.entries()) {
for (const listener of listeners) {
this.events.on(name, listener);
}
}
}
}
startServices() {
if (this.unlistenServices.length > 0) {
captureException(
new Error(
'App: startServices called while services are already running',
),
);
}
this.unlistenServices = this.services.map(service => service());
}
stopServices() {
this.unlistenServices.forEach(unlisten => {
if (unlisten) {
unlisten();
}
});
this.unlistenServices = [];
}
}
export function createApp<T>() {
return new App<T>();
}