Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@anephenix/sarus

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@anephenix/sarus - npm Package Compare versions

Comparing version 0.4.6 to 0.5.0

__tests__/index/state.test.ts

3

__tests__/helpers/delay.ts
import { module } from "window-or-global";
export const delay = (duration: number) =>
new Promise((resolve) => setTimeout(resolve, duration));
new Promise((resolve) => setTimeout(resolve, duration));

@@ -21,2 +21,22 @@ // File Dependencies

it("should correctly validate invalid WebSocket URLs", () => {
// Testing with jest-websocket-mock will not give us a TypeError here.
// We re-throw the error therefore. Testing it in a browser we can
// see that a TypeError is handled correctly.
expect(() => {
new Sarus({ url: "invalid-url" });
}).toThrow("invalid");
expect(() => {
new Sarus({ url: "http://wrong-protocol" });
}).toThrow("have protocol");
expect(() => {
new Sarus({ url: "https://also-wrong-protocol" });
}).toThrow("have protocol");
new Sarus({ url: "ws://this-will-pass" });
new Sarus({ url: "wss://this-too-shall-pass" });
});
it("should set the WebSocket protocols value to an empty string if nothing is passed", async () => {

@@ -44,3 +64,3 @@ const sarus: Sarus = new Sarus({ url });

expect(sarus.ws?.binaryType).toBe(binaryTypes[0]);
})
});
});

@@ -20,4 +20,4 @@ // File Dependencies

close: [mockClose],
error: [mockError]
}
error: [mockError],
},
});

@@ -55,4 +55,4 @@ await server.connected;

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -70,4 +70,4 @@ expect(sarus.eventListeners.open).toEqual([]);

eventListeners: {
message: [myFunc]
}
message: [myFunc],
},
});

@@ -88,4 +88,4 @@ expect(sarus.eventListeners.open).toEqual([]);

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -108,4 +108,4 @@

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -127,4 +127,4 @@

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -150,4 +150,4 @@ await server.connected;

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -167,4 +167,4 @@

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -185,4 +185,4 @@

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -205,4 +205,4 @@

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -225,4 +225,4 @@

close: [],
message: [myFunc]
}
message: [myFunc],
},
});

@@ -229,0 +229,0 @@

@@ -52,3 +52,3 @@ // File Dependencies

storageType: any,
sarusConfig: SarusClassParams
sarusConfig: SarusClassParams,
) => {

@@ -88,3 +88,3 @@ storageType.clear();

storageType: "local",
storageKey: "sarusWS"
storageKey: "sarusWS",
});

@@ -106,3 +106,3 @@ expect(sarus.storageKey).toBe("sarusWS");

const processExistingMessagesFromStorage = async (
sarusConfig: SarusClassParams
sarusConfig: SarusClassParams,
) => {

@@ -133,3 +133,3 @@ const sarusTwo = retrieveMessagesFromStorage(sarusConfig);

storageType: "session",
reconnectAutomatically: true
reconnectAutomatically: true,
});

@@ -142,5 +142,5 @@ });

storageType: "local",
reconnectAutomatically: true
reconnectAutomatically: true,
});
});
});
// File Dependencies
import Sarus from "../../src/index";
import { WS } from "jest-websocket-mock";
import { delay} from '../helpers/delay';
import { delay } from "../helpers/delay";

@@ -15,5 +15,7 @@ const url: string = "ws://localhost:1234";

sarus.connect = mockConnect;
const setTimeout = jest.spyOn(window, "setTimeout");
server.close();
await delay(1000);
expect(sarus.connect).toBeCalled();
expect(setTimeout).toHaveBeenCalledTimes(2);
});

@@ -26,3 +28,3 @@

url,
reconnectAutomatically: false
reconnectAutomatically: false,
});

@@ -35,4 +37,4 @@ await server.connected;

