UNPKG

@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
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