diff --git a/.gitignore b/.gitignore index 3bcb4e6..8451e94 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ - .npm .idea/ .vscode/ +.builder/cache/ node_modules/ -package-lock.json \ No newline at end of file +package-lock.json + diff --git a/README.md b/README.md deleted file mode 100644 index deccd66..0000000 --- a/README.md +++ /dev/null @@ -1,162 +0,0 @@ -

ClusterWS JavaScript Client

-
WebSocket & Node JS Cluster
- -

- Node.js -

- -

- - -

- -**This README, logo and animation will be changed soon, we are currently implementing new GUIDES in wikis and working with new logo and animation** - -## Overview -This is official JavaScript client for [ClusterWS](https://github.com/ClusterWS/ClusterWS). - -[ClusterWS](https://github.com/ClusterWS/ClusterWS) - is a minimal **Node JS http & real-time** framework which allows to scale WebSocket ([uWS](https://github.com/uNetworking/uWebSockets) - one of the fastest WebSocket libraries) between **Workers** in [Node JS Cluster](https://nodejs.org/api/cluster.html) and utilize all available CPU. - -**Current minified version is under 6KB.** - -**This library requires [ClusterWS](https://github.com/ClusterWS/ClusterWS) on the server** - -## Installation -To install ClusterWS Client JS run: -```js -npm install --save clusterws-client-js -``` -or use globally: - -1. Find `ClusterWS.(min).js` in `dist/browser` folder. -2. Use standard script tag to import library ``. -3. Done, now you can use it as `ClusterWS`. - - -## Socket -### 1. Connecting -You can connect to the server with the following code: -```js -var cws = new ClusterWS({ - url: 'localhost', - port: 80 -}) -``` - -in case if you are using builders like `webpack` and `npm` then you need to import library at the top with: -```js -var ClusterWS = require('clusterws-client-js').ClusterWS -``` - -*All available options of ClusterWS:* -```js -{ - url: '{string} url of the server without http or https. (must be provided)', - port: '{number} port of the server. (must be provided)', - autoReconnect: '{boolean} allow to auto-reconnect to the server on lost connection. (default false)', - reconnectionIntervalMin: '{number} how long min time waut. (default 1000) in ms', - reconnectionIntervalMax: '{number} how long max time wait. (default 5000) in ms', - reconnectionAttempts: '{number} how many times to try, 0 means without limit. (default 0)', - secure: '{boolean} user secure connection or not wss/ws. (default false)' -} -``` - -*Auto reconnect count random time between Max and Min interval value this will reduce amount of users which are connection at the same time on reconnection and reduce server load on restart of the server* - -### 2. Listen on events -To listen on events from the server you should use `on` method witch is provided by `cws` -```js -/** - event name: string - can be any string you wish - data: any - is what you send from the client -*/ -cws.on('event name', function(data){ - // in here you can write any logic -}) -``` - -*Also `cws` gets **Reserved Events** such as `'connect'`, `'disconnect'` and `'error'`* -```js -cws.on('connect', function(){ - // in here you can write any logic -}) - -/** - err: any - display the problem with your weboscket -*/ -cws.on('error', function(err){ - // in here you can write any logic -}) - -/** - code: number - represent the reason in number - reason: string - reason why your socket was disconnected -*/ -cws.on('disconnect', function(code, reason){ - // in here you can write any logic -}) -``` - -### 3. Send events -To send events to the server use `send` method witch is provided by `cws` -```js -/** - event name: string - can be any string you wish (client must listen on this event name) - data: any - is what you want to send to the client -*/ -cws.send('event name', data) -``` - -*Avoid emitting **Reserved Events** such as `'connect'`, `'connection'`, `'disconnect'` and `'error'`. Also avoid emitting event and events with `'#'` at the start.* - -## Pub/Sub -You can `subscribe`, `watch`, `unsubscribe` and `publish`, `getChannelByName` to/from the channels -```js -/** - channel name: string - can be any string you wish -*/ -var channel = cws.subscribe('channel name') - -/** - data: any - is what you get when you or some one else publish to the channel -*/ -channel.watch(function(data){ - // in here you can write any logic -}) - -/** - data: any - is what you want to publish to the channel (everyone who is subscribe will get it) -*/ -channel.publish(data) - -/** - This method is used to unsubscribe from the channel -*/ -channel.unsubscribe() - -/** - Also you can chain everything in one expression -*/ -var channel = cws.subscribe('channel name').watch(function(data){ - // in here you can write any logic -}).publish(data) - - -/** - You can get channel by channel name only if you were subscribed before - You can use any methods as with usual channel -*/ -cws.getChannelByName('channel name') - -``` - -**To make sure that user is connected to the server before subscribing, do it on `connect` event or on any other events which you emit from the server, otherwise subscription may not work properly** - -## See Also -* [Medium ClusterWS](https://medium.com/clusterws) -* [ClusterWS Tests](https://github.com/ClusterWS/ClusterWS-Tests) -* [ClusterWS Example Chat](https://github.com/goriunov/ClusterWS-Chat-Example) - -*Docs are still under development. If you have found any errors please submit pull request or leave issue* - -## Happy coding !!! :sunglasses: \ No newline at end of file diff --git a/dist/README.md b/dist/README.md index deccd66..ae37cf8 100644 --- a/dist/README.md +++ b/dist/README.md @@ -1,8 +1,8 @@

ClusterWS JavaScript Client

-
WebSocket & Node JS Cluster
+
Build Scalable Node.js WebSocket Applications

- Node.js +

@@ -10,153 +10,11 @@

-**This README, logo and animation will be changed soon, we are currently implementing new GUIDES in wikis and working with new logo and animation** - -## Overview -This is official JavaScript client for [ClusterWS](https://github.com/ClusterWS/ClusterWS). - -[ClusterWS](https://github.com/ClusterWS/ClusterWS) - is a minimal **Node JS http & real-time** framework which allows to scale WebSocket ([uWS](https://github.com/uNetworking/uWebSockets) - one of the fastest WebSocket libraries) between **Workers** in [Node JS Cluster](https://nodejs.org/api/cluster.html) and utilize all available CPU. - -**Current minified version is under 6KB.** - -**This library requires [ClusterWS](https://github.com/ClusterWS/ClusterWS) on the server** - -## Installation -To install ClusterWS Client JS run: -```js -npm install --save clusterws-client-js -``` -or use globally: - -1. Find `ClusterWS.(min).js` in `dist/browser` folder. -2. Use standard script tag to import library ``. -3. Done, now you can use it as `ClusterWS`. - - -## Socket -### 1. Connecting -You can connect to the server with the following code: -```js -var cws = new ClusterWS({ - url: 'localhost', - port: 80 -}) -``` - -in case if you are using builders like `webpack` and `npm` then you need to import library at the top with: -```js -var ClusterWS = require('clusterws-client-js').ClusterWS -``` - -*All available options of ClusterWS:* -```js -{ - url: '{string} url of the server without http or https. (must be provided)', - port: '{number} port of the server. (must be provided)', - autoReconnect: '{boolean} allow to auto-reconnect to the server on lost connection. (default false)', - reconnectionIntervalMin: '{number} how long min time waut. (default 1000) in ms', - reconnectionIntervalMax: '{number} how long max time wait. (default 5000) in ms', - reconnectionAttempts: '{number} how many times to try, 0 means without limit. (default 0)', - secure: '{boolean} user secure connection or not wss/ws. (default false)' -} -``` - -*Auto reconnect count random time between Max and Min interval value this will reduce amount of users which are connection at the same time on reconnection and reduce server load on restart of the server* - -### 2. Listen on events -To listen on events from the server you should use `on` method witch is provided by `cws` -```js -/** - event name: string - can be any string you wish - data: any - is what you send from the client -*/ -cws.on('event name', function(data){ - // in here you can write any logic -}) -``` - -*Also `cws` gets **Reserved Events** such as `'connect'`, `'disconnect'` and `'error'`* -```js -cws.on('connect', function(){ - // in here you can write any logic -}) - -/** - err: any - display the problem with your weboscket -*/ -cws.on('error', function(err){ - // in here you can write any logic -}) - -/** - code: number - represent the reason in number - reason: string - reason why your socket was disconnected -*/ -cws.on('disconnect', function(code, reason){ - // in here you can write any logic -}) -``` - -### 3. Send events -To send events to the server use `send` method witch is provided by `cws` -```js -/** - event name: string - can be any string you wish (client must listen on this event name) - data: any - is what you want to send to the client -*/ -cws.send('event name', data) -``` - -*Avoid emitting **Reserved Events** such as `'connect'`, `'connection'`, `'disconnect'` and `'error'`. Also avoid emitting event and events with `'#'` at the start.* - -## Pub/Sub -You can `subscribe`, `watch`, `unsubscribe` and `publish`, `getChannelByName` to/from the channels -```js -/** - channel name: string - can be any string you wish -*/ -var channel = cws.subscribe('channel name') - -/** - data: any - is what you get when you or some one else publish to the channel -*/ -channel.watch(function(data){ - // in here you can write any logic -}) - -/** - data: any - is what you want to publish to the channel (everyone who is subscribe will get it) -*/ -channel.publish(data) - -/** - This method is used to unsubscribe from the channel -*/ -channel.unsubscribe() - -/** - Also you can chain everything in one expression -*/ -var channel = cws.subscribe('channel name').watch(function(data){ - // in here you can write any logic -}).publish(data) - - -/** - You can get channel by channel name only if you were subscribed before - You can use any methods as with usual channel -*/ -cws.getChannelByName('channel name') - -``` - -**To make sure that user is connected to the server before subscribing, do it on `connect` event or on any other events which you emit from the server, otherwise subscription may not work properly** - -## See Also -* [Medium ClusterWS](https://medium.com/clusterws) -* [ClusterWS Tests](https://github.com/ClusterWS/ClusterWS-Tests) -* [ClusterWS Example Chat](https://github.com/goriunov/ClusterWS-Chat-Example) - -*Docs are still under development. If you have found any errors please submit pull request or leave issue* +

