Socket
Socket
Sign inDemoInstall

graphql-ws

Package Overview
Dependencies
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-ws - npm Package Compare versions

Comparing version 3.0.2 to 3.1.0

12

CHANGELOG.md

@@ -0,1 +1,13 @@

# [3.1.0](https://github.com/enisdenjo/graphql-ws/compare/v3.0.2...v3.1.0) (2020-12-11)
### Bug Fixes
* **client:** Time retries and socket change waits ([7c707db](https://github.com/enisdenjo/graphql-ws/commit/7c707db3c8c3b1991a7e1b95a225efd8d58d5615)), closes [#85](https://github.com/enisdenjo/graphql-ws/issues/85)
### Features
* **client:** `onNonLazyError` allows you to catch errors reported in non-lazy mode ([cd1e7df](https://github.com/enisdenjo/graphql-ws/commit/cd1e7df70ab63b59bbfac1354b8779173fb1f333))
## [3.0.2](https://github.com/enisdenjo/graphql-ws/compare/v3.0.1...v3.0.2) (2020-12-10)

@@ -2,0 +14,0 @@

@@ -53,2 +53,22 @@ /**

/**
* Used ONLY when the client is in non-lazy mode (`lazy = false`). When
* using this mode, the errors might have no sinks to report to; however,
* to avoid swallowing errors, consider using `onNonLazyError`, which will
* be called when either:
* - An unrecoverable error/close event occurs
* - Silent retry attempts have been exceeded
*
* After a client has errored out, it will NOT perform any automatic actions.
*
* The argument can be a websocket `CloseEvent` or an `Error`. To avoid bundling
* DOM types, you should derive and assert the correct type. When receiving:
* - A `CloseEvent`: retry attempts have been exceeded or the specific
* close event is labeled as fatal (read more in `retryAttempts`).
* - An `Error`: some internal issue has occured, all internal errors are
* fatal by nature.
*
* @default console.error
*/
onNonLazyError?: (errorOrCloseEvent: unknown) => void;
/**
* How long should the client wait before closing the socket after the last oparation has

@@ -55,0 +75,0 @@ * completed. This is meant to be used in combination with `lazy`. You might want to have

38

lib/client.js

@@ -14,3 +14,3 @@ "use strict";

function createClient(options) {
const { url, connectionParams, lazy = true, keepAlive = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) {
const { url, connectionParams, lazy = true, onNonLazyError = console.error, keepAlive = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) {
let retryDelay = 1000; // start with 1s delay

@@ -103,8 +103,12 @@ for (let i = 0; i < retries; i++) {

var _a;
// prevents too many recursive calls when reavaluating/re-connecting
if (callDepth > 10) {
throw new Error('Kept trying to connect but the socket never settled.');
if (callDepth) {
// prevents too many recursive calls when reavaluating/re-connecting
if (callDepth > 10) {
throw new Error('Kept trying to connect but the socket never settled.');
}
// wait a bit for socket state changes in recursive calls
await new Promise((resolve) => setTimeout(resolve, callDepth * 50));
}
// retry wait strategy only on root caller
if (state.retrying && callDepth === 0) {
// retry wait strategy for all callers
if (state.retrying) {
if (retryWaiting.length) {

@@ -120,2 +124,3 @@ // if others are waiting for retry, I'll wait too

await retryWait(state.retries);
state = Object.assign(Object.assign({}, state), { retrying: false, retries: state.retries + 1 });
// complete all waiting and clear the queue

@@ -127,6 +132,2 @@ while (retryWaiting.length) {

}
// if recursive call, wait a bit for socket change
if (callDepth) {
await new Promise((resolve) => setTimeout(resolve, callDepth * 50));
}
// socket already exists. can be ready or pending, check and behave accordingly

@@ -137,5 +138,4 @@ if (state.socket) {

// if the socket is not acknowledged, wait a bit and reavaluate
if (!state.acknowledged) {
if (!state.acknowledged)
return connect(cancellerRef, callDepth + 1);
}
return makeConnectReturn(state.socket, cancellerRef);

@@ -159,3 +159,3 @@ }

const socket = new WebSocketImpl(url, protocol_1.GRAPHQL_TRANSPORT_WS_PROTOCOL);
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket, retries: state.retries + (state.retrying ? 1 : 0) });
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket });
emitter.emit('connecting');

@@ -319,5 +319,11 @@ await new Promise((resolve, reject) => {

catch (errOrCloseEvent) {
// return if shouldnt try again
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
return;
try {
// return and report if shouldnt try again
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent);
}
catch (_a) {
// report thrown error, no further retries
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent);
}
}

@@ -324,0 +330,0 @@ }

{
"name": "graphql-ws",
"version": "3.0.2",
"version": "3.1.0",
"description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client",

@@ -64,3 +64,3 @@ "keywords": [

"@semantic-release/git": "^9.0.0",
"@types/jest": "^26.0.18",
"@types/jest": "^26.0.19",
"@types/ws": "^7.4.0",

@@ -79,4 +79,5 @@ "@typescript-eslint/eslint-plugin": "^4.9.1",

"semantic-release": "^17.3.0",
"tslib": "^2.0.3",
"typedoc": "^0.19.2",
"typedoc-plugin-markdown": "^3.1.0",
"typedoc-plugin-markdown": "^3.1.1",
"typescript": "^4.1.2",

@@ -83,0 +84,0 @@ "ws": "^7.4.1"

@@ -125,3 +125,3 @@ (function (global, factory) {

function createClient(options) {
const { url, connectionParams, lazy = true, keepAlive = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) {
const { url, connectionParams, lazy = true, onNonLazyError = console.error, keepAlive = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) {
let retryDelay = 1000; // start with 1s delay

@@ -214,8 +214,12 @@ for (let i = 0; i < retries; i++) {

var _a;
// prevents too many recursive calls when reavaluating/re-connecting
if (callDepth > 10) {
throw new Error('Kept trying to connect but the socket never settled.');
if (callDepth) {
// prevents too many recursive calls when reavaluating/re-connecting
if (callDepth > 10) {
throw new Error('Kept trying to connect but the socket never settled.');
}
// wait a bit for socket state changes in recursive calls
await new Promise((resolve) => setTimeout(resolve, callDepth * 50));
}
// retry wait strategy only on root caller
if (state.retrying && callDepth === 0) {
// retry wait strategy for all callers
if (state.retrying) {
if (retryWaiting.length) {

@@ -231,2 +235,3 @@ // if others are waiting for retry, I'll wait too

await retryWait(state.retries);
state = Object.assign(Object.assign({}, state), { retrying: false, retries: state.retries + 1 });
// complete all waiting and clear the queue

@@ -238,6 +243,2 @@ while (retryWaiting.length) {

}
// if recursive call, wait a bit for socket change
if (callDepth) {
await new Promise((resolve) => setTimeout(resolve, callDepth * 50));
}
// socket already exists. can be ready or pending, check and behave accordingly

@@ -248,5 +249,4 @@ if (state.socket) {

// if the socket is not acknowledged, wait a bit and reavaluate
if (!state.acknowledged) {
if (!state.acknowledged)
return connect(cancellerRef, callDepth + 1);
}
return makeConnectReturn(state.socket, cancellerRef);

@@ -270,3 +270,3 @@ }

const socket = new WebSocketImpl(url, GRAPHQL_TRANSPORT_WS_PROTOCOL);
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket, retries: state.retries + (state.retrying ? 1 : 0) });
state = Object.assign(Object.assign({}, state), { acknowledged: false, socket });
emitter.emit('connecting');

@@ -430,5 +430,11 @@ await new Promise((resolve, reject) => {

catch (errOrCloseEvent) {
// return if shouldnt try again
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
return;
try {
// return and report if shouldnt try again
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent);
}
catch (_a) {
// report thrown error, no further retries
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent);
}
}

@@ -435,0 +441,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,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 g=y,w=(()=>{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={disposed:!1,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(n&&await new Promise((e=>setTimeout(e,50*n))),b.socket)switch(b.socket.readyState){case g.OPEN:return b.acknowledged?x(b.socket,e):v(e,n+1);case g.CONNECTING:return v(e,n+1);case g.CLOSED:break;case g.CLOSING:return v(e,n+1);default:throw new Error(`Impossible ready state ${b.socket.readyState}`)}const c=new g(t,"graphql-transport-ws");return b=Object.assign(Object.assign({},b),{acknowledged:!1,socket:c,retries:b.retries+(b.retrying?1:0)}),w.emit("connecting"),await new Promise(((t,n)=>{let r=!1;e.current=()=>r=!0;const s=setTimeout((()=>{c.close(3408,"Waited 5 seconds but socket connect never settled")}),5e3);c.onclose=e=>(c.onclose=null,clearTimeout(s),b=Object.assign(Object.assign({},b),{acknowledged:!1,socket:null}),w.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=a(e.data);if(n.type!==i.ConnectionAck)throw new Error(`First message cannot be of type ${n.type}`);return clearTimeout(s),b=Object.assign(Object.assign({},b),{acknowledged:!0,socket:c,retrying:!1,retries:0}),w.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===g.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(b.disposed||1e3===e.code)return!1;if(3499===e.code)return!1;if(!s||b.retries>=s)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:w.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=a(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,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(!h(e))return}})().catch(t.error).then(t.complete),()=>{var e;null===(e=r.current)||void 0===e||e.call(r)}},dispose(){var e;b.disposed=!0,null===(e=b.socket)||void 0===e||e.close(1e3,"Normal Closure"),w.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 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,onNonLazyError:c=console.error,keepAlive:s=0,retryAttempts:u=5,retryWait:d=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:f,webSocketImpl:p,generateID:y=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 g;if(p){if(!("function"==typeof(m=p)&&"constructor"in m&&"CLOSED"in m&&"CLOSING"in m&&"CONNECTING"in m&&"OPEN"in m))throw new Error("Invalid WebSocket implementation provided");g=p}else"undefined"!=typeof WebSocket?g=WebSocket:"undefined"!=typeof global?g=global.WebSocket||global.MozWebSocket:"undefined"!=typeof window&&(g=window.WebSocket||window.MozWebSocket);var m;if(!g)throw new Error("WebSocket implementation missing");const w=g,b=(()=>{const e={connecting:(null==f?void 0:f.connecting)?[f.connecting]:[],connected:(null==f?void 0:f.connected)?[f.connected]:[],closed:(null==f?void 0:f.closed)?[f.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 v={disposed:!1,socket:null,acknowledged:!1,locks:0,retrying:!1,retries:0};const h=[];async function k(e,n=0){var r;if(n){if(n>10)throw new Error("Kept trying to connect but the socket never settled.");await new Promise((e=>setTimeout(e,50*n)))}if(v.retrying)if(h.length)await new Promise((e=>h.push(e)));else for(h.push((()=>{})),await d(v.retries),v=Object.assign(Object.assign({},v),{retrying:!1,retries:v.retries+1});h.length;)null===(r=h.pop())||void 0===r||r();if(v.socket)switch(v.socket.readyState){case w.OPEN:return v.acknowledged?x(v.socket,e):k(e,n+1);case w.CONNECTING:return k(e,n+1);case w.CLOSED:break;case w.CLOSING:return k(e,n+1);default:throw new Error(`Impossible ready state ${v.socket.readyState}`)}const c=new w(t,"graphql-transport-ws");return v=Object.assign(Object.assign({},v),{acknowledged:!1,socket:c}),b.emit("connecting"),await new Promise(((t,n)=>{let r=!1;e.current=()=>r=!0;const s=setTimeout((()=>{c.close(3408,"Waited 5 seconds but socket connect never settled")}),5e3);c.onclose=e=>(c.onclose=null,clearTimeout(s),v=Object.assign(Object.assign({},v),{acknowledged:!1,socket:null}),b.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=a(e.data);if(n.type!==i.ConnectionAck)throw new Error(`First message cannot be of type ${n.type}`);return clearTimeout(s),v=Object.assign(Object.assign({},v),{acknowledged:!0,socket:c,retrying:!1,retries:0}),b.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 c(n){return t.current=null,v.locks--,e.removeEventListener("close",c),r(n)}v.locks++,e.addEventListener("close",c),t.current=()=>(t.current=null,null==n||n(),v.locks--,v.locks||(s>0&&isFinite(s)?setTimeout((()=>{!v.locks&&e.OPEN&&e.close(1e3,"Normal Closure")}),s):e.close(1e3,"Normal Closure")),e.removeEventListener("close",c),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(v.disposed||1e3===e.code)return!1;if(3499===e.code)return!1;if(!u||v.retries>=u)throw e;return v.retrying=!0,!0}let C,O;return r||(async()=>{for(;;)try{const[,e]=await k({current:null});return void await e()}catch(e){try{if(!E(e))return null==c?void 0:c(e)}catch(t){return null==c?void 0:c(e)}}})(),{on:b.on,subscribe(e,t){const n=y();let o=!1;const r={current:null},c=({data:e})=>{const c=function(e){return e!==C&&(O=a(e),C=e),O}(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 k(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}})().catch(t.error).then(t.complete),()=>{var e;null===(e=r.current)||void 0===e||e.call(r)}},dispose(){var e;v.disposed=!0,null===(e=v.socket)||void 0===e||e.close(1e3,"Normal Closure"),b.reset()}}},Object.defineProperty(e,"__esModule",{value:!0})}));

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc