New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

react-dynamic-help

Package Overview
Dependencies
Maintainers
1
Versions
72
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-dynamic-help - npm Package Compare versions

Comparing version 0.5.0 to 0.7.0

lib/es5/storage.d.ts

6

lib/es5/components/HelpController.d.ts
import * as React from "react";
import { AppTargetsState, SystemState, TargetId, ItemId, AppApiSetter, FlowId, TargetItemHelpers } from "..";
import { AppTargetsState, SystemState, TargetId, ItemId, AppApiSetter, FlowId, TargetItemHelpers, DynamicHelpStorageAPI } from "..";
declare type HelpControllerProps = {
provideControllerApi: AppApiSetter;
storage: DynamicHelpStorageAPI;
children: JSX.Element | JSX.Element[];

@@ -24,2 +25,3 @@ };

systemState: SystemState;
propagateSystemState: () => void;
constructor(props: HelpControllerProps);

@@ -38,6 +40,8 @@ componentDidMount: () => void;

enableHelp: (enabled?: boolean) => void;
resetHelp: () => void;
addHelpFlow: (id: FlowId, showInitially: boolean) => void;
addHelpItem: (flowId: FlowId, itemId: ItemId, target: TargetId) => void;
signalItemDismissed: (itemId: ItemId) => void;
render(): JSX.Element;
}
export {};

63

lib/es5/components/HelpController.js

@@ -51,2 +51,9 @@ "use strict";

