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

ink

Package Overview
Dependencies
Maintainers
2
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ink - npm Package Compare versions

Comparing version 4.0.0 to 4.1.0

72

build/components/Box.d.ts

@@ -7,2 +7,20 @@ import React from 'react';

/**
* Size of the gap between an element's columns.
*
* @default 0
*/
readonly columnGap?: number;
/**
* Size of the gap between element's rows.
*
* @default 0
*/
readonly rowGap?: number;
/**
* Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`.
*
* @default 0
*/
readonly gap?: number;
/**
* Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`.

@@ -43,2 +61,20 @@ *

readonly paddingY?: number;
/**
* Behavior for an element's overflow in both directions.
*
* @default 'visible'
*/
readonly overflow?: 'visible' | 'hidden';
/**
* Behavior for an element's overflow in horizontal direction.
*
* @default 'visible'
*/
readonly overflowX?: 'visible' | 'hidden';
/**
* Behavior for an element's overflow in vertical direction.
*
* @default 'visible'
*/
readonly overflowY?: 'visible' | 'hidden';
};

@@ -50,2 +86,20 @@ /**

/**
* Size of the gap between an element's columns.
*
* @default 0
*/
readonly columnGap?: number | undefined;
/**
* Size of the gap between element's rows.
*
* @default 0
*/
readonly rowGap?: number | undefined;
/**
* Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`.
*
* @default 0
*/
readonly gap?: number | undefined;
/**
* Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`.

@@ -86,2 +140,20 @@ *

readonly paddingY?: number | undefined;
/**
* Behavior for an element's overflow in both directions.
*
* @default 'visible'
*/
readonly overflow?: "visible" | "hidden" | undefined;
/**
* Behavior for an element's overflow in horizontal direction.
*
* @default 'visible'
*/
readonly overflowX?: "visible" | "hidden" | undefined;
/**
* Behavior for an element's overflow in vertical direction.
*
* @default 'visible'
*/
readonly overflowY?: "visible" | "hidden" | undefined;
} & {

@@ -88,0 +160,0 @@ children?: React.ReactNode;

7

build/components/Box.js

@@ -9,2 +9,4 @@ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */

...style,
columnGap: style.columnGap || style.gap || 0,
rowGap: style.rowGap || style.gap || 0,
marginLeft: style.marginLeft || style.marginX || style.margin || 0,

@@ -17,3 +19,5 @@ marginRight: style.marginRight || style.marginX || style.margin || 0,

paddingTop: style.paddingTop || style.paddingY || style.padding || 0,
paddingBottom: style.paddingBottom || style.paddingY || style.padding || 0
paddingBottom: style.paddingBottom || style.paddingY || style.padding || 0,
overflowX: style.overflowX || style.overflow || 'visible',
overflowY: style.overflowY || style.overflow || 'visible'
};

@@ -24,2 +28,3 @@ return (React.createElement("ink-box", { ref: ref, style: transformedStyle }, children));

