@casual-simulation/aux-vm-browser
Version:
A set of utilities required to securely run an AUX in a web browser.
180 lines • 6.13 kB
JavaScript
/* 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/>.
*/
import { remapProgressPercent, } from '@casual-simulation/aux-common';
import { Subject } from 'rxjs';
/**
* Defines a class that represents an AUX VM that provides no isolation or sandboxing.
*/
export default class AuxNoVM {
/**
* The ID of the simulation.
*/
get id() {
return this._id;
}
get configBotId() {
return this._configBotId;
}
/**
* Creates a new AUX VM that provides no isolation or sandboxing.
* @param id The ID of the inst.
* @param origin The origin of the inst.
* @param config The config that should be used.
*/
constructor(id, origin, configBotId, channel) {
this._id = id;
this._origin = origin;
this._configBotId = configBotId;
this._channel = channel;
this._localEvents = new Subject();
this._deviceEvents = new Subject();
this._stateUpdated = new Subject();
this._versionUpdated = new Subject();
this._connectionStateChanged = new Subject();
this._onError = new Subject();
this._subVMAdded = new Subject();
this._subVMRemoved = new Subject();
this._subVMMap = new Map();
this._onAuthMessage = new Subject();
}
async init() {
// if (!this._channel) {
// }
let statusMapper = remapProgressPercent(0.2, 1);
return await this._channel.init((events) => this._localEvents.next(events), (events) => this._deviceEvents.next(events), (state) => this._stateUpdated.next(state), (version) => this._versionUpdated.next(version), (state) => this._connectionStateChanged.next(statusMapper(state)), (err) => this._onError.next(err),
// TODO: Support sub channels properly
(channel) => { }, //this._handleAddedSubChannel(channel),
(id) => { }, //this._handleRemovedSubChannel(id),
(message) => this._onAuthMessage.next(message));
}
unsubscribe() {
if (this.closed) {
return;
}
this.closed = true;
if (this._channel) {
this._channel.unsubscribe();
this._channel = null;
}
this._connectionStateChanged.unsubscribe();
this._connectionStateChanged = null;
this._localEvents.unsubscribe();
this._localEvents = null;
}
get origin() {
return this._origin;
}
get subVMAdded() {
return this._subVMAdded;
}
get subVMRemoved() {
return this._subVMRemoved;
}
get connectionStateChanged() {
return this._connectionStateChanged;
}
get onError() {
return this._onError;
}
get onAuthMessage() {
return this._onAuthMessage;
}
/**
* The observable list of events that should be produced locally.
*/
get localEvents() {
return this._localEvents;
}
get deviceEvents() {
return this._deviceEvents;
}
/**
* The observable list of bot state updates from this simulation.
*/
get stateUpdated() {
return this._stateUpdated;
}
get versionUpdated() {
return this._versionUpdated;
}
async sendEvents(events) {
return await this._channel.sendEvents(events);
}
async shout(eventName, botIds, arg) {
return await this._channel.shout(eventName, botIds, arg);
}
async formulaBatch(formulas) {
return await this._channel.formulaBatch(formulas);
}
async forkAux(newId) {
return await this._channel.forkAux(newId);
}
async exportBots(botIds) {
return await this._channel.exportBots(botIds);
}
async export() {
return await this._channel.export();
}
async getTags() {
return await this._channel.getTags();
}
async updateDevice(device) {
return await this._channel.updateDevice(device);
}
async sendAuthMessage(message) {
return await this._channel.sendAuthMessage(message);
}
_createSubVM(id, origin, configBotId, channel) {
return new AuxNoVM(id, origin, configBotId, channel);
}
async _handleAddedSubChannel(subChannel) {
const { id, configBotId } = await subChannel.getInfo();
const channel = await subChannel.getChannel();
const subVM = {
id: id,
vm: this._createSubVM(id, this.origin, configBotId, channel),
channel,
};
this._subVMMap.set(id, subVM);
this._subVMAdded.next(subVM);
}
async _handleRemovedSubChannel(channelId) {
const vm = this._subVMMap.get(channelId);
if (vm) {
this._subVMMap.delete(channelId);
this._subVMRemoved.next(vm);
}
}
}
export function processPartitions(config) {
const processed = { ...config, partitions: { ...config.partitions } };
for (let key in processed.partitions) {
const partition = processed.partitions[key];
if (!partition) {
delete processed.partitions[key];
}
else if (partition.type === 'proxy') {
processed.partitions[key] = {
type: 'injected',
partition: partition.partition,
};
}
}
return config;
}
//# sourceMappingURL=AuxNoVM.js.map