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

@iconify/react

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

@iconify/react - npm Package Compare versions

Comparing version 4.1.1 to 5.0.0-beta.1

43

dist/iconify.d.ts

@@ -0,6 +1,6 @@

/// <reference types="react" />
import { IconifyIcon } from '@iconify/types';
import { IconifyJSON } from '@iconify/types';
import { IconifyTransformations } from '@iconify/types';
import { default as React_2 } from 'react';
import type { RefAttributes } from 'react';
import type { SVGProps } from 'react';

@@ -76,8 +76,5 @@

*/
export declare const Icon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>;
export declare const Icon: IconComponentType;
/**
* Check if icon exists
*/
export declare function iconExists(name: string): boolean;
declare type IconComponentType = (props: IconProps) => JSX.Element;

@@ -212,2 +209,3 @@ /**

};
viewBox: SVGViewBox;
body: string;

@@ -265,2 +263,3 @@ }

id?: string;
ssr?: boolean;
onLoad?: IconifyIconOnLoad;

@@ -304,4 +303,10 @@ }

/**
* Check if icon exists
* Check if icon data is available
*/
iconLoaded: (name: string) => boolean;
/**
* Older, badly named, version of iconLoaded()
*
* @deprecated
*/
iconExists: (name: string) => boolean;

@@ -327,8 +332,13 @@ /**

/**
* Check if icon data is available
*/
declare function iconLoaded(name: string): boolean;
export { iconLoaded as iconExists }
export { iconLoaded }
/**
* Mix of icon properties and SVGSVGElement properties
*/
export declare type IconProps = IconifyElementProps & IconifyIconProps & ReactRefProp;
export declare type IconProps = IconifyElementProps & IconifyIconProps;
declare type IconRef = RefAttributes<SVGSVGElement>;
/**

@@ -339,3 +349,3 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font)

*/
export declare const InlineIcon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>;
export declare const InlineIcon: IconComponentType;

