export default class ServiceManager{createRemoteService(serviceName){if(!this._remoteConnection){const url=Root.Runtime.queryParam('service-backend');if(!url){console.error('No endpoint address specified');return(Promise.resolve(null));} this._remoteConnection=new Connection(new RemoteServicePort(url));} return this._remoteConnection._createService(serviceName);} createAppService(appName,serviceName){let url=appName+'.js';const remoteBase=Root.Runtime.queryParam('remoteBase');const debugFrontend=Root.Runtime.queryParam('debugFrontend');const isUnderTest=Host.isUnderTest();const queryParams=[];if(remoteBase){queryParams.push('remoteBase='+remoteBase);} if(debugFrontend){queryParams.push('debugFrontend='+debugFrontend);} if(isUnderTest){queryParams.push('isUnderTest=true');} if(queryParams.length){url+=`?${queryParams.join('&')}`;} const worker=new Worker(url);const connection=new Connection(new WorkerServicePort(worker));return connection._createService(serviceName);}} class Connection{constructor(port){this._port=port;this._port.setHandlers(this._onMessage.bind(this),this._connectionClosed.bind(this));this._lastId=1;this._callbacks=new Map();this._services=new Map();} _createService(serviceName){return this._sendCommand(serviceName+'.create').then(result=>{if(!result){console.error('Could not initialize service: '+serviceName);return null;} const service=new Service(this,serviceName,result.id);this._services.set(serviceName+':'+result.id,service);return service;});} _serviceDisposed(service){this._services.delete(service._serviceName+':'+service._objectId);if(!this._services.size){this._port.close();}} _sendCommand(method,params){const id=this._lastId++;const message=JSON.stringify({id:id,method:method,params:params||{}});return this._port.send(message).then(success=>{if(!success){return Promise.resolve(null);} return new Promise(fulfill=>this._callbacks.set(id,fulfill));});} _onMessage(data){let object;try{object=JSON.parse(data);}catch(e){console.error(e);return;} if(object.id){if(object.error){console.error('Service error: '+object.error);} this._callbacks.get(object.id)(object.error?null:object.result);this._callbacks.delete(object.id);return;} const tokens=object.method.split('.');const serviceName=tokens[0];const methodName=tokens[1];const service=this._services.get(serviceName+':'+object.params.id);if(!service){console.error('Unable to lookup stub for '+serviceName+':'+object.params.id);return;} service._dispatchNotification(methodName,object.params);} _connectionClosed(){for(const callback of this._callbacks.values()){callback(null);} this._callbacks.clear();for(const service of this._services.values()){service._dispatchNotification('disposed');} this._services.clear();}} export class Service{constructor(connection,serviceName,objectId){this._connection=connection;this._serviceName=serviceName;this._objectId=objectId;this._notificationHandlers=new Map();} dispose(){const params={id:this._objectId};return this._connection._sendCommand(this._serviceName+'.dispose',params).then(()=>{this._connection._serviceDisposed(this);});} on(methodName,handler){this._notificationHandlers.set(methodName,handler);} send(methodName,params){params=params||{};params.id=this._objectId;return this._connection._sendCommand(this._serviceName+'.'+methodName,params);} _dispatchNotification(methodName,params){const handler=this._notificationHandlers.get(methodName);if(!handler){console.error('Could not report notification \''+methodName+'\' on \''+this._objectId+'\'');return;} handler(params);}} class RemoteServicePort{constructor(url){this._url=url;} setHandlers(messageHandler,closeHandler){this._messageHandler=messageHandler;this._closeHandler=closeHandler;} _open(){if(!this._connectionPromise){this._connectionPromise=new Promise(promiseBody.bind(this));} return this._connectionPromise;function promiseBody(fulfill){let socket;try{socket=new WebSocket((this._url));socket.onmessage=onMessage.bind(this);socket.onclose=onClose.bind(this);socket.onopen=onConnect.bind(this);}catch(e){fulfill(false);} function onConnect(){this._socket=socket;fulfill(true);} function onMessage(event){this._messageHandler(event.data);} function onClose(){if(!this._socket){fulfill(false);} this._socketClosed(!!this._socket);}}} send(message){return this._open().then(()=>{if(this._socket){this._socket.send(message);return true;} return false;});} close(){return this._open().then(()=>{if(this._socket){this._socket.close();this._socketClosed(true);} return true;});} _socketClosed(notifyClient){this._socket=null;delete this._connectionPromise;if(notifyClient){this._closeHandler();}}} class WorkerServicePort{constructor(worker){this._worker=worker;let fulfill;this._workerPromise=new Promise(resolve=>fulfill=resolve);this._worker.onmessage=onMessage.bind(this);this._worker.onclose=this._closeHandler;function onMessage(event){if(event.data==='workerReady'){fulfill(true);return;} this._messageHandler(event.data);}} setHandlers(messageHandler,closeHandler){this._messageHandler=messageHandler;this._closeHandler=closeHandler;} send(message){return this._workerPromise.then(()=>{try{this._worker.postMessage(message);return true;}catch(e){return false;}});} close(){return this._workerPromise.then(()=>{if(this._worker){this._worker.terminate();} return false;});}} self.Services=self.Services||{};Services=Services||{};Services.ServiceManager=ServiceManager;Services.ServiceManager.Service=Service;Services.serviceManager=new ServiceManager();