const __1 = require("..");
const _resetState = {
systemEnabled: true,
flows: {},
flowMap: {},
items: {},
itemMap: {},
};
/**

@@ -66,10 +73,9 @@ * The component that

// we accumulate multiple updates per render cycle (due to refs changing on render) here .
// .. the ultimate value of these ends up in this.state when that finally updates, to be passed out on the Context.
// .. the ultimate value of these ends up in this.state (and storage) when react finally updates the state,
// to be passed out on the Context.
this.appTargets = { targetItems: {} };
this.systemState = {
systemEnabled: true,
flows: {},
flowMap: {},
items: {},
itemMap: {},
this.systemState = _resetState;
this.propagateSystemState = () => {
this.props.storage.set("system-state", this.systemState);
this.setState({ systemState: this.systemState });
};

@@ -83,3 +89,7 @@ this.componentDidMount = () => {

enableHelp: this.enableHelp,
resetHelp: this.resetHelp,
});
this.systemState = this.props.storage.get("system-state", _resetState);
console.log("Initial state loaded:", this.systemState);
this.setState({ systemState: this.systemState });
};

@@ -134,3 +144,3 @@ //

state.flows[flowId] = flow;
this.setState({ systemState: state });
this.propagateSystemState();
}

@@ -141,9 +151,16 @@ });

console.log("Turning on flow", flow);
const initialItem = this.systemState.flows[flow].items[0];
this.systemState.flows[flow].visible = enabled;
this.setState({ systemState: this.systemState });
this.systemState.items[initialItem].visible = enabled;
this.systemState.flows[flow].activeItem = 0;
this.propagateSystemState();
};
this.enableHelp = (enabled = true) => {
this.systemState.systemEnabled = enabled;
this.setState({ systemState: this.systemState });
this.propagateSystemState();
};
this.resetHelp = () => {
this.systemState = _resetState;
this.propagateSystemState();
};
//

@@ -162,3 +179,3 @@ // API for Help Flows and Help Items to interact with systemState.

};
this.setState({ systemState: this.systemState });
this.propagateSystemState();
}

@@ -177,8 +194,25 @@ };

if (!this.systemState.itemMap[target]) {
this.systemState.itemMap[target] = new Set();
this.systemState.itemMap[target] = [];
}
this.systemState.itemMap[target].add(itemId);
this.setState({ systemState: this.systemState });
if (!this.systemState.itemMap[target].includes(itemId)) {
this.systemState.itemMap[target].push(itemId);
}
this.propagateSystemState();
}
};
this.signalItemDismissed = (itemId) => {
console.log("signal dismissed called for", itemId);
const state = this.systemState; // just alias for ease of reading
const flowId = state.flowMap[itemId];
const flow = state.flows[flowId];
state.items[itemId].visible = false;
// turn off the flow and reset it
flow.activeItem = 0;
flow.visible = false;
const initialItemId = flow.items[0];
state.items[initialItemId].visible = true;
console.log("Reset flow", flowId);
state.flows[flowId] = flow;
this.propagateSystemState();
};
this.state = {

@@ -197,2 +231,3 @@ appTargetsState: this.appTargets,

addHelpItem: this.addHelpItem,
signalItemDismissed: this.signalItemDismissed,
},

@@ -199,0 +234,0 @@ } }, { children: this.props.children })) }));

@@ -84,13 +84,12 @@ "use strict";

});
// The primary reason for this chicanery is to allow the user to not have to provide an id for the HelpItems,
// while also providing for the future when they will want to (to address it in an api).
// The primary reason for this chicanery is to allow the user to not have to provide an id for the HelpItems.
// A side effect is that we have to provide the Item's state to it via its props, because it doesn't necessarily
// know it's own id, and hence it can't look up its own state in the context via that id.
// It doesn't work to try naievly to provide the id to the child by inserting it on props, because we are using the
// absence of it on props to recognise we need to default it! (I guess we could add a separate prop for
// 'actualId' that info, if that sort of refactor becomes necessary)
// absence of it on props to recognise we need to default it! (I guess we could add a separate prop 'actualId',
// if that sort of refactor becomes necessary)
const children = React.useMemo(() => React.Children.toArray(props.children), [props.children]);
const childrenWithState = children.map((child, index) => {
const id = child.props.id || defaultId(flowId, child.props.target, index);
return React.cloneElement(child, Object.assign(Object.assign({}, child.props), { state: systemState === null || systemState === void 0 ? void 0 : systemState.items[id], flowState: systemState === null || systemState === void 0 ? void 0 : systemState.flows[flowId], systemEnabled: systemState === null || systemState === void 0 ? void 0 : systemState.systemEnabled }));
return React.cloneElement(child, Object.assign(Object.assign({}, child.props), { state: systemState === null || systemState === void 0 ? void 0 : systemState.items[id], flowState: systemState === null || systemState === void 0 ? void 0 : systemState.flows[flowId], systemEnabled: systemState === null || systemState === void 0 ? void 0 : systemState.systemEnabled, signalDismissed: () => api.signalItemDismissed(id) }));
});

@@ -97,0 +96,0 @@ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: childrenWithState });

@@ -9,2 +9,3 @@ import "./HelpItem.css";

margin?: string;
layout?: "left" | "right";
id?: HelpTypes.ItemId;

@@ -14,2 +15,3 @@ state?: HelpTypes.ItemState;

systemEnabled?: boolean;
signalDismissed?: () => void;
children: React.ReactNode;

@@ -16,0 +18,0 @@ };

@@ -160,4 +160,16 @@ "use strict";

}
// Now we make sure that the dismiss button is in a sensible place, with a sensible margin,
// unless they specified it...
let layout = "right";
if (props.layout) {
layout = props.layout;
}
else if (position.includes("left")) {
layout = "left";
}
const dismissStyle = layout === "right"
? "rdh-dismiss-margin-left"
: "rdh-dismiss-margin-right";
//console.log("HelpItem render", props.state.target);
return ReactDOM.createPortal((0, jsx_runtime_1.jsx)("div", Object.assign({ className: "rdh-help-item rdh-help-item-custom", id: props.id, style: Object.assign({ position: "absolute", margin: props.margin }, itemPosition) }, { children: props.children })), document.body);
return ReactDOM.createPortal((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: "rdh-help-item", id: props.id, style: Object.assign({ position: "absolute", margin: props.margin, flexDirection: layout === "right" ? "row" : "row-reverse" }, itemPosition) }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ className: "rdh-help-item-custom" }, { children: props.children })), (0, jsx_runtime_1.jsx)("span", Object.assign({ className: `rdh-help-item-dismiss ${dismissStyle}`, onClick: props.signalDismissed }, { children: "\u2612" }))] })), document.body);
}

@@ -164,0 +176,0 @@ else {

/// <reference types="react" />
import { DynamicHelpStorageAPI } from "..";
declare type HelpProviderProps = {
storageApi?: DynamicHelpStorageAPI;
children: JSX.Element | JSX.Element[];

@@ -17,3 +19,3 @@ };

*/
export declare const HelpProvider: (props: HelpProviderProps) => JSX.Element;
export declare const HelpProvider: ({ storageApi, ...props }: HelpProviderProps) => JSX.Element;
export {};

@@ -25,2 +25,13 @@ "use strict";

};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -64,3 +75,4 @@ exports.HelpProvider = void 0;

*/
const HelpProvider = (props) => {
const HelpProvider = (_a) => {
var { storageApi = __1.StorageApi } = _a, props = __rest(_a, ["storageApi"]);
const [app, ...helpFlows] = React.Children.toArray(props.children);

@@ -78,3 +90,3 @@ // Here we store the Contoller API functions on our state, when they provided to us by the HelpController...

ref: (target) => {
console.log("Info: registration of target before controller initialied:", id, target);
console.log("Info: registration of target before controller initialised:", id, target);
},

@@ -94,2 +106,5 @@ used: () => {

},
resetHelp: () => {
console.log("Info: App signalled help-reset before controller initialized.");
},
});