@@ -383,6 +393,2 @@ /**

declare interface ReactRefProp {
ref?: IconRef;
}
/**

@@ -423,2 +429,7 @@ * Configuration object

/**
* SVG viewBox: x, y, width, height
*/
declare type SVGViewBox = [x: number, y: number, width: number, height: number];
export { }
'use strict';
var React = require('react');
var react = require('react');
const matchIconName = /^[a-z0-9]+(-[a-z0-9]+)*$/;
const stringToIcon = (value, validate, allowSimpleName, provider = "") => {
const colonSeparated = value.split(":");
if (value.slice(0, 1) === "@") {
if (colonSeparated.length < 2 || colonSeparated.length > 3) {
return null;
}
provider = colonSeparated.shift().slice(1);
}
if (colonSeparated.length > 3 || !colonSeparated.length) {
return null;
}
if (colonSeparated.length > 1) {
const name2 = colonSeparated.pop();
const prefix = colonSeparated.pop();
const result = {
// Allow provider without '@': "provider:prefix:name"
provider: colonSeparated.length > 0 ? colonSeparated[0] : provider,
prefix,
name: name2
};
return validate && !validateIconName(result) ? null : result;
}
const name = colonSeparated[0];
const dashSeparated = name.split("-");
if (dashSeparated.length > 1) {
const result = {
provider,
prefix: dashSeparated.shift(),
name: dashSeparated.join("-")
};
return validate && !validateIconName(result) ? null : result;
}
if (allowSimpleName && provider === "") {
const result = {
provider,
prefix: "",
name
};
return validate && !validateIconName(result, allowSimpleName) ? null : result;
}
return null;
};
const validateIconName = (icon, allowSimpleName) => {
if (!icon) {
return false;
}
return !!((icon.provider === "" || icon.provider.match(matchIconName)) && (allowSimpleName && icon.prefix === "" || icon.prefix.match(matchIconName)) && icon.name.match(matchIconName));
};
const defaultIconDimensions = Object.freeze(

@@ -168,2 +118,52 @@ {

const matchIconName = /^[a-z0-9]+(-[a-z0-9]+)*$/;
const stringToIcon = (value, validate, allowSimpleName, provider = "") => {
const colonSeparated = value.split(":");
if (value.slice(0, 1) === "@") {
if (colonSeparated.length < 2 || colonSeparated.length > 3) {
return null;
}
provider = colonSeparated.shift().slice(1);
}
if (colonSeparated.length > 3 || !colonSeparated.length) {
return null;
}
if (colonSeparated.length > 1) {
const name2 = colonSeparated.pop();
const prefix = colonSeparated.pop();
const result = {
// Allow provider without '@': "provider:prefix:name"
provider: colonSeparated.length > 0 ? colonSeparated[0] : provider,
prefix,
name: name2
};
return validate && !validateIconName(result) ? null : result;
}
const name = colonSeparated[0];
const dashSeparated = name.split("-");
if (dashSeparated.length > 1) {
const result = {
provider,
prefix: dashSeparated.shift(),
name: dashSeparated.join("-")
};
return validate && !validateIconName(result) ? null : result;
}
if (allowSimpleName && provider === "") {
const result = {
provider,
prefix: "",
name
};
return validate && !validateIconName(result, allowSimpleName) ? null : result;
}
return null;
};
const validateIconName = (icon, allowSimpleName) => {
if (!icon) {
return false;
}
return !!((icon.provider === "" || icon.provider.match(matchIconName)) && (allowSimpleName && icon.prefix === "" || icon.prefix.match(matchIconName)) && icon.name.match(matchIconName));
};
const optionalPropertyDefaults = {

@@ -323,3 +323,3 @@ provider: "",

}
function iconExists(name) {
function iconLoaded(name) {
return !!getIconData(name);

@@ -385,2 +385,31 @@ }

function splitSVGDefs(content, tag = "defs") {
let defs = "";
const index = content.indexOf("<" + tag);
while (index >= 0) {
const start = content.indexOf(">", index);
const end = content.indexOf("</" + tag);
if (start === -1 || end === -1) {
break;
}
const endEnd = content.indexOf(">", end);
if (endEnd === -1) {
break;
}
defs += content.slice(start + 1, end).trim();
content = content.slice(0, index).trim() + content.slice(endEnd + 1);
}
return {
defs,
content
};
}
function mergeDefsAndContent(defs, content) {
return defs ? "<defs>" + defs + "</defs>" + content : content;
}
function wrapSVGContent(body, start, end) {
const split = splitSVGDefs(body);
return mergeDefsAndContent(split.defs, start + split.content + end);
}
const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none";

@@ -462,3 +491,7 @@ function iconToSVG(icon, customisations) {

if (transformations.length) {
body = '<g transform="' + transformations.join(" ") + '">' + body + "</g>";
body = wrapSVGContent(
body,
'<g transform="' + transformations.join(" ") + '">',
"</g>"
);
}

@@ -487,5 +520,7 @@ });

setAttr("height", height);
attributes.viewBox = box.left.toString() + " " + box.top.toString() + " " + boxWidth.toString() + " " + boxHeight.toString();
const viewBox = [box.left, box.top, boxWidth, boxHeight];
attributes.viewBox = viewBox.join(" ");
return {
attributes,
viewBox,
body

@@ -1136,2 +1171,3 @@ };

const browserStorageCacheExpiration = 168;
const browserStorageLimit = 50;

@@ -1295,3 +1331,3 @@ function getStoredItem(func, key) {

index = getBrowserStorageItemsCount(func);
if (!setBrowserStorageItemsCount(func, index + 1)) {
if (index >= browserStorageLimit || !setBrowserStorageItemsCount(func, index + 1)) {
return;

@@ -1643,8 +1679,6 @@ }

props,
// True if icon should have vertical-align added
inline,
// Optional reference for SVG/SPAN, extracted by React.forwardRef()
ref) => {
// Icon name
name) => {
// Get default properties
const defaultProps = inline
const defaultProps = props.inline
? inlineDefaults

@@ -1662,4 +1696,19 @@ : defaultExtendedIconCustomisations;

...(mode === 'svg' ? svgDefaults : {}),
ref,
};
if (name) {
const iconName = stringToIcon(name, false, true);
if (iconName) {
const classNames = ['iconify'];
const props = [
'provider',
'prefix',
];
for (const prop of props) {
if (iconName[prop]) {
classNames.push('iconify--' + iconName[prop]);
}
}
componentProps.className = classNames.join(' ');
}
}
// Get element properties

@@ -1678,5 +1727,14 @@ for (let key in props) {

case 'mode':
case 'ssr':
break;
// Forward ref
case '_ref':
case '_inline':
componentProps.ref = value;
break;
// Merge class names
case 'className':
componentProps[key] =
(componentProps[key] ? componentProps[key] + ' ' : '') +
value;
break;
// Boolean attributes

@@ -1748,3 +1806,3 @@ case 'inline':

};
return React.createElement('svg', componentProps);
return react.createElement('svg', componentProps);
}

@@ -1771,3 +1829,3 @@ // Render <span> with style

};
return React.createElement('span', componentProps);
return react.createElement('span', componentProps);
};

