UNPKG

@casual-simulation/aux-vm-browser

Version:

A set of utilities required to securely run an AUX in a web browser.

122 lines 4.45 kB
/* CasualOS is a set of web-based tools designed to facilitate the creation of real-time, multi-user, context-aware interactive experiences. * * Copyright (c) 2019-2025 Casual Simulation, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ const _bootstrapFnSource = function _bootstrapFn(workerUrl) { const listener = (event) => { // uninstall handler self.removeEventListener('message', listener); // get data const port = event.data; // postMessage // onmessage Object.defineProperties(self, { postMessage: { value(data, transferOrOptions) { port.postMessage(data, transferOrOptions); }, }, onmessage: { get() { return port.onmessage; }, set(value) { port.onmessage = value; }, }, // todo onerror }); port.addEventListener('message', (msg) => { self.dispatchEvent(new MessageEvent('message', { data: msg.data })); }); port.start(); // fake recursively nested worker self.Worker = class { constructor() { throw new TypeError('Nested workers from within nested worker are not supported.'); } }; // load module importScripts(workerUrl); }; self.addEventListener('message', listener); }.toString(); export class NestedWorker extends EventTarget { constructor(nativePostMessage, stringOrUrl, options) { super(); this.onmessage = null; this.onmessageerror = null; this.onerror = null; // create bootstrap script const bootstrap = `((${_bootstrapFnSource})('${stringOrUrl}'))`; const blob = new Blob([bootstrap], { type: 'application/javascript' }); const blobUrl = URL.createObjectURL(blob); const channel = new MessageChannel(); const id = blobUrl; // works because blob url is unique, needs ID pool otherwise const msg = { type: 'new_worker', id, port: channel.port2, url: blobUrl, options, }; nativePostMessage(msg, [channel.port2]); // worker-impl: functions this.postMessage = channel.port1.postMessage.bind(channel.port1); this.terminate = () => { const msg = { type: 'terminate_worker', id, }; channel.port1.postMessage(msg); URL.revokeObjectURL(blobUrl); channel.port1.close(); channel.port2.close(); }; // worker-impl: events Object.defineProperties(this, { onmessage: { get() { return channel.port1.onmessage; }, set(value) { channel.port1.onmessage = value; }, }, onmessageerror: { get() { return channel.port1.onmessageerror; }, set(value) { channel.port1.onmessageerror = value; }, }, // todo onerror }); channel.port1.addEventListener('messageerror', (evt) => { const msgEvent = new MessageEvent('messageerror', { data: evt.data, }); this.dispatchEvent(msgEvent); }); channel.port1.addEventListener('message', (evt) => { const msgEvent = new MessageEvent('message', { data: evt.data }); this.dispatchEvent(msgEvent); }); channel.port1.start(); } } //# sourceMappingURL=NestedWorker.js.map