+ Official JavaScript Client library for ClusterWS - lightweight, fast and powerful framework for building horizontally & vertically scalable WebSocket applications in Node.js +

-## Happy coding !!! :sunglasses: \ No newline at end of file +

+

+ ClusterWS JavaScript Client Documentation +

diff --git a/dist/browser/ClusterWS.js b/dist/browser/ClusterWS.js deleted file mode 100644 index 531456e..0000000 --- a/dist/browser/ClusterWS.js +++ /dev/null @@ -1,224 +0,0 @@ -!function(e, t) { - if ("object" == typeof exports && "object" == typeof module) module.exports = t(); else if ("function" == typeof define && define.amd) define([], t); else { - var n = t(); - for (var o in n) ("object" == typeof exports ? exports : e)[o] = n[o]; - } -}("undefined" != typeof self ? self : this, function() { - return function(e) { - function t(o) { - if (n[o]) return n[o].exports; - var r = n[o] = { - i: o, - l: !1, - exports: {} - }; - return e[o].call(r.exports, r, r.exports, t), r.l = !0, r.exports; - } - var n = {}; - return t.m = e, t.c = n, t.d = function(e, n, o) { - t.o(e, n) || Object.defineProperty(e, n, { - configurable: !1, - enumerable: !0, - get: o - }); - }, t.n = function(e) { - var n = e && e.__esModule ? function() { - return e.default; - } : function() { - return e; - }; - return t.d(n, "a", n), n; - }, t.o = function(e, t) { - return Object.prototype.hasOwnProperty.call(e, t); - }, t.p = "", t(t.s = 1); - }([ function(e, t, n) { - "use strict"; - function o(e) { - return console.log(e); - } - Object.defineProperty(t, "__esModule", { - value: !0 - }), t.logError = o; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 - }); - var o = n(2), r = n(3), i = n(4), s = n(0), c = function() { - function e(e) { - return this.channels = {}, this.missedPing = 0, this.events = new r.EventEmitter(), - this.useBinary = !1, e.url && "string" == typeof e.url ? e.port && "number" == typeof e.port ? (this.options = { - url: e.url, - port: e.port, - autoReconnect: e.autoReconnect || !1, - reconnectionIntervalMin: e.reconnectionIntervalMin || 1e3, - reconnectionIntervalMax: e.reconnectionIntervalMax || 5e3, - reconnectionAttempts: e.reconnectionAttempts || 0, - secure: e.secure || !1 - }, this.options.reconnectionIntervalMin > this.options.reconnectionIntervalMax ? s.logError("reconnectionIntervalMin can not be more then reconnectionIntervalMax") : (this.reconnection = new i.Reconnection(this), - void this.create())) : s.logError("Port must be provided and it must be number") : s.logError("Url must be provided and it must be string"); - } - return e.buffer = function(e) { - for (var t = e.length, n = new Uint8Array(t), o = 0; o < t; o++) n[o] = e.charCodeAt(o); - return n.buffer; - }, e.decode = function(e, t) { - switch (t["#"][0]) { - case "e": - return e.events.emit(t["#"][1], t["#"][2]); - - case "p": - return e.channels[t["#"][1]] ? e.channels[t["#"][1]].onMessage(t["#"][2]) : ""; - - case "s": - switch (t["#"][1]) { - case "c": - e.pingInterval = setInterval(function() { - return e.missedPing++ > 2 ? e.disconnect(4001, "Did not get pings") : ""; - }, t["#"][2].ping), e.useBinary = t["#"][2].binary, e.events.emit("connect"); - } - } - }, e.encode = function(e, t, n) { - switch (n) { - case "ping": - return e; - - case "emit": - return JSON.stringify({ - "#": [ "e", e, t ] - }); - - case "publish": - return JSON.stringify({ - "#": [ "p", e, t ] - }); - - case "system": - switch (e) { - case "subscribe": - return JSON.stringify({ - "#": [ "s", "s", t ] - }); - - case "unsubscribe": - return JSON.stringify({ - "#": [ "s", "u", t ] - }); - - case "configuration": - return JSON.stringify({ - "#": [ "s", "c", t ] - }); - } - } - }, e.prototype.create = function() { - var t = this, n = window.MozWebSocket || window.WebSocket, o = this.options.secure ? "wss://" : "ws://"; - this.websocket = new n(o + this.options.url + ":" + this.options.port), this.websocket.binaryType = "arraybuffer", - this.websocket.onopen = function() { - return t.reconnection.isConnected(); - }, this.websocket.onerror = function(e) { - return t.events.emit("error", e.message); - }, this.websocket.onmessage = function(n) { - var o = n.data; - if (t.useBinary && "string" != typeof o && (o = String.fromCharCode.apply(null, new Uint8Array(o))), - "#0" === o) return t.missedPing = 0, t.send("#1", null, "ping"); - try { - o = JSON.parse(o); - } catch (e) { - return s.logError(e); - } - e.decode(t, o); - }, this.websocket.onclose = function(e) { - if (t.missedPing = 0, clearInterval(t.pingInterval), t.events.emit("disconnect", e.code, e.reason), - !t.reconnection.inReconnectionState) { - if (t.options.autoReconnect && 1e3 !== e.code) return t.reconnection.reconnect(); - t.events.removeAllEvents(); - for (var n in t) t.hasOwnProperty(n) && delete t[n]; - } - }; - }, e.prototype.on = function(e, t) { - this.events.on(e, t); - }, e.prototype.send = function(t, n, o) { - void 0 === o && (o = "emit"), this.websocket.send(this.useBinary ? e.buffer(e.encode(t, n, o)) : e.encode(t, n, o)); - }, e.prototype.disconnect = function(e, t) { - this.websocket.close(e || 1e3, t); - }, e.prototype.getState = function() { - return this.websocket.readyState; - }, e.prototype.subscribe = function(e) { - return this.channels[e] ? this.channels[e] : this.channels[e] = new o.Channel(this, e); - }, e.prototype.getChannelByName = function(e) { - return this.channels[e]; - }, e; - }(); - t.ClusterWS = c; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 - }); - var o = n(0), r = function() { - function e(e, t) { - this.socket = e, this.channel = t, this.subscribe(); - } - return e.prototype.watch = function(e) { - return "[object Function]" !== {}.toString.call(e) ? o.logError("Listener must be a function") : (this.listener = e, - this); - }, e.prototype.publish = function(e) { - return this.socket.send(this.channel, e, "publish"), this; - }, e.prototype.unsubscribe = function() { - this.socket.send("unsubscribe", this.channel, "system"), this.socket.channels[this.channel] = null; - }, e.prototype.onMessage = function(e) { - this.listener && this.listener.call(null, e); - }, e.prototype.subscribe = function() { - this.socket.send("subscribe", this.channel, "system"); - }, e; - }(); - t.Channel = r; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 - }); - var o = n(0), r = function() { - function e() { - this.events = {}; - } - return e.prototype.on = function(e, t) { - if ("[object Function]" !== {}.toString.call(t)) return o.logError("Listener must be a function"); - this.events[e] || (this.events[e] = t); - }, e.prototype.emit = function(e) { - for (var t = [], n = 1; n < arguments.length; n++) t[n - 1] = arguments[n]; - this.events[e] && (o = this.events[e]).call.apply(o, [ null ].concat(t)); - var o; - }, e.prototype.removeAllEvents = function() { - this.events = {}; - }, e; - }(); - t.EventEmitter = r; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 - }); - var o = function() { - function e(e) { - this.socket = e, this.inReconnectionState = !1, this.reconnectionAttempted = 0, - this.autoReconnect = this.socket.options.autoReconnect; - } - return e.prototype.isConnected = function() { - clearTimeout(this.timer), clearInterval(this.interval), this.inReconnectionState = !1, - this.reconnectionAttempted = 0; - for (var e in this.socket.channels) this.socket.channels.hasOwnProperty(e) && this.socket.channels[e].subscribe(); - }, e.prototype.reconnect = function() { - var e = this; - this.inReconnectionState = !0, this.interval = setInterval(function() { - e.socket.getState() === e.socket.websocket.CLOSED && (e.reconnectionAttempted++, - 0 !== e.socket.options.reconnectionAttempts && e.reconnectionAttempted >= e.socket.options.reconnectionAttempts && (clearInterval(e.interval), - e.autoReconnect = !1, e.inReconnectionState = !1), clearTimeout(e.timer), e.timer = setTimeout(function() { - return e.socket.create(); - }, Math.floor(Math.random() * (e.socket.options.reconnectionIntervalMax - e.socket.options.reconnectionIntervalMin + 1)))); - }, this.socket.options.reconnectionIntervalMin); - }, e; - }(); - t.Reconnection = o; - } ]); -}); \ No newline at end of file diff --git a/dist/browser/ClusterWS.min.js b/dist/browser/ClusterWS.min.js deleted file mode 100644 index 49b28ef..0000000 --- a/dist/browser/ClusterWS.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var o in n)("object"==typeof exports?exports:e)[o]=n[o]}}("undefined"!=typeof self?self:this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,n){"use strict";function o(e){return console.log(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.logError=o},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=n(2),r=n(3),i=n(4),s=n(0);t.ClusterWS=function(){function e(e){return this.channels={},this.missedPing=0,this.events=new r.EventEmitter,this.useBinary=!1,e.url&&"string"==typeof e.url?e.port&&"number"==typeof e.port?(this.options={url:e.url,port:e.port,autoReconnect:e.autoReconnect||!1,reconnectionIntervalMin:e.reconnectionIntervalMin||1e3,reconnectionIntervalMax:e.reconnectionIntervalMax||5e3,reconnectionAttempts:e.reconnectionAttempts||0,secure:e.secure||!1},this.options.reconnectionIntervalMin>this.options.reconnectionIntervalMax?s.logError("reconnectionIntervalMin can not be more then reconnectionIntervalMax"):(this.reconnection=new i.Reconnection(this),void this.create())):s.logError("Port must be provided and it must be number"):s.logError("Url must be provided and it must be string")}return e.buffer=function(e){for(var t=e.length,n=new Uint8Array(t),o=0;t>o;o++)n[o]=e.charCodeAt(o);return n.buffer},e.decode=function(e,t){switch(t["#"][0]){case"e":return e.events.emit(t["#"][1],t["#"][2]);case"p":return e.channels[t["#"][1]]?e.channels[t["#"][1]].onMessage(t["#"][2]):"";case"s":switch(t["#"][1]){case"c":e.pingInterval=setInterval(function(){return e.missedPing++>2?e.disconnect(4001,"Did not get pings"):""},t["#"][2].ping),e.useBinary=t["#"][2].binary,e.events.emit("connect")}}},e.encode=function(e,t,n){switch(n){case"ping":return e;case"emit":return JSON.stringify({"#":["e",e,t]});case"publish":return JSON.stringify({"#":["p",e,t]});case"system":switch(e){case"subscribe":return JSON.stringify({"#":["s","s",t]});case"unsubscribe":return JSON.stringify({"#":["s","u",t]});case"configuration":return JSON.stringify({"#":["s","c",t]})}}},e.prototype.create=function(){var t=this;this.websocket=new(window.MozWebSocket||window.WebSocket)((this.options.secure?"wss://":"ws://")+this.options.url+":"+this.options.port),this.websocket.binaryType="arraybuffer",this.websocket.onopen=function(){return t.reconnection.isConnected()},this.websocket.onerror=function(e){return t.events.emit("error",e.message)},this.websocket.onmessage=function(n){var o=n.data;if(t.useBinary&&"string"!=typeof o&&(o=String.fromCharCode.apply(null,new Uint8Array(o))),"#0"===o)return t.missedPing=0,t.send("#1",null,"ping");try{o=JSON.parse(o)}catch(e){return s.logError(e)}e.decode(t,o)},this.websocket.onclose=function(e){if(t.missedPing=0,clearInterval(t.pingInterval),t.events.emit("disconnect",e.code,e.reason),!t.reconnection.inReconnectionState){if(t.options.autoReconnect&&1e3!==e.code)return t.reconnection.reconnect();t.events.removeAllEvents();for(var n in t)t.hasOwnProperty(n)&&delete t[n]}}},e.prototype.on=function(e,t){this.events.on(e,t)},e.prototype.send=function(t,n,o){void 0===o&&(o="emit"),this.websocket.send(this.useBinary?e.buffer(e.encode(t,n,o)):e.encode(t,n,o))},e.prototype.disconnect=function(e,t){this.websocket.close(e||1e3,t)},e.prototype.getState=function(){return this.websocket.readyState},e.prototype.subscribe=function(e){return this.channels[e]?this.channels[e]:this.channels[e]=new o.Channel(this,e)},e.prototype.getChannelByName=function(e){return this.channels[e]},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=n(0);t.Channel=function(){function e(e,t){this.socket=e,this.channel=t,this.subscribe()}return e.prototype.watch=function(e){return"[object Function]"!=={}.toString.call(e)?o.logError("Listener must be a function"):(this.listener=e,this)},e.prototype.publish=function(e){return this.socket.send(this.channel,e,"publish"),this},e.prototype.unsubscribe=function(){this.socket.send("unsubscribe",this.channel,"system"),this.socket.channels[this.channel]=null},e.prototype.onMessage=function(e){this.listener&&this.listener.call(null,e)},e.prototype.subscribe=function(){this.socket.send("subscribe",this.channel,"system")},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=n(0);t.EventEmitter=function(){function e(){this.events={}}return e.prototype.on=function(e,t){if("[object Function]"!=={}.toString.call(t))return o.logError("Listener must be a function");this.events[e]||(this.events[e]=t)},e.prototype.emit=function(e){for(var t=[],n=1;arguments.length>n;n++)t[n-1]=arguments[n];this.events[e]&&(o=this.events[e]).call.apply(o,[null].concat(t));var o},e.prototype.removeAllEvents=function(){this.events={}},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Reconnection=function(){function e(e){this.socket=e,this.inReconnectionState=!1,this.reconnectionAttempted=0,this.autoReconnect=this.socket.options.autoReconnect}return e.prototype.isConnected=function(){clearTimeout(this.timer),clearInterval(this.interval),this.inReconnectionState=!1,this.reconnectionAttempted=0;for(var e in this.socket.channels)this.socket.channels.hasOwnProperty(e)&&this.socket.channels[e].subscribe()},e.prototype.reconnect=function(){var e=this;this.inReconnectionState=!0,this.interval=setInterval(function(){e.socket.getState()===e.socket.websocket.CLOSED&&(e.reconnectionAttempted++,0===e.socket.options.reconnectionAttempts||e.socket.options.reconnectionAttempts>e.reconnectionAttempted||(clearInterval(e.interval),e.autoReconnect=!1,e.inReconnectionState=!1),clearTimeout(e.timer),e.timer=setTimeout(function(){return e.socket.create()},Math.floor(Math.random()*(e.socket.options.reconnectionIntervalMax-e.socket.options.reconnectionIntervalMin+1))))},this.socket.options.reconnectionIntervalMin)},e}()}])}); \ No newline at end of file diff --git a/dist/browser/clusterws.js b/dist/browser/clusterws.js new file mode 100644 index 0000000..f9c3fb0 --- /dev/null +++ b/dist/browser/clusterws.js @@ -0,0 +1,157 @@ +var ClusterWS = function() { + "use strict"; + function t(t) { + return console.log(t); + } + var n = function() { + function n(t, n) { + this.socket = t, this.name = n, this.subscribe(); + } + return n.prototype.watch = function(n) { + return "[object Function]" !== {}.toString.call(n) ? t("Listener must be a function") : (this.listener = n, + this); + }, n.prototype.publish = function(t) { + return this.socket.send(this.name, t, "publish"), this; + }, n.prototype.unsubscribe = function() { + this.socket.send("unsubscribe", this.name, "system"), this.socket.channels[this.name] = null; + }, n.prototype.onMessage = function(t) { + this.listener && this.listener.call(null, t); + }, n.prototype.subscribe = function() { + this.socket.send("subscribe", this.name, "system"); + }, n; + }(), e = function() { + function n() { + this.events = {}; + } + return n.prototype.on = function(n, e) { + if ("[object Function]" !== {}.toString.call(e)) return t("Listener must be a function"); + this.events[n] = e; + }, n.prototype.emit = function(t) { + for (var n = [], e = 1; e < arguments.length; e++) n[e - 1] = arguments[e]; + this.events[t] && (o = this.events[t]).call.apply(o, [ null ].concat(n)); + var o; + }, n.prototype.removeAllEvents = function() { + this.events = {}; + }, n; + }(), o = function() { + function t(t) { + this.socket = t, this.inReconnectionState = !1, this.reconnectionAttempted = 0, + this.autoReconnect = this.socket.options.autoReconnect; + } + return t.prototype.isConnected = function() { + clearTimeout(this.timer), clearInterval(this.interval), this.inReconnectionState = !1, + this.reconnectionAttempted = 0; + for (var t in this.socket.channels) this.socket.channels[t] && this.socket.channels[t].subscribe(); + }, t.prototype.reconnect = function() { + var t = this; + this.inReconnectionState || (this.inReconnectionState = !0, this.interval = setInterval(function() { + t.socket.getState() === t.socket.websocket.CLOSED && (t.reconnectionAttempted++, + 0 !== t.socket.options.reconnectionAttempts && t.reconnectionAttempted >= t.socket.options.reconnectionAttempts && (clearInterval(t.interval), + t.autoReconnect = !1, t.inReconnectionState = !1), clearTimeout(t.timer), t.timer = setTimeout(function() { + return t.socket.create(); + }, Math.floor(Math.random() * (t.socket.options.reconnectionIntervalMax - t.socket.options.reconnectionIntervalMin + 1)))); + }, this.socket.options.reconnectionIntervalMin)); + }, t; + }(); + function i(t, n, e) { + switch (e) { + case "ping": + return t; + + case "emit": + return JSON.stringify({ + "#": [ "e", t, n ] + }); + + case "publish": + return JSON.stringify({ + "#": [ "p", t, n ] + }); + + case "system": + switch (t) { + case "subscribe": + return JSON.stringify({ + "#": [ "s", "s", n ] + }); + + case "unsubscribe": + return JSON.stringify({ + "#": [ "s", "u", n ] + }); + + case "configuration": + return JSON.stringify({ + "#": [ "s", "c", n ] + }); + } + } + } + return function() { + function s(n) { + return this.channels = {}, this.events = new e(), this.missedPing = 0, this.useBinary = !1, + n.url ? (this.options = { + url: n.url, + autoReconnect: n.autoReconnect || !1, + reconnectionAttempts: n.reconnectionAttempts || 0, + reconnectionIntervalMin: n.reconnectionIntervalMin || 1e3, + reconnectionIntervalMax: n.reconnectionIntervalMax || 5e3 + }, this.options.reconnectionIntervalMin > this.options.reconnectionIntervalMax ? t("reconnectionIntervalMin can not be more then reconnectionIntervalMax") : (this.reconnection = new o(this), + void this.create())) : t("Url must be provided and it must be string"); + } + return s.prototype.create = function() { + var n = this, e = window.MozWebSocket || window.WebSocket; + this.websocket = new e(this.options.url), this.websocket.binaryType = "arraybuffer", + this.websocket.onopen = function() { + return n.reconnection.isConnected(); + }, this.websocket.onerror = function(t) { + return n.events.emit("error", t.message); + }, this.websocket.onmessage = function(e) { + var o = "string" != typeof e.data ? String.fromCharCode.apply(null, new Uint8Array(e.data)) : e.data; + if ("#0" === o) return n.missedPing = 0, n.send("#1", null, "ping"); + try { + o = JSON.parse(o); + } catch (n) { + return t(n); + } + !function(t, n) { + switch (n["#"][0]) { + case "e": + return t.events.emit(n["#"][1], n["#"][2]); + + case "p": + t.channels[n["#"][1]] && t.channels[n["#"][1]].onMessage(n["#"][2]); + + case "s": + switch (n["#"][1]) { + case "c": + t.pingInterval = setInterval(function() { + return t.missedPing++ > 2 && t.disconnect(4001, "Did not get pings"); + }, n["#"][2].ping), t.useBinary = n["#"][2].binary, t.events.emit("connect"); + } + } + }(n, o); + }, this.websocket.onclose = function(t) { + if (n.missedPing = 0, clearInterval(n.pingInterval), n.events.emit("disconnect", t.code, t.reason), + n.options.autoReconnect && 1e3 !== t.code) return n.reconnection.reconnect(); + n.events.removeAllEvents(); + for (var e in n) n[e] && (n[e] = null); + }; + }, s.prototype.on = function(t, n) { + this.events.on(t, n); + }, s.prototype.send = function(t, n, e) { + void 0 === e && (e = "emit"), this.websocket.send(this.useBinary ? function(t) { + for (var n = t.length, e = new Uint8Array(n), o = 0; o < n; o++) e[o] = t.charCodeAt(o); + return e.buffer; + }(i(t, n, e)) : i(t, n, e)); + }, s.prototype.disconnect = function(t, n) { + this.websocket.close(t || 1e3, n); + }, s.prototype.getState = function() { + return this.websocket.readyState; + }, s.prototype.subscribe = function(t) { + return this.channels[t] ? this.channels[t] : this.channels[t] = new n(this, t); + }, s.prototype.getChannelByName = function(t) { + return this.channels[t]; + }, s; + }(); +}(); diff --git a/dist/browser/clusterws.min.js b/dist/browser/clusterws.min.js new file mode 100644 index 0000000..2120f81 --- /dev/null +++ b/dist/browser/clusterws.min.js @@ -0,0 +1 @@ +var ClusterWS=function(){"use strict";function t(t){return console.log(t)}var n=function(){function n(t,n){this.socket=t,this.name=n,this.subscribe()}return n.prototype.watch=function(n){return"[object Function]"!=={}.toString.call(n)?t("Listener must be a function"):(this.listener=n,this)},n.prototype.publish=function(t){return this.socket.send(this.name,t,"publish"),this},n.prototype.unsubscribe=function(){this.socket.send("unsubscribe",this.name,"system"),this.socket.channels[this.name]=null},n.prototype.onMessage=function(t){this.listener&&this.listener.call(null,t)},n.prototype.subscribe=function(){this.socket.send("subscribe",this.name,"system")},n}(),e=function(){function n(){this.events={}}return n.prototype.on=function(n,e){if("[object Function]"!=={}.toString.call(e))return t("Listener must be a function");this.events[n]=e},n.prototype.emit=function(t){for(var n=[],e=1;e=t.socket.options.reconnectionAttempts&&(clearInterval(t.interval),t.autoReconnect=!1,t.inReconnectionState=!1),clearTimeout(t.timer),t.timer=setTimeout(function(){return t.socket.create()},Math.floor(Math.random()*(t.socket.options.reconnectionIntervalMax-t.socket.options.reconnectionIntervalMin+1))))},this.socket.options.reconnectionIntervalMin))},t}();function i(t,n,e){switch(e){case"ping":return t;case"emit":return JSON.stringify({"#":["e",t,n]});case"publish":return JSON.stringify({"#":["p",t,n]});case"system":switch(t){case"subscribe":return JSON.stringify({"#":["s","s",n]});case"unsubscribe":return JSON.stringify({"#":["s","u",n]});case"configuration":return JSON.stringify({"#":["s","c",n]})}}}return function(){function s(n){return this.channels={},this.events=new e,this.missedPing=0,this.useBinary=!1,n.url?(this.options={url:n.url,autoReconnect:n.autoReconnect||!1,reconnectionAttempts:n.reconnectionAttempts||0,reconnectionIntervalMin:n.reconnectionIntervalMin||1e3,reconnectionIntervalMax:n.reconnectionIntervalMax||5e3},this.options.reconnectionIntervalMin>this.options.reconnectionIntervalMax?t("reconnectionIntervalMin can not be more then reconnectionIntervalMax"):(this.reconnection=new o(this),void this.create())):t("Url must be provided and it must be string")}return s.prototype.create=function(){var n=this,e=window.MozWebSocket||window.WebSocket;this.websocket=new e(this.options.url),this.websocket.binaryType="arraybuffer",this.websocket.onopen=function(){return n.reconnection.isConnected()},this.websocket.onerror=function(t){return n.events.emit("error",t.message)},this.websocket.onmessage=function(e){var o="string"!=typeof e.data?String.fromCharCode.apply(null,new Uint8Array(e.data)):e.data;if("#0"===o)return n.missedPing=0,n.send("#1",null,"ping");try{o=JSON.parse(o)}catch(n){return t(n)}!function(t,n){switch(n["#"][0]){case"e":return t.events.emit(n["#"][1],n["#"][2]);case"p":t.channels[n["#"][1]]&&t.channels[n["#"][1]].onMessage(n["#"][2]);case"s":switch(n["#"][1]){case"c":t.pingInterval=setInterval(function(){return t.missedPing++>2&&t.disconnect(4001,"Did not get pings")},n["#"][2].ping),t.useBinary=n["#"][2].binary,t.events.emit("connect")}}}(n,o)},this.websocket.onclose=function(t){if(n.missedPing=0,clearInterval(n.pingInterval),n.events.emit("disconnect",t.code,t.reason),n.options.autoReconnect&&1e3!==t.code)return n.reconnection.reconnect();n.events.removeAllEvents();for(var e in n)n[e]&&(n[e]=null)}},s.prototype.on=function(t,n){this.events.on(t,n)},s.prototype.send=function(t,n,e){void 0===e&&(e="emit"),this.websocket.send(this.useBinary?function(t){for(var n=t.length,e=new Uint8Array(n),o=0;o void; -export type TSocketMessage = any; -export interface IObject { +export type Listener = (...args: any[]) => void; +export interface CustomObject { [propName: string]: any; } -export interface IUserOptions { +export interface Options { url: string; - port: number; - autoReconnect?: boolean; - reconnectionIntervalMin?: number; - reconnectionIntervalMax?: number; - reconnectionAttempts?: number; - secure?: boolean; -} -export interface IOptions { - url: string; - port: number; autoReconnect: boolean; + reconnectionAttempts: number; reconnectionIntervalMin: number; reconnectionIntervalMax: number; - reconnectionAttempts: number; - secure: boolean; +} +export interface Configurations { + url: string; + autoReconnect?: boolean; + reconnectionAttempts?: number; + reconnectionIntervalMin?: number; + reconnectionIntervalMax?: number; } export function logError(data: T): any; diff --git a/dist/index.js b/dist/index.js index 531456e..112593c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,224 +1,165 @@ -!function(e, t) { - if ("object" == typeof exports && "object" == typeof module) module.exports = t(); else if ("function" == typeof define && define.amd) define([], t); else { - var n = t(); - for (var o in n) ("object" == typeof exports ? exports : e)[o] = n[o]; - } -}("undefined" != typeof self ? self : this, function() { - return function(e) { - function t(o) { - if (n[o]) return n[o].exports; - var r = n[o] = { - i: o, - l: !1, - exports: {} - }; - return e[o].call(r.exports, r, r.exports, t), r.l = !0, r.exports; - } - var n = {}; - return t.m = e, t.c = n, t.d = function(e, n, o) { - t.o(e, n) || Object.defineProperty(e, n, { - configurable: !1, - enumerable: !0, - get: o - }); - }, t.n = function(e) { - var n = e && e.__esModule ? function() { - return e.default; - } : function() { - return e; - }; - return t.d(n, "a", n), n; - }, t.o = function(e, t) { - return Object.prototype.hasOwnProperty.call(e, t); - }, t.p = "", t(t.s = 1); - }([ function(e, t, n) { - "use strict"; - function o(e) { - return console.log(e); - } - Object.defineProperty(t, "__esModule", { - value: !0 - }), t.logError = o; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 - }); - var o = n(2), r = n(3), i = n(4), s = n(0), c = function() { - function e(e) { - return this.channels = {}, this.missedPing = 0, this.events = new r.EventEmitter(), - this.useBinary = !1, e.url && "string" == typeof e.url ? e.port && "number" == typeof e.port ? (this.options = { - url: e.url, - port: e.port, - autoReconnect: e.autoReconnect || !1, - reconnectionIntervalMin: e.reconnectionIntervalMin || 1e3, - reconnectionIntervalMax: e.reconnectionIntervalMax || 5e3, - reconnectionAttempts: e.reconnectionAttempts || 0, - secure: e.secure || !1 - }, this.options.reconnectionIntervalMin > this.options.reconnectionIntervalMax ? s.logError("reconnectionIntervalMin can not be more then reconnectionIntervalMax") : (this.reconnection = new i.Reconnection(this), - void this.create())) : s.logError("Port must be provided and it must be number") : s.logError("Url must be provided and it must be string"); - } - return e.buffer = function(e) { - for (var t = e.length, n = new Uint8Array(t), o = 0; o < t; o++) n[o] = e.charCodeAt(o); - return n.buffer; - }, e.decode = function(e, t) { - switch (t["#"][0]) { - case "e": - return e.events.emit(t["#"][1], t["#"][2]); +"use strict"; + +function logError(t) { + return console.log(t); +} - case "p": - return e.channels[t["#"][1]] ? e.channels[t["#"][1]].onMessage(t["#"][2]) : ""; +var Channel = function() { + function t(t, e) { + this.socket = t, this.name = e, this.subscribe(); + } + return t.prototype.watch = function(t) { + return "[object Function]" !== {}.toString.call(t) ? logError("Listener must be a function") : (this.listener = t, + this); + }, t.prototype.publish = function(t) { + return this.socket.send(this.name, t, "publish"), this; + }, t.prototype.unsubscribe = function() { + this.socket.send("unsubscribe", this.name, "system"), this.socket.channels[this.name] = null; + }, t.prototype.onMessage = function(t) { + this.listener && this.listener.call(null, t); + }, t.prototype.subscribe = function() { + this.socket.send("subscribe", this.name, "system"); + }, t; +}(), EventEmitter = function() { + function t() { + this.events = {}; + } + return t.prototype.on = function(t, e) { + if ("[object Function]" !== {}.toString.call(e)) return logError("Listener must be a function"); + this.events[t] = e; + }, t.prototype.emit = function(t) { + for (var e = [], n = 1; n < arguments.length; n++) e[n - 1] = arguments[n]; + this.events[t] && (o = this.events[t]).call.apply(o, [ null ].concat(e)); + var o; + }, t.prototype.removeAllEvents = function() { + this.events = {}; + }, t; +}(), Reconnection = function() { + function t(t) { + this.socket = t, this.inReconnectionState = !1, this.reconnectionAttempted = 0, + this.autoReconnect = this.socket.options.autoReconnect; + } + return t.prototype.isConnected = function() { + clearTimeout(this.timer), clearInterval(this.interval), this.inReconnectionState = !1, + this.reconnectionAttempted = 0; + for (var t in this.socket.channels) this.socket.channels[t] && this.socket.channels[t].subscribe(); + }, t.prototype.reconnect = function() { + var t = this; + this.inReconnectionState || (this.inReconnectionState = !0, this.interval = setInterval(function() { + t.socket.getState() === t.socket.websocket.CLOSED && (t.reconnectionAttempted++, + 0 !== t.socket.options.reconnectionAttempts && t.reconnectionAttempted >= t.socket.options.reconnectionAttempts && (clearInterval(t.interval), + t.autoReconnect = !1, t.inReconnectionState = !1), clearTimeout(t.timer), t.timer = setTimeout(function() { + return t.socket.create(); + }, Math.floor(Math.random() * (t.socket.options.reconnectionIntervalMax - t.socket.options.reconnectionIntervalMin + 1)))); + }, this.socket.options.reconnectionIntervalMin)); + }, t; +}(); - case "s": - switch (t["#"][1]) { - case "c": - e.pingInterval = setInterval(function() { - return e.missedPing++ > 2 ? e.disconnect(4001, "Did not get pings") : ""; - }, t["#"][2].ping), e.useBinary = t["#"][2].binary, e.events.emit("connect"); - } - } - }, e.encode = function(e, t, n) { - switch (n) { - case "ping": - return e; +function buffer(t) { + for (var e = t.length, n = new Uint8Array(e), o = 0; o < e; o++) n[o] = t.charCodeAt(o); + return n.buffer; +} - case "emit": - return JSON.stringify({ - "#": [ "e", e, t ] - }); +function decode(t, e) { + switch (e["#"][0]) { + case "e": + return t.events.emit(e["#"][1], e["#"][2]); - case "publish": - return JSON.stringify({ - "#": [ "p", e, t ] - }); + case "p": + t.channels[e["#"][1]] && t.channels[e["#"][1]].onMessage(e["#"][2]); - case "system": - switch (e) { - case "subscribe": - return JSON.stringify({ - "#": [ "s", "s", t ] - }); + case "s": + switch (e["#"][1]) { + case "c": + t.pingInterval = setInterval(function() { + return t.missedPing++ > 2 && t.disconnect(4001, "Did not get pings"); + }, e["#"][2].ping), t.useBinary = e["#"][2].binary, t.events.emit("connect"); + } + } +} - case "unsubscribe": - return JSON.stringify({ - "#": [ "s", "u", t ] - }); +function encode(t, e, n) { + switch (n) { + case "ping": + return t; - case "configuration": - return JSON.stringify({ - "#": [ "s", "c", t ] - }); - } - } - }, e.prototype.create = function() { - var t = this, n = window.MozWebSocket || window.WebSocket, o = this.options.secure ? "wss://" : "ws://"; - this.websocket = new n(o + this.options.url + ":" + this.options.port), this.websocket.binaryType = "arraybuffer", - this.websocket.onopen = function() { - return t.reconnection.isConnected(); - }, this.websocket.onerror = function(e) { - return t.events.emit("error", e.message); - }, this.websocket.onmessage = function(n) { - var o = n.data; - if (t.useBinary && "string" != typeof o && (o = String.fromCharCode.apply(null, new Uint8Array(o))), - "#0" === o) return t.missedPing = 0, t.send("#1", null, "ping"); - try { - o = JSON.parse(o); - } catch (e) { - return s.logError(e); - } - e.decode(t, o); - }, this.websocket.onclose = function(e) { - if (t.missedPing = 0, clearInterval(t.pingInterval), t.events.emit("disconnect", e.code, e.reason), - !t.reconnection.inReconnectionState) { - if (t.options.autoReconnect && 1e3 !== e.code) return t.reconnection.reconnect(); - t.events.removeAllEvents(); - for (var n in t) t.hasOwnProperty(n) && delete t[n]; - } - }; - }, e.prototype.on = function(e, t) { - this.events.on(e, t); - }, e.prototype.send = function(t, n, o) { - void 0 === o && (o = "emit"), this.websocket.send(this.useBinary ? e.buffer(e.encode(t, n, o)) : e.encode(t, n, o)); - }, e.prototype.disconnect = function(e, t) { - this.websocket.close(e || 1e3, t); - }, e.prototype.getState = function() { - return this.websocket.readyState; - }, e.prototype.subscribe = function(e) { - return this.channels[e] ? this.channels[e] : this.channels[e] = new o.Channel(this, e); - }, e.prototype.getChannelByName = function(e) { - return this.channels[e]; - }, e; - }(); - t.ClusterWS = c; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 - }); - var o = n(0), r = function() { - function e(e, t) { - this.socket = e, this.channel = t, this.subscribe(); - } - return e.prototype.watch = function(e) { - return "[object Function]" !== {}.toString.call(e) ? o.logError("Listener must be a function") : (this.listener = e, - this); - }, e.prototype.publish = function(e) { - return this.socket.send(this.channel, e, "publish"), this; - }, e.prototype.unsubscribe = function() { - this.socket.send("unsubscribe", this.channel, "system"), this.socket.channels[this.channel] = null; - }, e.prototype.onMessage = function(e) { - this.listener && this.listener.call(null, e); - }, e.prototype.subscribe = function() { - this.socket.send("subscribe", this.channel, "system"); - }, e; - }(); - t.Channel = r; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 + case "emit": + return JSON.stringify({ + "#": [ "e", t, e ] }); - var o = n(0), r = function() { - function e() { - this.events = {}; - } - return e.prototype.on = function(e, t) { - if ("[object Function]" !== {}.toString.call(t)) return o.logError("Listener must be a function"); - this.events[e] || (this.events[e] = t); - }, e.prototype.emit = function(e) { - for (var t = [], n = 1; n < arguments.length; n++) t[n - 1] = arguments[n]; - this.events[e] && (o = this.events[e]).call.apply(o, [ null ].concat(t)); - var o; - }, e.prototype.removeAllEvents = function() { - this.events = {}; - }, e; - }(); - t.EventEmitter = r; - }, function(e, t, n) { - "use strict"; - Object.defineProperty(t, "__esModule", { - value: !0 + + case "publish": + return JSON.stringify({ + "#": [ "p", t, e ] }); - var o = function() { - function e(e) { - this.socket = e, this.inReconnectionState = !1, this.reconnectionAttempted = 0, - this.autoReconnect = this.socket.options.autoReconnect; + + case "system": + switch (t) { + case "subscribe": + return JSON.stringify({ + "#": [ "s", "s", e ] + }); + + case "unsubscribe": + return JSON.stringify({ + "#": [ "s", "u", e ] + }); + + case "configuration": + return JSON.stringify({ + "#": [ "s", "c", e ] + }); + } + } +} + +var ClusterWS = function() { + function t(t) { + return this.channels = {}, this.events = new EventEmitter(), this.missedPing = 0, + this.useBinary = !1, t.url ? (this.options = { + url: t.url, + autoReconnect: t.autoReconnect || !1, + reconnectionAttempts: t.reconnectionAttempts || 0, + reconnectionIntervalMin: t.reconnectionIntervalMin || 1e3, + reconnectionIntervalMax: t.reconnectionIntervalMax || 5e3 + }, this.options.reconnectionIntervalMin > this.options.reconnectionIntervalMax ? logError("reconnectionIntervalMin can not be more then reconnectionIntervalMax") : (this.reconnection = new Reconnection(this), + void this.create())) : logError("Url must be provided and it must be string"); + } + return t.prototype.create = function() { + var t = this, e = window.MozWebSocket || window.WebSocket; + this.websocket = new e(this.options.url), this.websocket.binaryType = "arraybuffer", + this.websocket.onopen = function() { + return t.reconnection.isConnected(); + }, this.websocket.onerror = function(e) { + return t.events.emit("error", e.message); + }, this.websocket.onmessage = function(e) { + var n = "string" != typeof e.data ? String.fromCharCode.apply(null, new Uint8Array(e.data)) : e.data; + if ("#0" === n) return t.missedPing = 0, t.send("#1", null, "ping"); + try { + n = JSON.parse(n); + } catch (t) { + return logError(t); } - return e.prototype.isConnected = function() { - clearTimeout(this.timer), clearInterval(this.interval), this.inReconnectionState = !1, - this.reconnectionAttempted = 0; - for (var e in this.socket.channels) this.socket.channels.hasOwnProperty(e) && this.socket.channels[e].subscribe(); - }, e.prototype.reconnect = function() { - var e = this; - this.inReconnectionState = !0, this.interval = setInterval(function() { - e.socket.getState() === e.socket.websocket.CLOSED && (e.reconnectionAttempted++, - 0 !== e.socket.options.reconnectionAttempts && e.reconnectionAttempted >= e.socket.options.reconnectionAttempts && (clearInterval(e.interval), - e.autoReconnect = !1, e.inReconnectionState = !1), clearTimeout(e.timer), e.timer = setTimeout(function() { - return e.socket.create(); - }, Math.floor(Math.random() * (e.socket.options.reconnectionIntervalMax - e.socket.options.reconnectionIntervalMin + 1)))); - }, this.socket.options.reconnectionIntervalMin); - }, e; - }(); - t.Reconnection = o; - } ]); -}); \ No newline at end of file + decode(t, n); + }, this.websocket.onclose = function(e) { + if (t.missedPing = 0, clearInterval(t.pingInterval), t.events.emit("disconnect", e.code, e.reason), + t.options.autoReconnect && 1e3 !== e.code) return t.reconnection.reconnect(); + t.events.removeAllEvents(); + for (var n in t) t[n] && (t[n] = null); + }; + }, t.prototype.on = function(t, e) { + this.events.on(t, e); + }, t.prototype.send = function(t, e, n) { + void 0 === n && (n = "emit"), this.websocket.send(this.useBinary ? buffer(encode(t, e, n)) : encode(t, e, n)); + }, t.prototype.disconnect = function(t, e) { + this.websocket.close(t || 1e3, e); + }, t.prototype.getState = function() { + return this.websocket.readyState; + }, t.prototype.subscribe = function(t) { + return this.channels[t] ? this.channels[t] : this.channels[t] = new Channel(this, t); + }, t.prototype.getChannelByName = function(t) { + return this.channels[t]; + }, t; +}(); + +module.exports = ClusterWS; diff --git a/dist/package.json b/dist/package.json index 919699d..dd98aa6 100644 --- a/dist/package.json +++ b/dist/package.json @@ -1,13 +1,12 @@ { "name": "clusterws-client-js", - "version": "1.4.0", + "version": "1.5.0", "description": "JavaScript Client for ClusterWS - lightweight, fast and powerful framework for building horizontally & vertically scalable WebSocket applications in Node.js.", "main": "index.js", - "author": "Dmitrii Goriunov ", + "author": "Dmitrii Goriunov", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/ClusterWS/ClusterWS-Client-JS" - }, - "types": "./index.d.ts" + } } \ No newline at end of file diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..63cf2c9 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team . The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/docs/ISSUE_TEMPLATE.md b/docs/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..3a08b31 --- /dev/null +++ b/docs/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ +### Submitting + +- [ ] Bug +- [ ] Question +- [ ] Suggetion +- [ ] Other + +### Details + \ No newline at end of file diff --git a/LICENSE b/docs/LICENSE similarity index 100% rename from LICENSE rename to docs/LICENSE diff --git a/docs/PULL_REQUEST_TEMPLATE.md b/docs/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..0837183 --- /dev/null +++ b/docs/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +### Submitting + +- [ ] Bug fix +- [ ] Feature +- [ ] Other + +### Details + \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..ae37cf8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,20 @@ +

ClusterWS JavaScript Client

+
Build Scalable Node.js WebSocket Applications
+ +

+ +

+ +

+ + +

+ +

+ Official JavaScript Client library for ClusterWS - lightweight, fast and powerful framework for building horizontally & vertically scalable WebSocket applications in Node.js +

+ +

+

+ ClusterWS JavaScript Client Documentation +

diff --git a/package.json b/package.json index 571c405..4664d68 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "clusterws-client-js", - "version": "1.4.0", + "version": "1.5.0", "description": "JavaScript Client for ClusterWS - lightweight, fast and powerful framework for building horizontally & vertically scalable WebSocket applications in Node.js.", "main": "index.js", - "author": "Dmitrii Goriunov ", + "author": "Dmitrii Goriunov", "scripts": { - "build": "ts-builder --client", - "build:watch": "ts-builder --client --watch", - "build:prod": "ts-builder --client --prod", + "build:npm": "ts-builder -npm -folder src -mainfile index.ts -outfolder dist -outfile index.js", + "build:browser": "ts-builder -format iife -folder src -mainfile index.ts -outfolder dist/browser -outfile clusterws.js && ts-builder -prod -format iife -folder src -mainfile index.ts -outfolder dist/browser -outfile clusterws.min.js", + "build:prod": "npm run build:npm && npm run build:browser", "publish": "npm run build:prod && cd dist && npm publish && cd .." }, "license": "MIT", @@ -16,6 +16,6 @@ "url": "https://github.com/ClusterWS/ClusterWS-Client-JS" }, "devDependencies": { - "ts-builder": "^0.2.20" + "ts-builder": "^0.5.11" } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 0120af2..07859e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,72 +1,33 @@ -import { Channel } from './modules/channel' -import { EventEmitter } from './utils/emitter' -import { Reconnection } from './modules/reconnection' -import { IObject, TSocketMessage, TListener, IUserOptions, IOptions, logError } from './utils/utils' +import { Channel } from './modules/channel/channel' +import { EventEmitter } from './modules/emitter/emitter' +import { Reconnection } from './modules/reconnection/reconnection' +import { buffer, decode, encode } from './modules/parser/parser' +import { Options, Configurations, logError, Listener, CustomObject } from './utils/utils' declare const window: any -export class ClusterWS { - private static buffer(str: string): ByteString { - const length: number = str.length - const uint: any = new Uint8Array(length) - for (let i: number = 0; i < length; i++) uint[i] = str.charCodeAt(i) - return uint.buffer - } - - private static decode(socket: ClusterWS, message: TSocketMessage): any { - switch (message['#'][0]) { - case 'e': return socket.events.emit(message['#'][1], message['#'][2]) - case 'p': return socket.channels[message['#'][1]] ? socket.channels[message['#'][1]].onMessage(message['#'][2]) : '' - case 's': - switch (message['#'][1]) { - case 'c': - socket.pingInterval = setInterval((): void | string => socket.missedPing++ > 2 ? socket.disconnect(4001, 'Did not get pings') : '', message['#'][2].ping) - socket.useBinary = message['#'][2].binary - socket.events.emit('connect') - default: break - } - default: break - } - } +export default class ClusterWS { + public options: Options + public websocket: WebSocket + public channels: CustomObject = {} - private static encode(event: string, data: any, type: string): any { - switch (type) { - case 'ping': return event - case 'emit': return JSON.stringify({ '#': ['e', event, data] }) - case 'publish': return JSON.stringify({ '#': ['p', event, data] }) - case 'system': switch (event) { - case 'subscribe': return JSON.stringify({ '#': ['s', 's', data] }) - case 'unsubscribe': return JSON.stringify({ '#': ['s', 'u', data] }) - case 'configuration': return JSON.stringify({ '#': ['s', 'c', data] }) - default: break - } - default: break - } - } + public events: EventEmitter = new EventEmitter() + public missedPing: number = 0 + public useBinary: boolean = false + public pingInterval: any - public options: IOptions - public channels: IObject = {} - public websocket: WebSocket - private missedPing: number = 0 - private events: EventEmitter = new EventEmitter() - private useBinary: boolean = false - private pingInterval: number private reconnection: Reconnection - constructor(configuration: IUserOptions) { - if (!configuration.url || typeof configuration.url !== 'string') + constructor(configurations: Configurations) { + if (!configurations.url) return logError('Url must be provided and it must be string') - if (!configuration.port || typeof configuration.port !== 'number') - return logError('Port must be provided and it must be number') this.options = { - url: configuration.url, - port: configuration.port, - autoReconnect: configuration.autoReconnect || false, - reconnectionIntervalMin: configuration.reconnectionIntervalMin || 1000, - reconnectionIntervalMax: configuration.reconnectionIntervalMax || 5000, - reconnectionAttempts: configuration.reconnectionAttempts || 0, - secure: configuration.secure || false + url: configurations.url, + autoReconnect: configurations.autoReconnect || false, + reconnectionAttempts: configurations.reconnectionAttempts || 0, + reconnectionIntervalMin: configurations.reconnectionIntervalMin || 1000, + reconnectionIntervalMax: configurations.reconnectionIntervalMax || 5000 } if (this.options.reconnectionIntervalMin > this.options.reconnectionIntervalMax) @@ -78,41 +39,45 @@ export class ClusterWS { public create(): void { const Socket: any = window.MozWebSocket || window.WebSocket - const protocol: string = this.options.secure ? 'wss://' : 'ws://' - this.websocket = new Socket(protocol + this.options.url + ':' + this.options.port) - this.websocket.binaryType = 'arraybuffer' + this.websocket = new Socket(this.options.url) + this.websocket.binaryType = 'arraybuffer' this.websocket.onopen = (): void => this.reconnection.isConnected() - this.websocket.onerror = (err: TSocketMessage): void => this.events.emit('error', err.message) - this.websocket.onmessage = (message: TSocketMessage): void => { - let data: string = message.data - if (this.useBinary && typeof data !== 'string') data = String.fromCharCode.apply(null, new Uint8Array(data)) + this.websocket.onerror = (err: any): void => this.events.emit('error', err.message) + this.websocket.onmessage = (message: any): void => { + let data: string = typeof message.data !== 'string' ? + String.fromCharCode.apply(null, new Uint8Array(message.data)) : message.data + if (data === '#0') { this.missedPing = 0 return this.send('#1', null, 'ping') - } - try { data = JSON.parse(data) } catch (e) { return logError(e) } - ClusterWS.decode(this, data) + } else try { + data = JSON.parse(data) + } catch (e) { return logError(e) } + + decode(this, data) } this.websocket.onclose = (event: CloseEvent): void => { this.missedPing = 0 clearInterval(this.pingInterval) this.events.emit('disconnect', event.code, event.reason) - if (this.reconnection.inReconnectionState) return if (this.options.autoReconnect && event.code !== 1000) return this.reconnection.reconnect() this.events.removeAllEvents() - for (const key in this) this.hasOwnProperty(key) ? delete this[key] : '' + for (const key in this) + this[key] ? this[key] = null : null } } - public on(event: string, listener: TListener): void { + public on(event: string, listener: Listener): void { this.events.on(event, listener) } public send(event: string, data: any, type: string = 'emit'): void { - this.websocket.send(this.useBinary ? ClusterWS.buffer(ClusterWS.encode(event, data, type)) : ClusterWS.encode(event, data, type)) + this.websocket.send(this.useBinary ? + buffer(encode(event, data, type)) : + encode(event, data, type)) } public disconnect(code?: number, msg?: any): void { @@ -123,8 +88,9 @@ export class ClusterWS { return this.websocket.readyState } - public subscribe(channel: string): void { - return this.channels[channel] ? this.channels[channel] : this.channels[channel] = new Channel(this, channel) + public subscribe(channelName: string): Channel { + return this.channels[channelName] ? this.channels[channelName] : + this.channels[channelName] = new Channel(this, channelName) } public getChannelByName(channelName: string): Channel { diff --git a/src/modules/channel.ts b/src/modules/channel.ts deleted file mode 100644 index d3c6069..0000000 --- a/src/modules/channel.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ClusterWS } from '../index' -import { TListener, logError } from '../utils/utils' - -export class Channel { - private listener: TListener - - constructor(private socket: ClusterWS, private channel: string) { - this.subscribe() - } - - public watch(listener: TListener): Channel { - if ({}.toString.call(listener) !== '[object Function]') return logError('Listener must be a function') - this.listener = listener - return this - } - - public publish(data: any): Channel { - this.socket.send(this.channel, data, 'publish') - return this - } - - public unsubscribe(): void { - this.socket.send('unsubscribe', this.channel, 'system') - this.socket.channels[this.channel] = null - } - - public onMessage(data: any): void { - if (this.listener) this.listener.call(null, data) - } - - public subscribe(): void { - this.socket.send('subscribe', this.channel, 'system') - } -} \ No newline at end of file diff --git a/src/modules/channel/channel.ts b/src/modules/channel/channel.ts new file mode 100644 index 0000000..c5b37db --- /dev/null +++ b/src/modules/channel/channel.ts @@ -0,0 +1,35 @@ +import ClusterWS from '../../index' +import { Listener, logError } from '../../utils/utils' + +export class Channel { + private listener: Listener + + constructor(private socket: ClusterWS, public name: string) { + this.subscribe() + } + + public watch(listener: Listener): Channel { + if ({}.toString.call(listener) !== '[object Function]') + return logError('Listener must be a function') + this.listener = listener + return this + } + + public publish(data: any): Channel { + this.socket.send(this.name, data, 'publish') + return this + } + + public unsubscribe(): void { + this.socket.send('unsubscribe', this.name, 'system') + this.socket.channels[this.name] = null + } + + public onMessage(data: any): void { + this.listener && this.listener.call(null, data) + } + + public subscribe(): void { + this.socket.send('subscribe', this.name, 'system') + } +} \ No newline at end of file diff --git a/src/modules/emitter/emitter.ts b/src/modules/emitter/emitter.ts new file mode 100644 index 0000000..cb18f25 --- /dev/null +++ b/src/modules/emitter/emitter.ts @@ -0,0 +1,19 @@ +import { Listener, logError } from '../../utils/utils' + +export class EventEmitter { + private events: any = {} + + public on(event: string, listener: Listener): void { + if ({}.toString.call(listener) !== '[object Function]') + return logError('Listener must be a function') + this.events[event] = listener + } + + public emit(event: string, ...args: any[]): void { + if (this.events[event]) this.events[event].call(null, ...args) + } + + public removeAllEvents(): void { + this.events = {} + } +} \ No newline at end of file diff --git a/src/modules/parser/parser.ts b/src/modules/parser/parser.ts new file mode 100644 index 0000000..da854dc --- /dev/null +++ b/src/modules/parser/parser.ts @@ -0,0 +1,40 @@ +import ClusterWS from '../../index' + +export function buffer(str: string): ByteString { + const length: number = str.length + const uint: any = new Uint8Array(length) + for (let i: number = 0; i < length; i++) uint[i] = str.charCodeAt(i) + return uint.buffer +} + +export function decode(socket: ClusterWS, message: any): any { + switch (message['#'][0]) { + case 'e': return socket.events.emit(message['#'][1], message['#'][2]) + case 'p': socket.channels[message['#'][1]] && socket.channels[message['#'][1]].onMessage(message['#'][2]) + case 's': + switch (message['#'][1]) { + case 'c': + socket.pingInterval = setInterval((): void => + socket.missedPing++ > 2 && socket.disconnect(4001, 'Did not get pings'), message['#'][2].ping) + socket.useBinary = message['#'][2].binary + socket.events.emit('connect') + default: break + } + default: break + } +} + +export function encode(event: string, data: any, type: string): any { + switch (type) { + case 'ping': return event + case 'emit': return JSON.stringify({ '#': ['e', event, data] }) + case 'publish': return JSON.stringify({ '#': ['p', event, data] }) + case 'system': switch (event) { + case 'subscribe': return JSON.stringify({ '#': ['s', 's', data] }) + case 'unsubscribe': return JSON.stringify({ '#': ['s', 'u', data] }) + case 'configuration': return JSON.stringify({ '#': ['s', 'c', data] }) + default: break + } + default: break + } +} \ No newline at end of file diff --git a/src/modules/reconnection.ts b/src/modules/reconnection/reconnection.ts similarity index 74% rename from src/modules/reconnection.ts rename to src/modules/reconnection/reconnection.ts index 950a3b3..7e63b79 100644 --- a/src/modules/reconnection.ts +++ b/src/modules/reconnection/reconnection.ts @@ -1,11 +1,11 @@ -import { ClusterWS } from '../index' +import ClusterWS from '../../index' export class Reconnection { public inReconnectionState: boolean = false private reconnectionAttempted: number = 0 private autoReconnect: boolean - private interval: NodeJS.Timer - private timer: NodeJS.Timer + private interval: any + private timer: any constructor(public socket: ClusterWS) { this.autoReconnect = this.socket.options.autoReconnect @@ -18,10 +18,12 @@ export class Reconnection { this.inReconnectionState = false this.reconnectionAttempted = 0 - for (const key in this.socket.channels) this.socket.channels.hasOwnProperty(key) ? this.socket.channels[key].subscribe() : '' + for (const key in this.socket.channels) + this.socket.channels[key] && this.socket.channels[key].subscribe() } public reconnect(): void { + if (this.inReconnectionState) return this.inReconnectionState = true this.interval = setInterval((): void => { if (this.socket.getState() === this.socket.websocket.CLOSED) { @@ -32,7 +34,8 @@ export class Reconnection { this.inReconnectionState = false } clearTimeout(this.timer) - this.timer = setTimeout((): void => this.socket.create(), Math.floor(Math.random() * (this.socket.options.reconnectionIntervalMax - this.socket.options.reconnectionIntervalMin + 1))) + this.timer = setTimeout((): void => this.socket.create(), + Math.floor(Math.random() * (this.socket.options.reconnectionIntervalMax - this.socket.options.reconnectionIntervalMin + 1))) } }, this.socket.options.reconnectionIntervalMin) } diff --git a/src/utils/emitter.ts b/src/utils/emitter.ts deleted file mode 100644 index 21b29ff..0000000 --- a/src/utils/emitter.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TListener, logError } from './utils' - -export class EventEmitter { - private events: any = {} - - public on(event: string, listener: TListener): void { - if ({}.toString.call(listener) !== '[object Function]') return logError('Listener must be a function') - if (!this.events[event]) this.events[event] = listener - } - - public emit(event: string, ...args: any[]): void { - if (this.events[event]) this.events[event].call(null, ...args) - } - - public removeAllEvents(): void { - this.events = {} - } -} \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 577d49d..567b73a 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,28 +1,23 @@ -export type TListener = (...args: any[]) => void -export type TSocketMessage = any +export type Listener = (...args: any[]) => void -export interface IObject { +export interface CustomObject { [propName: string]: any } -export interface IUserOptions { +export interface Options { url: string - port: number - autoReconnect?: boolean - reconnectionIntervalMin?: number - reconnectionIntervalMax?: number - reconnectionAttempts?: number - secure?: boolean -} - -export interface IOptions { - url: string - port: number autoReconnect: boolean + reconnectionAttempts: number reconnectionIntervalMin: number reconnectionIntervalMax: number - reconnectionAttempts: number - secure: boolean +} + +export interface Configurations { + url: string + autoReconnect?: boolean + reconnectionAttempts?: number + reconnectionIntervalMin?: number + reconnectionIntervalMax?: number } export function logError(data: T): any {