• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const figgyPudding = require('figgy-pudding')
4const getStream = require('get-stream')
5const npa = require('npm-package-arg')
6const npmFetch = require('npm-registry-fetch')
7const {PassThrough} = require('stream')
8const validate = require('aproba')
9
10const AccessConfig = figgyPudding({
11  Promise: {default: () => Promise}
12})
13
14const eu = encodeURIComponent
15const npar = spec => {
16  spec = npa(spec)
17  if (!spec.registry) {
18    throw new Error('`spec` must be a registry spec')
19  }
20  return spec
21}
22
23const cmd = module.exports = {}
24
25cmd.public = (spec, opts) => setAccess(spec, 'public', opts)
26cmd.restricted = (spec, opts) => setAccess(spec, 'restricted', opts)
27function setAccess (spec, access, opts) {
28  opts = AccessConfig(opts)
29  return pwrap(opts, () => {
30    spec = npar(spec)
31    validate('OSO', [spec, access, opts])
32    const uri = `/-/package/${eu(spec.name)}/access`
33    return npmFetch(uri, opts.concat({
34      method: 'POST',
35      body: {access},
36      spec
37    }))
38  }).then(res => res.body.resume() && true)
39}
40
41cmd.grant = (spec, entity, permissions, opts) => {
42  opts = AccessConfig(opts)
43  return pwrap(opts, () => {
44    spec = npar(spec)
45    const {scope, team} = splitEntity(entity)
46    validate('OSSSO', [spec, scope, team, permissions, opts])
47    if (permissions !== 'read-write' && permissions !== 'read-only') {
48      throw new Error('`permissions` must be `read-write` or `read-only`. Got `' + permissions + '` instead')
49    }
50    const uri = `/-/team/${eu(scope)}/${eu(team)}/package`
51    return npmFetch(uri, opts.concat({
52      method: 'PUT',
53      body: {package: spec.name, permissions},
54      scope,
55      spec,
56      ignoreBody: true
57    }))
58  }).then(() => true)
59}
60
61cmd.revoke = (spec, entity, opts) => {
62  opts = AccessConfig(opts)
63  return pwrap(opts, () => {
64    spec = npar(spec)
65    const {scope, team} = splitEntity(entity)
66    validate('OSSO', [spec, scope, team, opts])
67    const uri = `/-/team/${eu(scope)}/${eu(team)}/package`
68    return npmFetch(uri, opts.concat({
69      method: 'DELETE',
70      body: {package: spec.name},
71      scope,
72      spec,
73      ignoreBody: true
74    }))
75  }).then(() => true)
76}
77
78cmd.lsPackages = (entity, opts) => {
79  opts = AccessConfig(opts)
80  return pwrap(opts, () => {
81    return getStream.array(
82      cmd.lsPackages.stream(entity, opts)
83    ).then(data => data.reduce((acc, [key, val]) => {
84      if (!acc) {
85        acc = {}
86      }
87      acc[key] = val
88      return acc
89    }, null))
90  })
91}
92
93cmd.lsPackages.stream = (entity, opts) => {
94  validate('SO|SZ', [entity, opts])
95  opts = AccessConfig(opts)
96  const {scope, team} = splitEntity(entity)
97  let uri
98  if (team) {
99    uri = `/-/team/${eu(scope)}/${eu(team)}/package`
100  } else {
101    uri = `/-/org/${eu(scope)}/package`
102  }
103  opts = opts.concat({
104    query: {format: 'cli'},
105    mapJson (value, [key]) {
106      if (value === 'read') {
107        return [key, 'read-only']
108      } else if (value === 'write') {
109        return [key, 'read-write']
110      } else {
111        return [key, value]
112      }
113    }
114  })
115  const ret = new PassThrough({objectMode: true})
116  npmFetch.json.stream(uri, '*', opts).on('error', err => {
117    if (err.code === 'E404' && !team) {
118      uri = `/-/user/${eu(scope)}/package`
119      npmFetch.json.stream(uri, '*', opts).on(
120        'error', err => ret.emit('error', err)
121      ).pipe(ret)
122    } else {
123      ret.emit('error', err)
124    }
125  }).pipe(ret)
126  return ret
127}
128
129cmd.lsCollaborators = (spec, user, opts) => {
130  if (typeof user === 'object' && !opts) {
131    opts = user
132    user = undefined
133  }
134  opts = AccessConfig(opts)
135  return pwrap(opts, () => {
136    return getStream.array(
137      cmd.lsCollaborators.stream(spec, user, opts)
138    ).then(data => data.reduce((acc, [key, val]) => {
139      if (!acc) {
140        acc = {}
141      }
142      acc[key] = val
143      return acc
144    }, null))
145  })
146}
147
148cmd.lsCollaborators.stream = (spec, user, opts) => {
149  if (typeof user === 'object' && !opts) {
150    opts = user
151    user = undefined
152  }
153  opts = AccessConfig(opts)
154  spec = npar(spec)
155  validate('OSO|OZO', [spec, user, opts])
156  const uri = `/-/package/${eu(spec.name)}/collaborators`
157  return npmFetch.json.stream(uri, '*', opts.concat({
158    query: {format: 'cli', user: user || undefined},
159    mapJson (value, [key]) {
160      if (value === 'read') {
161        return [key, 'read-only']
162      } else if (value === 'write') {
163        return [key, 'read-write']
164      } else {
165        return [key, value]
166      }
167    }
168  }))
169}
170
171cmd.tfaRequired = (spec, opts) => setRequires2fa(spec, true, opts)
172cmd.tfaNotRequired = (spec, opts) => setRequires2fa(spec, false, opts)
173function setRequires2fa (spec, required, opts) {
174  opts = AccessConfig(opts)
175  return new opts.Promise((resolve, reject) => {
176    spec = npar(spec)
177    validate('OBO', [spec, required, opts])
178    const uri = `/-/package/${eu(spec.name)}/access`
179    return npmFetch(uri, opts.concat({
180      method: 'POST',
181      body: {publish_requires_tfa: required},
182      spec,
183      ignoreBody: true
184    })).then(resolve, reject)
185  }).then(() => true)
186}
187
188cmd.edit = () => {
189  throw new Error('Not implemented yet')
190}
191
192function splitEntity (entity = '') {
193  let [, scope, team] = entity.match(/^@?([^:]+)(?::(.*))?$/) || []
194  return {scope, team}
195}
196
197function pwrap (opts, fn) {
198  return new opts.Promise((resolve, reject) => {
199    fn().then(resolve, reject)
200  })
201}
202