graphql-ws
Advanced tools
Comparing version 2.0.1 to 3.0.0
@@ -0,1 +1,31 @@ | ||
# [3.0.0](https://github.com/enisdenjo/graphql-ws/compare/v2.0.1...v3.0.0) (2020-12-09) | ||
### Features | ||
* **client:** Retry with randomised exponential backoff or provide your own strategy ([#84](https://github.com/enisdenjo/graphql-ws/issues/84)) ([d3e7a17](https://github.com/enisdenjo/graphql-ws/commit/d3e7a171603a3ef181c5af533768dcda416a1731)) | ||
### BREAKING CHANGES | ||
* **client:** Client `retryTimeout` option has been replaced with the new `retryWait`. | ||
`retryWait` allows you to control the retry timeout strategy by resolving the returned promise when ready. The default implements the randomised exponential backoff like so: | ||
```ts | ||
// this is the default | ||
const retryWait = async function randomisedExponentialBackoff(retries: number) { | ||
let retryDelay = 1000; // start with 1s delay | ||
for (let i = 0; i < retries; i++) { | ||
retryDelay *= 2; // square `retries` times | ||
} | ||
await new Promise((resolve) => | ||
setTimeout( | ||
// resolve pending promise with added random timeout from 300ms to 3s | ||
resolve, | ||
retryDelay + Math.floor(Math.random() * (3000 - 300) + 300), | ||
), | ||
); | ||
}; | ||
``` | ||
## [2.0.1](https://github.com/enisdenjo/graphql-ws/compare/v2.0.0...v2.0.1) (2020-12-03) | ||
@@ -2,0 +32,0 @@ |
@@ -77,7 +77,8 @@ /** | ||
/** | ||
* How long should the client wait until attempting to retry. | ||
* Control the wait time between retries. You may implement your own strategy | ||
* by timing the resolution of the returned promise with the retries count. | ||
* | ||
* @default 3 * 1000 (3 seconds) | ||
* @default Randomised exponential backoff | ||
*/ | ||
retryTimeout?: number; | ||
retryWait?: (retries: number) => Promise<void>; | ||
/** | ||
@@ -84,0 +85,0 @@ * Register listeners before initialising the client. This way |
@@ -14,4 +14,11 @@ "use strict"; | ||
function createClient(options) { | ||
const { url, connectionParams, lazy = true, keepAlive = 0, retryAttempts = 5, retryTimeout = 3 * 1000, // 3 seconds | ||
on, webSocketImpl, | ||
const { url, connectionParams, lazy = true, keepAlive = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) { | ||
let retryDelay = 1000; // start with 1s delay | ||
for (let i = 0; i < retries; i++) { | ||
retryDelay *= 2; | ||
} | ||
await new Promise((resolve) => setTimeout(resolve, retryDelay + | ||
// add random timeout from 300ms to 3s | ||
Math.floor(Math.random() * (3000 - 300) + 300))); | ||
}, on, webSocketImpl, | ||
/** | ||
@@ -41,8 +48,12 @@ * Generates a v4 UUID to be used as the ID using `Math` | ||
else if (typeof global !== 'undefined') { | ||
// @ts-expect-error: Support more browsers | ||
ws = global.WebSocket || global.MozWebSocket; | ||
ws = | ||
global.WebSocket || | ||
// @ts-expect-error: Support more browsers | ||
global.MozWebSocket; | ||
} | ||
else if (typeof window !== 'undefined') { | ||
// @ts-expect-error: Support more browsers | ||
ws = window.WebSocket || window.MozWebSocket; | ||
ws = | ||
window.WebSocket || | ||
// @ts-expect-error: Support more browsers | ||
window.MozWebSocket; | ||
} | ||
@@ -85,5 +96,9 @@ if (!ws) { | ||
locks: 0, | ||
tries: 0, | ||
retrying: false, | ||
retries: 0, | ||
}; | ||
// all those waiting for the `retryWait` to resolve | ||
const retryWaiting = []; | ||
async function connect(cancellerRef, callDepth = 0) { | ||
var _a; | ||
// prevents too many recursive calls when reavaluating/re-connecting | ||
@@ -93,2 +108,22 @@ if (callDepth > 10) { | ||
} | ||
// retry wait strategy only on root caller | ||
if (state.retrying && callDepth === 0) { | ||
if (retryWaiting.length) { | ||
// if others are waiting for retry, I'll wait too | ||
await new Promise((resolve) => retryWaiting.push(resolve)); | ||
} | ||
else { | ||
retryWaiting.push(() => { | ||
/** fake waiter to lead following connects in the `retryWaiting` queue */ | ||
}); | ||
// use retry wait strategy | ||
await retryWait(state.retries++); | ||
// complete all waiting and clear the queue | ||
while (retryWaiting.length) { | ||
(_a = retryWaiting.pop()) === null || _a === void 0 ? void 0 : _a(); | ||
} | ||
} | ||
} | ||
// if recursive call, wait a bit for socket change | ||
await new Promise((resolve) => setTimeout(resolve, callDepth * 50)); | ||
// socket already exists. can be ready or pending, check and behave accordingly | ||
@@ -100,3 +135,2 @@ if (state.socket) { | ||
if (!state.acknowledged) { | ||
await new Promise((resolve) => setTimeout(resolve, 300)); | ||
return connect(cancellerRef, callDepth + 1); | ||
@@ -108,3 +142,2 @@ } | ||
// if the socket is in the connecting phase, wait a bit and reavaluate | ||
await new Promise((resolve) => setTimeout(resolve, 300)); | ||
return connect(cancellerRef, callDepth + 1); | ||
@@ -116,3 +149,2 @@ } | ||
// if the socket is in the closing phase, wait a bit and connect | ||
await new Promise((resolve) => setTimeout(resolve, 300)); | ||
return connect(cancellerRef, callDepth + 1); | ||
@@ -126,3 +158,3 @@ } | ||
const socket = new WebSocketImpl(url, protocol_1.GRAPHQL_TRANSPORT_WS_PROTOCOL); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket, tries: state.tries + 1 }); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket }); | ||
emitter.emit('connecting'); | ||
@@ -162,3 +194,3 @@ await new Promise((resolve, reject) => { | ||
clearTimeout(tooLong); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: true, socket, tries: 0 }); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: true, socket, retrying: false, retries: 0 }); | ||
emitter.emit('connected', socket, message.payload); // connected = socket opened + acknowledged | ||
@@ -267,6 +299,7 @@ return resolve(); | ||
// retries are not allowed or we tried to many times, report error | ||
if (!retryAttempts || state.tries > retryAttempts) { | ||
if (!retryAttempts || state.retries >= retryAttempts) { | ||
throw errOrCloseEvent; | ||
} | ||
// looks good, please retry | ||
// looks good, start retrying | ||
state.retrying = true; | ||
return true; | ||
@@ -288,7 +321,4 @@ } | ||
// return if shouldnt try again | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) { | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) | ||
return; | ||
} | ||
// if should try again, wait a bit and continue loop | ||
await new Promise((resolve) => setTimeout(resolve, retryTimeout)); | ||
} | ||
@@ -378,7 +408,4 @@ } | ||
// return if shouldnt try again | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) { | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) | ||
return; | ||
} | ||
// if should try again, wait a bit and continue loop | ||
await new Promise((resolve) => setTimeout(resolve, retryTimeout)); | ||
} | ||
@@ -385,0 +412,0 @@ } |
{ | ||
"name": "graphql-ws", | ||
"version": "2.0.1", | ||
"version": "3.0.0", | ||
"description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -446,2 +446,29 @@ <div align="center"> | ||
<details id="retry-strategy"> | ||
<summary><a href="#retry-strategy">🔗</a> Client usage with custom retry timeout strategy</summary> | ||
```typescript | ||
import { createClient } from 'graphql-ws'; | ||
const client = createClient({ | ||
url: 'wss://i.want.retry/control/graphql', | ||
// this is the default | ||
retryWait: async function randomisedExponentialBackoff(retries: number) { | ||
let retryDelay = 1000; // start with 1s delay | ||
for (let i = 0; i < retries; i++) { | ||
retryDelay *= 2; // square `retries` times | ||
} | ||
await new Promise((resolve) => | ||
setTimeout( | ||
// resolve pending promise with added random timeout from 300ms to 3s | ||
resolve, | ||
retryDelay + Math.floor(Math.random() * (3000 - 300) + 300), | ||
), | ||
); | ||
}, | ||
}); | ||
``` | ||
</details> | ||
<details id="browser"> | ||
@@ -746,2 +773,67 @@ <summary><a href="#browser">🔗</a> Client usage in browser</summary> | ||
<details id="ws-backwards-compat"> | ||
<summary><a href="#ws-backwards-compat">🔗</a> <a href="https://github.com/websockets/ws">ws</a> server usage with <a href="https://github.com/apollographql/subscriptions-transport-ws">subscriptions-transport-ws</a> backwards compatibility</summary> | ||
```ts | ||
import https from 'https'; | ||
import ws from 'ws'; // yarn add ws | ||
import { execute, subscribe } from 'graphql'; | ||
import { GRAPHQL_TRANSPORT_WS_PROTOCOL } from 'graphql-ws'; | ||
import { useServer } from 'graphql-ws/lib/use/ws'; | ||
import { SubscriptionServer, GRAPHQL_WS } from 'subscriptions-transport-ws'; | ||
import { schema } from 'my-graphql-schema'; | ||
// graphql-ws | ||
const graphqlWs = new ws.Server({ noServer: true }); | ||
useServer( | ||
{ | ||
schema, | ||
execute, | ||
subscribe, | ||
}, | ||
graphqlWs, | ||
); | ||
// subscriptions-transport-ws | ||
const subTransWs = new ws.Server({ noServer: true }); | ||
SubscriptionServer.create( | ||
{ | ||
schema, | ||
execute, | ||
subscribe, | ||
}, | ||
subTransWs, | ||
); | ||
// create https server | ||
const server = https.createServer(function weServeSocketsOnly(_, res) { | ||
res.writeHead(404); | ||
res.end(); | ||
}); | ||
// listen for upgrades and delegate requests according to the WS subprotocol | ||
server.on('upgrade', (req, socket, head) => { | ||
// extract websocket subprotocol from header | ||
const protocol = req.headers['sec-websocket-protocol']; | ||
const protocols = Array.isArray(protocol) | ||
? protocol | ||
: protocol?.split(',').map((p) => p.trim()); | ||
// decide which websocket server to use | ||
const wss = | ||
protocols?.includes(GRAPHQL_WS) && // subscriptions-transport-ws subprotocol | ||
!protocols.includes(GRAPHQL_TRANSPORT_WS_PROTOCOL) // graphql-ws subprotocol | ||
? subTransWs | ||
: // graphql-ws will welcome its own subprotocol and | ||
// gracefully reject invalid ones. if the client supports | ||
// both transports, graphql-ws will prevail | ||
graphqlWs; | ||
wss.handleUpgrade(req, socket, head, (ws) => { | ||
wss.emit('connection', ws, req); | ||
}); | ||
}); | ||
``` | ||
</details> | ||
<details id="logging"> | ||
@@ -748,0 +840,0 @@ <summary><a href="#logging">🔗</a> <a href="https://github.com/websockets/ws">ws</a> server usage with console logging</summary> |
@@ -125,4 +125,11 @@ (function (global, factory) { | ||
function createClient(options) { | ||
const { url, connectionParams, lazy = true, keepAlive = 0, retryAttempts = 5, retryTimeout = 3 * 1000, // 3 seconds | ||
on, webSocketImpl, | ||
const { url, connectionParams, lazy = true, keepAlive = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) { | ||
let retryDelay = 1000; // start with 1s delay | ||
for (let i = 0; i < retries; i++) { | ||
retryDelay *= 2; | ||
} | ||
await new Promise((resolve) => setTimeout(resolve, retryDelay + | ||
// add random timeout from 300ms to 3s | ||
Math.floor(Math.random() * (3000 - 300) + 300))); | ||
}, on, webSocketImpl, | ||
/** | ||
@@ -152,8 +159,12 @@ * Generates a v4 UUID to be used as the ID using `Math` | ||
else if (typeof global !== 'undefined') { | ||
// @ts-expect-error: Support more browsers | ||
ws = global.WebSocket || global.MozWebSocket; | ||
ws = | ||
global.WebSocket || | ||
// @ts-expect-error: Support more browsers | ||
global.MozWebSocket; | ||
} | ||
else if (typeof window !== 'undefined') { | ||
// @ts-expect-error: Support more browsers | ||
ws = window.WebSocket || window.MozWebSocket; | ||
ws = | ||
window.WebSocket || | ||
// @ts-expect-error: Support more browsers | ||
window.MozWebSocket; | ||
} | ||
@@ -196,5 +207,9 @@ if (!ws) { | ||
locks: 0, | ||
tries: 0, | ||
retrying: false, | ||
retries: 0, | ||
}; | ||
// all those waiting for the `retryWait` to resolve | ||
const retryWaiting = []; | ||
async function connect(cancellerRef, callDepth = 0) { | ||
var _a; | ||
// prevents too many recursive calls when reavaluating/re-connecting | ||
@@ -204,2 +219,22 @@ if (callDepth > 10) { | ||
} | ||
// retry wait strategy only on root caller | ||
if (state.retrying && callDepth === 0) { | ||
if (retryWaiting.length) { | ||
// if others are waiting for retry, I'll wait too | ||
await new Promise((resolve) => retryWaiting.push(resolve)); | ||
} | ||
else { | ||
retryWaiting.push(() => { | ||
/** fake waiter to lead following connects in the `retryWaiting` queue */ | ||
}); | ||
// use retry wait strategy | ||
await retryWait(state.retries++); | ||
// complete all waiting and clear the queue | ||
while (retryWaiting.length) { | ||
(_a = retryWaiting.pop()) === null || _a === void 0 ? void 0 : _a(); | ||
} | ||
} | ||
} | ||
// if recursive call, wait a bit for socket change | ||
await new Promise((resolve) => setTimeout(resolve, callDepth * 50)); | ||
// socket already exists. can be ready or pending, check and behave accordingly | ||
@@ -211,3 +246,2 @@ if (state.socket) { | ||
if (!state.acknowledged) { | ||
await new Promise((resolve) => setTimeout(resolve, 300)); | ||
return connect(cancellerRef, callDepth + 1); | ||
@@ -219,3 +253,2 @@ } | ||
// if the socket is in the connecting phase, wait a bit and reavaluate | ||
await new Promise((resolve) => setTimeout(resolve, 300)); | ||
return connect(cancellerRef, callDepth + 1); | ||
@@ -227,3 +260,2 @@ } | ||
// if the socket is in the closing phase, wait a bit and connect | ||
await new Promise((resolve) => setTimeout(resolve, 300)); | ||
return connect(cancellerRef, callDepth + 1); | ||
@@ -237,3 +269,3 @@ } | ||
const socket = new WebSocketImpl(url, GRAPHQL_TRANSPORT_WS_PROTOCOL); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket, tries: state.tries + 1 }); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket }); | ||
emitter.emit('connecting'); | ||
@@ -273,3 +305,3 @@ await new Promise((resolve, reject) => { | ||
clearTimeout(tooLong); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: true, socket, tries: 0 }); | ||
state = Object.assign(Object.assign({}, state), { acknowledged: true, socket, retrying: false, retries: 0 }); | ||
emitter.emit('connected', socket, message.payload); // connected = socket opened + acknowledged | ||
@@ -378,6 +410,7 @@ return resolve(); | ||
// retries are not allowed or we tried to many times, report error | ||
if (!retryAttempts || state.tries > retryAttempts) { | ||
if (!retryAttempts || state.retries >= retryAttempts) { | ||
throw errOrCloseEvent; | ||
} | ||
// looks good, please retry | ||
// looks good, start retrying | ||
state.retrying = true; | ||
return true; | ||
@@ -399,7 +432,4 @@ } | ||
// return if shouldnt try again | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) { | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) | ||
return; | ||
} | ||
// if should try again, wait a bit and continue loop | ||
await new Promise((resolve) => setTimeout(resolve, retryTimeout)); | ||
} | ||
@@ -489,7 +519,4 @@ } | ||
// return if shouldnt try again | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) { | ||
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) | ||
return; | ||
} | ||
// if should try again, wait a bit and continue loop | ||
await new Promise((resolve) => setTimeout(resolve, retryTimeout)); | ||
} | ||
@@ -496,0 +523,0 @@ } |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).graphqlWs={})}(this,(function(e){"use strict";const t=Object.prototype.hasOwnProperty;function n(e){return"object"==typeof e&&null!==e}function o(e,n){return t.call(e,n)}function r(e,o){return t.call(e,o)&&n(e[o])}function c(e,n){return t.call(e,n)&&"string"==typeof e[n]}var i;function s(e){if(n(e)){if(!c(e,"type"))return!1;switch(e.type){case i.ConnectionInit:case i.ConnectionAck:return!o(e,"payload")||void 0===e.payload||n(e.payload);case i.Subscribe:return c(e,"id")&&r(e,"payload")&&(!o(e.payload,"operationName")||void 0===e.payload.operationName||null===e.payload.operationName||"string"==typeof e.payload.operationName)&&c(e.payload,"query")&&(!o(e.payload,"variables")||void 0===e.payload.variables||null===e.payload.variables||r(e.payload,"variables"));case i.Next:return c(e,"id")&&r(e,"payload");case i.Error:return c(e,"id")&&(t=e.payload,Array.isArray(t)&&t.length>0&&t.every((e=>"message"in e)));case i.Complete:return c(e,"id");default:return!1}}var t;return!1}function a(e){if(s(e))return e;if("string"!=typeof e)throw new Error("Message not parsable");const t=JSON.parse(e);if(!s(t))throw new Error("Invalid message");return t}function l(e){if(!s(e))throw new Error("Cannot stringify invalid message");return JSON.stringify(e)}!function(e){e.ConnectionInit="connection_init",e.ConnectionAck="connection_ack",e.Subscribe="subscribe",e.Next="next",e.Error="error",e.Complete="complete"}(i||(i={})),e.createClient=function(e){const{url:t,connectionParams:o,lazy:r=!0,keepAlive:c=0,retryAttempts:s=5,retryTimeout:u=3e3,on:d,webSocketImpl:f,generateID:p=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(e=>{const t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)}))}}=e;let y;if(f){if(!("function"==typeof(m=f)&&"constructor"in m&&"CLOSED"in m&&"CLOSING"in m&&"CONNECTING"in m&&"OPEN"in m))throw new Error("Invalid WebSocket implementation provided");y=f}else"undefined"!=typeof WebSocket?y=WebSocket:"undefined"!=typeof global?y=global.WebSocket||global.MozWebSocket:"undefined"!=typeof window&&(y=window.WebSocket||window.MozWebSocket);var m;if(!y)throw new Error("WebSocket implementation missing");const w=y,b=(()=>{const e={connecting:(null==d?void 0:d.connecting)?[d.connecting]:[],connected:(null==d?void 0:d.connected)?[d.connected]:[],closed:(null==d?void 0:d.closed)?[d.closed]:[]};return{on(t,n){const o=e[t];return o.push(n),()=>{o.splice(o.indexOf(n),1)}},emit(t,...n){for(const o of e[t])o(...n)},reset(){Object.keys(e).forEach((t=>{e[t]=[]}))}}})();let g,k,x={socket:null,acknowledged:!1,locks:0,tries:0};async function v(e,n=0){if(n>10)throw new Error("Kept trying to connect but the socket never settled.");if(x.socket)switch(x.socket.readyState){case w.OPEN:return x.acknowledged?h(x.socket,e):(await new Promise((e=>setTimeout(e,300))),v(e,n+1));case w.CONNECTING:return await new Promise((e=>setTimeout(e,300))),v(e,n+1);case w.CLOSED:break;case w.CLOSING:return await new Promise((e=>setTimeout(e,300))),v(e,n+1);default:throw new Error(`Impossible ready state ${x.socket.readyState}`)}const r=new w(t,"graphql-transport-ws");return x=Object.assign(Object.assign({},x),{acknowledged:!1,socket:r,tries:x.tries+1}),b.emit("connecting"),await new Promise(((t,n)=>{let c=!1;e.current=()=>c=!0;const s=setTimeout((()=>{r.close(3408,"Waited 5 seconds but socket connect never settled")}),5e3);r.onclose=e=>(r.onclose=null,clearTimeout(s),x=Object.assign(Object.assign({},x),{acknowledged:!1,socket:null}),b.emit("closed",e),n(e)),r.onmessage=e=>{if(r.onmessage=null,c)r.close(3499,"Client cancelled the socket before connecting");else try{const n=a(e.data);if(n.type!==i.ConnectionAck)throw new Error(`First message cannot be of type ${n.type}`);return clearTimeout(s),x=Object.assign(Object.assign({},x),{acknowledged:!0,socket:r,tries:0}),b.emit("connected",r,n.payload),t()}catch(e){r.close(4400,e instanceof Error?e.message:new Error(e).message)}},r.onopen=()=>{r.onopen=null,c?r.close(3499,"Client cancelled the socket before connecting"):(async()=>{try{r.send(l({type:i.ConnectionInit,payload:"function"==typeof o?await o():o}))}catch(e){r.close(4400,e instanceof Error?e.message:new Error(e).message)}})()}})),h(r,e)}async function h(e,t){return[e,n=>new Promise(((o,r)=>{if(e.readyState===w.CLOSED)return r(new Error("Socket has already been closed"));function i(n){return t.current=null,x.locks--,e.removeEventListener("close",i),r(n)}x.locks++,e.addEventListener("close",i),t.current=()=>(t.current=null,null==n||n(),x.locks--,x.locks||(c>0&&isFinite(c)?setTimeout((()=>{!x.locks&&e.OPEN&&e.close(1e3,"Normal Closure")}),c):e.close(1e3,"Normal Closure")),e.removeEventListener("close",i),o())}))]}function E(e){if(!function(e){return n(e)&&"code"in e&&"reason"in e}(e))throw e;if([1002,1011,4400,4401,4409,4429].includes(e.code))throw e;if(1e3===e.code)return!1;if(3499===e.code)return!1;if(!s||x.tries>s)throw e;return!0}return r||(async()=>{for(;;)try{const[,e]=await v({current:null});return void await e()}catch(e){if(!E(e))return;await new Promise((e=>setTimeout(e,u)))}})(),{on:b.on,subscribe(e,t){const n=p();let o=!1;const r={current:null},c=({data:e})=>{const c=function(e){return e!==g&&(k=a(e),g=e),k}(e);switch(c.type){case i.Next:return void(c.id===n&&t.next(c.payload));case i.Error:return void(c.id===n&&(t.error(c.payload),r.current()));case i.Complete:return void(c.id===n&&(o=!0,r.current()))}};return(async()=>{for(;;)try{const[t,s]=await v(r);return t.addEventListener("message",c),t.send(l({id:n,type:i.Subscribe,payload:e})),await s((()=>{o||t.send(l({id:n,type:i.Complete}))})),void t.removeEventListener("message",c)}catch(e){if(!E(e))return;await new Promise((e=>setTimeout(e,u)))}})().catch(t.error).then(t.complete),()=>{var e;null===(e=r.current)||void 0===e||e.call(r)}},dispose(){var e;null===(e=x.socket)||void 0===e||e.close(1e3,"Normal Closure"),b.reset()}}},Object.defineProperty(e,"__esModule",{value:!0})})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).graphqlWs={})}(this,(function(e){"use strict";const t=Object.prototype.hasOwnProperty;function n(e){return"object"==typeof e&&null!==e}function o(e,n){return t.call(e,n)}function r(e,o){return t.call(e,o)&&n(e[o])}function c(e,n){return t.call(e,n)&&"string"==typeof e[n]}var i;function a(e){if(n(e)){if(!c(e,"type"))return!1;switch(e.type){case i.ConnectionInit:case i.ConnectionAck:return!o(e,"payload")||void 0===e.payload||n(e.payload);case i.Subscribe:return c(e,"id")&&r(e,"payload")&&(!o(e.payload,"operationName")||void 0===e.payload.operationName||null===e.payload.operationName||"string"==typeof e.payload.operationName)&&c(e.payload,"query")&&(!o(e.payload,"variables")||void 0===e.payload.variables||null===e.payload.variables||r(e.payload,"variables"));case i.Next:return c(e,"id")&&r(e,"payload");case i.Error:return c(e,"id")&&(t=e.payload,Array.isArray(t)&&t.length>0&&t.every((e=>"message"in e)));case i.Complete:return c(e,"id");default:return!1}}var t;return!1}function s(e){if(a(e))return e;if("string"!=typeof e)throw new Error("Message not parsable");const t=JSON.parse(e);if(!a(t))throw new Error("Invalid message");return t}function l(e){if(!a(e))throw new Error("Cannot stringify invalid message");return JSON.stringify(e)}!function(e){e.ConnectionInit="connection_init",e.ConnectionAck="connection_ack",e.Subscribe="subscribe",e.Next="next",e.Error="error",e.Complete="complete"}(i||(i={})),e.createClient=function(e){const{url:t,connectionParams:o,lazy:r=!0,keepAlive:c=0,retryAttempts:a=5,retryWait:u=async function(e){let t=1e3;for(let n=0;n<e;n++)t*=2;await new Promise((e=>setTimeout(e,t+Math.floor(2700*Math.random()+300))))},on:d,webSocketImpl:f,generateID:p=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(e=>{const t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)}))}}=e;let y;if(f){if(!("function"==typeof(m=f)&&"constructor"in m&&"CLOSED"in m&&"CLOSING"in m&&"CONNECTING"in m&&"OPEN"in m))throw new Error("Invalid WebSocket implementation provided");y=f}else"undefined"!=typeof WebSocket?y=WebSocket:"undefined"!=typeof global?y=global.WebSocket||global.MozWebSocket:"undefined"!=typeof window&&(y=window.WebSocket||window.MozWebSocket);var m;if(!y)throw new Error("WebSocket implementation missing");const w=y,g=(()=>{const e={connecting:(null==d?void 0:d.connecting)?[d.connecting]:[],connected:(null==d?void 0:d.connected)?[d.connected]:[],closed:(null==d?void 0:d.closed)?[d.closed]:[]};return{on(t,n){const o=e[t];return o.push(n),()=>{o.splice(o.indexOf(n),1)}},emit(t,...n){for(const o of e[t])o(...n)},reset(){Object.keys(e).forEach((t=>{e[t]=[]}))}}})();let b={socket:null,acknowledged:!1,locks:0,retrying:!1,retries:0};const k=[];async function v(e,n=0){var r;if(n>10)throw new Error("Kept trying to connect but the socket never settled.");if(b.retrying&&0===n)if(k.length)await new Promise((e=>k.push(e)));else for(k.push((()=>{})),await u(b.retries++);k.length;)null===(r=k.pop())||void 0===r||r();if(await new Promise((e=>setTimeout(e,50*n))),b.socket)switch(b.socket.readyState){case w.OPEN:return b.acknowledged?x(b.socket,e):v(e,n+1);case w.CONNECTING:return v(e,n+1);case w.CLOSED:break;case w.CLOSING:return v(e,n+1);default:throw new Error(`Impossible ready state ${b.socket.readyState}`)}const c=new w(t,"graphql-transport-ws");return b=Object.assign(Object.assign({},b),{acknowledged:!1,socket:c}),g.emit("connecting"),await new Promise(((t,n)=>{let r=!1;e.current=()=>r=!0;const a=setTimeout((()=>{c.close(3408,"Waited 5 seconds but socket connect never settled")}),5e3);c.onclose=e=>(c.onclose=null,clearTimeout(a),b=Object.assign(Object.assign({},b),{acknowledged:!1,socket:null}),g.emit("closed",e),n(e)),c.onmessage=e=>{if(c.onmessage=null,r)c.close(3499,"Client cancelled the socket before connecting");else try{const n=s(e.data);if(n.type!==i.ConnectionAck)throw new Error(`First message cannot be of type ${n.type}`);return clearTimeout(a),b=Object.assign(Object.assign({},b),{acknowledged:!0,socket:c,retrying:!1,retries:0}),g.emit("connected",c,n.payload),t()}catch(e){c.close(4400,e instanceof Error?e.message:new Error(e).message)}},c.onopen=()=>{c.onopen=null,r?c.close(3499,"Client cancelled the socket before connecting"):(async()=>{try{c.send(l({type:i.ConnectionInit,payload:"function"==typeof o?await o():o}))}catch(e){c.close(4400,e instanceof Error?e.message:new Error(e).message)}})()}})),x(c,e)}async function x(e,t){return[e,n=>new Promise(((o,r)=>{if(e.readyState===w.CLOSED)return r(new Error("Socket has already been closed"));function i(n){return t.current=null,b.locks--,e.removeEventListener("close",i),r(n)}b.locks++,e.addEventListener("close",i),t.current=()=>(t.current=null,null==n||n(),b.locks--,b.locks||(c>0&&isFinite(c)?setTimeout((()=>{!b.locks&&e.OPEN&&e.close(1e3,"Normal Closure")}),c):e.close(1e3,"Normal Closure")),e.removeEventListener("close",i),o())}))]}function h(e){if(!function(e){return n(e)&&"code"in e&&"reason"in e}(e))throw e;if([1002,1011,4400,4401,4409,4429].includes(e.code))throw e;if(1e3===e.code)return!1;if(3499===e.code)return!1;if(!a||b.retries>=a)throw e;return b.retrying=!0,!0}let E,C;return r||(async()=>{for(;;)try{const[,e]=await v({current:null});return void await e()}catch(e){if(!h(e))return}})(),{on:g.on,subscribe(e,t){const n=p();let o=!1;const r={current:null},c=({data:e})=>{const c=function(e){return e!==E&&(C=s(e),E=e),C}(e);switch(c.type){case i.Next:return void(c.id===n&&t.next(c.payload));case i.Error:return void(c.id===n&&(t.error(c.payload),r.current()));case i.Complete:return void(c.id===n&&(o=!0,r.current()))}};return(async()=>{for(;;)try{const[t,a]=await v(r);return t.addEventListener("message",c),t.send(l({id:n,type:i.Subscribe,payload:e})),await a((()=>{o||t.send(l({id:n,type:i.Complete}))})),void t.removeEventListener("message",c)}catch(e){if(!h(e))return}})().catch(t.error).then(t.complete),()=>{var e;null===(e=r.current)||void 0===e||e.call(r)}},dispose(){var e;null===(e=b.socket)||void 0===e||e.close(1e3,"Normal Closure"),g.reset()}}},Object.defineProperty(e,"__esModule",{value:!0})})); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
163405
2060
1099