Socket
Socket
Sign inDemoInstall

@conform-to/react

Package Overview
Dependencies
Maintainers
1
Versions
66
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@conform-to/react - npm Package Compare versions

Comparing version 0.5.0 to 0.5.1

20

helpers.d.ts
import type { FieldConfig } from '@conform-to/dom';
import type { HTMLInputTypeAttribute } from 'react';
import type { CSSProperties, HTMLInputTypeAttribute } from 'react';
interface FieldProps {

@@ -9,4 +9,7 @@ id?: string;

autoFocus?: boolean;
tabIndex?: number;
style?: CSSProperties;
'aria-invalid': boolean;
'aria-describedby'?: string;
'aria-hidden'?: boolean;
}

@@ -37,9 +40,8 @@ interface InputProps<Schema> extends FieldProps {

type: 'checkbox' | 'radio';
hidden?: boolean;
value?: string;
} | {
type: 'file';
type?: Exclude<HTMLInputTypeAttribute, 'button' | 'submit' | 'hidden'>;
hidden?: boolean;
value?: never;
} | {
type?: Exclude<HTMLInputTypeAttribute, 'button' | 'submit' | 'hidden' | 'file'>;
value?: never;
};

@@ -50,4 +52,8 @@ export declare function input<Schema extends File | File[]>(config: FieldConfig<Schema>, options: {

export declare function input<Schema extends any>(config: FieldConfig<Schema>, options?: InputOptions): InputProps<Schema>;
export declare function select<Schema>(config: FieldConfig<Schema>): SelectProps;
export declare function textarea<Schema>(config: FieldConfig<Schema>): TextareaProps;
export declare function select<Schema>(config: FieldConfig<Schema>, options?: {
hidden?: boolean;
}): SelectProps;
export declare function textarea<Schema>(config: FieldConfig<Schema>, options?: {
hidden?: boolean;
}): TextareaProps;
export {};

@@ -5,2 +5,17 @@ 'use strict';

/**
* Style to make the input element visually hidden
* Based on the `sr-only` class from tailwindcss
*/
var hiddenStyle = {
position: 'absolute',
width: '1px',
height: '1px',
padding: 0,
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
whiteSpace: 'nowrap',
border: 0
};
function input(config) {

@@ -25,2 +40,7 @@ var _config$initialError;

};
if (options !== null && options !== void 0 && options.hidden) {
attributes.style = hiddenStyle;
attributes.tabIndex = -1;
attributes['aria-hidden'] = true;
}
if (config.initialError && config.initialError.length > 0) {

@@ -38,3 +58,3 @@ attributes.autoFocus = true;

}
function select(config) {
function select(config, options) {
var _config$defaultValue, _config$initialError2;

@@ -51,2 +71,7 @@ var attributes = {

};
if (options !== null && options !== void 0 && options.hidden) {
attributes.style = hiddenStyle;
attributes.tabIndex = -1;
attributes['aria-hidden'] = true;
}
if (config.initialError && config.initialError.length > 0) {

@@ -57,3 +82,3 @@ attributes.autoFocus = true;

}
function textarea(config) {
function textarea(config, options) {
var _config$defaultValue2, _config$initialError3;

@@ -72,2 +97,7 @@ var attributes = {

};
if (options !== null && options !== void 0 && options.hidden) {
attributes.style = hiddenStyle;
attributes.tabIndex = -1;
attributes['aria-hidden'] = true;
}
if (config.initialError && config.initialError.length > 0) {

@@ -74,0 +104,0 @@ attributes.autoFocus = true;

@@ -139,3 +139,3 @@ import { type FieldConfig, type FieldElement, type FieldValue, type FieldsetConstraint, type Primitive, type Submission } from '@conform-to/dom';

}
interface InputControl<Element extends {
interface LegacyInputControl<Element extends {
focus: () => void;

@@ -158,2 +158,3 @@ }> {

*
* @deprecated Please use the `useInputEvent` hook instead
* @see https://conform.guide/api/react#usecontrolledinput

@@ -163,3 +164,26 @@ */

focus: () => void;
} = HTMLInputElement, Schema extends Primitive = Primitive>(config: FieldConfig<Schema>): [ShadowInputProps, InputControl<Element>];
} = HTMLInputElement, Schema extends Primitive = Primitive>(config: FieldConfig<Schema>): [ShadowInputProps, LegacyInputControl<Element>];
interface InputControl {
change: (eventOrValue: {
target: {
value: string;
};
} | string) => void;
focus: () => void;
blur: () => void;
}
/**
* Returns a ref object and a set of helpers that dispatch corresponding dom event.
*
* @see https://conform.guide/api/react#useinputevent
*/
export declare function useInputEvent<RefShape extends FieldElement = HTMLInputElement>(options?: {
onSubmit?: (event: SubmitEvent) => void;
onReset?: (event: Event) => void;
}): [RefObject<RefShape>, InputControl];
export declare function useInputEvent<RefShape extends Exclude<any, FieldElement>>(options: {
getElement: (ref: RefShape | null) => FieldElement | null | undefined;
onSubmit?: (event: SubmitEvent) => void;
onReset?: (event: Event) => void;
}): [RefObject<RefShape>, InputControl];
export {};

@@ -492,2 +492,3 @@ 'use strict';

*
* @deprecated Please use the `useInputEvent` hook instead
* @see https://conform.guide/api/react#usecontrolledinput

@@ -548,15 +549,2 @@ */

ref,
style: {
position: 'absolute',
width: '1px',
height: '1px',
padding: 0,
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
whiteSpace: 'nowrap',
borderWidth: 0
},
tabIndex: -1,
'aria-hidden': true,
onFocus() {

@@ -566,3 +554,5 @@ var _inputRef$current;

}
}, helpers.input(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), uncontrolledState))), {
}, helpers.input(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), uncontrolledState), {
hidden: true
})), {
ref: inputRef,

@@ -576,2 +566,177 @@ value,

/**
* Triggering react custom change event
* Solution based on dom-testing-library
* @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
* @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
*/
function setNativeValue(element, value) {
if (element.value === value) {
// It will not trigger a change event if `element.value` is the same as the set value
return;
}
var {
set: valueSetter
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
var prototype = Object.getPrototypeOf(element);
var {
set: prototypeValueSetter
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
if (valueSetter) {
valueSetter.call(element, value);
} else {
throw new Error('The given element does not have a value setter');
}
}
}
/**
* useLayoutEffect is client-only.
* This basically makes it a no-op on server
*/
var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
function useInputEvent(options) {
var ref = react.useRef(null);
var optionsRef = react.useRef(options);
var changeDispatched = react.useRef(false);
var focusDispatched = react.useRef(false);
var blurDispatched = react.useRef(false);
useSafeLayoutEffect(() => {
optionsRef.current = options;
});
useSafeLayoutEffect(() => {
var getInputElement = () => {
var _optionsRef$current$g, _optionsRef$current, _optionsRef$current$g2;
return (_optionsRef$current$g = (_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 ? void 0 : (_optionsRef$current$g2 = _optionsRef$current.getElement) === null || _optionsRef$current$g2 === void 0 ? void 0 : _optionsRef$current$g2.call(_optionsRef$current, ref.current)) !== null && _optionsRef$current$g !== void 0 ? _optionsRef$current$g : ref.current;
};
var inputHandler = event => {
var input = getInputElement();
if (input && event.target === input) {
changeDispatched.current = true;
}
};
var focusHandler = event => {
var input = getInputElement();
if (input && event.target === input) {
focusDispatched.current = true;
}
};
var blurHandler = event => {
var input = getInputElement();
if (input && event.target === input) {
blurDispatched.current = true;
}
};
var submitHandler = event => {
var input = getInputElement();
if (input !== null && input !== void 0 && input.form && event.target === input.form) {
var _optionsRef$current2, _optionsRef$current2$;
(_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : (_optionsRef$current2$ = _optionsRef$current2.onSubmit) === null || _optionsRef$current2$ === void 0 ? void 0 : _optionsRef$current2$.call(_optionsRef$current2, event);
}
};
var resetHandler = event => {
var input = getInputElement();
if (input !== null && input !== void 0 && input.form && event.target === input.form) {
var _optionsRef$current3, _optionsRef$current3$;
(_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : (_optionsRef$current3$ = _optionsRef$current3.onReset) === null || _optionsRef$current3$ === void 0 ? void 0 : _optionsRef$current3$.call(_optionsRef$current3, event);
}
};
document.addEventListener('input', inputHandler, true);
document.addEventListener('focus', focusHandler, true);
document.addEventListener('blur', blurHandler, true);
document.addEventListener('submit', submitHandler);
document.addEventListener('reset', resetHandler);
return () => {
document.removeEventListener('input', inputHandler, true);
document.removeEventListener('focus', focusHandler, true);
document.removeEventListener('blur', blurHandler, true);
document.removeEventListener('submit', submitHandler);
document.removeEventListener('reset', resetHandler);
};
}, []);
var control = react.useMemo(() => {
var getInputElement = () => {
var _optionsRef$current$g3, _optionsRef$current4, _optionsRef$current4$;
return (_optionsRef$current$g3 = (_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 ? void 0 : (_optionsRef$current4$ = _optionsRef$current4.getElement) === null || _optionsRef$current4$ === void 0 ? void 0 : _optionsRef$current4$.call(_optionsRef$current4, ref.current)) !== null && _optionsRef$current$g3 !== void 0 ? _optionsRef$current$g3 : ref.current;
};
return {
change(eventOrValue) {
var input = getInputElement();
if (!input) {
console.warn('Missing input ref; No change-related events will be dispatched');
return;
}
if (changeDispatched.current) {
changeDispatched.current = false;
return;
}
var previousValue = input.value;
var nextValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
// This make sure no event is dispatched on the first effect run
if (nextValue === previousValue) {
return;
}
// Dispatch beforeinput event before updating the input value
input.dispatchEvent(new Event('beforeinput', {
bubbles: true
}));
// Update the input value to trigger a change event
setNativeValue(input, nextValue);
// Dispatch input event with the updated input value
input.dispatchEvent(new InputEvent('input', {
bubbles: true
}));
// Reset the dispatched flag
changeDispatched.current = false;
},
focus() {
var input = getInputElement();
if (!input) {
console.warn('Missing input ref; No focus-related events will be dispatched');
return;
}
if (focusDispatched.current) {
focusDispatched.current = false;
return;
}
var focusinEvent = new FocusEvent('focusin', {
bubbles: true
});
var focusEvent = new FocusEvent('focus');
input.dispatchEvent(focusinEvent);
input.dispatchEvent(focusEvent);
// Reset the dispatched flag
focusDispatched.current = false;
},
blur() {
var input = getInputElement();
if (!input) {
console.warn('Missing input ref; No blur-related events will be dispatched');
return;
}
if (blurDispatched.current) {
blurDispatched.current = false;
return;
}
var focusoutEvent = new FocusEvent('focusout', {
bubbles: true
});
var blurEvent = new FocusEvent('blur');
input.dispatchEvent(focusoutEvent);
input.dispatchEvent(blurEvent);
// Reset the dispatched flag
blurDispatched.current = false;
}
};
}, []);
return [ref, control];
}
exports.useControlledInput = useControlledInput;

@@ -581,1 +746,2 @@ exports.useFieldList = useFieldList;

exports.useForm = useForm;
exports.useInputEvent = useInputEvent;

@@ -47,2 +47,3 @@ 'use strict';

exports.useForm = hooks.useForm;
exports.useInputEvent = hooks.useInputEvent;
exports.conform = helpers;

@@ -0,1 +1,16 @@

/**
* Style to make the input element visually hidden
* Based on the `sr-only` class from tailwindcss
*/
var hiddenStyle = {
position: 'absolute',
width: '1px',
height: '1px',
padding: 0,
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
whiteSpace: 'nowrap',
border: 0
};
function input(config) {

@@ -20,2 +35,7 @@ var _config$initialError;

};
if (options !== null && options !== void 0 && options.hidden) {
attributes.style = hiddenStyle;
attributes.tabIndex = -1;
attributes['aria-hidden'] = true;
}
if (config.initialError && config.initialError.length > 0) {

@@ -33,3 +53,3 @@ attributes.autoFocus = true;

}
function select(config) {
function select(config, options) {
var _config$defaultValue, _config$initialError2;

@@ -46,2 +66,7 @@ var attributes = {

};
if (options !== null && options !== void 0 && options.hidden) {
attributes.style = hiddenStyle;
attributes.tabIndex = -1;
attributes['aria-hidden'] = true;
}
if (config.initialError && config.initialError.length > 0) {

@@ -52,3 +77,3 @@ attributes.autoFocus = true;

}
function textarea(config) {
function textarea(config, options) {
var _config$defaultValue2, _config$initialError3;

@@ -67,2 +92,7 @@ var attributes = {

};
if (options !== null && options !== void 0 && options.hidden) {
attributes.style = hiddenStyle;
attributes.tabIndex = -1;
attributes['aria-hidden'] = true;
}
if (config.initialError && config.initialError.length > 0) {

@@ -69,0 +99,0 @@ attributes.autoFocus = true;

import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
import { shouldValidate, reportSubmission, getFormData, parse, isFieldElement, hasError, getPaths, getName, requestCommand, validate, getFormElement, parseListCommand, updateList } from '@conform-to/dom';
import { useRef, useState, useEffect } from 'react';
import { useRef, useState, useEffect, useMemo, useLayoutEffect } from 'react';
import { input } from './helpers.js';

@@ -488,2 +488,3 @@

*
* @deprecated Please use the `useInputEvent` hook instead
* @see https://conform.guide/api/react#usecontrolledinput

@@ -544,15 +545,2 @@ */

ref,
style: {
position: 'absolute',
width: '1px',
height: '1px',
padding: 0,
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
whiteSpace: 'nowrap',
borderWidth: 0
},
tabIndex: -1,
'aria-hidden': true,
onFocus() {

@@ -562,3 +550,5 @@ var _inputRef$current;

}
}, input(_objectSpread2(_objectSpread2({}, config), uncontrolledState))), {
}, input(_objectSpread2(_objectSpread2({}, config), uncontrolledState), {
hidden: true
})), {
ref: inputRef,

@@ -572,2 +562,177 @@ value,

export { useControlledInput, useFieldList, useFieldset, useForm };
/**
* Triggering react custom change event
* Solution based on dom-testing-library
* @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
* @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
*/
function setNativeValue(element, value) {
if (element.value === value) {
// It will not trigger a change event if `element.value` is the same as the set value
return;
}
var {
set: valueSetter
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
var prototype = Object.getPrototypeOf(element);
var {
set: prototypeValueSetter
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
if (valueSetter) {
valueSetter.call(element, value);
} else {
throw new Error('The given element does not have a value setter');
}
}
}
/**
* useLayoutEffect is client-only.
* This basically makes it a no-op on server
*/
var useSafeLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect;
function useInputEvent(options) {
var ref = useRef(null);
var optionsRef = useRef(options);
var changeDispatched = useRef(false);
var focusDispatched = useRef(false);
var blurDispatched = useRef(false);
useSafeLayoutEffect(() => {
optionsRef.current = options;
});
useSafeLayoutEffect(() => {
var getInputElement = () => {
var _optionsRef$current$g, _optionsRef$current, _optionsRef$current$g2;
return (_optionsRef$current$g = (_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 ? void 0 : (_optionsRef$current$g2 = _optionsRef$current.getElement) === null || _optionsRef$current$g2 === void 0 ? void 0 : _optionsRef$current$g2.call(_optionsRef$current, ref.current)) !== null && _optionsRef$current$g !== void 0 ? _optionsRef$current$g : ref.current;
};
var inputHandler = event => {
var input = getInputElement();
if (input && event.target === input) {
changeDispatched.current = true;
}
};
var focusHandler = event => {
var input = getInputElement();
if (input && event.target === input) {
focusDispatched.current = true;
}
};
var blurHandler = event => {
var input = getInputElement();
if (input && event.target === input) {
blurDispatched.current = true;
}
};
var submitHandler = event => {
var input = getInputElement();
if (input !== null && input !== void 0 && input.form && event.target === input.form) {
var _optionsRef$current2, _optionsRef$current2$;
(_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : (_optionsRef$current2$ = _optionsRef$current2.onSubmit) === null || _optionsRef$current2$ === void 0 ? void 0 : _optionsRef$current2$.call(_optionsRef$current2, event);
}
};
var resetHandler = event => {
var input = getInputElement();
if (input !== null && input !== void 0 && input.form && event.target === input.form) {
var _optionsRef$current3, _optionsRef$current3$;
(_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : (_optionsRef$current3$ = _optionsRef$current3.onReset) === null || _optionsRef$current3$ === void 0 ? void 0 : _optionsRef$current3$.call(_optionsRef$current3, event);
}
};
document.addEventListener('input', inputHandler, true);
document.addEventListener('focus', focusHandler, true);
document.addEventListener('blur', blurHandler, true);
document.addEventListener('submit', submitHandler);
document.addEventListener('reset', resetHandler);
return () => {
document.removeEventListener('input', inputHandler, true);
document.removeEventListener('focus', focusHandler, true);
document.removeEventListener('blur', blurHandler, true);
document.removeEventListener('submit', submitHandler);
document.removeEventListener('reset', resetHandler);
};
}, []);
var control = useMemo(() => {
var getInputElement = () => {
var _optionsRef$current$g3, _optionsRef$current4, _optionsRef$current4$;
return (_optionsRef$current$g3 = (_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 ? void 0 : (_optionsRef$current4$ = _optionsRef$current4.getElement) === null || _optionsRef$current4$ === void 0 ? void 0 : _optionsRef$current4$.call(_optionsRef$current4, ref.current)) !== null && _optionsRef$current$g3 !== void 0 ? _optionsRef$current$g3 : ref.current;
};
return {
change(eventOrValue) {
var input = getInputElement();
if (!input) {
console.warn('Missing input ref; No change-related events will be dispatched');
return;
}
if (changeDispatched.current) {
changeDispatched.current = false;
return;
}
var previousValue = input.value;
var nextValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
// This make sure no event is dispatched on the first effect run
if (nextValue === previousValue) {
return;
}
// Dispatch beforeinput event before updating the input value
input.dispatchEvent(new Event('beforeinput', {
bubbles: true
}));
// Update the input value to trigger a change event
setNativeValue(input, nextValue);
// Dispatch input event with the updated input value
input.dispatchEvent(new InputEvent('input', {
bubbles: true
}));
// Reset the dispatched flag
changeDispatched.current = false;
},
focus() {
var input = getInputElement();
if (!input) {
console.warn('Missing input ref; No focus-related events will be dispatched');
return;
}
if (focusDispatched.current) {
focusDispatched.current = false;
return;
}
var focusinEvent = new FocusEvent('focusin', {
bubbles: true
});
var focusEvent = new FocusEvent('focus');
input.dispatchEvent(focusinEvent);
input.dispatchEvent(focusEvent);
// Reset the dispatched flag
focusDispatched.current = false;
},
blur() {
var input = getInputElement();
if (!input) {
console.warn('Missing input ref; No blur-related events will be dispatched');
return;
}
if (blurDispatched.current) {
blurDispatched.current = false;
return;
}
var focusoutEvent = new FocusEvent('focusout', {
bubbles: true
});
var blurEvent = new FocusEvent('blur');
input.dispatchEvent(focusoutEvent);
input.dispatchEvent(blurEvent);
// Reset the dispatched flag
blurDispatched.current = false;
}
};
}, []);
return [ref, control];
}
export { useControlledInput, useFieldList, useFieldset, useForm, useInputEvent };
export { getFormElements, hasError, list, parse, requestCommand, requestSubmit, shouldValidate, validate } from '@conform-to/dom';
export { useControlledInput, useFieldList, useFieldset, useForm } from './hooks.js';
export { useControlledInput, useFieldList, useFieldset, useForm, useInputEvent } from './hooks.js';
import * as helpers from './helpers.js';
export { helpers as conform };

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

"license": "MIT",
"version": "0.5.0",
"version": "0.5.1",
"main": "index.js",

@@ -23,3 +23,3 @@ "module": "module/index.js",

"dependencies": {
"@conform-to/dom": "0.5.0"
"@conform-to/dom": "0.5.1"
},

@@ -26,0 +26,0 @@ "peerDependencies": {

@@ -12,4 +12,8 @@ # @conform-to/react

- [useFieldList](#usefieldlist)
- [useInputEvent](#useinputevent)
- [useControlledInput](#usecontrolledinput)
- [conform](#conform)
- [list](#list)
- [validate](#validate)
- [requestCommand](#requestcommand)
- [getFormElements](#getformelements)

@@ -282,4 +286,54 @@ - [hasError](#haserror)

### useInputEvent
It returns a ref object and a set of helpers that dispatch corresponding dom event.
```tsx
import { useForm, useInputEvent } from '@conform-to/react';
import { Select, MenuItem } from '@mui/material';
import { useState, useRef } from 'react';
function MuiForm() {
const [form, { category }] = useForm();
const [value, setValue] = useState(category.config.defaultValue ?? '');
const [ref, control] = useInputEvent({
onReset: () => setValue(category.config.defaultValue ?? ''),
});
const inputRef = useRef<HTMLInputElement>(null);
return (
<form {...form.props}>
{/* Render a shadow input somewhere */}
<input
ref={ref}
{...conform.input(category.config, { hidden: true })}
onChange={(e) => setValue(e.target.value)}
onFocus={() => inputRef.current?.focus()}
/>
{/* MUI Select is a controlled component */}
<TextField
label="Category"
inputRef={inputRef}
value={value}
onChange={control.change}
onBlur={control.blur}
select
>
<MenuItem value="">Please select</MenuItem>
<MenuItem value="a">Category A</MenuItem>
<MenuItem value="b">Category B</MenuItem>
<MenuItem value="c">Category C</MenuItem>
</TextField>
</form>
);
}
```
---
### useControlledInput
> This API is deprecated and replaced with the [useInputEvent](#useinputevent) hook.
It returns the properties required to configure a shadow input for validation and helper to integrate it. This is particularly useful when [integrating custom input components](/docs/integrations.md#custom-input-component) like dropdown and datepicker.

@@ -290,3 +344,2 @@

import { Select, MenuItem } from '@mui/material';
import { useRef } from 'react';

@@ -293,0 +346,0 @@ function MuiForm() {

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