Skip to content
This repository was archived by the owner on Aug 11, 2021. It is now read-only.

Commit 2182ba9

Browse files
authored
Merge branch 'master' into bugfix-deep-clone-instead-of-stringify-parse
2 parents 9796aeb + 690d62c commit 2182ba9

27 files changed

+793
-123
lines changed

.travis.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
language: node_js
22
node_js:
3-
- "0.12"
4-
- "0.10"
5-
- "0.8"
6-
- iojs
3+
- "7"
4+
- "6"
5+
- "4"
76
script: "npm test"
87
sudo: false
98
before_install:

README.md

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Every call to the client follows the same pattern:
3838

3939
### Credentials
4040

41-
Many requests to the registry can by authenticated, and require credentials
41+
Many requests to the registry can be authenticated, and require credentials
4242
for authorization. These credentials always look the same:
4343

4444
* `username` {String}
@@ -53,6 +53,19 @@ for authorization. These credentials always look the same:
5353
* `alwaysAuth` {Boolean} Whether calls to the target registry are always
5454
authed.
5555

56+
## Requests
57+
58+
As of `npm-registry-client@8`, all requests are made with an `Accept` header
59+
of `application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*`.
60+
61+
This enables filtered document responses to requests for package metadata.
62+
You know that you got a filtered response if the mime type is set to
63+
`application/vnd.npm.install-v1+json` and not `application/json`.
64+
65+
This filtering substantially reduces the over all data size. For example
66+
for `https://registry.npmjs.org/npm`, the compressed metadata goes from
67+
410kB to 21kB.
68+
5669
## API
5770

5871
### client.access(uri, params, cb)
@@ -125,7 +138,7 @@ Add (or replace) a single dist-tag onto the named package.
125138
* `cb` {Function}
126139

127140
Set all of the `dist-tags` for the named package at once, creating any
128-
`dist-tags` that do not already exit. Any `dist-tags` not included in the
141+
`dist-tags` that do not already exist. Any `dist-tags` not included in the
129142
`distTags` map will be removed.
130143

131144
### client.distTags.update(uri, params, cb)
@@ -164,6 +177,8 @@ Remove a single `dist-tag` from the named package.
164177
the callback quickly, and update the cache the background. Optional
165178
(default: false).
166179
* `auth` {Credentials} Optional.
180+
* `fullMetadata` {Boolean} If true, don't attempt to fetch filtered
181+
("corgi") registry metadata. (default: false)
167182
* `cb` {Function}
168183

169184
Fetches data from the registry via a GET request, saving it in the cache folder
@@ -183,6 +198,20 @@ Publish a package to the registry.
183198

184199
Note that this does not create the tarball from a folder.
185200

201+
### client.sendAnonymousCLIMetrics(uri, params, cb)
202+
203+
- `uri` {String} Base URL for the registry.
204+
- `params` {Object} Object containing per-request properties.
205+
- `metricId` {String} A uuid unique to this dataset.
206+
- `metrics` {Object} The metrics to share with the registry, with the following properties:
207+
- `from` {Date} When the first data in this report was collected.
208+
- `to` {Date} When the last data in this report was collected. Usually right now.
209+
- `successfulInstalls` {Number} The number of successful installs in this period.
210+
- `failedInstalls` {Number} The number of installs that ended in error in this period.
211+
- `cb` {Function}
212+
213+
PUT a metrics object to the `/-/npm/anon-metrics/v1/` endpoint on the registry.
214+
186215
### client.star(uri, params, cb)
187216