Box.defaultProps = {
flexWrap: 'nowrap',
flexDirection: 'row',

@@ -26,0 +31,0 @@ flexGrow: 0,

6

build/dom.d.ts

@@ -1,3 +0,2 @@

/// <reference types="yoga-layout" />
import Yoga from 'yoga-layout-prebuilt';
import { type Node as YogaNode } from 'yoga-wasm-web/auto';
import { type Styles } from './styles.js';

@@ -7,3 +6,3 @@ import { type OutputTransformer } from './render-node-to-output.js';

parentNode: DOMElement | undefined;
yogaNode?: Yoga.YogaNode;
yogaNode?: YogaNode;
internal_static?: boolean;

@@ -22,2 +21,3 @@ style: Styles;

staticNode?: DOMElement;
onComputeLayout?: () => void;
onRender?: () => void;

@@ -24,0 +24,0 @@ onImmediateRender?: () => void;

@@ -1,2 +0,3 @@

import Yoga from 'yoga-layout-prebuilt';
// eslint-disable-next-line n/file-extension-in-import
import Yoga from 'yoga-wasm-web/auto';
import measureText from './measure-text.js';

@@ -3,0 +4,0 @@ import applyStyles from './styles.js';

@@ -1,3 +0,3 @@

import Yoga from 'yoga-layout-prebuilt';
declare const getMaxWidth: (yogaNode: Yoga.YogaNode) => number;
import { type Node as YogaNode } from 'yoga-wasm-web/auto';
declare const getMaxWidth: (yogaNode: YogaNode) => number;
export default getMaxWidth;

@@ -1,2 +0,3 @@

import Yoga from 'yoga-layout-prebuilt';
// eslint-disable-next-line n/file-extension-in-import
import Yoga from 'yoga-wasm-web/auto';
const getMaxWidth = (yogaNode) => {

@@ -3,0 +4,0 @@ return (yogaNode.getComputedWidth() -

@@ -25,5 +25,7 @@ /// <reference types="node" resolution-mode="require"/>

constructor(options: Options);
resized: () => void;
resolveExitPromise: () => void;
rejectExitPromise: (reason?: Error) => void;
unsubscribeExit: () => void;
calculateLayout: () => void;
onRender: () => void;

@@ -30,0 +32,0 @@ render(node: ReactNode): void;

import process from 'node:process';
import React from 'react';
import { throttle } from 'lodash-es';
import throttle from 'lodash/throttle.js';
import ansiEscapes from 'ansi-escapes';

@@ -9,2 +9,4 @@ import originalIsCi from 'is-ci';

import patchConsole from 'patch-console';
// eslint-disable-next-line n/file-extension-in-import
import Yoga from 'yoga-wasm-web/auto';
import reconciler from './reconciler.js';

@@ -89,2 +91,11 @@ import render from './renderer.js';

});
Object.defineProperty(this, "resized", {
enumerable: true,
configurable: true,
writable: true,
value: () => {
this.calculateLayout();
this.onRender();
}
});
Object.defineProperty(this, "resolveExitPromise", {

@@ -108,2 +119,14 @@ enumerable: true,

});
Object.defineProperty(this, "calculateLayout", {
enumerable: true,
configurable: true,
writable: true,
value: () => {
// The 'columns' property can be undefined or 0 when not using a TTY.
// In that case we fall back to 80.
const terminalWidth = this.options.stdout.columns || 80;
this.rootNode.yogaNode.setWidth(terminalWidth);
this.rootNode.yogaNode.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);
}
});
Object.defineProperty(this, "onRender", {

@@ -117,6 +140,3 @@ enumerable: true,

}
const { output, outputHeight, staticOutput } = render(this.rootNode,
// The 'columns' property can be undefined or 0 when not using a TTY.
// In that case we fall back to 80.
this.options.stdout.columns || 80);
const { output, outputHeight, staticOutput } = render(this.rootNode);
// If <Static> output isn't empty, it means new children have been added to it

@@ -161,2 +181,3 @@ const hasStaticOutput = staticOutput && staticOutput !== '\n';

this.rootNode = dom.createNode('ink-root');
this.rootNode.onComputeLayout = this.calculateLayout;
this.rootNode.onRender = options.debug

@@ -202,5 +223,5 @@ ? this.onRender

if (!isCi) {
options.stdout.on('resize', this.onRender);
options.stdout.on('resize', this.resized);
this.unsubscribeResize = () => {
options.stdout.off('resize', this.onRender);
options.stdout.off('resize', this.resized);
};

@@ -251,2 +272,3 @@ }

}
this.calculateLayout();
this.onRender();

@@ -253,0 +275,0 @@ this.unsubscribeExit();

