From 76dee64a5461988d7f4a883c67986d51272e1ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCbleitner?= Date: Wed, 18 Sep 2019 02:58:51 +0200 Subject: [PATCH] fix --- lib/RPCaller.min.js | 19 -- .../{frontend/RPCSocket.d.ts => Client.d.ts} | 11 +- lib/src/{frontend/RPCSocket.js => Client.js} | 17 +- lib/src/Response.d.ts | 17 ++ lib/src/Response.js | 30 ++ lib/src/Server.d.ts | 14 + lib/src/Server.js | 58 ++++ lib/src/Types.d.ts | 54 ++++ lib/src/Types.js | 2 + lib/src/Util.d.ts | 4 + lib/src/Util.js | 75 +++++ lib/src/backend/RPCSocketServer.d.ts | 98 ------- lib/src/backend/RPCSocketServer.js | 165 ----------- lib/src/interfaces/Exporter.d.ts | 6 + lib/src/interfaces/Exporter.js | 2 + lib/src/interfaces/Socket.d.ts | 11 + lib/src/interfaces/Socket.js | 2 + lib/test/test.js | 10 +- package.json | 2 +- src/{frontend/RPCSocket.ts => Client.ts} | 22 +- src/Response.ts | 38 +++ src/Server.ts | 60 ++++ src/Types.ts | 66 +++++ src/Util.ts | 82 ++++++ src/backend/RPCSocketServer.ts | 267 ------------------ src/interfaces/Exporter.ts | 7 + src/interfaces/Socket.ts | 12 + src/{frontend => }/webpack.prod.js | 4 +- test/test.ts | 8 +- 29 files changed, 567 insertions(+), 596 deletions(-) delete mode 100644 lib/RPCaller.min.js rename lib/src/{frontend/RPCSocket.d.ts => Client.d.ts} (59%) rename lib/src/{frontend/RPCSocket.js => Client.js} (88%) create mode 100644 lib/src/Response.d.ts create mode 100644 lib/src/Response.js create mode 100644 lib/src/Server.d.ts create mode 100644 lib/src/Server.js create mode 100644 lib/src/Types.d.ts create mode 100644 lib/src/Types.js create mode 100644 lib/src/Util.d.ts create mode 100644 lib/src/Util.js delete mode 100644 lib/src/backend/RPCSocketServer.d.ts delete mode 100644 lib/src/backend/RPCSocketServer.js create mode 100644 lib/src/interfaces/Exporter.d.ts create mode 100644 lib/src/interfaces/Exporter.js create mode 100644 lib/src/interfaces/Socket.d.ts create mode 100644 lib/src/interfaces/Socket.js rename src/{frontend/RPCSocket.ts => Client.ts} (84%) create mode 100644 src/Response.ts create mode 100644 src/Server.ts create mode 100644 src/Types.ts create mode 100644 src/Util.ts delete mode 100644 src/backend/RPCSocketServer.ts create mode 100644 src/interfaces/Exporter.ts create mode 100644 src/interfaces/Socket.ts rename src/{frontend => }/webpack.prod.js (87%) diff --git a/lib/RPCaller.min.js b/lib/RPCaller.min.js deleted file mode 100644 index 3d523aa..0000000 --- a/lib/RPCaller.min.js +++ /dev/null @@ -1,19 +0,0 @@ -!function(t,e){for(var r in e)t[r]=e[r]}(exports,function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=6)}([function(t,e,r){"use strict"; -/*! - * assert.js - assertions for javascript - * Copyright (c) 2018, Christopher Jeffrey (MIT License). - * https://github.com/chjj/bsert - */class n extends Error{constructor(t){"string"==typeof t&&(t={message:t}),null!==t&&"object"==typeof t||(t={});let e=null,r="fail",n=Boolean(t.generatedMessage);var o;if(null!=t.message&&(e="string"==typeof(o=t.message)?o:E(o)?d(o):p(o)),"string"==typeof t.operator&&(r=t.operator),null==e){if("fail"===r)e="Assertion failed.";else{e=`${p(t.actual)} ${r} ${p(t.expected)}`}n=!0}super(e);let s=this.constructor;"function"==typeof t.stackStartFunction?s=t.stackStartFunction:"function"==typeof t.stackStartFn&&(s=t.stackStartFn),this.type="AssertionError",this.name="AssertionError [ERR_ASSERTION]",this.code="ERR_ASSERTION",this.generatedMessage=n,this.actual=t.actual,this.expected=t.expected,this.operator=r,Error.captureStackTrace&&Error.captureStackTrace(this,s)}}function o(t,e){if(!t){let r=!1;if(0===arguments.length)e="No value argument passed to `assert()`.",r=!0;else if(null==e)e="Assertion failed.",r=!0;else if(E(e))throw e;throw new n({message:e,actual:t,expected:!0,operator:"==",generatedMessage:r,stackStartFn:o})}}function s(t,e,r){if(!Object.is(t,e)){if(E(r))throw r;throw new n({message:r,actual:t,expected:e,operator:"strictEqual",stackStartFn:s})}}function i(t,e,r){if(Object.is(t,e)){if(E(r))throw r;throw new n({message:r,actual:t,expected:e,operator:"notStrictEqual",stackStartFn:i})}}function a(t,e,r){"string"==typeof e&&(r=e,e=void 0);let o=!1,s=null;l("function"==typeof t,"func","function");try{t()}catch(t){o=!0,s=t}if(o){if(g(s,e,r,a)){let t=!1;throw null==r&&(r="Got unwanted exception.",t=!0),new n({message:r,actual:s,expected:e,operator:"doesNotThrow",generatedMessage:t,stackStartFn:a})}throw s}}async function h(t,e,r){"string"==typeof e&&(r=e,e=void 0);let o=!1,s=null;"function"!=typeof t&&l(O(t),"func","promise");try{O(t)?await t:await t()}catch(t){o=!0,s=t}if(!o){let t=!1;throw null==r&&(r="Missing expected rejection.",t=!0),new n({message:r,actual:void 0,expected:e,operator:"rejects",generatedMessage:t,stackStartFn:h})}if(!g(s,e,r,h))throw s}async function u(t,e,r){"string"==typeof e&&(r=e,e=void 0);let o=!1,s=null;"function"!=typeof t&&l(O(t),"func","promise");try{O(t)?await t:await t()}catch(t){o=!0,s=t}if(o){if(g(s,e,r,u)){let t=!1;throw null==r&&(r="Got unwanted rejection.",t=!0),new n({message:r,actual:void 0,expected:e,operator:"doesNotReject",generatedMessage:t,stackStartFn:u})}throw s}}function c(t,e,r){if(!y(t,e,!1)){if(E(r))throw r;throw new n({message:r,actual:t,expected:e,operator:"deepStrictEqual",stackStartFn:c})}}function f(t,e,r){if(y(t,e,!0)){if(E(r))throw r;throw new n({message:r,actual:t,expected:e,operator:"notDeepStrictEqual",stackStartFn:f})}}function l(t,e,r){if(!t){let t;t=null==e?"Invalid type for parameter.":null==r?`Invalid type for "${e}".`:`"${e}" must be a(n) ${r}.`;const n=new TypeError(t);throw Error.captureStackTrace&&Error.captureStackTrace(n,l),n}}function p(t){switch(typeof t){case"undefined":return"undefined";case"object":return null===t?"null":`[${function(t){const e=function(t){return v(t).slice(8,-1)}(t);if(null==t)return e;if("Object"!==e&&"Error"!==e)return e;let r,n;try{r=t.constructor}catch(t){}if(null==r)return e;try{n=r.name}catch(t){return e}return"string"!=typeof n||0===n.length?e:n}(t)}]`;case"boolean":case"number":return`${t}`;case"string":return t.length>80&&(t=`${t.substring(0,77)}...`),JSON.stringify(t);case"symbol":return d(t);case"function":return`[${function(t){let e;try{e=t.name}catch(t){}return"string"!=typeof e||0===e.length?"Function":`Function: ${e}`}(t)}]`;case"bigint":return`${t}n`;default:return`[${typeof t}]`}}function d(t){try{return String(t)}catch(t){return"Object"}}function g(t,e,r,o){if(null==e)return!0;if(S(e))return e.test(t);if("function"!=typeof e){if(o===a||o===u)throw new TypeError('"expected" must not be an object.');if("object"!=typeof e)throw new TypeError('"expected" must be an object.');let s=!1;if(null==r){r=`Missing expected ${o===h?"rejection":"exception"}.`,s=!0}if(null==t||"object"!=typeof t)throw new n({actual:t,expected:e,message:r,operator:o.name,generatedMessage:s,stackStartFn:o});const i=Object.keys(e);if(E(e)&&i.push("name","message"),0===i.length)throw new TypeError('"expected" may not be an empty object.');for(const a of i){const i=e[a],h=t[a];if(("string"!=typeof h||!S(i)||!i.test(h))&&!(a in t&&y(h,i,!1)))throw new n({actual:t,expected:e,message:r,operator:o.name,generatedMessage:s,stackStartFn:o})}return!0}return void 0!==e.prototype&&t instanceof e||!Error.isPrototypeOf(e)&&!0===e.call({},t)}function y(t,e,r){try{return m(t,e,null)}catch(t){return r}}function m(t,e,r){if(Object.is(t,e))return!0;if(!A(t)||!A(e))return!1;if(v(t)!==v(e))return!1;if(Object.getPrototypeOf(t)!==Object.getPrototypeOf(e))return!1;if(b(t)&&b(e))return t.equals(e);if(t instanceof Date)return Object.is(t.getTime(),e.getTime());if(S(t))return t.source===e.source&&t.global===e.global&&t.multiline===e.multiline&&t.lastIndex===e.lastIndex&&t.ignoreCase===e.ignoreCase;if(E(t)&&t.message!==e.message)return!1;if(function(t){return t instanceof ArrayBuffer}(t)&&(t=new Uint8Array(t),e=new Uint8Array(e)),function(t){return ArrayBuffer.isView(t)}(t)&&!b(t)){if(b(e))return!1;const r=new Uint8Array(t.buffer),n=new Uint8Array(e.buffer);if(r.length!==n.length)return!1;for(let t=0;t>>1?null:o}return e}o.AssertionError=n,o.assert=o,o.strict=o,o.ok=o,o.equal=s,o.notEqual=i,o.strictEqual=s,o.notStrictEqual=i,o.fail=function t(e){let r=!1;if(E(e))throw e;throw null==e&&(e="Assertion failed.",r=!0),new n({message:e,actual:!1,expected:!0,operator:"fail",generatedMessage:r,stackStartFn:t})},o.throws=function t(e,r,o){"string"==typeof r&&(o=r,r=void 0);let s=!1,i=null;l("function"==typeof e,"func","function");try{e()}catch(t){s=!0,i=t}if(!s){let e=!1;throw null==o&&(o="Missing expected exception.",e=!0),new n({message:o,actual:void 0,expected:r,operator:"throws",generatedMessage:e,stackStartFn:t})}if(!g(i,r,o,t))throw i},o.doesNotThrow=a,o.rejects=h,o.doesNotReject=u,o.ifError=function t(e){if(null!=e){let r="ifError got unwanted exception: ";throw"object"==typeof e&&"string"==typeof e.message?0===e.message.length&&e.constructor?r+=e.constructor.name:r+=e.message:r+=p(e),new n({message:r,actual:e,expected:null,operator:"ifError",generatedMessage:!0,stackStartFn:t})}},o.deepEqual=c,o.notDeepEqual=f,o.deepStrictEqual=c,o.notDeepStrictEqual=f,o.bufferEqual=function t(e,r,o,s){if(R(o)||(s=o,o=null),null==o&&(o="hex"),r=_(e,r,o),l(b(e),"actual","buffer"),l(b(r),"expected","buffer"),e!==r&&!e.equals(r)){if(E(s))throw s;throw new n({message:s,actual:e.toString(o),expected:r.toString(o),operator:"bufferEqual",stackStartFn:t})}},o.notBufferEqual=function t(e,r,o,s){if(R(o)||(s=o,o=null),null==o&&(o="hex"),r=_(e,r,o),l(b(e),"actual","buffer"),l(b(r),"expected","buffer"),e===r||e.equals(r)){if(E(s))throw s;throw new n({message:s,actual:e.toString(o),expected:r.toString(o),operator:"notBufferEqual",stackStartFn:t})}},o.enforce=l,o.range=function t(e,r){if(!e){const e=new RangeError(null!=r?`"${r}" is out of range.`:"Parameter is out of range.");throw Error.captureStackTrace&&Error.captureStackTrace(e,t),e}},t.exports=o},function(t,e,r){"use strict";(function(t){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -var n=r(10),o=r(11),s=r(12);function i(){return h.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function a(t,e){if(i()=i())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+i().toString(16)+" bytes");return 0|t}function d(t,e){if(h.isBuffer(t))return t.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer))return t.byteLength;"string"!=typeof t&&(t=""+t);var r=t.length;if(0===r)return 0;for(var n=!1;;)switch(e){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return F(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return q(t).length;default:if(n)return F(t).length;e=(""+e).toLowerCase(),n=!0}}function g(t,e,r){var n=!1;if((void 0===e||e<0)&&(e=0),e>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return j(this,e,r);case"utf8":case"utf-8":return _(this,e,r);case"ascii":return P(this,e,r);case"latin1":case"binary":return T(this,e,r);case"base64":return R(this,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,e,r);default:if(n)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),n=!0}}function y(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}function m(t,e,r,n,o){if(0===t.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=o?0:t.length-1),r<0&&(r=t.length+r),r>=t.length){if(o)return-1;r=t.length-1}else if(r<0){if(!o)return-1;r=0}if("string"==typeof e&&(e=h.from(e,n)),h.isBuffer(e))return 0===e.length?-1:w(t,e,r,n,o);if("number"==typeof e)return e&=255,h.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(t,e,r):Uint8Array.prototype.lastIndexOf.call(t,e,r):w(t,[e],r,n,o);throw new TypeError("val must be string, number or Buffer")}function w(t,e,r,n,o){var s,i=1,a=t.length,h=e.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(t.length<2||e.length<2)return-1;i=2,a/=2,h/=2,r/=2}function u(t,e){return 1===i?t[e]:t.readUInt16BE(e*i)}if(o){var c=-1;for(s=r;sa&&(r=a-h),s=r;s>=0;s--){for(var f=!0,l=0;lo&&(n=o):n=o;var s=e.length;if(s%2!=0)throw new TypeError("Invalid hex string");n>s/2&&(n=s/2);for(var i=0;i>8,o=r%256,s.push(o),s.push(n);return s}(e,t.length-r),t,r,n)}function R(t,e,r){return 0===e&&r===t.length?n.fromByteArray(t):n.fromByteArray(t.slice(e,r))}function _(t,e,r){r=Math.min(t.length,r);for(var n=[],o=e;o239?4:u>223?3:u>191?2:1;if(o+f<=r)switch(f){case 1:u<128&&(c=u);break;case 2:128==(192&(s=t[o+1]))&&(h=(31&u)<<6|63&s)>127&&(c=h);break;case 3:s=t[o+1],i=t[o+2],128==(192&s)&&128==(192&i)&&(h=(15&u)<<12|(63&s)<<6|63&i)>2047&&(h<55296||h>57343)&&(c=h);break;case 4:s=t[o+1],i=t[o+2],a=t[o+3],128==(192&s)&&128==(192&i)&&128==(192&a)&&(h=(15&u)<<18|(63&s)<<12|(63&i)<<6|63&a)>65535&&h<1114112&&(c=h)}null===c?(c=65533,f=1):c>65535&&(c-=65536,n.push(c>>>10&1023|55296),c=56320|1023&c),n.push(c),o+=f}return function(t){var e=t.length;if(e<=k)return String.fromCharCode.apply(String,t);var r="",n=0;for(;n0&&(t=this.toString("hex",0,r).match(/.{2}/g).join(" "),this.length>r&&(t+=" ... ")),""},h.prototype.compare=function(t,e,r,n,o){if(!h.isBuffer(t))throw new TypeError("Argument must be a Buffer");if(void 0===e&&(e=0),void 0===r&&(r=t?t.length:0),void 0===n&&(n=0),void 0===o&&(o=this.length),e<0||r>t.length||n<0||o>this.length)throw new RangeError("out of range index");if(n>=o&&e>=r)return 0;if(n>=o)return-1;if(e>=r)return 1;if(this===t)return 0;for(var s=(o>>>=0)-(n>>>=0),i=(r>>>=0)-(e>>>=0),a=Math.min(s,i),u=this.slice(n,o),c=t.slice(e,r),f=0;fo)&&(r=o),t.length>0&&(r<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var s=!1;;)switch(n){case"hex":return v(this,t,e,r);case"utf8":case"utf-8":return b(this,t,e,r);case"ascii":return E(this,t,e,r);case"latin1":case"binary":return A(this,t,e,r);case"base64":return O(this,t,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,t,e,r);default:if(s)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),s=!0}},h.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var k=4096;function P(t,e,r){var n="";r=Math.min(t.length,r);for(var o=e;on)&&(r=n);for(var o="",s=e;sr)throw new RangeError("Trying to access beyond buffer length")}function N(t,e,r,n,o,s){if(!h.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>o||et.length)throw new RangeError("Index out of range")}function I(t,e,r,n){e<0&&(e=65535+e+1);for(var o=0,s=Math.min(t.length-r,2);o>>8*(n?o:1-o)}function B(t,e,r,n){e<0&&(e=4294967295+e+1);for(var o=0,s=Math.min(t.length-r,4);o>>8*(n?o:3-o)&255}function U(t,e,r,n,o,s){if(r+n>t.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function L(t,e,r,n,s){return s||U(t,0,r,4),o.write(t,e,r,n,23,4),r+4}function M(t,e,r,n,s){return s||U(t,0,r,8),o.write(t,e,r,n,52,8),r+8}h.prototype.slice=function(t,e){var r,n=this.length;if((t=~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),(e=void 0===e?n:~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),e0&&(o*=256);)n+=this[t+--e]*o;return n},h.prototype.readUInt8=function(t,e){return e||x(t,1,this.length),this[t]},h.prototype.readUInt16LE=function(t,e){return e||x(t,2,this.length),this[t]|this[t+1]<<8},h.prototype.readUInt16BE=function(t,e){return e||x(t,2,this.length),this[t]<<8|this[t+1]},h.prototype.readUInt32LE=function(t,e){return e||x(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},h.prototype.readUInt32BE=function(t,e){return e||x(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},h.prototype.readIntLE=function(t,e,r){t|=0,e|=0,r||x(t,e,this.length);for(var n=this[t],o=1,s=0;++s=(o*=128)&&(n-=Math.pow(2,8*e)),n},h.prototype.readIntBE=function(t,e,r){t|=0,e|=0,r||x(t,e,this.length);for(var n=e,o=1,s=this[t+--n];n>0&&(o*=256);)s+=this[t+--n]*o;return s>=(o*=128)&&(s-=Math.pow(2,8*e)),s},h.prototype.readInt8=function(t,e){return e||x(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},h.prototype.readInt16LE=function(t,e){e||x(t,2,this.length);var r=this[t]|this[t+1]<<8;return 32768&r?4294901760|r:r},h.prototype.readInt16BE=function(t,e){e||x(t,2,this.length);var r=this[t+1]|this[t]<<8;return 32768&r?4294901760|r:r},h.prototype.readInt32LE=function(t,e){return e||x(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},h.prototype.readInt32BE=function(t,e){return e||x(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},h.prototype.readFloatLE=function(t,e){return e||x(t,4,this.length),o.read(this,t,!0,23,4)},h.prototype.readFloatBE=function(t,e){return e||x(t,4,this.length),o.read(this,t,!1,23,4)},h.prototype.readDoubleLE=function(t,e){return e||x(t,8,this.length),o.read(this,t,!0,52,8)},h.prototype.readDoubleBE=function(t,e){return e||x(t,8,this.length),o.read(this,t,!1,52,8)},h.prototype.writeUIntLE=function(t,e,r,n){(t=+t,e|=0,r|=0,n)||N(this,t,e,r,Math.pow(2,8*r)-1,0);var o=1,s=0;for(this[e]=255&t;++s=0&&(s*=256);)this[e+o]=t/s&255;return e+r},h.prototype.writeUInt8=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,1,255,0),h.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[e]=255&t,e+1},h.prototype.writeUInt16LE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,2,65535,0),h.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):I(this,t,e,!0),e+2},h.prototype.writeUInt16BE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,2,65535,0),h.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):I(this,t,e,!1),e+2},h.prototype.writeUInt32LE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,4,4294967295,0),h.TYPED_ARRAY_SUPPORT?(this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t):B(this,t,e,!0),e+4},h.prototype.writeUInt32BE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,4,4294967295,0),h.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):B(this,t,e,!1),e+4},h.prototype.writeIntLE=function(t,e,r,n){if(t=+t,e|=0,!n){var o=Math.pow(2,8*r-1);N(this,t,e,r,o-1,-o)}var s=0,i=1,a=0;for(this[e]=255&t;++s>0)-a&255;return e+r},h.prototype.writeIntBE=function(t,e,r,n){if(t=+t,e|=0,!n){var o=Math.pow(2,8*r-1);N(this,t,e,r,o-1,-o)}var s=r-1,i=1,a=0;for(this[e+s]=255&t;--s>=0&&(i*=256);)t<0&&0===a&&0!==this[e+s+1]&&(a=1),this[e+s]=(t/i>>0)-a&255;return e+r},h.prototype.writeInt8=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,1,127,-128),h.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),t<0&&(t=255+t+1),this[e]=255&t,e+1},h.prototype.writeInt16LE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,2,32767,-32768),h.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):I(this,t,e,!0),e+2},h.prototype.writeInt16BE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,2,32767,-32768),h.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):I(this,t,e,!1),e+2},h.prototype.writeInt32LE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,4,2147483647,-2147483648),h.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24):B(this,t,e,!0),e+4},h.prototype.writeInt32BE=function(t,e,r){return t=+t,e|=0,r||N(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),h.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):B(this,t,e,!1),e+4},h.prototype.writeFloatLE=function(t,e,r){return L(this,t,e,!0,r)},h.prototype.writeFloatBE=function(t,e,r){return L(this,t,e,!1,r)},h.prototype.writeDoubleLE=function(t,e,r){return M(this,t,e,!0,r)},h.prototype.writeDoubleBE=function(t,e,r){return M(this,t,e,!1,r)},h.prototype.copy=function(t,e,r,n){if(r||(r=0),n||0===n||(n=this.length),e>=t.length&&(e=t.length),e||(e=0),n>0&&n=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),t.length-e=0;--o)t[o+e]=this[o+r];else if(s<1e3||!h.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,r=void 0===r?this.length:r>>>0,t||(t=0),"number"==typeof t)for(s=e;s55295&&r<57344){if(!o){if(r>56319){(e-=3)>-1&&s.push(239,191,189);continue}if(i+1===n){(e-=3)>-1&&s.push(239,191,189);continue}o=r;continue}if(r<56320){(e-=3)>-1&&s.push(239,191,189),o=r;continue}r=65536+(o-55296<<10|r-56320)}else o&&(e-=3)>-1&&s.push(239,191,189);if(o=null,r<128){if((e-=1)<0)break;s.push(r)}else if(r<2048){if((e-=2)<0)break;s.push(r>>6|192,63&r|128)}else if(r<65536){if((e-=3)<0)break;s.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;s.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return s}function q(t){return n.toByteArray(function(t){if((t=function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}(t).replace(D,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function $(t,e,r,n){for(var o=0;o=e.length||o>=t.length);++o)e[o+r]=t[o];return o}}).call(this,r(2))},function(t,e){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(t){"object"==typeof window&&(r=window)}t.exports=r},function(t,e,r){"use strict";var n,o="object"==typeof Reflect?Reflect:null,s=o&&"function"==typeof o.apply?o.apply:function(t,e,r){return Function.prototype.apply.call(t,e,r)};n=o&&"function"==typeof o.ownKeys?o.ownKeys:Object.getOwnPropertySymbols?function(t){return Object.getOwnPropertyNames(t).concat(Object.getOwnPropertySymbols(t))}:function(t){return Object.getOwnPropertyNames(t)};var i=Number.isNaN||function(t){return t!=t};function a(){a.init.call(this)}t.exports=a,a.EventEmitter=a,a.prototype._events=void 0,a.prototype._eventsCount=0,a.prototype._maxListeners=void 0;var h=10;function u(t){return void 0===t._maxListeners?a.defaultMaxListeners:t._maxListeners}function c(t,e,r,n){var o,s,i,a;if("function"!=typeof r)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof r);if(void 0===(s=t._events)?(s=t._events=Object.create(null),t._eventsCount=0):(void 0!==s.newListener&&(t.emit("newListener",e,r.listener?r.listener:r),s=t._events),i=s[e]),void 0===i)i=s[e]=r,++t._eventsCount;else if("function"==typeof i?i=s[e]=n?[r,i]:[i,r]:n?i.unshift(r):i.push(r),(o=u(t))>0&&i.length>o&&!i.warned){i.warned=!0;var h=new Error("Possible EventEmitter memory leak detected. "+i.length+" "+String(e)+" listeners added. Use emitter.setMaxListeners() to increase limit");h.name="MaxListenersExceededWarning",h.emitter=t,h.type=e,h.count=i.length,a=h,console&&console.warn&&console.warn(a)}return t}function f(){for(var t=[],e=0;e0&&(i=e[0]),i instanceof Error)throw i;var a=new Error("Unhandled error."+(i?" ("+i.message+")":""));throw a.context=i,a}var h=o[t];if(void 0===h)return!1;if("function"==typeof h)s(h,this,e);else{var u=h.length,c=g(h,u);for(r=0;r=0;s--)if(r[s]===e||r[s].listener===e){i=r[s].listener,o=s;break}if(o<0)return this;0===o?r.shift():function(t,e){for(;e+1=0;n--)this.removeListener(t,e[n]);return this},a.prototype.listeners=function(t){return p(this,t,!0)},a.prototype.rawListeners=function(t){return p(this,t,!1)},a.listenerCount=function(t,e){return"function"==typeof t.listenerCount?t.listenerCount(e):d.call(t,e)},a.prototype.listenerCount=d,a.prototype.eventNames=function(){return this._eventsCount>0?n(this._events):[]}},function(t,e,r){"use strict";(function(e){t.exports={Client:e.WebSocket||e.MozWebSocket,EventSource:e.EventSource}}).call(this,r(2))},function(t,e,r){"use strict";(function(e){const n=r(0),o=e.alloc(0),s={OPEN:0,CLOSE:1,PING:2,PONG:3,MESSAGE:4,UPGRADE:5,NOOP:6};class i{constructor(t,r,i){n("number"==typeof t),n(t>>>0===t),n(t<=s.NOOP),n("boolean"==typeof i),i?(null==r&&(r=o),n(e.isBuffer(r))):(null==r&&(r=""),n("string"==typeof r)),this.type=t,this.data=r,this.binary=i}toString(){let t="";return this.binary?(t+="b",t+=this.type.toString(10),t+=this.data.toString("base64")):(t+=this.type.toString(10),t+=this.data),t}static fromString(t){n("string"==typeof t);let r,o=t.charCodeAt(0),i=!1;return 98===o?(n(t.length>1),o=t.charCodeAt(1),r=e.from(t.substring(2),"base64"),i=!0):r=t.substring(1),n((o-=48)>=0&&o<=9),n(o<=s.NOOP),new this(o,r,i)}size(){let t=1;return this.binary?t+=this.data.length:t+=e.byteLength(this.data,"utf8"),t}toRaw(){const t=e.allocUnsafe(this.size());return t[0]=this.type,this.binary?this.data.copy(t,1):this.data.length>0&&t.write(this.data,1,"utf8"),t}static fromRaw(t){n(e.isBuffer(t)),n(t.length>0);const r=t[0];return n(r<=s.NOOP),new this(r,t.slice(1),!0)}}i.types=s,i.table=["open","close","ping","pong","message","upgrade","noop"],t.exports=i}).call(this,r(1).Buffer)},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var bsock=__webpack_require__(7);function stripAfterEquals(t){return t.split("=")[0]}class RPCSocket{constructor(t,e,r=!1){this.port=t,this.server=e,this.tls=r}hook(t,e){return this.socket.hook(t,e)}unhook(t){return this.socket.unhook(t)}on(t,e){return this.socket.on(t,name)}destroy(){return this.socket.destroy()}close(){return this.socket.close()}async call(t,...e){return await this.socket.call.apply(this.socket,[t,...e])}async fire(t,...e){return await this.socket.fire.apply(this.socket,[t,...e])}async connect(){this.socket=await bsock.connect(this.port,this.server,this.tls),(await this.info()).forEach(t=>{let e;switch(t.type){case"call":e=this.callGenerator(t.uniqueName,t.argNames);break;case"hook":e=this.hookGenerator(t.uniqueName,t.argNames);break;case"unhook":e=this.unhookGenerator(t.uniqueName,t.argNames)}null==this[t.owner]&&(this[t.owner]={}),this[t.owner][t.name]=e,this[t.owner][t.name].bind(this)})}async info(){return await this.socket.call("info")}callGenerator(fnName,fnArgs){const headerArgs=fnArgs.join(","),argParams=fnArgs.map(stripAfterEquals).join(",");return eval("( () => async ("+headerArgs+') => { return await this.socket.call("'+fnName+'", '+argParams+")} )()")}hookGenerator(fnName,fnArgs){const headerArgs=fnArgs.join(","),argParams=fnArgs.map(stripAfterEquals).join(",");return eval("( () => async ("+headerArgs+(0!==headerArgs.length?",":"")+' callback) => {\n const r = await this.socket.call("'+fnName+'", '+argParams+")\n if(r.uid != null){\n this.socket.hook(res.uid, callback)\n }\n return res\n } )()")}unhookGenerator(fnName,fnArgs){const headerArgs=fnArgs.join(","),argParams=fnArgs.map(stripAfterEquals).join(",");return 1!=fnArgs.length&&console.error("UnhookFunction",fnName,"specified more than one argument: ("+headerArgs+")"),eval("( () => async ("+headerArgs+') => {\n const r = await this.socket.call("'+fnName+'", '+argParams+")\n this.socket.unhook("+argParams+")\n return res\n } )()")}}exports.RPCSocket=RPCSocket},function(t,e,r){"use strict";const n=r(4),o=r(8),s=r(9);e.WebSocket=n,e.Server=o,e.server=()=>new o,e.createServer=o.createServer.bind(o),e.attach=o.attach.bind(o),e.Socket=s,e.socket=()=>new s,e.connect=s.connect.bind(s)},function(t,e,r){"use strict";const n=r(3);t.exports=class extends n{constructor(t){super(),this.sockets=new Set,this.channels=new Map,this.mounts=[]}attach(){return this}mount(){}async open(){}async close(){}join(){return!0}leave(){return!0}channel(){return null}to(){}all(){}static attach(t,e){return new this(e).attach(t)}static createServer(t){return new this(t)}}},function(t,e,r){"use strict";(function(e){const n=r(0),o=r(3),s=r(4).Client,i=r(13),a=r(5),h=r(14),u=r(22),c=r(23),f=r(24);class l{constructor(t,e,r){this.resolve=t,this.reject=e,this.time=r}}function p(t){return null!==t&&"number"!=typeof t&&"string"!=typeof t?null:t}function d(t){return"string"!=typeof t?"No message.":t}function g(t){return"string"!=typeof t?null:t}function y(t,e,r){if(!t){const t=new TypeError(`'${e}' must be a(n) ${r}.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,y),t}}t.exports=class extends o{constructor(){super(),this.server=null,this.ws=null,this.protocol="",this.url="ws://127.0.0.1:80/socket.io/?transport=websocket",this.ssl=!1,this.host="127.0.0.1",this.port=80,this.inbound=!1,this.handshake=!1,this.opened=!1,this.connected=!1,this.challenge=!1,this.destroyed=!1,this.reconnection=!0,this.time=0,this.sequence=0,this.pingInterval=25e3,this.pingTimeout=6e4,this.lastPing=0,this.parser=new u,this.binary=!1,this.packet=null,this.timer=null,this.jobs=new Map,this.hooks=new Map,this.channels=new Set,this.events=new o,this.buffer=[],this.admin=!1,this.auth=!1}accept(t,e,r,o){n(!this.ws,"Cannot accept twice."),n(t),n(e),n(r),n(r.remoteAddress),n(null!=r.remotePort),n(o);let s="ws",i=r.remoteAddress,a=r.remotePort;return r.encrypted&&(s="wss"),-1!==i.indexOf(":")&&(i=`[${i}]`),a||(a=0),this.server=t,this.binary=-1===e.url.indexOf("b64=1"),this.url=`${s}://${i}:${a}/socket.io/?transport=websocket`,this.ssl="wss"===s,this.host=r.remoteAddress,this.port=r.remotePort,this.inbound=!0,this.ws=o,this.init(),this}connect(t,e,r,o){n(!this.ws,"Cannot connect twice."),"string"==typeof t&&(o=e,[t,e,r]=h.parseURL(t));let i="ws";r&&(i="wss"),e||(e="127.0.0.1"),n("string"==typeof e),n((65535&t)===t,"Must pass a port."),n(!r||"boolean"==typeof r),n(!o||Array.isArray(o));let a=e;-1!==e.indexOf(":")&&"["!==e[0]&&(a=`[${e}]`);const u=`${i}://${a}:${t}/socket.io/?transport=websocket`;return this.binary=!0,this.url=u,this.ssl=r,this.host=e,this.port=t,this.inbound=!1,this.ws=new s(u,o),this.init(),this}init(){this.protocol=this.ws.protocol,this.time=Date.now(),this.observe(),this.parser.on("error",t=>{this.emit("error",t)}),this.parser.on("frame",async t=>{try{await this.handleFrame(t)}catch(t){this.emit("error",t)}}),this.start()}observe(){const t=this.ws;n(t),t.binaryType="arraybuffer",t.onopen=async()=>{await this.onOpen()},t.onmessage=async t=>{await this.onMessage(t)},t.onerror=async t=>{await this.onError(t)},t.onclose=async t=>{await this.onClose(t)}}async onOpen(){this.destroyed||this.inbound&&(n(!this.opened),n(!this.connected),n(!this.handshake),this.opened=!0,this.handshake=!0,await this.emitAsync("open"),this.sendHandshake(),this.connected=!0,await this.emitAsync("connect"),this.sendConnect())}async emitAsync(t,...e){const r=this.listeners(t);for(const t of r)try{await t(...e)}catch(t){this.emit("error",t)}}async onMessage(t){if(this.destroyed)return;let r;try{r=await function(t){return new Promise((r,n)=>{if("string"!=typeof t)if(t&&"object"==typeof t)if(e.isBuffer(t))r(t);else if(t instanceof ArrayBuffer){const n=e.from(t);r(n)}else if(t.buffer instanceof ArrayBuffer){const n=e.from(t.buffer,t.byteOffset,t.byteLength);r(n)}else{if("undefined"!=typeof Blob&&Blob&&t instanceof Blob){const n=new FileReader;return n.onloadend=()=>{const t=e.from(n.result);r(t)},void n.readAsArrayBuffer(t)}n(new Error("Bad data object."))}else n(new Error("Bad data object."));else r(t)})}(t.data)}catch(t){return void this.emit("error",t)}"string"!=typeof r?this.parser.feedBinary(r):this.parser.feedString(r)}async onError(t){this.destroyed||(this.emit("error",new Error(t.message)),this.inbound?this.destroy():this.close())}async onClose(t){if(this.destroyed)return;if(1e3===t.code||1001===t.code)return this.connected||this.emit("error",new Error("Could not connect.")),this.inbound?void this.destroy():void this.close();const e=c[t.code]||"UNKNOWN_CODE",r=t.reason||"Unknown reason",n=new Error(`Websocket Closed: ${r} (code=${e}).`);n.reason=t.reason||"",n.code=t.code||0,this.emit("error",n),this.inbound?this.destroy():this.reconnection?this.close():this.destroy()}close(){if(!this.destroyed){this.time=Date.now(),this.packet=null,this.handshake=!1,this.connected=!1,this.challenge=!1,this.sequence=0,this.lastPing=0;for(const[t,e]of this.jobs)this.jobs.delete(t),e.reject(new Error("Job timed out."));n(this.ws),this.ws.onopen=()=>{},this.ws.onmessage=()=>{},this.ws.onerror=()=>{},this.ws.onclose=()=>{},this.ws.close(),this.emitAsync("disconnect")}}error(t){this.destroyed||this.emit("error",new Error(t))}destroy(){this.destroyed||(this.close(),this.stop(),this.opened=!1,this.destroyed=!0,this.buffer.length=0,this.emitAsync("close"),this.removeAllListeners(),this.on("error",()=>{}))}send(t){this.destroyed||(n(this.ws),t.binary&&this.binary?this.ws.send(t.toRaw()):this.ws.send(t.toString()))}reconnect(){n(!this.inbound),this.close(),this.ws=new s(this.url),this.time=Date.now(),this.observe()}start(){n(this.ws),n(null==this.timer),this.timer=setInterval(()=>this.stall(),5e3)}stop(){null!=this.timer&&(clearInterval(this.timer),this.timer=null)}stall(){const t=Date.now();if(n(this.ws),!this.connected)return t-this.time>1e4?this.inbound||!this.reconnection?(this.error("Timed out waiting for connection."),void this.destroy()):(this.error("Timed out waiting for connection. Reconnecting..."),void this.reconnect()):void 0;for(const[e,r]of this.jobs)t-r.time>6e5&&(this.jobs.delete(e),r.reject(new Error("Job timed out.")));return this.inbound||this.challenge?!this.inbound&&t-this.lastPing>this.pingTimeout?(this.error("Connection is stalling (ping)."),this.inbound?void this.destroy():void this.close()):void 0:(this.challenge=!0,this.lastPing=t,void this.sendPing())}async handleFrame(t){if(!this.destroyed)switch(t.type){case a.types.OPEN:return this.handleOpen(t);case a.types.CLOSE:return this.handleClose(t);case a.types.PING:return this.handlePing(t);case a.types.PONG:return this.handlePong(t);case a.types.MESSAGE:return this.handleMessage(t);case a.types.UPGRADE:return this.handleUpgrade(t);case a.types.NOOP:return this.handleNoop(t);default:throw new Error("Unknown frame.")}}async handleOpen(t){if(this.inbound)throw new Error("Inbound socket sent an open frame.");if(t.binary)throw new Error("Received a binary open frame.");if(this.handshake)throw new Error("Duplicate open frame.");const e=JSON.parse(t.data);y(e&&"object"==typeof e,"open","object");const{pingInterval:r,pingTimeout:n}=e;y(r>>>0===r,"interval","uint32"),y(n>>>0===n,"timeout","uint32"),this.pingInterval=r,this.pingTimeout=n,this.handshake=!0,this.opened||(this.opened=!0,await this.emitAsync("open"))}async handleClose(t){if(this.inbound)throw new Error("Inbound socket sent a close frame.");this.close()}async handlePing(){if(!this.inbound)throw new Error("Outbound socket sent a ping frame.");this.sendPong()}async handlePong(){if(this.inbound)throw new Error("Inbound socket sent a pong frame.");if(!this.challenge)return this.error("Remote node sent bad pong."),void this.destroy();this.challenge=!1}async handleMessage(t){if(this.packet){const e=this.packet;if(!t.binary)throw new Error("Received non-binary frame as attachment.");return e.buffers.push(t.data),e.buffers.length===e.attachments?(this.packet=null,this.handlePacket(e)):void 0}if(t.binary)throw new Error("Received binary frame as a message.");const e=i.fromString(t.data);if(!(e.attachments>0))return this.handlePacket(e);this.packet=e}async handleUpgrade(t){if(!this.inbound)throw new Error("Outbound socket sent an upgrade frame.");throw new Error("Cannot upgrade from websocket.")}async handleNoop(t){}sendFrame(t,e,r){this.send(new a(t,e,r))}sendOpen(t){this.sendFrame(a.types.OPEN,t,!1)}sendClose(t){this.sendFrame(a.types.CLOSE,t,!1)}sendPing(t){this.sendFrame(a.types.PING,t,!1)}sendPong(t){this.sendFrame(a.types.PONG,t,!1)}sendMessage(t){this.sendFrame(a.types.MESSAGE,t,!1)}sendBinary(t){this.sendFrame(a.types.MESSAGE,t,!0)}sendHandshake(){const t=JSON.stringify({sid:"00000000000000000000",upgrades:[],pingInterval:this.pingInterval,pingTimeout:this.pingTimeout});this.sendOpen(t)}async handlePacket(t){if(!this.destroyed)switch(t.type){case i.types.CONNECT:return this.handleConnect();case i.types.DISCONNECT:return this.handleDisconnect();case i.types.EVENT:case i.types.BINARY_EVENT:{const e=t.getData();return y(Array.isArray(e),"args","array"),y(e.length>0,"args","array"),y("string"==typeof e[0],"event","string"),-1!==t.id?this.handleCall(t.id,e):this.handleEvent(e)}case i.types.ACK:case i.types.BINARY_ACK:{y(-1!==t.id,"id","uint32");const e=t.getData();y(null==e||Array.isArray(e),"args","array");let r=null,n=null;return e&&e.length>0&&(r=e[0]),e&&e.length>1&&(n=e[1]),null==n&&(n=null),r?(y("object"==typeof r,"error","object"),this.handleError(t.id,r)):this.handleAck(t.id,n)}case i.types.ERROR:{const e=t.getData();return y(e&&"object"==typeof e,"error","object"),this.handleError(-1,e)}default:throw new Error("Unknown packet.")}}async handleConnect(){if(this.inbound)throw new Error("Inbound socket sent connect packet.");this.connected=!0,await this.emitAsync("connect");for(const t of this.buffer)this.sendPacket(t);this.buffer.length=0}async handleDisconnect(){this.close()}async handleEvent(t){try{const e=t[0];if(f.hasOwnProperty(e))throw new Error(`Cannot emit blacklisted event: ${e}.`);this.events.emit(...t)}catch(t){this.emit("error",t),this.sendError(-1,t)}}async handleCall(t,e){let r;try{const t=e.shift();if(f.hasOwnProperty(t))throw new Error(`Cannot emit blacklisted event: ${t}.`);const n=this.hooks.get(t);if(!n)throw new Error(`Call not found: ${t}.`);r=await n(...e)}catch(e){return this.emit("error",e),void this.sendError(t,e)}null==r&&(r=null),this.sendAck(t,r)}async handleAck(t,e){const r=this.jobs.get(t);if(!r)throw new Error(`Job not found for ${t}.`);this.jobs.delete(t),r.resolve(e)}async handleError(t,e){const r=d(e.message),n=g(e.name),o=g(e.type),s=p(e.code);if(-1===t){const t=new Error(r);return t.name=n,t.type=o,t.code=s,void this.emit("error",t)}const i=this.jobs.get(t);if(!i)throw new Error(`Job not found for ${t}.`);this.jobs.delete(t);const a=new Error(r);a.name=n,a.type=o,a.code=s,i.reject(a)}sendPacket(t){this.sendMessage(t.toString());for(const e of t.buffers)this.sendBinary(e)}sendConnect(){this.sendPacket(new i(i.types.CONNECT))}sendDisconnect(){this.sendPacket(new i(i.types.DISCONNECT))}sendEvent(t){const e=new i;e.type=i.types.EVENT,e.setData(t),this.connected?this.sendPacket(e):this.buffer.push(e)}sendCall(t,e){const r=new i;r.type=i.types.EVENT,r.id=t,r.setData(e),this.connected?this.sendPacket(r):this.buffer.push(r)}sendAck(t,e){const r=new i;r.type=i.types.ACK,r.id=t,r.setData([null,e]),this.sendPacket(r)}sendError(t,e){const r=d(e.message),n=g(e.name),o=g(e.type),s=p(e.code);if(-1===t){const t=new i;return t.type=i.types.ERROR,t.setData({message:r,name:n,type:o,code:s}),void this.sendPacket(t)}const a=new i;a.type=i.types.ACK,a.id=t,a.setData([{message:r,name:n,type:o,code:s}]),this.sendPacket(a)}bind(t,e){y("string"==typeof t,"event","string"),y("function"==typeof e,"handler","function"),n(!f.hasOwnProperty(t),"Blacklisted event."),this.events.on(t,e)}unbind(t,e){y("string"==typeof t,"event","string"),y("function"==typeof e,"handler","function"),n(!f.hasOwnProperty(t),"Blacklisted event."),this.events.removeListener(t,e)}fire(...t){y(t.length>0,"event","string"),y("string"==typeof t[0],"event","string"),this.sendEvent(t)}hook(t,e){y("string"==typeof t,"event","string"),y("function"==typeof e,"handler","function"),n(!this.hooks.has(t),"Hook already bound."),n(!f.hasOwnProperty(t),"Blacklisted event."),this.hooks.set(t,e)}unhook(t){y("string"==typeof t,"event","string"),n(!f.hasOwnProperty(t),"Blacklisted event."),this.hooks.delete(t)}call(...t){y(t.length>0,"event","string"),y("string"==typeof t[0],"event","string");const e=this.sequence;return this.sequence+=1,this.sequence>>>=0,n(!this.jobs.has(e),"ID collision."),this.sendCall(e,t),new Promise((t,r)=>{this.jobs.set(e,new l(t,r,Date.now()))})}channel(t){return this.channels.has(t)}join(t){return!!this.server&&this.server.join(this,t)}leave(t){return!!this.server&&this.server.leave(this,t)}static accept(t,e,r,n){return(new this).accept(t,e,r,n)}static connect(t,e,r,n){return(new this).connect(t,e,r,n)}}}).call(this,r(1).Buffer)},function(t,e,r){"use strict";e.byteLength=function(t){var e=u(t),r=e[0],n=e[1];return 3*(r+n)/4-n},e.toByteArray=function(t){var e,r,n=u(t),i=n[0],a=n[1],h=new s(function(t,e,r){return 3*(e+r)/4-r}(0,i,a)),c=0,f=a>0?i-4:i;for(r=0;r>16&255,h[c++]=e>>8&255,h[c++]=255&e;2===a&&(e=o[t.charCodeAt(r)]<<2|o[t.charCodeAt(r+1)]>>4,h[c++]=255&e);1===a&&(e=o[t.charCodeAt(r)]<<10|o[t.charCodeAt(r+1)]<<4|o[t.charCodeAt(r+2)]>>2,h[c++]=e>>8&255,h[c++]=255&e);return h},e.fromByteArray=function(t){for(var e,r=t.length,o=r%3,s=[],i=0,a=r-o;ia?a:i+16383));1===o?(e=t[r-1],s.push(n[e>>2]+n[e<<4&63]+"==")):2===o&&(e=(t[r-2]<<8)+t[r-1],s.push(n[e>>10]+n[e>>4&63]+n[e<<2&63]+"="));return s.join("")};for(var n=[],o=[],s="undefined"!=typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,h=i.length;a0)throw new Error("Invalid string. Length must be a multiple of 4");var r=t.indexOf("=");return-1===r&&(r=e),[r,r===e?0:4-r%4]}function c(t,e,r){for(var o,s,i=[],a=e;a>18&63]+n[s>>12&63]+n[s>>6&63]+n[63&s]);return i.join("")}o["-".charCodeAt(0)]=62,o["_".charCodeAt(0)]=63},function(t,e){e.read=function(t,e,r,n,o){var s,i,a=8*o-n-1,h=(1<>1,c=-7,f=r?o-1:0,l=r?-1:1,p=t[e+f];for(f+=l,s=p&(1<<-c)-1,p>>=-c,c+=a;c>0;s=256*s+t[e+f],f+=l,c-=8);for(i=s&(1<<-c)-1,s>>=-c,c+=n;c>0;i=256*i+t[e+f],f+=l,c-=8);if(0===s)s=1-u;else{if(s===h)return i?NaN:1/0*(p?-1:1);i+=Math.pow(2,n),s-=u}return(p?-1:1)*i*Math.pow(2,s-n)},e.write=function(t,e,r,n,o,s){var i,a,h,u=8*s-o-1,c=(1<>1,l=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,p=n?0:s-1,d=n?1:-1,g=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(a=isNaN(e)?1:0,i=c):(i=Math.floor(Math.log(e)/Math.LN2),e*(h=Math.pow(2,-i))<1&&(i--,h*=2),(e+=i+f>=1?l/h:l*Math.pow(2,1-f))*h>=2&&(i++,h/=2),i+f>=c?(a=0,i=c):i+f>=1?(a=(e*h-1)*Math.pow(2,o),i+=f):(a=e*Math.pow(2,f-1)*Math.pow(2,o),i=0));o>=8;t[r+p]=255&a,p+=d,a/=256,o-=8);for(i=i<0;t[r+p]=255&i,p+=d,i/=256,u-=8);t[r+p-d]|=128*g}},function(t,e){var r={}.toString;t.exports=Array.isArray||function(t){return"[object Array]"==r.call(t)}},function(t,e,r){"use strict";(function(e){const n=r(0),o={CONNECT:0,DISCONNECT:1,EVENT:2,ACK:3,ERROR:4,BINARY_EVENT:5,BINARY_ACK:6};class s{constructor(t){this.type=t||0,this.attachments=0,this.nsp="/",this.id=-1,this.data="",this.buffers=[]}setData(t){n(void 0!==t),n("number"!=typeof t),n("function"!=typeof t);const[r,s]=function(t){const r=[],n=function t(r,n,o,s){if(null===n||"object"!=typeof n)return n;if(e.isBuffer(n)){const t=s.get(n);if(null!=t)return t;const e={_placeholder:!0,num:o.length};return s.set(n,e),o.push(n),e}if(s.has(n))throw new TypeError("Converting circular structure to JSON.");if(Array.isArray(n)){const e=[];s.set(n,null);for(let r=0;r0)switch(this.type){case o.EVENT:this.type=o.BINARY_EVENT;break;case o.ACK:this.type=o.BINARY_ACK}return this}getData(){return 0===this.data.length?null:(t=this.data,e=this.buffers,JSON.parse(t,(t,r)=>(function(t){return null!==t&&"object"==typeof t&&!0===t._placeholder&&t.num>>>0===t.num})(r)&&r.num0);let e=0,r=0,s=0,a="/",h=-1,u="";switch([e,r]=function(t,e){const r=t.charCodeAt(e)-48;return r<0||r>9?-1:[e+1,r]}(t,e),n(-1!==r),n(r<=o.BINARY_ACK),r){case o.BINARY_EVENT:case o.BINARY_ACK:[e,s]=i(t,e),n(-1!==s),n(e9)break;o*=10,o+=s,n((r+=1)<=10)}return n(o<=4294967295),0===r&&(o=-1),[e,o]}s.types=o,t.exports=s}).call(this,r(1).Buffer)},function(t,e,r){"use strict";const n=r(0),o=r(15);e.parseURL=function(t){-1===t.indexOf("://")&&(t=`ws://${t}`);const e=o.parse(t);if("http:"!==e.protocol&&"https:"!==e.protocol&&"ws:"!==e.protocol&&"wss:"!==e.protocol)throw new Error("Invalid protocol for websocket URL.");if(!e.hostname)throw new Error("Malformed URL.");const r=e.hostname;let s=80,i=!1;return"https:"!==e.protocol&&"wss:"!==e.protocol||(s=443,i=!0),e.port&&(s=parseInt(e.port,10),n((65535&s)===s),n(0!==s)),[s,r,i]}},function(t,e,r){"use strict";var n=r(16),o=r(18);function s(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}e.parse=v,e.resolve=function(t,e){return v(t,!1,!0).resolve(e)},e.resolveObject=function(t,e){return t?v(t,!1,!0).resolveObject(e):e},e.format=function(t){o.isString(t)&&(t=v(t));return t instanceof s?t.format():s.prototype.format.call(t)},e.Url=s;var i=/^([a-z0-9.+-]+:)/i,a=/:[0-9]*$/,h=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,u=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),c=["'"].concat(u),f=["%","/","?",";","#"].concat(c),l=["/","?","#"],p=/^[+a-z0-9A-Z_-]{0,63}$/,d=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,g={javascript:!0,"javascript:":!0},y={javascript:!0,"javascript:":!0},m={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},w=r(19);function v(t,e,r){if(t&&o.isObject(t)&&t instanceof s)return t;var n=new s;return n.parse(t,e,r),n}s.prototype.parse=function(t,e,r){if(!o.isString(t))throw new TypeError("Parameter 'url' must be a string, not "+typeof t);var s=t.indexOf("?"),a=-1!==s&&s127?N+="x":N+=x[I];if(!N.match(p)){var U=j.slice(0,k),L=j.slice(k+1),M=x.match(d);M&&(U.push(M[1]),L.unshift(M[2])),L.length&&(v="/"+L.join(".")+v),this.hostname=U.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),T||(this.hostname=n.toASCII(this.hostname));var D=this.port?":"+this.port:"",Y=this.hostname||"";this.host=Y+D,this.href+=this.host,T&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==v[0]&&(v="/"+v))}if(!g[A])for(k=0,C=c.length;k0)&&r.host.split("@"))&&(r.auth=T.shift(),r.host=r.hostname=T.shift());return r.search=t.search,r.query=t.query,o.isNull(r.pathname)&&o.isNull(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.href=r.format(),r}if(!O.length)return r.pathname=null,r.search?r.path="/"+r.search:r.path=null,r.href=r.format(),r;for(var R=O.slice(-1)[0],_=(r.host||t.host||O.length>1)&&("."===R||".."===R)||""===R,k=0,P=O.length;P>=0;P--)"."===(R=O[P])?O.splice(P,1):".."===R?(O.splice(P,1),k++):k&&(O.splice(P,1),k--);if(!E&&!A)for(;k--;k)O.unshift("..");!E||""===O[0]||O[0]&&"/"===O[0].charAt(0)||O.unshift(""),_&&"/"!==O.join("/").substr(-1)&&O.push("");var T,j=""===O[0]||O[0]&&"/"===O[0].charAt(0);S&&(r.hostname=r.host=j?"":O.length?O.shift():"",(T=!!(r.host&&r.host.indexOf("@")>0)&&r.host.split("@"))&&(r.auth=T.shift(),r.host=r.hostname=T.shift()));return(E=E||r.host&&O.length)&&!j&&O.unshift(""),O.length?r.pathname=O.join("/"):(r.pathname=null,r.path=null),o.isNull(r.pathname)&&o.isNull(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.auth=t.auth||r.auth,r.slashes=r.slashes||t.slashes,r.href=r.format(),r},s.prototype.parseHost=function(){var t=this.host,e=a.exec(t);e&&(":"!==(e=e[0])&&(this.port=e.substr(1)),t=t.substr(0,t.length-e.length)),t&&(this.hostname=t)}},function(t,e,r){(function(t,n){var o;/*! https://mths.be/punycode v1.4.1 by @mathias */!function(s){e&&e.nodeType,t&&t.nodeType;var i="object"==typeof n&&n;i.global!==i&&i.window!==i&&i.self;var a,h=2147483647,u=36,c=1,f=26,l=38,p=700,d=72,g=128,y="-",m=/^xn--/,w=/[^\x20-\x7E]/,v=/[\x2E\u3002\uFF0E\uFF61]/g,b={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},E=u-c,A=Math.floor,O=String.fromCharCode;function S(t){throw new RangeError(b[t])}function R(t,e){for(var r=t.length,n=[];r--;)n[r]=e(t[r]);return n}function _(t,e){var r=t.split("@"),n="";return r.length>1&&(n=r[0]+"@",t=r[1]),n+R((t=t.replace(v,".")).split("."),e).join(".")}function k(t){for(var e,r,n=[],o=0,s=t.length;o=55296&&e<=56319&&o65535&&(e+=O((t-=65536)>>>10&1023|55296),t=56320|1023&t),e+=O(t)})).join("")}function T(t,e){return t+22+75*(t<26)-((0!=e)<<5)}function j(t,e,r){var n=0;for(t=r?A(t/p):t>>1,t+=A(t/e);t>E*f>>1;n+=u)t=A(t/E);return A(n+(E+1)*t/(t+l))}function C(t){var e,r,n,o,s,i,a,l,p,m,w,v=[],b=t.length,E=0,O=g,R=d;for((r=t.lastIndexOf(y))<0&&(r=0),n=0;n=128&&S("not-basic"),v.push(t.charCodeAt(n));for(o=r>0?r+1:0;o=b&&S("invalid-input"),((l=(w=t.charCodeAt(o++))-48<10?w-22:w-65<26?w-65:w-97<26?w-97:u)>=u||l>A((h-E)/i))&&S("overflow"),E+=l*i,!(l<(p=a<=R?c:a>=R+f?f:a-R));a+=u)i>A(h/(m=u-p))&&S("overflow"),i*=m;R=j(E-s,e=v.length+1,0==s),A(E/e)>h-O&&S("overflow"),O+=A(E/e),E%=e,v.splice(E++,0,O)}return P(v)}function x(t){var e,r,n,o,s,i,a,l,p,m,w,v,b,E,R,_=[];for(v=(t=k(t)).length,e=g,r=0,s=d,i=0;i=e&&wA((h-r)/(b=n+1))&&S("overflow"),r+=(a-e)*b,e=a,i=0;ih&&S("overflow"),w==e){for(l=r,p=u;!(l<(m=p<=s?c:p>=s+f?f:p-s));p+=u)R=l-m,E=u-m,_.push(O(T(m+R%E,0))),l=A(R/E);_.push(O(T(l,0))),s=j(r,b,n==o),r=0,++n}++r,++e}return _.join("")}a={version:"1.4.1",ucs2:{decode:k,encode:P},decode:C,encode:x,toASCII:function(t){return _(t,(function(t){return w.test(t)?"xn--"+x(t):t}))},toUnicode:function(t){return _(t,(function(t){return m.test(t)?C(t.slice(4).toLowerCase()):t}))}},void 0===(o=function(){return a}.call(e,r,e,t))||(t.exports=o)}()}).call(this,r(17)(t),r(2))},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,r){"use strict";t.exports={isString:function(t){return"string"==typeof t},isObject:function(t){return"object"==typeof t&&null!==t},isNull:function(t){return null===t},isNullOrUndefined:function(t){return null==t}}},function(t,e,r){"use strict";e.decode=e.parse=r(20),e.encode=e.stringify=r(21)},function(t,e,r){"use strict";function n(t,e){return Object.prototype.hasOwnProperty.call(t,e)}t.exports=function(t,e,r,s){e=e||"&",r=r||"=";var i={};if("string"!=typeof t||0===t.length)return i;var a=/\+/g;t=t.split(e);var h=1e3;s&&"number"==typeof s.maxKeys&&(h=s.maxKeys);var u=t.length;h>0&&u>h&&(u=h);for(var c=0;c=0?(f=g.substr(0,y),l=g.substr(y+1)):(f=g,l=""),p=decodeURIComponent(f),d=decodeURIComponent(l),n(i,p)?o(i[p])?i[p].push(d):i[p]=[i[p],d]:i[p]=d}return i};var o=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)}},function(t,e,r){"use strict";var n=function(t){switch(typeof t){case"string":return t;case"boolean":return t?"true":"false";case"number":return isFinite(t)?t:"";default:return""}};t.exports=function(t,e,r,a){return e=e||"&",r=r||"=",null===t&&(t=void 0),"object"==typeof t?s(i(t),(function(i){var a=encodeURIComponent(n(i))+r;return o(t[i])?s(t[i],(function(t){return a+encodeURIComponent(n(t))})).join(e):a+encodeURIComponent(n(t[i]))})).join(e):a?encodeURIComponent(n(a))+r+encodeURIComponent(n(t)):""};var o=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)};function s(t,e){if(t.map)return t.map(e);for(var r=[],n=0;ni)return void this.error("Frame too large.");let r;try{r=s.fromRaw(t)}catch(t){return void this.emit("error",t)}this.emit("frame",r)}feedString(t){if(n("string"==typeof t),e.byteLength(t,"utf8")>i)return void this.error("Frame too large.");let r;try{r=s.fromString(t)}catch(t){return void this.emit("error",t)}this.emit("frame",r)}}}).call(this,r(1).Buffer)},function(t,e,r){"use strict";t.exports={1e3:"NORMAL_CLOSURE",1001:"GOING_AWAY",1002:"PROTOCOL_ERROR",1003:"UNSUPPORTED_DATA",1004:"RESERVED",1005:"NO_STATUS_RECVD",1006:"ABNORMAL_CLOSURE",1007:"INVALID_FRAME_PAYLOAD_DATA",1008:"POLICY_VIOLATION",1009:"MESSAGE_TOO_BIG",1010:"MISSING_EXTENSION",1011:"INTERNAL_ERROR",1012:"SERVICE_RESTART",1013:"TRY_AGAIN_LATER",1014:"BAD_GATEWAY",1015:"TLS_HANDSHAKE"}},function(t,e,r){"use strict";t.exports={connect:!0,connect_error:!0,connect_timeout:!0,connecting:!0,disconnect:!0,error:!0,reconnect:!0,reconnect_attempt:!0,reconnect_failed:!0,reconnect_error:!0,reconnecting:!0,ping:!0,pong:!0}}])); \ No newline at end of file diff --git a/lib/src/frontend/RPCSocket.d.ts b/lib/src/Client.d.ts similarity index 59% rename from lib/src/frontend/RPCSocket.d.ts rename to lib/src/Client.d.ts index ce9de7e..01a3bb8 100644 --- a/lib/src/frontend/RPCSocket.d.ts +++ b/lib/src/Client.d.ts @@ -1,12 +1,5 @@ -import { Socket } from "../backend/RPCSocketServer"; -/** - * Dynamic library to communicate with FrontblockService remotely - * - * This will be automatically injected into the webpages served by FrontblockService - * Will ask it's service for available RPCs and parse them into methods of this object - * for convenient access. - */ -export declare class RPCSocket implements Socket { +import { Socket } from './interfaces/Socket'; +export declare class Client implements Socket { port: number; private server; private tls; diff --git a/lib/src/frontend/RPCSocket.js b/lib/src/Client.js similarity index 88% rename from lib/src/frontend/RPCSocket.js rename to lib/src/Client.js index 5675957..c890c61 100644 --- a/lib/src/frontend/RPCSocket.js +++ b/lib/src/Client.js @@ -5,14 +5,7 @@ var bsock = require('bsock'); function stripAfterEquals(str) { return str.split("=")[0]; } -/** - * Dynamic library to communicate with FrontblockService remotely - * - * This will be automatically injected into the webpages served by FrontblockService - * Will ask it's service for available RPCs and parse them into methods of this object - * for convenient access. - */ -class RPCSocket { +class Client { constructor(port, server, tls = false) { this.port = port; this.server = server; @@ -45,13 +38,13 @@ class RPCSocket { info.forEach(i => { let f; switch (i.type) { - case 'call': + case 'Call': f = this.callGenerator(i.uniqueName, i.argNames); break; - case 'hook': + case 'Hook': f = this.hookGenerator(i.uniqueName, i.argNames); break; - case 'unhook': + case 'Unhook': f = this.unhookGenerator(i.uniqueName, i.argNames); break; } @@ -92,4 +85,4 @@ class RPCSocket { } )()`); } } -exports.RPCSocket = RPCSocket; +exports.Client = Client; diff --git a/lib/src/Response.d.ts b/lib/src/Response.d.ts new file mode 100644 index 0000000..ee1072b --- /dev/null +++ b/lib/src/Response.d.ts @@ -0,0 +1,17 @@ +export declare type Outcome = "Success" | "Error"; +export declare class Response { + message?: string | undefined; + constructor(message?: string | undefined); +} +export declare class SuccessResponse extends Response { + result: Outcome; + constructor(message?: string); +} +export declare class ErrorResponse extends Response { + result: Outcome; + constructor(message?: string); +} +export declare class SubscriptionResponse extends SuccessResponse { + uid: string; + constructor(uid: string, message?: string); +} diff --git a/lib/src/Response.js b/lib/src/Response.js new file mode 100644 index 0000000..9acd085 --- /dev/null +++ b/lib/src/Response.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/* Responses */ +class Response { + constructor(message) { + this.message = message; + } +} +exports.Response = Response; +class SuccessResponse extends Response { + constructor(message) { + super(message); + this.result = "Success"; + } +} +exports.SuccessResponse = SuccessResponse; +class ErrorResponse extends Response { + constructor(message = "Unknown error") { + super(message); + this.result = "Error"; + } +} +exports.ErrorResponse = ErrorResponse; +class SubscriptionResponse extends SuccessResponse { + constructor(uid, message) { + super(message); + this.uid = uid; + } +} +exports.SubscriptionResponse = SubscriptionResponse; diff --git a/lib/src/Server.d.ts b/lib/src/Server.d.ts new file mode 100644 index 0000000..89e01c0 --- /dev/null +++ b/lib/src/Server.d.ts @@ -0,0 +1,14 @@ +import { SocketConf } from './Types'; +import { Exporter } from './interfaces/Exporter'; +import { Socket } from "./interfaces/Socket"; +export declare class Server { + private port; + private rpcExporters; + private conf; + private io; + private wsServer; + constructor(port: number, rpcExporters?: Exporter[], conf?: SocketConf); + private startWebsocket; + protected initRPCs(socket: Socket): void; + protected initPublicRPCs(socket: Socket): void; +} diff --git a/lib/src/Server.js b/lib/src/Server.js new file mode 100644 index 0000000..32c8911 --- /dev/null +++ b/lib/src/Server.js @@ -0,0 +1,58 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const http = require("http"); +const bsock = require("bsock"); +const Util_1 = require("./Util"); +class Server { + constructor(port, rpcExporters = [], conf = { + errorHandler: (socket) => (error) => { socket.destroy(); console.error(error); }, + closeHandler: (socket) => () => { console.log("Socket closing"); }, + connectionHandler: (socket) => { console.log("New websocket connection in port " + socket.port); }, + visibility: "127.0.0.1" + }) { + this.port = port; + this.rpcExporters = rpcExporters; + this.conf = conf; + this.io = bsock.createServer(); + this.wsServer = http.createServer(); + this.startWebsocket(); + } + startWebsocket() { + try { + this.io.attach(this.wsServer); + this.io.on('socket', (socket) => { + socket.on('error', this.conf.errorHandler(socket)); + socket.on('close', this.conf.closeHandler(socket)); + if (this.conf.visibility === "127.0.0.1") + this.initRPCs(socket); + else + this.initPublicRPCs(socket); + }); + this.wsServer.listen(this.port, this.conf.visibility); + } + catch (e) { + //@ts-ignore + this.errorHandler(undefined)("Unable to connect to socket"); + } + } + initRPCs(socket) { + const infoRPC = [ + { + name: 'info', + type: 'Call', + func: async () => rpcInfos + } + ]; + const rpcInfos = [ + ...Util_1.rpcHooker(socket, "RPC", infoRPC, false), + ...this.rpcExporters.flatMap(exporter => Util_1.rpcHooker(socket, exporter.name, [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()])) + ]; + } + initPublicRPCs(socket) { + const rpcInfos = [ + ...Util_1.rpcHooker(socket, "Admin", adminRPCs, false), + ...this.rpcExporters.flatMap(exporter => Util_1.rpcHooker(socket, exporter.name, exporter.exportPublicRPCs())) + ]; + } +} +exports.Server = Server; diff --git a/lib/src/Types.d.ts b/lib/src/Types.d.ts new file mode 100644 index 0000000..2d87875 --- /dev/null +++ b/lib/src/Types.d.ts @@ -0,0 +1,54 @@ +import { SuccessResponse, ErrorResponse, SubscriptionResponse } from "./Response"; +import { Socket } from "./interfaces/Socket"; +export declare type Visibility = "127.0.0.1" | "0.0.0.0"; +export declare type Name = string; +export declare type SocketConf = { + connectionHandler: (socket: Socket) => void; + errorHandler: (socket: Socket) => (error: any) => void; + closeHandler: (socket: Socket) => () => void; + visibility: Visibility; +}; +export declare type rpcType = 'Hook' | 'Unhook' | 'Call'; +export declare type BaseRPC = { + type: rpcType; + name: string; +}; +export declare type HookRPC = BaseRPC & { + type: 'Hook'; + func: CallbackFunction; + unhook: UnhookFunction; +}; +export declare type UnhookRPC = BaseRPC & { + type: 'Unhook'; + func: UnhookFunction; +}; +export declare type CallRPC = BaseRPC & { + type: 'Call'; + func: (...args: any[]) => Promise; +}; +export declare type SocketioRPC = CallRPC | UnhookRPC | HookRPC; +export declare type BaseInfo = { + owner: string; + argNames: string[]; +}; +export declare type HookInfo = BaseRPC & BaseInfo & { + type: 'Hook'; + generator: (socket: any) => CallbackFunction; + unhook: UnhookFunction; +}; +export declare type UnhookInfo = BaseRPC & BaseInfo & { + type: 'Unhook'; + func: UnhookFunction; +}; +export declare type CallInfo = BaseRPC & BaseInfo & { + type: 'Call'; + func: AsyncFunction; +}; +export declare type RpcInfo = HookInfo | UnhookInfo | CallInfo; +export declare type ExtendedRpcInfo = RpcInfo & { + uniqueName: string; +}; +export declare type OnFunction = (type: 'error' | 'close', f: (e?: any) => void) => Socket; +export declare type UnhookFunction = (uid: string) => Promise; +export declare type CallbackFunction = (...args: any[]) => Promise; +export declare type AsyncFunction = (...args: any[]) => Promise; diff --git a/lib/src/Types.js b/lib/src/Types.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/src/Types.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/src/Util.d.ts b/lib/src/Util.d.ts new file mode 100644 index 0000000..0763df1 --- /dev/null +++ b/lib/src/Util.d.ts @@ -0,0 +1,4 @@ +import { SocketioRPC, RpcInfo, ExtendedRpcInfo } from "./Types"; +import { Socket } from "./interfaces/Socket"; +export declare const rpcToRpcinfo: (rpc: SocketioRPC, owner: string) => RpcInfo; +export declare const rpcHooker: (socket: Socket, owner: string, RPCs: SocketioRPC[], makeUnique?: boolean) => ExtendedRpcInfo[]; diff --git a/lib/src/Util.js b/lib/src/Util.js new file mode 100644 index 0000000..74dd8fb --- /dev/null +++ b/lib/src/Util.js @@ -0,0 +1,75 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const uuid = require("uuid/v4"); +exports.rpcToRpcinfo = (rpc, owner) => { + switch (rpc.type) { + case "Call": + return { + owner: owner, + argNames: extractArgs(rpc.func), + type: rpc.type, + name: rpc.name, + func: rpc.func, + }; + case "Unhook": + return { + owner: owner, + argNames: extractArgs(rpc.func), + type: rpc.type, + name: rpc.name, + func: rpc.func, + }; + case "Hook": + const generator = hookGenerator(rpc); + return { + owner: owner, + argNames: extractArgs(generator(undefined)), + type: rpc.type, + name: rpc.name, + unhook: rpc.unhook, + generator: generator, + }; + } +}; +exports.rpcHooker = (socket, owner, RPCs, makeUnique = true) => { + const suffix = makeUnique ? "-" + uuid().substr(0, 4) : ""; + return RPCs.map(rpc => exports.rpcToRpcinfo(rpc, owner)) + .map(info => { + const ret = info; + ret.uniqueName = info.name + suffix; + switch (info.type) { + case "Hook": + socket.hook(ret.uniqueName, info.generator(socket)); + break; + default: + socket.hook(ret.uniqueName, info.func); + } + socket.on('close', () => socket.unhook(info.name)); + return ret; + }); +}; +const hookGenerator = (rpc) => { + const argsArr = extractArgs(rpc.func); + argsArr.pop(); + const args = argsArr.join(','); + return eval(`(socket) => async (` + args + `) => { + const res = await rpc.func(` + args + (args.length !== 0 ? ',' : '') + ` (x) => { + socket.call(res.uid, x) + }) + if(res.result == 'Success'){ + socket.on('close', async () => { + const unhookRes = await rpc.unhook(res.uid) + console.log("Specific close handler for", rpc.name, res.uid, unhookRes) + }) + + } + return res + }`); +}; +const extractArgs = (f) => { + let fn = String(f); + let args = fn.substr(0, fn.indexOf(")")); + args = args.substr(fn.indexOf("(") + 1); + let ret = args.split(","); + return ret; +}; diff --git a/lib/src/backend/RPCSocketServer.d.ts b/lib/src/backend/RPCSocketServer.d.ts deleted file mode 100644 index 04b4643..0000000 --- a/lib/src/backend/RPCSocketServer.d.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Socket } from "./RPCSocketServer"; -declare type rpcType = 'hook' | 'unhook' | 'call'; -export declare type Outcome = "Success" | "Error"; -export declare type Visibility = "127.0.0.1" | "0.0.0.0"; -export declare class Response { - message?: string | undefined; - constructor(message?: string | undefined); -} -export declare class SuccessResponse extends Response { - result: Outcome; - constructor(message?: string); -} -export declare class ErrorResponse extends Response { - result: Outcome; - constructor(message?: string); -} -export declare class SubscriptionResponse extends SuccessResponse { - uid: string; - constructor(uid: string, message?: string); -} -export declare type UnhookFunction = (uid: string) => Promise; -export declare type callbackFunction = (...args: any[]) => Promise; -export declare type AsyncFunction = (...args: any[]) => Promise; -export interface RPCExporter { - name: string; - exportRPCs(): socketioRPC[]; - exportPublicRPCs(): socketioRPC[]; -} -declare type baseRPC = { - type: rpcType; - name: string; -}; -declare type hookRPC = baseRPC & { - type: 'hook'; - func: callbackFunction; - unhook: UnhookFunction; -}; -declare type unhookRPC = baseRPC & { - type: 'unhook'; - func: UnhookFunction; -}; -declare type callRPC = baseRPC & { - type: 'call'; - func: (...args: any[]) => Promise; -}; -export declare type socketioRPC = callRPC | unhookRPC | hookRPC; -export declare type baseInfo = { - owner: string; - argNames: string[]; -}; -declare type HookInfo = baseRPC & baseInfo & { - type: 'hook'; - generator: (socket: any) => callbackFunction; - unhook: UnhookFunction; -}; -declare type UnhookInfo = baseRPC & baseInfo & { - type: 'unhook'; - func: UnhookFunction; -}; -declare type CallInfo = baseRPC & baseInfo & { - type: 'call'; - func: AsyncFunction; -}; -declare type RpcInfo = HookInfo | UnhookInfo | CallInfo; -export declare type ExtendedRpcInfo = RpcInfo & { - uniqueName: string; -}; -export declare const rpcToRpcinfo: (rpc: socketioRPC, owner: string) => RpcInfo; -export declare const rpcHooker: (socket: Socket, owner: string, RPCs: socketioRPC[], makeUnique?: boolean) => ExtendedRpcInfo[]; -declare type OnFunction = (type: 'error' | 'close', f: (e?: any) => void) => Socket; -export interface Socket { - port: number; - hook: (rpcname: string, ...args: any[]) => Socket; - unhook: (rpcname: string) => Socket; - call: (rpcname: string, ...args: any[]) => Promise; - fire: (rpcname: string, ...args: any[]) => Promise; - on: OnFunction; - destroy: () => void; - close: () => void; -} -export declare type RPCSocketConf = { - connectionHandler: (socket: Socket) => void; - errorHandler: (socket: Socket) => (error: any) => void; - closeHandler: (socket: Socket) => () => void; -}; -export declare class RPCSocketServer { - private port; - private rpcExporters; - private visibility; - private conf; - private io; - private wsServer; - constructor(port: number, rpcExporters?: RPCExporter[], visibility?: Visibility, conf?: RPCSocketConf); - private startWebsocket; - protected initApis(socket: Socket): void; - protected initPublicApis(socket: Socket): void; -} -export {}; diff --git a/lib/src/backend/RPCSocketServer.js b/lib/src/backend/RPCSocketServer.js deleted file mode 100644 index 45baa23..0000000 --- a/lib/src/backend/RPCSocketServer.js +++ /dev/null @@ -1,165 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const http = require("http"); -const bsock = require("bsock"); -const uuid = require("uuid/v4"); -/* Responses */ -class Response { - constructor(message) { - this.message = message; - } -} -exports.Response = Response; -class SuccessResponse extends Response { - constructor(message) { - super(message); - this.result = "Success"; - } -} -exports.SuccessResponse = SuccessResponse; -class ErrorResponse extends Response { - constructor(message = "Unknown error") { - super(message); - this.result = "Error"; - } -} -exports.ErrorResponse = ErrorResponse; -class SubscriptionResponse extends SuccessResponse { - constructor(uid, message) { - super(message); - this.uid = uid; - } -} -exports.SubscriptionResponse = SubscriptionResponse; -exports.rpcToRpcinfo = (rpc, owner) => { - switch (rpc.type) { - case "call": - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - }; - case "unhook": - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - }; - case "hook": - const generator = hookGenerator(rpc); - return { - owner: owner, - argNames: extractArgs(generator(undefined)), - type: rpc.type, - name: rpc.name, - unhook: rpc.unhook, - generator: generator, - }; - } -}; -exports.rpcHooker = (socket, owner, RPCs, makeUnique = true) => { - const suffix = makeUnique ? "-" + uuid().substr(0, 4) : ""; - return RPCs.map(rpc => exports.rpcToRpcinfo(rpc, owner)) - .map(info => { - const ret = info; - ret.uniqueName = info.name + suffix; - switch (info.type) { - case "hook": - socket.hook(ret.uniqueName, info.generator(socket)); - break; - default: - socket.hook(ret.uniqueName, info.func); - } - socket.on('close', () => socket.unhook(info.name)); - return ret; - }); -}; -const hookGenerator = (rpc) => { - const argsArr = extractArgs(rpc.func); - argsArr.pop(); - const args = argsArr.join(','); - return eval(`(socket) => async (` + args + `) => { - const res = await rpc.func(` + args + (args.length !== 0 ? ',' : '') + ` (x) => { - socket.call(res.uid, x) - }) - if(res.result == 'Success'){ - socket.on('close', async () => { - const unhookRes = await rpc.unhook(res.uid) - console.log("Specific close handler for", rpc.name, res.uid, unhookRes) - }) - - } - return res - }`); -}; -const extractArgs = (f) => { - let fn = String(f); - let args = fn.substr(0, fn.indexOf(")")); - args = args.substr(fn.indexOf("(") + 1); - let ret = args.split(","); - return ret; -}; -class RPCSocketServer { - constructor(port, rpcExporters = [], visibility = "127.0.0.1", conf = { - errorHandler: (socket) => (error) => { socket.destroy(); console.error(error); }, - closeHandler: (socket) => () => { console.log("Socket closing"); }, - connectionHandler: (socket) => { console.log("New websocket connection in port " + socket.port); } - }) { - this.port = port; - this.rpcExporters = rpcExporters; - this.visibility = visibility; - this.conf = conf; - this.io = bsock.createServer(); - this.wsServer = http.createServer(); - this.startWebsocket(); - } - startWebsocket() { - try { - this.io.attach(this.wsServer); - this.io.on('socket', (socket) => { - socket.on('error', this.conf.errorHandler(socket)); - socket.on('close', this.conf.closeHandler(socket)); - if (this.visibility === "127.0.0.1") - this.initApis(socket); - else - this.initPublicApis(socket); - }); - this.wsServer.listen(this.port, this.visibility); - } - catch (e) { - //@ts-ignore - this.errorHandler(undefined)("Unable to connect to socket"); - } - } - initApis(socket) { - const adminRPCs = [ - { - name: 'info', - type: 'call', - func: async () => rpcInfos - } - ]; - const rpcInfos = [ - ...exports.rpcHooker(socket, "Admin", adminRPCs, false), - ...this.rpcExporters.flatMap(exporter => exports.rpcHooker(socket, exporter.name, [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()])) - ]; - } - initPublicApis(socket) { - const adminRPCs = [ - { - name: 'info', - type: 'call', - func: async () => rpcInfos - } - ]; - const rpcInfos = [ - ...exports.rpcHooker(socket, "Admin", adminRPCs, false), - ...this.rpcExporters.flatMap(exporter => exports.rpcHooker(socket, exporter.name, exporter.exportPublicRPCs())) - ]; - } -} -exports.RPCSocketServer = RPCSocketServer; diff --git a/lib/src/interfaces/Exporter.d.ts b/lib/src/interfaces/Exporter.d.ts new file mode 100644 index 0000000..b61cd0b --- /dev/null +++ b/lib/src/interfaces/Exporter.d.ts @@ -0,0 +1,6 @@ +import { SocketioRPC, Name } from "../Types"; +export interface Exporter { + name: Name; + exportRPCs(): SocketioRPC[]; + exportPublicRPCs(): SocketioRPC[]; +} diff --git a/lib/src/interfaces/Exporter.js b/lib/src/interfaces/Exporter.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/src/interfaces/Exporter.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/src/interfaces/Socket.d.ts b/lib/src/interfaces/Socket.d.ts new file mode 100644 index 0000000..276a1c2 --- /dev/null +++ b/lib/src/interfaces/Socket.d.ts @@ -0,0 +1,11 @@ +import { OnFunction } from "../Types"; +export interface Socket { + port: number; + hook: (rpcname: string, ...args: any[]) => Socket; + unhook: (rpcname: string) => Socket; + call: (rpcname: string, ...args: any[]) => Promise; + fire: (rpcname: string, ...args: any[]) => Promise; + on: OnFunction; + destroy: () => void; + close: () => void; +} diff --git a/lib/src/interfaces/Socket.js b/lib/src/interfaces/Socket.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/src/interfaces/Socket.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/test/test.js b/lib/test/test.js index 0cc623d..9969bd8 100644 --- a/lib/test/test.js +++ b/lib/test/test.js @@ -1,9 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const RPCSocketServer_1 = require("../src/backend/RPCSocketServer"); +const Server_1 = require("../src/Server"); //@ts-ignore -const RPCSocket_1 = require("../src/frontend/RPCSocket"); -new RPCSocketServer_1.RPCSocketServer(20000, [{ +const Client_1 = require("../src/Client"); +new Server_1.Server(20000, [{ name: "HelloWorldRPCGroup", exportPublicRPCs: () => [], exportRPCs: () => [{ @@ -11,8 +11,8 @@ new RPCSocketServer_1.RPCSocketServer(20000, [{ name: 'echo', func: async (s) => s, }], - }], "0.0.0.0"); -const caller = new RPCSocket_1.RPCSocket(20000, 'localhost'); + }]); +const caller = new Client_1.Client(20000, 'localhost'); caller.connect().then(_ => { caller.info().then(console.log); caller["HelloWorldRPCGroup"].echo("x").then(console.log); diff --git a/package.json b/package.json index 56c06db..efbdfe7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "build": "npm run clean; npm run tsc; npm run webpack", "tsc": "tsc", - "webpack": "webpack --config src/frontend/webpack.prod.js --progress --colors", + "webpack": "webpack --config src/webpack.prod.js --progress --colors", "clean": "rm -rf lib" }, "author": "", diff --git a/src/frontend/RPCSocket.ts b/src/Client.ts similarity index 84% rename from src/frontend/RPCSocket.ts rename to src/Client.ts index 0585404..b2fc995 100644 --- a/src/frontend/RPCSocket.ts +++ b/src/Client.ts @@ -1,20 +1,14 @@ -import { ExtendedRpcInfo, UnhookFunction, callbackFunction, AsyncFunction, Socket } from "../backend/RPCSocketServer"; var bsock = require('bsock') +import { ExtendedRpcInfo, UnhookFunction, CallbackFunction, AsyncFunction } from "./Types"; +import { Socket } from './interfaces/Socket' + //fix args with defaults like "force = true" -> "force" function stripAfterEquals(str:string){ return str.split("=")[0] } - -/** - * Dynamic library to communicate with FrontblockService remotely - * - * This will be automatically injected into the webpages served by FrontblockService - * Will ask it's service for available RPCs and parse them into methods of this object - * for convenient access. - */ -export class RPCSocket implements Socket{ +export class Client implements Socket{ private socket: Socket constructor(public port:number, private server: string, private tls: boolean = false){ } @@ -54,13 +48,13 @@ export class RPCSocket implements Socket{ info.forEach(i => { let f: any switch (i.type) { - case 'call': + case 'Call': f = this.callGenerator(i.uniqueName, i.argNames) break - case 'hook': + case 'Hook': f = this.hookGenerator(i.uniqueName, i.argNames) break - case 'unhook': + case 'Unhook': f = this.unhookGenerator(i.uniqueName, i.argNames) break } @@ -81,7 +75,7 @@ export class RPCSocket implements Socket{ return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", '+argParams+')} )()' ) } - private hookGenerator(fnName, fnArgs:string[]): callbackFunction{ + private hookGenerator(fnName, fnArgs:string[]): CallbackFunction{ const headerArgs = fnArgs.join(",") const argParams = fnArgs.map(stripAfterEquals).join(",") return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { diff --git a/src/Response.ts b/src/Response.ts new file mode 100644 index 0000000..7f470bf --- /dev/null +++ b/src/Response.ts @@ -0,0 +1,38 @@ + +export type Outcome = "Success" | "Error" + +/* Responses */ +export class Response{ + constructor( + public message?:string + ){} +} + +export class SuccessResponse extends Response{ + result:Outcome = "Success" + + constructor( + message?:string + ){ + super(message) + } +} + +export class ErrorResponse extends Response{ + result:Outcome = "Error" + + constructor( + message: string = "Unknown error" + ){ + super(message) + } +} + +export class SubscriptionResponse extends SuccessResponse{ + constructor( + public uid: string, + message?:string + ){ + super(message) + } +} \ No newline at end of file diff --git a/src/Server.ts b/src/Server.ts new file mode 100644 index 0000000..eedba7f --- /dev/null +++ b/src/Server.ts @@ -0,0 +1,60 @@ +import http = require('http'); +import bsock = require('bsock'); + +import { ExtendedRpcInfo, SocketConf, SocketioRPC } from './Types'; +import { rpcHooker } from './Util'; +import { Exporter } from './interfaces/Exporter'; +import { Socket } from "./interfaces/Socket"; + +export class Server{ + + private io = bsock.createServer() + private wsServer = http.createServer() + + constructor( + private port:number, + private rpcExporters: Exporter[] = [], + private conf: SocketConf = { + errorHandler: (socket:Socket) => (error:any) => { socket.destroy(); console.error(error) }, + closeHandler: (socket:Socket) => () => { console.log("Socket closing") }, + connectionHandler: (socket:Socket) => { console.log("New websocket connection in port "+socket.port)}, + visibility: "127.0.0.1" + } + ){ + this.startWebsocket() + } + + private startWebsocket(){ + try{ + this.io.attach(this.wsServer) + this.io.on('socket', (socket:Socket) => { + socket.on('error', this.conf.errorHandler(socket)) + socket.on('close', this.conf.closeHandler(socket)) + if(this.conf.visibility === "127.0.0.1") + this.initRPCs(socket) + else + this.initPublicRPCs(socket) + }) + this.wsServer.listen(this.port, this.conf.visibility) + }catch(e){ + //@ts-ignore + this.errorHandler(undefined)("Unable to connect to socket") + } + } + + protected initRPCs(socket:Socket){ + const infoRPC:SocketioRPC[] = [ + { + name: 'info', + type: 'Call', + func: async () => rpcInfos + } + ] + const rpcInfos:ExtendedRpcInfo[] = [ + ...rpcHooker(socket, "RPC", infoRPC, false), + ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter.name, [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()])) + ] + } + + protected initPublicRPCs(socket:Socket){} +} \ No newline at end of file diff --git a/src/Types.ts b/src/Types.ts new file mode 100644 index 0000000..cddbf3f --- /dev/null +++ b/src/Types.ts @@ -0,0 +1,66 @@ +import { SuccessResponse, ErrorResponse, SubscriptionResponse } from "./Response"; +import { Socket } from "./interfaces/Socket"; + +export type Visibility = "127.0.0.1" | "0.0.0.0" +export type Name = string + +export type SocketConf = { + connectionHandler: (socket:Socket) => void + errorHandler: (socket:Socket) => (error:any) => void + closeHandler: (socket:Socket) => () => void + visibility: Visibility +} + +export type rpcType = 'Hook' | 'Unhook' | 'Call' + +export type BaseRPC = { + type: rpcType + name: string +} + +export type HookRPC = BaseRPC & { + type: 'Hook' + func: CallbackFunction + unhook: UnhookFunction +} + +export type UnhookRPC = BaseRPC & { + type: 'Unhook' + func: UnhookFunction +} + +export type CallRPC = BaseRPC & { + type: 'Call' + func: (...args) => Promise +} + +export type SocketioRPC = CallRPC | UnhookRPC | HookRPC + +export type BaseInfo = { + owner: string, + argNames: string[], +} + +export type HookInfo = BaseRPC & BaseInfo & { + type: 'Hook', + generator: (socket) => CallbackFunction + unhook: UnhookFunction +} + +export type UnhookInfo = BaseRPC & BaseInfo & { + type: 'Unhook', + func: UnhookFunction +} + +export type CallInfo = BaseRPC & BaseInfo & { + type: 'Call', + func: AsyncFunction +} + +export type RpcInfo = HookInfo | UnhookInfo | CallInfo +export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } + +export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => Socket +export type UnhookFunction = (uid:string) => Promise +export type CallbackFunction = (...args) => Promise +export type AsyncFunction = (...args) => Promise diff --git a/src/Util.ts b/src/Util.ts new file mode 100644 index 0000000..18d2bdd --- /dev/null +++ b/src/Util.ts @@ -0,0 +1,82 @@ +import * as uuid from "uuid/v4" + +import { HookRPC, HookInfo, SocketioRPC, RpcInfo, ExtendedRpcInfo } from "./Types"; +import { Socket } from "./interfaces/Socket"; + +export const rpcToRpcinfo = (rpc : SocketioRPC, owner: string):RpcInfo => { + switch(rpc.type){ + case "Call" : + return { + owner: owner, + argNames: extractArgs(rpc.func), + type: rpc.type, + name: rpc.name, + func: rpc.func, + } + case "Unhook" : + return { + owner: owner, + argNames: extractArgs(rpc.func), + type: rpc.type, + name: rpc.name, + func: rpc.func, + } + case "Hook" : + const generator = hookGenerator(rpc) + return { + owner: owner, + argNames: extractArgs(generator(undefined)), + type: rpc.type, + name: rpc.name, + unhook: rpc.unhook, + generator: generator, + } + } +} + +export const rpcHooker = (socket: Socket, owner:string, RPCs: SocketioRPC[], makeUnique = true):ExtendedRpcInfo[] => { + const suffix = makeUnique?"-"+uuid().substr(0,4):"" + return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) + .map(info => { + const ret:any = info + ret.uniqueName = info.name+suffix + + switch(info.type){ + case "Hook": + socket.hook(ret.uniqueName, info.generator(socket)) + break; + default: + socket.hook(ret.uniqueName, info.func) + } + socket.on('close', () => socket.unhook(info.name)) + return ret + }) +} + +const hookGenerator = (rpc:HookRPC): HookInfo['generator'] => { + const argsArr = extractArgs(rpc.func) + argsArr.pop() + const args = argsArr.join(',') + + return eval(`(socket) => async (`+args+`) => { + const res = await rpc.func(`+args+(args.length!==0?',':'')+` (x) => { + socket.call(res.uid, x) + }) + if(res.result == 'Success'){ + socket.on('close', async () => { + const unhookRes = await rpc.unhook(res.uid) + console.log("Specific close handler for", rpc.name, res.uid, unhookRes) + }) + + } + return res + }`) +} + +const extractArgs = (f:Function):string[] => { + let fn = String(f) + let args = fn.substr(0, fn.indexOf(")")) + args = args.substr(fn.indexOf("(")+1) + let ret = args.split(",") + return ret +} \ No newline at end of file diff --git a/src/backend/RPCSocketServer.ts b/src/backend/RPCSocketServer.ts deleted file mode 100644 index 92baadb..0000000 --- a/src/backend/RPCSocketServer.ts +++ /dev/null @@ -1,267 +0,0 @@ -import http = require('http'); -import bsock = require('bsock'); - -import * as uuid from "uuid/v4" -import { Socket } from "./RPCSocketServer" - -type rpcType = 'hook' | 'unhook' | 'call' -export type Outcome = "Success" | "Error" -export type Visibility = "127.0.0.1" | "0.0.0.0" - -/* Responses */ -export class Response{ - constructor( - public message?:string - ){} -} - -export class SuccessResponse extends Response{ - result:Outcome = "Success" - - constructor( - message?:string - ){ - super(message) - } -} - -export class ErrorResponse extends Response{ - result:Outcome = "Error" - - constructor( - message: string = "Unknown error" - ){ - super(message) - } -} - -export class SubscriptionResponse extends SuccessResponse{ - constructor( - public uid: string, - message?:string - ){ - super(message) - } -} -export type UnhookFunction = (uid:string) => Promise -export type callbackFunction = (...args) => Promise -export type AsyncFunction = (...args) => Promise - -export interface RPCExporter{ - name: string - exportRPCs() : socketioRPC[] - exportPublicRPCs() : socketioRPC[] -} - -type baseRPC = { - type: rpcType - name: string -} - -type hookRPC = baseRPC & { - type: 'hook' - func: callbackFunction - unhook: UnhookFunction -} - -type unhookRPC = baseRPC & { - type: 'unhook' - func: UnhookFunction -} - -type callRPC = baseRPC & { - type: 'call' - func: (...args) => Promise -} - -export type socketioRPC = callRPC | unhookRPC | hookRPC - -export type baseInfo = { - owner: string, - argNames: string[], -} - -type HookInfo = baseRPC & baseInfo & { - type: 'hook', - generator: (socket) => callbackFunction - unhook: UnhookFunction -} - -type UnhookInfo = baseRPC & baseInfo & { - type: 'unhook', - func: UnhookFunction -} - -type CallInfo = baseRPC & baseInfo & { - type: 'call', - func: AsyncFunction -} - -type RpcInfo = HookInfo | UnhookInfo | CallInfo - -export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } - -export const rpcToRpcinfo = (rpc : socketioRPC, owner: string):RpcInfo => { - switch(rpc.type){ - case "call" : - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - } - case "unhook" : - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - } - case "hook" : - const generator = hookGenerator(rpc) - return { - owner: owner, - argNames: extractArgs(generator(undefined)), - type: rpc.type, - name: rpc.name, - unhook: rpc.unhook, - generator: generator, - } - } -} - -export const rpcHooker = (socket: Socket, owner:string, RPCs: socketioRPC[], makeUnique = true):ExtendedRpcInfo[] => { - const suffix = makeUnique?"-"+uuid().substr(0,4):"" - return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) - .map(info => { - const ret:any = info - ret.uniqueName = info.name+suffix - - switch(info.type){ - case "hook": - socket.hook(ret.uniqueName, info.generator(socket)) - break; - default: - socket.hook(ret.uniqueName, info.func) - } - socket.on('close', () => socket.unhook(info.name)) - return ret - }) -} - -const hookGenerator = (rpc:hookRPC): HookInfo['generator'] => { - const argsArr = extractArgs(rpc.func) - argsArr.pop() - const args = argsArr.join(',') - - return eval(`(socket) => async (`+args+`) => { - const res = await rpc.func(`+args+(args.length!==0?',':'')+` (x) => { - socket.call(res.uid, x) - }) - if(res.result == 'Success'){ - socket.on('close', async () => { - const unhookRes = await rpc.unhook(res.uid) - console.log("Specific close handler for", rpc.name, res.uid, unhookRes) - }) - - } - return res - }`) -} - -const extractArgs = (f:Function):string[] => { - let fn = String(f) - let args = fn.substr(0, fn.indexOf(")")) - args = args.substr(fn.indexOf("(")+1) - let ret = args.split(",") - return ret -} - - -type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => Socket - -export interface Socket { - port: number - hook: (rpcname: string, ...args: any[]) => Socket - unhook: (rpcname:string) => Socket - call: (rpcname:string, ...args: any[]) => Promise - fire: (rpcname:string, ...args: any[]) => Promise - on: OnFunction - destroy: ()=>void - close: ()=>void -} - -export type RPCSocketConf = { - connectionHandler: (socket:Socket) => void - errorHandler: (socket:Socket) => (error:any) => void - closeHandler: (socket:Socket) => () => void -} - -export class RPCSocketServer{ - - private io = bsock.createServer() - private wsServer = http.createServer() - - constructor( - private port:number, - private rpcExporters: RPCExporter[] = [], - private visibility: Visibility = "127.0.0.1", - private conf: RPCSocketConf = { - errorHandler: (socket:Socket) => (error:any) => { socket.destroy(); console.error(error) }, - closeHandler: (socket:Socket) => () => { console.log("Socket closing") }, - connectionHandler: (socket:Socket) => { console.log("New websocket connection in port "+socket.port) } - } - ){ - this.startWebsocket() - } - - private startWebsocket(){ - try{ - this.io.attach(this.wsServer) - this.io.on('socket', (socket:Socket) => { - socket.on('error', this.conf.errorHandler(socket)) - socket.on('close', this.conf.closeHandler(socket)) - if(this.visibility === "127.0.0.1") - this.initRPCs(socket) - else - this.initPublicRPCs(socket) - }) - this.wsServer.listen(this.port, this.visibility) - }catch(e){ - //@ts-ignore - this.errorHandler(undefined)("Unable to connect to socket") - } - } - - protected initRPCs(socket:Socket){ - const infoRPC:socketioRPC[] = [ - { - name: 'info', - type: 'call', - func: async () => rpcInfos - } - ] - - const rpcInfos:ExtendedRpcInfo[] = [ - ...rpcHooker(socket, "RPC", infoRPC, false), - ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter.name, [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()])) - ] - } - - protected initPublicRPCs(socket:Socket){ - const adminRPCs:socketioRPC[] = [ - { - name: 'info', - type: 'call', - func: async () => rpcInfos - } - ] - - const rpcInfos:ExtendedRpcInfo[] = [ - ...rpcHooker(socket, "Admin", adminRPCs, false), - ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter.name, exporter.exportPublicRPCs())) - ] - } -} \ No newline at end of file diff --git a/src/interfaces/Exporter.ts b/src/interfaces/Exporter.ts new file mode 100644 index 0000000..047ddcb --- /dev/null +++ b/src/interfaces/Exporter.ts @@ -0,0 +1,7 @@ +import { SocketioRPC, Name } from "../Types"; + +export interface Exporter{ + name: Name + exportRPCs() : SocketioRPC[] + exportPublicRPCs() : SocketioRPC[] +} \ No newline at end of file diff --git a/src/interfaces/Socket.ts b/src/interfaces/Socket.ts new file mode 100644 index 0000000..bc1b6ac --- /dev/null +++ b/src/interfaces/Socket.ts @@ -0,0 +1,12 @@ +import { OnFunction } from "../Types"; + +export interface Socket { + port: number + hook: (rpcname: string, ...args: any[]) => Socket + unhook: (rpcname:string) => Socket + call: (rpcname:string, ...args: any[]) => Promise + fire: (rpcname:string, ...args: any[]) => Promise + on: OnFunction + destroy: ()=>void + close: ()=>void +} \ No newline at end of file diff --git a/src/frontend/webpack.prod.js b/src/webpack.prod.js similarity index 87% rename from src/frontend/webpack.prod.js rename to src/webpack.prod.js index 99bbde6..18530dc 100644 --- a/src/frontend/webpack.prod.js +++ b/src/webpack.prod.js @@ -4,10 +4,10 @@ const TerserPlugin = require('terser-webpack-plugin'); module.exports = { mode: 'production', target: "web", - entry: path.resolve(__dirname, 'RPCSocket.ts'), + entry: path.resolve(__dirname, 'Client.ts'), output: { path: path.resolve(__dirname, '../../lib'), - filename: 'RPCaller.min.js', + filename: 'Frontend.min.js', libraryTarget: 'commonjs', }, resolve: { diff --git a/test/test.ts b/test/test.ts index 40b7a7f..da259d4 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,8 +1,8 @@ -import { RPCSocketServer } from '../src/backend/RPCSocketServer' +import { Server } from '../src/Server' //@ts-ignore -import {RPCSocket} from '../src/frontend/RPCSocket' +import {Client} from '../src/Client' -new RPCSocketServer(20000, [{ +new Server(20000, [{ name: "HelloWorldRPCGroup", exportPublicRPCs: () => [], exportRPCs: () => [{ @@ -12,7 +12,7 @@ new RPCSocketServer(20000, [{ }], }]) -const caller = new RPCSocket(20000, 'localhost') +const caller = new Client(20000, 'localhost') caller.connect().then(_ => { caller.info().then(console.log) caller["HelloWorldRPCGroup"].echo("x").then(console.log)