index.js 4.6 KB

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