@@ -1851,148 +1909,73 @@

}
class IconComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
// Render placeholder before component is mounted
icon: null,
};
}
/**
* Abort loading icon
*/
_abortLoading() {
if (this._loading) {
this._loading.abort();
this._loading = null;
function IconComponent(props) {
const [mounted, setMounted] = react.useState(!!props.ssr);
const [abort, setAbort] = react.useState({});
const [state, setState] = react.useState({
name: '',
});
// Cancel loading
function cleanup() {
const callback = abort.callback;
if (callback) {
callback();
setAbort({});
}
}
/**
* Update state
*/
_setData(icon) {
if (this.state.icon !== icon) {
this.setState({
icon,
// Update state
function updateState() {
var _a;
const name = props.icon;
if (typeof name === 'object') {
// Icon as object
cleanup();
setState({
name: '',
data: name,
});
}
}
/**
* Check if icon should be loaded
*/
_checkIcon(changed) {
const state = this.state;
const icon = this.props.icon;
// Icon is an object
if (typeof icon === 'object' &&
icon !== null &&
typeof icon.body === 'string') {
// Stop loading
this._icon = '';
this._abortLoading();
if (changed || state.icon === null) {
// Set data if it was changed
this._setData({
data: icon,
});
}
return;
}
// Invalid icon?
let iconName;
if (typeof icon !== 'string' ||
(iconName = stringToIcon(icon, false, true)) === null) {
this._abortLoading();
this._setData(null);
return;
}
// Load icon
const data = getIconData(iconName);
if (!data) {
// Icon data is not available
if (!this._loading || this._loading.name !== icon) {
// New icon to load
this._abortLoading();
this._icon = '';
this._setData(null);
if (data !== null) {
// Icon was not loaded
this._loading = {
name: icon,
abort: loadIcons([iconName], this._checkIcon.bind(this, false)),
};
}
}
return;
}
// Icon data is available
if (this._icon !== icon || state.icon === null) {
// New icon or icon has been loaded
this._abortLoading();
this._icon = icon;
// Add classes
const classes = ['iconify'];
if (iconName.prefix !== '') {
classes.push('iconify--' + iconName.prefix);
}
if (iconName.provider !== '') {
classes.push('iconify--' + iconName.provider);
}
// Set data
this._setData({
// New icon or got icon data
const data = getIconData(name);
if (state.name !== name || data !== state.data) {
cleanup();
setState({
name,
data,
classes,
});
if (this.props.onLoad) {
this.props.onLoad(icon);
if (data === undefined) {
// Load icon, update state when done
const callback = loadIcons([name], updateState);
setAbort({
callback,
});
}
else if (data) {
// Icon data is available: trigger onLoad callback if present
(_a = props.onLoad) === null || _a === void 0 ? void 0 : _a.call(props, name);
}
}
}
/**
* Component mounted
*/
componentDidMount() {
this._checkIcon(false);
}
/**
* Component updated
*/
componentDidUpdate(oldProps) {
if (oldProps.icon !== this.props.icon) {
this._checkIcon(true);
// Mounted state, cleanup for loader
react.useEffect(() => {
setMounted(true);
updateState();
return cleanup;
}, []);
// Icon changed
react.useEffect(() => {
if (mounted) {
updateState();
}
}, [props.icon]);
// Render icon
const { name, data } = state;
if (!data) {
return props.children
? props.children
: react.createElement('span', {});
}
/**
* Abort loading
*/
componentWillUnmount() {
this._abortLoading();
}
/**
* Render
*/
render() {
const props = this.props;
const icon = this.state.icon;
if (icon === null) {
// Render placeholder
return props.children
? props.children
: React.createElement('span', {});
}
// Add classes
let newProps = props;
if (icon.classes) {
newProps = {
...props,
className: (typeof props.className === 'string'
? props.className + ' '
: '') + icon.classes.join(' '),
};
}
// Render icon
return render({
...defaultIconProps,
...icon.data,
}, newProps, props._inline, props._ref);
}
return render({
...defaultIconProps,
...data,
}, props, name);
}

@@ -2004,10 +1987,6 @@ /**

*/
const Icon = React.forwardRef(function Icon(props, ref) {
const newProps = {
...props,
_ref: ref,
_inline: false,
};
return React.createElement(IconComponent, newProps);
});
const Icon = react.forwardRef((props, ref) => IconComponent({
...props,
_ref: ref,
}));
/**

@@ -2018,10 +1997,7 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font)

*/
const InlineIcon = React.forwardRef(function InlineIcon(props, ref) {
const newProps = {
...props,
_ref: ref,
_inline: true,
};
return React.createElement(IconComponent, newProps);
});
const InlineIcon = react.forwardRef((props, ref) => IconComponent({
inline: true,
...props,
_ref: ref,
}));
/**

@@ -2050,3 +2026,4 @@ * Internal API

exports.getIcon = getIcon;
exports.iconExists = iconExists;
exports.iconExists = iconLoaded;
exports.iconLoaded = iconLoaded;
exports.listIcons = listIcons;

@@ -2053,0 +2030,0 @@ exports.loadIcon = loadIcon;

@@ -0,6 +1,6 @@

/// <reference types="react" />
import type { IconifyIcon } from '@iconify/types';
import type { IconifyJSON } from '@iconify/types';
import { IconifyTransformations } from '@iconify/types';
import { default as React_2 } from 'react';
import type { RefAttributes } from 'react';
import type { SVGProps } from 'react';

@@ -29,4 +29,6 @@

*/
export declare const Icon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>;
export declare const Icon: IconComponentType;
declare type IconComponentType = (props: IconProps) => JSX.Element;
/**

@@ -67,2 +69,3 @@ * React component properties: generic element for Icon component, SVG for generated component

id?: string;
ssr?: boolean;
onLoad?: IconifyIconOnLoad;

@@ -99,6 +102,4 @@ }

*/
export declare type IconProps = IconifyElementProps & IconifyIconProps & ReactRefProp;
export declare type IconProps = IconifyElementProps & IconifyIconProps;
declare type IconRef = RefAttributes<SVGSVGElement>;
/**

@@ -109,8 +110,4 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font)

*/
export declare const InlineIcon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>;
export declare const InlineIcon: IconComponentType;
declare interface ReactRefProp {
ref?: IconRef;
}
export { }
'use strict';
var React = require('react');
var react = require('react');

@@ -119,2 +119,50 @@ const defaultIconDimensions = Object.freeze(

const matchIconName = /^[a-z0-9]+(-[a-z0-9]+)*$/;
const stringToIcon = (value, validate, allowSimpleName, provider = "") => {
const colonSeparated = value.split(":");
if (value.slice(0, 1) === "@") {
if (colonSeparated.length < 2 || colonSeparated.length > 3) {
return null;
}
provider = colonSeparated.shift().slice(1);
}
if (colonSeparated.length > 3 || !colonSeparated.length) {
return null;
}
if (colonSeparated.length > 1) {
const name2 = colonSeparated.pop();
const prefix = colonSeparated.pop();
const result = {
// Allow provider without '@': "provider:prefix:name"
provider: colonSeparated.length > 0 ? colonSeparated[0] : provider,
prefix,
name: name2
};
return validate && !validateIconName(result) ? null : result;
}
const name = colonSeparated[0];
const dashSeparated = name.split("-");
if (dashSeparated.length > 1) {
const result = {
provider,
prefix: dashSeparated.shift(),
name: dashSeparated.join("-")
};
return validate && !validateIconName(result) ? null : result;
}
if (allowSimpleName && provider === "") {
const result = {
provider,
prefix: "",
name
};
return validate && !validateIconName(result, allowSimpleName) ? null : result;
}
return null;
};
const validateIconName = (icon, allowSimpleName) => {
if (!icon) {
return false;
}
return !!((icon.provider === "" || icon.provider.match(matchIconName)) && (allowSimpleName && icon.prefix === "" || icon.prefix.match(matchIconName)) && icon.name.match(matchIconName));
};

@@ -285,2 +333,31 @@ const optionalPropertyDefaults = {

function splitSVGDefs(content, tag = "defs") {
let defs = "";
const index = content.indexOf("<" + tag);
while (index >= 0) {
const start = content.indexOf(">", index);
const end = content.indexOf("</" + tag);
if (start === -1 || end === -1) {
break;
}
const endEnd = content.indexOf(">", end);
if (endEnd === -1) {
break;
}
defs += content.slice(start + 1, end).trim();
content = content.slice(0, index).trim() + content.slice(endEnd + 1);
}
return {
defs,
content
};
}
function mergeDefsAndContent(defs, content) {
return defs ? "<defs>" + defs + "</defs>" + content : content;
}
function wrapSVGContent(body, start, end) {
const split = splitSVGDefs(body);
return mergeDefsAndContent(split.defs, start + split.content + end);
}
const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none";

@@ -362,3 +439,7 @@ function iconToSVG(icon, customisations) {

if (transformations.length) {
body = '<g transform="' + transformations.join(" ") + '">' + body + "</g>";
body = wrapSVGContent(
body,
'<g transform="' + transformations.join(" ") + '">',
"</g>"
);
}

@@ -387,5 +468,7 @@ });

setAttr("height", height);
attributes.viewBox = box.left.toString() + " " + box.top.toString() + " " + boxWidth.toString() + " " + boxHeight.toString();
const viewBox = [box.left, box.top, boxWidth, boxHeight];
attributes.viewBox = viewBox.join(" ");
return {
attributes,
viewBox,
body

@@ -522,8 +605,6 @@ };

props,
// True if icon should have vertical-align added
inline,
// Optional reference for SVG/SPAN, extracted by React.forwardRef()
ref) => {
// Icon name
name) => {
// Get default properties
const defaultProps = inline
const defaultProps = props.inline
? inlineDefaults

@@ -541,4 +622,19 @@ : defaultExtendedIconCustomisations;

...(mode === 'svg' ? svgDefaults : {}),
ref,
};
if (name) {
const iconName = stringToIcon(name, false, true);
if (iconName) {
const classNames = ['iconify'];
const props = [
'provider',
'prefix',
];
for (const prop of props) {
if (iconName[prop]) {
classNames.push('iconify--' + iconName[prop]);
}
}
componentProps.className = classNames.join(' ');
}
}
// Get element properties

@@ -557,5 +653,14 @@ for (let key in props) {

case 'mode':
case 'ssr':
break;
// Forward ref
case '_ref':
case '_inline':
componentProps.ref = value;
break;
// Merge class names
case 'className':
componentProps[key] =
(componentProps[key] ? componentProps[key] + ' ' : '') +
value;
break;
// Boolean attributes

@@ -627,3 +732,3 @@ case 'inline':

};
return React.createElement('svg', componentProps);
return react.createElement('svg', componentProps);
}

@@ -650,3 +755,3 @@ // Render <span> with style

};
return React.createElement('span', componentProps);
return react.createElement('span', componentProps);
};

@@ -658,26 +763,14 @@

const storage = Object.create(null);
/**
* Generate icon
*/
function component(props, inline, ref) {
// Split properties
const propsIcon = props.icon;
const icon = typeof propsIcon === 'string'
? storage[propsIcon]
: typeof propsIcon === 'object'
? propsIcon
: null;
// Validate icon object
if (icon === null ||
typeof icon !== 'object' ||
typeof icon.body !== 'string') {
function IconComponent(props) {
const icon = props.icon;
const data = typeof icon === 'string' ? storage[icon] : icon;
if (!data) {
return props.children
? props.children
: React.createElement('span', {});
: react.createElement('span', {});
}
// Valid icon: render it
return render({
...defaultIconProps,
...icon,
}, props, inline, ref);
...data,
}, props, typeof icon === 'string' ? icon : undefined);
}

@@ -689,5 +782,6 @@ /**

*/
const Icon = React.forwardRef(function Icon(props, ref) {
return component(props, false, ref);
});
const Icon = react.forwardRef((props, ref) => IconComponent({
...props,
_ref: ref,
}));
/**

@@ -698,5 +792,7 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font)

*/
const InlineIcon = React.forwardRef(function InlineIcon(props, ref) {
return component(props, true, ref);
});
const InlineIcon = react.forwardRef((props, ref) => IconComponent({
inline: true,
...props,
_ref: ref,
}));
/**

@@ -703,0 +799,0 @@ * Add icon to storage, allowing to call it by name

@@ -5,3 +5,7 @@ {

"author": "Vjacheslav Trushkin",
"version": "4.1.1",
"version": "5.0.0-beta.1",
"publishConfig": {
"access": "public",
"tag": "next"
},
"license": "MIT",

@@ -41,16 +45,16 @@ "bugs": "https://github.com/iconify/iconify/issues",

"devDependencies": {
"@babel/preset-env": "^7.22.4",
"@babel/preset-react": "^7.22.3",
"@microsoft/api-extractor": "^7.35.1",
"@rollup/plugin-node-resolve": "^15.1.0",
"@types/react": "^18.2.8",
"babel-jest": "^29.5.0",
"jest": "^29.5.0",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.24.1",
"@microsoft/api-extractor": "^7.43.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/react": "^18.2.78",
"babel-jest": "^29.7.0",
"jest": "^29.7.0",
"react": "^18.2.0",
"react-test-renderer": "^18.2.0",
"rimraf": "^4.4.1",
"rollup": "^3.23.1",
"typescript": "^5.1.3",
"@iconify/core": "^2.0.1",
"@iconify/utils": "^2.1.6"
"rollup": "^3.29.4",
"typescript": "^5.4.5",
"@iconify/core": "^2.0.2",
"@iconify/utils": "^2.1.23"
},

@@ -68,5 +72,4 @@ "peerDependencies": {

"build:api": "api-extractor run --local --verbose --config api-extractor.iconify.json",
"build:cleanup": "node cleanup",
"test": "jest --runInBand"
}
}

@@ -14,3 +14,3 @@ # Iconify for React

- One syntax for over 150,000 icons from 100+ icon sets.
- One syntax for over 200,000 icons from 150+ icon sets.
- Renders SVG. Many components simply render icon fonts, which look ugly. Iconify renders pixel perfect SVG.

@@ -51,3 +51,3 @@ - Loads icons on demand. No need to bundle icons, component will automatically load icon data for icons that you use from Iconify API.

Component will automatically retrieve data for "mdi-light:home" from Iconify API and render it. There are over 150,000 icons available on Iconify API from various free and open source icon sets, including all the most popular icon sets.
Component will automatically retrieve data for "mdi-light:home" from Iconify API and render it. There are over 200,000 icons available on Iconify API from various free and open source icon sets, including all the most popular icon sets.

@@ -54,0 +54,0 @@ ## Offline usage

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