libnpmaccess
Advanced tools
Comparing version
# Change Log | ||
<a name="4.0.0"></a> | ||
## [4.0.0](https://github.com/npm/libnpmaccess/compare/v3.0.2...v4.0.0) (2020-03-02) | ||
### BREAKING CHANGES | ||
- `25ac61b` fix: remove figgy-pudding ([@claudiahdz](https://github.com/claudiahdz)) | ||
- `8d6f692` chore: rename opts.mapJson to opts.mapJSON ([@mikemimik](https://github.com/mikemimik)) | ||
### Features | ||
- `257879a` chore: removed standard-version as a dep; updated scripts for version/publishing ([@mikemimik](https://github.com/mikemimik)) | ||
- `46c6740` fix: pull-request feedback; read full commit message ([@mikemimik](https://github.com/mikemimik)) | ||
- `778c102` chore: updated test, made case more clear ([@mikemimik](https://github.com/mikemimik)) | ||
- `6dc9852` fix: refactored 'pwrap' function out of code base; use native promises ([@mikemimik](https://github.com/mikemimik)) | ||
- `d2e7219` chore: updated package scripts; update CI workflow ([@mikemimik](https://github.com/mikemimik)) | ||
- `5872364` chore: renamed test/util/ to test/fixture/; tap will ignore now ([@mikemimik](https://github.com/mikemimik)) | ||
- `3c6b71d` chore: linted test file; made tap usage 'better' ([@mikemimik](https://github.com/mikemimik)) | ||
- `20f0858` fix: added default values to params for API functions (with tests) ([@mikemimik](https://github.com/mikemimik)) | ||
- `3218289` feat: replace get-stream with minipass ([@mikemimik](https://github.com/mikemimik)) | ||
### Documentation | ||
- `6c8ffa0` docs: removed opts.Promise from docs; no longer in use ([@mikemimik](https://github.com/mikemimik)) | ||
- `311bff5` chore: added return types to function docs in README ([@mikemimik](https://github.com/mikemimik)) | ||
- `823726a` chore: removed travis badge, added github actions badge ([@mikemimik](https://github.com/mikemimik)) | ||
- `80e80ac` chore: updated README ([@mikemimik](https://github.com/mikemimik)) | ||
### Dependencies | ||
- `baed2b9` deps: standard-version@7.1.0 (audit fix) ([@mikemimik](https://github.com/mikemimik)) | ||
- `65c2204` deps: nock@12.0.1 (audit fix) ([@mikemimik](https://github.com/mikemimik)) | ||
- `2668386` deps: npm-registry-fetch@8.0.0 ([@mikemimik](https://github.com/mikemimik)) | ||
- `ef093e2` deps: tap@14.10.6 ([@mikemimik](https://github.com/mikemimik)) | ||
### Miscellanieous | ||
- `8e33902` chore: basic project updates ([@claudiahdz](https://github.com/claudiahdz)) | ||
- `50e1433` fix: update return value; add tests ([@mikemimik](https://github.com/mikemimik)) | ||
- `36d5c80` chore: updated gitignore; includes coverage folder ([@mikemimik](https://github.com/mikemimik)) | ||
--- | ||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. | ||
@@ -4,0 +41,0 @@ |
201
index.js
'use strict' | ||
const figgyPudding = require('figgy-pudding') | ||
const getStream = require('get-stream') | ||
const Minipass = require('minipass') | ||
const npa = require('npm-package-arg') | ||
const npmFetch = require('npm-registry-fetch') | ||
const {PassThrough} = require('stream') | ||
const validate = require('aproba') | ||
const AccessConfig = figgyPudding({ | ||
Promise: {default: () => Promise} | ||
}) | ||
const eu = encodeURIComponent | ||
@@ -22,2 +16,11 @@ const npar = spec => { | ||
} | ||
const mapJSON = (value, [key]) => { | ||
if (value === 'read') { | ||
return [key, 'read-only'] | ||
} else if (value === 'write') { | ||
return [key, 'read-write'] | ||
} else { | ||
return [key, value] | ||
} | ||
} | ||
@@ -28,21 +31,20 @@ const cmd = module.exports = {} | ||
cmd.restricted = (spec, opts) => setAccess(spec, 'restricted', opts) | ||
function setAccess (spec, access, opts) { | ||
opts = AccessConfig(opts) | ||
return pwrap(opts, () => { | ||
function setAccess (spec, access, opts = {}) { | ||
return Promise.resolve().then(() => { | ||
spec = npar(spec) | ||
validate('OSO', [spec, access, opts]) | ||
const uri = `/-/package/${eu(spec.name)}/access` | ||
return npmFetch(uri, opts.concat({ | ||
return npmFetch(uri, { | ||
...opts, | ||
method: 'POST', | ||
body: {access}, | ||
body: { access }, | ||
spec | ||
})) | ||
}).then(res => res.body.resume() && true) | ||
}).then(() => true) | ||
}) | ||
} | ||
cmd.grant = (spec, entity, permissions, opts) => { | ||
opts = AccessConfig(opts) | ||
return pwrap(opts, () => { | ||
cmd.grant = (spec, entity, permissions, opts = {}) => { | ||
return Promise.resolve().then(() => { | ||
spec = npar(spec) | ||
const {scope, team} = splitEntity(entity) | ||
const { scope, team } = splitEntity(entity) | ||
validate('OSSSO', [spec, scope, team, permissions, opts]) | ||
@@ -53,48 +55,49 @@ if (permissions !== 'read-write' && permissions !== 'read-only') { | ||
const uri = `/-/team/${eu(scope)}/${eu(team)}/package` | ||
return npmFetch(uri, opts.concat({ | ||
return npmFetch(uri, { | ||
...opts, | ||
method: 'PUT', | ||
body: {package: spec.name, permissions}, | ||
body: { package: spec.name, permissions }, | ||
scope, | ||
spec, | ||
ignoreBody: true | ||
})) | ||
}).then(() => true) | ||
}) | ||
.then(() => true) | ||
}) | ||
} | ||
cmd.revoke = (spec, entity, opts) => { | ||
opts = AccessConfig(opts) | ||
return pwrap(opts, () => { | ||
cmd.revoke = (spec, entity, opts = {}) => { | ||
return Promise.resolve().then(() => { | ||
spec = npar(spec) | ||
const {scope, team} = splitEntity(entity) | ||
const { scope, team } = splitEntity(entity) | ||
validate('OSSO', [spec, scope, team, opts]) | ||
const uri = `/-/team/${eu(scope)}/${eu(team)}/package` | ||
return npmFetch(uri, opts.concat({ | ||
return npmFetch(uri, { | ||
...opts, | ||
method: 'DELETE', | ||
body: {package: spec.name}, | ||
body: { package: spec.name }, | ||
scope, | ||
spec, | ||
ignoreBody: true | ||
})) | ||
}).then(() => true) | ||
}) | ||
.then(() => true) | ||
}) | ||
} | ||
cmd.lsPackages = (entity, opts) => { | ||
opts = AccessConfig(opts) | ||
return pwrap(opts, () => { | ||
return getStream.array( | ||
cmd.lsPackages.stream(entity, opts) | ||
).then(data => data.reduce((acc, [key, val]) => { | ||
if (!acc) { | ||
acc = {} | ||
} | ||
acc[key] = val | ||
return acc | ||
}, null)) | ||
}) | ||
return cmd.lsPackages.stream(entity, opts) | ||
.collect() | ||
.then(data => { | ||
return data.reduce((acc, [key, val]) => { | ||
if (!acc) { | ||
acc = {} | ||
} | ||
acc[key] = val | ||
return acc | ||
}, null) | ||
}) | ||
} | ||
cmd.lsPackages.stream = (entity, opts) => { | ||
cmd.lsPackages.stream = (entity, opts = {}) => { | ||
validate('SO|SZ', [entity, opts]) | ||
opts = AccessConfig(opts) | ||
const {scope, team} = splitEntity(entity) | ||
const { scope, team } = splitEntity(entity) | ||
let uri | ||
@@ -106,25 +109,20 @@ if (team) { | ||
} | ||
opts = opts.concat({ | ||
query: {format: 'cli'}, | ||
mapJson (value, [key]) { | ||
if (value === 'read') { | ||
return [key, 'read-only'] | ||
} else if (value === 'write') { | ||
return [key, 'read-write'] | ||
const nextOpts = { | ||
...opts, | ||
query: { format: 'cli' }, | ||
mapJSON | ||
} | ||
const ret = new Minipass({ objectMode: true }) | ||
npmFetch.json.stream(uri, '*', nextOpts) | ||
.on('error', err => { | ||
if (err.code === 'E404' && !team) { | ||
uri = `/-/user/${eu(scope)}/package` | ||
npmFetch.json.stream(uri, '*', nextOpts) | ||
.on('error', err => ret.emit('error', err)) | ||
.pipe(ret) | ||
} else { | ||
return [key, value] | ||
ret.emit('error', err) | ||
} | ||
} | ||
}) | ||
const ret = new PassThrough({objectMode: true}) | ||
npmFetch.json.stream(uri, '*', opts).on('error', err => { | ||
if (err.code === 'E404' && !team) { | ||
uri = `/-/user/${eu(scope)}/package` | ||
npmFetch.json.stream(uri, '*', opts).on( | ||
'error', err => ret.emit('error', err) | ||
).pipe(ret) | ||
} else { | ||
ret.emit('error', err) | ||
} | ||
}).pipe(ret) | ||
}) | ||
.pipe(ret) | ||
return ret | ||
@@ -134,17 +132,14 @@ } | ||
cmd.lsCollaborators = (spec, user, opts) => { | ||
if (typeof user === 'object' && !opts) { | ||
opts = user | ||
user = undefined | ||
} | ||
opts = AccessConfig(opts) | ||
return pwrap(opts, () => { | ||
return getStream.array( | ||
cmd.lsCollaborators.stream(spec, user, opts) | ||
).then(data => data.reduce((acc, [key, val]) => { | ||
if (!acc) { | ||
acc = {} | ||
} | ||
acc[key] = val | ||
return acc | ||
}, null)) | ||
return Promise.resolve().then(() => { | ||
return cmd.lsCollaborators.stream(spec, user, opts) | ||
.collect() | ||
.then(data => { | ||
return data.reduce((acc, [key, val]) => { | ||
if (!acc) { | ||
acc = {} | ||
} | ||
acc[key] = val | ||
return acc | ||
}, null) | ||
}) | ||
}) | ||
@@ -157,19 +152,13 @@ } | ||
user = undefined | ||
} else if (!opts) { | ||
opts = {} | ||
} | ||
opts = AccessConfig(opts) | ||
spec = npar(spec) | ||
validate('OSO|OZO', [spec, user, opts]) | ||
const uri = `/-/package/${eu(spec.name)}/collaborators` | ||
return npmFetch.json.stream(uri, '*', opts.concat({ | ||
query: {format: 'cli', user: user || undefined}, | ||
mapJson (value, [key]) { | ||
if (value === 'read') { | ||
return [key, 'read-only'] | ||
} else if (value === 'write') { | ||
return [key, 'read-write'] | ||
} else { | ||
return [key, value] | ||
} | ||
} | ||
})) | ||
return npmFetch.json.stream(uri, '*', { | ||
...opts, | ||
query: { format: 'cli', user: user || undefined }, | ||
mapJSON | ||
}) | ||
} | ||
@@ -179,15 +168,15 @@ | ||
cmd.tfaNotRequired = (spec, opts) => setRequires2fa(spec, false, opts) | ||
function setRequires2fa (spec, required, opts) { | ||
opts = AccessConfig(opts) | ||
return new opts.Promise((resolve, reject) => { | ||
function setRequires2fa (spec, required, opts = {}) { | ||
return Promise.resolve().then(() => { | ||
spec = npar(spec) | ||
validate('OBO', [spec, required, opts]) | ||
const uri = `/-/package/${eu(spec.name)}/access` | ||
return npmFetch(uri, opts.concat({ | ||
return npmFetch(uri, { | ||
...opts, | ||
method: 'POST', | ||
body: {publish_requires_tfa: required}, | ||
body: { publish_requires_tfa: required }, | ||
spec, | ||
ignoreBody: true | ||
})).then(resolve, reject) | ||
}).then(() => true) | ||
}).then(() => true) | ||
}) | ||
} | ||
@@ -200,10 +189,4 @@ | ||
function splitEntity (entity = '') { | ||
let [, scope, team] = entity.match(/^@?([^:]+)(?::(.*))?$/) || [] | ||
return {scope, team} | ||
const [, scope, team] = entity.match(/^@?([^:]+)(?::(.*))?$/) || [] | ||
return { scope, team } | ||
} | ||
function pwrap (opts, fn) { | ||
return new opts.Promise((resolve, reject) => { | ||
fn().then(resolve, reject) | ||
}) | ||
} |
{ | ||
"name": "libnpmaccess", | ||
"version": "3.0.2", | ||
"version": "4.0.0", | ||
"description": "programmatic library for `npm access` commands", | ||
"author": { | ||
"name": "Kat Marchán", | ||
"email": "kzm@zkat.tech", | ||
"twitter": "maybekatz" | ||
}, | ||
"author": "Kat Marchán <kzm@sykosomatic.org>", | ||
"license": "ISC", | ||
"scripts": { | ||
"prerelease": "npm t", | ||
"release": "standard-version -s", | ||
"postrelease": "npm publish && git push --follow-tags", | ||
"pretest": "standard", | ||
"test": "tap -J --100 test/*.js", | ||
"update-coc": "weallbehave -o . && git add CODE_OF_CONDUCT.md && git commit -m 'docs(coc): updated CODE_OF_CONDUCT.md'", | ||
"update-contrib": "weallcontribute -o . && git add CONTRIBUTING.md && git commit -m 'docs(contributing): updated CONTRIBUTING.md'" | ||
"preversion": "npm test", | ||
"postversion": "npm publish", | ||
"postpublish": "git push origin --follow-tags", | ||
"lint": "standard", | ||
"test": "tap" | ||
}, | ||
"devDependencies": { | ||
"nock": "^9.6.1", | ||
"standard": "*", | ||
"standard-version": "*", | ||
"tap": "*", | ||
"weallbehave": "*", | ||
"weallcontribute": "*" | ||
"nock": "^12.0.1", | ||
"standard": "^14.3.0", | ||
"tap": "^14.10.6" | ||
}, | ||
@@ -36,6 +27,12 @@ "repository": { | ||
"aproba": "^2.0.0", | ||
"get-stream": "^4.0.0", | ||
"npm-package-arg": "^6.1.0", | ||
"npm-registry-fetch": "^4.0.0" | ||
"minipass": "^3.1.1", | ||
"npm-package-arg": "^8.0.0", | ||
"npm-registry-fetch": "^8.0.0" | ||
}, | ||
"engines": { | ||
"node": ">=10" | ||
}, | ||
"tap": { | ||
"check-coverage": true | ||
} | ||
} |
@@ -1,3 +0,8 @@ | ||
# libnpmaccess [](https://npm.im/libnpmaccess) [](https://npm.im/libnpmaccess) [](https://travis-ci.org/npm/libnpmaccess) [](https://ci.appveyor.com/project/zkat/libnpmaccess) [](https://coveralls.io/github/npm/libnpmaccess?branch=latest) | ||
# libnpmaccess | ||
[](https://npm.im/libnpmaccess) | ||
[](https://npm.im/libnpmaccess) | ||
[](https://github.com/npm/libnpmaccess/actions?query=workflow%3A%22Node+CI%22) | ||
[](https://coveralls.io/github/npm/libnpmaccess?branch=latest) | ||
[`libnpmaccess`](https://github.com/npm/libnpmaccess) is a Node.js | ||
@@ -40,17 +45,2 @@ library that provides programmatic access to the guts of the npm CLI's `npm | ||
### Contributing | ||
The npm team enthusiastically welcomes contributions and project participation! | ||
There's a bunch of things you can do if you want to contribute! The [Contributor | ||
Guide](CONTRIBUTING.md) has all the information you need for everything from | ||
reporting bugs to contributing entire new features. Please don't hesitate to | ||
jump in if you'd like to, or even ask us questions if something isn't clear. | ||
All participants and maintainers in this project are expected to follow [Code of | ||
Conduct](CODE_OF_CONDUCT.md), and just generally be excellent to each other. | ||
Please refer to the [Changelog](CHANGELOG.md) for project history details, too. | ||
Happy hacking! | ||
### API | ||
@@ -70,5 +60,4 @@ | ||
* `opts.otp` - certain operations will require an OTP token to be passed in. If a `libnpmaccess` command fails with `err.code === EOTP`, please retry the request with `{otp: <2fa token>}` | ||
* `opts.Promise` - If you pass this in, the Promises returned by `libnpmaccess` commands will use this Promise class instead. For example: `{Promise: require('bluebird')}` | ||
#### <a name="public"></a> `> access.public(spec, [opts]) -> Promise` | ||
#### <a name="public"></a> `> access.public(spec, [opts]) -> Promise<Boolean>` | ||
@@ -87,3 +76,3 @@ `spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible | ||
#### <a name="restricted"></a> `> access.restricted(spec, [opts]) -> Promise` | ||
#### <a name="restricted"></a> `> access.restricted(spec, [opts]) -> Promise<Boolean>` | ||
@@ -102,3 +91,3 @@ `spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible | ||
#### <a name="grant"></a> `> access.grant(spec, team, permissions, [opts]) -> Promise` | ||
#### <a name="grant"></a> `> access.grant(spec, team, permissions, [opts]) -> Promise<Boolean>` | ||
@@ -121,3 +110,3 @@ `spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible | ||
#### <a name="revoke"></a> `> access.revoke(spec, team, [opts]) -> Promise` | ||
#### <a name="revoke"></a> `> access.revoke(spec, team, [opts]) -> Promise<Boolean>` | ||
@@ -140,3 +129,3 @@ `spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible | ||
#### <a name="tfa-required"></a> `> access.tfaRequired(spec, [opts]) -> Promise` | ||
#### <a name="tfa-required"></a> `> access.tfaRequired(spec, [opts]) -> Promise<Boolean>` | ||
@@ -156,3 +145,3 @@ `spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible | ||
#### <a name="tfa-not-required"></a> `> access.tfaNotRequired(spec, [opts]) -> Promise` | ||
#### <a name="tfa-not-required"></a> `> access.tfaNotRequired(spec, [opts]) -> Promise<Boolean>` | ||
@@ -173,3 +162,3 @@ `spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible | ||
#### <a name="ls-packages"></a> `> access.lsPackages(entity, [opts]) -> Promise` | ||
#### <a name="ls-packages"></a> `> access.lsPackages(entity, [opts]) -> Promise<Object | null>` | ||
@@ -224,3 +213,3 @@ `entity` must be either a valid org or user name, or a fully-qualified team name | ||
#### <a name="ls-collaborators"></a> `> access.lsCollaborators(spec, [user], [opts]) -> Promise` | ||
#### <a name="ls-collaborators"></a> `> access.lsCollaborators(spec, [user], [opts]) -> Promise<Object | null>` | ||
@@ -227,0 +216,0 @@ `spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible |
'use strict' | ||
const figgyPudding = require('figgy-pudding') | ||
const getStream = require('get-stream') | ||
const {test} = require('tap') | ||
const tnock = require('./util/tnock.js') | ||
const t = require('tap') | ||
const tnock = require('./fixtures/tnock.js') | ||
@@ -11,9 +9,9 @@ const access = require('../index.js') | ||
const REG = 'http://localhost:1337' | ||
const OPTS = figgyPudding({})({ | ||
const OPTS = { | ||
registry: REG | ||
}) | ||
} | ||
test('access public', t => { | ||
t.test('access public', t => { | ||
tnock(t, REG).post( | ||
'/-/package/%40foo%2Fbar/access', {access: 'public'} | ||
'/-/package/%40foo%2Fbar/access', { access: 'public' } | ||
).reply(200) | ||
@@ -25,5 +23,15 @@ return access.public('@foo/bar', OPTS).then(ret => { | ||
test('access restricted', t => { | ||
t.test('access public - failure', t => { | ||
tnock(t, REG).post( | ||
'/-/package/%40foo%2Fbar/access', {access: 'restricted'} | ||
'/-/package/%40foo%2Fbar/access', { access: 'public' } | ||
).reply(418) | ||
return access.public('@foo/bar', OPTS) | ||
.catch(err => { | ||
t.equals(err.statusCode, 418, 'fails with code from registry') | ||
}) | ||
}) | ||
t.test('access restricted', t => { | ||
tnock(t, REG).post( | ||
'/-/package/%40foo%2Fbar/access', { access: 'restricted' } | ||
).reply(200) | ||
@@ -35,6 +43,16 @@ return access.restricted('@foo/bar', OPTS).then(ret => { | ||
test('access 2fa-required', t => { | ||
t.test('access restricted - failure', t => { | ||
tnock(t, REG).post( | ||
'/-/package/%40foo%2Fbar/access', { access: 'restricted' } | ||
).reply(418) | ||
return access.restricted('@foo/bar', OPTS) | ||
.catch(err => { | ||
t.equals(err.statusCode, 418, 'fails with code from registry') | ||
}) | ||
}) | ||
t.test('access 2fa-required', t => { | ||
tnock(t, REG).post('/-/package/%40foo%2Fbar/access', { | ||
publish_requires_tfa: true | ||
}).reply(200, {ok: true}) | ||
}).reply(200, { ok: true }) | ||
return access.tfaRequired('@foo/bar', OPTS).then(ret => { | ||
@@ -45,6 +63,6 @@ t.deepEqual(ret, true, 'request succeeded') | ||
test('access 2fa-not-required', t => { | ||
t.test('access 2fa-not-required', t => { | ||
tnock(t, REG).post('/-/package/%40foo%2Fbar/access', { | ||
publish_requires_tfa: false | ||
}).reply(200, {ok: true}) | ||
}).reply(200, { ok: true }) | ||
return access.tfaNotRequired('@foo/bar', OPTS).then(ret => { | ||
@@ -55,3 +73,3 @@ t.deepEqual(ret, true, 'request succeeded') | ||
test('access grant basic read-write', t => { | ||
t.test('access grant basic read-write', t => { | ||
tnock(t, REG).put('/-/team/myorg/myteam/package', { | ||
@@ -68,3 +86,3 @@ package: '@foo/bar', | ||
test('access grant basic read-only', t => { | ||
t.test('access grant basic read-only', t => { | ||
tnock(t, REG).put('/-/team/myorg/myteam/package', { | ||
@@ -81,3 +99,3 @@ package: '@foo/bar', | ||
test('access grant bad perm', t => { | ||
t.test('access grant bad perm', t => { | ||
return access.grant( | ||
@@ -96,3 +114,3 @@ '@foo/bar', 'myorg:myteam', 'unknown', OPTS | ||
test('access grant no entity', t => { | ||
t.test('access grant no entity', t => { | ||
return access.grant( | ||
@@ -111,3 +129,3 @@ '@foo/bar', undefined, 'read-write', OPTS | ||
test('access grant basic unscoped', t => { | ||
t.test('access grant basic unscoped', t => { | ||
tnock(t, REG).put('/-/team/myorg/myteam/package', { | ||
@@ -124,3 +142,18 @@ package: 'bar', | ||
test('access revoke basic', t => { | ||
t.test('access grant no opts passed', t => { | ||
// NOTE: mocking real url, because no opts variable means `registry` value | ||
// will be defauled to real registry url | ||
tnock(t, 'https://registry.npmjs.org') | ||
.put('/-/team/myorg/myteam/package', { | ||
package: 'bar', | ||
permissions: 'read-write' | ||
}) | ||
.reply(201) | ||
return access.grant('bar', 'myorg:myteam', 'read-write') | ||
.then(ret => { | ||
t.equals(ret, true, 'request succeeded') | ||
}) | ||
}) | ||
t.test('access revoke basic', t => { | ||
tnock(t, REG).delete('/-/team/myorg/myteam/package', { | ||
@@ -134,6 +167,6 @@ package: '@foo/bar' | ||
test('access revoke basic unscoped', t => { | ||
t.test('access revoke basic unscoped', t => { | ||
tnock(t, REG).delete('/-/team/myorg/myteam/package', { | ||
package: 'bar' | ||
}).reply(200, {accessChanged: true}) | ||
}).reply(200, { accessChanged: true }) | ||
return access.revoke('bar', 'myorg:myteam', OPTS).then(ret => { | ||
@@ -144,3 +177,17 @@ t.deepEqual(ret, true, 'request succeeded') | ||
test('ls-packages on team', t => { | ||
t.test('access revoke no opts passed', t => { | ||
// NOTE: mocking real url, because no opts variable means `registry` value | ||
// will be defauled to real registry url | ||
tnock(t, 'https://registry.npmjs.org') | ||
.delete('/-/team/myorg/myteam/package', { | ||
package: 'bar' | ||
}) | ||
.reply(201) | ||
return access.revoke('bar', 'myorg:myteam') | ||
.then(ret => { | ||
t.equals(ret, true, 'request succeeded') | ||
}) | ||
}) | ||
t.test('ls-packages on team', t => { | ||
const serverPackages = { | ||
@@ -164,3 +211,3 @@ '@foo/bar': 'write', | ||
test('ls-packages on org', t => { | ||
t.test('ls-packages on org', t => { | ||
const serverPackages = { | ||
@@ -184,3 +231,3 @@ '@foo/bar': 'write', | ||
test('ls-packages on user', t => { | ||
t.test('ls-packages on user', t => { | ||
const serverPackages = { | ||
@@ -197,3 +244,3 @@ '@foo/bar': 'write', | ||
const srv = tnock(t, REG) | ||
srv.get('/-/org/myuser/package?format=cli').reply(404, {error: 'not found'}) | ||
srv.get('/-/org/myuser/package?format=cli').reply(404, { error: 'not found' }) | ||
srv.get('/-/user/myuser/package?format=cli').reply(200, serverPackages) | ||
@@ -205,3 +252,3 @@ return access.lsPackages('myuser', OPTS).then(data => { | ||
test('ls-packages error on team', t => { | ||
t.test('ls-packages error on team', t => { | ||
tnock(t, REG).get('/-/team/myorg/myteam/package?format=cli').reply(404) | ||
@@ -214,6 +261,6 @@ return access.lsPackages('myorg:myteam', OPTS).then( | ||
test('ls-packages error on user', t => { | ||
t.test('ls-packages error on user', t => { | ||
const srv = tnock(t, REG) | ||
srv.get('/-/org/myuser/package?format=cli').reply(404, {error: 'not found'}) | ||
srv.get('/-/user/myuser/package?format=cli').reply(404, {error: 'not found'}) | ||
srv.get('/-/org/myuser/package?format=cli').reply(404, { error: 'not found' }) | ||
srv.get('/-/user/myuser/package?format=cli').reply(404, { error: 'not found' }) | ||
return access.lsPackages('myuser', OPTS).then( | ||
@@ -225,3 +272,3 @@ () => { throw new Error('should not have succeeded') }, | ||
test('ls-packages bad response', t => { | ||
t.test('ls-packages bad response', t => { | ||
tnock(t, REG).get( | ||
@@ -235,3 +282,3 @@ '/-/team/myorg/myteam/package?format=cli' | ||
test('ls-packages stream', t => { | ||
t.test('ls-packages stream', t => { | ||
const serverPackages = { | ||
@@ -250,10 +297,33 @@ '@foo/bar': 'write', | ||
).reply(200, serverPackages) | ||
return getStream.array( | ||
access.lsPackages.stream('myorg:myteam', OPTS) | ||
).then(data => { | ||
t.deepEqual(data, clientPackages, 'got streamed client package info') | ||
}) | ||
return access.lsPackages.stream('myorg:myteam', OPTS) | ||
.collect() | ||
.then(data => { | ||
t.deepEqual(data, clientPackages, 'got streamed client package info') | ||
}) | ||
}) | ||
test('ls-collaborators', t => { | ||
t.test('ls-packages stream no opts', t => { | ||
const serverPackages = { | ||
'@foo/bar': 'write', | ||
'@foo/util': 'read', | ||
'@foo/other': 'shrödinger' | ||
} | ||
const clientPackages = [ | ||
['@foo/bar', 'read-write'], | ||
['@foo/util', 'read-only'], | ||
['@foo/other', 'shrödinger'] | ||
] | ||
// NOTE: mocking real url, because no opts variable means `registry` value | ||
// will be defauled to real registry url | ||
tnock(t, 'https://registry.npmjs.org') | ||
.get('/-/team/myorg/myteam/package?format=cli') | ||
.reply(200, serverPackages) | ||
return access.lsPackages.stream('myorg:myteam') | ||
.collect() | ||
.then(data => { | ||
t.deepEqual(data, clientPackages, 'got streamed client package info') | ||
}) | ||
}) | ||
t.test('ls-collaborators', t => { | ||
const serverCollaborators = { | ||
@@ -277,3 +347,3 @@ 'myorg:myteam': 'write', | ||
test('ls-collaborators stream', t => { | ||
t.test('ls-collaborators stream', t => { | ||
const serverCollaborators = { | ||
@@ -292,10 +362,10 @@ 'myorg:myteam': 'write', | ||
).reply(200, serverCollaborators) | ||
return getStream.array( | ||
access.lsCollaborators.stream('@foo/bar', OPTS) | ||
).then(data => { | ||
t.deepEqual(data, clientCollaborators, 'got collaborators') | ||
}) | ||
return access.lsCollaborators.stream('@foo/bar', OPTS) | ||
.collect() | ||
.then(data => { | ||
t.deepEqual(data, clientCollaborators, 'got collaborators') | ||
}) | ||
}) | ||
test('ls-collaborators w/scope', t => { | ||
t.test('ls-collaborators w/scope', t => { | ||
const serverCollaborators = { | ||
@@ -319,3 +389,3 @@ 'myorg:myteam': 'write', | ||
test('ls-collaborators w/o scope', t => { | ||
t.test('ls-collaborators w/o scope', t => { | ||
const serverCollaborators = { | ||
@@ -339,3 +409,3 @@ 'myorg:myteam': 'write', | ||
test('ls-collaborators bad response', t => { | ||
t.test('ls-collaborators bad response', t => { | ||
tnock(t, REG).get( | ||
@@ -349,3 +419,3 @@ '/-/package/%40foo%2Fbar/collaborators?format=cli' | ||
test('error on non-registry specs', t => { | ||
t.test('error on non-registry specs', t => { | ||
const resolve = () => { throw new Error('should not succeed') } | ||
@@ -356,3 +426,3 @@ const reject = err => t.match( | ||
return Promise.all([ | ||
access.public('foo/bar').then(resolve, reject), | ||
access.public('githubusername/reponame').then(resolve, reject), | ||
access.restricted('foo/bar').then(resolve, reject), | ||
@@ -367,3 +437,3 @@ access.grant('foo/bar', 'myorg', 'myteam', 'read-only').then(resolve, reject), | ||
test('edit', t => { | ||
t.test('edit', t => { | ||
t.equal(typeof access.edit, 'function', 'access.edit exists') | ||
@@ -370,0 +440,0 @@ t.throws(() => { |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
3
-50%565
9.71%37546
-40.45%9
-25%248
-4.25%1
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated