cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject git commit: Initial commit
Date Thu, 13 Mar 2014 23:58:28 GMT
Repository: cordova-registry
Updated Branches:
  refs/heads/master [created] 9166301a3


Initial commit


Project: http://git-wip-us.apache.org/repos/asf/cordova-registry/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-registry/commit/9166301a
Tree: http://git-wip-us.apache.org/repos/asf/cordova-registry/tree/9166301a
Diff: http://git-wip-us.apache.org/repos/asf/cordova-registry/diff/9166301a

Branch: refs/heads/master
Commit: 9166301a3705782002a7cffbc3c00fc1c32699f4
Parents: 
Author: Steven Gill <stevengill97@gmail.com>
Authored: Thu Mar 13 16:58:23 2014 -0700
Committer: Steven Gill <stevengill97@gmail.com>
Committed: Thu Mar 13 16:58:23 2014 -0700

----------------------------------------------------------------------
 README.md              |  11 +
 _auth.js               | 277 +++++++++++++++++++++++++
 app.js                 |  13 ++
 lists.js               | 387 +++++++++++++++++++++++++++++++++++
 modules.js             | 205 +++++++++++++++++++
 package.json           |  23 +++
 rewrites.js            |  97 +++++++++
 shadow.js              |  37 ++++
 shows.js               | 131 ++++++++++++
 test.js                |  91 +++++++++
 updates.js             | 132 ++++++++++++
 validate_doc_update.js | 353 ++++++++++++++++++++++++++++++++
 views.js               | 480 ++++++++++++++++++++++++++++++++++++++++++++
 13 files changed, 2237 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..363e90d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+# Cordova Registry
