index.js 5.2 KB

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