describe('if a websocket is closed and meant to reconnect automatically', () => {
it('should remove all eventListeners on the closed websocket before reconnecting', async () => {
describe("if a websocket is closed and meant to reconnect automatically", () => {
it("should remove all eventListeners on the closed websocket before reconnecting", async () => {
const server: WS = new WS(url);

@@ -53,9 +55,7 @@ const mockReconnect = jest.fn();

// @ts-ignore
expect(sarus.ws?.listeners?.error?.length).toBe(0);
expect(sarus.ws?.listeners?.error?.length).toBe(0);
// @ts-ignore
expect(sarus.ws?.listeners?.close?.length).toBe(0);
expect(sarus.ws?.listeners?.close?.length).toBe(0);
});
})
});
});

@@ -8,3 +8,3 @@ // File Dependencies

const condition = (func: Function) => {
return new Promise<void>(resolve => {
return new Promise<void>((resolve) => {
let check: Function;

@@ -11,0 +11,0 @@ check = () => {

# CHANGELOG
### 0.5.0 - Saturday 27th April, 2024
- Tidied up some missplaced development dependencies
- Track current connection state internally
- Made retryConnectionDelay a required parameter, and restricted type to number
- Cleaned up running commands without using npx
- Validate URLS when constructing a Sarus instance
- Updated License, authors and populated contributors
- Changed node build targets to latest LTS and current (18 & 20)
- Added Prettier Github Workflow
- Formatted code using Prettier
- Updated dependencies
### 0.4.6 - Sunday 18th June, 2023

@@ -4,0 +17,0 @@

@@ -23,3 +23,3 @@ import { PartialEventListenersInterface, EventListenersInterface } from "./lib/validators";

* @param {number} param0.retryProcessTimePeriod - An optional number for how long the time period between retrying to send a messgae to a WebSocket server should be
* @param {boolean|number} param0.retryConnectionDelay - An optional parameter for whether to delay WebSocket reconnection attempts by a time period. If true, the delay is 1000ms, otherwise it is the number passed
* @param {number} param0.retryConnectionDelay - A parameter for the amount of time to delay a reconnection attempt by, in miliseconds.
* @param {string} param0.storageType - An optional string specifying the type of storage to use for persisting messages in the message queue

@@ -30,3 +30,3 @@ * @param {string} param0.storageKey - An optional string specifying the key used to store the messages data against in sessionStorage/localStorage

export default class Sarus {
url: string;
url: URL;
binaryType?: BinaryType;

@@ -37,3 +37,3 @@ protocols?: string | Array<string>;

reconnectAutomatically?: boolean;
retryConnectionDelay?: boolean | number;
retryConnectionDelay: number;
storageType: string;

@@ -43,2 +43,3 @@ storageKey: string;

ws: WebSocket | undefined;
state: "connecting" | "connected" | "disconnected" | "closed";
constructor(props: SarusClassParams);

@@ -45,0 +46,0 @@ /**

@@ -55,2 +55,26 @@ "use strict";

};
var validateWebSocketUrl = function (rawUrl) {
var url;
try {
// Alternatively, we can also check with URL.canParse(), but since we need
// the URL object anyway to validate the protocol, we go ahead and parse it
// here.
url = new URL(rawUrl);
}
catch (e) {
// TypeError, as specified by WHATWG URL Standard:
// https://url.spec.whatwg.org/#url-class (see constructor steps)
if (!(e instanceof TypeError)) {
throw e;
}
// Untested - our URL mock does not give us an instance of TypeError
var message = e.message;
throw new Error("The WebSocket URL is not valid: ".concat(message));
}
var protocol = url.protocol;
if (!constants_1.ALLOWED_PROTOCOLS.includes(protocol)) {
throw new Error("Expected the WebSocket URL to have protocol 'ws:' or 'wss:', got '".concat(protocol, "' instead."));
}
return url;
};
/**

@@ -66,3 +90,3 @@ * The Sarus client class

* @param {number} param0.retryProcessTimePeriod - An optional number for how long the time period between retrying to send a messgae to a WebSocket server should be
* @param {boolean|number} param0.retryConnectionDelay - An optional parameter for whether to delay WebSocket reconnection attempts by a time period. If true, the delay is 1000ms, otherwise it is the number passed
* @param {number} param0.retryConnectionDelay - A parameter for the amount of time to delay a reconnection attempt by, in miliseconds.
* @param {string} param0.storageType - An optional string specifying the type of storage to use for persisting messages in the message queue

@@ -74,8 +98,32 @@ * @param {string} param0.storageKey - An optional string specifying the key used to store the messages data against in sessionStorage/localStorage

function Sarus(props) {
var _a;
/*
* Track the current state of the Sarus object. See the diagram below.
*
* reconnect() ┌──────┐
* ┌───────────────────────────────│closed│
* │ └──────┘
* │ ▲
* ▼ │ this.ws.onclose
* ┌──────────┐ this.ws.onopen ┌───┴─────┐
* │connecting├───────────────────────►│connected│
* └──────────┘ └───┬─────┘
* ▲ │ disconnect()
* │ ▼
* │ reconnect() ┌────────────┐
* └─────────────────────────────┤disconnected│
* └────────────┘
*
* connect(), disconnect() are generally called by the user
*
* this.reconnect() is called internally when automatic reconnection is
* enabled, but can also be called by the user
*/
this.state = "connecting";
// Extract the properties that are passed to the class
var url = props.url, binaryType = props.binaryType, protocols = props.protocols, _a = props.eventListeners, eventListeners = _a === void 0 ? constants_1.DEFAULT_EVENT_LISTENERS_OBJECT : _a, reconnectAutomatically = props.reconnectAutomatically, retryProcessTimePeriod = props.retryProcessTimePeriod, // TODO - write a test case to check this
retryConnectionDelay = props.retryConnectionDelay, _b = props.storageType, storageType = _b === void 0 ? "memory" : _b, _c = props.storageKey, storageKey = _c === void 0 ? "sarus" : _c;
var url = props.url, binaryType = props.binaryType, protocols = props.protocols, _b = props.eventListeners, eventListeners = _b === void 0 ? constants_1.DEFAULT_EVENT_LISTENERS_OBJECT : _b, reconnectAutomatically = props.reconnectAutomatically, retryProcessTimePeriod = props.retryProcessTimePeriod, // TODO - write a test case to check this
retryConnectionDelay = props.retryConnectionDelay, _c = props.storageType, storageType = _c === void 0 ? "memory" : _c, _d = props.storageKey, storageKey = _d === void 0 ? "sarus" : _d;
this.eventListeners = this.auditEventListeners(eventListeners);
// Sets the WebSocket server url for the client to connect to.
this.url = url;
this.url = validateWebSocketUrl(url);
// Sets the binaryType of the data being sent over the connection

@@ -102,3 +150,11 @@ this.binaryType = binaryType;

*/
this.retryConnectionDelay = retryConnectionDelay || true;
// Either retryConnectionDelay is
// undefined => default to 1000
// true => default to 1000
// false => default to 1000
// a number => set it to that number
this.retryConnectionDelay =
(_a = (typeof retryConnectionDelay === "boolean"
? undefined
: retryConnectionDelay)) !== null && _a !== void 0 ? _a : 1000;
/*

@@ -113,3 +169,3 @@ Sets the storage type for the messages in the message queue. By default

used as the key for calls to sessionStorage/localStorage getItem/setItem.
It can also be configured by the developer during initialization.

@@ -122,3 +178,3 @@ */

that might have been persisted there.
Say the user has done a page refresh, we want to make sure that messages

@@ -226,2 +282,3 @@ that were meant to be sent to the server make their way there.

Sarus.prototype.connect = function () {
this.state = "connecting";
this.ws = new WebSocket(this.url, this.protocols);

@@ -239,15 +296,3 @@ this.setBinaryType();

var retryConnectionDelay = self.retryConnectionDelay;
switch (typeof retryConnectionDelay) {
case "boolean":
if (retryConnectionDelay) {
setTimeout(self.connect, 1000);
}
else {
self.connect(); // NOTE - this line is not tested
}
break;
case "number":
setTimeout(self.connect, retryConnectionDelay);
break;
}
setTimeout(self.connect, retryConnectionDelay);
};

@@ -261,2 +306,3 @@ /**

Sarus.prototype.disconnect = function (overrideDisableReconnect) {
this.state = "disconnected";
var self = this;

@@ -379,3 +425,7 @@ // We do this to prevent automatic reconnections;

self.eventListeners[eventName].forEach(function (f) { return f(e); });
if (eventName === "close" && self.reconnectAutomatically) {
if (eventName === "open") {
self.state = "connected";
}
else if (eventName === "close" && self.reconnectAutomatically) {
self.state = "closed";
self.removeEventListeners();

@@ -382,0 +432,0 @@ self.reconnect();

import { EventListenersInterface } from "./validators";
export declare const ALLOWED_PROTOCOLS: Array<string>;
/**

@@ -3,0 +4,0 @@ * A definitive list of events for a WebSocket client to listen on

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_EVENT_LISTENERS_OBJECT = exports.DATA_STORAGE_TYPES = exports.WS_EVENT_NAMES = void 0;
exports.DEFAULT_EVENT_LISTENERS_OBJECT = exports.DATA_STORAGE_TYPES = exports.WS_EVENT_NAMES = exports.ALLOWED_PROTOCOLS = void 0;
exports.ALLOWED_PROTOCOLS = ["ws:", "wss:"];
/**

@@ -13,3 +14,3 @@ * A definitive list of events for a WebSocket client to listen on

"message",
"error"
"error",
];

@@ -36,3 +37,3 @@ /**

error: [],
close: []
close: [],
};
{
"name": "@anephenix/sarus",
"version": "0.4.6",
"version": "0.5.0",
"description": "A WebSocket JavaScript library",
"main": "dist/index.js",
"contributors": [
{
"name": "Paul Jensen",
"email": "paulbjensen@gmail.com",
"url": "https://paulbjensen.co.uk"
},
{
"name": "Chris Lajoie",
"email": "chris@ettaviation.com"
},
{
"name": "Justus Perlwitz",
"email": "justus@jwpconsulting.net",
"url": "https://www.jwpconsulting.net"
}
],
"devDependencies": {

@@ -11,11 +27,14 @@ "@babel/parser": "^7.22.5",

"@types/websocket": "^1.0.5",
"@types/window-or-global": "^1.0.6",
"coveralls": "^3.1.1",
"dom-storage": "^2.1.0",
"ip-regex": "^5.0.0",
"jest": "29.5.0",
"jest": "29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-websocket-mock": "^2.4.0",
"jsdoc": "^4.0.2",
"jsdom": "^22.1.0",
"jsdom": "^24.0.0",
"mock-socket": "^9.2.1",
"npm-upgrade": "^3.1.0",
"prettier": "^3.0.3",
"ts-jest": "^29.1.0",

@@ -25,6 +44,8 @@ "typescript": "^5.1.3"

"scripts": {
"build": "npx tsc --project tsconfig.json",
"watch": "npx tsc --project tsconfig.json --watch",
"cover": "npx jest --coverage --coverageReporters=text-lcov | coveralls",
"test": "npx jest --coverage"
"build": "tsc --project tsconfig.json",
"watch": "tsc --project tsconfig.json --watch",
"cover": "jest --coverage --coverageReporters=text-lcov | coveralls",
"test": "jest --coverage",
"prettier": "prettier src __tests__ --write",
"check-prettier": "prettier src __tests__ --check"
},

@@ -43,7 +64,3 @@ "repository": {

},
"homepage": "https://github.com/anephenix/sarus#readme",
"dependencies": {
"@types/window-or-global": "^1.0.4",
"jest-environment-jsdom": "^29.5.0"
}
"homepage": "https://github.com/anephenix/sarus#readme"
}
// File Dependencies
import { WS_EVENT_NAMES, DATA_STORAGE_TYPES, DEFAULT_EVENT_LISTENERS_OBJECT } from "./lib/constants";
import {
ALLOWED_PROTOCOLS,
WS_EVENT_NAMES,
DATA_STORAGE_TYPES,
DEFAULT_EVENT_LISTENERS_OBJECT,
} from "./lib/constants";
import { serialize, deserialize } from "./lib/dataTransformer";
import { PartialEventListenersInterface, EventListenersInterface } from "./lib/validators";
import {
PartialEventListenersInterface,
EventListenersInterface,
} from "./lib/validators";

@@ -42,2 +50,28 @@ interface StorageParams {

const validateWebSocketUrl = (rawUrl: string): URL => {
let url: URL;
try {
// Alternatively, we can also check with URL.canParse(), but since we need
// the URL object anyway to validate the protocol, we go ahead and parse it
// here.
url = new URL(rawUrl);
} catch (e) {
// TypeError, as specified by WHATWG URL Standard:
// https://url.spec.whatwg.org/#url-class (see constructor steps)
if (!(e instanceof TypeError)) {
throw e;
}
// Untested - our URL mock does not give us an instance of TypeError
const { message } = e;
throw new Error(`The WebSocket URL is not valid: ${message}`);
}
const { protocol } = url;
if (!ALLOWED_PROTOCOLS.includes(protocol)) {
throw new Error(
`Expected the WebSocket URL to have protocol 'ws:' or 'wss:', got '${protocol}' instead.`,
);
}
return url;
};
export interface SarusClassParams {

@@ -65,3 +99,3 @@ url: string;

* @param {number} param0.retryProcessTimePeriod - An optional number for how long the time period between retrying to send a messgae to a WebSocket server should be
* @param {boolean|number} param0.retryConnectionDelay - An optional parameter for whether to delay WebSocket reconnection attempts by a time period. If true, the delay is 1000ms, otherwise it is the number passed
* @param {number} param0.retryConnectionDelay - A parameter for the amount of time to delay a reconnection attempt by, in miliseconds.
* @param {string} param0.storageType - An optional string specifying the type of storage to use for persisting messages in the message queue

@@ -73,3 +107,3 @@ * @param {string} param0.storageKey - An optional string specifying the key used to store the messages data against in sessionStorage/localStorage

// Constructor params
url: string;
url: URL;
binaryType?: BinaryType;

@@ -80,3 +114,3 @@ protocols?: string | Array<string>;

reconnectAutomatically?: boolean;
retryConnectionDelay?: boolean | number;
retryConnectionDelay: number;
storageType: string;

@@ -88,2 +122,25 @@ storageKey: string;

ws: WebSocket | undefined;
/*
* Track the current state of the Sarus object. See the diagram below.
*
* reconnect() ┌──────┐
* ┌───────────────────────────────│closed│
* │ └──────┘
* │ ▲
* ▼ │ this.ws.onclose
* ┌──────────┐ this.ws.onopen ┌───┴─────┐
* │connecting├───────────────────────►│connected│
* └──────────┘ └───┬─────┘
* ▲ │ disconnect()
* │ ▼
* │ reconnect() ┌────────────┐
* └─────────────────────────────┤disconnected│
* └────────────┘
*
* connect(), disconnect() are generally called by the user
*
* this.reconnect() is called internally when automatic reconnection is
* enabled, but can also be called by the user
*/
state: "connecting" | "connected" | "disconnected" | "closed" = "connecting";

@@ -101,3 +158,3 @@ constructor(props: SarusClassParams) {

storageType = "memory",
storageKey = "sarus"
storageKey = "sarus",
} = props;

@@ -108,3 +165,3 @@

// Sets the WebSocket server url for the client to connect to.
this.url = url;
this.url = validateWebSocketUrl(url);

@@ -118,3 +175,3 @@ // Sets the binaryType of the data being sent over the connection

/*
When attempting to re-send a message when the WebSocket connection is
When attempting to re-send a message when the WebSocket connection is
not open, there is a retry process time period of 50ms. It can be set

@@ -134,10 +191,18 @@ to another value by the developer.

This handles whether to add a time delay to reconnecting the WebSocket
client. If true, a 1000ms delay is added. If a number, that number (as
client. If true, a 1000ms delay is added. If a number, that number (as
miliseconds) is used as the delay. Default is true.
*/
this.retryConnectionDelay = retryConnectionDelay || true;
// Either retryConnectionDelay is
// undefined => default to 1000
// true => default to 1000
// false => default to 1000
// a number => set it to that number
this.retryConnectionDelay =
(typeof retryConnectionDelay === "boolean"
? undefined
: retryConnectionDelay) ?? 1000;
/*
Sets the storage type for the messages in the message queue. By default
it is an in-memory option, but can also be set as 'session' for
it is an in-memory option, but can also be set as 'session' for
sessionStorage or 'local' for localStorage data persistence.

@@ -150,3 +215,3 @@ */

used as the key for calls to sessionStorage/localStorage getItem/setItem.
It can also be configured by the developer during initialization.

@@ -158,6 +223,6 @@ */

When initializing the client, if we are using sessionStorage/localStorage
for storing messages in the messageQueue, then we want to retrieve any
for storing messages in the messageQueue, then we want to retrieve any
that might have been persisted there.
Say the user has done a page refresh, we want to make sure that messages
Say the user has done a page refresh, we want to make sure that messages
that were meant to be sent to the server make their way there.

@@ -177,3 +242,3 @@

/*
/*
Gets the messages from the message queue.

@@ -261,3 +326,3 @@ */

};
const mergedEventListeners: EventListenersInterface = {

@@ -267,6 +332,5 @@ ...defaultEventListeners,

} as EventListenersInterface; // Type assertion added here
return mergedEventListeners;
}

@@ -277,2 +341,3 @@ /**

connect() {
this.state = "connecting";
this.ws = new WebSocket(this.url, this.protocols);

@@ -290,14 +355,3 @@ this.setBinaryType();

const { retryConnectionDelay } = self;
switch (typeof retryConnectionDelay) {
case "boolean":
if (retryConnectionDelay) {
setTimeout(self.connect, 1000);
} else {
self.connect(); // NOTE - this line is not tested
}
break;
case "number":
setTimeout(self.connect, retryConnectionDelay);
break;
}
setTimeout(self.connect, retryConnectionDelay);
}

@@ -312,2 +366,3 @@

disconnect(overrideDisableReconnect?: boolean) {
this.state = "disconnected";
const self = this;

@@ -330,3 +385,3 @@ // We do this to prevent automatic reconnections;

throw new Error(
`${eventFunc.name} has already been added to this event Listener`
`${eventFunc.name} has already been added to this event Listener`,
);

@@ -368,3 +423,3 @@ }

}
| undefined
| undefined,
) {

@@ -388,3 +443,3 @@ if (!existingFunc) {

eventFuncOrName: Function | string,
opts?: { doNotThrowError: boolean } | undefined
opts?: { doNotThrowError: boolean } | undefined,
) {

@@ -444,6 +499,9 @@ const existingFunc = this.findFunction(eventName, eventFuncOrName);

const self: any = this;
WS_EVENT_NAMES.forEach(eventName => {
WS_EVENT_NAMES.forEach((eventName) => {
self.ws[`on${eventName}`] = (e: Function) => {
self.eventListeners[eventName].forEach((f: Function) => f(e));
if (eventName === "close" && self.reconnectAutomatically) {
if (eventName === "open") {
self.state = "connected";
} else if (eventName === "close" && self.reconnectAutomatically) {
self.state = "closed";
self.removeEventListeners();

@@ -457,3 +515,3 @@ self.reconnect();

/**
* Removes the event listeners from a closed WebSocket instance, so that
* Removes the event listeners from a closed WebSocket instance, so that
* they are cleaned up

@@ -463,7 +521,7 @@ */

const self: any = this;
WS_EVENT_NAMES.forEach(eventName => {
WS_EVENT_NAMES.forEach((eventName) => {
if (self.ws.listeners && self.ws.listeners[eventName]) {
self.ws.listeners[eventName].forEach((iel:Function) => {
self.ws.listeners[eventName].forEach((iel: Function) => {
self.ws.removeEventListener(eventName, iel);
})
});
}

@@ -470,0 +528,0 @@ });

// Dependencies
import { EventListenersInterface } from "./validators";
export const ALLOWED_PROTOCOLS: Array<string> = ["ws:", "wss:"];
/**

@@ -13,3 +15,3 @@ * A definitive list of events for a WebSocket client to listen on

"message",
"error"
"error",
];

@@ -38,3 +40,3 @@

error: [],
close: []
};
close: [],
};

Sorry, the diff of this file is not supported yet

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