1 module.exports = unpublish
4 // modify to remove the version in question
5 // If no versions remaining, then DELETE
6 // else, PUT the modified data
9 var semver = require('semver')
10 var url = require('url')
11 var chain = require('slide').chain
12 var assert = require('assert')
14 function unpublish (uri, params, cb) {
15 assert(typeof uri === 'string', 'must pass registry URI to unpublish')
16 assert(params && typeof params === 'object', 'must pass params to unpublish')
17 assert(typeof cb === 'function', 'must pass callback to unpublish')
19 var ver = params.version
20 var auth = params.auth
21 assert(auth && typeof auth === 'object', 'must pass auth to unpublish')
28 this.get(uri + '?write=true', options, function (er, data) {
30 this.log.info('unpublish', uri + ' not published')
33 // remove all if no version specified
35 this.log.info('unpublish', 'No version specified, removing all')
36 return this.request(uri + '/-rev/' + data._rev, { method: 'DELETE', auth: auth }, cb)
39 var versions = data.versions || {}
40 var versionPublic = versions.hasOwnProperty(ver)
44 this.log.info('unpublish', uri + '@' + ver + ' not published')
46 dist = versions[ver].dist
47 this.log.verbose('unpublish', 'removing attachments', dist)
51 // if it was the only version, then delete the whole package.
52 if (!Object.keys(versions).length) {
53 this.log.info('unpublish', 'No versions remain, removing entire package')
54 return this.request(uri + '/-rev/' + data._rev, { method: 'DELETE', auth: auth }, cb)
57 if (!versionPublic) return cb()
59 var latestVer = data['dist-tags'].latest
60 for (var tag in data['dist-tags']) {
61 if (data['dist-tags'][tag] === ver) delete data['dist-tags'][tag]
64 if (latestVer === ver) {
65 data['dist-tags'].latest =
66 Object.getOwnPropertyNames(versions).sort(semver.compareLoose).pop()
70 delete data._revisions
71 delete data._attachments
72 var cb_ = detacher.call(this, uri, data, dist, auth, cb)
74 this.request(uri + '/-rev/' + rev, { method: 'PUT', body: data, auth: auth }, function (er) {
76 this.log.error('unpublish', 'Failed to update data')
83 function detacher (uri, data, dist, credentials, cb) {
84 return function (er) {
86 this.get(escape(uri, data.name), { auth: credentials }, function (er, data) {
89 var tb = url.parse(dist.tarball)
91 detach.call(this, uri, data, tb.pathname, data._rev, credentials, function (er) {
92 if (er || !dist.bin) return cb(er)
93 chain(Object.keys(dist.bin).map(function (bt) {
94 return function (cb) {
96 detach.call(this, uri, data, url.parse(d.tarball).pathname, null, credentials, cb)
104 function detach (uri, data, path, rev, credentials, cb) {
106 path += '/-rev/' + rev
107 this.log.info('detach', path)
108 return this.request(url.resolve(uri, path), { method: 'DELETE', auth: credentials }, cb)
110 this.get(escape(uri, data.name), { auth: credentials }, function (er, data) {
112 if (!rev) return cb(new Error('No _rev found in ' + data._id))
113 detach.call(this, data, path, rev, cb)
117 function escape (base, name) {
118 var escaped = name.replace(/\//, '%2f')
119 return url.resolve(base, escaped)