Socket
Socket
Sign inDemoInstall

nano

Package Overview
Dependencies
Maintainers
7
Versions
155
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nano - npm Package Compare versions

Comparing version 9.0.1 to 9.0.2

.asf.yaml

70

lib/nano.js

@@ -17,3 +17,6 @@ // Licensed under the Apache License, Version 2.0 (the 'License'); you may not

const axios = require('axios').default
const INVALID_PARAMETERS_ERROR = new Error('Invalid parameters')
const axiosCookieJarSupport = require('axios-cookiejar-support').default
const tough = require('tough-cookie')
axiosCookieJarSupport(axios)
const cookieJar = new tough.CookieJar()
const stream = require('stream')

@@ -24,2 +27,3 @@ const http = require('http')

const AGENT_DEFAULTS = { keepAlive: true, maxSockets: 50, keepAliveMsecs: 30000 }
const SCRUBBED_STR = 'XXXXXX'
const defaultHttpAgent = new http.Agent(AGENT_DEFAULTS)

@@ -52,3 +56,3 @@ const defaultHttpsAgent = new https.Agent(AGENT_DEFAULTS)

// calls the supplied callback or if not supplied, returns a rejected promise
function callbackOrRejectError (callback, error = INVALID_PARAMETERS_ERROR) {
function callbackOrRejectError (callback, error = new Error('Invalid parameters')) {
if (callback) {

@@ -97,5 +101,5 @@ return callback(error, null)

function scrub (str) {
function scrubURL (str) {
if (str) {
str = str.replace(/\/\/(.*)@/, '//XXXXXX:XXXXXX@')
str = str.replace(/\/\/(.*)@/, `//${SCRUBBED_STR}:${SCRUBBED_STR}@`)
}

@@ -153,4 +157,2 @@ return str

log({ err: 'couch', body: body, headers: responseHeaders })
// cloudant stacktrace

@@ -169,4 +171,4 @@ if (typeof body === 'string') {

// scrub credentials
req.url = scrub(req.url)
responseHeaders.url = scrub(responseHeaders.url)
req.url = scrubURL(req.url)
responseHeaders.uri = scrubURL(responseHeaders.uri)
if (req.headers.cookie) {

@@ -176,2 +178,4 @@ req.headers.cookie = 'XXXXXXX'

log({ err: 'couch', body: body, headers: responseHeaders })
const message = body.message || 'couch returned ' + statusCode

@@ -200,2 +204,29 @@ const errors = new Error(message)

const streamResponseHandler = function (response, req, stream) {
const statusCode = response.status || (response.response && response.response.status) || 500
if (response.isAxiosError && response.response) {
response = response.response
}
const message = response.statusText
const responseHeaders = Object.assign({
uri: req.url,
statusCode: statusCode
}, response.headers)
const error = new Error(message)
error.scope = 'couch'
error.statusCode = statusCode
error.request = req
error.headers = responseHeaders
error.errid = 'non_200'
error.name = 'Error'
error.description = message
error.reason = message
log({ err: 'couch', body: message, headers: responseHeaders })
stream.emit('error', error)
}
function relax (opts, callback) {

@@ -232,6 +263,7 @@ if (typeof opts === 'function') {

// https://github.com/mikeal/request#requestjar
const isJar = opts.jar || cfg.jar
const isJar = opts.jar || cfg.jar || (cfg.requestDefaults && cfg.requestDefaults.jar)
if (isJar) {
req.jar = isJar
req.jar = cookieJar
req.withCredentials = true
}

@@ -324,3 +356,3 @@

log(req)
cfg.cookies = cookieJar.getCookiesSync(cfg.url)

@@ -349,2 +381,11 @@ // This where the HTTP request is made.

// scrub and log
const scrubbedReq = JSON.parse(JSON.stringify(req))
scrubbedReq.url = scrubURL(scrubbedReq.url)
if (scrubbedReq.auth) {
scrubbedReq.auth.username = SCRUBBED_STR
scrubbedReq.auth.password = SCRUBBED_STR
}
log(scrubbedReq)
// actually do the HTTP request

@@ -354,3 +395,8 @@ if (opts.stream) {

const outStream = new stream.PassThrough()
axios(req).then((response) => { response.data.pipe(outStream) })
axios(req)
.then((response) => {
response.data.pipe(outStream)
}).catch(e => {
streamResponseHandler(e, req, outStream)
})
return outStream

@@ -357,0 +403,0 @@ } else {

18

package.json

@@ -7,3 +7,3 @@ {

"repository": "http://github.com/apache/couchdb-nano",
"version": "9.0.1",
"version": "9.0.2",
"author": "Apache CouchDB <dev@couchdb.apache.org> (http://couchdb.apache.org)",

@@ -21,11 +21,13 @@ "keywords": [

"dependencies": {
"axios": "^0.21.0",
"qs": "^6.9.4"
"axios": "^0.21.1",
"axios-cookiejar-support": "^1.0.1",
"qs": "^6.9.4",
"tough-cookie": "^4.0.0"
},
"devDependencies": {
"@types/node": "^14.14.6",
"jest": "^26.6.1",
"nock": "^13.0.4",
"standard": "^16.0.1",
"typescript": "^4.0.5"
"@types/node": "^14.14.20",
"jest": "^26.6.3",
"nock": "^13.0.5",
"standard": "^16.0.3",
"typescript": "^4.1.3"
},

@@ -32,0 +34,0 @@ "scripts": {

@@ -64,3 +64,3 @@ [![Build Status](https://travis-ci.org/apache/couchdb-nano.svg?branch=main)](https://travis-ci.org/apache/couchdb-nano)![Coverage](https://img.shields.io/badge/coverage-100%-ff69b4.svg)[![dependencies Status](https://david-dm.org/apache/couchdb-nano/status.svg)](https://david-dm.org/apache/couchdb-nano)[![NPM](http://img.shields.io/npm/v/nano.svg?style=flat-square)](https://www.npmjs.com/package/nano)

- [db.createIndex(indexDef, [callback])](#dbcreateindexindexdef-callback)
- [db.changesReader...](##reading-changes-feed)
- [db.changesReader](#reading-changes-feed)
- [Partitioned database functions](#partition-functions)

@@ -112,3 +112,3 @@ - [db.partitionInfo(partitionKey, [callback])](#dbpartitioninfopartitionkey-callback))

The URL you supply may also contain authentication credentials e.g. `http://admin:mypassword@localhost:5984`.
> Note: The URL you supply may also contain authentication credentials e.g. `http://admin:mypassword@localhost:5984`.

@@ -139,2 +139,15 @@ To create a new database:

or in the async/await style:
```js
try {
const response = await nano.db.create('alice')
// succeeded
console.log(response)
} catch (e) {
// failed
console.error(e)
}
```
2) Callbacks

@@ -155,3 +168,3 @@

The documentation will now follow the *Promises* style.
The documentation will follow the *async/await* style.

@@ -174,22 +187,5 @@ ------------------

or in the raw Promises-style
Running this example will produce:
```js
const nano = require('nano')('http://localhost:5984');
let alice;
nano.db.destroy('alice').then((response) => {
return nano.db.create('alice')
}).then((response) => {
alice = nano.use('alice')
return alice.insert({ happy: true }, 'rabbit')
}).then((response) => {
console.log('you have inserted a document with an _id of rabbit')
console.log(response);
})
```
If you run either of these examples (after starting CouchDB) you will see:
```
you have inserted a document with an _id of rabbit.

@@ -224,4 +220,4 @@ { ok: true,

const opts = {
url: "http://localhost:5984/foo",
requestDefaults: { "proxy" : "http://someproxy" }
url: 'http://localhost:5984/foo',
requestDefaults: { proxy: { 'protocol': 'http', 'host': 'myproxy.net' } }
};

@@ -231,3 +227,3 @@ const db = require('nano')(opts);

Please check [request] for more information on the defaults. They support features like cookie jar, proxies, ssl, etc.
Please check [axios] for more information on the defaults. They support features like proxies, timeout etc.

@@ -248,24 +244,17 @@ You can tell nano to not parse the URL (maybe the server is behind a proxy, is accessed through a rewrite rule or other):

A very important configuration parameter if you have a high traffic website and are using `nano` is setting up the `pool.size`. By default, the Node.js HTTP global agent (client) has a certain size of active connections that can run simultaneously, while others are kept in a queue. Pooling can be disabled by setting the `agent` property in `requestDefaults` to `false`, or adjust the global pool size using:
A very important configuration parameter if you have a high traffic website and are using `nano` is the HTTP pool size. By default, the Node.js HTTP global agent has a infinite number of active connections that can run simultaneously. This can be limited to user-defined number (`maxSockets`) of requests that are "in flight", while others are kept in a queue. Here's an example explicitly using the Node.js HTTP agent configured with [custom options](https://nodejs.org/api/http.html#http_new_agent_options):
```js
http.globalAgent.maxSockets = 20;
```
const http = require('http')
const myagent = new http.Agent({
keepAlive: true,
maxSockets: 25
})
You can also increase the size in your calling context using `requestDefaults` if this is problematic. Refer to the [request] documentation and examples for further clarification.
Here's an example explicitly using the keep alive agent (installed using `npm install agentkeepalive`), especially useful to limit your open sockets when doing high-volume access to CouchDB on localhost:
```js
const agentkeepalive = require('agentkeepalive');
const myagent = new agentkeepalive({
maxSockets: 50,
maxKeepAliveRequests: 0,
maxKeepAliveTime: 30000
const db = require('nano')({
url: 'http://localhost:5984/foo',
requestDefaults : {
agent : myagent
}
});
const db = require('nano')(
{ url: "http://localhost:5984/foo",
requestDefaults : { "agent" : myagent }
});
```

@@ -323,5 +312,3 @@

```js
nano.db.create('alice').then((body) => {
console.log('database alice created!');
})
await nano.db.create('alice')
```

@@ -334,5 +321,3 @@

```js
nano.db.get('alice').then((body) => {
console.log(body);
})
const info = await nano.db.get('alice')
```

@@ -345,5 +330,3 @@

```js
nano.db.destroy('alice').then((body) => {
// database destroyed
})
await nano.db.destroy('alice')
```

@@ -356,8 +339,3 @@

```js
nano.db.list().then((body) => {
// body is an array
body.forEach((db) => {
console.log(db);
});
});
const dblist = await nano.db.list()
```

@@ -370,3 +348,5 @@

```js
nano.db.list().pipe(process.stdout);
nano.db.listAsStream()
.on('error', (e) => console.error('error', e))
.pipe(process.stdout);
```

@@ -385,6 +365,5 @@

```js
nano.db.replicate('alice', 'http://admin:password@otherhost.com:5984/alice',
{ create_target:true }).then((body) => {
console.log(body);
});
const response = await nano.db.replicate('alice',
'http://admin:password@otherhost.com:5984/alice',
{ create_target:true })
```

@@ -399,6 +378,5 @@

```js
nano.db.replication.enable('alice', 'http://admin:password@otherhost.com:5984/alice',
{ create_target:true }).then((body) => {
console.log(body);
});
const response = await nano.db.replication.enable('alice',
'http://admin:password@otherhost.com:5984/alice',
{ create_target:true })
```

@@ -412,8 +390,6 @@

```js
nano.db.replication.enable('alice', 'http://admin:password@otherhost.com:5984/alice',
{ create_target:true }).then((body) => {
return nano.db.replication.query(body.id);
}).then((response) => {
console.log(response);
});
const r = await nano.db.replication.enable('alice',
'http://admin:password@otherhost.com:5984/alice',
{ create_target:true })
const q = await nano.db.replication.query(r.id)
```

@@ -427,8 +403,6 @@

```js
nano.db.replication.enable('alice', 'http://admin:password@otherhost.com:5984/alice',
{ create_target:true }).then((body) => {
return nano.db.replication.disable(body.id);
}).then((response) => {
console.log(response);
});
const r = await nano.db.replication.enable('alice',
'http://admin:password@otherhost.com:5984/alice',
{ create_target:true })
await nano.db.replication.disable(r.id);
```

@@ -442,5 +416,3 @@

```js
nano.db.changes('alice').then((body) => {
console.log(body);
});
const c = await nano.db.changes('alice')
```

@@ -461,5 +433,3 @@

```js
nano.db.info().then((body) => {
console.log('got database info', body);
});
const info = await nano.db.info()
```

@@ -473,7 +443,7 @@

const alice = nano.use('alice');
alice.insert({ happy: true }, 'rabbit').then((body) => {
// do something
});
await alice.insert({ happy: true }, 'rabbit')
```
The database object can be used to access the [Document Functions](#document-functions).
### nano.db.use(name)

@@ -540,5 +510,3 @@

const alice = nano.use('alice');
alice.insert({ happy: true }, 'rabbit').then((body) => {
console.log(body);
});
const response = await alice.insert({ happy: true }, 'rabbit')
```

@@ -550,5 +518,3 @@

const alice = nano.use('alice')
alice.insert({ _id: 'myid', happy: true }).then((body) => {
console.log(body)
})
const response alice.insert({ _id: 'myid', happy: true })
```

@@ -560,5 +526,3 @@

const alice = nano.use('alice')
alice.insert({ _id: 'myid', _rev: '1-23202479633c2b380f79507a776743d5', happy: false }).then((body) => {
console.log(body)
})
const response = await alice.insert({ _id: 'myid', _rev: '1-23202479633c2b380f79507a776743d5', happy: false })
```

@@ -571,5 +535,3 @@

```js
alice.destroy('rabbit', '3-66c01cdf99e84c83a9b3fe65b88db8c0').then((body) => {
console.log(body);
});
const response = await alice.destroy('rabbit', '3-66c01cdf99e84c83a9b3fe65b88db8c0')
```

@@ -582,5 +544,3 @@

```js
alice.get('rabbit').then((body) => {
console.log(body);
});
const doc = await alice.get('rabbit')
```

@@ -591,5 +551,3 @@

```js
alice.get('rabbit', { revs_info: true }).then((body) => {
console.log(body);
});
const doc = await alice.get('rabbit', { revs_info: true })
```

@@ -602,5 +560,3 @@

```js
alice.head('rabbit').then((headers) => {
console.log(headers);
});
const headers = await alice.head('rabbit')
```

@@ -620,5 +576,3 @@

];
alice.bulk({docs:documents}).then((body) => {
console.log(body);
});
const response = await alice.bulk({ docs: documents })
```

@@ -631,6 +585,5 @@

```js
alice.list().then((body) => {
body.rows.forEach((doc) => {
console.log(doc);
});
const doclist = await alice.list().then((body)
doclist.rows.forEach((doc) => {
console.log(doc);
});

@@ -642,8 +595,3 @@ ```

```js
alice.list({include_docs: true}).then((body) => {
body.rows.forEach((doc) => {
// output each document's body
console.log(doc.doc);
});
});
const doclist = await alice.list({include_docs: true})
```

@@ -656,3 +604,5 @@

```js
alice.list().pipe(process.stdout)
alice.listAsStream()
.on('error', (e) => console.error('error', e))
.pipe(process.stdout)
```

@@ -669,5 +619,3 @@

const keys = ['tiger', 'zebra', 'donkey'];
alice.fetch({keys: keys}).then((data) => {
console.log(data);
});
const datat = await alice.fetch({keys: keys})
```

@@ -694,5 +642,3 @@

};
alice.createIndex(indexDef).then((result) => {
console.log(result);
});
const response = await alice.createIndex(indexDef)
```

@@ -917,6 +863,10 @@

// fetch document id/revs from a partition
nano.db.partitionedListAsStream('canidae').pipe(process.stdout)
nano.db.partitionedListAsStream('canidae')
.on('error', (e) => console.error('error', e))
.pipe(process.stdout)
// add document bodies but limit size of response
nano.db.partitionedListAsStream('canidae', { include_docs: true, limit: 5 }).pipe(process.stdout)
nano.db.partitionedListAsStream('canidae', { include_docs: true, limit: 5 })
.on('error', (e) => console.error('error', e))
.pipe(process.stdout)
```

@@ -939,3 +889,5 @@

// find document whose name is 'wolf' in the 'canidae' partition
db.partitionedFindAsStream('canidae', { 'selector' : { 'name': 'Wolf' }}).pipe(process.stdout)
db.partitionedFindAsStream('canidae', { 'selector' : { 'name': 'Wolf' }})
.on('error', (e) => console.error('error', e))
.pipe(process.stdout)
```

@@ -963,3 +915,5 @@

}
db.partitionedSearchAsStream('canidae', 'search-ddoc', 'search-index', params).pipe(process.stdout)
db.partitionedSearchAsStream('canidae', 'search-ddoc', 'search-index', params)
.on('error', (e) => console.error('error', e))
.pipe(process.stdout)
// { total_rows: ... , bookmark: ..., rows: [ ...] }

@@ -992,3 +946,5 @@ ```

}
db.partitionedView('canidae', 'view-ddoc', 'view-name', params).pipe(process.stdout)
db.partitionedViewAsStream('canidae', 'view-ddoc', 'view-name', params)
.on('error', (e) => console.error('error', e))
.pipe(process.stdout)
// { rows: [ { key: ... , value: [Object] } ] }

@@ -1009,5 +965,3 @@ ```

if (!err) {
alice.multipart.insert({ foo: 'bar' }, [{name: 'rabbit.png', data: data, content_type: 'image/png'}], 'mydoc').then((body) => {
console.log(body);
});
await alice.multipart.insert({ foo: 'bar' }, [{name: 'rabbit.png', data: data, content_type: 'image/png'}], 'mydoc')
}

@@ -1023,5 +977,3 @@ });

```js
alice.multipart.get('rabbit').then((buffer) => {
console.log(buffer.toString());
});
const response = await alice.multipart.get('rabbit')
```

@@ -1042,6 +994,7 @@

if (!err) {
alice.attachment.insert('rabbit', 'rabbit.png', data, 'image/png',
{ rev: '12-150985a725ec88be471921a54ce91452' }).then((body) => {
console.log(body);
});
await alice.attachment.insert('rabbit',
'rabbit.png',
data,
'image/png',
{ rev: '12-150985a725ec88be471921a54ce91452' })
}

@@ -1064,5 +1017,4 @@ });

alice.attachment.get('rabbit', 'rabbit.png').then((body) => {
fs.writeFile('rabbit.png', body);
});
const body = await alice.attachment.get('rabbit', 'rabbit.png')
fs.writeFile('rabbit.png', body)
```

@@ -1074,4 +1026,5 @@

const fs = require('fs');
alice.attachment.getAsStream('rabbit', 'rabbit.png').pipe(fs.createWriteStream('rabbit.png'));
alice.attachment.getAsStream('rabbit', 'rabbit.png')
.on('error', e => console.error)
.pipe(fs.createWriteStream('rabbit.png'));
```

@@ -1086,6 +1039,3 @@

```js
alice.attachment.destroy('rabbit', 'rabbit.png',
{rev: '1-4701d73a08ce5c2f2983bf7c9ffd3320'}).then((body) => {
console.log(body);
});
const response = await alice.attachment.destroy('rabbit', 'rabbit.png', {rev: '1-4701d73a08ce5c2f2983bf7c9ffd3320'})
```

@@ -1101,10 +1051,6 @@

```js
alice.view('characters', 'happy_ones', {
'key': 'Tea Party',
'include_docs': true
}).then((body) => {
body.rows.forEach((doc) => {
console.log(doc.value);
});
});
const body = await alice.view('characters', 'happy_ones', { key: 'Tea Party', include_docs: true })
body.rows.forEach((doc) => {
console.log(doc.value)
})
```

@@ -1115,9 +1061,3 @@

```js
alice.view('characters', 'soldiers', {
'keys': ['Hearts', 'Clubs']
}).then((body) => {
body.rows.forEach((doc) => {
console.log(doc.value);
});
});
const body = await alice.view('characters', 'soldiers', { keys: ['Hearts', 'Clubs'] })
```

@@ -1128,15 +1068,7 @@

```js
alice.view('characters', 'happy_ones').then((body) => {
body.rows.forEach((doc) => {
console.log(doc.value);
});
});
const body = await alice.view('characters', 'happy_ones')
```
```js
alice.view('characters', 'happy_ones', { include_docs: true }).then((body) => {
body.rows.forEach((doc) => {
console.log(doc.value);
});
});
const body = alice.view('characters', 'happy_ones', { include_docs: true })
```

@@ -1149,3 +1081,5 @@

```js
alice.view('characters', 'happy_ones', {reduce: false}).pipe(process.stdout);
alice.viewAsStream('characters', 'happy_ones', {reduce: false})
.on('error', (e) => console.error('error', e))
.pipe(process.stdout);
```

@@ -1158,5 +1092,3 @@

```js
alice.viewWithList('characters', 'happy_ones', 'my_list').then((body) => {
console.log(body);
});
const body = await alice.viewWithList('characters', 'happy_ones', 'my_list')
```

@@ -1169,5 +1101,5 @@

```js
alice.viewWithListAsStream('characters', 'happy_ones', 'my_list').then((body) => {
console.log(body);
});
alice.viewWithListAsStream('characters', 'happy_ones', 'my_list')
.on('error', (e) => console.error('error', e))
.pipe(process.stdout);
```

@@ -1181,5 +1113,3 @@

```js
alice.show('characters', 'format_doc', '3621898430').then((doc) => {
console.log(doc);
});
const doc = await alice.show('characters', 'format_doc', '3621898430')
```

@@ -1195,6 +1125,3 @@

```js
db.atomic("update", "inplace", "foobar",
{field: "foo", value: "bar"}).then((response) => {
console.log(response);
});
const response = await db.atomic('update', 'inplace', 'foobar', {field: 'foo', value: 'bar'})
```

@@ -1208,9 +1135,8 @@

"in-place" : "function(doc, req) {
var request_body = JSON.parse(req.body);
var field = request_body.field;
var value = request_body.value;
var message = 'set ' + field + ' to ' + value;
doc[field] = value;
return [doc, message];
var request_body = JSON.parse(req.body)
var field = request_body.field
var value = request_body.value
var message = 'set ' + field + ' to ' + value
doc[field] = value
return [doc, message]
}"

@@ -1225,5 +1151,3 @@ }

```js
alice.search('characters', 'happy_ones', { q: 'cat' }).then((doc) => {
console.log(doc);
});
const response = await alice.search('characters', 'happy_ones', { q: 'cat' })
```

@@ -1235,5 +1159,3 @@

const drilldown = [['author', 'Dickens']['publisher','Penguin']]
alice.search('inventory', 'books', { q: '*:*', drilldown: drilldown }).then((doc) => {
console.log(doc);
});
const response = await alice.search('inventory', 'books', { q: '*:*', drilldown: drilldown })
```

@@ -1265,5 +1187,3 @@

};
alice.find(q).then((doc) => {
console.log(doc);
});
const response = await alice.find(q)
```

@@ -1285,3 +1205,5 @@

};
alice.findAsStream(q).pipe(process.stdout);
alice.findAsStream(q)
.on('error', (e) => console.error('error', e))
.pipe(process.stdout);
```

@@ -1294,12 +1216,18 @@

```js
const nano = require('nano')({url: 'http://localhost:5984', requestDefaults: {jar:true}}),
username = 'user',
userpass = 'pass',
db = nano.db.use('mydb');
const nano = require('nano')({
url: 'http://localhost:5984',
requestDefaults: {
jar: true
}
})
const username = 'user'
const userpass = 'pass'
const db = nano.db.use('mydb')
nano.auth(username, userpass).then((() => {
return db.get('mydoc');
}).then((doc) => {
console.log(doc);
});
// authenticate
await nano.auth(username, userpass)
// requests from now on are authenticated
const doc = await db.get('mydoc')
console.log(doc)
```

@@ -1312,6 +1240,4 @@

```js
nano.session().then((doc) => {
console.log(doc)
// { userCtx: { roles: [ '_admin', '_reader', '_writer' ], name: 'rita' }, ok: true }
});
const doc = await nano.session()
// { userCtx: { roles: [ '_admin', '_reader', '_writer' ], name: 'rita' }, ok: true }
```

@@ -1326,5 +1252,3 @@

```js
nano.uuids(3).then((doc) => {
console.log(doc);
});
const response = await nano.uuids(3)
// { uuids: [

@@ -1369,3 +1293,5 @@ // '5d1b3ef2bc7eea51f660c091e3dffa23',

const alice = nano.use('alice');
alice.attachment.getAsStream('rabbit', 'picture.png').pipe(fs.createWriteStream('/tmp/rabbit.png'));
alice.attachment.getAsStream('rabbit', 'picture.png')
.on('error', (e) => console.error('error', e))
.pipe(fs.createWriteStream('/tmp/rabbit.png'));
```

@@ -1460,3 +1386,3 @@

[8]: http://webchat.freenode.net?channels=%23couchdb-dev
[request]: https://github.com/request/request
[axios]: https://github.com/axios/axios

@@ -1463,0 +1389,0 @@ http://freenode.org/

Sorry, the diff of this file is too big to display

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