@dodona/papyros
Version:
Scratchpad for multiple programming languages in the browser.
94 lines • 3.92 kB
JavaScript
import { ProgrammingLanguage } from "./ProgrammingLanguage";
import { BackendEventType } from "./BackendEvent";
import { LogType, papyrosLog } from "./util/Logging";
import { makeChannel } from "sync-message";
import { SyncClient } from "comsync";
import { PyodideClient } from "pyodide-worker-runner";
/**
* Abstract class to implement the singleton pattern
* Static methods group functionality
*/
export class BackendManager {
/**
* @param {ProgrammingLanguage} language The language to support
* @param {Function} backendCreator The constructor for a SyncClient
*/
static registerBackend(language, backendCreator) {
BackendManager.removeBackend(language);
BackendManager.createBackendMap.set(language, backendCreator);
}
/**
* Start a backend for the given language and cache for reuse
* @param {ProgrammingLanguage} language The programming language supported by the backend
* @return {SyncClient<Backend>} A SyncClient for the Backend
*/
static getBackend(language) {
if (this.backendMap.has(language)) { // Cached
return this.backendMap.get(language);
}
else if (this.createBackendMap.has(language)) {
// Create and then cache
const syncClient = this.createBackendMap.get(language)();
this.backendMap.set(language, syncClient);
return syncClient;
}
else {
throw new Error(`${language} is not yet supported.`);
}
}
/**
* Remove a backend for the given language
* @param {ProgrammingLanguage} language The programming language supported by the backend
* @return {boolean} Whether the remove operation had any effect
*/
static removeBackend(language) {
this.backendMap.delete(language);
return this.createBackendMap.delete(language);
}
/**
* Register a callback for when an event of a certain type is published
* @param {BackendEventType} type The type of event to subscribe to
* @param {BackendEventListener} subscriber Callback for when an event
* of the given type is published
*/
static subscribe(type, subscriber) {
if (!this.subscriberMap.has(type)) {
this.subscriberMap.set(type, []);
}
const subscribers = this.subscriberMap.get(type);
if (!subscribers.includes(subscriber)) {
subscribers.push(subscriber);
}
}
/**
* Publish an event, notifying all listeners for its type
* @param {BackendEventType} e The event to publish
*/
static publish(e) {
papyrosLog(LogType.Debug, "Publishing event: ", e);
if (e.type === BackendEventType.Start) {
BackendManager.halted = false;
}
if ((!BackendManager.halted || e.type === BackendEventType.FrameChange) && this.subscriberMap.has(e.type)) {
this.subscriberMap.get(e.type).forEach(cb => cb(e));
}
}
static halt() {
BackendManager.halted = true;
}
}
/**
* Initialise the fields and setup the maps
*/
(() => {
BackendManager.channel = makeChannel();
BackendManager.createBackendMap = new Map();
BackendManager.backendMap = new Map();
BackendManager.subscriberMap = new Map();
BackendManager.registerBackend(ProgrammingLanguage.Python, () => new PyodideClient(() => new Worker(new URL("./workers/python/worker", import.meta.url)), BackendManager.channel));
BackendManager.registerBackend(ProgrammingLanguage.JavaScript, () => new SyncClient(() => new Worker(new URL("./workers/javascript/worker", import.meta.url)), BackendManager.channel));
BackendManager.halted = false;
BackendManager.subscribe(BackendEventType.End, () => BackendManager.halt());
BackendManager.subscribe(BackendEventType.Interrupt, () => BackendManager.halt());
})();
//# sourceMappingURL=BackendManager.js.map