@@ -103,4 +118,4 @@ /**

};
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(__1.ApiProvider, Object.assign({ value: controllerApi }, { children: app })), (0, jsx_runtime_1.jsx)(__1.HelpController, Object.assign({ provideControllerApi: provideControllerApi }, { children: helpFlows }))] }));
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(__1.ApiProvider, Object.assign({ value: controllerApi }, { children: app })), (0, jsx_runtime_1.jsx)(__1.HelpController, Object.assign({ provideControllerApi: provideControllerApi, storage: storageApi }, { children: helpFlows }))] }));
};
exports.HelpProvider = HelpProvider;

@@ -14,2 +14,3 @@ export declare type FlowId = string;

enableHelp: (enable: boolean) => void;
resetHelp: () => void;
};

@@ -44,2 +45,3 @@ export declare type TargetItemHelpers = {

addHelpItem: RegisterItem;
signalItemDismissed: (item: ItemId) => void;
};

@@ -77,3 +79,3 @@ export declare type AppTargetsState = {

declare type ItemMap = {
[target: TargetId]: Set<ItemId>;
[target: TargetId]: ItemId[];
};

@@ -80,0 +82,0 @@ /**

export * from "./DynamicHelpTypes";
export * from "./DynamicHelp";
export * from "./components";
export * from "./storage";

@@ -41,1 +41,2 @@ "use strict";

__exportStar(require("./components"), exports);
__exportStar(require("./storage"), exports);
import * as React from "react";
import { AppTargetsState, SystemState, TargetId, ItemId, AppApiSetter, FlowId, TargetItemHelpers } from "..";
import { AppTargetsState, SystemState, TargetId, ItemId, AppApiSetter, FlowId, TargetItemHelpers, DynamicHelpStorageAPI } from "..";
declare type HelpControllerProps = {
provideControllerApi: AppApiSetter;
storage: DynamicHelpStorageAPI;
children: JSX.Element | JSX.Element[];

@@ -24,2 +25,3 @@ };

systemState: SystemState;
propagateSystemState: () => void;
constructor(props: HelpControllerProps);

@@ -38,6 +40,8 @@ componentDidMount: () => void;

enableHelp: (enabled?: boolean) => void;
resetHelp: () => void;
addHelpFlow: (id: FlowId, showInitially: boolean) => void;
addHelpItem: (flowId: FlowId, itemId: ItemId, target: TargetId) => void;
signalItemDismissed: (itemId: ItemId) => void;
render(): JSX.Element;
}
export {};

@@ -25,2 +25,9 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";

import { SystemContextProvider, } from "..";
const _resetState = {
systemEnabled: true,
flows: {},
flowMap: {},
items: {},
itemMap: {},
};
/**

@@ -40,10 +47,9 @@ * The component that

// we accumulate multiple updates per render cycle (due to refs changing on render) here .
// .. the ultimate value of these ends up in this.state when that finally updates, to be passed out on the Context.
// .. the ultimate value of these ends up in this.state (and storage) when react finally updates the state,
// to be passed out on the Context.
this.appTargets = { targetItems: {} };
this.systemState = {
systemEnabled: true,
flows: {},
flowMap: {},
items: {},
itemMap: {},
this.systemState = _resetState;
this.propagateSystemState = () => {
this.props.storage.set("system-state", this.systemState);
this.setState({ systemState: this.systemState });
};

@@ -57,3 +63,7 @@ this.componentDidMount = () => {

enableHelp: this.enableHelp,
resetHelp: this.resetHelp,
});
this.systemState = this.props.storage.get("system-state", _resetState);
console.log("Initial state loaded:", this.systemState);
this.setState({ systemState: this.systemState });
};

@@ -108,3 +118,3 @@ //

state.flows[flowId] = flow;
this.setState({ systemState: state });
this.propagateSystemState();
}

@@ -115,9 +125,16 @@ });

console.log("Turning on flow", flow);
const initialItem = this.systemState.flows[flow].items[0];
this.systemState.flows[flow].visible = enabled;
this.setState({ systemState: this.systemState });
this.systemState.items[initialItem].visible = enabled;
this.systemState.flows[flow].activeItem = 0;
this.propagateSystemState();
};
this.enableHelp = (enabled = true) => {
this.systemState.systemEnabled = enabled;
this.setState({ systemState: this.systemState });
this.propagateSystemState();
};
this.resetHelp = () => {
this.systemState = _resetState;
this.propagateSystemState();
};
//

@@ -136,3 +153,3 @@ // API for Help Flows and Help Items to interact with systemState.

};
this.setState({ systemState: this.systemState });
this.propagateSystemState();
}

@@ -151,8 +168,25 @@ };

if (!this.systemState.itemMap[target]) {
this.systemState.itemMap[target] = new Set();
this.systemState.itemMap[target] = [];
}
this.systemState.itemMap[target].add(itemId);
this.setState({ systemState: this.systemState });
if (!this.systemState.itemMap[target].includes(itemId)) {
this.systemState.itemMap[target].push(itemId);
}
this.propagateSystemState();
}
};
this.signalItemDismissed = (itemId) => {
console.log("signal dismissed called for", itemId);
const state = this.systemState; // just alias for ease of reading
const flowId = state.flowMap[itemId];
const flow = state.flows[flowId];
state.items[itemId].visible = false;
// turn off the flow and reset it
flow.activeItem = 0;
flow.visible = false;
const initialItemId = flow.items[0];
state.items[initialItemId].visible = true;
console.log("Reset flow", flowId);
state.flows[flowId] = flow;
this.propagateSystemState();
};
this.state = {

@@ -171,2 +205,3 @@ appTargetsState: this.appTargets,

addHelpItem: this.addHelpItem,
signalItemDismissed: this.signalItemDismissed,
},

@@ -173,0 +208,0 @@ } }, { children: this.props.children })) }));

@@ -58,15 +58,14 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";

});
// The primary reason for this chicanery is to allow the user to not have to provide an id for the HelpItems,
// while also providing for the future when they will want to (to address it in an api).
// The primary reason for this chicanery is to allow the user to not have to provide an id for the HelpItems.
// A side effect is that we have to provide the Item's state to it via its props, because it doesn't necessarily
// know it's own id, and hence it can't look up its own state in the context via that id.
// It doesn't work to try naievly to provide the id to the child by inserting it on props, because we are using the
// absence of it on props to recognise we need to default it! (I guess we could add a separate prop for
// 'actualId' that info, if that sort of refactor becomes necessary)
// absence of it on props to recognise we need to default it! (I guess we could add a separate prop 'actualId',
// if that sort of refactor becomes necessary)
const children = React.useMemo(() => React.Children.toArray(props.children), [props.children]);
const childrenWithState = children.map((child, index) => {
const id = child.props.id || defaultId(flowId, child.props.target, index);
return React.cloneElement(child, Object.assign(Object.assign({}, child.props), { state: systemState === null || systemState === void 0 ? void 0 : systemState.items[id], flowState: systemState === null || systemState === void 0 ? void 0 : systemState.flows[flowId], systemEnabled: systemState === null || systemState === void 0 ? void 0 : systemState.systemEnabled }));
return React.cloneElement(child, Object.assign(Object.assign({}, child.props), { state: systemState === null || systemState === void 0 ? void 0 : systemState.items[id], flowState: systemState === null || systemState === void 0 ? void 0 : systemState.flows[flowId], systemEnabled: systemState === null || systemState === void 0 ? void 0 : systemState.systemEnabled, signalDismissed: () => api.signalItemDismissed(id) }));
});
return _jsx(_Fragment, { children: childrenWithState });
};

@@ -9,2 +9,3 @@ import "./HelpItem.css";

margin?: string;
layout?: "left" | "right";
id?: HelpTypes.ItemId;

@@ -14,2 +15,3 @@ state?: HelpTypes.ItemState;

systemEnabled?: boolean;
signalDismissed?: () => void;
children: React.ReactNode;

@@ -16,0 +18,0 @@ };

@@ -12,3 +12,3 @@ var __rest = (this && this.__rest) || function (s, e) {

};
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/*

@@ -135,4 +135,16 @@ Copyright (c) 2022 Martin Gregory.

}
// Now we make sure that the dismiss button is in a sensible place, with a sensible margin,
// unless they specified it...
let layout = "right";
if (props.layout) {
layout = props.layout;
}
else if (position.includes("left")) {
layout = "left";
}
const dismissStyle = layout === "right"
? "rdh-dismiss-margin-left"
: "rdh-dismiss-margin-right";
//console.log("HelpItem render", props.state.target);
return ReactDOM.createPortal(_jsx("div", Object.assign({ className: "rdh-help-item rdh-help-item-custom", id: props.id, style: Object.assign({ position: "absolute", margin: props.margin }, itemPosition) }, { children: props.children })), document.body);
return ReactDOM.createPortal(_jsxs("div", Object.assign({ className: "rdh-help-item", id: props.id, style: Object.assign({ position: "absolute", margin: props.margin, flexDirection: layout === "right" ? "row" : "row-reverse" }, itemPosition) }, { children: [_jsx("div", Object.assign({ className: "rdh-help-item-custom" }, { children: props.children })), _jsx("span", Object.assign({ className: `rdh-help-item-dismiss ${dismissStyle}`, onClick: props.signalDismissed }, { children: "\u2612" }))] })), document.body);
}

@@ -139,0 +151,0 @@ else {

/// <reference types="react" />
import { DynamicHelpStorageAPI } from "..";
declare type HelpProviderProps = {
storageApi?: DynamicHelpStorageAPI;
children: JSX.Element | JSX.Element[];

@@ -17,3 +19,3 @@ };

*/
export declare const HelpProvider: (props: HelpProviderProps) => JSX.Element;
export declare const HelpProvider: ({ storageApi, ...props }: HelpProviderProps) => JSX.Element;
export {};

