Socket
Socket
Sign inDemoInstall

@danmasta/walk

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@danmasta/walk - npm Package Compare versions

Comparing version 3.0.1 to 4.0.0

lib/constants.js

2

index.js
const Walk = require('./lib/walk');
module.exports = exports = Walk.factory();
exports = module.exports = Walk.factory();
exports.Walk = Walk;
const path = require('path');
const _ = require('lodash');
const util = require('./util');
const fs = require('fs');
const constants = require('./constants');
const defaults = {
cwd: process.cwd(),
root: null,
path: null,
relative: null,
relativeFromCwd: null,
dir: null,
base: null,
name: null,
ext: null,
stat: null,
contents: null
root: undefined,
path: undefined,
relative: undefined,
relativeFromCwd: undefined,
dir: undefined,
base: undefined,
name: undefined,
ext: undefined,
stat: undefined,
contents: undefined,
encoding: 'utf8'
};
class FileError extends Error {
constructor (str) {
super(str);
Error.captureStackTrace(this, FileError);
this.name = 'FileError';
this.code = 'FILEERROR';
}
}
class File {

@@ -32,3 +44,3 @@

if (!this.path) {
throw new Error('File Error: path or relative field is required');
throw new File.FileError('Path or relative field is required');
}

@@ -45,2 +57,65 @@

createReadStream (opts) {
return fs.createReadStream(
this.path,
_.defaults(opts, { encoding: this.encoding })
);
}
createWriteStream (opts) {
return fs.createWriteStream(
this.path,
_.defaults(opts, { encoding: this.encoding })
);
}
append (data, opts) {
return fs.promises.appendFile(
this.path,
data,
_.defaults(opts, { encoding: this.encoding })
);
}
read (opts) {
return fs.promises.readFile(
this.path,
_.defaults(opts, { encoding: this.encoding })
);
}
readAsString (opts) {
if ((!opts || opts && !opts.encoding) && !this.encoding) {
return Promise.reject(new File.FileError('Encoding is required to read as string'));
} else {
return this.read(opts);
}
}
readAsBuffer (opts) {
return this.read(
_.assign(opts, { encoding: null })
);
}
readStr (...args) {
return this.readAsString(...args);
}
readBuf (...args) {
return this.readAsBuffer(...args);
}
write (data, opts) {
return fs.promises.writeFile(
this.path,
data,
_.defaults(opts, { encoding: this.encoding })
);
}
require () {
return require(this.path);
}
isBuffer () {

@@ -94,4 +169,37 @@ return util.isBuffer(this.contents);

getEncodingFromBOM () {
let buff = Buffer.alloc(4);
let enc = undefined;
return fs.promises.open(this.path, 'r').then(fd => {
return fd.read(buff, 0, 4, 0).then(() => {
return fd.close();
});
}).then(() => {
_.some(File.constants.BOM, (val, key) => {
if (buff.indexOf(val) === 0) {
return enc = key;
}
});
return enc;
});
}
static get FileError () {
return FileError;
}
static get constants () {
return constants;
}
}
module.exports = File;

@@ -16,5 +16,5 @@ const path = require('path');

function isStream (contents) {
function isStream (obj) {
let pipe = contents && contents.pipe;
let pipe = obj && obj.pipe;

@@ -21,0 +21,0 @@ if (pipe && typeof pipe === 'function') {

const Readable = require('stream').Readable;
const fs = require('fs');
const path = require('path');
const Promisify = require('util').promisify;
const os = require('os');
const micromatch = require('micromatch');

@@ -9,28 +9,11 @@ const _ = require('lodash');

const util = require('./util');
const constants = require('./constants');
const readdirAsync = Promisify(fs.readdir);
const statAsync = Promisify(fs.stat);
const readFileAsync = Promisify(fs.readFile);
const accessAsync = Promisify(fs.access);
const constants = {
GLOBS: {
all: '*(../)*(**/)*',
ignore: '*(../)*(**/)(.git|node_modules)',
dot: '*(../)*(**/)!(.)*'
}
};
const defaults = {
cwd: process.cwd(),
root: './',
require: false,
stream: false,
read: false,
contents: false,
buffer: false,
src: constants.GLOBS.all,
dot: true,
ignore: constants.GLOBS.ignore,
encoding: null,
encoding: 'utf8',
objectMode: true,

@@ -70,5 +53,14 @@ resolve: true

if (!_.isPlainObject(opts)) {
opts = {};
}
if (paths) {
if (!Array.isArray(paths)) {
paths = [paths];
if (!_.isArray(paths)) {
if (!opts.root) {
opts.root = paths;
paths = [];
} else {
paths = [paths];
}
}

@@ -81,3 +73,3 @@ } else {

super(opts);
super({ objectMode: true });

@@ -88,12 +80,2 @@ if (!opts.dot && opts.src === constants.GLOBS.all) {

opts.root = util.stripTrailingSep(path.resolve(opts.cwd, opts.root));
if (opts.require || opts.read === 'require') {
opts.read = 'require';
} else if (opts.stream || opts.read === 'stream') {
opts.read = 'stream';
} else if (opts.read || opts.contents || opts.buffer) {
opts.read = 'contents';
}
this.opts = opts;

@@ -103,2 +85,4 @@ this.include = null;

opts.root = util.stripTrailingSep(this.getRootWalkPath(opts.root));
if (opts.src) {

@@ -116,110 +100,124 @@ this.include = micromatch.matcher(opts.src, { dot: opts.dot });

this.inFlight = 0;
this.res = [];
// Should be true until we push at least one file on each iteration
this.shouldRead = true;
if (!this.paths.length) {
this.paths.push(this.opts.root);
}
}
_read () {
this.readFromQueue();
if (!this.inFlight && !this.pending) {
this.handleRead();
}
}
resolveFilePath (str) {
return path.resolve(this.opts.root, str);
handleRead () {
if (this.canRead()) {
this.readFromQueue().then(() => {
if (this.shouldRead) {
this.handleRead();
}
});
} else {
if (this.paths.length) {
this.addPathIfExists(this.paths.shift()).then(() => {
if (this.shouldRead) {
this.handleRead();
}
});
} else if (this.isEmpty()) {
this.push(null);
}
}
}
addPathIfExists (str) {
canRead () {
return this.queue.length > 0;
}
str = this.resolveFilePath(str);
isEmpty () {
return !this.inFlight && !this.pending && !this.queue.length && !this.paths.length;
}
this.pending++;
resolvePathFromHomeOrRoot (str) {
if (str && str[0] === '~') {
return path.normalize(path.join(os.homedir(), str.slice(1)));
} else {
return path.resolve(this.opts.root, str);
}
}
accessAsync(str, fs.constants.F_OK).then(() => {
resolvePathFromHomeOrCWD (str) {
if (str && str[0] === '~') {
return path.normalize(path.join(os.homedir(), str.slice(1)));
} else {
return path.resolve(this.opts.cwd, str);
}
}
this.pending--;
this.pushToQueue(str);
this.readFromQueue();
// Resolve path from user home or cwd
// Verifies path exists or throws error
getRootWalkPath (str) {
}).catch(err => {
let res = this.resolvePathFromHomeOrCWD(str);
let stat = null;
this.pending--;
try {
stat = fs.statSync(res);
if (stat.isDirectory() || stat.isFile()) {
return res;
} else {
this.destroy(new Walk.NotFoundError(res));
}
} catch (err) {
if (this.opts.resolve) {
try {
str = require.resolve(str);
this.pushToQueue(str);
this.readFromQueue();
return require.resolve(res);
} catch (err) {
this.emit('error', new NotResolvedError(str));
this.destroy(new Walk.NotResolvedError(res));
}
} else {
this.emit('error', new NotFoundError(str));
this.destroy(new Walk.NotFoundError(res));
}
});
}
pushToQueue (str) {
this.queue.push(str);
}
readFromQueue () {
while (this.paths.length) {
this.addPathIfExists(this.paths.shift());
}
if (!this.inFlight) {
if (!this.queue.length) {
if (!this.pending) {
this.push(null);
}
} else {
this.inFlight++;
this.walkFileOrDir(this.queue.shift());
}
}
}
pushFile (file) {
this.inFlight--;
if (this.push(file)) {
this.readFromQueue();
}
}
addPathIfExists (str) {
getFileContents (file) {
str = this.resolvePathFromHomeOrRoot(str);
this.pending++;
return Promise.resolve().then(() => {
return fs.promises.access(str, fs.constants.F_OK).then(() => {
if (this.opts.read === 'require') {
this.pending--;
this.pushToQueue(str);
return require(file.path);
}).catch(err => {
} else if (this.opts.read === 'stream') {
return fs.createReadStream(file.path);
if (this.opts.resolve) {
try {
this.pending--;
this.pushToQueue(require.resolve(str));
} catch (err) {
this.destroy(new Walk.NotResolvedError(str));
}
} else {
return readFileAsync(file.path).then(buff => {
if (this.opts.buffer) {
return buff;
} else {
return buff.toString();
}
});
this.pending--;
this.destroy(new Walk.NotFoundError(str));
}
}).then(contents => {
});
file.contents = contents;
}
}).then(() => {
pushToQueue (str) {
return this.queue.push(str);
}
return file;
});
readFromQueue () {
return this.walkFileOrDir(this.queue.shift());
}

@@ -229,11 +227,13 @@

readdirAsync(str).then(list => {
this.inFlight++;
list.map(name => {
return fs.promises.readdir(str).then(list => {
let res = path.resolve(str, name);
let rel = path.relative(this.opts.root, res);
_.each(list, name => {
let abs = path.resolve(str, name);
let rel = path.relative(this.opts.root, abs);
if (!this.exclude || !this.exclude(rel)) {
this.pushToQueue(res);
this.pushToQueue(abs);
}

@@ -244,3 +244,3 @@

this.inFlight--;
this.readFromQueue();
this.shouldRead = true;

@@ -251,24 +251,32 @@ });

readFile (str, stat) {
createFile (str, stat) {
return new this.constructor.File({
path: str,
stat,
cwd: this.opts.cwd,
root: this.opts.root,
encoding: this.opts.encoding
});
}
let root = this.opts.root;
let cwd = this.opts.cwd;
addFile (str, stat) {
let file = new File({ path: str, stat, cwd, root });
this.inFlight++;
if (!this.include || this.include(file.relative || file.base)) {
return new Promise((resolve, reject) => {
if (this.opts.read) {
this.getFileContents(file).then(file => {
this.pushFile(file);
});
let file = this.createFile(str, stat);
if (!this.include || this.include(file.relative || file.base)) {
this.push(file);
this.shouldRead = false;
} else {
this.pushFile(file);
this.shouldRead = true;
}
} else {
this.inFlight--;
this.readFromQueue();
}
resolve();
});
}

@@ -278,10 +286,11 @@

statAsync(str).then(stat => {
this.pending++;
return fs.promises.stat(str).then(stat => {
this.pending--;
if (stat.isDirectory()) {
this.readDir(str);
return this.readDir(str);
} else {
this.readFile(str, stat);
return this.addFile(str, stat);
}
});

@@ -291,45 +300,14 @@

require () {
this.opts.require = true;
this.opts.read = 'require';
return this;
}
iterate (fn, res) {
stream () {
this.opts.stream = true;
this.opts.read = 'stream';
return this;
}
buffer () {
this.opts.buffer = true;
this.opts.read = 'contents';
return this;
}
contents (str) {
this.opts.contents = true;
if (str === 'require' || str === 'stream') {
this.opts.read = str;
} else if (str === 'buffer') {
this.opts.buffer = true;
this.opts.read = 'contents';
} else {
this.opts.read = 'contents';
}
return this;
}
iterateAsPromise (fn, collect) {
this.on('data', file => {
if (fn) {
file = fn(file);
try {
file = fn(file);
} catch (err) {
this.destroy(err);
}
}
if (collect) {
this.res.push(file);
if (res) {
res.push(file);
}

@@ -340,7 +318,3 @@ });

this.once('end', () => {
if (collect) {
resolve(this.res);
} else {
resolve();
}
resolve(res);
});

@@ -356,3 +330,3 @@ this.once('error', err => {

fn = _.isFunction(fn) ? fn : file => file;
return this.iterateAsPromise(fn, true);
return this.iterate(fn, []);
}

@@ -362,130 +336,14 @@

fn = _.isFunction(fn) ? fn : _.noop;
return this.iterateAsPromise(fn, false);
return this.iterate(fn);
}
promise () {
return this.iterateAsPromise(null, true);
return this.iterate(null, []);
}
addPathIfExistsSync (str) {
str = this.resolveFilePath(str);
try {
fs.accessSync(str, fs.constants.F_OK);
this.pushToQueue(str);
} catch (err) {
if (this.opts.resolve) {
try {
str = require.resolve(str);
this.pushToQueue(str);
} catch (err) {
throw new NotResolvedError(str);
}
} else {
throw new NotFoundError(str);
}
}
}
pushFileSync (file) {
this.res.push(file);
}
getFileContentsSync (file) {
if (this.opts.read === 'require') {
file.contents = require(file.path);
} else if (this.opts.read === 'stream') {
file.contents = fs.createReadStream(file.path);
} else {
let buff = fs.readFileSync(file.path);
if (this.opts.buffer) {
file.contents = buff;
} else {
file.contents = buff.toString();
}
}
return file;
}
readDirSync (str) {
fs.readdirSync(str).map(name => {
let res = path.resolve(str, name);
let rel = path.relative(this.opts.root, res);
if (!this.exclude || !this.exclude(rel)) {
this.pushToQueue(res);
}
});
}
readFileSync (str, stat) {
let root = this.opts.root;
let cwd = this.opts.cwd;
let file = new File({ path: str, stat, cwd, root });
if (!this.include || this.include(file.relative || file.base)) {
if (this.opts.read) {
this.pushFileSync(this.getFileContentsSync(file));
} else {
this.pushFileSync(file);
}
}
}
walkFileOrDirSync (str) {
let stat = fs.statSync(str);
if (stat.isDirectory()) {
this.readDirSync(str);
} else {
this.readFileSync(str, stat);
}
}
readFromQueueSync () {
this.walkFileOrDirSync(this.queue.shift());
}
sync () {
while (this.paths.length) {
this.addPathIfExistsSync(this.paths.shift());
}
while (this.queue.length) {
this.readFromQueueSync();
}
return this.res;
}
static factory () {
let Fn = this;
return function walkFactory (...args) {
return function factory (...args) {
return new Fn(...args);
}
};
}

@@ -501,4 +359,12 @@

static get constants () {
return constants;
}
static get File () {
return File;
}
}
module.exports = exports = Walk;
module.exports = Walk;
{
"name": "@danmasta/walk",
"version": "3.0.1",
"version": "4.0.0",
"author": "Daniel Smith <dannmasta@gmail.com>",
"description": "Directory and file walking utility for node apps",
"license": "MIT",
"keywords": [
"walk",
"walker",
"directory",
"file",
"utility"
"util"
],

@@ -16,7 +16,8 @@ "main": "index.js",

"index.js",
"sync.js",
"lib/**/*"
],
"scripts": {
"test": "mocha tests",
"coverage": "nyc --reporter=lcov mocha tests"
"test": "./node_modules/.bin/mocha tests",
"coverage": "./node_modules/.bin/nyc --reporter=lcov ./node_modules/.bin/mocha tests"
},

@@ -27,12 +28,2 @@ "repository": {

},
"devDependencies": {
"chai": "^4.2.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"mocha": "^8.0.1",
"nyc": "^15.1.0"
},
"engines": {

@@ -43,5 +34,14 @@ "npm": ">=3",

"dependencies": {
"lodash": "^4.17.15",
"micromatch": "^4.0.2"
"lodash": "^4.17.21",
"micromatch": "^4.0.5"
},
"devDependencies": {
"chai": "^5.0.3",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"mocha": "^10.2.0",
"nyc": "^15.1.0"
}
}

@@ -7,5 +7,5 @@ # Walk

* Simple, lightweight, and fast
* Sync, async, streams, and promise api
* Async, sync, streams, and promise api
* Simple filtering with glob pattern matching
* Require file contents, or read as stream, buffer, or string
* Require file contents or read as stream, buffer, or string
* Resolves require-style path strings

@@ -18,3 +18,3 @@ * Normalized file objects with helper methods

## About
I needed a better way to walk directories and read files during build and/or run time. I wanted an api that was simple, supported glob pattern matching like gulp, and returned objects with a similar format as vinyl. This package allows you to simply read any directory (or file), return an array of objects, and filter results with glob pattern matching. It can also require file contents, or read them as strings, streams, or buffers, and resolve require-style path strings.
I needed a better way to walk directories and read files during build and/or run time. I wanted an api that was simple, supported glob pattern matching like gulp, and returned objects with a similar format as vinyl. This package allows you to simply read any directory (or file), filter results with glob pattern matching, and return an array of file objects. It can also require file contents, read them as streams, buffers, or strings, and resolve require-style path strings.

@@ -31,3 +31,3 @@ ## Usage

```
By default walk returns a readable stream interface. You can use the methods: `promise()`, `map()`, `each()`, and `sync()` to consume file objects via promises or as a synchronous array.
*By default walk returns a readable stream interface. You can use the methods: `map()`, `each()`, and `promise()` to iterate file objects and return promises*

@@ -38,11 +38,7 @@ ### Options

`cwd` | *`string`* | Base directory to start walk from. Default is `process.cwd`
`root` | *`string`* | Directory or file path as root to walk from. This gets normalized as `path.resolve(cwd, root)`. Default is `./`
`require` | *`boolean`* | If true, `file.contents` will be a resolved object using `require`. Default is `false`
`stream` | *`boolean`* | If true, `file.contents` will be a `Readable` stream. Default is `false`
`read` | *`boolean\|string`* | If `true`, will read file contents as a buffer or string. If string, accepts either `'require'`, `'stream'`, or `'contents'`. Default is `false`
`contents` | *`boolean`* | If true, will read file contents as string. Default is `false`
`buffer` | *`boolean`* | If true, when reading file conents, contents will remain a buffer instead of being converted to a string. Default is `false`
`src` | *`Array\|String\|RegExp`* | [Micromatch pattern](https://github.com/micromatch/micromatch#matcher) for result filtering by including any matches. Can be a path string, glob pattern string, regular expression, or an array of strings. Defaults to `*(../)*(**/)*`
`ignore` | *`Array\|String\|RegExp`* | [Micromatch pattern](https://github.com/micromatch/micromatch#matcher) for result filtering by ignoring any matches. Can be a path string, glob pattern string, regular expression, or an array of strings. Defaults to `*(../)*(**/)(.git\|node_modules)`
`root` | *`string`* | Directory or file path as root to walk from. This affects the relative paths used in file objects and matching. Default is `./`
`src` | *`array\|string\|regexp`* | [Micromatch pattern](https://github.com/micromatch/micromatch#matcher) for result filtering by including any matches. Can be a path string, glob pattern string, regular expression, or an array of strings. Defaults to `*(../)*(**/)*`
`dot` | *`boolean`* | Whether or not to include dot files when matching. Default is `true`
`ignore` | *`array\|string\|regexp`* | [Micromatch pattern](https://github.com/micromatch/micromatch#matcher) for result filtering by ignoring any matches. Can be a path string, glob pattern string, regular expression, or an array of strings. Defaults to `*(../)*(**/)(.git\|node_modules)`
`encoding` | *`string`* | Which encoding to use when reading or writing file contents. Default is `utf8`
`resolve` | *`boolean`* | Whether or not to attempt to resolve file paths if not found. Default is `true`

@@ -53,16 +49,9 @@

-----|------------
`promise()` | Returns a promise that resolves with an array of file objects
`map(fn)` | Runs an iterator function over each file. Returns a promise that resolves with the new `array`
`map(fn)` | Runs an iterator function over each file. Returns a promise that resolves with a new `array`
`each(fn)` | Runs an iterator function over each file. Returns a promise that resolves with `undefined`
`sync()` | Walks files and directories in syncronous mode. Returns an array of file objects
`require()` | Enables reading of file contents by requiring them
`stream()` | Enables reading of file contents as a stream
`buffer()` | Enables reading of file contents as a buffer
`contents(str)` | Enables reading of file contents. First parameter can be: `'require'`, `'stream'`, or `'buffer'`
`resolveFilePath(str)` | Resolve a file path relative to options
`promise` | Returns a promise that resolves with an `array` of file objects
## File Objects
Each file returned from walk has the following signature
### Properties
### File Objects
Each file object returned from walk has the following signature:
#### Properties
name | type | description

@@ -80,7 +69,18 @@ -----|----- | -----------

`stat` | *`object`* | The [fs.stat](https://nodejs.org/api/fs.html#fs_class_fs_stats) object for the file
`contents` | *`string\|object`* | Contents of the file. If `require` is `true`, will be resolved `object`, otherwise `string`. Default is `null`
`contents` | *`string\|object`* | Contents of the file. Default is `undefined`
`encoding` | *`string`* | Default encoding to use when reading or writing file contents. Default is `utf8`
### Methods
#### Methods
name | description
-----| -----------
[`createReadStream(opts)`](https://nodejs.org/api/fs.html#fscreatereadstreampath-options) | Returns a readable stream for the file
[`createWriteStream(opts)`](https://nodejs.org/api/fs.html#fscreatewritestreampath-options) | Returns a writable stream for the file
[`append(data, opts)`](https://nodejs.org/api/fs.html#fspromisesappendfilepath-data-options) | Appends data to the file
[`read(opts)`](https://nodejs.org/api/fs.html#fspromisesreadfilepath-options) | Reads the file contents. Returns `string` or `buffer` based on encoding
`readAsString(opts)` | Shortcut for `read()` that ensures encoding is set or throws an error
`readAsBuffer(opts)` | Shortcut for `read()` that sets the encoding to `null`
`readStr` | Alias for `readAsString`
`readBuf` | Alias for `readAsBuffer`
[`write(data, opts)`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Writes data to the file
`require` | Reads the file contents using `require`
`isBuffer` | Returns `true` if `file.contents` is a [`buffer`](https://nodejs.org/api/buffer.html#buffer_class_method_buffer_isbuffer_obj)

@@ -98,3 +98,6 @@ `isStream` | Returns `true` if `file.contents` is a [`stream`](https://nodejs.org/api/stream.html)

`isEmpty` | Returns `true` if the file is empty (zero bytes)
`getEncodingFromBOM` | Returns the encoding from the file [byte order mark](https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding) if set, otherwise `undefined`
### Sync
This package also supports a fully synchronous api. Instead of requiring the default package just require `@danmasta/walk/sync`. The api is exactly the same for walking and file objects

@@ -107,3 +110,3 @@ ## Examples

```js
walk('./').pipe(myDestinationStream());
walk('./').pipe(writeStream());
```

@@ -130,6 +133,8 @@

Read the contents of all `pug` files in `./views`
Read the contents of all `pug` files in `./views` as a `string`
```js
walk('./views', { src: '**/*.pug', contents: true }).promise().then(res => {
console.log('templates:', res);
walk('./views', { src: '**/*.pug' }).map(file => {
file.readStr().then(res => {
console.log('template:', file.path, res);
});
});

@@ -139,4 +144,6 @@ ```

```js
walk('./views', { src: '**/*.pug', buffer: true }).promise().then(res => {
console.log('templates:', res);
walk('./views', { src: '**/*.pug' }).map(file => {
file.readBuf().then(res => {
console.log('template:', file.path, res);
});
});

@@ -147,4 +154,4 @@ ```

```js
walk('./routes', { src: '**/*.js', require: true }).each(route => {
app.use(route());
walk('./routes', { src: '**/*.js' }).each(route => {
app.use(route.require());
}).then(() => {

@@ -157,3 +164,6 @@ console.log('all routes loaded');

```js
const templates = walk('./views', { src: '**/*.pug' }).sync();
const walk = require('@danmasta/walk/sync');
let templates = walk('./views', { src: '**/*.pug' });
console.log('templates:', templates);

@@ -163,5 +173,5 @@ ```

## Testing
Testing is currently run using mocha and chai. To execute tests just run `npm run test`. To generate unit test coverage reports just run `npm run coverage`
Tests are currently run using mocha and chai. To execute tests run `npm run test`. To generate unit test coverage reports run `npm run coverage`
## Contact
If you have any questions feel free to get in touch
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