git ssb

3+

cel / ssb-npm-registry



Commit c06265915b5e5980e714b509676313cf0b0bd61f

Implement message scope

cel committed on 12/12/2017, 12:44:25 AM
Parent: fa07dfbb84d42ea5501bf7ffbab938aab5abc28d

Files changed

README.mdchanged
index.jschanged
README.mdView
@@ -106,8 +106,18 @@
106106 and then redirects to `http://localhost:8989/blobs/get/&wSehVYRREEqZhZgpMk82CHcXVNcPqXDacgj3Tit7TMc=.sha256`
107107
108108 [`prebuild-install`]: https://github.com/prebuild/prebuild-install
109109
110+### Message scope
111+
112+Requests to the registry server can be scoped to a message by prefixing the request pathname with a directory part with a url-encoded message id. You can make such requests by including that prefix in the registry URL. i.e. `npm --registry=http://localhost:8043/<MsgIdEnc>/ ...`
113+
114+If a request is scoped to a message, the registry server will answer the request using information contained in that message or messages that it links to (recursively), instead of by using `sbot`'s links indexes. Fetching messages for a message-scoped request will be done using [`ssb-ooo`][] if that sbot plugin is available.
115+
116+A message-scoped request allows you to install a package without having to replicate the feeds of the authors of the package or its dependencies, since the information needed to install the package is referenced by message id.
117+
118+[`ssb-ooo`]: %AlNuRW5JCvClmCcdnHVt5vMHegdn43dFw0kCBI1c+CI=.sha256
119+
110120 ## Bootstrapping Scuttlebot
111121
112122 This plugin includes a script for securely bootstrapping an npm-installation of scuttlebot from your machine to a peer's machine.
113123
index.jsView
@@ -358,8 +358,9 @@
358358 this.wsPort = config.ws && Number(config.ws.port) || '8989'
359359 this.blobsPrefix = 'http://' + (config.host || 'localhost') + ':'
360360 + this.wsPort + '/blobs/get/'
361361 this.getBootstrapInfo = onceify(this.getBootstrapInfo, this)
362+ this.getMsg = memo({cache: lru(100)}, this.getMsg)
362363 }
363364
364365 SsbNpmRegistryServer.prototype = Object.create(http.Server.prototype)
365366 SsbNpmRegistryServer.prototype.constructor = SsbNpmRegistryServer
@@ -504,8 +505,40 @@
504505 }
505506 })
506507 }
507508
509+SsbNpmRegistryServer.prototype.getMsg = function (id, cb) {
510+ if (this.sbot.ooo) return this.sbot.ooo.get(id, cb)
511+ else this.sbot.get(id, function (err, value) {
512+ if (err) return cb(err)
513+ cb(null, {key: id, value: value})
514+ })
515+}
516+
517+SsbNpmRegistryServer.prototype.streamTree = function (heads, prop) {
518+ var self = this
519+ var stack = heads.slice()
520+ var seen = {}
521+ return function (abort, cb) {
522+ if (abort) return cb(abort)
523+ for (var id; stack.length && seen[id = stack.pop()];);
524+ if (!id) return cb(true)
525+ seen[id] = true
526+ // TODO: use DFS
527+ self.getMsg(id, function (err, msg) {
528+ if (err) return cb(new Error(err.stack || err))
529+ var c = msg && msg.value && msg.value.content
530+ var links = c && c[prop]
531+ if (Array.isArray(links)) {
532+ stack.push.apply(stack, links)
533+ } else if (links) {
534+ stack.push(links)
535+ }
536+ cb(null, msg)
537+ })
538+ }
539+}
540+
508541 function Req(server, req, res) {
509542 this.server = server
510543 this.req = req
511544 this.res = res
@@ -516,8 +549,16 @@
516549 Req.prototype.serve = function () {
517550 console.log(this.req.method, this.req.url, this.req.socket.remoteAddress.replace(/^::ffff:/, ''))
518551 var pathname = this.req.url.replace(/\?.*/, '')
519552 var m
553+ if ((m = /^\/(%25.*sha256)(\/.*)$/.exec(pathname))) {
554+ try {
555+ this.headMsgId = decodeURIComponent(m[1])
556+ } catch(e) {
557+ return this.respondError(400, e.stack || e)
558+ }
559+ pathname = m[2]
560+ }
520561 if (pathname === '/') return this.serveHome()
521562 if (pathname === '/bootstrap') return this.serveBootstrap()
522563 if (pathname === '/-/whoami') return this.serveWhoami()
523564 if (pathname === '/-/ping') return this.respond(200, true)
@@ -594,8 +635,57 @@
594635 distTag: parts[3],
595636 }
596637 }
597638
639+Req.prototype.getMentions = function (name) {
640+ return this.headMsgId ? pull(
641+ this.server.streamTree([this.headMsgId], 'dependencyBranch'),
642+ // decryption could be done here
643+ pull.map(function (msg) {
644+ var c = msg.value && msg.value.content
645+ if (!c.mentions || !Array.isArray(c.mentions)) return []
646+ return c.mentions.map(function (mention) {
647+ return {
648+ name: mention.name,
649+ size: mention.size,
650+ link: mention.link,
651+ author: msg.value.author,
652+ ts: msg.value.timestamp,
653+ }
654+ })
655+ }),
656+ pull.flatten(),
657+ pull.filter(typeof name === 'string' ? function (link) {
658+ return link.name === name
659+ } : name && name.$prefix ? function (link) {
660+ return link.name.substr(0, name.$prefix.length) === name.$prefix
661+ } : function () {
662+ throw new TypeError('unsupported name filter')
663+ }),
664+ ) : this.server.getMentions(name)
665+}
666+
667+Req.prototype.getMentionLinks = function (blobId) {
668+ return pull(
669+ this.headMsgId
670+ ? this.server.streamTree([this.headMsgId], 'dependencyBranch')
671+ : this.server.sbot.links({
672+ dest: blobId,
673+ rel: 'mentions',
674+ values: true,
675+ }),
676+ // decryption could be done here
677+ pull.map(function (msg) {
678+ var c = msg.value && msg.value.content
679+ return c && Array.isArray(c.mentions) || []
680+ }),
681+ pull.flatten(),
682+ pull.filter(function (link) {
683+ return link && link.link === blobId
684+ })
685+ )
686+}
687+
598688 Req.prototype.servePkg = function (pathname) {
599689 var self = this
600690 var parts = pathname.split('/')
601691 var pkgName = parts.shift().replace(/%2f/i, '/')
@@ -611,9 +701,9 @@
611701 versions: {}
612702 }
613703 var distTags = {/* <tag>: {version, ts}*/}
614704 pull(
615- self.server.getMentions({$prefix: 'npm:' + pkgName + ':'}),
705+ self.getMentions({$prefix: 'npm:' + pkgName + ':'}),
616706 pull.drain(function (mention) {
617707 var data = decodeName(mention.name)
618708 if (!data.version) return
619709 if (data.distTag) {
@@ -646,9 +736,9 @@
646736 var version = obj['dist-tags'][spec]
647737 || semver.maxSatisfying(Object.keys(obj.versions), spec)
648738 obj = obj.versions[version]
649739 if (!obj) return self.respondError(404, 'version not found: ' + spec)
650- populatePackageJson(self.server.sbot, obj, function (err, pkg) {
740+ self.populatePackageJson(obj, function (err, pkg) {
651741 if (err) return self.respondError(500, err.stack || err)
652742 obj = pkg || obj
653743 done()
654744 })
@@ -656,9 +746,9 @@
656746 function resolveAll() {
657747 var waiting = 0
658748 for (var version in obj.versions) (function (version) {
659749 waiting++
660- populatePackageJson(self.server.sbot, obj.versions[version], function (err, pkg) {
750+ self.populatePackageJson(obj.versions[version], function (err, pkg) {
661751 if (err && waiting <= 0) return console.trace(err)
662752 if (err) return waiting = 0, self.respondError(500, err.stack || err)
663753 if (pkg) obj.versions[version] = pkg
664754 if (!--waiting) done()
@@ -672,9 +762,9 @@
672762 }
673763
674764 Req.prototype.servePrebuild = function (name) {
675765 var self = this
676- var getMention = self.server.getMentions('prebuild:' + name)
766+ var getMention = self.getMentions('prebuild:' + name)
677767 var blobsByAuthor = {/* <author>: BlobId */}
678768 getMention(null, function next(err, link) {
679769 if (err === true) return done()
680770 if (err) return self.respondError(500, err.stack || err)
@@ -822,31 +912,18 @@
822912 })
823913 })
824914 }
825915
826-function populatePackageJson(sbot, obj, cb) {
916+Req.prototype.populatePackageJson = function (obj, cb) {
827917 var blobId = obj.dist.tarball.replace(/.*\/blobs\/get\//, '')
918+ var self = this
828919 var deps, bundledDeps
829920
830921 // look for dependencies in links.
831922 // then fallback to getting it from the tarball blob
832923
833924 pull(
834- sbot.links({
835- dest: blobId,
836- rel: 'mentions',
837- values: true,
838- }),
839- // decryption could be done here
840- pull.map(function (msg) {
841- var c = msg.value && msg.value.content
842- var mentions = c && c.mentions
843- return Array.isArray(mentions) ? mentions : []
844- }),
845- pull.flatten(),
846- pull.filter(function (link) {
847- return link && link.link === blobId
848- }),
925+ self.getMentionLinks(blobId),
849926 pull.drain(function (link) {
850927 if (link.dependencies) deps = link.dependencies
851928 bundledDeps = link.bundledDependencies || link.bundleDependencies || bundledDeps
852929 // how to handle multiple assignments of dependencies to a package?
@@ -859,9 +936,9 @@
859936 obj.bundledDependencies = bundledDeps
860937 cb(null, obj)
861938 } else {
862939 // get dependencies from the tarball
863- getPackageJsonFromTarballBlob(sbot, blobId, function (err, pkg) {
940+ getPackageJsonFromTarballBlob(self.server.sbot, blobId, function (err, pkg) {
864941 if (err) return cb(err)
865942 pkg.dist = obj.dist
866943 pkg.dist.shasum = pkg._shasum
867944 pkg.author = pkg.author || obj.author

Built with git-ssb-web