@@ -0,1 +1,12 @@

var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";

@@ -24,3 +35,3 @@ /*

import * as React from "react";
import { ApiProvider, HelpController, } from "..";
import { ApiProvider, HelpController, StorageApi, } from "..";
/**

@@ -38,3 +49,4 @@ * The component that connects the help flows to the app.

*/
export const HelpProvider = (props) => {
export const HelpProvider = (_a) => {
var { storageApi = StorageApi } = _a, props = __rest(_a, ["storageApi"]);
const [app, ...helpFlows] = React.Children.toArray(props.children);

@@ -52,3 +64,3 @@ // Here we store the Contoller API functions on our state, when they provided to us by the HelpController...

ref: (target) => {
console.log("Info: registration of target before controller initialied:", id, target);
console.log("Info: registration of target before controller initialised:", id, target);
},

@@ -68,2 +80,5 @@ used: () => {

},
resetHelp: () => {
console.log("Info: App signalled help-reset before controller initialized.");
},
});

@@ -77,3 +92,3 @@ /**

};
return (_jsxs(_Fragment, { children: [_jsx(ApiProvider, Object.assign({ value: controllerApi }, { children: app })), _jsx(HelpController, Object.assign({ provideControllerApi: provideControllerApi }, { children: helpFlows }))] }));
return (_jsxs(_Fragment, { children: [_jsx(ApiProvider, Object.assign({ value: controllerApi }, { children: app })), _jsx(HelpController, Object.assign({ provideControllerApi: provideControllerApi, storage: storageApi }, { children: helpFlows }))] }));
};

@@ -14,2 +14,3 @@ export declare type FlowId = string;

enableHelp: (enable: boolean) => void;
resetHelp: () => void;
};

@@ -44,2 +45,3 @@ export declare type TargetItemHelpers = {

addHelpItem: RegisterItem;
signalItemDismissed: (item: ItemId) => void;
};

@@ -77,3 +79,3 @@ export declare type AppTargetsState = {

declare type ItemMap = {
[target: TargetId]: Set<ItemId>;
[target: TargetId]: ItemId[];
};

@@ -80,0 +82,0 @@ /**

export * from "./DynamicHelpTypes";
export * from "./DynamicHelp";
export * from "./components";
export * from "./storage";

@@ -25,1 +25,2 @@ /*

export * from "./components";
export * from "./storage";
{
"name": "react-dynamic-help",
"description": "React Dynamic Help - Create flows of helpful popups in your React App",
"version": "0.5.0",
"version": "0.7.0",
"repository": {

@@ -6,0 +6,0 @@ "type": "git",

@@ -10,3 +10,3 @@ # react-dynamic-help

V 0.5.0 - Basic implementation of the concept.
V 0.7.0 - Basic implementation of the concept, enough features to actually use.

@@ -62,3 +62,3 @@ Demo at https://github.com/GreenAsJade/react-dynamic-help-demo

</HelpFlow>
<HelpFlow id="basic" showInitially={false}>

@@ -76,3 +76,3 @@ <HelpItem target="add-stat-button">

<div>OK?</div>
// …

@@ -85,6 +85,2 @@ ```

- Be able to dismiss help items without "doing the thing".
- Save the help system state. That's important for a real app :)
- Support a no-op pseudo Item somehow, so you can have a break in a flow, with a resume.

@@ -100,2 +96,6 @@ - Wouldn't this just amount to "start another flow?"

- Be able to target items via css selector as an alternative to ref
- Don't know if it is even possible.
- Note that this would not support registration based features, but would be even less imact on the app.
- Fix up the packaging

@@ -102,0 +102,0 @@

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