@casual-simulation/aux-vm-browser
Version:
A set of utilities required to securely run an AUX in a web browser.
301 lines • 12.5 kB
JavaScript
import { createBot, TEMPORARY_BOT_PARTITION_ID, COOKIE_BOT_PARTITION_ID, isBot, TEMPORARY_SHARED_PARTITION_ID, REMOTE_TEMPORARY_SHARED_PARTITION_ID, BOOTSTRAP_PARTITION_ID, getTagValueForSpace, getUpdateForTagAndSpace, getBotsStateFromStoredAux, DEFAULT_BRANCH_NAME, hasValue, } from '@casual-simulation/aux-common';
import { BaseSimulation, LoginManager, RECORDS_WS_PROTOCOL, RecordsManager, } from '@casual-simulation/aux-vm';
import { BotPanelManager } from './BotPanelManager';
import { PortalManager, ProgressManager } from '@casual-simulation/aux-vm';
import { filter, tap } from 'rxjs/operators';
import { LocalStoragePartitionImpl } from '../partitions/LocalStoragePartition';
import { IdePortalManager } from './IdePortalManager';
import { AuthHelper } from './AuthHelper';
import { LivekitManager } from './LivekitManager';
import { SocketManager as WebSocketManager } from '@casual-simulation/websocket';
import { ApiGatewayWebsocketConnectionClient } from '@casual-simulation/aux-websocket-aws';
import { WebsocketConnectionClient } from '@casual-simulation/aux-websocket';
/**
* Defines a class that interfaces with the AppManager and SocketManager
* to reactively edit bots.
*/
export class BotManager extends BaseSimulation {
/**
* Gets the bots panel manager.
*/
get botPanel() {
return this._botPanel;
}
get origin() {
return this._origin;
}
get inst() {
var _a;
return (_a = this._origin.inst) !== null && _a !== void 0 ? _a : this.id;
}
get recordName() {
return this._origin.recordName;
}
get idePortal() {
return this._idePortal;
}
get login() {
return this._login;
}
get progress() {
return this._progress;
}
get auth() {
return this._authHelper;
}
get records() {
return this._recordsManager;
}
get livekit() {
return this._livekitManager;
}
get consoleMessages() {
return (this._vm.connectionStateChanged.pipe(filter((m) => m.type === 'log' ||
m.type === 'error' ||
m.type === 'warn')));
}
get portals() {
return this._portals;
}
static createDefaultPartitions(id, configBotId, origin, config, initialTempState = {}) {
var _a, _b;
const defaultPartitions = {
[TEMPORARY_BOT_PARTITION_ID]: {
type: 'memory',
private: true,
initialState: {
[configBotId]: createBot(configBotId, {
inst: (_a = origin.inst) !== null && _a !== void 0 ? _a : id,
}),
...initialTempState,
},
},
[COOKIE_BOT_PARTITION_ID]: ((_b = import.meta.env) === null || _b === void 0 ? void 0 : _b.MODE) === 'static'
? {
type: 'local_storage',
namespace: !origin.recordName
? `aux/${origin.inst}`
: `aux/${origin.recordName}/${origin.inst}`,
private: true,
}
: {
type: 'proxy',
partition: new LocalStoragePartitionImpl({
type: 'local_storage',
namespace: !origin.recordName
? `aux/${origin.inst}`
: `aux/${origin.recordName}/${origin.inst}`,
private: true,
}),
},
[BOOTSTRAP_PARTITION_ID]: {
type: 'memory',
initialState: config.bootstrapState
? getBotsStateFromStoredAux(config.bootstrapState)
: {},
private: true,
},
};
return defaultPartitions;
}
static createPartitions(id, configBotId, origin, config) {
var _a, _b;
const host = (_a = origin.host) !== null && _a !== void 0 ? _a : config.causalRepoConnectionUrl;
if (!host) {
throw new Error('[BotManager] Host is required.');
}
const protocol = config.causalRepoConnectionProtocol;
const versions = config.sharedPartitionsVersion;
const localPersistence = (_b = config.collaborativeRepLocalPersistence) !== null && _b !== void 0 ? _b : false;
console.log('[BotManager] Using v2 shared partitions');
if (localPersistence) {
console.log('[BotManager] Enabling local persistence.');
}
const defaultPartitions = BotManager.createDefaultPartitions(id, configBotId, origin, config);
const partitions = {
shared: {
type: 'remote_yjs',
recordName: origin.recordName,
inst: origin.inst,
branch: DEFAULT_BRANCH_NAME,
host: host,
connectionProtocol: protocol,
localPersistence: localPersistence
? {
saveToIndexedDb: true,
}
: null,
},
[TEMPORARY_SHARED_PARTITION_ID]: {
type: 'remote_yjs',
recordName: origin.recordName,
inst: origin.inst,
branch: `${DEFAULT_BRANCH_NAME}-player-${configBotId}`,
host: host,
connectionProtocol: protocol,
temporary: true,
remoteEvents: false,
},
[REMOTE_TEMPORARY_SHARED_PARTITION_ID]: {
type: 'other_players_repo',
recordName: origin.recordName,
inst: origin.inst,
branch: DEFAULT_BRANCH_NAME,
host: host,
connectionProtocol: protocol,
childPartitionType: 'yjs_client',
},
};
const finalPartitions = Object.assign({}, defaultPartitions, partitions);
return finalPartitions;
}
static createStaticPartitions(id, configBotId, origin, config, initialTempState = {}) {
var _a, _b;
const localPersistence = (_a = config.staticRepoLocalPersistence) !== null && _a !== void 0 ? _a : true;
console.log('[BotManager] Using static partitions');
if (localPersistence) {
console.log('[BotManager] Enabling local persistence.');
}
const defaultPartitions = BotManager.createDefaultPartitions(id, configBotId, origin, config, initialTempState);
let partitions = {
shared: {
type: 'memory',
initialState: {},
},
[TEMPORARY_SHARED_PARTITION_ID]: {
type: 'memory',
initialState: {},
},
[REMOTE_TEMPORARY_SHARED_PARTITION_ID]: null,
};
if (localPersistence) {
partitions.shared = {
type: 'yjs',
remoteEvents: true,
branch: `${(_b = origin.recordName) !== null && _b !== void 0 ? _b : ''}/${origin.inst}/${DEFAULT_BRANCH_NAME}`,
localPersistence: {
saveToIndexedDb: true,
},
connectionId: configBotId,
};
}
const finalPartitions = Object.assign({}, defaultPartitions, partitions);
return finalPartitions;
}
constructor(origin, config, vm, auth) {
super(vm);
this._origin = origin;
this._config = config;
this._authHelper =
auth !== null && auth !== void 0 ? auth : new AuthHelper(config.authOrigin, config.recordsOrigin, undefined, config.requirePrivoLogin);
this._login = new LoginManager(this._vm);
this._portals = new PortalManager(this._vm);
this._progress = new ProgressManager(this._vm, this._portals.portalLoaded);
}
async editBot(bot, tag, value, space = null) {
const val = getTagValueForSpace(this.helper.botsState[bot.id], tag, space);
if (val === value) {
return;
}
if (isBot(bot) && bot.id !== 'empty' && bot.id !== 'mod') {
await this.helper.updateBot(bot, getUpdateForTagAndSpace(tag, value, space));
}
}
_beforeVmInit() {
super._beforeVmInit();
this._botPanel = new BotPanelManager(this._watcher, this._helper);
this._idePortal = new IdePortalManager(this._watcher, this.helper);
this._recordsManager = new RecordsManager(this._config, this._helper, (endpoint, authenticateIfNotLoggedIn) => this._getRecordsEndpointInfo(endpoint, authenticateIfNotLoggedIn), undefined, (endpoint, protocol) => {
if (protocol === 'apiary-aws') {
const url = new URL(endpoint);
if (url.protocol === 'http:') {
url.protocol = 'ws:';
}
else if (url.protocol === 'https:') {
url.protocol = 'wss:';
}
const manager = new WebSocketManager(url, RECORDS_WS_PROTOCOL);
manager.init();
return new ApiGatewayWebsocketConnectionClient(manager.socket);
}
else {
const url = new URL('/websocket', endpoint);
if (url.protocol === 'http:') {
url.protocol = 'ws:';
}
else if (url.protocol === 'https:') {
url.protocol = 'wss:';
}
const manager = new WebSocketManager(url, RECORDS_WS_PROTOCOL);
manager.init();
return new WebsocketConnectionClient(manager.socket);
}
});
this._livekitManager = new LivekitManager(this._helper);
this._subscriptions.push(this._portals);
this._subscriptions.push(this._botPanel);
this._subscriptions.push(this._idePortal);
this._subscriptions.push(this._vm.localEvents
.pipe(tap((e) => this._recordsManager.handleEvents(e)))
.subscribe());
this._subscriptions.push(this._livekitManager, this._recordsManager.onRoomJoin.subscribe((join) => this._livekitManager.joinRoom(join)), this._recordsManager.onRoomLeave.subscribe((leave) => this._livekitManager.leaveRoom(leave)), this._recordsManager.onSetRoomOptions.subscribe((set) => this._livekitManager.setRoomOptions(set)), this._recordsManager.onGetRoomOptions.subscribe((set) => this._livekitManager.getRoomOptions(set)), this._vm.localEvents
.pipe(tap((e) => this._livekitManager.handleEvents(e)))
.subscribe());
}
_createSubSimulation(vm) {
const sim = new BotManager({
recordName: null,
inst: null,
isStatic: !!this._origin.isStatic,
}, {
version: this._config.version,
versionHash: this._config.versionHash,
}, vm);
sim._isSubSimulation = true;
return sim;
}
async _getRecordsEndpointInfo(endpoint, authenticateIfNotLoggedIn) {
const auth = this._getAuthEndpointHelper(endpoint);
if (!auth) {
return null;
}
let headers = {};
const token = await this._getAuthToken(auth, authenticateIfNotLoggedIn);
if (hasValue(token)) {
headers['Authorization'] = `Bearer ${token}`;
}
return {
error: false,
recordsOrigin: await auth.getRecordsOrigin(),
websocketOrigin: await auth.getWebsocketOrigin(),
websocketProtocol: await auth.getWebsocketProtocol(),
headers,
token,
};
}
async _getAuthToken(auth, authenticateIfNotLoggedIn) {
if (!auth) {
return null;
}
if (authenticateIfNotLoggedIn) {
if (!(await auth.isAuthenticated())) {
await auth.authenticate();
}
}
return auth.getAuthToken();
}
_getAuthEndpointHelper(endpoint) {
if (!endpoint) {
return null;
}
if (endpoint === this._authHelper.primaryAuthOrigin) {
return this._authHelper.primary;
}
else {
const helper = this._authHelper.getOrCreateEndpoint(endpoint);
this._subscriptions.push(helper);
return helper;
}
}
}
//# sourceMappingURL=BotManager.js.map