| language: node_js | ||
| node_js: | ||
| - "0.10" | ||
| - "0.11" | ||
| addons: | ||
| postgresql: "9.3" | ||
| before_script: | ||
| - psql -c 'create database w3gram_test;' -U postgres | ||
| env: DATABASE_URL=postgresql://postgres@localhost/w3gram_test |
| PingPong = W3gram._.PingPong | ||
| describe 'W3gram._.PingPong', -> | ||
| beforeEach -> | ||
| @pingPong = new PingPong( | ||
| silenceTimeoutMs: 10, pingSlackMs: 20, rttMs: 20) | ||
| describe 'when pings are returned immediately', -> | ||
| beforeEach -> | ||
| @pingCount = 0 | ||
| @pingPong.onPing = (data) => | ||
| @pingCount += 1 | ||
| setImmediate => | ||
| @pingPong.receivedPong data | ||
| it 'issues 8-10 ping requests in 100ms', (done) -> | ||
| @pingPong.onPingTimeout = -> | ||
| expect('Should not timeout').to.equal false | ||
| done() | ||
| onTimeout = => | ||
| @pingPong.disconnected() | ||
| expect(@pingCount).to.be.at.least 8 | ||
| expect(@pingCount).to.be.at.most 10 | ||
| done() | ||
| @pingPong.startedConnecting() | ||
| setTimeout onTimeout, 100 | ||
| null | ||
| describe 'when pings are not returned', -> | ||
| beforeEach -> | ||
| @pingCount = 0 | ||
| @pingPong.onPing = (data) => | ||
| @pingCount += 1 | ||
| it 'issues 1 ping and disconnects ', (done) -> | ||
| @pingPong.onPingTimeout = => | ||
| expect(@pingCount).to.equal 1 | ||
| done() | ||
| @pingPong.startedConnecting() | ||
| null |
| WsClient = W3gram._.WsClient | ||
| describe 'W3gram._.WsClient', -> | ||
| beforeEach -> | ||
| @sandbox = sinon.sandbox.create() | ||
| W3gram._.jsonRequest('POST', "#{testXhrServer}/reset") | ||
| .then (json) => | ||
| @serverUrl = json.url | ||
| @serverMak = json.mak | ||
| appOptions = name: 'App#register Test', origin: '*' | ||
| @server = new W3gram._.Server @serverUrl | ||
| @server.createApp @serverMak, appOptions | ||
| .then (app) => | ||
| @app = app | ||
| @deviceId = 'tablet-device-id' | ||
| @app.token @deviceId | ||
| .then (token) => | ||
| @token = token | ||
| @app.secret = null | ||
| @app.register @deviceId, @token | ||
| .then (registration) => | ||
| @registration = registration | ||
| @registration.route() | ||
| .then (wsUrl) => | ||
| @wsUrl = wsUrl | ||
| afterEach -> | ||
| @sandbox.restore() | ||
| describe 'with hyper-ping settings', -> | ||
| beforeEach -> | ||
| @wsClient = new WsClient @wsUrl, | ||
| silenceTimeoutMs: 10, pingSlackMs: 20, rttMs: 20 | ||
| it 'sends 8-10 pings in 100ms', (done) -> | ||
| @wsClient.connected.then => | ||
| pingSpy = @sandbox.spy @wsClient, 'sendPing' | ||
| closeSpy = @sandbox.spy @wsClient, 'close' | ||
| onTimeout = => | ||
| expect(closeSpy.callCount).to.equal 0 | ||
| expect(pingSpy.callCount).to.be.at.least 8 | ||
| expect(pingSpy.callCount).to.be.at.most 10 | ||
| done() | ||
| setTimeout onTimeout, 100 | ||
| null |
+8
-4
@@ -449,3 +449,3 @@ // Generated by CoffeeScript 1.8.0 | ||
| options || (options = {}); | ||
| this._silenceTimeout = options.pingTimeoutMs || 5000; | ||
| this._silenceTimeout = options.silenceTimeoutMs || 5000; | ||
| this._roundTrip = options.rttMs || 20000; | ||
@@ -469,3 +469,3 @@ this._pingSlack = options.pingSlackMs || 5000; | ||
| if (data.nonce === this._pongNonce) { | ||
| roundTrip = Date.now() - message.ts; | ||
| roundTrip = Date.now() - data.ts; | ||
| this._roundTrip = this._roundTrip * 0.2 + roundTrip * 0.8; | ||
@@ -502,3 +502,2 @@ } | ||
| this._silenceTimer = null; | ||
| return; | ||
| if (this._pingTimer !== null) { | ||
@@ -788,3 +787,7 @@ return; | ||
| this._pingPong = new W3gram._.PingPong(options); | ||
| this._pingPong.onPing = this.sendPing.bind(this); | ||
| this._pingPong.onPing = (function(_this) { | ||
| return function(data) { | ||
| return _this.sendPing(data); | ||
| }; | ||
| })(this); | ||
| this._pingPong.onPingTimeout = (function(_this) { | ||
@@ -797,2 +800,3 @@ return function() { | ||
| this._resolveConnected = null; | ||
| this.connected = null; | ||
| this.closed = new W3gram._.Promise((function(_this) { | ||
@@ -799,0 +803,0 @@ return function(resolve) { |
@@ -1,2 +0,2 @@ | ||
| (function(){var e,t,n,i,r,o;if(e=function(){function e(){}return e.prototype.setupPushManager=function(e){return this.pushRegistrationManager=new t.PushRegistrationManager(e)},e.prototype.pushRegistrationManager=null,e}(),t=new e,t.ServiceWorkerRegistration=e,t._={},"undefined"!=typeof global&&"undefined"!=typeof module&&"exports"in module)r=global,o=module.require.bind(module),module.exports=t;else if("undefined"!=typeof window&&"undefined"!=typeof navigator)r=window,o=null,window.W3gram=t;else{if("undefined"==typeof self||"undefined"==typeof navigator)throw new Error("w3gram.js loaded in an unsupported JavaScript environment.");n=self,i=self.importScripts.bind(self),self.W3gram=t}t._.global=r,t._.require=o,t._.App=function(){function e(e){var t;if(!(this.apiKey=e.key))throw new Error("The key option must be a W3gram API key");if(this.secret=e.secret||null,this.name=e.name||null,this.origin=e.origin||null,!(t=e.server))throw new Error("The server option must be a W3gram server URL");"/"===t.substring(t.length-1)&&(t=t.substring(0,t.length-1)),this.serverUrl=t,this._registerUrl=""+t+"/register"}return e.prototype.toJSON=function(){return{name:this.name,origin:this.origin,key:this.apiKey,secret:this.secret,server:this.serverUrl}},e.prototype.register=function(e,n){var i;return i={app:this.apiKey,device:e,token:n},t._.jsonRequest("POST",this._registerUrl,i).then(function(i){return function(r){return r.server=i.serverUrl,r.app=i.apiKey,r.device=e,r.token=n,new t._.DeviceRegistration(r)}}(this))},e.prototype.token=function(e){return t._.App.checkDeviceId(e).then(function(n){return function(){return n.checkCanCreateTokens().then(function(){return t._.hmac(n.secret,"device-id|"+e)})}}(this))},e.checkDeviceId=function(e){return t._.App.isValidDeviceId(e)?t._.Promise.resolve(!0):t._.Promise.reject(new t._.DOMException(t._.DOMException.SyntaxError,"Invalid device ID"))},e.isValidDeviceId=function(e){return e.length<=64?/^[A-Za-z0-9_\-]+$/.test(e):!1},e.prototype.checkCanCreateTokens=function(){return this.secret?t._.Promise.resolve(!0):t._.Promise.reject(new t._.DOMException(t._.DOMException.InvalidStateError,"Cannot generate tokens without the app secret"))},e}(),t._.DeviceRegistration=function(){function e(e){var t;this.pushUrl=e.push,this.receiverId=e.receiver,this.apiKey=e.app,this.deviceId=e.device,this.token=e.token,t=e.server,"/"===t.substring(t.length-1)&&(t=t.substring(0,t.length-1)),this.serverUrl=t,this._routeUrl=""+t+"/route"}return e.prototype.push=function(e){var n;return n={receiver:this.receiverId,message:e},t._.jsonRequest("POST",this.pushUrl,n).then(function(){return!0})},e.prototype.route=function(){var e;return e={app:this.apiKey,device:this.deviceId,receiver:this.receiverId,token:this.token},t._.jsonRequest("POST",this._routeUrl,e).then(function(e){return e.listen})},e.prototype.toJSON=function(){return{push:this.pushUrl,receiver:this.receiverId,server:this.serverUrl,app:this.apiKey,device:this.deviceId,token:this.token}},e}(),t._.DOMException=function(){function e(e,t){this.name=e,this.message=t}return e.name=null,e.NotFoundError="NotFoundError",e.InvalidStateError="InvalidStateError",e.SyntaxError="SyntaxError",e.NotFoundError="NotFoundError",e.SecurityError="SecurityError",e.NetworkError="NetworkError",e.AbortError="AbortError",e.ABORT_ERR=20,e}(),function(){var e,n,i,r,o,s,c,u,h;if(t._.hmac=function(n,r){return new t._.Promise(function(t){return t(e(i(u(n),u(r),n.length,r.length)))})},t._.require)try{n=t._.require("crypto"),n.createHmac&&(t._.hmac=function(e,i){return new t._.Promise(function(t){return t(n.createHmac("sha256",e).update(i).digest("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/\=/g,""))})})}catch(a){r=a}return i=function(e,t,n,i){var r,s,c,u;return e.length>16&&(e=o(e,n)),c=function(){var t,n;for(n=[],s=t=0;16>t;s=++t)n.push(909522486^e[s]);return n}(),u=function(){var t,n;for(n=[],s=t=0;16>t;s=++t)n.push(1549556828^e[s]);return n}(),r=o(c.concat(t),64+i),o(u.concat(r),96)},o=function(e,t){var n,i,r,o,u,h,a,l,p,_,f,d,g,v,m,w,y,P,k,T,E,S,b,N,C,M,R,O,x,I,D,U;for(e[t>>2]|=1<<31-((3&t)<<3),e[(t+8>>6<<4)+15]=t<<3,x=Array(80),n=s[0],r=s[1],u=s[2],l=s[3],_=s[4],d=s[5],v=s[6],T=s[7],S=0,N=e.length;N>S;){for(i=n,o=r,h=u,p=l,f=_,g=d,m=v,E=T,b=U=0;64>U;b=++U)16>b?O=x[b]=0|e[S+b<<2>>2]:(y=0|x[b-15<<2>>2],w=(y<<25|y>>>7)^(y<<14|y>>>18)^y>>>3,k=0|x[b-2<<2>>2],P=(k<<15|k>>>17)^(k<<13|k>>>19)^k>>>10,O=x[b]=(w+(0|x[b-7<<2>>2])|0)+(P+(0|x[b-16<<2>>2])|0)|0),a=_&d^~_&v,C=n&r^n&u^r&u,M=(n<<30|n>>>2)^(n<<19|n>>>13)^(n<<10|n>>>22),R=(_<<26|_>>>6)^(_<<21|_>>>11)^(_<<7|_>>>25),I=((T+R|0)+(a+O|0)|0)+(0|c[b<<2>>2])|0,D=M+C|0,T=v,v=d,d=_,_=l+I|0,l=u,u=r,r=n,n=I+D|0;n=i+n|0,r=o+r|0,u=h+u|0,l=p+l|0,_=f+_|0,d=g+d|0,v=m+v|0,T=E+T|0,S+=16}return[n,r,u,l,_,d,v,T]},s=[],c=[],function(){var e,t,n,i,r,o,u;for(t=function(e){return 4294967296*(e-Math.floor(e))|0},r=2,u=[],n=o=0;64>o;n=++o){for(;;){for(i=!0,e=2;r>=e*e;){if(r%e===0){i=!1;break}e+=1}if(i)break;r+=1}8>n&&(s[n]=t(Math.pow(r,.5))),c[n]=t(Math.pow(r,1/3)),u.push(r+=1)}return u}(),e=function(e){var t,n,i,r,o;for(r="",t=0,i=4*e.length;i>t;)n=t,o=(e[n>>2]>>(3-(3&n)<<3)&255)<<16,n+=1,o|=(e[n>>2]>>(3-(3&n)<<3)&255)<<8,n+=1,o|=e[n>>2]>>(3-(3&n)<<3)&255,r+=h[o>>18&63],r+=h[o>>12&63],t+=1,i>t&&(r+=h[o>>6&63]),t+=1,i>t&&(r+=h[63&o]),t+=1;return r},h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",u=function(e){var t,n,i,r;for(t=[],n=i=0,r=e.length;r>=0?r>i:i>r;n=r>=0?++i:--i)t[n>>2]|=(255&e.charCodeAt(n))<<(3-(3&n)<<3);return t}}(),t._.jsonRequest=function(e,n,i){return new t._.Promise(function(r,o){var s;s=new t._.XMLHttpRequest,s.open(e,n,!0),s.onreadystatechange=function(){var e,n,i,c,u,h,a,l;if(4!==s.readyState)return!0;if(e=s.getResponseHeader("Content-Type"),e&&(a=e.indexOf(";"),-1!==a&&(e=e.substring(0,a))),u=null,h=null,"application/json"===e)try{u=JSON.parse(s.responseText||s.response)}catch(p){h=p}return l=s.status,l>=200&&300>l?h?void o(h):void r(u||{}):(0===l?(c=t._.DOMException.NetworkError,i="Could not reach Push Notification Server"):(c=t._.DOMException.SecurityError,i=u&&u.error?"Push Notification Server error: "+u.error:"Push Notification Server error: HTTP "+l),n=new t._.DOMException(c,i),0!==l&&(n.httpCode=l),void o(n))},i?(s.setRequestHeader("Content-Type","application/json; charset=utf-8"),s.send(JSON.stringify(i))):s.send()})},t._.PingPong=function(){function e(e){e||(e={}),this._silenceTimeout=e.pingTimeoutMs||5e3,this._roundTrip=e.rttMs||2e4,this._pingSlack=e.pingSlackMs||5e3,this._nextPongNonce=0,this._pongNonce=null,this._silenceTimer=null,this._pingTimer=null,this._silenceTimerHandler=this._onSilenceTimeout.bind(this),this._pingTimerHandler=this._onPingTimeout.bind(this)}return e.prototype.startedConnecting=function(){this._resetSilenceTimer()},e.prototype.receivedPong=function(e){var t;e.nonce===this._pongNonce&&(t=Date.now()-message.ts,this._roundTrip=.2*this._roundTrip+.8*t),this._resetSilenceTimer()},e.prototype.receivedMessage=function(){this._resetSilenceTimer()},e.prototype.disconnected=function(){this._disableTimers()},e.prototype.onPing=function(){throw new Error("onPing not assigned")},e.prototype.onPingTimeout=function(){throw new Error("onPingTimeout not assigned")},e.prototype._resetSilenceTimer=function(){this._disableTimers(),this._silenceTimer=setTimeout(this._silenceTimerHandler,this._silenceTimeout)},e.prototype._onSilenceTimeout=function(){null!==this._silenceTimer&&(this._silenceTimer=null)},e.prototype._sendPing=function(){this._pongNonce=this._nextPongNonce,this._nextPongNonce=this._nextPongNouce+1|0,this.onPing({nonce:this._pongNonce,ts:Date.now()}),this._pingTimer=setTimeout(this._pingTimerHandler,this._roundTrip+this._pingSlack)},e.prototype._onPingTimeout=function(){null!==this._pingTimer&&(this._pingTimer=null,this._pongNonce=null,this._disableTimers(),this.onPingTimeout())},e.prototype._disableTimers=function(){null!==this._pingTimer&&(clearTimeout(this._pingTimer),this._pingTimer=null,this._pongNonce=null),null!==this._silenceTimer&&(clearTimeout(this._silenceTimer),this._silenceTimer=null)},e}(),t._.Promise="undefined"==typeof Promise?o("es6-promise-polyfill").Promise:Promise,t._.WebSocket="undefined"==typeof WebSocket?o("ws"):WebSocket,t._.XMLHttpRequest="undefined"==typeof XMLHttpRequest?o("xhr2"):XMLHttpRequest,t.PushEvent=function(){function e(e,t){this.name="PushEvent",this.target=e,this.data=t}return e}(),t._.createPushEvent=function(e,n){return new t.PushEvent(e,n)},t.PushRegistration=function(){function e(e){if(this._deviceRegistration=e,!(this._deviceRegistration instanceof t._.DeviceRegistration))throw new Error("Illegal constructor");this.registrationId=e.receiverId,this.endpoint=e.pushUrl,this._wsClient=null,this._canceled=!1,this._initialBackoff=1e3,this._nextBackoff=null,this._resolveConnected=null,this._connected=new t._.Promise(function(e){return function(t){return e._resolveConnected=t}}(this)),this._connect()}return e.prototype.registrationId=null,e.prototype.endpoint=null,e.prototype.onpush=null,e.prototype.onpushregistrationlost=null,e.prototype._cancel=function(){this._canceled=!0,this._wsClient&&this._wsClient.close(1e3,"Receiver unregistered")},e.prototype._connect=function(){return this._wsClient&&new t._.Promise.resolve(!0),this._canceled&&new t._.Promise.reject(new Error("Unregistered")),this._nextBackoff=this._initialBackoff,this._connectOnce()},e.prototype._connectOnce=function(){this._deviceRegistration.route().then(function(e){return function(n){return e._wsClient=new t._.WsClient(n),e._wsClient.onNotification=e._onNotification.bind(e),e._wsClient.closed.then(e._onWsClosed.bind(e)),e._wsClient.connected}}(this)).then(function(e){return function(){return e._nextBackoff=e._initialBackoff,e._resolveConnected(!0),!0}}(this))["catch"](function(e){return function(){var n;e._wsClient.close(4e3,"Network error"),n=e._nextBackoff,e._nextBackoff*=2,new t._.Promise(function(t){var i;return i=function(){return t(e._connect())},setTimeout(i,n)})}}(this))},e.prototype._onNotification=function(e){this._canceled||this.onpush&&this.onpush(t._.createPushEvent(this,JSON.stringify(e)))},e.prototype._onWsClosed=function(){this._wsClient=null,this._connect()},e}(),t.PushRegistrationManager=function(){function e(e){if(!e.server)throw new Error("The server option must be a W3gram server URL");if(!e.key)throw new Error("The key option must be a W3gram API key");if(!(this._deviceId=e.device))throw new Error("The device option must be a device ID");if(!(this._token=e.token))throw new Error("The token option must be the device ID's token");this._registration=null,this._app=new t._.App({server:e.server,key:e.key})}return e.prototype.register=function(){return null!==this._registration?t._.Promise.resolve(this._registration):this._app.register(this._deviceId,this._token).then(function(e){return function(n){return e._registration||(e._registration=new t.PushRegistration(n)),e._registration._connected.then(function(){return e._registration})}}(this))},e.prototype.unregister=function(){var e,n;return this._registration?(n=this._registration,this._registration=null,n._cancel(),void t._.Promise.resolve(n)):(e=new t._.DOMException(t._.DOMException.NotFoundError,"No compatible W3gram registration found on this device"),t._.Promise.reject(e))},e.prototype.getRegistration=function(){var e;return this._registration?void t._.Promise.resolve(this._registration):(e=new t._.DOMException(t._.DOMException.NotFoundError,"No compatible W3gram registration found on this device"),t._.Promise.reject(e))},e.prototype.hasPermission=function(){return t._.Promise.resolve("granted")},e}(),t._.Server=function(){function e(e){"/"===e.substring(e.length-1)&&(e=e.substring(0,e.length-1)),this.serverUrl=e,this._makUrl=""+e+"/mak",this._appsUrl=""+e+"/apps"}return e.prototype.getMak=function(){return t._.jsonRequest("GET",""+this.serverUrl+"/mak").then(function(e){return e.mak})},e.prototype.createApp=function(e,n){var i;return i={name:n.name,origin:n.origin||"*"},t._.jsonRequest("POST",this._appsUrl,{mak:e,app:i}).then(function(e){return function(n){return n.server=e.serverUrl,new t._.App(n)}}(this))},e}(),t._.WsClient=function(){function e(e,n){this._wsUrl=e,this._ws=null,this._pingPong=new t._.PingPong(n),this._pingPong.onPing=this.sendPing.bind(this),this._pingPong.onPingTimeout=function(e){return function(){return e.close(4e3,"Ping timeout")}}(this),this._resolveClosed=null,this._resolveConnected=null,this.closed=new t._.Promise(function(e){return function(n){e._resolveClosed=n,e.connected=new t._.Promise(function(t){e._resolveConnected=t,e._connect()})}}(this))}return e.prototype.close=function(e,t){null!==this._ws&&(this._ws.onclose=null,this._ws.onerror=null,this._ws.onmessage=null,this._ws.onopen=null,e||(e=1e3),this._ws.close(e,t),this._ws=null,this._pingPong.disconnected(),this._resolveClosed({code:e,reason:t}))},e.prototype.sendPing=function(e){this._ws&&this._ws.send(JSON.stringify({type:"ping",data:e}))},e.prototype.connected=null,e.prototype.closed=null,e.prototype.onNotification=function(){},e.prototype._connect=function(){null===this._ws&&(this._ws=new t._.WebSocket(this._wsUrl),this._ws.onclose=this._onSocketClose.bind(this),this._ws.onerror=this._onSocketError.bind(this),this._ws.onmessage=this._onSocketMessage.bind(this),this._ws.onopen=this._onSocketOpen.bind(this),this._pingPong.startedConnecting())},e.prototype._onMessage=function(e){var t;return t=e.type,"pong"===t?void this._pingPong.receivedPong(e.data):(this._pingPong.receivedMessage(),"note"===t?void this.onNotification(e.data):"hi"===t?void this._resolveConnected(!0):void this._close(4400,"Invalid message type: "+t))},e.prototype._onSocketClose=function(e){null!==this._ws&&this.close(e.code,e.reason)},e.prototype._onSocketError=function(){null!==this._ws&&this.close(4e3,event.message)},e.prototype._onSocketMessage=function(e){var t,n,i;if(this._ws){i=e.data,n=null;try{n=JSON.parse(i)}catch(r){t=r,this.close(4400,"Invalid JSON message")}this._onMessage(n)}},e.prototype._onSocketOpen=function(){},e}()}).call(this); | ||
| (function(){var e,t,n,i,r,o;if(e=function(){function e(){}return e.prototype.setupPushManager=function(e){return this.pushRegistrationManager=new t.PushRegistrationManager(e)},e.prototype.pushRegistrationManager=null,e}(),t=new e,t.ServiceWorkerRegistration=e,t._={},"undefined"!=typeof global&&"undefined"!=typeof module&&"exports"in module)r=global,o=module.require.bind(module),module.exports=t;else if("undefined"!=typeof window&&"undefined"!=typeof navigator)r=window,o=null,window.W3gram=t;else{if("undefined"==typeof self||"undefined"==typeof navigator)throw new Error("w3gram.js loaded in an unsupported JavaScript environment.");n=self,i=self.importScripts.bind(self),self.W3gram=t}t._.global=r,t._.require=o,t._.App=function(){function e(e){var t;if(!(this.apiKey=e.key))throw new Error("The key option must be a W3gram API key");if(this.secret=e.secret||null,this.name=e.name||null,this.origin=e.origin||null,!(t=e.server))throw new Error("The server option must be a W3gram server URL");"/"===t.substring(t.length-1)&&(t=t.substring(0,t.length-1)),this.serverUrl=t,this._registerUrl=""+t+"/register"}return e.prototype.toJSON=function(){return{name:this.name,origin:this.origin,key:this.apiKey,secret:this.secret,server:this.serverUrl}},e.prototype.register=function(e,n){var i;return i={app:this.apiKey,device:e,token:n},t._.jsonRequest("POST",this._registerUrl,i).then(function(i){return function(r){return r.server=i.serverUrl,r.app=i.apiKey,r.device=e,r.token=n,new t._.DeviceRegistration(r)}}(this))},e.prototype.token=function(e){return t._.App.checkDeviceId(e).then(function(n){return function(){return n.checkCanCreateTokens().then(function(){return t._.hmac(n.secret,"device-id|"+e)})}}(this))},e.checkDeviceId=function(e){return t._.App.isValidDeviceId(e)?t._.Promise.resolve(!0):t._.Promise.reject(new t._.DOMException(t._.DOMException.SyntaxError,"Invalid device ID"))},e.isValidDeviceId=function(e){return e.length<=64?/^[A-Za-z0-9_\-]+$/.test(e):!1},e.prototype.checkCanCreateTokens=function(){return this.secret?t._.Promise.resolve(!0):t._.Promise.reject(new t._.DOMException(t._.DOMException.InvalidStateError,"Cannot generate tokens without the app secret"))},e}(),t._.DeviceRegistration=function(){function e(e){var t;this.pushUrl=e.push,this.receiverId=e.receiver,this.apiKey=e.app,this.deviceId=e.device,this.token=e.token,t=e.server,"/"===t.substring(t.length-1)&&(t=t.substring(0,t.length-1)),this.serverUrl=t,this._routeUrl=""+t+"/route"}return e.prototype.push=function(e){var n;return n={receiver:this.receiverId,message:e},t._.jsonRequest("POST",this.pushUrl,n).then(function(){return!0})},e.prototype.route=function(){var e;return e={app:this.apiKey,device:this.deviceId,receiver:this.receiverId,token:this.token},t._.jsonRequest("POST",this._routeUrl,e).then(function(e){return e.listen})},e.prototype.toJSON=function(){return{push:this.pushUrl,receiver:this.receiverId,server:this.serverUrl,app:this.apiKey,device:this.deviceId,token:this.token}},e}(),t._.DOMException=function(){function e(e,t){this.name=e,this.message=t}return e.name=null,e.NotFoundError="NotFoundError",e.InvalidStateError="InvalidStateError",e.SyntaxError="SyntaxError",e.NotFoundError="NotFoundError",e.SecurityError="SecurityError",e.NetworkError="NetworkError",e.AbortError="AbortError",e.ABORT_ERR=20,e}(),function(){var e,n,i,r,o,s,c,u,h;if(t._.hmac=function(n,r){return new t._.Promise(function(t){return t(e(i(u(n),u(r),n.length,r.length)))})},t._.require)try{n=t._.require("crypto"),n.createHmac&&(t._.hmac=function(e,i){return new t._.Promise(function(t){return t(n.createHmac("sha256",e).update(i).digest("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/\=/g,""))})})}catch(a){r=a}return i=function(e,t,n,i){var r,s,c,u;return e.length>16&&(e=o(e,n)),c=function(){var t,n;for(n=[],s=t=0;16>t;s=++t)n.push(909522486^e[s]);return n}(),u=function(){var t,n;for(n=[],s=t=0;16>t;s=++t)n.push(1549556828^e[s]);return n}(),r=o(c.concat(t),64+i),o(u.concat(r),96)},o=function(e,t){var n,i,r,o,u,h,a,l,p,_,f,d,g,v,m,w,y,P,k,T,E,S,b,N,C,M,R,O,x,I,D,U;for(e[t>>2]|=1<<31-((3&t)<<3),e[(t+8>>6<<4)+15]=t<<3,x=Array(80),n=s[0],r=s[1],u=s[2],l=s[3],_=s[4],d=s[5],v=s[6],T=s[7],S=0,N=e.length;N>S;){for(i=n,o=r,h=u,p=l,f=_,g=d,m=v,E=T,b=U=0;64>U;b=++U)16>b?O=x[b]=0|e[S+b<<2>>2]:(y=0|x[b-15<<2>>2],w=(y<<25|y>>>7)^(y<<14|y>>>18)^y>>>3,k=0|x[b-2<<2>>2],P=(k<<15|k>>>17)^(k<<13|k>>>19)^k>>>10,O=x[b]=(w+(0|x[b-7<<2>>2])|0)+(P+(0|x[b-16<<2>>2])|0)|0),a=_&d^~_&v,C=n&r^n&u^r&u,M=(n<<30|n>>>2)^(n<<19|n>>>13)^(n<<10|n>>>22),R=(_<<26|_>>>6)^(_<<21|_>>>11)^(_<<7|_>>>25),I=((T+R|0)+(a+O|0)|0)+(0|c[b<<2>>2])|0,D=M+C|0,T=v,v=d,d=_,_=l+I|0,l=u,u=r,r=n,n=I+D|0;n=i+n|0,r=o+r|0,u=h+u|0,l=p+l|0,_=f+_|0,d=g+d|0,v=m+v|0,T=E+T|0,S+=16}return[n,r,u,l,_,d,v,T]},s=[],c=[],function(){var e,t,n,i,r,o,u;for(t=function(e){return 4294967296*(e-Math.floor(e))|0},r=2,u=[],n=o=0;64>o;n=++o){for(;;){for(i=!0,e=2;r>=e*e;){if(r%e===0){i=!1;break}e+=1}if(i)break;r+=1}8>n&&(s[n]=t(Math.pow(r,.5))),c[n]=t(Math.pow(r,1/3)),u.push(r+=1)}return u}(),e=function(e){var t,n,i,r,o;for(r="",t=0,i=4*e.length;i>t;)n=t,o=(e[n>>2]>>(3-(3&n)<<3)&255)<<16,n+=1,o|=(e[n>>2]>>(3-(3&n)<<3)&255)<<8,n+=1,o|=e[n>>2]>>(3-(3&n)<<3)&255,r+=h[o>>18&63],r+=h[o>>12&63],t+=1,i>t&&(r+=h[o>>6&63]),t+=1,i>t&&(r+=h[63&o]),t+=1;return r},h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",u=function(e){var t,n,i,r;for(t=[],n=i=0,r=e.length;r>=0?r>i:i>r;n=r>=0?++i:--i)t[n>>2]|=(255&e.charCodeAt(n))<<(3-(3&n)<<3);return t}}(),t._.jsonRequest=function(e,n,i){return new t._.Promise(function(r,o){var s;s=new t._.XMLHttpRequest,s.open(e,n,!0),s.onreadystatechange=function(){var e,n,i,c,u,h,a,l;if(4!==s.readyState)return!0;if(e=s.getResponseHeader("Content-Type"),e&&(a=e.indexOf(";"),-1!==a&&(e=e.substring(0,a))),u=null,h=null,"application/json"===e)try{u=JSON.parse(s.responseText||s.response)}catch(p){h=p}return l=s.status,l>=200&&300>l?h?void o(h):void r(u||{}):(0===l?(c=t._.DOMException.NetworkError,i="Could not reach Push Notification Server"):(c=t._.DOMException.SecurityError,i=u&&u.error?"Push Notification Server error: "+u.error:"Push Notification Server error: HTTP "+l),n=new t._.DOMException(c,i),0!==l&&(n.httpCode=l),void o(n))},i?(s.setRequestHeader("Content-Type","application/json; charset=utf-8"),s.send(JSON.stringify(i))):s.send()})},t._.PingPong=function(){function e(e){e||(e={}),this._silenceTimeout=e.silenceTimeoutMs||5e3,this._roundTrip=e.rttMs||2e4,this._pingSlack=e.pingSlackMs||5e3,this._nextPongNonce=0,this._pongNonce=null,this._silenceTimer=null,this._pingTimer=null,this._silenceTimerHandler=this._onSilenceTimeout.bind(this),this._pingTimerHandler=this._onPingTimeout.bind(this)}return e.prototype.startedConnecting=function(){this._resetSilenceTimer()},e.prototype.receivedPong=function(e){var t;e.nonce===this._pongNonce&&(t=Date.now()-e.ts,this._roundTrip=.2*this._roundTrip+.8*t),this._resetSilenceTimer()},e.prototype.receivedMessage=function(){this._resetSilenceTimer()},e.prototype.disconnected=function(){this._disableTimers()},e.prototype.onPing=function(){throw new Error("onPing not assigned")},e.prototype.onPingTimeout=function(){throw new Error("onPingTimeout not assigned")},e.prototype._resetSilenceTimer=function(){this._disableTimers(),this._silenceTimer=setTimeout(this._silenceTimerHandler,this._silenceTimeout)},e.prototype._onSilenceTimeout=function(){null!==this._silenceTimer&&(this._silenceTimer=null,null===this._pingTimer&&this._sendPing())},e.prototype._sendPing=function(){this._pongNonce=this._nextPongNonce,this._nextPongNonce=this._nextPongNouce+1|0,this.onPing({nonce:this._pongNonce,ts:Date.now()}),this._pingTimer=setTimeout(this._pingTimerHandler,this._roundTrip+this._pingSlack)},e.prototype._onPingTimeout=function(){null!==this._pingTimer&&(this._pingTimer=null,this._pongNonce=null,this._disableTimers(),this.onPingTimeout())},e.prototype._disableTimers=function(){null!==this._pingTimer&&(clearTimeout(this._pingTimer),this._pingTimer=null,this._pongNonce=null),null!==this._silenceTimer&&(clearTimeout(this._silenceTimer),this._silenceTimer=null)},e}(),t._.Promise="undefined"==typeof Promise?o("es6-promise-polyfill").Promise:Promise,t._.WebSocket="undefined"==typeof WebSocket?o("ws"):WebSocket,t._.XMLHttpRequest="undefined"==typeof XMLHttpRequest?o("xhr2"):XMLHttpRequest,t.PushEvent=function(){function e(e,t){this.name="PushEvent",this.target=e,this.data=t}return e}(),t._.createPushEvent=function(e,n){return new t.PushEvent(e,n)},t.PushRegistration=function(){function e(e){if(this._deviceRegistration=e,!(this._deviceRegistration instanceof t._.DeviceRegistration))throw new Error("Illegal constructor");this.registrationId=e.receiverId,this.endpoint=e.pushUrl,this._wsClient=null,this._canceled=!1,this._initialBackoff=1e3,this._nextBackoff=null,this._resolveConnected=null,this._connected=new t._.Promise(function(e){return function(t){return e._resolveConnected=t}}(this)),this._connect()}return e.prototype.registrationId=null,e.prototype.endpoint=null,e.prototype.onpush=null,e.prototype.onpushregistrationlost=null,e.prototype._cancel=function(){this._canceled=!0,this._wsClient&&this._wsClient.close(1e3,"Receiver unregistered")},e.prototype._connect=function(){return this._wsClient&&new t._.Promise.resolve(!0),this._canceled&&new t._.Promise.reject(new Error("Unregistered")),this._nextBackoff=this._initialBackoff,this._connectOnce()},e.prototype._connectOnce=function(){this._deviceRegistration.route().then(function(e){return function(n){return e._wsClient=new t._.WsClient(n),e._wsClient.onNotification=e._onNotification.bind(e),e._wsClient.closed.then(e._onWsClosed.bind(e)),e._wsClient.connected}}(this)).then(function(e){return function(){return e._nextBackoff=e._initialBackoff,e._resolveConnected(!0),!0}}(this))["catch"](function(e){return function(){var n;e._wsClient.close(4e3,"Network error"),n=e._nextBackoff,e._nextBackoff*=2,new t._.Promise(function(t){var i;return i=function(){return t(e._connect())},setTimeout(i,n)})}}(this))},e.prototype._onNotification=function(e){this._canceled||this.onpush&&this.onpush(t._.createPushEvent(this,JSON.stringify(e)))},e.prototype._onWsClosed=function(){this._wsClient=null,this._connect()},e}(),t.PushRegistrationManager=function(){function e(e){if(!e.server)throw new Error("The server option must be a W3gram server URL");if(!e.key)throw new Error("The key option must be a W3gram API key");if(!(this._deviceId=e.device))throw new Error("The device option must be a device ID");if(!(this._token=e.token))throw new Error("The token option must be the device ID's token");this._registration=null,this._app=new t._.App({server:e.server,key:e.key})}return e.prototype.register=function(){return null!==this._registration?t._.Promise.resolve(this._registration):this._app.register(this._deviceId,this._token).then(function(e){return function(n){return e._registration||(e._registration=new t.PushRegistration(n)),e._registration._connected.then(function(){return e._registration})}}(this))},e.prototype.unregister=function(){var e,n;return this._registration?(n=this._registration,this._registration=null,n._cancel(),void t._.Promise.resolve(n)):(e=new t._.DOMException(t._.DOMException.NotFoundError,"No compatible W3gram registration found on this device"),t._.Promise.reject(e))},e.prototype.getRegistration=function(){var e;return this._registration?void t._.Promise.resolve(this._registration):(e=new t._.DOMException(t._.DOMException.NotFoundError,"No compatible W3gram registration found on this device"),t._.Promise.reject(e))},e.prototype.hasPermission=function(){return t._.Promise.resolve("granted")},e}(),t._.Server=function(){function e(e){"/"===e.substring(e.length-1)&&(e=e.substring(0,e.length-1)),this.serverUrl=e,this._makUrl=""+e+"/mak",this._appsUrl=""+e+"/apps"}return e.prototype.getMak=function(){return t._.jsonRequest("GET",""+this.serverUrl+"/mak").then(function(e){return e.mak})},e.prototype.createApp=function(e,n){var i;return i={name:n.name,origin:n.origin||"*"},t._.jsonRequest("POST",this._appsUrl,{mak:e,app:i}).then(function(e){return function(n){return n.server=e.serverUrl,new t._.App(n)}}(this))},e}(),t._.WsClient=function(){function e(e,n){this._wsUrl=e,this._ws=null,this._pingPong=new t._.PingPong(n),this._pingPong.onPing=function(e){return function(t){return e.sendPing(t)}}(this),this._pingPong.onPingTimeout=function(e){return function(){return e.close(4e3,"Ping timeout")}}(this),this._resolveClosed=null,this._resolveConnected=null,this.connected=null,this.closed=new t._.Promise(function(e){return function(n){e._resolveClosed=n,e.connected=new t._.Promise(function(t){e._resolveConnected=t,e._connect()})}}(this))}return e.prototype.close=function(e,t){null!==this._ws&&(this._ws.onclose=null,this._ws.onerror=null,this._ws.onmessage=null,this._ws.onopen=null,e||(e=1e3),this._ws.close(e,t),this._ws=null,this._pingPong.disconnected(),this._resolveClosed({code:e,reason:t}))},e.prototype.sendPing=function(e){this._ws&&this._ws.send(JSON.stringify({type:"ping",data:e}))},e.prototype.connected=null,e.prototype.closed=null,e.prototype.onNotification=function(){},e.prototype._connect=function(){null===this._ws&&(this._ws=new t._.WebSocket(this._wsUrl),this._ws.onclose=this._onSocketClose.bind(this),this._ws.onerror=this._onSocketError.bind(this),this._ws.onmessage=this._onSocketMessage.bind(this),this._ws.onopen=this._onSocketOpen.bind(this),this._pingPong.startedConnecting())},e.prototype._onMessage=function(e){var t;return t=e.type,"pong"===t?void this._pingPong.receivedPong(e.data):(this._pingPong.receivedMessage(),"note"===t?void this.onNotification(e.data):"hi"===t?void this._resolveConnected(!0):void this._close(4400,"Invalid message type: "+t))},e.prototype._onSocketClose=function(e){null!==this._ws&&this.close(e.code,e.reason)},e.prototype._onSocketError=function(){null!==this._ws&&this.close(4e3,event.message)},e.prototype._onSocketMessage=function(e){var t,n,i;if(this._ws){i=e.data,n=null;try{n=JSON.parse(i)}catch(r){t=r,this.close(4400,"Invalid JSON message")}this._onMessage(n)}},e.prototype._onSocketOpen=function(){},e}()}).call(this); | ||
| //# sourceMappingURL=w3gram.min.map |
+1
-1
| { | ||
| "name": "w3gram", | ||
| "version": "0.1.0", | ||
| "version": "0.1.1", | ||
| "description": "W3gram Push Notification JavaScript Client Library", | ||
@@ -5,0 +5,0 @@ "keywords": ["push", "notifications", "w3gram"], |
+23
-85
@@ -1,94 +0,32 @@ | ||
| # W3gram Server | ||
| # W3gram Client JavaScript Library | ||
| [](https://heroku.com/deploy) | ||
| [](https://travis-ci.org/pwnall/w3gram-server) | ||
| [](http://coffeedoc.info/github/pwnall/w3gram-server) | ||
| [](https://www.npmjs.org/package/w3gram-server) | ||
| [](https://travis-ci.org/pwnall/w3gram-js) | ||
| [](http://coffeedoc.info/github/pwnall/w3gram-js) | ||
| [](https://www.npmjs.org/package/w3gram-js) | ||
| This is a [node.js](http://nodejs.org/) server for the | ||
| [W3gram push notification protocol](Protocol.md). | ||
| This is a [W3c Push API](http://w3c.github.io/push-api/) polyfill that uses the | ||
| [W3gram push notification server](https://github.com/pwnall/w3gram-server). The | ||
| library works in modern browsers and in [node.js](http://nodejs.org/). | ||
| The server was designed to be deployed to [Heroku](https://www.heroku.com/) | ||
| using free resources, so it fits in a single dyno. The code has great test | ||
| coverage using [mocha](http://visionmedia.github.io/mocha/). | ||
| ## Setup | ||
| ## Easy Setup | ||
| ```javascript | ||
| W3gram.setupPushManager({ | ||
| "server": "https://your-w3gram-server.herokuapp.com", | ||
| "app": "your-api-key", | ||
| "device": "the-device-id", | ||
| "token": "token-for-the-device-id" | ||
| }); | ||
| Click the ''Deploy to Heroku'' button at the top of this page to create your own | ||
| W3gram server running on Heroku. Don't worry, the project only uses free | ||
| add-ons! | ||
| Get your server's MAK and store it somewhere safely. | ||
| ```bash | ||
| curl -i https://w3gram-test.herokuapp.com/mak | ||
| # Response example: | ||
| # { "mak": "3LwwhZCuqPxO_0fNATuZbOxRgXjWuCLXzOzVaH5dZ4k" } | ||
| // pushRegistrationManager returns a PushRegistrationManager that can be used | ||
| // according to the Push API spec. | ||
| W3gram.pushRegistrationManager.register(); | ||
| ``` | ||
| Create an application and note its API key and secret. | ||
| ```bash | ||
| curl -i -X POST -H 'Content-Type: application/json' \ | ||
| -d '{"mak": "3LwwhZCuqPxO_0fNATuZbOxRgXjWuCLXzOzVaH5dZ4k", "app": { "name": "Testing", "origin": "*"}}' \ | ||
| https://w3gram-test.herokuapp.com/apps | ||
| # Response example: | ||
| # { | ||
| # "key":"MYp4g89u3OafCtZP", | ||
| # "secret":"z7zlLM44rFWQzmgXZX0d2r7rX9mdAP7Gg56V6YjFscY", | ||
| # "origin":"*", | ||
| # "name":"Testing" | ||
| # } | ||
| ``` | ||
| ### Manual Interaction Example | ||
| Create a token for a device ID. | ||
| ```bash | ||
| echo -n "device-id|my-tablet" | \ | ||
| openssl dgst -sha256 -hmac "z7zlLM44rFWQzmgXZX0d2r7rX9mdAP7Gg56V6YjFscY" \ | ||
| -binary | base64 | ||
| # Response example: | ||
| # Xm3U6AU4qj7z5axtldHsNvhHqlsLdMRKQQbXoiFhmDU | ||
| ``` | ||
| Do the routing step. | ||
| ```bash | ||
| curl -i -X POST -H 'Content-Type: application/json' \ | ||
| -d '{"app": "MYp4g89u3OafCtZP", "device": "my-tablet", "token": "Xm3U6AU4qj7z5axtldHsNvhHqlsLdMRKQQbXoiFhmDU"}' \ | ||
| https://w3gram-test.herokuapp.com/route | ||
| # Response example: | ||
| # { | ||
| # "receiverId":"2.my-tablet.Wk3Lgc_dy0wu8smU7vHhL-Z2oDhkcF6V3Fj3O1ta2a4", | ||
| # "push":"https://w3gram-test.herokuapp.com/push", | ||
| # "listen":"wss://w3gram-test.herokuapp.com/ws/2.my-tablet.Wk3Lgc_dy0wu8smU7vHhL-Z2oDhkcF6V3Fj3O1ta2a4" | ||
| # } | ||
| ``` | ||
| Start a WebSocket connection: | ||
| ```bash | ||
| wscat -c "wss://w3gram-test.herokuapp.com/ws/2.my-tablet.Wk3Lgc_dy0wu8smU7vHhL-Z2oDhkcF6V3Fj3O1ta2a4" | ||
| ``` | ||
| Send a notification: | ||
| ```bash | ||
| curl -i -X POST -H 'Content-Type: application/json' \ | ||
| -d '{"receiver": "2.my-tablet.Wk3Lgc_dy0wu8smU7vHhL-Z2oDhkcF6V3Fj3O1ta2a4", "message": { "data": "Hello push world" } }' \ | ||
| https://w3gram-test.herokuapp.com/push | ||
| ``` | ||
| ## Development Setup | ||
| Install all dependencies and create PostgreSQL database for development and | ||
| testing. | ||
| Install all dependencies and create the PostgreSQL database used by the W3gram | ||
| server in the test suite. | ||
@@ -98,9 +36,9 @@ ```bash | ||
| createdb w3gram_test | ||
| createdb w3gram_dev | ||
| ``` | ||
| Run the server in developmentm mode. | ||
| Run tests. | ||
| ```bash | ||
| npm start | ||
| cake test | ||
| cake webtest | ||
| ``` | ||
@@ -107,0 +45,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
131833
0.98%46
6.98%863
0.58%50
-55.36%