+
+This repo contains the couchapp and design documents for the cordova registry.
+
+## Setup
+
+`npm install`
+
+## Pushing to local repository
+
+couchapp push app.js http://localhost:5984/registry

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/_auth.js
----------------------------------------------------------------------
diff --git a/_auth.js b/_auth.js
new file mode 100644
index 0000000..874e021
--- /dev/null
+++ b/_auth.js
@@ -0,0 +1,277 @@
+// sync to $host/_users/_design/_auth
+
+var ddoc = {_id:"_design/_auth", language:"javascript"}
+
+module.exports = ddoc
+
+ddoc.lists = {
+  index: function (head,req) {
+    var row
+      , out = {}
+      , id, data
+    while (row = getRow()) {
+      id = row.id.replace(/^org\.couchdb\.user:/, '')
+      data = row.value
+      delete data._id
+      delete data._rev
+      delete data.salt
+      delete data.password_sha
+      delete data.type
+      delete data.roles
+      delete data._deleted_conflicts
+      out[id] = data
+    }
+    send(toJSON(out))
+  },
+  email:function (head, req) {
+    var row
+      , data
+      , id
+      , email = req.query.email || undefined
+      , out = []
+    while (row = getRow()) {
+      id = row.id.replace(/^org\.couchdb\.user:/, '')
+      data = row.value
+      var dm = data.email || undefined
+      if (data.email !== email) continue
+      out.push(row.value.name)
+    }
+    send(toJSON(out))
+  }
+
+}
+
+
+ddoc.validate_doc_update = function (newDoc, oldDoc, userCtx, secObj) {
+  if (newDoc._deleted === true) {
+    // allow deletes by admins
+    if ((userCtx.roles.indexOf('_admin') !== -1)) {
+      return;
+    } else {
+      throw({forbidden: 'Only admins may delete user docs.'});
+    }
+  }
+
+  if ((oldDoc && oldDoc.type !== 'user') || newDoc.type !== 'user') {
+    throw({forbidden : 'doc.type must be user'});
+  } // we only allow user docs for now
+
+  if (!newDoc.name) {
+    throw({forbidden: 'doc.name is required'});
+  }
+
+  if (newDoc.roles && !isArray(newDoc.roles)) {
+    throw({forbidden: 'doc.roles must be an array'});
+  }
+
+  if (newDoc._id !== ('org.couchdb.user:' + newDoc.name)) {
+    throw({
+      forbidden: 'Doc ID must be of the form org.couchdb.user:name'
+    });
+  }
+
+  if (newDoc.name !== newDoc.name.toLowerCase()) {
+    throw({
+      forbidden: 'Name must be lower-case'
+    })
+  }
+
+  if (newDoc.name !== encodeURIComponent(newDoc.name)) {
+    throw({
+      forbidden: 'Name cannot contain non-url-safe characters'
+    })
+  }
+
+  if (newDoc.name.charAt(0) === '.') {
+    throw({
+      forbidden: 'Name cannot start with .'
+    })
+  }
+
+  if (!(newDoc.email && newDoc.email.match(/^.+@.+\..+$/))) {
+    throw({forbidden: 'Email must be an email address'})
+  }
+
+  if (oldDoc) { // validate all updates
+    if (oldDoc.name !== newDoc.name) {
+      throw({forbidden: 'Usernames can not be changed.'});
+    }
+  }
+
+  if (newDoc.password_sha && !newDoc.salt) {
+    throw({
+      forbidden: 'Users with password_sha must have a salt.'
+    });
+  }
+
+  var is_server_or_database_admin = function(userCtx, secObj) {
+    // see if the user is a server admin
+    if(userCtx.roles.indexOf('_admin') !== -1) {
+      return true; // a server admin
+    }
+
+    // see if the user a database admin specified by name
+    if(secObj && secObj.admins && secObj.admins.names) {
+      if(secObj.admins.names.indexOf(userCtx.name) !== -1) {
+        return true; // database admin
+      }
+    }
+
+    // see if the user a database admin specified by role
+    if(secObj && secObj.admins && secObj.admins.roles) {
+      var db_roles = secObj.admins.roles;
+      for(var idx = 0; idx < userCtx.roles.length; idx++) {
+        var user_role = userCtx.roles[idx];
+        if(db_roles.indexOf(user_role) !== -1) {
+          return true; // role matches!
+        }
+      }
+    }
+
+    return false; // default to no admin
+  }
+
+  if (!is_server_or_database_admin(userCtx, secObj)) {
+    if (oldDoc) { // validate non-admin updates
+      if (userCtx.name !== newDoc.name) {
+        throw({
+          forbidden: 'You may only update your own user document.'
+        });
+      }
+      if (oldDoc.email !== newDoc.email) {
+        throw({
+          forbidden: 'You may not change your email address\n' +
+            'Please visit https://npmjs.org/email-edit to do so.'
+        })
+      }
+      // validate role updates
+      var oldRoles = oldDoc.roles.sort();
+      var newRoles = newDoc.roles.sort();
+
+      if (oldRoles.length !== newRoles.length) {
+        throw({forbidden: 'Only _admin may edit roles'});
+      }
+
+      for (var i = 0; i < oldRoles.length; i++) {
+        if (oldRoles[i] !== newRoles[i]) {
+          throw({forbidden: 'Only _admin may edit roles'});
+        }
+      }
+    } else if (newDoc.roles.length > 0) {
+      throw({forbidden: 'Only _admin may set roles'});
+    }
+  }
+
+  // no system roles in users db
+  for (var i = 0; i < newDoc.roles.length; i++) {
+    if (newDoc.roles[i][0] === '_') {
+      throw({
+        forbidden: 'No system roles (starting with underscore) in users db.'
+      });
+    }
+  }
+
+  // no system names as names
+  if (newDoc.name[0] === '_') {
+    throw({forbidden: 'Username may not start with underscore.'});
+  }
+
+  var badUserNameChars = [':'];
+
+  for (var i = 0; i < badUserNameChars.length; i++) {
+    if (newDoc.name.indexOf(badUserNameChars[i]) >= 0) {
+      throw({forbidden: 'Character `' + badUserNameChars[i] +
+          '` is not allowed in usernames.'});
+    }
+  }
+}
+
+ddoc.views = {
+  listAll: { map : function (doc) { return emit(doc._id, doc) } },
+
+  invalidUser: { map: function (doc) {
+    var errors = []
+    if (doc.type !== 'user') {
+      errors.push('doc.type must be user')
+    }
+
+    if (!doc.name) {
+      errors.push('doc.name is required')
+    }
+
+    if (doc.roles && !isArray(doc.roles)) {
+      errors.push('doc.roles must be an array')
+    }
+
+    if (doc._id !== ('org.couchdb.user:' + doc.name)) {
+      errors.push('Doc ID must be of the form org.couchdb.user:name')
+    }
+
+    if (doc.name !== doc.name.toLowerCase()) {
+      errors.push('Name must be lower-case')
+    }
+
+    if (doc.name !== encodeURIComponent(doc.name)) {
+      errors.push('Name cannot contain non-url-safe characters')
+    }
+
+    if (doc.name.charAt(0) === '.') {
+      errors.push('Name cannot start with .')
+    }
+
+    if (!(doc.email && doc.email.match(/^.+@.+\..+$/))) {
+      errors.push('Email must be an email address')
+    }
+
+    if (doc.password_sha && !doc.salt) {
+      errors.push('Users with password_sha must have a salt.')
+    }
+    if (!errors.length) return
+    emit([doc.name, doc.email], errors)
+  }},
+
+  invalid: { map: function (doc) {
+    if (doc.type !== 'user') {
+      return emit(['doc.type must be user', doc.email, doc.name], 1)
+    }
+
+    if (!doc.name) {
+      return emit(['doc.name is required', doc.email, doc.name], 1)
+    }
+
+    if (doc.roles && !isArray(doc.roles)) {
+      return emit(['doc.roles must be an array', doc.email, doc.name], 1)
+    }
+
+    if (doc._id !== ('org.couchdb.user:' + doc.name)) {
+      return emit(['Doc ID must be of the form org.couchdb.user:name', doc.email, doc.name], 1)
+    }
+
+    if (doc.name !== doc.name.toLowerCase()) {
+      return emit(['Name must be lower-case', doc.email, doc.name], 1)
+    }
+
+    if (doc.name !== encodeURIComponent(doc.name)) {
+      return emit(['Name cannot contain non-url-safe characters', doc.email, doc.name], 1)
+    }
+
+    if (doc.name.charAt(0) === '.') {
+      return emit(['Name cannot start with .', doc.email, doc.name], 1)
+    }
+
+    if (!(doc.email && doc.email.match(/^.+@.+\..+$/))) {
+      return emit(['Email must be an email address', doc.email, doc.name], 1)
+    }
+
+    if (doc.password_sha && !doc.salt) {
+      return emit(['Users with password_sha must have a salt.', doc.email, doc.name], 1)
+    }
+  }, reduce: '_sum'}
+}
+
+if (require.main === module) {
+  console.log(JSON.stringify(ddoc, function (k, v) {
+    if (typeof v !== 'function') return v;
+    return v.toString()
+  }))
+}

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/app.js
----------------------------------------------------------------------
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..f050aae
--- /dev/null
+++ b/app.js
@@ -0,0 +1,13 @@
+var ddoc = module.exports =
+  { _id:'_design/app'
+  , shows: require("./shows.js")
+  , updates: require("./updates.js")
+  , rewrites: require("./rewrites.js")
+  , views: require("./views.js")
+  , lists: require("./lists.js")
+  , validate_doc_update: require("./validate_doc_update.js")
+  , language: "javascript"
+  }
+
+var modules = require("./modules.js")
+for (var i in modules) ddoc[i] = modules[i]

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/lists.js
----------------------------------------------------------------------
diff --git a/lists.js b/lists.js
new file mode 100644
index 0000000..e51957a
--- /dev/null
+++ b/lists.js
@@ -0,0 +1,387 @@
+var lists = module.exports = {}
+
+lists.first = function (head, req) {
+  var row = getRow()
+  send(toJSON([row, row.id, row.doc]))
+}
+
+lists.short = function (head, req) {
+  require("monkeypatch").patch(Object, Date, Array, String)
+
+  var out = {}
+    , row
+    , show = (req.query.show || "").split(",")
+    , v = show.indexOf("version") !== -1
+    , t = show.indexOf("tag") !== -1
+  while (row = getRow()) {
+    if (!row.id) continue
+    if (!t && !v) {
+      out[row.id] = true
+      continue
+    }
+    var val = row.value
+    if (t) Object.keys(val["dist-tags"] || {}).forEach(function (t) {
+      out[row.id + "@" + t] = true
+    })
+    if (v) Object.keys(val.versions || {}).forEach(function (v) {
+      out[row.id + "@" + v] = true
+    })
+  }
+  send(toJSON(Object.keys(out)))
+}
+
+
+lists.rss = function (head, req) {
+  var limit = +req.query.limit
+    , desc = req.query.descending
+  if (!desc || !limit || limit > 50 || limit < 0) {
+    start({ code: 403
+           , headers: { 'Content-type': 'text/xml' }})
+    send('<error><![CDATA[Please retry your request with '
+        + '?descending=true&limit=50 query params]]></error>')
+    return
+  }
+
+  start({ code: 200
+        // application/rss+xml is correcter, but also annoyinger
+        , headers: { "Content-Type": "text/xml" } })
+  send('<?xml version="1.0" encoding="UTF-8"?>'
+      +'\n<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" '
+        +'"http://my.netscape.com/publish/formats/rss-0.91.dtd">'
+      +'\n<rss version="0.91">'
+      +'\n  <channel>'
+      +'\n    <title>npm recent updates</title>'
+      +'\n    <link>http://search.npmjs.org/</link>'
+      +'\n    <description>Updates to the npm package registry</description>'
+      +'\n    <language>en</language>')
+
+  var row
+  while (row = getRow()) {
+    if (!row.value || !row.value["dist-tags"]) continue
+
+    var doc = row.value
+    var authors = doc.maintainers.map(function (m) {
+      return '<author>' + m.name + '</author>'
+    }).join('\n      ')
+
+    var latest = doc["dist-tags"].latest
+    var time = doc.time && doc.time[latest]
+    var date = new Date(time)
+    doc = doc.versions[latest]
+    if (!doc || !time || !date) continue
+
+    var url = "https://npmjs.org/package/" + doc.name
+
+    send('\n    <item>'
+        +'\n      <title>' + doc._id + '</title>'
+        +'\n      <link>' + url + '</link>'
+        +'\n      ' + authors
+        +'\n      <description><![CDATA['
+          + (doc.description || '').trim() + ']]></description>'
+        +'\n      <pubDate>' + date.toISOString() + '</pubDate>'
+        +'\n    </item>')
+  }
+  send('\n  </channel>'
+      +'\n</rss>')
+}
+
+
+
+lists.index = function (head, req) {
+  require("monkeypatch").patch(Object, Date, Array, String)
+  var basePath = req.requested_path
+  if (basePath.indexOf("_list") === -1) basePath = ""
+  else {
+    basePath = basePath.slice(0, basePath.indexOf("_list"))
+                       .concat(["_rewrite", ""]).join("/")
+  }
+
+  var row
+    , semver = require("semver")
+    , res = []
+
+  if (req.query.jsonp) send(req.query.jsonp + "(")
+  send('{"_updated":' + Date.now())
+  while (row = getRow()) {
+    if (!row.id) continue
+
+    var doc = row.value
+    if (!doc.name || !doc._id ||
+        encodeURIComponent(doc._id) !== doc._id) continue
+
+    var p = {}
+
+    // legacy kludge
+    delete doc.mtime
+    delete doc.ctime
+    if (doc.versions) for (var v in doc.versions) {
+      var clean = semver.clean(v)
+      delete doc.versions[v].ctime
+      delete doc.versions[v].mtime
+      if (clean !== v) {
+        var x = doc.versions[v]
+        delete doc.versions[v]
+        x.version = v = clean
+        doc.versions[clean] = x
+      }
+    }
+    if (doc["dist-tags"]) for (var tag in doc["dist-tags"]) {
+      var clean = semver.clean(doc["dist-tags"][tag])
+      if (!clean) delete doc["dist-tags"][tag]
+      else doc["dist-tags"][tag] = clean
+    }
+    // end kludge
+
+    for (var i in doc) {
+      if (i === "versions" || i.charAt(0) === "_" || i === 'readme' ||
+          i === 'time') continue
+      p[i] = doc[i]
+    }
+    if (doc.time) {
+      p.time = { modified: doc.time.modified }
+    }
+    if (p['dist-tags'] && typeof p['dist-tags'] === 'object') {
+      p.versions = Object.keys(p['dist-tags']).reduce(function (ac, v) {
+        ac[ p['dist-tags'][v] ] = v
+        return ac
+      }, {})
+    }
+    if (doc.repositories && Array.isArray(doc.repositories)) {
+      doc.repository = doc.repositories[0]
+      delete doc.repositories
+    }
+    if (doc.repository) p.repository = doc.repository
+    if (doc.description) p.description = doc.description
+    for (var i in doc.versions) {
+      if (doc.versions[i].repository && !doc.repository) {
+        p.repository = doc.versions[i].repository
+      }
+      if (doc.versions[i].keywords) p.keywords = doc.versions[i].keywords
+    }
+    send(',' + JSON.stringify(doc._id) + ':' + JSON.stringify(p))
+  }
+  send('}')
+  if (req.query.jsonp) send(')')
+
+}
+
+
+lists.byField = function (head, req) {
+  require("monkeypatch").patch(Object, Date, Array, String)
+
+  if (!req.query.field) {
+    start({"code":"400", "headers": {"Content-Type": "application/json"}})
+    send('{"error":"Please specify a field parameter"}')
+    return
+  }
+
+  start({"code": 200, "headers": {"Content-Type": "application/json"}})
+  var row
+    , out = {}
+    , field = req.query.field
+    , not = field.charAt(0) === "!"
+  if (not) field = field.substr(1)
+  while (row = getRow()) {
+    if (!row.id) continue
+    var has = row.value.hasOwnProperty(field)
+    if (!not && !has || not && has) continue
+    out[row.key] = { "maintainers": row.value.maintainers.map(function (m) {
+      return m.name + " <" + m.email + ">"
+    }) }
+    if (has) out[row.key][field] = row.value[field]
+  }
+  send(JSON.stringify(out))
+}
+
+
+
+lists.preBuilt = function (head, req) {
+  start({"code": 200, "headers": {"Content-Type": "text/plain"}});
+  var row
+    , out = []
+  while (row = getRow()) {
+    if (!row.id) continue
+    if (!(req.query.bindist && row.value[req.query.bindist])) continue
+    out.push(row.key)
+  }
+  send(out.join("\n"))
+}
+
+lists.needBuild = function (head, req) {
+  start({"code": 200, "headers": {"Content-Type": "text/plain"}});
+  var row
+    , first = true
+  while (row = getRow()) {
+    if (!row.id) continue
+    if (req.query.bindist && row.value[req.query.bindist]) continue
+    // out.push(row.key)
+    send((first ? "{" : ",")
+        + JSON.stringify(row.key)
+        + ":"
+        + JSON.stringify(Object.keys(row.value))
+        + "\n")
+    first = false
+  }
+  send("}\n")
+}
+
+lists.scripts = function (head, req) {
+  var row
+    , out = {}
+    , scripts = req.query.scripts && req.query.scripts.split(",")
+    , match = req.query.match
+
+  if (match) match = new RegExp(match)
+
+  while (row = getRow()) {
+    inc = true
+    if (!row.id) continue
+    if (req.query.package && row.id !== req.query.package) continue
+    if (scripts && scripts.length) {
+      var inc = false
+      for (var s = 0, l = scripts.length; s < l && !inc; s ++) {
+        inc = row.value[scripts[s]]
+        if (match) inc = inc && row.value[scripts[s]].match(match)
+      }
+      if (!inc) continue
+    }
+    out[row.id] = row.value
+  }
+  send(toJSON(out))
+}
+
+
+lists.rowdump = function (head, req) {
+  var rows = []
+  while (row = getRow()) rows.push(row)
+  send(toJSON(rows))
+}
+
+lists.passthrough = function (head, req) {
+  var out = {}
+    , row
+  while (row = getRow()) {
+    if (!row.id) continue
+    out[row.id] = row.value
+  }
+  send(toJSON(out))
+}
+
+lists.byUser = function (head, req) {
+  var out = {}
+    , user = req.query.user && req.query.user !== "-" ? req.query.user : null
+    , users = user && user.split("|")
+  while (row = getRow()) {
+    if (!user || users.indexOf(row.key) !== -1) {
+      var l = out[row.key] = out[row.key] || []
+      l.push(row.value)
+    }
+  }
+  send(toJSON(out))
+}
+
+lists.sortCount = function (head, req) {
+  var out = []
+  while (row = getRow()) {
+    out.push([row.key, row.value])
+  }
+  out = out.sort(function (a, b) {
+    return a[1] === b[1] ? 0
+         : a[1] < b[1] ? 1 : -1
+  })
+  var outObj = {}
+  for (var i = 0, l = out.length; i < l; i ++) {
+    outObj[out[i][0]] = out[i][1]
+  }
+  send(toJSON(outObj))
+}
+
+lists.size = function (head, req) {
+  var row
+    , out = []
+    , max = 0
+  while (row = getRow()) {
+    if (!row.id) continue
+    out.push(row.value)
+  }
+  var list = []
+  out = out.sort(function (a, b) {
+             max = Math.max(max, a.size, b.size)
+             return a.size > b.size ? -1 : 1
+           })
+           .reduce(function (l, r) {
+             var stars = new Array(Math.ceil(80 * (r.size/max)) + 1).join("\u25FE")
+             l[r._id] = { size: r.size
+                        , count: r.count
+                        , avg: r.avg
+                        , rel: r.size / max
+                        , s: stars
+                        }
+             return l
+           }, {})
+  send(JSON.stringify(out))
+}
+
+lists.histogram = function (head, req) {
+  require("monkeypatch").patch(Object, Date, Array, String)
+  start({"code": 200, "headers": {"Content-Type": "text/plain"}});
+  var row
+    , out = []
+    , max = {}
+    , field = req.query.field
+    , sort = req.query.sort
+    , doAll = !field
+
+  while (row = getRow()) {
+    if (!row.id) continue
+    out.push(row.value)
+  }
+
+  if (!doAll) out.sort(function (a, b) {
+    max[field] = Math.max(max[field] || -Infinity, a[field], b[field])
+    return a[field] > b[field] ? -1 : 1
+  })
+  else out.sort(function (a, b) {
+    for (var field in a) if (field.charAt(0) !== "_" && !isNaN(a[field])) {
+      max[field] = Math.max(max[field] || -Infinity, a[field])
+    }
+    for (var field in b) if (field.charAt(0) !== "_" && !isNaN(b[field])) {
+      max[field] = Math.max(max[field] || -Infinity, b[field])
+    }
+    if (sort) {
+      return Number(a[sort]) > Number(b[sort]) ? -1 : 1
+    } else {
+      return 0
+    }
+  })
+  if (doAll) {
+    // sort the fields by the max sizes.
+    var m = {}
+    Object.keys(max).sort(function (a, b) {
+      return max[a] > max[b] ? -1 : 1
+    }).forEach(function (k) { m[k] = max[k] })
+    max = m
+  }
+  out = out.map(function (a) {
+    var o = {}
+    for (var f in max) {
+      var blk = new Array(Math.ceil(80*(a[f] / max[f])+1)).join("#")
+        , spc = new Array(80 - blk.length + 1).join(" ")
+      o[f] = spc + blk + " " + a[f]
+    }
+    o._id = a._id
+    return o
+  }).reduce(function (l, r) {
+    l[r._id] = r
+    return l
+  }, {})
+
+  var spc = new Array(82).join(" ")
+  send(Object.keys(out).map(function (i) {
+    if (doAll) return [spc + i].concat(Object.keys(max).map(function (f) {
+      return out[i][f] + " " + f
+    })).join("\n") + "\n"
+    return out[i][field] + " " + i
+  }).join("\n"))
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/modules.js
----------------------------------------------------------------------
diff --git a/modules.js b/modules.js
new file mode 100644
index 0000000..f66809e
--- /dev/null
+++ b/modules.js
@@ -0,0 +1,205 @@
+// Each of these is a string that will be require()'d in the couchapp
+
+exports.deep =
+  [ "exports.deepEquals = deepEquals"
+  , "exports.extend = deepExtend"
+  , function deepExtend(o1, o2) {
+      // extend o1 with o2 (in-place)
+      for (var prop in o2) {
+        if (hOP(o2, prop)) {
+          if (hOP(o1, prop)) {
+            if (typeof o1[prop] === "object") {
+              deepExtend(o1[prop], o2[prop])
+            }
+          } else {
+            o1[prop] = o2[prop]
+          }
+        }
+      }
+      return o1
+    }
+  , function fullPath(pathPrefix, p){
+      return pathPrefix.concat([p])
+    }
+  , function isObject(v){
+      return typeof v === 'object'
+    }
+  , function arrayInArray(v, arr) {
+    // Check whether `arr` contains an array that's shallowly equal to `v`.
+      return arr.some(function(e) {
+        if (e.length !== v.length) return false
+        for (var i=0; i<e.length; i++) {
+          if (e[i] !== v[i]) {
+            return false
+          }
+        }
+        return true
+      })
+    }
+  , function deepEquals(o1, o2, ignoreKeys, pathPrefix){
+      pathPrefix = pathPrefix || []
+      ignoreKeys = ignoreKeys || []
+      function hOP (obj, prop) {
+        return Object.prototype.hasOwnProperty.call(obj, prop)
+      }
+      if (typeof o1 !== typeof o2) {
+        return false
+      } else if (!isObject(o1)) {
+        return o1 === o2
+      }
+      for (var prop in o1) {
+        if (hOP(o1, prop) &&
+            !arrayInArray(fullPath(pathPrefix, prop), ignoreKeys)) {
+          if (!hOP(o2, prop) ||
+              !deepEquals(o1[prop],
+                          o2[prop],
+                          ignoreKeys,
+                          fullPath(pathPrefix, prop))) {
+            return false
+          }
+        }
+      }
+      for (var prop in o2) {
+        if (hOP(o2, prop) &&
+            !hOP(o1, prop) &&
+            !arrayInArray(fullPath(pathPrefix, prop), ignoreKeys)) {
+          return false
+        }
+      }
+      return true
+    }
+  ].map(function (s) { return s.toString() }).join("\n")
+
+exports.semver = require('fs').readFileSync(require.resolve('semver'), 'utf8')
+
+exports.valid =
+  [ 'var semver = require("semver")'
+  , 'exports.name = validName'
+  , 'exports.package = validPackage'
+  , function validName (name) {
+     if (!name) return false
+     var n = name.trim()
+     if (!n || n.charAt(0) === "."
+         || !n.match(/^[a-zA-Z0-9]/)
+         || n.match(/[\/\(\)&\?#\|<>@:%\s\\\*'"!~`]/)
+         || n.toLowerCase() === "node_modules"
+         || n !== encodeURIComponent(n)
+         || n.toLowerCase() === "favicon.ico") {
+       return false
+     }
+     return n
+    }
+  , function validPackage (pkg) {
+      return validName(pkg.name) && semver.valid(pkg.version)
+    }
+  ].map(function (s) { return s.toString() }).join("\n")
+
+
+// monkey patchers
+exports.Date =
+  [ "exports.parse = parse"
+  , "exports.toISOString = toISOString"
+  , "exports.now = now"
+  , function now () {
+      return new Date().getTime()
+    }
+  , function parse (s) {
+      // s is something like "2010-12-29T07:31:06Z"
+      s = s.split("T")
+      var ds = s[0]
+        , ts = s[1]
+        , d = new Date()
+      ds = ds.split("-")
+      ts = ts.split(":")
+      var tz = ts[2].substr(2)
+      ts[2] = ts[2].substr(0, 2)
+      d.setUTCFullYear(+ds[0])
+      d.setUTCMonth(+ds[1]-1)
+      d.setUTCDate(+ds[2])
+      d.setUTCHours(+ts[0])
+      d.setUTCMinutes(+ts[1])
+      d.setUTCSeconds(+ts[2])
+      d.setUTCMilliseconds(0)
+      return d.getTime()
+    }
+  , "exports.toISOString = toISOString"
+  , function toISOString () { return ISODateString(this) }
+  , function pad(n){return n<10 ? '0'+n : n}
+  , function ISODateString(d){
+      return d.getUTCFullYear()+'-'
+           + pad(d.getUTCMonth()+1)+'-'
+           + pad(d.getUTCDate())+'T'
+           + pad(d.getUTCHours())+':'
+           + pad(d.getUTCMinutes())+':'
+           + pad(d.getUTCSeconds())+'Z'}
+  ].map(function (s) { return s.toString() }).join("\n")
+
+exports.Object =
+  [ "exports.keys = keys"
+  , function keys (o) {
+      var a = []
+      for (var i in o) a.push(i)
+      return a }
+  ].map(function (s) { return s.toString() }).join("\n")
+
+exports.Array =
+  [ "exports.isArray = isArray"
+  , "exports.forEach = forEach",
+  , "exports.reduce = reduce",
+  , function forEach (fn) {
+      for (var i = 0, l = this.length; i < l; i ++) {
+        if (this.hasOwnProperty(i)) {
+          fn(this[i], i, this)
+        }
+      }
+    }
+  , function reduce (callback, initialValue) {
+      var previousValue = initialValue || this[0];
+      for (var i = initialValue ? 0 : 1; i < this.length; i++) {
+        previousValue = callback(previousValue, this[i], i, this);
+      }
+      return previousValue;
+    }
+  , function isArray (a) {
+      return a instanceof Array
+        || Object.prototype.toString.call(a) === "[object Array]"
+        || (typeof a === "object" && typeof a.length === "number") }
+  ].map(function (s) { return s.toString() }).join("\n")
+
+exports.String =
+  [ "exports.trim = trim"
+  , function trim () {
+      return this.replace(/^\s+|\s+$/g, "")
+    }
+  ].map(function (s) { return s.toString() }).join("\n")
+
+exports.monkeypatch =
+  [ "exports.patch = patch"
+  , function patch (Object, Date, Array, String) {
+      if (!Date.parse || isNaN(Date.parse("2010-12-29T07:31:06Z"))) {
+        Date.parse = require("Date").parse
+      }
+
+      Date.prototype.toISOString = Date.prototype.toISOString
+        || require("Date").toISOString
+
+      Date.now = Date.now
+        || require("Date").now
+
+      Object.keys = Object.keys
+        || require("Object").keys
+
+      Array.prototype.forEach = Array.prototype.forEach
+        || require("Array").forEach
+
+      Array.prototype.reduce = Array.prototype.reduce
+        || require("Array").reduce
+
+      Array.isArray = Array.isArray
+        || require("Array").isArray
+
+      String.prototype.trim = String.prototype.trim
+        || require("String").trim
+    }
+  ].map(function (s) { return s.toString() }).join("\n")
+

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..33e4ef5
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+  "name": "cordova-registry",
+  "version": "0.0.1",
+  "description": "Registry files for the Cordova Plugin Registry. http://plugins.cordova.io",
+  "main": "app.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://git-wip-us.apache.org/repos/asf/cordova-registry.git"
+  },
+  "keywords": [
+    "cordova",
+    "registry"
+  ],
+  "author": "Anis Kadri, Steve Gill",
+  "license": "Apache V2",
+  "dependencies": {
+    "couchapp": "^0.10.0",
+    "semver": "^2.2.1"
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/rewrites.js
----------------------------------------------------------------------
diff --git a/rewrites.js b/rewrites.js
new file mode 100644
index 0000000..3ce2581
--- /dev/null
+++ b/rewrites.js
@@ -0,0 +1,97 @@
+module.exports =
+  [ { from: "/", to:"../../../registry", method: "GET" }
+  , { from: "/-/jsonp/:jsonp", to:"_list/short/listAll", method: "GET" }
+
+  , { from: "/_session", to: "../../../_session", method: "GET" }
+  , { from: "/_session", to: "../../../_session", method: "PUT" }
+  , { from: "/_session", to: "../../../_session", method: "POST" }
+  , { from: "/_session", to: "../../../_session", method: "DELETE" }
+  , { from: "/_session", to: "../../../_session", method: "HEAD" }
+
+  , { from: "/-/all/since", to:"_list/index/modified", method: "GET" }
+
+  , { from: "/-/rss", to: "_list/rss/modified"
+    , method: "GET" }
+
+  , { from: "/-/rss/:package", to: "_list/rss/modifiedPackage"
+    , method: "GET" }
+
+  , { from: "/-/all", to:"_list/index/listAll", method: "GET" }
+  , { from: "/-/all/-/jsonp/:jsonp", to:"_list/index/listAll", method: "GET" }
+
+  , { from: "/-/short", to:"_list/short/listAll", method: "GET" }
+  , { from: "/-/scripts", to:"_list/scripts/scripts", method: "GET" }
+  , { from: "/-/by-field", to:"_list/byField/byField", method: "GET" }
+  , { from: "/-/fields", to:"_list/sortCount/fieldsInUse", method: "GET",
+      query: { group: "true" } }
+
+  , { from: "/-/needbuild", to:"_list/needBuild/needBuild", method: "GET" }
+  , { from: "/-/prebuilt", to:"_list/preBuilt/needBuild", method: "GET" }
+  , { from: "/-/nonlocal", to:"_list/short/nonlocal", method: "GET" }
+
+  , { from : "/favicon.ico", to:"../../npm/favicon.ico", method:"GET" }
+
+  , { from: "/-/users", to:"../../../_users/_design/_auth/_list/index/listAll"
+    , method: "GET" }
+  , { from: "/-/user/:user", to:"../../../_users/:user", method: "PUT" }
+
+  , { from: "/-/user/:user/-rev/:rev", to:"../../../_users/:user"
+    , method: "PUT" }
+
+  , { from: "/-/user/:user", to:"../../../_users/:user", method: "GET" }
+
+  // Just have it work like a regular old couchdb thing
+  // this means that the couch-login module can be used to create accounts.
+  , { from: "/_users/:user", to:"../../../_users/:user", method: "PUT" }
+  , { from: "/_users/:user", to:"../../../_users/:user", method: "GET" }
+  , { from: "/public_users/:user", to:"../../../public_users/:user", method: "PUT" }
+  , { from: "/public_users/:user", to:"../../../public_users/:user", method: "GET" }
+
+  , { from: "/-/user-by-email/:email"
+    , to:"../../../_users/_design/_auth/_list/email/listAll"
+    , method: "GET" }
+
+  , { from: "/-/top"
+    , to:"_view/npmTop"
+    , query: { group_level: 1 }
+    , method: "GET" }
+
+  , { from: "/-/by-user/:user", to: "_list/byUser/byUser", method: "GET" }
+  , { from: "/-/starred-by-user/:user", to: "_list/byUser/starredByUser", method: "GET" }
+  , { from: "/-/starred-by-package/:user", to: "_list/byUser/starredByPackage", method: "GET" }
+
+  , { from: "/:pkg", to: "/_show/package/:pkg", method: "GET" }
+  , { from: "/:pkg/-/jsonp/:jsonp", to: "/_show/package/:pkg", method: "GET" }
+  , { from: "/:pkg/:version", to: "_show/package/:pkg", method: "GET" }
+  , { from: "/:pkg/:version/-/jsonp/:jsonp", to: "_show/package/:pkg"
+    , method: "GET" }
+
+  , { from: "/:pkg/-/:att", to: "../../:pkg/:att", method: "GET" }
+  , { from: "/:pkg/-/:att/:rev", to: "../../:pkg/:att", method: "PUT" }
+  , { from: "/:pkg/-/:att/-rev/:rev", to: "../../:pkg/:att", method: "PUT" }
+  , { from: "/:pkg/-/:att/:rev", to: "../../:pkg/:att", method: "DELETE" }
+  , { from: "/:pkg/-/:att/-rev/:rev", to: "../../:pkg/:att", method: "DELETE" }
+
+  , { from: "/:pkg", to: "/_update/package/:pkg", method: "PUT" }
+  , { from: "/:pkg/-rev/:rev", to: "/_update/package/:pkg", method: "PUT" }
+
+  , { from: "/:pkg/:version", to: "_update/package/:pkg", method: "PUT" }
+  , { from: "/:pkg/:version/-rev/:rev", to: "_update/package/:pkg"
+    , method: "PUT" }
+
+  , { from: "/:pkg/:version/-tag/:tag", to: "_update/package/:pkg"
+    , method: "PUT" }
+  , { from: "/:pkg/:version/-tag/:tag/-rev/:rev", to: "_update/package/:pkg"
+    , method: "PUT" }
+
+  , { from: "/:pkg/:version/-pre/:pre", to: "_update/package/:pkg"
+    , method: "PUT" }
+  , { from: "/:pkg/:version/-pre/:pre/-rev/:rev", to: "_update/package/:pkg"
+    , method: "PUT" }
+
+  , { from: "/:pkg/-rev/:rev", to: "../../:pkg", method: "DELETE" }
+
+  , {from:'/-/_view/*', to:'_view/*', method: 'GET'}
+  , {from:'/-/_list/*', to:'_list/*', method: 'GET'}
+  , {from:'/-/_show/*', to:'_show/*', method: 'GET'}
+  ]

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/shadow.js
----------------------------------------------------------------------
diff --git a/shadow.js b/shadow.js
new file mode 100644
index 0000000..0dead41
--- /dev/null
+++ b/shadow.js
@@ -0,0 +1,37 @@
+module.exports =
+  { _id:'_design/ghost'
+  , rewrites: ghostRewrites()
+  , language: "javascript"
+  }
+
+function ghostRewrites () {
+  return require("./rewrites.js").map(function (rule) {
+
+    var to = rule.to
+    // Note: requests to /_users/blah are still passed through directly.
+    if (rule.to.match(/\/_users(?:\/|$)/) &&
+        !rule.from.match(/^\/?_users/)) {
+      if (rule.method === "GET") {
+        to = to.replace(/\/_users(\/|$)/, "/public_users$1")
+      } else if (rule.method === "PUT") {
+        // when couchdb lets us PUT to _update functions on _users,
+        // uncomment this.  For now, we'll just leave it as-is,
+        // and use newedits=false to sync the rev fields to public_users.
+        //
+        // to = to.replace(/\/_users(\/|$)/,
+        //                 "/_users/_design/auth/_update/norev$1")
+      }
+    } else {
+      to = "../app/" + to
+    }
+    to = to.replace(/\/\/+/g, '/')
+
+    return { from: rule.from
+           , method: rule.method
+           , query: rule.query
+           , to: to
+           }
+  })
+}
+
+if (require.main === module) console.log(module.exports)

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/shows.js
----------------------------------------------------------------------
diff --git a/shows.js b/shows.js
new file mode 100644
index 0000000..a812f76
--- /dev/null
+++ b/shows.js
@@ -0,0 +1,131 @@
+
+var shows = exports
+
+shows.package = function (doc, req) {
+  require("monkeypatch").patch(Object, Date, Array, String)
+
+  var semver = require("semver")
+    , code = 200
+    , headers = {"Content-Type":"application/json"}
+    , body = null
+
+  delete doc.ctime
+  delete doc.mtime
+  if (doc.versions) Object.keys(doc.versions).forEach(function (v) {
+    delete doc.versions[v].ctime
+    delete doc.versions[v].mtime
+  })
+
+  // legacy kludge
+  if (doc.versions) for (var v in doc.versions) {
+    var clean = semver.clean(v, true)
+    doc.versions[v].directories = doc.versions[v].directories || {}
+    if (clean !== v) {
+      var p = doc.versions[v]
+      delete doc.versions[v]
+      p.version = v = clean
+      p._id = p.name + '@' + p.version
+      doc.versions[clean] = p
+    }
+    if (doc.versions[v].dist.tarball) {
+      // if there is an attachment for this tarball, then use that.
+      // make it point at THIS registry that is being requested,
+      // with the full /db/_design/app/_rewrite if that is being used,
+      // or just the /name if not.
+
+      var t = doc.versions[v].dist.tarball
+      t = t.replace(/^https?:\/\/[^\/:]+(:[0-9]+)?/, '')
+      var f = t.match(/[^\/]+$/)[0]
+      var requestedPath = req.requested_path
+      if (doc._attachments && doc._attachments[f]) {
+        // workaround for old couch versions that didn't
+        // have requested_path
+        if (requestedPath && -1 === requestedPath.indexOf('show'))
+          requestedPath = requestedPath.slice(0)
+        else {
+          var path = req.path
+          if (path) {
+            var i = path.indexOf('_show')
+            if (i !== -1) {
+              requestedPath = path.slice(0)
+              requestedPath.splice(i, i + 2, '_rewrite')
+            }
+          } else return {
+            code : 500,
+            body : JSON.stringify({error: 'bad couch'}),
+            headers : headers
+          }
+        }
+
+        // doc.versions[v].dist._origTarball = doc.versions[v].dist.tarball
+        // doc.versions[v].dist._headers = req.headers
+        // doc.versions[v].dist._query = req.query
+        // doc.versions[v].dist._reqPath = req.requested_path
+        // doc.versions[v].dist._path = req.path
+        // doc.versions[v].dist._t = t.slice(0)
+
+        // actual location of tarball should always be:
+        // .../_rewrite/pkg/-/pkg-version.tgz
+        // or: /pkg/-/pkg-version.tgz
+        // depending on what requested path is.
+        var tf = [doc.name, '-', t.split('/').pop()]
+        var i = requestedPath.indexOf('_rewrite')
+        if (i !== -1) {
+          tf = requestedPath.slice(0, i + 1).concat(tf)
+        }
+        t = '/' + tf.join('/')
+        var h = "http://" + req.headers.Host
+
+        doc.versions[v].dist.tarball = h + t
+      } else {
+        doc.versions[v].dist.noattachment = true
+      }
+    }
+  }
+  if (doc["dist-tags"]) for (var tag in doc["dist-tags"]) {
+    var clean = semver.clean(doc["dist-tags"][tag], true)
+    if (!clean) delete doc["dist-tags"][tag]
+    else doc["dist-tags"][tag] = clean
+  }
+  // end kludge
+
+  if (req.query.version) {
+    // could be either one!
+    var ver = req.query.version
+    var clean = semver.clean(ver, true)
+
+    if (clean && clean !== ver && (clean in doc.versions))
+      ver = clean
+
+    // if not a valid version, then treat as a tag.
+    if ((!(ver in doc.versions) && (ver in doc["dist-tags"]))
+        || !semver.valid(ver)) {
+      ver = doc["dist-tags"][ver]
+    }
+    body = doc.versions[ver]
+    if (!body) {
+      code = 404
+      body = {"error" : "version not found: "+req.query.version}
+    }
+  } else {
+    body = doc
+    for (var i in body) if (i.charAt(0) === "_" && i !== "_id" && i !== "_rev" && i !== "_attachments") {
+      delete body[i]
+    }
+    for (var i in body.time) {
+      if (!body.versions[i]) delete body.time[i]
+      else body.time[i] = new Date(Date.parse(body.time[i])).toISOString()
+    }
+  }
+
+  body = req.query.jsonp
+       ? req.query.jsonp + "(" + JSON.stringify(body) + ")"
+       : toJSON(body)
+
+  return {
+    code : code,
+    body : body,
+    headers : headers
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/test.js
----------------------------------------------------------------------
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..a75105e
--- /dev/null
+++ b/test.js
@@ -0,0 +1,91 @@
+var http = require("http"),
+    url = require("url"),
+    sys = require("sys");
+
+function request (path, method, headers, body, callback) {
+  if (!headers) {
+    headers = {'Content-Type':'application/json', "Accept":'application/json', "Host":"jsregistry:5984"};
+  }
+  if (!method) {
+    method = "GET"
+  }
+  if (body) {
+    if (typeof body !== "string") {
+      body = JSON.stringify(body)
+    }
+  }
+  
+  var client = http.createClient(5984, "jsregistry");
+  var request = client.request(method, path, headers);  
+  request.addListener("response", function (response) {
+    var buffer = ''
+    response.addListener("data", function (chunk) {
+      buffer += chunk;
+    })
+    response.addListener("end", function () {
+      callback(response, buffer);
+    })
+  })
+  request.write(body)
+  request.close()
+  
+}
+
+function requestQueue (args, doneCallback) {
+  var runner = function (args, i) {
+    var validation = args[i].pop();
+    args[i].push(function (request, response) {
+      validation(request, response);
+      if (i<(args.length - 1)) {
+        runner(args, i + 1)
+      } else if (doneCallback) {
+        doneCallback();
+      }
+    })
+    request.apply(request, args[i])
+  }
+  runner(args, 0);
+}
+
+// Test new module creation
+
+function assertStatus (code) {
+  var c = code;
+  return function (response, body) {
+    if (response.statusCode != c) {
+      sys.puts("Status is not "+c+" it is "+response.statusCode+'. '+body)
+      throw "Status is not "+c+" it is "+response.statusCode+'. '+body;
+    } else {
+      sys.puts(body);
+    }
+  }
+}
+
+// request('/foo', "PUT", undefined, {id:"foo", description:"new module"}, function () {sys.puts('done')})
+
+var sha = require('./deps/sha1'),
+    base64 = require('./deps/base64');
+    
+var userDoc = {_id:"org.couchdb.user:testuser", 
+               name:"testuser", 
+               password_sha: sha.hex_sha1('testing' + 'pepper'), 
+               salt:"pepper", type:"user", roles:[]}
+var auth = {'Content-Type':'application/json', 
+            accept:'application/json', 
+            authorization:'Basic ' + base64.encode('testuser:testing'),
+            host:"jsregistry:5984"}
+
+requestQueue([
+  ["/adduser/org.couchdb.user:testuser", "PUT", undefined, userDoc, assertStatus(201)],
+  // ["/session", "POST", undefined, "user=testuser&password=testing", function (response, body) {
+  //   sys.puts('s', body, 'd')
+  //   auth.cookie = response.headers['set-cookie'];
+  // }],
+  ["/foo", "PUT", auth, {_id:"foo", description:"new module"}, assertStatus(201)],
+  ["/foo/0.1.0", "PUT", auth, 
+    {_id:"foo", description:"new module", dist:{tarball:"http://path/to/tarball"}}, assertStatus(201)],
+  ["/foo/stable", "PUT", auth, "0.1.0", assertStatus(201)],
+  ["/foo", "GET", undefined, "0.1.0", assertStatus(200)],
+  ["/foo/0.1.0", "GET", undefined, "0.1.0", assertStatus(200)],
+  ["/foo/stable", "GET", undefined, "0.1.0", assertStatus(200)],
+  ], function () {sys.puts('done')})

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/updates.js
----------------------------------------------------------------------
diff --git a/updates.js b/updates.js
new file mode 100644
index 0000000..1d6674d
--- /dev/null
+++ b/updates.js
@@ -0,0 +1,132 @@
+var updates = exports
+
+updates.package = function (doc, req) {
+  require("monkeypatch").patch(Object, Date, Array, String)
+
+  var semver = require("semver")
+  var valid = require("valid")
+  function error (reason) {
+    return [{_id: "error: forbidden", forbidden:reason}, JSON.stringify({forbidden:reason})]
+  }
+
+  function ok (doc, message) {
+    delete doc.mtime
+    delete doc.ctime
+    var time = doc.time = doc.time || {}
+    time.modified = (new Date()).toISOString()
+    time.created = time.created || time.modified
+    for (var v in doc.versions) {
+      var ver = doc.versions[v]
+      delete ver.ctime
+      delete ver.mtime
+      time[v] = time[v] || (new Date()).toISOString()
+    }
+    return [doc, JSON.stringify({ok:message})]
+  }
+
+  if (doc) {
+    if (req.query.version) {
+      var parsed = semver.valid(req.query.version, true)
+      if (!parsed) {
+        // it's a tag.
+        var tag = req.query.version
+          , ver = JSON.parse(req.body)
+        if (!semver.valid(ver)) {
+          return error("setting tag "+tag+" to invalid version: "+req.body)
+        }
+        doc["dist-tags"][tag] = semver.clean(ver, true)
+        return ok(doc, "updated tag")
+      }
+      // adding a new version.
+      var ver = req.query.version
+      if (!semver.valid(ver, true)) {
+        return error("invalid version: "+ver)
+      }
+
+      if ((ver in doc.versions) || (semver.clean(ver) in doc.versions)) {
+        // attempting to overwrite an existing version.
+        // not supported at this time.
+        if (!req.query.rev || req.query.rev !== doc._rev) {
+          return error("cannot modify existing version")
+        }
+      }
+
+      var body = JSON.parse(req.body)
+      if (!valid.name(body.name)) {
+        return error( "Invalid name: "+JSON.stringify(body.name))
+      }
+      body.version = semver.clean(body.version, true)
+      ver = semver.clean(ver, true)
+      if (body.version !== ver) {
+        return error( "version in doc doesn't match version in request: "
+                    + JSON.stringify(body.version)
+                    + " !== " + JSON.stringify(ver) )
+      }
+      body._id = body.name + "@" + body.version
+      if (body.description) doc.description = body.description
+      if (body.author) doc.author = body.author
+      if (body.repository) doc.repository = body.repository
+      body.maintainers = doc.maintainers
+
+      if (body.publishConfig && typeof body.publishConfig === 'object') {
+        Object.keys(body.publishConfig).filter(function (k) {
+          return k.match(/^_/)
+        }).forEach(function (k) {
+          delete body.publishConfig[k]
+        })
+      }
+
+      var tag = req.query.tag
+              || (body.publishConfig && body.publishConfig.tag)
+              || body.tag
+              || "latest"
+
+      if (!req.query.pre)
+        doc["dist-tags"][tag] = body.version
+      if (!doc["dist-tags"].latest)
+        doc["dist-tags"].latest = body.version
+      doc.versions[ver] = body
+      doc.time = doc.time || {}
+      doc.time[ver] = (new Date()).toISOString()
+      return ok(doc, "added version")
+    }
+
+    // update the package info
+    var newdoc = JSON.parse(req.body)
+      , changed = false
+    if (doc._rev && doc._rev !== newdoc._rev) {
+      return error( "must supply latest _rev to update existing package" )
+    }
+    for (var i in newdoc) if (typeof newdoc[i] === "string" || i === "maintainers") {
+      doc[i] = newdoc[i]
+    }
+    if (newdoc.versions) {
+      doc.versions = newdoc.versions
+    }
+    if (newdoc["dist-tags"]) {
+      doc["dist-tags"] = newdoc["dist-tags"]
+    }
+    if (newdoc.users) {
+      if (!doc.users) doc.users = {}
+      doc.users[req.userCtx.name] = newdoc.users[req.userCtx.name]
+    }
+    return ok(doc, "updated package metadata")
+  } else {
+    // Create new package doc
+    doc = JSON.parse(req.body)
+    if (!doc._id) doc._id = doc.name
+    if (!doc.versions) doc.versions = {}
+    var latest
+    for (var v in doc.versions) {
+      if (!semver.valid(v, true)) return error("Invalid version: "+JSON.stringify(v))
+      var p = doc.versions[v]
+      if (p.version !== v) return error("Version mismatch: "+JSON.stringify(v)
+                                       +" !== "+JSON.stringify(p.version))
+      if (!valid.name(p.name)) return error("Invalid name: "+JSON.stringify(p.name))
+      latest = semver.clean(v, true)
+    }
+    if (!doc['dist-tags']) doc['dist-tags'] = {}
+    if (latest) doc["dist-tags"].latest = latest
+    return ok(doc, "created new entry")
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/validate_doc_update.js
----------------------------------------------------------------------
diff --git a/validate_doc_update.js b/validate_doc_update.js
new file mode 100644
index 0000000..8407174
--- /dev/null
+++ b/validate_doc_update.js
@@ -0,0 +1,353 @@
+module.exports = function (doc, oldDoc, user, dbCtx) {
+  function assert (ok, message) {
+    if (!ok) throw {forbidden:message}
+  }
+
+  // can't write to the db without logging in.
+  if (!user) {
+    throw { unauthorized: "Please log in before writing to the db" }
+  }
+
+  try {
+    require("monkeypatch").patch(Object, Date, Array, String)
+  } catch (er) {
+    assert(false, "failed monkeypatching")
+  }
+
+  try {
+    var semver = require("semver")
+    var valid = require("valid")
+    var deep = require("deep")
+    var deepEquals = deep.deepEquals
+  } catch (er) {
+    assert(false, "failed loading modules")
+  }
+
+  try {
+    if (oldDoc) oldDoc.users = oldDoc.users || {}
+    doc.users = doc.users || {}
+  } catch (er) {
+    assert(false, "failed checking users")
+  }
+
+
+  // admins can do ANYTHING (even break stuff)
+  try {
+    if (isAdmin()) return
+  } catch (er) {
+    assert(false, "failed checking admin-ness")
+  }
+
+  // figure out what changed in the doc.
+  function diffObj (o, n, p) {
+    p = p || ""
+    var d = []
+    var seenKeys = []
+
+    for (var i in o) {
+      seenKeys.push(i)
+      if (n[i] === undefined) {
+        d.push("Deleted: "+p+i)
+      }
+      else if (typeof o[i] !== typeof n[i]) {
+        d.push("Changed Type: "+p+i)
+      }
+      else if (typeof o[i] === "object") {
+        if (o[i]) {
+          if (n[i]) {
+            d = d.concat(diffObj(o[i], n[i], p + i + "."))
+          } else {
+            d.push("Nulled: "+p+i)
+          }
+        } else {
+          if (n[i]) {
+            d.push("Un-nulled: "+p+i)
+          } else {
+            // they're both null, and thus equal.  do nothing.
+          }
+        }
+      }
+      // non-object, non-null
+      else if (o[i] !== n[i]) {
+          d.push("Changed: "+p+i+" "+JSON.stringify(o[i]) + " -> "
+                 +JSON.stringify(n[i]))
+      }
+    }
+
+    for (var i in n) {
+      if (-1 === seenKeys.indexOf(i)) {
+        d.push("Added: "+p+i)
+      }
+    }
+    return d
+  }
+
+  // if the doc is an {error:"blerg"}, then throw that right out.
+  // something detected in the _updates/package script.
+  // XXX: Make this not ever happen ever.  Validation belongs here,
+  // not in the update function.
+  try {
+    assert(!doc.forbidden || doc._deleted, doc.forbidden)
+  } catch (er) {
+    assert(false, "failed checking doc.forbidden or doc._deleted")
+  }
+
+  // everyone may alter his "starred" status on any package
+  try {
+    if (oldDoc &&
+        !doc._deleted &&
+        deepEquals(doc, oldDoc,
+                   [["users", user.name], ["time", "modified"]])) {
+      return
+    }
+  } catch (er) {
+    assert(false, "failed checking starred stuff")
+  }
+
+
+  // check if the user is allowed to write to this package.
+  function validUser () {
+    if ( !oldDoc || !oldDoc.maintainers ) return true
+    if (isAdmin()) return true
+    if (typeof oldDoc.maintainers !== "object") return true
+    for (var i = 0, l = oldDoc.maintainers.length; i < l; i ++) {
+      if (oldDoc.maintainers[i].name === user.name) return true
+    }
+    return false
+  }
+
+  function isAdmin () {
+    if (dbCtx &&
+        dbCtx.admins) {
+      if (dbCtx.admins.names &&
+          dbCtx.admins.roles &&
+          dbCtx.admins.names.indexOf(user.name) !== -1) return true
+      for (var i=0;i<user.roles.length;i++) {
+        if (dbCtx.admins.roles.indexOf(user.roles[i]) !== -1) return true
+      }
+    }
+    return user && user.roles.indexOf("_admin") >= 0
+  }
+
+  try {
+    var vu = validUser()
+  } catch (er) {
+    assert(false, "problem checking user validity");
+  }
+
+  if (!vu) {
+    assert(vu, "user: " + user.name + " not authorized to modify "
+                        + oldDoc.name + "\n"
+                        + diffObj(oldDoc, doc).join("\n"))
+  }
+
+  // deleting a document entirely *is* allowed.
+  if (doc._deleted) return
+
+  // sanity checks.
+  assert(valid.name(doc.name), "name invalid: "+doc.name)
+
+  // New documents may only be created with all lowercase names.
+  // At some point, existing docs will be migrated to lowercase names
+  // as well.
+  if (!oldDoc && doc.name !== doc.name.toLowerCase()) {
+    assert(false, "New packages must have all-lowercase names")
+  }
+
+  assert(doc.name === doc._id, "name must match _id")
+  assert(!doc.mtime, "doc.mtime is deprecated")
+  assert(!doc.ctime, "doc.ctime is deprecated")
+  assert(typeof doc.time === "object", "time must be object")
+
+  assert(typeof doc["dist-tags"] === "object", "dist-tags must be object")
+
+  var versions = doc.versions
+  assert(typeof versions === "object", "versions must be object")
+
+  var latest = doc["dist-tags"].latest
+  if (latest) {
+    assert(versions[latest], "dist-tags.latest must be valid version")
+  }
+
+  // the 'latest' version must have a dist and shasum
+  // I'd like to also require this of all past versions, but that
+  // means going back and cleaning up about 2000 old package versions,
+  // or else *new* versions of those packages can't be published.
+  // Until that time, do this instead:
+  var version = versions[latest]
+  if (version) {
+    if (!version.dist)
+      assert(false, "no dist object in " + latest + " version")
+    if (!version.dist.tarball)
+      assert(false, "no tarball in " + latest + " version")
+    if (!version.dist.shasum)
+      assert(false, "no shasum in " + latest + " version")
+  }
+
+  for (var v in doc["dist-tags"]) {
+    var ver = doc["dist-tags"][v]
+    assert(semver.valid(ver, true),
+           v + " version invalid version: " + ver)
+    assert(versions[ver],
+           v + " version missing: " + ver)
+  }
+
+  var depCount = 0
+  var maxDeps = 5000
+  function ridiculousDeps() {
+    if (++depCount > maxDeps)
+      assert(false, "too many deps.  please be less ridiculous.")
+  }
+  for (var ver in versions) {
+    var version = versions[ver]
+    assert(semver.valid(ver, true),
+           "invalid version: " + ver)
+    assert(typeof version === "object",
+           "version entries must be objects")
+    assert(version.version === ver,
+           "version must match: "+ver)
+    assert(version.name === doc._id,
+           "version "+ver+" has incorrect name: "+version.name)
+
+    depCount = 0
+    for (var dep in version.dependencies || {}) ridiculousDeps()
+    for (var dep in version.devDependencies || {}) ridiculousDeps()
+    for (var dep in version.optionalDependencies || {}) ridiculousDeps()
+  }
+
+  assert(Array.isArray(doc.maintainers),
+         "maintainers should be a list of owners")
+  doc.maintainers.forEach(function (m) {
+    assert(m.name && m.email,
+           "Maintainer should have name and email: " + JSON.stringify(m))
+  })
+
+  var time = doc.time
+  var c = new Date(Date.parse(time.created))
+    , m = new Date(Date.parse(time.modified))
+  assert(c.toString() !== "Invalid Date",
+         "invalid created time: " + JSON.stringify(time.created))
+
+  assert(m.toString() !== "Invalid Date",
+         "invalid modified time: " + JSON.stringify(time.modified))
+
+  if (oldDoc &&
+      oldDoc.time &&
+      oldDoc.time.created &&
+      Date.parse(oldDoc.time.created)) {
+    assert(Date.parse(oldDoc.time.created) === Date.parse(time.created),
+           "created time cannot be changed")
+  }
+
+  if (oldDoc && oldDoc.users) {
+    assert(deepEquals(doc.users,
+                      oldDoc.users, [[user.name]]),
+           "you may only alter your own 'star' setting")
+  }
+
+  if (doc.url) {
+    assert(false,
+           "Package redirection has been removed. "+
+           "Please update your publish scripts.")
+  }
+
+  if (doc.description) {
+    assert(typeof doc.description === 'string',
+           '"description" field must be a string')
+  }
+
+  // at this point, we've passed the basic sanity tests.
+  // Time to dig into more details.
+  // Valid operations:
+  // 1. Add a version
+  // 2. Remove a version
+  // 3. Modify a version
+  // 4. Add or remove onesself from the "users" hash (already done)
+  //
+  // If a version is being added or changed, make sure that the
+  // _npmUser field matches the current user, and that the
+  // time object has the proper entry, and that the "maintainers"
+  // matches the current "maintainers" field.
+  //
+  // Things that must not change:
+  //
+  // 1. More than one version being modified.
+  // 2. Removing keys from the "time" hash
+  //
+  // Later, once we are off of the update function 3-stage approach,
+  // these things should also be errors:
+  //
+  // 1. Lacking an attachment for any published version.
+  // 2. Having an attachment for any version not published.
+
+  var oldVersions = oldDoc ? oldDoc.versions || {} : {}
+  var oldTime = oldDoc ? oldDoc.time || {} : {}
+
+  var versions = Object.keys(doc.versions)
+    , modified = null
+
+  for (var i = 0, l = versions.length; i < l; i ++) {
+    var v = versions[i]
+    if (!v) continue
+    assert(doc.time[v], "must have time entry for "+v)
+
+    if (!deepEquals(doc.versions[v], oldVersions[v], [["directories"], ["deprecated"]]) &&
+        doc.versions[v]) {
+      // this one was modified
+      // if it's more than a few minutes off, then something is wrong.
+      var t = Date.parse(doc.time[v])
+        , n = Date.now()
+      // assert(doc.time[v] !== oldTime[v] &&
+      //        Math.abs(n - t) < 1000 * 60 * 60,
+      //        v + " time needs to be updated\n" +
+      //        "new=" + JSON.stringify(doc.versions[v]) + "\n" +
+      //        "old=" + JSON.stringify(oldVersions[v]))
+
+      // var mt = Date.parse(doc.time.modified).getTime()
+      //   , vt = t.getTime()
+      // assert(Math.abs(mt - vt) < 1000 * 60 * 60,
+      //        v + " is modified, should match modified time")
+
+      // XXX Remove the guard these once old docs have been found and
+      // fixed.  It's too big of a pain to have to manually fix
+      // each one every time someone complains.
+      if (typeof doc.versions[v]._npmUser !== "object") continue
+
+
+      assert(typeof doc.versions[v]._npmUser === "object",
+             "_npmUser field must be object\n"+
+             "(You probably need to upgrade your npm version)")
+      assert(doc.versions[v]._npmUser.name === user.name,
+             "_npmUser.name must === user.name")
+      assert(deepEquals(doc.versions[v].maintainers,
+                        doc.maintainers),
+             "modified version 'maintainers' must === doc.maintainers")
+
+      // make sure that the _npmUser is one of the maintainers
+      var found = false
+      for (var j = 0, lm = doc.maintainers.length; j < lm; j ++) {
+        var m = doc.maintainers[j]
+        if (m.name === doc.versions[v]._npmUser.name) {
+          found = true
+          break
+        }
+      }
+      assert(found, "_npmUser must be a current maintainer.\n"+
+                    "maintainers=" + JSON.stringify(doc.maintainers)+"\n"+
+                    "current user=" + JSON.stringify(doc.versions[v]._npmUser))
+
+    } else if (oldTime[v]) {
+      assert(oldTime[v] === doc.time[v],
+             v + " time should not be modified 1")
+    }
+  }
+
+  // now go through all the time settings that weren't covered
+  for (var v in oldTime) {
+    if (doc.versions[v] || !oldVersions[v]) continue
+    assert(doc.time[v] === oldTime[v],
+           v + " time should not be modified 2")
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-registry/blob/9166301a/views.js
----------------------------------------------------------------------
diff --git a/views.js b/views.js
new file mode 100644
index 0000000..2a7af5c
--- /dev/null
+++ b/views.js
@@ -0,0 +1,480 @@
+
+var views = module.exports = exports = {}
+
+views.noCDN = { map: function (doc) {
+  if (!doc.versions || Object.keys(doc.versions).length === 0)
+    return
+  Object.keys(doc.versions).forEach(function(v) {
+    if (doc.versions[v].dist.cdn)
+      return
+    emit([doc._id, v], 1)
+  })
+}, reduce: "_sum" }
+
+views.updated = {map: function (doc) {
+  var l = doc["dist-tags"].latest
+    , t = doc.time && doc.time[l]
+  if (t) emit(t, 1)
+}}
+
+views.listAll = {
+  map : function (doc) { return emit(doc._id, doc) }
+}
+
+views.allVersions = { map: function(doc) {
+  if (!doc || !doc.versions)
+    return
+  for (var i in doc.versions)
+    emit([i, doc._id], 1)
+}, reduce: "_sum" }
+
+views.modified = { map: modifiedTimeMap }
+function modifiedTimeMap (doc) {
+  if (!doc.versions || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var latest = doc["dist-tags"].latest
+  if (!doc.versions[latest]) return
+  var time = doc.time && doc.time[latest] || 0
+  var t = new Date(time)
+  emit(t.getTime(), doc)
+}
+
+views.modifiedPackage = { map: function (doc) {
+  if (!doc.versions || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var latest = doc["dist-tags"].latest
+  if (!doc.versions[latest]) return
+  var time = doc.time && doc.time[latest] || 0
+  var t = new Date(time)
+  emit([doc._id, t.getTime()], doc)
+}}
+
+
+views.noShasum = { map: function (doc) {
+  if (!doc || !doc.versions)
+    return
+
+  for (var ver in doc.versions) {
+    var version = doc.versions[ver]
+    if (!version || !version.dist || !version.dist.shasum) {
+      emit([doc.name, ver, !!version, !!version.dist, !!version.shasum], 1)
+    }
+  }
+}, reduce: "_sum" }
+
+views.byEngine = {
+  map: function (doc) {
+    if (!doc || !doc.versions || !doc["dist-tags"] || doc.deprecated) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var v = doc["dist-tags"].latest
+    var d = doc.versions[v]
+    if (d && d.engines) emit(doc._id, [d.engines, doc.maintainers])
+  }
+}
+
+views.countVersions = { map: function (doc) {
+  if (!doc || !doc.name || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var i = 0
+  if (!doc.versions) return emit([i, doc._id], 1)
+  for (var v in doc.versions) i++
+  emit([i, doc._id], 1)
+}, reduce: "_sum"}
+
+views.byKeyword = {
+  map: function (doc) {
+    if (!doc || !doc.versions || !doc['dist-tags'] || doc.deprecated) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var v = doc.versions[doc['dist-tags'].latest]
+    if (!v || !v.keywords || !Array.isArray(v.keywords)) return
+    v.keywords.forEach(function (kw) {
+      emit([kw.toLowerCase(), doc.name, doc.description], 1)
+    })
+  }, reduce: "_sum"
+}
+
+
+views.byField = {
+  map: function (doc) {
+    if (!doc || !doc.versions || !doc["dist-tags"]) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var v = doc["dist-tags"].latest
+    //Object.keys(doc.versions).forEach(function (v) {
+      var d = doc.versions[v]
+      if (!d) return
+      //emit(d.name + "@" + d.version, d.dist.bin || {})
+      var out = {}
+      for (var i in d) {
+        out[i] = d[i] //true
+        if (d[i] && typeof d[i] === "object" &&
+            (i === "scripts" || i === "directories")) {
+          for (var j in d[i]) out[i + "." + j] = d[i][j]
+        }
+      }
+      out.maintainers = doc.maintainers
+      emit(doc._id, out)
+    //})
+  }
+}
+
+views.needBuild = {
+  map : function (doc) {
+
+    if (!doc || !doc.versions || !doc["dist-tags"]) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var v = doc["dist-tags"].latest
+    //Object.keys(doc.versions).forEach(function (v) {
+      var d = doc.versions[v]
+      if (!d) return
+      if (!d.scripts) return
+      var inst =  d.scripts.install
+               || d.scripts.preinstall
+               || d.scripts.postinstall
+      if (!inst) return
+      //emit(d.name + "@" + d.version, d.dist.bin || {})
+      emit(d._id, d.dist.bin || {})
+    //})
+  }
+}
+
+views.scripts = {
+  map : function (doc) {
+    if (!doc || !doc.versions || !doc["dist-tags"]) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var v = doc["dist-tags"].latest
+    v = doc.versions[v]
+    if (!v || !v.scripts) return
+    var out = {}
+    var any = false
+    for (var i in v.scripts) {
+      out[i] = v.scripts[i]
+      any = true
+    }
+    if (!any) return
+    out.maintainers = doc.maintainers
+    emit(doc._id, out)
+  }
+}
+
+views.nodeWafInstall = {
+  map : function (doc) {
+    if (!doc || !doc.versions || !doc["dist-tags"]) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var v = doc["dist-tags"].latest
+    if (!doc.versions[v]) return
+    if (!doc.versions[v].scripts) return
+    for (var i in doc.versions[v].scripts) {
+      if (doc.versions[v].scripts[i].indexOf("node-waf") !== -1 ||
+          doc.versions[v].scripts[i].indexOf("make") !== -1) {
+        emit(doc._id, doc.versions[v]._id)
+        return
+      }
+    }
+  }
+}
+
+views.badBins = {
+  map : function (doc) {
+    if (!doc || !doc.versions || !doc["dist-tags"]) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var v = doc["dist-tags"].latest
+    if (!doc.versions[v]) return
+    v = doc.versions[v]
+    var b = v.bin
+      , d = v.directories && v.directories.bin
+    if (!b && !d) return
+    if (b && (typeof b === "string" || Object.keys(b).length === 1)) {
+      // it's ok.
+      return
+    }
+    emit(doc._id, {binHash:b, binDir:d})
+  }
+}
+
+
+views.orphanAttachments = {
+  map : function (doc) {
+    if (!doc || !doc._attachments) return
+    var orphans = []
+      , size = 0
+    for (var i in doc._attachments) {
+      var n = i.substr(doc._id.length + 1).replace(/\.tgz$/, "")
+               .replace(/^v/, "")
+      if (!doc.versions[n] && i.match(/\.tgz$/)) {
+        orphans.push(i)
+        size += doc._attachments[i].length
+      }
+    }
+    if (orphans.length) emit(doc._id, {size:size, orphans:orphans})
+  }
+}
+
+views.starredByUser = { map : function (doc) {
+  if (!doc || !doc.users) return
+  if (doc._id.match(/^npm-test-.+$/) && doc.maintainers[0].name === 'isaacs')
+    return
+  Object.keys(doc.users).forEach(function (m) {
+    if (!doc.users[m]) return
+    emit(m, doc._id)
+  })
+}}
+
+views.starredByPackage = { map : function (doc) {
+  if (!doc || !doc.users) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  Object.keys(doc.users).forEach(function (m) {
+    if (!doc.users[m]) return
+    emit(doc._id, m)
+  })
+}}
+
+views.byUser = { map : function (doc) {
+  if (!doc || !doc.maintainers) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  doc.maintainers.forEach(function (m) {
+    emit(m.name, doc._id)
+  })
+}}
+
+
+
+views.browseAuthorsRecent = { map: function (doc) {
+  if (!doc || !doc.maintainers || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  l = l && doc.versions && doc.versions[l]
+  if (!l) return
+  var t = doc.time && doc.time[l.version]
+  if (!t) return
+  var desc = doc.description || l.description || ''
+  var readme = doc.readme || l.readme || ''
+  doc.maintainers.forEach(function (m) {
+    // Have to sum it up by the author name in the app.
+    // couchdb makes me sad sometimes.
+    emit([t, m.name, doc._id, desc, readme], 1)
+  })
+}, reduce: "_sum" }
+
+views.browseAuthors = views.npmTop = { map: function (doc) {
+  if (!doc || !doc.maintainers || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  l = l && doc.versions && doc.versions[l]
+  if (!l) return
+  var t = doc.time && doc.time[l.version]
+  if (!t) return
+  var desc = doc.description || l.description || ''
+  var readme = doc.readme || l.readme || ''
+  doc.maintainers.forEach(function (m) {
+    emit([m.name, doc._id, desc, t, readme], 1)
+  })
+}, reduce: "_sum" }
+
+views.browseUpdated = { map: function (doc) {
+  if (!doc || !doc.versions || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  if (!l) return
+  var t = doc.time && doc.time[l]
+  if (!t) return
+  var v = doc.versions[l]
+  if (!v) return
+  var d = new Date(t)
+  if (!d.getTime()) return
+  emit([ d.toISOString(),
+         doc._id,
+         v.description,
+         v.readme ], 1)
+}, reduce: "_sum" }
+
+views.browseAll = { map: function (doc) {
+  if (!doc || !doc.versions || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  if (!l) return
+  l = doc.versions && doc.versions[l]
+  if (!l) return
+  var desc = doc.description || l.description || ''
+  var readme = doc.readme || l.readme || ''
+  emit([doc.name, desc, readme], 1)
+}, reduce: '_sum' }
+
+views.analytics = { map: function (doc) {
+  if (!doc || !doc.time || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  for (var i in doc.time) {
+    var t = doc.time[i]
+    var d = new Date(t)
+    if (!d.getTime()) return
+    var type = i === 'modified' ? 'latest'
+             : i === 'created' ? 'created'
+             : 'update'
+    emit([ type,
+           d.getUTCFullYear(),
+           d.getUTCMonth() + 1,
+           d.getUTCDate(),
+           doc._id ], 1)
+  }
+}, reduce: '_sum' }
+
+views.dependedUpon = { map: function (doc) {
+  if (!doc || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  if (!l) return
+  l = doc.versions && doc.versions[l]
+  if (!l) return
+  var desc = doc.description || l.description || ''
+  var readme = doc.readme || l.readme || ''
+  var d = l.dependencies
+  if (!d) return
+  for (var dep in d) {
+    emit([dep, doc._id, desc, readme], 1)
+  }
+}, reduce: '_sum' }
+
+views.dependentVersions = { map: function (doc) {
+  if (!doc || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  if (!l) return
+  l = doc.versions && doc.versions[l]
+  if (!l) return
+  var deps = l.dependencies
+  if (!deps) return
+  for (var dep in deps)
+    emit([dep, deps[dep], doc._id], 1)
+}, reduce: '_sum' }
+
+views.browseStarUser = { map: function (doc) {
+  if (!doc) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  if (!l) return
+  l = doc.versions && doc.versions[l]
+  if (!l) return
+  var desc = doc.description || l.description || ''
+  var readme = doc.readme || l.readme || ''
+  var d = doc.users
+  if (!d) return
+  for (var user in d) {
+    emit([user, doc._id, desc, readme], 1)
+  }
+}, reduce: '_sum' }
+
+views.browseStarPackage = { map: function (doc) {
+  if (!doc || doc.deprecated) return
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var l = doc['dist-tags'] && doc['dist-tags'].latest
+  if (!l) return
+  l = doc.versions && doc.versions[l]
+  if (!l) return
+  var desc = doc.description || l.description || ''
+  var readme = doc.readme || l.readme || ''
+  var d = doc.users
+  if (!d) return
+  for (var user in d) {
+    emit([doc._id, desc, user, readme], 1)
+  }
+}, reduce: '_sum' }
+
+
+views.fieldsInUse = { map : function (doc) {
+  if (!doc.versions || !doc["dist-tags"] || !doc["dist-tags"].latest || doc.deprecated) {
+    return
+  }
+  if (doc._id.match(/^npm-test-.+$/) &&
+      doc.maintainers &&
+      doc.maintainers[0].name === 'isaacs')
+    return
+  var d = doc.versions[doc["dist-tags"].latest]
+  if (!d) return
+  for (var f in d) {
+    emit(f, 1)
+    if (d[f] && typeof d[f] === "object" &&
+        (f === "scripts" || f === "directories")) {
+      for (var i in d[f]) emit(f+"."+i, 1)
+    }
+  }
+} , reduce : "_sum" }
+
+views.howBigIsYourPackage = {
+  map : function (doc) {
+    if (!doc) return
+    if (doc._id.match(/^npm-test-.+$/) &&
+        doc.maintainers &&
+        doc.maintainers[0].name === 'isaacs')
+      return
+    var s = 0
+      , c = 0
+    for (var i in doc._attachments) {
+      s += doc._attachments[i].length
+      c ++
+    }
+    if (s === 0) return
+    emit(doc._id, {_id: doc._id, size: s, count: c, avg: s/c})
+  }
+}


Mime
View raw message