188217
* `uri` {String} The complete registry URI for the package to star.
@@ -259,6 +288,8 @@ caching logic directly.
259288
* `etag` {String} The cached ETag. Optional.
260289
* `lastModified` {String} The cached Last-Modified timestamp. Optional.
261290
* `follow` {Boolean} Follow 302/301 responses. Optional (default: true).
291+
* `streaming` {Boolean} Stream the request body as it comes, handling error
292+
responses in a non-streaming way.
262293
* `auth` {Credentials} Optional.
263294
* `cb` {Function}
264295
* `error` {Error | null}
@@ -310,5 +341,17 @@ any):
310341
Default = `"latest"`
311342
* `couchToken` {Object} A token for use with
312343
[couch-login](https://npmjs.org/package/couch-login).
313-
* `sessionToken` {string} A random identifier for this set of client requests.
344+
* `sessionToken` {String} A random identifier for this set of client requests.
314345
Default = 8 random hexadecimal bytes.
346+
* `maxSockets` {Number} The maximum number of connections that will be open per
347+
origin (unique combination of protocol:host:port). Passed to the
348+
[httpAgent](https://nodejs.org/api/http.html#http_agent_maxsockets).
349+
Default = 50
350+
* `isFromCI` {Boolean} Identify to severs if this request is coming from CI (for statistics purposes).
351+
Default = detected from environment– primarily this is done by looking for
352+
the CI environment variable to be set to `true`. Also accepted are the
353+
existence of the `JENKINS_URL`, `bamboo.buildKey` and `TDDIUM` environment
354+
variables.
355+
* `scope` {String} The scope of the project this command is being run for. This is the
356+
top level npm module in which a command was run.
357+
Default = none

index.js

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
module.exports = RegClient
44

5-
var join = require('path').join
6-
var fs = require('graceful-fs')
7-
85
var npmlog
96
try {
107
npmlog = require('npmlog')
@@ -39,6 +36,7 @@ function RegClient (config) {
3936
if (typeof this.config.retry.factor !== 'number') this.config.retry.factor = 10
4037
if (typeof this.config.retry.minTimeout !== 'number') this.config.retry.minTimeout = 10000
4138
if (typeof this.config.retry.maxTimeout !== 'number') this.config.retry.maxTimeout = 60000
39+
if (typeof this.config.maxSockets !== 'number') this.config.maxSockets = 50
4240

4341
this.config.userAgent = this.config.userAgent || 'node/' + process.version
4442
this.config.defaultTag = this.config.defaultTag || 'latest'
@@ -47,32 +45,30 @@ function RegClient (config) {
4745
delete this.config.log
4846

4947
var client = this
50-
fs.readdirSync(join(__dirname, 'lib')).forEach(function (f) {
51-
var entry = join(__dirname, 'lib', f)
52-
53-
// lib/group-name/operation.js -> client.groupName.operation
54-
var stat = fs.statSync(entry)
55-
if (stat.isDirectory()) {
56-
var groupName = f.replace(/-([a-z])/, dashToCamel)
57-
fs.readdirSync(entry).forEach(function (f) {
58-
if (!f.match(/\.js$/)) return
59-
60-
if (!client[groupName]) {
61-
// keep client.groupName.operation from stomping client.operation
62-
client[groupName] = Object.create(client)
63-
}
64-
var name = f.replace(/\.js$/, '').replace(/-([a-z])/, dashToCamel)
65-
client[groupName][name] = require(join(entry, f))
66-
})
67-
return
68-
}
69-
70-
if (!f.match(/\.js$/)) return
71-
var name = f.replace(/\.js$/, '').replace(/-([a-z])/, dashToCamel)
72-
client[name] = require(entry)
73-
})
74-
}
75-
76-
function dashToCamel (_, l) {
77-
return l.toUpperCase()
48+
client.access = require('./lib/access')
49+
client.adduser = require('./lib/adduser')
50+
client.attempt = require('./lib/attempt')
51+
client.authify = require('./lib/authify')
52+
client.deprecate = require('./lib/deprecate')
53+
client.distTags = Object.create(client)
54+
client.distTags.add = require('./lib/dist-tags/add')
55+
client.distTags.fetch = require('./lib/dist-tags/fetch')
56+
client.distTags.rm = require('./lib/dist-tags/rm')
57+
client.distTags.set = require('./lib/dist-tags/set')
58+
client.distTags.update = require('./lib/dist-tags/update')
59+
client.fetch = require('./lib/fetch')
60+
client.get = require('./lib/get')
61+
client.initialize = require('./lib/initialize')
62+
client.logout = require('./lib/logout')
63+
client.org = require('./lib/org')
64+
client.ping = require('./lib/ping')
65+
client.publish = require('./lib/publish')
66+
client.request = require('./lib/request')
67+
client.sendAnonymousCLIMetrics = require('./lib/send-anonymous-CLI-metrics')
68+
client.star = require('./lib/star')
69+
client.stars = require('./lib/stars')
70+
client.tag = require('./lib/tag')
71+
client.team = require('./lib/team')
72+
client.unpublish = require('./lib/unpublish')
73+
client.whoami = require('./lib/whoami')
7874
}

lib/access.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,20 @@ function accessAssertions (subcommand, uri, params, cb) {
102102
'callback': [cb, 'function']
103103
})
104104
if (contains([
105-
'public', 'restricted', 'grant', 'revoke', 'ls-collaborators'
105+
'public', 'restricted'
106106
], subcommand)) {
107-
typeChecks({ 'package': [params.package, 'string']})
107+
typeChecks({ 'package': [params.package, 'string'] })
108108
assert(!!npa(params.package).scope,
109109
'access commands are only accessible for scoped packages')
110110
}
111111
if (contains(['grant', 'revoke', 'ls-packages'], subcommand)) {
112-
typeChecks({ 'scope': [params.scope, 'string']})
112+
typeChecks({ 'scope': [params.scope, 'string'] })
113113
}
114114
if (contains(['grant', 'revoke'], subcommand)) {
115-
typeChecks({ 'team': [params.team, 'string']})
115+
typeChecks({ 'team': [params.team, 'string'] })
116116
}
117117
if (subcommand === 'grant') {
118-
typeChecks({ 'permissions': [params.permissions, 'string']})
118+
typeChecks({ 'permissions': [params.permissions, 'string'] })
119119
assert(params.permissions === 'read-only' ||
120120
params.permissions === 'read-write',
121121
'permissions must be either read-only or read-write')
@@ -125,6 +125,7 @@ function accessAssertions (subcommand, uri, params, cb) {
125125
function typeChecks (specs) {
126126
Object.keys(specs).forEach(function (key) {
127127
var checks = specs[key]
128+
/* eslint valid-typeof:0 */
128129
assert(typeof checks[0] === checks[1],
129130
key + ' is required and must be of type ' + checks[1])
130131
})

lib/adduser.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module.exports = adduser
22

33
var url = require('url')
44
var assert = require('assert')
5+
var extend = Object.assign || require('util')._extend
56

67
function adduser (uri, params, cb) {
78
assert(typeof uri === 'string', 'must pass registry URI to adduser')
@@ -28,7 +29,7 @@ function adduser (uri, params, cb) {
2829
if (!username) return cb(new Error('No username supplied.'))
2930
if (!password) return cb(new Error('No password supplied.'))
3031
if (!email) return cb(new Error('No email address supplied.'))
31-
if (!email.match(/^[^@]+@[^\.]+\.[^\.]+/)) {
32+
if (!email.match(/^[^@]+@[^.]+\.[^.]+/)) {
3233
return cb(new Error('Please use a real email address.'))
3334
}
3435

@@ -67,7 +68,7 @@ function adduser (uri, params, cb) {
6768
}
6869
this.request(
6970
uri,
70-
options,
71+
extend({}, options),
7172
function (error, data, json, response) {
7273
if (!error || !response || response.statusCode !== 409) {
7374
return cb(error, data, json, response)
@@ -110,7 +111,7 @@ function adduser (uri, params, cb) {
110111
client.log.verbose('adduser', 'back', [error, data, json])
111112
if (!error) {
112113
error = new Error(
113-
(response && response.statusCode || '') + ' ' +
114+
((response && response.statusCode) || '') + ' ' +
114115
'Could not create user\n' + JSON.stringify(data)
115116
)
116117
}

lib/fetch.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@ function fetch (uri, params, cb) {
1818
makeRequest.call(client, uri, params, function (er, req) {
1919
if (er) return cb(er)
2020

21-
req.on('error', function (er) {
21+
req.once('error', retryOnError)
22+
23+
function retryOnError (er) {
2224
if (operation.retry(er)) {
2325
client.log.info('retry', 'will retry, error on last attempt: ' + er)
2426
} else {
2527
cb(er)
2628
}
27-
})
29+
}
2830

2931
req.on('response', function (res) {
3032
client.log.http('fetch', '' + res.statusCode, uri)
33+
req.removeListener('error', retryOnError)
3134

3235
var er
3336
var statusCode = res && res.statusCode
@@ -37,6 +40,10 @@ function fetch (uri, params, cb) {
3740
res.resume()
3841
if (process.version === 'v0.10.0') unstick(res)
3942

43+
req.once('error', function (er) {
44+
res.emit('error', er)
45+
})
46+
4047
return cb(null, res)
4148
// Only retry on 408, 5xx or no `response`.
4249
} else if (statusCode === 408) {

lib/initialize.js

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ var HttpsAgent = require('https').Agent
44

55
var pkg = require('../package.json')
66

7-
var httpAgent
8-
var httpsAgent
9-
107
module.exports = initialize
118

129
function initialize (uri, method, accept, headers) {
1310
if (!this.config.sessionToken) {
1411
this.config.sessionToken = crypto.randomBytes(8).toString('hex')
1512
this.log.verbose('request id', this.config.sessionToken)
1613
}
14+
if (this.config.isFromCI == null) {
15+
this.config.isFromCI = Boolean(
16+
process.env['CI'] === 'true' || process.env['TDDIUM'] ||
17+
process.env['JENKINS_URL'] || process.env['bamboo.buildKey'])
18+
}
1719

1820
var opts = {
1921
url: uri,
@@ -24,7 +26,7 @@ function initialize (uri, method, accept, headers) {
2426
cert: this.config.ssl.certificate,
2527
key: this.config.ssl.key,
2628
ca: this.config.ssl.ca,
27-
agent: getAgent(uri.protocol, this.config)
29+
agent: getAgent.call(this, uri.protocol)
2830
}
2931

3032
// allow explicit disabling of proxy in environment via CLI
@@ -50,33 +52,39 @@ function initialize (uri, method, accept, headers) {
5052
if (this.refer) headers.referer = this.refer
5153

5254
headers['npm-session'] = this.config.sessionToken
55+
headers['npm-in-ci'] = String(this.config.isFromCI)
5356
headers['user-agent'] = this.config.userAgent
57+
if (this.config.scope) {
58+
headers['npm-scope'] = this.config.scope
59+
}
5460

5561
return opts
5662
}
5763

58-
function getAgent (protocol, config) {
64+
function getAgent (protocol) {
5965
if (protocol === 'https:') {
60-
if (!httpsAgent) {
61-
httpsAgent = new HttpsAgent({
66+
if (!this.httpsAgent) {
67+
this.httpsAgent = new HttpsAgent({
6268
keepAlive: true,
63-
localAddress: config.proxy.localAddress,
64-
rejectUnauthorized: config.ssl.strict,
65-
ca: config.ssl.ca,
66-
cert: config.ssl.certificate,
67-
key: config.ssl.key
69+
maxSockets: this.config.maxSockets,
70+
localAddress: this.config.proxy.localAddress,
71+
rejectUnauthorized: this.config.ssl.strict,
72+
ca: this.config.ssl.ca,
73+
cert: this.config.ssl.certificate,
74+
key: this.config.ssl.key
6875
})
6976
}
7077

71-
return httpsAgent
78+
return this.httpsAgent
7279
} else {
73-
if (!httpAgent) {
74-
httpAgent = new HttpAgent({
80+
if (!this.httpAgent) {
81+
this.httpAgent = new HttpAgent({
7582
keepAlive: true,
76-
localAddress: config.proxy.localAddress
83+
maxSockets: this.config.maxSockets,
84+
localAddress: this.config.proxy.localAddress
7785
})
7886
}
7987

80-
return httpAgent
88+
return this.httpAgent
8189
}
8290
}

0 commit comments

Comments
 (0)