@@ -14,6 +14,12 @@ import { type OutputTransformer } from './render-node-to-output.js';

};
type Clip = {
x1: number | undefined;
x2: number | undefined;
y1: number | undefined;
y2: number | undefined;
};
export default class Output {
width: number;
height: number;
private readonly writes;
private readonly operations;
constructor(options: Options);

@@ -23,2 +29,4 @@ write(x: number, y: number, text: string, options: {

}): void;
clip(clip: Clip): void;
unclip(): void;
get(): {

@@ -25,0 +33,0 @@ output: string;

import sliceAnsi from 'slice-ansi';
import stringWidth from 'string-width';
import widestLine from 'widest-line';
export default class Output {

@@ -17,4 +18,3 @@ constructor(options) {

});
// Initialize output array with a specific set of rows, so that margin/padding at the bottom is preserved
Object.defineProperty(this, "writes", {
Object.defineProperty(this, "operations", {
enumerable: true,

@@ -34,5 +34,23 @@ configurable: true,

}
this.writes.push({ x, y, text, transformers });
this.operations.push({
type: 'write',
x,
y,
text,
transformers
});
}
clip(clip) {
this.operations.push({
type: 'clip',
clip
});
}
unclip() {
this.operations.push({
type: 'unclip'
});
}
get() {
// Initialize output array with a specific set of rows, so that margin/padding at the bottom is preserved
const output = [];

@@ -42,21 +60,70 @@ for (let y = 0; y < this.height; y++) {

}
for (const write of this.writes) {
const { x, y, text, transformers } = write;
const lines = text.split('\n');
let offsetY = 0;
for (let line of lines) {
const currentLine = output[y + offsetY];
// Line can be missing if `text` is taller than height of pre-initialized `this.output`
if (!currentLine) {
continue;
const clips = [];
for (const operation of this.operations) {
if (operation.type === 'clip') {
clips.push(operation.clip);
}
if (operation.type === 'unclip') {
clips.pop();
}
if (operation.type === 'write') {
const { text, transformers } = operation;
let { x, y } = operation;
let lines = text.split('\n');
const clip = clips[clips.length - 1];
if (clip) {
const clipHorizontally = typeof clip?.x1 === 'number' && typeof clip?.x2 === 'number';
const clipVertically = typeof clip?.y1 === 'number' && typeof clip?.y2 === 'number';
// If text is positioned outside of clipping area altogether,
// skip to the next operation to avoid unnecessary calculations
if (clipHorizontally) {
const width = widestLine(text);
if (x + width < clip.x1 || x > clip.x2) {
continue;
}
}
if (clipVertically) {
const height = lines.length;
if (y + height < clip.y1 || y > clip.y2) {
continue;
}
}
if (clipHorizontally) {
lines = lines.map(line => {
const from = x < clip.x1 ? clip.x1 - x : 0;
const width = stringWidth(line);
const to = x + width > clip.x2 ? clip.x2 - x : width;
return sliceAnsi(line, from, to);
});
if (x < clip.x1) {
x = clip.x1;
}
}
if (clipVertically) {
const from = y < clip.y1 ? clip.y1 - y : 0;
const height = lines.length;
const to = y + height > clip.y2 ? clip.y2 - y : height;
lines = lines.slice(from, to);
if (y < clip.y1) {
y = clip.y1;
}
}
}
const width = stringWidth(line);
for (const transformer of transformers) {
line = transformer(line);
let offsetY = 0;
for (let line of lines) {
const currentLine = output[y + offsetY];
// Line can be missing if `text` is taller than height of pre-initialized `this.output`
if (!currentLine) {
continue;
}
const width = stringWidth(line);
for (const transformer of transformers) {
line = transformer(line);
}
output[y + offsetY] =
sliceAnsi(currentLine, 0, x) +
line +
sliceAnsi(currentLine, x + width);
offsetY++;
}
output[y + offsetY] =
sliceAnsi(currentLine, 0, x) +
line +
sliceAnsi(currentLine, x + width);
offsetY++;
}

@@ -63,0 +130,0 @@ }

import process from 'node:process';
import createReconciler from 'react-reconciler';
import { DefaultEventPriority } from 'react-reconciler/constants.js';
import Yoga from 'yoga-layout-prebuilt';
// eslint-disable-next-line n/file-extension-in-import
import Yoga from 'yoga-wasm-web/auto';
import { createTextNode, appendChildNode, insertBeforeNode, removeChildNode, setStyle, setTextNodeValue, createNode, setAttribute } from './dom.js';

@@ -40,2 +41,5 @@ // We need to conditionally perform devtools connection to avoid

resetAfterCommit(rootNode) {
if (typeof rootNode.onComputeLayout === 'function') {
rootNode.onComputeLayout();
}
// Since renders are throttled at the instance level and <Static> component children

@@ -161,2 +165,3 @@ // are rendered only once and then get deleted, we need an escape hatch to

// Always include `borderColor` and `borderStyle` to ensure border is rendered,
// and `overflowX` and `overflowY` to ensure content is clipped,
// otherwise resulting `updatePayload` may not contain them

@@ -174,2 +179,4 @@ // if they weren't changed during this update

newStyle.borderColor;
updatePayload['style'].overflowX = newStyle.overflowX;
updatePayload['style'].overflowY = newStyle.overflowY;
}

@@ -176,0 +183,0 @@ if (newStyle[styleKey] !== oldStyle[styleKey]) {

@@ -1,4 +0,5 @@

import Yoga from 'yoga-layout-prebuilt';
import widestLine from 'widest-line';
import indentString from 'indent-string';
// eslint-disable-next-line n/file-extension-in-import
import Yoga from 'yoga-wasm-web/auto';
import wrapText from './wrap-text.js';

@@ -57,4 +58,27 @@ import getMaxWidth from './get-max-width.js';

}
let clipped = false;
if (node.nodeName === 'ink-box') {
renderBorder(x, y, node, output);
const clipHorizontally = node.style.overflowX === 'hidden';
const clipVertically = node.style.overflowY === 'hidden';
if (clipHorizontally || clipVertically) {
const x1 = clipHorizontally
? x + yogaNode.getComputedBorder(Yoga.EDGE_LEFT)
: undefined;
const x2 = clipHorizontally
? x +
yogaNode.getComputedWidth() -
yogaNode.getComputedBorder(Yoga.EDGE_RIGHT)
: undefined;
const y1 = clipVertically
? y + yogaNode.getComputedBorder(Yoga.EDGE_TOP)
: undefined;
const y2 = clipVertically
? y +
yogaNode.getComputedHeight() -
yogaNode.getComputedBorder(Yoga.EDGE_BOTTOM)
: undefined;
output.clip({ x1, x2, y1, y2 });
clipped = true;
}
}

@@ -70,2 +94,5 @@ if (node.nodeName === 'ink-root' || node.nodeName === 'ink-box') {

}
if (clipped) {
output.unclip();
}
}

@@ -72,0 +99,0 @@ }

@@ -7,3 +7,3 @@ import { type DOMElement } from './dom.js';

};
declare const renderer: (node: DOMElement, terminalWidth: number) => Result;
declare const renderer: (node: DOMElement) => Result;
export default renderer;

@@ -1,8 +0,5 @@

import Yoga from 'yoga-layout-prebuilt';
import renderNodeToOutput from './render-node-to-output.js';
import Output from './output.js';
const renderer = (node, terminalWidth) => {
node.yogaNode.setWidth(terminalWidth);
const renderer = (node) => {
if (node.yogaNode) {
node.yogaNode.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);
const output = new Output({

@@ -9,0 +6,0 @@ width: node.yogaNode.getComputedWidth(),

@@ -1,5 +0,5 @@

import { type YogaNode } from 'yoga-layout-prebuilt';
import { type Boxes } from 'cli-boxes';
import { type LiteralUnion } from 'type-fest';
import { type ForegroundColorName } from 'chalk';
import { type Node as YogaNode } from 'yoga-wasm-web/auto';
export type Styles = {

@@ -9,2 +9,10 @@ readonly textWrap?: 'wrap' | 'end' | 'middle' | 'truncate-end' | 'truncate' | 'truncate-middle' | 'truncate-start';

/**
* Gap between element's columns.
*/
readonly columnGap?: number;
/**
* Gap between element's rows.
*/
readonly rowGap?: number;
/**
* Top margin.

@@ -62,2 +70,7 @@ */

/**
* It defines whether the flex items are forced in a single line or can be flowed into multiple lines. If set to multiple lines, it also defines the cross-axis which determines the direction new lines are stacked in.
* See [flex-wrap](https://css-tricks.com/almanac/properties/f/flex-wrap/).
*/
readonly flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
/**
* The align-items property defines the default behavior for how items are laid out along the cross axis (perpendicular to the main axis).

@@ -109,4 +122,12 @@ * See [align-items](https://css-tricks.com/almanac/properties/a/align-items/).

readonly borderColor?: LiteralUnion<ForegroundColorName, string>;
/**
* Behavior for an element's overflow in horizontal direction.
*/
readonly overflowX?: 'visible' | 'hidden';
/**
* Behavior for an element's overflow in vertical direction.
*/
readonly overflowY?: 'visible' | 'hidden';
};
declare const styles: (node: YogaNode, style?: Styles) => void;
export default styles;

@@ -1,3 +0,3 @@

/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
import Yoga from 'yoga-layout-prebuilt';
// eslint-disable-next-line n/file-extension-in-import
import Yoga from 'yoga-wasm-web/auto';
const applyPositionStyles = (node, style) => {

@@ -45,2 +45,13 @@ if ('position' in style) {

}
if ('flexWrap' in style) {
if (style.flexWrap === 'nowrap') {
node.setFlexWrap(Yoga.WRAP_NO_WRAP);
}
if (style.flexWrap === 'wrap') {
node.setFlexWrap(Yoga.WRAP_WRAP);
}
if (style.flexWrap === 'wrap-reverse') {
node.setFlexWrap(Yoga.WRAP_WRAP_REVERSE);
}
}
if ('flexDirection' in style) {

@@ -172,2 +183,10 @@ if (style.flexDirection === 'row') {

};
const applyGapStyles = (node, style) => {
if ('columnGap' in style) {
node.setGap(Yoga.GUTTER_COLUMN, style.columnGap ?? 0);
}
if ('rowGap' in style) {
node.setGap(Yoga.GUTTER_ROW, style.rowGap ?? 0);
}
};
const styles = (node, style = {}) => {

@@ -181,4 +200,5 @@ applyPositionStyles(node, style);

applyBorderStyles(node, style);
applyGapStyles(node, style);
};
export default styles;
//# sourceMappingURL=styles.js.map
{
"name": "ink",
"version": "4.0.0",
"version": "4.1.0",
"description": "React for CLI",

@@ -54,3 +54,3 @@ "license": "MIT",

"is-ci": "^3.0.1",
"lodash-es": "^4.17.21",
"lodash": "^4.17.21",
"patch-console": "^2.0.0",

@@ -60,3 +60,3 @@ "react-reconciler": "^0.29.0",

"signal-exit": "^3.0.7",
"slice-ansi": "^5.0.0",
"slice-ansi": "^6.0.0",
"stack-utils": "^2.0.6",

@@ -68,3 +68,3 @@ "string-width": "^5.1.2",

"ws": "^8.12.0",
"yoga-layout-prebuilt": "^1.9.6"
"yoga-wasm-web": "~0.3.3"
},

@@ -76,3 +76,3 @@ "devDependencies": {

"@types/is-ci": "^2.0.0",
"@types/lodash-es": "^4.17.6",
"@types/lodash": "^4.14.191",
"@types/ms": "^0.7.31",

@@ -79,0 +79,0 @@ "@types/node": "*",

@@ -130,2 +130,3 @@ [![](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)

- [Shopify CLI](https://github.com/Shopify/cli) - Build apps, themes, and storefronts for Shopify.
- [ToDesktop CLI](https://www.todesktop.com/electron) - An all-in-one platform for building Electron apps.

@@ -584,2 +585,54 @@ ## Contents

#### Gap
#### gap
Type: `number`\
Default: `0`
Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`.
```jsx
<Box gap={1} width={3} flexWrap="wrap">
<Text>A</Text>
<Text>B</Text>
<Text>C</Text>
</Box>
// A B
//
// C
```
#### columnGap
Type: `number`\
Default: `0`
Size of the gap between an element's columns.
```jsx
<Box gap={1}>
<Text>A</Text>
<Text>B</Text>
</Box>
// A B
```
#### rowGap
Type: `number`\
Default: `0`
Size of the gap between element's rows.
```jsx
<Box flexDirection="column" gap={1}>
<Text>A</Text>
<Text>B</Text>
</Box>
// A
//
// B
```
#### Flex

@@ -686,2 +739,28 @@

##### flexWrap
Type: `string`\
Allowed values: `nowrap` `wrap` `wrap-reverse`
See [flex-wrap](https://css-tricks.com/almanac/properties/f/flex-wrap/).
```jsx
<Box width={2} flexWrap="wrap">
<Text>A</Text>
<Text>BC</Text>
</Box>
// A
// B C
```
```jsx
<Box flexDirection="column" height={2} flexWrap="wrap">
<Text>A</Text>
<Text>B</Text>
<Text>C</Text>
</Box>
// A C
// B
```
##### alignItems

@@ -827,2 +906,26 @@

##### overflowX
Type: `string`\
Allowed values: `visible` `hidden`\
Default: `visible`
Behavior for an element's overflow in horizontal direction.
##### overflowY
Type: `string`\
Allowed values: `visible` `hidden`\
Default: `visible`
Behavior for an element's overflow in vertical direction.
##### overflow
Type: `string`\
Allowed values: `visible` `hidden`\
Default: `visible`
Shortcut for setting `overflowX` and `overflowY` at the same time.
#### Borders

@@ -1840,13 +1943,13 @@

- [Jest](examples/jest/jest.js) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
- [Counter](examples/counter/counter.js) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
- [Jest](examples/jest/jest.tsx) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
- [Counter](examples/counter/counter.tsx) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
- [Form with validation](https://github.com/final-form/rff-cli-example) - Manage form state using [Final Form](https://github.com/final-form/final-form#-final-form).
- [Borders](examples/borders/borders.js) - Add borders to `<Box>` component.
- [Suspense](examples/suspense/suspense.js) - Use React Suspense.
- [Table](examples/table/table.js) - Render a table with multiple columns and rows.
- [Focus management](examples/use-focus/use-focus.js) - Use `useFocus` hook to manage focus between components.
- [User input](examples/use-input/use-input.js) - Listen to user input.
- [Write to stdout](examples/use-stdout/use-stdout.js) - Write to stdout bypassing main Ink output.
- [Write to stderr](examples/use-stderr/use-stderr.js) - Write to stderr bypassing main Ink output.
- [Static](examples/static/static.js) - Use `<Static>` to render permanent output.
- [Borders](examples/borders/borders.tsx) - Add borders to `<Box>` component.
- [Suspense](examples/suspense/suspense.tsx) - Use React Suspense.
- [Table](examples/table/table.tsx) - Render a table with multiple columns and rows.
- [Focus management](examples/use-focus/use-focus.tsx) - Use `useFocus` hook to manage focus between components.
- [User input](examples/use-input/use-input.tsx) - Listen to user input.
- [Write to stdout](examples/use-stdout/use-stdout.tsx) - Write to stdout bypassing main Ink output.
- [Write to stderr](examples/use-stderr/use-stderr.tsx) - Write to stderr bypassing main Ink output.
- [Static](examples/static/static.tsx) - Use `<Static>` to render permanent output.
- [Child process](examples/subprocess-output) - Render output from a child process.

@@ -1853,0 +1956,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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