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

bedrock

Package Overview
Dependencies
Maintainers
5
Versions
68
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bedrock - npm Package Compare versions

Comparing version 4.4.3 to 4.5.0

lib/configUtil.js

8

.eslintrc.js
module.exports = {
root: true,
parserOptions: {
// this is required for dynamic import()
ecmaVersion: 2020
},
env: {
node: true
},
extends: [
'eslint-config-digitalbazaar'
],
extends: ['digitalbazaar', 'digitalbazaar/jsdoc'],
ignorePatterns: ['node_modules/']
};
# bedrock ChangeLog
## 4.5.0 - 2022-03-22
### Added
- Add `main` export that includes information about the main program
module such as its `filename`. The main program module will be
properly computed on bedrock workers to express not the worker
filename but the filename of the first loaded module, e.g., if
an application `foo.js` calls `bedrock.start`, then `main` includes
the full path `filename` for `foo.js` whether running in the primary
process on a worker process.
### Changed
- Change internal implementation to use ESM + transpile using esm.js to
CommonJS. Should be a non-breaking change.
## 4.4.3 - 2021-12-08

@@ -4,0 +19,0 @@

/*!
* Copyright (c) 2012-2019 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2021 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
export const config = {};
const config = {};
module.exports = config;
// cli info

@@ -10,0 +7,0 @@ config.cli = {};

/*!
* Copyright (c) 2012-2019 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2021 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import {EventEmitter} from 'async-node-events';
const {EventEmitter} = require('async-node-events');
const emitter = new EventEmitter();
emitter.setMaxListeners(Infinity);
emitter.removeAllListeners('maxListenersPassed');
export {emitter};
const api = new EventEmitter();
api.setMaxListeners(Infinity);
api.removeAllListeners('maxListenersPassed');
module.exports = api;
/**
* Schedules an event to be emitted on the next tick (via process.nextTick).
*
* @return a Promise that resolves to undefined once the event has been emitted
* to all listeners (or to `false` if it was canceled).
* @param {Array} args - The arguments to emit.
*
* @returns {Promise} A Promise that resolves to undefined once the event has
* been emitted to all listeners (or to `false` if it was canceled).
*/
api.emitLater = function(...args) {
emitter.emitLater = function(...args) {
// emit asynchronously
return new Promise((resolve, reject) => {
process.nextTick(() => {
api.emit(...args).then(resolve, reject);
emitter.emit(...args).then(resolve, reject);
});
});
};
/*!
* Copyright (c) 2012-2021 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
const brUtil = require('../util');
import * as brUtil from '../util.js';
import * as formatters from './formatters.js';
import {config} from '../config.js';
import path from 'path';
import {promises as fs} from 'fs';
import uidNumber from 'uid-number';
import winston from 'winston';
const cc = brUtil.config.main.computer();
const config = require('../config');
const formatters = require('./formatters');
const fs = require('fs').promises;
const path = require('path');
const uidNumber = require('uid-number');
const winston = require('winston');

@@ -23,3 +21,3 @@ // config filenames

module.exports.init = async ({transports}) => {
export async function init({transports}) {
transports.app = new winston.transports.File({

@@ -66,3 +64,3 @@ ...config.loggers.app,

transports.error.name = 'error';
};
}

@@ -69,0 +67,0 @@ async function _chown(filename) {

/*!
* Copyright (c) 2012-2020 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import jsonStringify from 'fast-safe-stringify';
import {MESSAGE} from 'triple-beam';
import winston from 'winston';
const jsonStringify = require('fast-safe-stringify');
const {MESSAGE} = require('triple-beam');
const winston = require('winston');
const {format} = winston;
const api = {};
module.exports = api;
api.bedrock = format(info => {
export const bedrock = format(info => {
const {timestamp, workerId, workerPid} = info;

@@ -40,3 +35,4 @@ const stringifiedRest = jsonStringify({

// this filter is only operative when one of the only/exclude sets is active
api.filterModules = format((info, {excludeModulesSet, onlyModulesSet}) => {
export const filterModules = format((
info, {excludeModulesSet, onlyModulesSet}) => {
const {module} = info;

@@ -61,3 +57,11 @@

api.fromConfig = config => {
export const modulePrefix = format(info => {
const {module} = info;
if(module) {
info.message = `[${module}] ${info.message}`;
}
return info;
});
export function fromConfig(config) {
const {colorize, excludeModules, formatter, onlyModules} = config;

@@ -72,7 +76,7 @@ let fmt;

const onlyModulesSet = onlyModules ? new Set(onlyModules) : false;
fmts.push(api.filterModules({excludeModulesSet, onlyModulesSet}));
fmts.push(filterModules({excludeModulesSet, onlyModulesSet}));
}
fmts.push(
format.timestamp(),
api.modulePrefix(),
modulePrefix(),
);

@@ -82,3 +86,3 @@ if(colorize) {

}
fmts.push(api.bedrock());
fmts.push(bedrock());
fmt = format.combine(...fmts);

@@ -101,11 +105,2 @@ } else if(formatter === 'json') {

return fmt;
};
api.modulePrefix = format(info => {
const {module} = info;
if(module) {
info.message = `[${module}] ${info.message}`;
}
return info;
});
}
/*!
* Copyright (c) 2012-2021 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import * as fileLogger from './fileLogger.js';
import * as formatters from './formatters.js';
import cluster from 'cluster';
import {config} from '../config.js';
import crypto from 'crypto';
import {Mail as WinstonMail} from 'winston-mail';
import util from 'util';
import winston from 'winston';
import {WorkerTransport} from './WorkerTransport.js';
const cluster = require('cluster');
const config = require('../config');
const crypto = require('crypto');
const fileLogger = require('./fileLogger');
const formatters = require('./formatters');
const util = require('util');
const winston = require('winston');
const WinstonMail = require('winston-mail').Mail;
const WorkerTransport = require('./WorkerTransport');
const randomBytes = util.promisify(crypto.randomBytes);

@@ -39,3 +37,3 @@

// create the container for the primary and all of the workers
const container = new winston.Container();
export const container = new winston.Container();
// override get to use a wrapper so loggers that are retrieved via `.get()`

@@ -80,3 +78,2 @@ // prior to logger initialization will receive the updated configuration

};
module.exports = container;

@@ -141,3 +138,3 @@ if(cluster.isMaster) {

*
* @param worker the worker to attach the message handler to.
* @param {object} worker - The worker to attach the message handler to.
*/

@@ -183,5 +180,5 @@ container.attach = function(worker) {

*
* @param name the name of the transport to add; if a name has already been
* taken, an error will be thrown.
* @param transport the transport to add.
* @param {string} name - The name of the transport to add; if a name has
* already been taken, an error will be thrown.
* @param {object} transport - The transport to add.
*/

@@ -188,0 +185,0 @@ container.addTransport = function(name, transport) {

/*!
* Copyright (c) 2012-2020 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import cycle from 'cycle';
import Transport from 'winston-transport';
const cycle = require('cycle');
const Transport = require('winston-transport');
module.exports = class WorkerTransport extends Transport {
export class WorkerTransport extends Transport {
constructor(config) {

@@ -45,5 +43,5 @@ super(config);

}
};
}
function _noop() {
}
/*!
* Copyright (c) 2012-2019 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import {config} from './config.js';
import delay from 'delay';
import util from 'util';
import uuid from 'uuid-random';
const config = require('./config');
const delay = require('delay');
const lodashGet = require('lodash.get');
const lodashSet = require('lodash.set');
const lodashTemplate = require('lodash.template');
const lodashToPath = require('lodash.topath');
const util = require('util');
const uuid = require('uuid-random');
// export config utilities under `config` namespace
export * as config from './configUtil.js';
const api = {};
module.exports = api;
/**

@@ -23,6 +17,6 @@ * Create a promise which resolves after the specified milliseconds.

*/
api.delay = delay;
export {delay};
// BedrockError class
api.BedrockError = function(message, type, details, cause) {
export const BedrockError = function(message, type, details, cause) {
Error.call(this, message);

@@ -35,5 +29,5 @@ Error.captureStackTrace(this, this.constructor);

};
util.inherits(api.BedrockError, Error);
api.BedrockError.prototype.name = 'BedrockError';
api.BedrockError.prototype.toObject = function(options) {
util.inherits(BedrockError, Error);
BedrockError.prototype.name = 'BedrockError';
BedrockError.prototype.toObject = function(options) {
options = options || {};

@@ -54,12 +48,12 @@ options.public = options.public || false;

// check type of this error
api.BedrockError.prototype.isType = function(type) {
return api.hasValue(this, 'name', type);
BedrockError.prototype.isType = function(type) {
return hasValue(this, 'name', type);
};
// check type of this error or one of it's causes
api.BedrockError.prototype.hasType = function(type) {
BedrockError.prototype.hasType = function(type) {
return this.isType(type) || this.hasCauseOfType(type);
};
// check type of error cause or one of it's causes
api.BedrockError.prototype.hasCauseOfType = function(type) {
if(this.cause && this.cause instanceof api.BedrockError) {
BedrockError.prototype.hasCauseOfType = function(type) {
if(this.cause && this.cause instanceof BedrockError) {
return this.cause.hasType(type);

@@ -70,102 +64,11 @@ }

const _genericErrorJSON = {
message: 'An internal server error occurred.',
type: 'bedrock.InternalServerError'
};
const _errorMessageRegex = /^Error:\s*/;
const _errorAtRegex = /^\s+at\s*/;
/**
* Parse an Error stack property into a JSON structure.
*
* NOTE: Uses some format heuristics and may be fooled by tricky errors.
*
* TODO: look into better stack parsing libraries.
* See: https://github.com/digitalbazaar/bedrock/issues/87
*/
function _parseStack(stack) {
try {
const lines = stack.split('\n');
const messageLines = [];
const atLines = [];
for(let i = 0; i < lines.length; ++i) {
const line = lines[i];
// push location-like lines to a stack array
if(line.match(_errorAtRegex)) {
atLines.push(line.replace(_errorAtRegex, ''));
} else {
// push everything else to a message array
messageLines.push(line.replace(_errorMessageRegex, ''));
}
}
return {
message: messageLines.join('\n'),
at: atLines
};
} catch(e) {
// FIXME: add parse error handling
// see: https://github.com/digitalbazaar/bedrock/issues/87
return stack;
}
}
function _toObject(err, options) {
if(!err) {
return null;
}
if(options.public) {
// public conversion
// FIXME also check if a validation type?
if(err instanceof api.BedrockError &&
err.details && err.details.public) {
const details = api.clone(err.details);
delete details.public;
// mask cause if it is not a public bedrock error
let {cause} = err;
if(!(cause && cause instanceof api.BedrockError &&
cause.details && cause.details.public)) {
cause = null;
}
return {
message: err.message,
type: err.name,
details,
cause: _toObject(cause, options)
};
} else {
// non-bedrock error or not public, return generic error
return _genericErrorJSON;
}
} else {
// full private conversion
if(err instanceof api.BedrockError) {
return {
message: err.message,
type: err.name,
details: err.details,
cause: _toObject(err.cause, options)
};
} else {
return {
message: err.message,
type: err.name,
details: {
inspect: util.inspect(err, false, 10),
stack: _parseStack(err.stack)
},
cause: null
};
}
}
}
/**
* Gets the passed date in W3C format (eg: 2011-03-09T21:55:41Z).
*
* @param date the date.
* @param {Date|string|number} [date=new Date] - The date; if passing a number
* use milliseconds since the epoch.
*
* @return the date in W3C format.
* @returns {string} The date in W3C format.
*/
api.w3cDate = function(date) {
export function w3cDate(date) {
if(date === undefined || date === null) {

@@ -183,3 +86,3 @@ date = new Date();

_zeroFill(date.getUTCSeconds()));
};
}

@@ -193,9 +96,10 @@ function _zeroFill(num) {

*
* @param deep (optional), true to do a deep-merge.
* @param target the target object to merge properties into.
* @param objects N objects to merge into the target.
* Arguments:
* `deep` (optional), true to do a deep-merge
* `target` the target object to merge properties into
* `objects` N objects to merge into the target.
*
* @return the default Bedrock JSON-LD context.
* @returns {object} - The extended object.
*/
api.extend = function() {
export function extend() {
let deep = false;

@@ -213,4 +117,4 @@ let i = 0;

const value = obj[name];
if(deep && api.isObject(value) && !Array.isArray(value)) {
target[name] = api.extend(true, target[name], value);
if(deep && isObject(value) && !Array.isArray(value)) {
target[name] = extend(true, target[name], value);
} else {

@@ -222,3 +126,3 @@ target[name] = value;

return target;
};
}

@@ -228,18 +132,19 @@ /**

*
* @param value the value to check.
* @param {*} value - The value to check.
*
* @return true if it is an Object, false if not.
* @returns {boolean} True if it is an Object, false if not.
*/
api.isObject = function(value) {
export function isObject(value) {
return (Object.prototype.toString.call(value) === '[object Object]');
};
}
/**
* Clones a value. If the value is an array or an object it will be deep cloned.
* Clones a value. If the value is an array or an object it will be deep
* cloned.
*
* @param value the value to clone.
* @param {*} value - The value to clone.
*
* @return the clone.
* @returns {*} The clone.
*/
api.clone = function(value) {
export function clone(value) {
if(value && typeof value === 'object') {

@@ -250,3 +155,3 @@ let rval;

for(let i = 0; i < rval.length; i++) {
rval[i] = api.clone(value[i]);
rval[i] = clone(value[i]);
}

@@ -256,3 +161,3 @@ } else {

for(const j in value) {
rval[j] = api.clone(value[j]);
rval[j] = clone(value[j]);
}

@@ -263,202 +168,10 @@ }

return value;
};
// config utilities
// config namespace
api.config = {};
// check if argument looks like a string or array path
function _isPath(maybePath) {
return typeof maybePath === 'string' || Array.isArray(maybePath);
}
// set default for path if it does not exist
function _setDefault(object, path, value) {
// ensure path is array
if(typeof path === 'string') {
path = lodashToPath(path);
}
if(path.length) {
let target = lodashGet(object, path);
if(!target) {
target = value;
lodashSet(object, path, target);
}
return target;
} else {
return object;
}
}
/**
* Wrapper with helpers for config objects.
*
* @param object the config object.
* @param [options] options to use:
* config: parent config object
* locals: object containing variables used for string templates.
* Defaults to main config object.
*/
api.config.Config = function(object, options) {
this.object = object;
this.options = options || {};
};
/**
* Set a path to a value.
*
* Multiple paths can be set at once with an object with many string path keys
* and associated values.
*
* @param path lodash-style string or array path, or an object with many path
* key and value pairs.
* @param value value to set at the path when using single path.
*/
api.config.Config.prototype.set = function(path, value) {
if(!_isPath(path)) {
Object.keys(path).forEach(key => lodashSet(this.object, key, path[key]));
return;
}
lodashSet(this.object, path, value);
};
/**
* Set a path to a default value if it does not exist. All elements of the path
* will be initialized as an empty object if they do not exist.
*
* Multiple paths can be set at once with an object with many string path keys
* and associated default values;
*
* Note: To initialize the final element of a path to the empty object even if
* it already exists, use c.set(path, {});
*
* @param path lodash-style string or array path, or an object with many path
* key and default value pairs.
* @param value default value to set at the path when using a single path.
* @return the last element of the path or a path indexed object with element
* values.
*/
api.config.Config.prototype.setDefault = function(path, value) {
if(!_isPath(path)) {
const paths = {};
Object.keys(path).forEach(key => {
paths[key] = _setDefault(this.object, key, path[key]);
});
return paths;
}
return _setDefault(this.object, path, value);
};
/**
* Assigns a getter to a config path. When the config path is read, the getter
* will execute and compute the configured value. This is particularly useful
* for config values that depend on other config values; it removes the need
* to update such a value when its dependencies change.
*
* The value can be computed from a function or from a lodash template that
* will be evaluated using `bedrock.config` for its local variables.
*
* @param path lodash-style string or array path, or an object with many path
* key and value pairs.
* @param fnOrExpression a lodash template or a function used to compute the
* path value.
* @param [options] options to use:
* locals: object containing variables used for string templates.
* parentDefault: value for parent if it does not exist.
*/
api.config.Config.prototype.setComputed =
function(path, fnOrExpression, options) {
if(!_isPath(path)) {
options = fnOrExpression;
Object.keys(path).forEach(key => this.setComputed(
key, path[key], options));
return;
}
if(typeof fnOrExpression === 'string') {
// handle strings as templates
fnOrExpression = lodashTemplate(fnOrExpression);
} else if(typeof fnOrExpression !== 'function') {
// handle non-string non-functions as simple values
return this.set(path, fnOrExpression);
}
// ensure path is array
if(typeof path === 'string') {
path = lodashToPath(path);
}
// locals
options = options || {};
const locals = options.locals || this.options.locals || config;
// get target object path
const targetPath = path.slice(0, -1);
// get key
const targetKey = path.slice(-1);
// get or create target parent object
const parentDefault = options.parentDefault || {};
const target = _setDefault(this.object, targetPath, parentDefault);
// setup property
let _isSet = false;
let _value;
Object.defineProperty(target, targetKey, {
configurable: true,
enumerable: true,
get: () => {
if(_isSet) {
return _value;
}
return fnOrExpression(locals);
},
set: value => {
_isSet = true;
_value = value;
}
});
};
/**
* Create a bound setComputed function for this Config instance. Used to
* simplify code.
*
* let cc = bedrock.util.config.main.computer();
* cc('...', ...);
*
* @return bound setComputed function.
*/
api.config.Config.prototype.computer = function() {
return this.setComputed.bind(this);
};
/**
* Push a getter to an array specified by a config path. See setComputed for an
* explaination of how getters work.
*
* @param path lodash-style string or array path.
* @param fnOrExpression a lodash template or a function used to compute the
* path value.
* @param [options] options to use:
* locals: object containing variables used for string templates.
*/
api.config.Config.prototype.pushComputed =
function(path, fnOrExpression, options) {
// get target or default array
const target = lodashGet(this.object, path, []);
// add next index
const pushPath = lodashToPath(path);
pushPath.push(target.length);
// use default parent array
const pushOptions = Object.assign({}, options, {parentDefault: []});
// set computed array element
this.setComputed(pushPath, fnOrExpression, pushOptions);
};
/**
* Shared wrapper for the standard bedrock config.
*/
api.config.main = new api.config.Config(config);
/**
* Generates a new v4 UUID.
*
* @return the new v4 UUID.
* @returns {string} The new v4 UUID.
*/
api.uuid = uuid;
export {uuid};

@@ -470,7 +183,7 @@ /**

*
* @param value a string of value.
* @param {string} value - The value to convert to a boolean.
*
* @return the boolean conversion of the value.
* @returns {boolean} The boolean conversion of the value.
*/
api.boolify = function(value) {
export function boolify(value) {
if(typeof value === 'boolean') {

@@ -497,5 +210,5 @@ return value;

throw new Error('Invalid boolean:' + value);
};
}
api.callbackify = fn => {
export function callbackify(fn) {
const callbackVersion = util.callbackify(fn);

@@ -509,6 +222,6 @@ return function(...args) {

};
};
}
// a replacement for jsonld.hasValue
api.hasValue = (obj, key, value) => {
export function hasValue(obj, key, value) {
const t = obj[key];

@@ -519,2 +232,100 @@ if(Array.isArray(t)) {

return t === value;
}
const _genericErrorJSON = {
message: 'An internal server error occurred.',
type: 'bedrock.InternalServerError'
};
const _errorMessageRegex = /^Error:\s*/;
const _errorAtRegex = /^\s+at\s*/;
/**
* Parse an Error stack property into an object structure that can be
* serialized to JSON.
*
* NOTE: Uses some format heuristics and may be fooled by tricky errors.
*
* TODO: look into better stack parsing libraries.
* See: https://github.com/digitalbazaar/bedrock/issues/87.
*
* @param {string} stack - The stack trace.
*
* @returns {object} Stack trace as an object.
*/
function _parseStack(stack) {
try {
const lines = stack.split('\n');
const messageLines = [];
const atLines = [];
for(let i = 0; i < lines.length; ++i) {
const line = lines[i];
// push location-like lines to a stack array
if(line.match(_errorAtRegex)) {
atLines.push(line.replace(_errorAtRegex, ''));
} else {
// push everything else to a message array
messageLines.push(line.replace(_errorMessageRegex, ''));
}
}
return {
message: messageLines.join('\n'),
at: atLines
};
} catch(e) {
// FIXME: add parse error handling
// see: https://github.com/digitalbazaar/bedrock/issues/87
return stack;
}
}
function _toObject(err, options) {
if(!err) {
return null;
}
if(options.public) {
// public conversion
// FIXME also check if a validation type?
if(err instanceof BedrockError &&
err.details && err.details.public) {
const details = clone(err.details);
delete details.public;
// mask cause if it is not a public bedrock error
let {cause} = err;
if(!(cause && cause instanceof BedrockError &&
cause.details && cause.details.public)) {
cause = null;
}
return {
message: err.message,
type: err.name,
details,
cause: _toObject(cause, options)
};
} else {
// non-bedrock error or not public, return generic error
return _genericErrorJSON;
}
} else {
// full private conversion
if(err instanceof BedrockError) {
return {
message: err.message,
type: err.name,
details: err.details,
cause: _toObject(err.cause, options)
};
} else {
return {
message: err.message,
type: err.name,
details: {
inspect: util.inspect(err, false, 10),
stack: _parseStack(err.stack)
},
cause: null
};
}
}
}
/*!
* Copyright (c) 2012-2021 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2012-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
// wait for initialization options from primary

@@ -20,3 +18,5 @@ process.on('message', init);

require(msg.script);
import(msg.script).catch(e => {
throw e;
});
}
{
"name": "bedrock",
"version": "4.4.3",
"version": "4.5.0",
"description": "A core foundation for rich Web applications.",

@@ -32,2 +32,3 @@ "license": "SEE LICENSE IN LICENSE.md",

"errio": "^1.2.2",
"esm": "^3.2.25",
"fast-safe-stringify": "^2.0.7",

@@ -38,3 +39,2 @@ "lodash.get": "^4.4.2",

"lodash.topath": "^4.5.2",
"pkginfo": "^0.4.1",
"triple-beam": "^1.3.0",

@@ -48,4 +48,7 @@ "uid-number": "0.0.6",

"devDependencies": {
"eslint": "^7.0.0",
"eslint-config-digitalbazaar": "^2.5.0"
"eslint": "^7.32.0",
"eslint-config-digitalbazaar": "^2.8.0",
"eslint-plugin-jsdoc": "^37.9.7",
"jsdoc": "^3.6.10",
"jsdoc-to-markdown": "^7.1.1"
},

@@ -55,3 +58,3 @@ "engines": {

},
"main": "./lib/bedrock"
"main": "./lib/index.js"
}

@@ -7,4 +7,4 @@ {

"test": "node --preserve-symlinks test test",
"coverage": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text-summary npm test",
"coverage-ci": "cross-env NODE_ENV=test nyc --reporter=lcovonly npm test",
"coverage": "cross-env NODE_ENV=test ESM_OPTIONS='{cache:false}' nyc --reporter=lcov --reporter=text-summary npm test",
"coverage-ci": "cross-env NODE_ENV=test ESM_OPTIONS='{cache:false}' nyc --reporter=lcovonly npm test",
"coverage-report": "nyc report"

@@ -11,0 +11,0 @@ },

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