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

actionsflow

Package Overview
Dependencies
Maintainers
1
Versions
140
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

actionsflow - npm Package Compare versions

Comparing version 0.0.6 to 0.0.7

LICENSE

11

CHANGELOG.md

@@ -6,2 +6,13 @@ # Change Log

## [0.0.7](https://github.com/actionsflow/actionsflow/compare/actionsflow@0.0.6...actionsflow@0.0.7) (2020-08-19)
### Features
* 🎸 add actionsflow-interface ([f7ba6a9](https://github.com/actionsflow/actionsflow/commit/f7ba6a91c8083f3379c70735975020386d1dc86a))
## 0.0.6 (2020-08-19)

@@ -8,0 +19,0 @@

4

dist/src/trigger.d.ts

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

import { TriggerName, ITriggerContext, ITriggerResult } from "./interfaces";
import { ITriggerContext, ITriggerResult } from "actionsflow-interface";
interface ITriggerOptions {
trigger: {
name: TriggerName;
name: string;
options: Record<string, unknown>;

@@ -6,0 +6,0 @@ workflowRelativePath: string;

@@ -5,12 +5,6 @@ "use strict";

const tslib_1 = require("tslib");
const triggers_1 = require("./triggers");
const Triggers = tslib_1.__importStar(require("./triggers"));
const helpers_1 = require("./helpers");
const log_1 = tslib_1.__importDefault(require("./log"));
const MAX_CACHE_KEYS_COUNT = 1000;
const triggerNameMap = {
rss: triggers_1.Rss,
poll: triggers_1.Poll,
webhook: triggers_1.Webhook,
telegram_bot: triggers_1.TelegramBot,
};
const MAX_CACHE_KEYS_COUNT = 5000;
exports.run = ({ trigger, context, }) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {

@@ -32,3 +26,14 @@ log_1.default.debug("trigger:", trigger);

};
if (triggerNameMap[trigger.name]) {
const AllTriggers = Triggers;
const triggersKeys = Object.keys(AllTriggers);
const TriggerMap = {};
triggersKeys.forEach((triggerKey) => {
const triggerInstance = new AllTriggers[triggerKey]({
options: {},
context: context,
helpers: { createContentDigest: helpers_1.createContentDigest, cache: helpers_1.getCache(`trigger-temp`) },
});
TriggerMap[triggerInstance.name] = AllTriggers[triggerKey];
});
if (TriggerMap[trigger.name]) {
const triggerHelpers = {

@@ -43,6 +48,6 @@ createContentDigest: helpers_1.createContentDigest,

};
const Trigger = triggerNameMap[trigger.name];
const triggerInstance = new Trigger();
const triggerResult = yield triggerInstance.run(triggerOptions);
const { shouldDeduplicate, getItemKey, updateInterval } = triggerResult;
const Trigger = TriggerMap[trigger.name];
const triggerInstance = new Trigger(triggerOptions);
const triggerResult = yield triggerInstance.run();
const { shouldDeduplicate, getItemKey, every } = triggerInstance;
let { items } = triggerResult;

@@ -56,4 +61,4 @@ const maxItemsCount = trigger.options.max_items_count;

log_1.default.debug("lastUpdatedAt: ", lastUpdatedAt);
if (updateInterval) {
const shouldUpdateUtil = lastUpdatedAt + updateInterval * 60 * 1000;
if (every) {
const shouldUpdateUtil = lastUpdatedAt + every * 60 * 1000;
const now = Date.now();

@@ -60,0 +65,0 @@ const shouldUpdate = shouldUpdateUtil - now <= 0;

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

import { ITriggerClassType, ITriggerRunFunction, ITriggerRunFunctionResult, TriggerName } from "../interfaces";
import { ITriggerClassType, ITriggerContructorParams, ITriggerRunFunctionResult, IHelpers, AnyObject } from "actionsflow-interface";
export default class Poll implements ITriggerClassType {
id: TriggerName;
run({ helpers, options, }: ITriggerRunFunction): Promise<ITriggerRunFunctionResult>;
name: string;
options: AnyObject;
helpers: IHelpers;
every: number;
shouldDeduplicate: boolean;
getItemKey(item: AnyObject): string;
constructor({ helpers, options }: ITriggerContructorParams);
run(): Promise<ITriggerRunFunctionResult>;
}

@@ -9,9 +9,29 @@ "use strict";

class Poll {
constructor() {
this.id = "poll";
constructor({ helpers, options }) {
this.name = "poll";
this.options = {};
this.every = 5;
this.shouldDeduplicate = true;
this.options = options;
this.helpers = helpers;
if (options.every) {
this.every = options.every;
}
}
run({ helpers, options, }) {
getItemKey(item) {
const deduplication_key = this.options.deduplication_key;
if (deduplication_key) {
return item[deduplication_key];
}
if (item.id)
return item.id;
if (item.guid)
return item.guid;
if (item.key)
return item.key;
return this.helpers.createContentDigest(item);
}
run() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const _a = options, { url, items_path, deduplication_key, every } = _a, requestOptions = tslib_1.__rest(_a, ["url", "items_path", "deduplication_key", "every"]);
const updateInterval = every || 5;
const _a = this.options, { url, items_path } = _a, requestOptions = tslib_1.__rest(_a, ["url", "items_path"]);
if (!url) {

@@ -42,19 +62,4 @@ throw new Error("Miss param url!");

}
const getItemKey = (item) => {
if (deduplication_key) {
return item[deduplication_key];
}
if (item.id)
return item.id;
if (item.guid)
return item.guid;
if (item.key)
return item.key;
return helpers.createContentDigest(item);
};
return {
shouldDeduplicate: true,
updateInterval: updateInterval,
items,
getItemKey,
};

@@ -61,0 +66,0 @@ });

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

import { ITriggerClassType, ITriggerRunFunction, TriggerName, ITriggerRunFunctionResult } from "../interfaces";
import { ITriggerClassType, ITriggerContructorParams, ITriggerRunFunctionResult, IHelpers, AnyObject } from "actionsflow-interface";
export default class Rss implements ITriggerClassType {
id: TriggerName;
run({ helpers, options, }: ITriggerRunFunction): Promise<ITriggerRunFunctionResult>;
name: string;
options: AnyObject;
helpers: IHelpers;
every: number;
shouldDeduplicate: boolean;
getItemKey(item: AnyObject): string;
constructor({ helpers, options }: ITriggerContructorParams);
run(): Promise<ITriggerRunFunctionResult>;
}

@@ -7,13 +7,29 @@ "use strict";

class Rss {
constructor() {
this.id = "rss";
constructor({ helpers, options }) {
this.name = "rss";
this.options = {};
this.every = 5;
this.shouldDeduplicate = true;
this.options = options;
this.helpers = helpers;
if (!options.event) {
this.options.event = "new_item";
}
if (options.every) {
this.every = options.every;
}
}
run({ helpers, options, }) {
getItemKey(item) {
if (item.guid)
return item.guid;
if (item.id)
return item.id;
return this.helpers.createContentDigest(item);
}
run() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const event = options.event || "new_item";
const url = options.url;
const updateInterval = options.every || 5;
const { event, url } = this.options;
let urls = [];
if (event === "new_item_in_multiple_feeds") {
const urlsParam = options.urls;
const urlsParam = this.options.urls;
if (!urlsParam) {

@@ -58,14 +74,4 @@ throw new Error("Miss param urls");

}
const getItemKey = (item) => {
if (item.guid)
return item.guid;
if (item.id)
return item.id;
return helpers.createContentDigest(item);
};
return {
shouldDeduplicate: true,
updateInterval: updateInterval,
items,
getItemKey,
};

@@ -72,0 +78,0 @@ });

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

import { ITriggerClassType, ITriggerRunFunction, TriggerName, ITriggerRunFunctionResult } from "../interfaces";
import { ITriggerClassType, ITriggerContructorParams, ITriggerRunFunctionResult, AnyObject, IHelpers } from "actionsflow-interface";
export default class TelegramBot implements ITriggerClassType {
id: TriggerName;
run({ helpers, options, }: ITriggerRunFunction): Promise<ITriggerRunFunctionResult>;
name: string;
options: AnyObject;
helpers: IHelpers;
every: number;
shouldDeduplicate: boolean;
getItemKey: (item: AnyObject) => string;
constructor({ helpers, options }: ITriggerContructorParams);
run(): Promise<ITriggerRunFunctionResult>;
}

@@ -7,6 +7,18 @@ "use strict";

class TelegramBot {
constructor() {
this.id = "telegram_bot";
constructor({ helpers, options }) {
this.name = "telegram_bot";
this.every = 5;
this.shouldDeduplicate = true;
this.getItemKey = (item) => {
if (item.update_id)
return item.update_id;
return this.helpers.createContentDigest(item);
};
this.options = options;
this.helpers = helpers;
if (options.every) {
this.every = options.every;
}
}
run({ helpers, options, }) {
run() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {

@@ -43,5 +55,4 @@ const _messageTypes = [

];
const _a = options, { token, every, event } = _a, requestOptions = tslib_1.__rest(_a, ["token", "every", "event"]);
let { events } = options;
const updateInterval = every || 5;
const _a = this.options, { token, event } = _a, requestOptions = tslib_1.__rest(_a, ["token", "event"]);
let { events } = this.options;
if (!token) {

@@ -86,12 +97,4 @@ throw new Error("Miss param token!");

}
const getItemKey = (item) => {
if (item.update_id)
return item.update_id;
return helpers.createContentDigest(item);
};
return {
shouldDeduplicate: true,
updateInterval: updateInterval,
items,
getItemKey,
};

@@ -98,0 +101,0 @@ });

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

import { ITriggerClassType, ITriggerRunFunction, ITriggerRunFunctionResult, TriggerName } from "../interfaces";
import { ITriggerClassType, ITriggerContructorParams, ITriggerRunFunctionResult, ITriggerContext } from "actionsflow-interface";
export default class Webhook implements ITriggerClassType {
id: TriggerName;
run({ context, }: ITriggerRunFunction): Promise<ITriggerRunFunctionResult>;
name: string;
context: ITriggerContext;
constructor({ context }: ITriggerContructorParams);
run(): Promise<ITriggerRunFunctionResult>;
}

@@ -5,7 +5,9 @@ "use strict";

class Webhook {
constructor() {
this.id = "webhook";
constructor({ context }) {
this.name = "webhook";
this.context = context;
}
run({ context, }) {
run() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const context = this.context;
let items = [];

@@ -12,0 +14,0 @@ if (context &&

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

import { AnyObject } from "./interfaces";
import { AnyObject } from "actionsflow-interface";
interface IOptions {

@@ -3,0 +3,0 @@ interpolate?: RegExp;

import path from "path";
import { ITriggerContext, ITrigger, IWorkflow, AnyObject } from "./interfaces";
import { ITriggerContext, ITrigger, IWorkflow, AnyObject } from "actionsflow-interface";
interface IGetWorkflowsOptions {

@@ -4,0 +4,0 @@ src: string;

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

const map_obj_1 = tslib_1.__importDefault(require("map-obj"));
const helpers_1 = require("./helpers");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));

@@ -19,4 +20,8 @@ const log_1 = tslib_1.__importDefault(require("./log"));

const Trigger = AllTriggers[triggerKey];
const triggerInstance = new Trigger();
return triggerInstance.id;
const triggerInstance = new Trigger({
options: {},
context: context,
helpers: { createContentDigest: helpers_1.createContentDigest, cache: helpers_1.getCache(`trigger-temp`) },
});
return triggerInstance.name;
});

@@ -23,0 +28,0 @@ const triggers = [];

{
"version": "0.0.6",
"version": "0.0.7",
"license": "MIT",

@@ -33,2 +33,3 @@ "main": "dist/src/index.js",

"actionsflow-cli": "^0.0.6",
"actionsflow-interface": "^0.0.2",
"axios": "^0.19.2",

@@ -53,3 +54,3 @@ "cache-manager": "^3.3.0",

},
"gitHead": "9b6ef0314a286134834f2babff00e2b459edb703"
"gitHead": "3af532281d09c75fc0eba65714af8836a8c74b41"
}

@@ -19,3 +19,3 @@ import "./env";

IGithub,
} from "./interfaces";
} from "actionsflow-interface";

@@ -22,0 +22,0 @@ interface IBuildOptions {

@@ -1,25 +0,14 @@

import { Rss, TelegramBot, Webhook, Poll } from "./triggers";
import * as Triggers from "./triggers";
import { createContentDigest, getCache } from "./helpers";
import log from "./log";
import {
TriggerName,
ITriggerContext,
ITriggerResult,
IItem,
} from "./interfaces";
const MAX_CACHE_KEYS_COUNT = 1000;
type TriggerMapType = Record<
TriggerName,
typeof Rss | typeof TelegramBot | typeof Webhook | typeof Poll
>;
const triggerNameMap: TriggerMapType = {
rss: Rss,
poll: Poll,
webhook: Webhook,
telegram_bot: TelegramBot,
};
AnyObject,
ITriggerClassTypeConstructable,
} from "actionsflow-interface";
const MAX_CACHE_KEYS_COUNT = 5000;
interface ITriggerOptions {
trigger: {
name: TriggerName;
name: string;
options: Record<string, unknown>;

@@ -50,3 +39,19 @@ workflowRelativePath: string;

};
if (triggerNameMap[trigger.name]) {
const AllTriggers = Triggers as Record<
string,
ITriggerClassTypeConstructable
>;
const triggersKeys = Object.keys(AllTriggers);
const TriggerMap: Record<string, ITriggerClassTypeConstructable> = {};
triggersKeys.forEach((triggerKey) => {
const triggerInstance = new AllTriggers[triggerKey]({
options: {},
context: context,
helpers: { createContentDigest, cache: getCache(`trigger-temp`) },
});
TriggerMap[triggerInstance.name] = AllTriggers[triggerKey];
});
if (TriggerMap[trigger.name]) {
const triggerHelpers = {

@@ -61,7 +66,7 @@ createContentDigest,

};
const Trigger = triggerNameMap[trigger.name];
const triggerInstance = new Trigger();
const Trigger = TriggerMap[trigger.name];
const triggerInstance = new Trigger(triggerOptions);
const triggerResult = await triggerInstance.run(triggerOptions);
const { shouldDeduplicate, getItemKey, updateInterval } = triggerResult;
const triggerResult = await triggerInstance.run();
const { shouldDeduplicate, getItemKey, every } = triggerInstance;
let { items } = triggerResult;

@@ -79,8 +84,7 @@ const maxItemsCount = trigger.options.max_items_count as number;

if (updateInterval) {
if (every) {
// check if should update
// unit minutes
// get latest update time
const shouldUpdateUtil =
(lastUpdatedAt as number) + updateInterval * 60 * 1000;
const shouldUpdateUtil = (lastUpdatedAt as number) + every * 60 * 1000;
const now = Date.now();

@@ -100,3 +104,3 @@ const shouldUpdate = shouldUpdateUtil - now <= 0;

getItemKey ||
((item: IItem): string => {
((item: AnyObject): string => {
if (item.guid) return item.guid as string;

@@ -133,3 +137,3 @@ if (item.id) return item.id as string;

deduplicationKeys = (deduplicationKeys as string[]).concat(
items.map((item: IItem) => getItemKeyFn(item))
items.map((item: AnyObject) => getItemKeyFn(item))
);

@@ -136,0 +140,0 @@ deduplicationKeys = (deduplicationKeys as string[]).slice(

@@ -7,21 +7,35 @@ import log from "../log";

ITriggerClassType,
ITriggerRunFunction,
IItem,
ITriggerContructorParams,
ITriggerRunFunctionResult,
TriggerName,
} from "../interfaces";
IHelpers,
AnyObject,
} from "actionsflow-interface";
export default class Poll implements ITriggerClassType {
id: TriggerName = "poll";
async run({
helpers,
options,
}: ITriggerRunFunction): Promise<ITriggerRunFunctionResult> {
const {
url,
items_path,
deduplication_key,
every,
...requestOptions
} = options as {
name = "poll";
options: AnyObject = {};
helpers: IHelpers;
every = 5;
shouldDeduplicate = true;
getItemKey(item: AnyObject): string {
// TODO adapt every cases
const deduplication_key = this.options.deduplication_key;
if (deduplication_key) {
return item[deduplication_key as string] as string;
}
if (item.id) return item.id as string;
if (item.guid) return item.guid as string;
if (item.key) return item.key as string;
return this.helpers.createContentDigest(item);
}
constructor({ helpers, options }: ITriggerContructorParams) {
this.options = options;
this.helpers = helpers;
if (options.every) {
this.every = options.every as number;
}
}
async run(): Promise<ITriggerRunFunctionResult> {
const { url, items_path, ...requestOptions } = this.options as {
url?: string;

@@ -32,3 +46,2 @@ items_path?: string;

};
const updateInterval = (every as number) || 5;

@@ -38,3 +51,3 @@ if (!url) {

}
const items: IItem[] = [];
const items: AnyObject[] = [];
const config: AxiosRequestConfig = {

@@ -60,3 +73,3 @@ ...requestOptions,

if (requestResult && requestResult.data) {
const itemsArray: IItem[] = items_path
const itemsArray: AnyObject[] = items_path
? get(requestResult.data, items_path)

@@ -71,21 +84,7 @@ : requestResult.data;

const getItemKey = (item: IItem): string => {
// TODO adapt every cases
if (deduplication_key) {
return item[deduplication_key] as string;
}
if (item.id) return item.id as string;
if (item.guid) return item.guid as string;
if (item.key) return item.key as string;
return helpers.createContentDigest(item);
};
// if need
return {
shouldDeduplicate: true,
updateInterval: updateInterval,
items,
getItemKey,
};
}
}
import Parser from "rss-parser";
import {
ITriggerClassType,
ITriggerRunFunction,
IItem,
TriggerName,
ITriggerContructorParams,
ITriggerRunFunctionResult,
} from "../interfaces";
IHelpers,
AnyObject,
} from "actionsflow-interface";
import log from "../log";
export default class Rss implements ITriggerClassType {
id: TriggerName = "rss";
async run({
helpers,
options,
}: ITriggerRunFunction): Promise<ITriggerRunFunctionResult> {
const event = options.event || "new_item";
const url = options.url;
const updateInterval: number = (options.every as number) || 5;
name = "rss";
options: AnyObject = {};
helpers: IHelpers;
every = 5;
shouldDeduplicate = true;
getItemKey(item: AnyObject): string {
// TODO adapt every cases
if (item.guid) return item.guid as string;
if (item.id) return item.id as string;
return this.helpers.createContentDigest(item);
}
constructor({ helpers, options }: ITriggerContructorParams) {
this.options = options;
this.helpers = helpers;
if (!options.event) {
this.options.event = "new_item";
}
if (options.every) {
this.every = options.every as number;
}
}
async run(): Promise<ITriggerRunFunctionResult> {
const { event, url } = this.options;
let urls = [];
if (event === "new_item_in_multiple_feeds") {
const urlsParam = options.urls;
const urlsParam = this.options.urls;
if (!urlsParam) {

@@ -40,3 +58,3 @@ throw new Error("Miss param urls");

}
const items: IItem[] = [];
const items: AnyObject[] = [];

@@ -68,17 +86,7 @@ for (let index = 0; index < urls.length; index++) {

const getItemKey = (item: IItem): string => {
// TODO adapt every cases
if (item.guid) return item.guid as string;
if (item.id) return item.id as string;
return helpers.createContentDigest(item);
};
// if need
return {
shouldDeduplicate: true,
updateInterval: updateInterval,
items,
getItemKey,
};
}
}

@@ -5,15 +5,26 @@ import log from "../log";

ITriggerClassType,
ITriggerRunFunction,
IItem,
TriggerName,
ITriggerContructorParams,
ITriggerRunFunctionResult,
IObject,
} from "../interfaces";
AnyObject,
IHelpers,
} from "actionsflow-interface";
export default class TelegramBot implements ITriggerClassType {
id: TriggerName = "telegram_bot";
async run({
helpers,
options,
}: ITriggerRunFunction): Promise<ITriggerRunFunctionResult> {
name = "telegram_bot";
options: AnyObject;
helpers: IHelpers;
every = 5;
shouldDeduplicate = true;
getItemKey = (item: AnyObject): string => {
if (item.update_id) return item.update_id as string;
return this.helpers.createContentDigest(item);
};
constructor({ helpers, options }: ITriggerContructorParams) {
this.options = options;
this.helpers = helpers;
if (options.every) {
this.every = options.every as number;
}
}
async run(): Promise<ITriggerRunFunctionResult> {
const _messageTypes = [

@@ -49,3 +60,3 @@ "text",

];
const { token, every, event, ...requestOptions } = options as {
const { token, event, ...requestOptions } = this.options as {
token?: string;

@@ -55,6 +66,5 @@ every?: number;

};
let { events } = options as {
let { events } = this.options as {
events?: string[];
};
const updateInterval = every || 5;

@@ -67,3 +77,3 @@ if (!token) {

}
const items: IItem[] = [];
const items: AnyObject[] = [];
const url = `https://api.telegram.org/bot${token}/getUpdates`;

@@ -96,3 +106,3 @@ const config: AxiosRequestConfig = {

const itemsArray = requestResult.data.result;
itemsArray.forEach((item: IObject) => {
itemsArray.forEach((item: AnyObject) => {
const message = item.message as {

@@ -118,16 +128,7 @@ update_id: string;

const getItemKey = (item: IItem): string => {
if (item.update_id) return item.update_id as string;
return helpers.createContentDigest(item);
};
// if need
return {
shouldDeduplicate: true,
updateInterval: updateInterval,
items,
getItemKey,
};
}
}
import {
ITriggerClassType,
ITriggerRunFunction,
IItem,
ITriggerContructorParams,
AnyObject,
ITriggerRunFunctionResult,
TriggerName,
} from "../interfaces";
ITriggerContext,
} from "actionsflow-interface";
export default class Webhook implements ITriggerClassType {
id: TriggerName = "webhook";
async run({
context,
}: ITriggerRunFunction): Promise<ITriggerRunFunctionResult> {
// if need
let items: IItem[] = [];
name = "webhook";
context: ITriggerContext;
constructor({ context }: ITriggerContructorParams) {
this.context = context;
}
async run(): Promise<ITriggerRunFunctionResult> {
const context = this.context;
let items: AnyObject[] = [];
if (

@@ -17,0 +19,0 @@ context &&

import has from "lodash.has";
import { IObject, AnyObject } from "./interfaces";
import { AnyObject } from "actionsflow-interface";
interface IOptions {

@@ -14,3 +14,3 @@ interpolate?: RegExp;

shouldReplaceUndefinedToEmpty: boolean;
context: IObject;
context: AnyObject;
}

@@ -17,0 +17,0 @@ const variableHandle = ({

@@ -5,2 +5,3 @@ import path from "path";

import mapObj from "map-obj";
import { createContentDigest, getCache } from "./helpers";
import fs from "fs-extra";

@@ -14,6 +15,5 @@ import log from "./log";

IWorkflow,
TriggerName,
AnyObject,
ITriggerClassTypeConstructable,
} from "./interfaces";
} from "actionsflow-interface";

@@ -31,4 +31,8 @@ const getSupportedTriggers = (

const Trigger = AllTriggers[triggerKey];
const triggerInstance = new Trigger();
return triggerInstance.id;
const triggerInstance = new Trigger({
options: {},
context: context,
helpers: { createContentDigest, cache: getCache(`trigger-temp`) },
});
return triggerInstance.name;
});

@@ -40,3 +44,3 @@ const triggers = [];

for (let index = 0; index < keys.length; index++) {
const key = keys[index] as TriggerName;
const key = keys[index] as string;
if (supportTriggerIds.includes(key)) {

@@ -43,0 +47,0 @@ // is active

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