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

@umbraco-ui/uui-base

Package Overview
Dependencies
Maintainers
7
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@umbraco-ui/uui-base - npm Package Compare versions

Comparing version 1.3.0-rc.0 to 1.3.0-rc.1

2

lib/events/index.js

@@ -70,4 +70,4 @@ var __defProp$2 = Object.defineProperty;

UUISelectableEvent.SELECTED = "selected";
UUISelectableEvent.UNSELECTED = "unselected";
UUISelectableEvent.DESELECTED = "deselected";
export { UUIEvent, UUIFormControlEvent, UUISelectableEvent };

@@ -5,4 +5,4 @@ import { SelectableMixinInterface } from '../mixins';

static readonly SELECTED = "selected";
static readonly UNSELECTED = "unselected";
static readonly DESELECTED = "deselected";
constructor(evName: string, eventInit?: any | null);
}
import 'lit';
import 'lit/decorators.js';
export * from './animations';
export * from './events';
export * from './mixins';
export * from './registration';
export * from './types';
export * from './utils';

@@ -1,2 +0,693 @@

import 'lit';
import 'lit/decorators.js';
import { css, unsafeCSS, html } from 'lit';
import { property, state } from 'lit/decorators.js';
const UUIBlinkKeyframes = css`
@keyframes uui-blink {
0%,
100% {
opacity: 0.5;
}
50% {
opacity: 1;
}
}
`;
const UUIBlinkAnimationValue = unsafeCSS("uui-blink 0.9s infinite both");
const UUIHorizontalPulseKeyframes = css`
@keyframes pulse {
0% {
-webkit-transform: translate(-50%, -50%) scale(0.2);
transform: translate(-50%, -50%) scale(0.2);
opacity: 0.9;
}
80% {
-webkit-transform: translate(-50%, -50%) scale(1.2);
transform: translate(-50%, -50%) scale(1.2);
opacity: 0;
}
100% {
-webkit-transform: translate(-50%, -50%) scale(2.2);
transform: translate(-50%, -50%) scale(2.2);
opacity: 0;
}
}
`;
const UUIHorizontalPulseAnimationValue = unsafeCSS(
"pulse 0.8s ease-in-out infinite both"
);
const UUIHorizontalShakeKeyframes = css`
@keyframes uui-horizontal-shake {
10%,
90% {
transform: translateX(-1px);
}
20%,
80% {
transform: translateX(1px);
}
30%,
50%,
70% {
transform: translateX(-2px);
}
40%,
60% {
transform: translateX(2px);
}
}
`;
const UUIHorizontalShakeAnimationValue = unsafeCSS(
"uui-horizontal-shake 600ms ease backwards"
);
var __defProp$7 = Object.defineProperty;
var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols;
var __hasOwnProp$2 = Object.prototype.hasOwnProperty;
var __propIsEnum$2 = Object.prototype.propertyIsEnumerable;
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues$2 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp$2.call(b, prop))
__defNormalProp$2(a, prop, b[prop]);
if (__getOwnPropSymbols$2)
for (var prop of __getOwnPropSymbols$2(b)) {
if (__propIsEnum$2.call(b, prop))
__defNormalProp$2(a, prop, b[prop]);
}
return a;
};
class UUIEvent extends Event {
constructor(evName, eventInit = {}) {
super(evName, __spreadValues$2({}, eventInit));
this.detail = eventInit.detail || {};
}
}
var __defProp$6 = Object.defineProperty;
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues$1 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp$1.call(b, prop))
__defNormalProp$1(a, prop, b[prop]);
if (__getOwnPropSymbols$1)
for (var prop of __getOwnPropSymbols$1(b)) {
if (__propIsEnum$1.call(b, prop))
__defNormalProp$1(a, prop, b[prop]);
}
return a;
};
class UUIFormControlEvent extends UUIEvent {
constructor(evName, eventInit = {}) {
super(evName, __spreadValues$1(__spreadValues$1({}, { bubbles: true }), eventInit));
}
}
UUIFormControlEvent.VALID = "valid";
UUIFormControlEvent.INVALID = "invalid";
var __defProp$5 = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
class UUISelectableEvent extends UUIEvent {
constructor(evName, eventInit = {}) {
super(evName, __spreadValues(__spreadValues({}, { bubbles: true, cancelable: true }), eventInit));
}
}
UUISelectableEvent.SELECTED = "selected";
UUISelectableEvent.DESELECTED = "deselected";
var __defProp$4 = Object.defineProperty;
var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
var __decorateClass$4 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$4(target, key, result);
return result;
};
const ActiveMixin = (superClass) => {
class ActiveMixinClass extends superClass {
constructor() {
super(...arguments);
this.active = false;
}
}
__decorateClass$4([
property({ type: Boolean, reflect: true })
], ActiveMixinClass.prototype, "active", 2);
return ActiveMixinClass;
};
var __defProp$3 = Object.defineProperty;
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
var __decorateClass$3 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$3(target, key, result);
return result;
};
const LabelMixin = (labelSlotName, superClass) => {
class LabelMixinClass extends superClass {
constructor() {
super(...arguments);
this._labelSlotHasContent = false;
}
connectedCallback() {
super.connectedCallback();
if (!this.label) {
console.warn(this.tagName + " needs a `label`", this);
}
}
labelSlotChanged(e) {
this._labelSlotHasContent = e.target.assignedNodes({ flatten: true }).length > 0;
}
/**
* Call in the mixed element to render the label template. It contains a slot. This is optional.
* @method renderLabel
* @returns {TemplateResult}
*/
renderLabel() {
return html`
${this._labelSlotHasContent === false ? html`<span class="label">${this.label}</span>` : ""}
<slot
class="label"
style=${this._labelSlotHasContent ? "" : "visibility: hidden"}
name=${labelSlotName ? labelSlotName : ""}
@slotchange=${this.labelSlotChanged}></slot>
`;
}
}
__decorateClass$3([
property({ type: String })
], LabelMixinClass.prototype, "label", 2);
__decorateClass$3([
state()
], LabelMixinClass.prototype, "_labelSlotHasContent", 2);
return LabelMixinClass;
};
var __defProp$2 = Object.defineProperty;
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
var __decorateClass$2 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$2(target, key, result);
return result;
};
const SelectableMixin = (superClass) => {
class SelectableMixinClass extends superClass {
constructor(...args) {
super(...args);
this._selectable = false;
this.deselectable = true;
this.selected = false;
this.selectableTarget = this;
this.addEventListener("click", this._handleClick);
this.addEventListener("keydown", this.handleSelectKeydown);
}
get selectable() {
return this._selectable;
}
set selectable(newVal) {
const oldVal = this._selectable;
this._selectable = newVal;
this.setAttribute("tabindex", `${newVal ? "0" : "-1"}`);
this.requestUpdate("selected", oldVal);
}
handleSelectKeydown(e) {
if (e.composedPath().indexOf(this.selectableTarget) !== -1) {
if (e.key !== " " && e.key !== "Enter")
return;
this._toggleSelect();
}
}
_select() {
if (!this.selectable)
return;
const selectEvent = new UUISelectableEvent(UUISelectableEvent.SELECTED);
this.dispatchEvent(selectEvent);
if (selectEvent.defaultPrevented)
return;
this.selected = true;
}
_deselect() {
if (!this.deselectable)
return;
const selectEvent = new UUISelectableEvent(UUISelectableEvent.DESELECTED);
this.dispatchEvent(selectEvent);
if (selectEvent.defaultPrevented)
return;
this.selected = false;
}
_handleClick(e) {
if (e.composedPath().indexOf(this.selectableTarget) !== -1) {
this._toggleSelect();
}
}
_toggleSelect() {
if (this.deselectable === false) {
this._select();
} else {
this.selected ? this._deselect() : this._select();
}
}
}
__decorateClass$2([
property({ type: Boolean, reflect: true })
], SelectableMixinClass.prototype, "selectable", 1);
__decorateClass$2([
property({ type: Boolean, reflect: true })
], SelectableMixinClass.prototype, "selected", 2);
return SelectableMixinClass;
};
var __defProp$1 = Object.defineProperty;
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
var __decorateClass$1 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$1(target, key, result);
return result;
};
const SelectOnlyMixin = (superClass) => {
class SelectOnlyMixinClass extends superClass {
constructor() {
super(...arguments);
this._selectOnly = false;
}
get selectOnly() {
return this._selectOnly;
}
set selectOnly(newVal) {
const oldVal = this._selectOnly;
this._selectOnly = newVal;
this.requestUpdate("selectOnly", oldVal);
}
}
__decorateClass$1([
property({ type: Boolean, reflect: true, attribute: "select-only" })
], SelectOnlyMixinClass.prototype, "selectOnly", 1);
return SelectOnlyMixinClass;
};
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp(target, key, result);
return result;
};
const FormControlMixin = (superClass) => {
class FormControlMixinClass extends superClass {
constructor(...args) {
super(...args);
this.name = "";
// Validation
this._validityState = {};
this.pristine = true;
this.required = false;
this.requiredMessage = "This field is required";
this.error = false;
this.errorMessage = "This field is invalid";
this._value = "";
this._form = null;
this._validators = [];
this._formCtrlElements = [];
this._onFormSubmit = () => {
this.pristine = false;
};
this._internals = this.attachInternals();
this.addValidator(
"valueMissing",
() => this.requiredMessage,
() => this.hasAttribute("required") && this.hasValue() === false
);
this.addValidator(
"customError",
() => this.errorMessage,
() => this.error
);
this.addEventListener("blur", () => {
this.pristine = false;
});
}
// Do not 'reflect' as the attribute is used as fallback.
get value() {
return this._value;
}
set value(newValue) {
const oldValue = this._value;
this._value = newValue;
if ("ElementInternals" in window && //@ts-ignore
"setFormValue" in window.ElementInternals.prototype) {
this._internals.setFormValue(this._value);
}
this.requestUpdate("value", oldValue);
}
/**
* Determn wether this FormControl has a value.
* @method hasValue
* @returns {boolean}
*/
hasValue() {
return this.value !== "";
}
disconnectedCallback() {
super.disconnectedCallback();
this._removeFormListeners();
}
_removeFormListeners() {
if (this._form) {
this._form.removeEventListener("submit", this._onFormSubmit);
}
}
/**
* Add validator, to validate this Form Control.
* See https://developer.mozilla.org/en-US/docs/Web/API/ValidityState for available Validator FlagTypes.
*
* @example
* this.addValidator(
* 'tooLong',
* () => 'This input contains too many characters',
* () => this._value.length > 10
* );
* @method hasValue
* @param {FlagTypes} flagKey the type of validation.
* @param {method} getMessageMethod method to retrieve relevant message. Is executed every time the validator is re-executed.
* @param {method} checkMethod method to determine if this validator should invalidate this form control. Return true if this should prevent submission.
*/
addValidator(flagKey, getMessageMethod, checkMethod) {
const obj = {
flagKey,
getMessageMethod,
checkMethod
};
this._validators.push(obj);
return obj;
}
removeValidator(validator) {
const index = this._validators.indexOf(validator);
if (index !== -1) {
this._validators.splice(index, 1);
}
}
/**
* @method addFormControlElement
* @description Important notice if adding a native form control then ensure that its value and thereby validity is updated when value is changed from the outside.
* @param element {NativeFormControlElement} - element to validate and include as part of this form association.
*/
addFormControlElement(element) {
this._formCtrlElements.push(element);
}
/**
* @method setCustomValidity
* @description Set custom validity state, set to empty string to remove the custom message.
* @param message {string} - The message to be shown
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity|HTMLObjectElement:setCustomValidity}
*/
setCustomValidity(message) {
if (this._customValidityObject) {
this.removeValidator(this._customValidityObject);
}
if (message != null && message !== "") {
this._customValidityObject = this.addValidator(
"customError",
() => message,
() => true
);
}
this._runValidators();
}
_runValidators() {
this._validityState = {};
this._formCtrlElements.forEach((formCtrlEl) => {
for (const key in formCtrlEl.validity) {
if (key !== "valid" && formCtrlEl.validity[key]) {
this._validityState[key] = true;
this._internals.setValidity(
this._validityState,
formCtrlEl.validationMessage,
formCtrlEl
);
}
}
});
this._validators.forEach((validator) => {
if (validator.checkMethod()) {
this._validityState[validator.flagKey] = true;
this._internals.setValidity(
this._validityState,
validator.getMessageMethod(),
this.getFormElement()
);
}
});
const hasError = Object.values(this._validityState).includes(true);
this._validityState.valid = !hasError;
if (hasError) {
this.dispatchEvent(
new UUIFormControlEvent(UUIFormControlEvent.INVALID)
);
} else {
this._internals.setValidity({});
this.dispatchEvent(new UUIFormControlEvent(UUIFormControlEvent.VALID));
}
}
updated(changedProperties) {
super.updated(changedProperties);
this._runValidators();
}
submit() {
var _a;
(_a = this._form) == null ? void 0 : _a.requestSubmit();
}
formAssociatedCallback() {
this._removeFormListeners();
this._form = this._internals.form;
if (this._form) {
if (this._form.hasAttribute("submit-invalid")) {
this.pristine = false;
}
this._form.addEventListener("submit", this._onFormSubmit);
}
}
formResetCallback() {
this.pristine = true;
this.value = this.getAttribute("value") || "";
}
checkValidity() {
var _a;
for (const key in this._formCtrlElements) {
if (this._formCtrlElements[key].checkValidity() === false) {
return false;
}
}
return (_a = this._internals) == null ? void 0 : _a.checkValidity();
}
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity
get validity() {
return this._validityState;
}
get validationMessage() {
var _a;
return (_a = this._internals) == null ? void 0 : _a.validationMessage;
}
}
/**
* This is a static class field indicating that the element is can be used inside a native form and participate in its events.
* It may require a polyfill, check support here https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals.
* Read more about form controls here https://web.dev/more-capable-form-controls/
* @type {boolean}
*/
FormControlMixinClass.formAssociated = true;
__decorateClass([
property({ type: String })
], FormControlMixinClass.prototype, "name", 2);
__decorateClass([
property()
], FormControlMixinClass.prototype, "value", 1);
__decorateClass([
property({ type: Boolean, reflect: true })
], FormControlMixinClass.prototype, "pristine", 2);
__decorateClass([
property({ type: Boolean, reflect: true })
], FormControlMixinClass.prototype, "required", 2);
__decorateClass([
property({ type: String, attribute: "required-message" })
], FormControlMixinClass.prototype, "requiredMessage", 2);
__decorateClass([
property({ type: Boolean, reflect: true })
], FormControlMixinClass.prototype, "error", 2);
__decorateClass([
property({ type: String, attribute: "error-message" })
], FormControlMixinClass.prototype, "errorMessage", 2);
return FormControlMixinClass;
};
function defineElement(name, options) {
return (constructor) => {
const isValidElementName = name.indexOf("-") > 0;
if (isValidElementName === false) {
console.error(
`${name} is not a valid custom element name. A custom element name should consist of at least two words separated by a hyphen.`
);
return;
}
const existingElement = window.customElements.get(name);
if (!existingElement) {
window.customElements.define(name, constructor, options);
}
};
}
const InterfaceLookValues = [
"default",
"primary",
"secondary",
"outline",
"placeholder"
];
const InterfaceColorValues = [
"default",
"positive",
"warning",
"danger"
];
class Timer {
constructor(_callback, duration) {
this._callback = _callback;
this._timerId = null;
this._remaining = null;
this._onComplete = () => {
this._remaining = null;
this._callback();
};
this.setDuration(duration);
}
setDuration(duration) {
this._duration = duration;
if (this._timerId !== null) {
this.restart();
}
}
/** starts the timer */
start() {
if (this._timerId === null) {
this.resume();
}
}
/** restarts the timer by setting remaining time to duration. */
restart() {
this._remaining = this._duration;
this.resume();
}
pause() {
if (this._timerId !== null) {
window.clearTimeout(this._timerId);
this._timerId = null;
if (this._remaining !== null) {
this._remaining -= Date.now() - this._startTime;
}
}
}
resume() {
if (this._timerId !== null) {
window.clearTimeout(this._timerId);
}
if (this._remaining === null) {
this._remaining = this._duration;
}
this._startTime = Date.now();
this._timerId = window.setTimeout(this._onComplete, this._remaining);
}
destroy() {
this.pause();
}
}
const demandCustomElement = (requester, elementName, message = `This element has to be present for ${requester.nodeName} to work appropriate.`) => {
if (!customElements.get(elementName)) {
console.warn(
`%c ${requester.nodeName} requires ${elementName} element to be registered!`,
"font-weight: bold;",
message,
requester
);
}
};
const drag = (container, options) => {
function move(pointerEvent) {
const dims = container.getBoundingClientRect();
const defaultView = container.ownerDocument.defaultView;
const offsetX = dims.left + defaultView.pageXOffset;
const offsetY = dims.top + defaultView.pageYOffset;
const x = pointerEvent.pageX - offsetX;
const y = pointerEvent.pageY - offsetY;
if (options == null ? void 0 : options.onMove) {
options.onMove(x, y);
}
}
function stop() {
document.removeEventListener("pointermove", move);
document.removeEventListener("pointerup", stop);
if (options == null ? void 0 : options.onStop) {
options.onStop();
}
}
document.addEventListener("pointermove", move, { passive: true });
document.addEventListener("pointerup", stop);
if (options == null ? void 0 : options.initialEvent) {
move(options.initialEvent);
}
};
const clamp = (value, min, max) => {
return Math.min(Math.max(value, min), max);
};
const reverseNumberInRange = (num, min, max) => {
return max + min - num;
};
const toHex = (value) => {
const hex = Math.round(value).toString(16);
return hex.length === 1 ? `0${hex}` : hex;
};
export { ActiveMixin, FormControlMixin, InterfaceColorValues, InterfaceLookValues, LabelMixin, SelectOnlyMixin, SelectableMixin, Timer, UUIBlinkAnimationValue, UUIBlinkKeyframes, UUIEvent, UUIFormControlEvent, UUIHorizontalPulseAnimationValue, UUIHorizontalPulseKeyframes, UUIHorizontalShakeAnimationValue, UUIHorizontalShakeKeyframes, UUISelectableEvent, clamp, defineElement, demandCustomElement, drag, reverseNumberInRange, toHex };

@@ -124,3 +124,3 @@ import { property, state } from 'lit/decorators.js';

UUISelectableEvent$1.SELECTED = "selected";
UUISelectableEvent$1.UNSELECTED = "unselected";
UUISelectableEvent$1.DESELECTED = "deselected";

@@ -143,3 +143,3 @@ var __defProp$5 = Object.defineProperty;

this._selectable = false;
this.unselectable = true;
this.deselectable = true;
this.selected = false;

@@ -175,6 +175,6 @@ this.selectableTarget = this;

}
_unselect() {
if (!this.unselectable)
_deselect() {
if (!this.deselectable)
return;
const selectEvent = new UUISelectableEvent$1(UUISelectableEvent$1.UNSELECTED);
const selectEvent = new UUISelectableEvent$1(UUISelectableEvent$1.DESELECTED);
this.dispatchEvent(selectEvent);

@@ -191,6 +191,6 @@ if (selectEvent.defaultPrevented)

_toggleSelect() {
if (this.unselectable === false) {
if (this.deselectable === false) {
this._select();
} else {
this.selected ? this._unselect() : this._select();
this.selected ? this._deselect() : this._select();
}

@@ -307,3 +307,3 @@ }

UUISelectableEvent.SELECTED = "selected";
UUISelectableEvent.UNSELECTED = "unselected";
UUISelectableEvent.DESELECTED = "deselected";

@@ -310,0 +310,0 @@ var __defProp = Object.defineProperty;

@@ -5,3 +5,3 @@ import { LitElement } from 'lit';

selectable: boolean;
unselectable: boolean;
deselectable: boolean;
selected: boolean;

@@ -8,0 +8,0 @@ selectableTarget: EventTarget;

{
"name": "@umbraco-ui/uui-base",
"version": "1.3.0-rc.0",
"version": "1.3.0-rc.1",
"license": "MIT",

@@ -34,3 +34,3 @@ "keywords": [

"build": "tsc --build --force && rollup -c rollup.config.js",
"clean": "tsc --build --clean && rimraf -g dist lib/*.js lib/**/*.js"
"clean": "tsc --build --clean && rimraf -g dist lib/*.js lib/**/*.js *.tgz lib/**/*.d.ts"
},

@@ -41,3 +41,3 @@ "publishConfig": {

"homepage": "https://uui.umbraco.com/?path=/story/uui-base",
"gitHead": "45c3824056586d9817efb3f61dc0bef5478747f0"
"gitHead": "0c517175884931aa0bc0d8f05974852a7704626e"
}
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