@casual-simulation/aux-vm-browser
Version:
A set of utilities required to securely run an AUX in a web browser.
139 lines • 4.47 kB
JavaScript
import { merge, applyUpdates, stateUpdatedEvent, } from '@casual-simulation/aux-common';
import { BehaviorSubject, Subject } from 'rxjs';
import { wrap, proxy, releaseProxy } from 'comlink';
import { startWith } from 'rxjs/operators';
/**
* Attempts to create a proxy client partition that is loaded from a remote inst.
* @param options The options to use.
* @param config The config to use.
*/
export async function createProxyClientPartition(config) {
if (config.type === 'proxy_client') {
const partition = new ProxyClientPartitionImpl(config);
await partition.init();
return partition;
}
return undefined;
}
export class ProxyClientPartitionImpl {
get space() {
return this._space;
}
set space(value) {
this._space = value;
this._bridge.setSpace(value);
}
get onBotsAdded() {
return this._onBotsAdded.pipe(startWith(Object.values(this.state)));
}
get onBotsRemoved() {
return this._onBotsRemoved;
}
get onBotsUpdated() {
return this._onBotsUpdated;
}
get onStateUpdated() {
return this._onStateUpdated.pipe(startWith(stateUpdatedEvent(this.state)));
}
get onVersionUpdated() {
return this._onVersionUpdated;
}
get onError() {
return this._onError;
}
get onEvents() {
return this._onEvents;
}
get onStatusUpdated() {
return this._onStatusUpdated;
}
constructor(config) {
this._bridge = wrap(config.port);
this.private = config.private;
this.realtimeStrategy = config.editStrategy;
this.state = {};
this._onBotsAdded = new Subject();
this._onBotsRemoved = new Subject();
this._onBotsUpdated = new Subject();
this._onStateUpdated = new Subject();
this._onVersionUpdated = new BehaviorSubject({
currentSite: null,
remoteSite: null,
vector: {},
});
this._onError = new Subject();
this._onEvents = new Subject();
this._onStatusUpdated = new Subject();
}
async init() {
const proxies = [
proxy((bots) => this._handleOnBotsAdded(bots)),
proxy((bots) => this._handleOnBotsRemoved(bots)),
proxy((bots) => this._handleOnBotsUpdated(bots)),
proxy((update) => this._handleOnStateUpdated(update)),
proxy((error) => this._onError.next(error)),
proxy((events) => this._onEvents.next(events)),
proxy((status) => this._onStatusUpdated.next(status)),
proxy((version) => this._onVersionUpdated.next(version)),
];
this._proxies = proxies;
await this._bridge.addListeners(...proxies);
}
_handleOnBotsAdded(bots) {
let newState = Object.assign({}, this.state);
for (let b of bots) {
newState[b.id] = {
...b,
space: this.space,
};
}
this.state = newState;
this._onBotsAdded.next(bots);
}
_handleOnBotsRemoved(bots) {
let newState = Object.assign({}, this.state);
for (let b of bots) {
delete newState[b];
}
this.state = newState;
this._onBotsRemoved.next(bots);
}
_handleOnBotsUpdated(bots) {
let newState = Object.assign({}, this.state);
for (let b of bots) {
const existing = newState[b.bot.id];
newState[b.bot.id] = merge(existing, b.bot);
}
this.state = newState;
this._onBotsUpdated.next(bots);
}
_handleOnStateUpdated(update) {
this.state = applyUpdates(this.state, update);
this._onStateUpdated.next(update);
}
applyEvents(events) {
// Unwrap the nested promise
// (technically gets unwrapped automatically, but this
// fixes a return type issue)
return this._bridge.applyEvents(events).then((a) => a);
}
async sendRemoteEvents(events) {
if (this._bridge.sendRemoteEvents) {
await this._bridge.sendRemoteEvents(events);
}
}
connect() {
this._bridge.connect();
}
unsubscribe() {
if (this.closed) {
return;
}
this._bridge.unsubscribe();
for (let p of this._proxies) {
p[releaseProxy]();
}
this.closed = true;
}
}
//# sourceMappingURL=ProxyClientPartition.js.map