diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index 1ddd85b3d411d..105761dcb15bd 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -323,8 +323,6 @@ graph LR;
init-package-json-->validate-npm-package-name;
is-cidr-->cidr-regex;
is-core-module-->has;
- libnpmaccess-->aproba;
- libnpmaccess-->minipass;
libnpmaccess-->nock;
libnpmaccess-->npm-package-arg;
libnpmaccess-->npm-registry-fetch;
diff --git a/package-lock.json b/package-lock.json
index bf31065aeb9a5..e0a2304732744 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13917,8 +13917,6 @@
"version": "7.0.0-pre.0",
"license": "ISC",
"dependencies": {
- "aproba": "^2.0.0",
- "minipass": "^3.1.1",
"npm-package-arg": "^9.0.1",
"npm-registry-fetch": "^13.0.0"
},
diff --git a/workspaces/libnpmaccess/README.md b/workspaces/libnpmaccess/README.md
index 3e35562cfddbc..060016bc2a0b6 100644
--- a/workspaces/libnpmaccess/README.md
+++ b/workspaces/libnpmaccess/README.md
@@ -6,241 +6,88 @@
[`libnpmaccess`](https://github.com/npm/libnpmaccess) is a Node.js
library that provides programmatic access to the guts of the npm CLI's `npm
-access` command and its various subcommands. This includes managing account 2FA,
-listing packages and permissions, looking at package collaborators, and defining
+access` command. This includes managing account mfa settings, listing
+packages and permissions, looking at package collaborators, and defining
package permissions for users, orgs, and teams.
## Example
```javascript
const access = require('libnpmaccess')
+const opts = { '//registry.npmjs.org/:_authToken: 'npm_token }
// List all packages @zkat has access to on the npm registry.
-console.log(Object.keys(await access.lsPackages('zkat')))
+console.log(Object.keys(await access.getPackages('zkat', opts)))
```
-## Table of Contents
-
-* [Installing](#install)
-* [Example](#example)
-* [Contributing](#contributing)
-* [API](#api)
- * [access opts](#opts)
- * [`public()`](#public)
- * [`restricted()`](#restricted)
- * [`grant()`](#grant)
- * [`revoke()`](#revoke)
- * [`tfaRequired()`](#tfa-required)
- * [`tfaNotRequired()`](#tfa-not-required)
- * [`lsPackages()`](#ls-packages)
- * [`lsPackages.stream()`](#ls-packages-stream)
- * [`lsCollaborators()`](#ls-collaborators)
- * [`lsCollaborators.stream()`](#ls-collaborators-stream)
-
-### Install
-
-`$ npm install libnpmaccess`
-
### API
-#### `opts` for `libnpmaccess` commands
+#### `opts` for all `libnpmaccess` commands
`libnpmaccess` uses [`npm-registry-fetch`](https://npm.im/npm-registry-fetch).
-All options are passed through directly to that library, so please refer to [its
-own `opts`
+
+All options are passed through directly to that library, so please refer
+to [its own `opts`
documentation](https://www.npmjs.com/package/npm-registry-fetch#fetch-options)
for options that can be passed in.
-A couple of options of note for those in a hurry:
-
-* `opts.token` - can be passed in and will be used as the authentication token for the registry. For other ways to pass in auth details, see the n-r-f docs.
-* `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>}`
-
-#### `> access.public(spec, [opts]) -> Promise`
+#### `spec` parameter for all `libnpmaccess` commands
`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
registry spec.
-Makes package described by `spec` public.
-
-##### Example
-
-```javascript
-await access.public('@foo/bar', {token: 'myregistrytoken'})
-// `@foo/bar` is now public
-```
-
-#### `> access.restricted(spec, [opts]) -> Promise`
-
-`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
-registry spec.
-
-Makes package described by `spec` private/restricted.
-
-##### Example
+#### `access.getCollaborators(spec, opts) -> Promise`
-```javascript
-await access.restricted('@foo/bar', {token: 'myregistrytoken'})
-// `@foo/bar` is now private
-```
+Gets collaborators for a given package
-#### `> access.grant(spec, team, permissions, [opts]) -> Promise`
+#### `access.getPackages(user|scope|team, opts) -> Promise`
-`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
-registry spec. `team` must be a fully-qualified team name, in the `scope:team`
-format, with or without the `@` prefix, and the team must be a valid team within
-that scope. `permissions` must be one of `'read-only'` or `'read-write'`.
+Gets all packages for a given user, scope, or team.
-Grants `read-only` or `read-write` permissions for a certain package to a team.
+Teams should be in the format `scope:team` or `@scope:team`
-##### Example
+Users and scopes can be in the format `@scope` or `scope`
-```javascript
-await access.grant('@foo/bar', '@foo:myteam', 'read-write', {
- token: 'myregistrytoken'
-})
-// `@foo/bar` is now read/write enabled for the @foo:myteam team.
-```
+#### `access.getVisibility(spec, opts) -> Promise`
-#### `> access.revoke(spec, team, [opts]) -> Promise`
+Gets the visibility of a given package
-`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
-registry spec. `team` must be a fully-qualified team name, in the `scope:team`
-format, with or without the `@` prefix, and the team must be a valid team within
-that scope. `permissions` must be one of `'read-only'` or `'read-write'`.
+#### `access.removePermissions(team, spec, opts) -> Promise`
-Removes access to a package from a certain team.
+Removes the access for a given team to a package.
-##### Example
+Teams should be in the format `scope:team` or `@scope:team`
-```javascript
-await access.revoke('@foo/bar', '@foo:myteam', {
- token: 'myregistrytoken'
-})
-// @foo:myteam can no longer access `@foo/bar`
-```
+#### `access.setAccess(package, access, opts) -> Promise`
-#### `> access.tfaRequired(spec, [opts]) -> Promise`
+Sets access level for package described by `spec`.
-`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
-registry spec.
-
-Makes it so publishing or managing a package requires using 2FA tokens to
-complete operations.
+The npm registry accepts the following `access` levels:
-##### Example
+`public`: package is public
+`private`: package is private
-```javascript
-await access.tfaRequires('lodash', {token: 'myregistrytoken'})
-// Publishing or changing dist-tags on `lodash` now require OTP to be enabled.
-```
+The npm registry also only allows scoped packages to have their access
+level set.
-#### `> access.tfaNotRequired(spec, [opts]) -> Promise`
+#### access.setMfa(spec, level, opts) -> Promise`
-`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
-registry spec.
+Sets the publishing mfa requirements for a given package. Level must be one of the
+following
-Disabled the package-level 2FA requirement for `spec`. Note that you will need
-to pass in an `otp` token in `opts` in order to complete this operation.
+`none`: mfa is not required to publish this package.
+`publish`: mfa is required to publish this package, automation tokens
+cannot be used to publish.
+`automation`: mfa is required to publish this package, automation tokens
+may also be used for publishing from continuous integration workflows.
-##### Example
+#### access.setPermissions(team, spec, permssions, opts) -> Promise`
-```javascript
-await access.tfaNotRequired('lodash', {otp: '123654', token: 'myregistrytoken'})
-// Publishing or editing dist-tags on `lodash` no longer requires OTP to be
-// enabled.
-```
+Sets permissions levels for a given team to a package.
-#### `> access.lsPackages(entity, [opts]) -> Promise`
+Teams should be in the format `scope:team` or `@scope:team`
-`entity` must be either a valid org or user name, or a fully-qualified team name
-in the `scope:team` format, with or without the `@` prefix.
+The npm registry accepts the following `permissions`:
-Lists out packages a user, org, or team has access to, with corresponding
-permissions. Packages that the access token does not have access to won't be
-listed.
-
-In order to disambiguate between users and orgs, two requests may end up being
-made when listing orgs or users.
-
-For a streamed version of these results, see
-[`access.lsPackages.stream()`](#ls-package-stream).
-
-##### Example
-
-```javascript
-await access.lsPackages('zkat', {
- token: 'myregistrytoken'
-})
-// Lists all packages `@zkat` has access to on the registry, and the
-// corresponding permissions.
-```
-
-#### `> access.lsPackages.stream(scope, [team], [opts]) -> Stream`
-
-`entity` must be either a valid org or user name, or a fully-qualified team name
-in the `scope:team` format, with or without the `@` prefix.
-
-Streams out packages a user, org, or team has access to, with corresponding
-permissions, with each stream entry being formatted like `[packageName,
-permissions]`. Packages that the access token does not have access to won't be
-listed.
-
-In order to disambiguate between users and orgs, two requests may end up being
-made when listing orgs or users.
-
-The returned stream is a valid `asyncIterator`.
-
-##### Example
-
-```javascript
-for await (let [pkg, perm] of access.lsPackages.stream('zkat')) {
- console.log('zkat has', perm, 'access to', pkg)
-}
-// zkat has read-write access to eggplant
-// zkat has read-only access to @npmcorp/secret
-```
-
-#### `> access.lsCollaborators(spec, [user], [opts]) -> Promise`
-
-`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
-registry spec. `user` must be a valid user name, with or without the `@`
-prefix.
-
-Lists out access privileges for a certain package. Will only show permissions
-for packages to which you have at least read access. If `user` is passed in, the
-list is filtered only to teams _that_ user happens to belong to.
-
-For a streamed version of these results, see [`access.lsCollaborators.stream()`](#ls-collaborators-stream).
-
-##### Example
-
-```javascript
-await access.lsCollaborators('@npm/foo', 'zkat', {
- token: 'myregistrytoken'
-})
-// Lists all teams with access to @npm/foo that @zkat belongs to.
-```
-
-#### `> access.lsCollaborators.stream(spec, [user], [opts]) -> Stream`
-
-`spec` must be an [`npm-package-arg`](https://npm.im/npm-package-arg)-compatible
-registry spec. `user` must be a valid user name, with or without the `@`
-prefix.
-
-Stream out access privileges for a certain package, with each entry in `[user,
-permissions]` format. Will only show permissions for packages to which you have
-at least read access. If `user` is passed in, the list is filtered only to teams
-_that_ user happens to belong to.
-
-The returned stream is a valid `asyncIterator`.
-
-##### Example
-
-```javascript
-for await (let [usr, perm] of access.lsCollaborators.stream('npm')) {
- console.log(usr, 'has', perm, 'access to npm')
-}
-// zkat has read-write access to npm
-// iarna has read-write access to npm
-```
+`read-only`: Read only permissions
+`read-write`: Read and write (aka publish) permissions
diff --git a/workspaces/libnpmaccess/lib/index.js b/workspaces/libnpmaccess/lib/index.js
index 71219d0098cfe..fca0e47279bfb 100644
--- a/workspaces/libnpmaccess/lib/index.js
+++ b/workspaces/libnpmaccess/lib/index.js
@@ -1,186 +1,140 @@
'use strict'
-const Minipass = require('minipass')
const npa = require('npm-package-arg')
const npmFetch = require('npm-registry-fetch')
-const validate = require('aproba')
-const eu = encodeURIComponent
-const npar = spec => {
+const npar = (spec) => {
spec = npa(spec)
if (!spec.registry) {
- throw new Error('`spec` must be a registry spec')
+ throw new Error('must use package name only')
}
return spec
}
-const mapJSON = (value, [key]) => {
- if (value === 'read') {
- return [key, 'read-only']
- } else if (value === 'write') {
- return [key, 'read-write']
- } else {
- return [key, value]
+
+const parseTeam = (scopeTeam) => {
+ let slice = 0
+ if (scopeTeam.startsWith('@')) {
+ slice = 1
}
+ const [scope, team] = scopeTeam.slice(slice).split(':').map(encodeURIComponent)
+ return { scope, team }
}
-const cmd = module.exports = {}
+const getPackages = async (scopeTeam, opts) => {
+ const { scope, team } = parseTeam(scopeTeam)
-cmd.public = (spec, opts) => setAccess(spec, 'public', opts)
-cmd.restricted = (spec, opts) => setAccess(spec, 'restricted', 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,
- method: 'POST',
- body: { access },
- spec,
- }).then(() => true)
- })
-}
-
-cmd.grant = (spec, entity, permissions, opts = {}) => {
- return Promise.resolve().then(() => {
- spec = npar(spec)
- const { scope, team } = splitEntity(entity)
- validate('OSSSO', [spec, scope, team, permissions, opts])
- if (permissions !== 'read-write' && permissions !== 'read-only') {
- throw new Error(
- '`permissions` must be `read-write` or `read-only`. Got `'
- + permissions + '` instead')
+ let uri
+ if (team) {
+ uri = `/-/team/${scope}/${team}/package`
+ } else {
+ uri = `/-/org/${scope}/package`
+ }
+ try {
+ return await npmFetch.json(uri, opts)
+ } catch (err) {
+ if (err.code === 'E404') {
+ uri = `/-/user/${scope}/package`
+ return npmFetch.json(uri, opts)
}
- const uri = `/-/team/${eu(scope)}/${eu(team)}/package`
- return npmFetch(uri, {
- ...opts,
- method: 'PUT',
- body: { package: spec.name, permissions },
- scope,
- spec,
- ignoreBody: true,
- })
- .then(() => true)
- })
+ throw err
+ }
}
-cmd.revoke = (spec, entity, opts = {}) => {
- return Promise.resolve().then(() => {
- spec = npar(spec)
- const { scope, team } = splitEntity(entity)
- validate('OSSO', [spec, scope, team, opts])
- const uri = `/-/team/${eu(scope)}/${eu(team)}/package`
- return npmFetch(uri, {
- ...opts,
- method: 'DELETE',
- body: { package: spec.name },
- scope,
- spec,
- ignoreBody: true,
- })
- .then(() => true)
- })
+const getCollaborators = async (pkg, opts) => {
+ const spec = npar(pkg)
+ const uri = `/-/package/${spec.escapedName}/collaborators`
+ return npmFetch.json(uri, opts)
}
-cmd.lsPackages = (entity, opts) => {
- return cmd.lsPackages.stream(entity, opts)
- .collect()
- .then(data => {
- return data.reduce((acc, [key, val]) => {
- if (!acc) {
- acc = {}
- }
- acc[key] = val
- return acc
- }, null)
- })
+const getVisibility = async (pkg, opts) => {
+ const spec = npar(pkg)
+ const uri = `/-/package/${spec.escapedName}/visibility`
+ return npmFetch.json(uri, opts)
}
-cmd.lsPackages.stream = (entity, opts = {}) => {
- validate('SO|SZ', [entity, opts])
- const { scope, team } = splitEntity(entity)
- let uri
- if (team) {
- uri = `/-/team/${eu(scope)}/${eu(team)}/package`
- } else {
- uri = `/-/org/${eu(scope)}/package`
- }
- const nextOpts = {
+const setAccess = async (pkg, access, opts) => {
+ const spec = npar(pkg)
+ const uri = `/-/package/${spec.escapedName}/access`
+ await npmFetch(uri, {
...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', streamErr => ret.emit('error', streamErr))
- .pipe(ret)
- } else {
- ret.emit('error', err)
- }
- })
- .pipe(ret)
- return ret
-}
-
-cmd.lsCollaborators = (spec, user, opts) => {
- 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)
- })
+ method: 'POST',
+ body: { access },
+ spec,
+ ignoreBody: true,
})
+ return true
}
-cmd.lsCollaborators.stream = (spec, user, opts) => {
- if (typeof user === 'object' && !opts) {
- opts = user
- user = undefined
- } else if (!opts) {
- opts = {}
+const setMfa = async (pkg, level, opts) => {
+ const spec = npar(pkg)
+ const body = {}
+ switch (level) {
+ case 'none':
+ body.publish_requires_tfa = false
+ break
+ case 'publish':
+ // tfa is required, automation tokens can not override tfa
+ body.publish_requires_tfa = true
+ body.automation_token_overrides_tfa = false
+ break
+ case 'automation':
+ // tfa is required, automation tokens can override tfa
+ body.publish_requires_tfa = true
+ body.automation_token_overrides_tfa = true
+ break
+ default:
+ throw new Error(`Invalid mfa setting ${level}`)
}
- spec = npar(spec)
- validate('OSO|OZO', [spec, user, opts])
- const uri = `/-/package/${eu(spec.name)}/collaborators`
- return npmFetch.json.stream(uri, '*', {
+ const uri = `/-/package/${spec.escapedName}/access`
+ await npmFetch(uri, {
...opts,
- query: { format: 'cli', user: user || undefined },
- mapJSON,
+ method: 'POST',
+ body,
+ spec,
+ ignoreBody: true,
})
+ return true
}
-cmd.tfaRequired = (spec, opts) => setRequires2fa(spec, true, opts)
-cmd.tfaNotRequired = (spec, opts) => setRequires2fa(spec, false, opts)
-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,
- method: 'POST',
- body: { publish_requires_tfa: required },
- spec,
- ignoreBody: true,
- }).then(() => true)
+const setPermissions = async (scopeTeam, pkg, permissions, opts) => {
+ const spec = npar(pkg)
+ const { scope, team } = parseTeam(scopeTeam)
+ if (!scope || !team) {
+ throw new Error('team must be in format `scope:team`')
+ }
+ const uri = `/-/team/${scope}/${team}/package`
+ await npmFetch(uri, {
+ ...opts,
+ method: 'PUT',
+ body: { package: spec.name, permissions },
+ scope,
+ spec,
+ ignoreBody: true,
})
+ return true
}
-cmd.edit = () => {
- throw new Error('Not implemented yet')
+const removePermissions = async (scopeTeam, pkg, opts) => {
+ const spec = npar(pkg)
+ const { scope, team } = parseTeam(scopeTeam)
+ const uri = `/-/team/${scope}/${team}/package`
+ await npmFetch(uri, {
+ ...opts,
+ method: 'DELETE',
+ body: { package: spec.name },
+ scope,
+ spec,
+ ignoreBody: true,
+ })
+ return true
}
-function splitEntity (entity = '') {
- const [, scope, team] = entity.match(/^@?([^:]+)(?::(.*))?$/) || []
- return { scope, team }
+module.exports = {
+ getCollaborators,
+ getPackages,
+ getVisibility,
+ removePermissions,
+ setAccess,
+ setMfa,
+ setPermissions,
}
diff --git a/workspaces/libnpmaccess/package.json b/workspaces/libnpmaccess/package.json
index d05156710256b..8e73fd2676dd9 100644
--- a/workspaces/libnpmaccess/package.json
+++ b/workspaces/libnpmaccess/package.json
@@ -29,8 +29,6 @@
"bugs": "https://github.com/npm/libnpmaccess/issues",
"homepage": "https://npmjs.com/package/libnpmaccess",
"dependencies": {
- "aproba": "^2.0.0",
- "minipass": "^3.1.1",
"npm-package-arg": "^9.0.1",
"npm-registry-fetch": "^13.0.0"
},
diff --git a/workspaces/libnpmaccess/test/index.js b/workspaces/libnpmaccess/test/index.js
index 689788d5269f7..bf73c3c8c929f 100644
--- a/workspaces/libnpmaccess/test/index.js
+++ b/workspaces/libnpmaccess/test/index.js
@@ -10,353 +10,150 @@ const OPTS = {
registry: REG,
}
-t.test('access public', async t => {
- tnock(t, REG).post(
- '/-/package/%40foo%2Fbar/access', { access: 'public' }
- ).reply(200)
- await t.resolves(access.public('@foo/bar', OPTS))
-})
-
-t.test('access public - failure', async t => {
- tnock(t, REG).post(
- '/-/package/%40foo%2Fbar/access', { access: 'public' }
- ).reply(418)
- await t.rejects(
- access.public('@foo/bar', OPTS),
- { statusCode: 418 },
- 'fails with code from registry'
- )
-})
-
-t.test('access restricted', async t => {
- tnock(t, REG).post(
- '/-/package/%40foo%2Fbar/access', { access: 'restricted' }
- ).reply(200)
- await t.resolves(access.restricted('@foo/bar', OPTS))
-})
-
-t.test('access restricted - failure', async t => {
- tnock(t, REG).post(
- '/-/package/%40foo%2Fbar/access', { access: 'restricted' }
- ).reply(418)
- await t.rejects(
- access.restricted('@foo/bar', OPTS),
- { statusCode: 418 },
- 'fails with code from registry')
-})
-
-t.test('access 2fa-required', async t => {
- tnock(t, REG).post('/-/package/%40foo%2Fbar/access', {
- publish_requires_tfa: true,
- }).reply(200, { ok: true })
- await t.resolves(access.tfaRequired('@foo/bar', OPTS))
-})
-
-t.test('access 2fa-not-required', async t => {
- tnock(t, REG).post('/-/package/%40foo%2Fbar/access', {
- publish_requires_tfa: false,
- }).reply(200, { ok: true })
- await t.resolves(access.tfaNotRequired('@foo/bar', OPTS))
-})
-
-t.test('access grant basic read-write', async t => {
- tnock(t, REG).put('/-/team/myorg/myteam/package', {
- package: '@foo/bar',
- permissions: 'read-write',
- }).reply(201)
- await t.resolves(access.grant('@foo/bar', 'myorg:myteam', 'read-write', OPTS))
-})
-
-t.test('access grant basic read-only', async t => {
- tnock(t, REG).put('/-/team/myorg/myteam/package', {
- package: '@foo/bar',
- permissions: 'read-only',
- }).reply(201)
- await t.resolves(access.grant('@foo/bar', 'myorg:myteam', 'read-only', OPTS))
-})
-
-t.test('access grant bad perm', async t => {
- await t.rejects(
- access.grant('@foo/bar', 'myorg:myteam', 'unknown', OPTS),
- { message: /must be.*read-write.*read-only/ },
- 'only read-write and read-only are accepted'
- )
-})
-
-t.test('access grant no entity', async t => {
- await t.rejects(
- access.grant('@foo/bar', undefined, 'read-write', OPTS),
- { message: /Expected string/ },
- 'passing undefined entity gives useful error'
- )
-})
-
-t.test('access grant basic unscoped', async t => {
- tnock(t, REG).put('/-/team/myorg/myteam/package', {
- package: 'bar',
- permissions: 'read-write',
- }).reply(201)
- await t.resolves(access.grant('bar', 'myorg:myteam', 'read-write', OPTS))
-})
-
-t.test('access grant no opts passed', async 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)
- await t.resolves(access.grant('bar', 'myorg:myteam', 'read-write'))
-})
-
-t.test('access revoke basic', async t => {
- tnock(t, REG).delete('/-/team/myorg/myteam/package', {
- package: '@foo/bar',
- }).reply(200)
- await t.resolves(access.revoke('@foo/bar', 'myorg:myteam', OPTS))
-})
-
-t.test('access revoke basic unscoped', async t => {
- tnock(t, REG).delete('/-/team/myorg/myteam/package', {
- package: 'bar',
- }).reply(200, { accessChanged: true })
- await t.resolves(access.revoke('bar', 'myorg:myteam', OPTS))
-})
-
-t.test('access revoke no opts passed', async 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)
- await t.resolves(access.revoke('bar', 'myorg:myteam'))
-})
-
-t.test('ls-packages on team', async 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',
- }
- tnock(t, REG).get(
- '/-/team/myorg/myteam/package?format=cli'
- ).reply(200, serverPackages)
- const data = await access.lsPackages('myorg:myteam', OPTS)
- t.same(data, clientPackages, 'got client package info')
-})
-
-t.test('ls-packages on org', async 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',
- }
- tnock(t, REG).get(
- '/-/org/myorg/package?format=cli'
- ).reply(200, serverPackages)
- const data = await access.lsPackages('myorg', OPTS)
- t.same(data, clientPackages, 'got client package info')
-})
-
-t.test('ls-packages on user', async 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',
- }
- 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(200, serverPackages)
- const data = await access.lsPackages('myuser', OPTS)
- t.same(data, clientPackages, 'got client package info')
-})
-
-t.test('ls-packages error on team', async t => {
- tnock(t, REG).get('/-/team/myorg/myteam/package?format=cli').reply(404)
- await t.rejects(
- access.lsPackages('myorg:myteam', OPTS),
- { code: 'E404' },
- 'spit out 404 directly if team provided'
- )
-})
-
-t.test('ls-packages error on user', async 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' })
- await t.rejects(
- access.lsPackages('myuser', OPTS),
- { code: 'E404' },
- 'spit out 404 if both reqs fail'
- )
-})
-
-t.test('ls-packages bad response', async t => {
- tnock(t, REG).get(
- '/-/team/myorg/myteam/package?format=cli'
- ).reply(200, JSON.stringify(null))
- const data = await access.lsPackages('myorg:myteam', OPTS)
- t.same(data, null, 'succeeds with null')
-})
-
-t.test('ls-packages stream', async 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'],
- ]
- tnock(t, REG).get(
- '/-/team/myorg/myteam/package?format=cli'
- ).reply(200, serverPackages)
- const data = await access.lsPackages.stream('myorg:myteam', OPTS).collect()
- t.same(data, clientPackages, 'got streamed client package info')
-})
-
-t.test('ls-packages stream no opts', async 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)
- const data = await access.lsPackages.stream('myorg:myteam').collect()
- t.same(data, clientPackages, 'got streamed client package info')
+t.test('getCollaborators', t => {
+ t.test('success', async t => {
+ const collaborators = {
+ 'npm:myteam': 'write',
+ 'npm:anotherteam': 'read',
+ 'npm:thirdteam': 'special-case',
+ }
+ tnock(t, REG).get('/-/package/@npmcli%2ftest-package/collaborators').reply(200, collaborators)
+ const data = await access.getCollaborators('@npmcli/test-package', OPTS)
+ t.same(data, collaborators)
+ })
+ t.test('non registry package', async t => {
+ await t.rejects(access.getCollaborators('./local', OPTS), /package name only/)
+ })
+ t.end()
})
-t.test('ls-collaborators', async t => {
- const serverCollaborators = {
- 'myorg:myteam': 'write',
- 'myorg:anotherteam': 'read',
- 'myorg:thirdteam': 'special-case',
+t.test('getPackages', t => {
+ const packages = {
+ '@npmcli/test-package': 'write',
+ '@npmcli/util': 'read',
+ '@npmcli/other': 'shrödinger',
}
- const clientCollaborators = {
- 'myorg:myteam': 'read-write',
- 'myorg:anotherteam': 'read-only',
- 'myorg:thirdteam': 'special-case',
- }
- tnock(t, REG).get(
- '/-/package/%40foo%2Fbar/collaborators?format=cli'
- ).reply(200, serverCollaborators)
- const data = await access.lsCollaborators('@foo/bar', OPTS)
- t.same(data, clientCollaborators, 'got collaborators')
+ t.test('team', async t => {
+ tnock(t, REG).get('/-/team/npm/myteam/package').reply(200, packages)
+ const data = await access.getPackages('npm:myteam', OPTS)
+ t.same(data, packages)
+ })
+ t.test('org', async t => {
+ tnock(t, REG).get('/-/org/npm/package').reply(200, packages)
+ const data = await access.getPackages('npm', OPTS)
+ t.same(data, packages)
+ })
+ t.test('user', async t => {
+ tnock(t, REG).get('/-/org/testuser/package').reply(404, {})
+ tnock(t, REG).get('/-/user/testuser/package').reply(200, packages)
+ const data = await access.getPackages('testuser', OPTS)
+ t.same(data, packages)
+ })
+ t.test('registry error', async t => {
+ tnock(t, REG).get('/-/org/npm/package').reply(500, {})
+ await t.rejects(access.getPackages('npm', OPTS), { code: 'E500' })
+ })
+ t.end()
})
-t.test('ls-collaborators stream', async t => {
- const serverCollaborators = {
- 'myorg:myteam': 'write',
- 'myorg:anotherteam': 'read',
- 'myorg:thirdteam': 'special-case',
- }
- const clientCollaborators = [
- ['myorg:myteam', 'read-write'],
- ['myorg:anotherteam', 'read-only'],
- ['myorg:thirdteam', 'special-case'],
- ]
- tnock(t, REG).get(
- '/-/package/%40foo%2Fbar/collaborators?format=cli'
- ).reply(200, serverCollaborators)
- const data = await access.lsCollaborators.stream('@foo/bar', OPTS).collect()
- t.same(data, clientCollaborators, 'got collaborators')
+t.test('getVisibility', t => {
+ t.test('success', async t => {
+ const visibility = { public: true }
+ tnock(t, REG).get('/-/package/@npmcli%2ftest-package/visibility').reply(200, visibility)
+ const data = await access.getVisibility('@npmcli/test-package', OPTS)
+ t.same(data, visibility)
+ })
+ t.test('non registry package', async t => {
+ await t.rejects(access.getVisibility('./local', OPTS), /package name only/)
+ })
+ t.end()
})
-t.test('ls-collaborators w/scope', async t => {
- const serverCollaborators = {
- 'myorg:myteam': 'write',
- 'myorg:anotherteam': 'read',
- 'myorg:thirdteam': 'special-case',
- }
- const clientCollaborators = {
- 'myorg:myteam': 'read-write',
- 'myorg:anotherteam': 'read-only',
- 'myorg:thirdteam': 'special-case',
- }
- tnock(t, REG).get(
- '/-/package/%40foo%2Fbar/collaborators?format=cli&user=zkat'
- ).reply(200, serverCollaborators)
- const data = await access.lsCollaborators('@foo/bar', 'zkat', OPTS)
- t.same(data, clientCollaborators, 'got collaborators')
+t.test('removePermissions', t => {
+ t.test('success', async t => {
+ tnock(t, REG).delete('/-/team/npm/myteam/package', {
+ package: '@npmcli/test-package',
+ }).reply(200)
+ await t.resolves(access.removePermissions('npm:myteam', '@npmcli/test-package', OPTS))
+ })
+ t.test('non registry spec', async t => {
+ await t.rejects(access.removePermissions('npm:myteam', './local', OPTS), /package name only/)
+ })
+ t.end()
})
-t.test('ls-collaborators w/o scope', async t => {
- const serverCollaborators = {
- 'myorg:myteam': 'write',
- 'myorg:anotherteam': 'read',
- 'myorg:thirdteam': 'special-case',
- }
- const clientCollaborators = {
- 'myorg:myteam': 'read-write',
- 'myorg:anotherteam': 'read-only',
- 'myorg:thirdteam': 'special-case',
- }
- tnock(t, REG).get(
- '/-/package/bar/collaborators?format=cli&user=zkat'
- ).reply(200, serverCollaborators)
- const data = await access.lsCollaborators('bar', 'zkat', OPTS)
- t.same(data, clientCollaborators, 'got collaborators')
+t.test('setAccess', t => {
+ t.test('public', async t => {
+ tnock(t, REG).post(
+ '/-/package/@npmcli%2ftest-package/access', { access: 'public' }
+ ).reply(200)
+ await t.resolves(access.setAccess('@npmcli/test-package', 'public', OPTS))
+ })
+ t.test('restricted', async t => {
+ tnock(t, REG).post(
+ '/-/package/@npmcli%2ftest-package/access', { access: 'restricted' }
+ ).reply(200)
+ await t.resolves(access.setAccess('@npmcli/test-package', 'restricted', OPTS))
+ })
+ t.test('non registry package', async t => {
+ await t.rejects(access.setAccess('./local', 'public', OPTS), /package name only/)
+ })
+ t.end()
})
-t.test('ls-collaborators bad response', async t => {
- tnock(t, REG).get(
- '/-/package/%40foo%2Fbar/collaborators?format=cli'
- ).reply(200, JSON.stringify(null))
- const data = await access.lsCollaborators('@foo/bar', null, OPTS)
- t.same(data, null, 'succeeds with null')
+t.test('setMfa', t => {
+ t.test('none', async t => {
+ tnock(t, REG).post('/-/package/@npmcli%2ftest-package/access', {
+ publish_requires_tfa: false,
+ }).reply(200)
+ await t.resolves(access.setMfa('@npmcli/test-package', 'none', OPTS))
+ })
+ t.test('publish', async t => {
+ tnock(t, REG).post('/-/package/@npmcli%2ftest-package/access', {
+ publish_requires_tfa: true,
+ automation_token_overrides_tfa: false,
+ }).reply(200)
+ await t.resolves(access.setMfa('@npmcli/test-package', 'publish', OPTS))
+ })
+ t.test('automation', async t => {
+ tnock(t, REG).post('/-/package/@npmcli%2ftest-package/access', {
+ publish_requires_tfa: true,
+ automation_token_overrides_tfa: true,
+ }).reply(200)
+ await t.resolves(access.setMfa('@npmcli/test-package', 'automation', OPTS))
+ })
+ t.test('invalid', async t => {
+ await t.rejects(access.setMfa('@npmcli/test-package', 'invalid', OPTS), /Invalid mfa setting/)
+ })
+ t.test('non registry spec', async t => {
+ await t.rejects(access.setMfa('./local', 'none', OPTS, /package name only/))
+ })
+ t.end()
})
-t.test('error on non-registry specs', async t => {
- await t.rejects(access.public('githubusername/reponame'),
- /spec.*must be a registry spec/, 'registry spec required')
- await t.rejects(access.restricted('foo/bar'),
- /spec.*must be a registry spec/, 'registry spec required')
- await t.rejects(access.grant('foo/bar', 'myorg', 'myteam', 'read-only'),
- /spec.*must be a registry spec/, 'registry spec required')
- await t.rejects(access.revoke('foo/bar', 'myorg', 'myteam'),
- /spec.*must be a registry spec/, 'registry spec required')
- await t.rejects(access.lsCollaborators('foo/bar'),
- /spec.*must be a registry spec/, 'registry spec required')
- await t.rejects(access.tfaRequired('foo/bar'),
- /spec.*must be a registry spec/, 'registry spec required')
- await t.rejects(access.tfaNotRequired('foo/bar'),
- /spec.*must be a registry spec/, 'registry spec required')
-})
+t.test('setPermissions', t => {
+ t.test('scope:team read-only', async t => {
+ tnock(t, REG).put('/-/team/npmcli/myteam/package', {
+ package: '@npmcli/test-package',
+ permissions: 'read-only',
+ }).reply(201)
+ await t.resolves(
+ access.setPermissions('npmcli:myteam', '@npmcli/test-package', 'read-only', OPTS)
+ )
+ })
+ t.test('scope only', async t => {
+ await t.rejects(
+ access.setPermissions('npmcli', '@npmcli/test-package', 'read-only', OPTS),
+ /scope:team/
+ )
+ })
+
+ t.test('no scope or team', async t => {
+ await t.rejects(
+ access.setPermissions('@:myteam', '@npmcli/test-package', 'read-only', OPTS),
+ /scope:team/
+ )
+ })
-t.test('edit', t => {
- t.equal(typeof access.edit, 'function', 'access.edit exists')
- t.throws(() => {
- access.edit()
- }, /Not implemented/, 'directly throws NIY message')
t.end()
})