You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ti...@apache.org on 2015/10/12 20:59:56 UTC

[10/35] cordova-browser git commit: Update to use new 'express' implementation of cordova-serve.

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/test/ipaddr.test.coffee
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/test/ipaddr.test.coffee b/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/test/ipaddr.test.coffee
new file mode 100644
index 0000000..361561e
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/test/ipaddr.test.coffee
@@ -0,0 +1,262 @@
+ipaddr = require '../lib/ipaddr'
+
+module.exports =
+  'should define main classes': (test) ->
+    test.ok(ipaddr.IPv4?, 'defines IPv4 class')
+    test.ok(ipaddr.IPv6?, 'defines IPv6 class')
+    test.done()
+
+  'can construct IPv4 from octets': (test) ->
+    test.doesNotThrow ->
+      new ipaddr.IPv4([192, 168, 1, 2])
+    test.done()
+
+  'refuses to construct invalid IPv4': (test) ->
+    test.throws ->
+      new ipaddr.IPv4([300, 1, 2, 3])
+    test.throws ->
+      new ipaddr.IPv4([8, 8, 8])
+    test.done()
+
+  'converts IPv4 to string correctly': (test) ->
+    addr = new ipaddr.IPv4([192, 168, 1, 1])
+    test.equal(addr.toString(), '192.168.1.1')
+    test.done()
+
+  'returns correct kind for IPv4': (test) ->
+    addr = new ipaddr.IPv4([1, 2, 3, 4])
+    test.equal(addr.kind(), 'ipv4')
+    test.done()
+
+  'allows to access IPv4 octets': (test) ->
+    addr = new ipaddr.IPv4([42, 0, 0, 0])
+    test.equal(addr.octets[0], 42)
+    test.done()
+
+  'checks IPv4 address format': (test) ->
+    test.equal(ipaddr.IPv4.isIPv4('192.168.007.0xa'), true)
+    test.equal(ipaddr.IPv4.isIPv4('1024.0.0.1'),      true)
+    test.equal(ipaddr.IPv4.isIPv4('8.0xa.wtf.6'),     false)
+    test.done()
+
+  'validates IPv4 addresses': (test) ->
+    test.equal(ipaddr.IPv4.isValid('192.168.007.0xa'), true)
+    test.equal(ipaddr.IPv4.isValid('1024.0.0.1'),      false)
+    test.equal(ipaddr.IPv4.isValid('8.0xa.wtf.6'),     false)
+    test.done()
+
+  'parses IPv4 in several weird formats': (test) ->
+    test.deepEqual(ipaddr.IPv4.parse('192.168.1.1').octets,  [192, 168, 1, 1])
+    test.deepEqual(ipaddr.IPv4.parse('0xc0.168.1.1').octets, [192, 168, 1, 1])
+    test.deepEqual(ipaddr.IPv4.parse('192.0250.1.1').octets, [192, 168, 1, 1])
+    test.deepEqual(ipaddr.IPv4.parse('0xc0a80101').octets,   [192, 168, 1, 1])
+    test.deepEqual(ipaddr.IPv4.parse('030052000401').octets, [192, 168, 1, 1])
+    test.deepEqual(ipaddr.IPv4.parse('3232235777').octets,   [192, 168, 1, 1])
+    test.done()
+
+  'barfs at invalid IPv4': (test) ->
+    test.throws ->
+      ipaddr.IPv4.parse('10.0.0.wtf')
+    test.done()
+
+  'matches IPv4 CIDR correctly': (test) ->
+    addr = new ipaddr.IPv4([10, 5, 0, 1])
+    test.equal(addr.match(ipaddr.IPv4.parse('0.0.0.0'), 0),   true)
+    test.equal(addr.match(ipaddr.IPv4.parse('11.0.0.0'), 8),  false)
+    test.equal(addr.match(ipaddr.IPv4.parse('10.0.0.0'), 8),  true)
+    test.equal(addr.match(ipaddr.IPv4.parse('10.0.0.1'), 8),  true)
+    test.equal(addr.match(ipaddr.IPv4.parse('10.0.0.10'), 8), true)
+    test.equal(addr.match(ipaddr.IPv4.parse('10.5.5.0'), 16), true)
+    test.equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 16), false)
+    test.equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 15), true)
+    test.equal(addr.match(ipaddr.IPv4.parse('10.5.0.2'), 32), false)
+    test.equal(addr.match(addr, 32), true)
+    test.done()
+
+  'parses IPv4 CIDR correctly': (test) ->
+    addr = new ipaddr.IPv4([10, 5, 0, 1])
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('0.0.0.0/0')),   true)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('11.0.0.0/8')),  false)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.0/8')),  true)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.1/8')),  true)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.10/8')), true)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.5.0/16')), true)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/16')), false)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/15')), true)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.2/32')), false)
+    test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.1/32')), true)
+    test.throws ->
+      ipaddr.IPv4.parseCIDR('10.5.0.1')
+    test.done()
+
+  'detects reserved IPv4 networks': (test) ->
+    test.equal(ipaddr.IPv4.parse('0.0.0.0').range(),         'unspecified')
+    test.equal(ipaddr.IPv4.parse('0.1.0.0').range(),         'unspecified')
+    test.equal(ipaddr.IPv4.parse('10.1.0.1').range(),        'private')
+    test.equal(ipaddr.IPv4.parse('192.168.2.1').range(),     'private')
+    test.equal(ipaddr.IPv4.parse('224.100.0.1').range(),     'multicast')
+    test.equal(ipaddr.IPv4.parse('169.254.15.0').range(),    'linkLocal')
+    test.equal(ipaddr.IPv4.parse('127.1.1.1').range(),       'loopback')
+    test.equal(ipaddr.IPv4.parse('255.255.255.255').range(), 'broadcast')
+    test.equal(ipaddr.IPv4.parse('240.1.2.3').range(),       'reserved')
+    test.equal(ipaddr.IPv4.parse('8.8.8.8').range(),         'unicast')
+    test.done()
+
+  'can construct IPv6 from parts': (test) ->
+    test.doesNotThrow ->
+      new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1])
+    test.done()
+
+  'refuses to construct invalid IPv6': (test) ->
+    test.throws ->
+      new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 0, 1])
+    test.throws ->
+      new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 1])
+    test.done()
+
+  'converts IPv6 to string correctly': (test) ->
+    addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1])
+    test.equal(addr.toNormalizedString(), '2001:db8:f53a:0:0:0:0:1')
+    test.equal(addr.toString(), '2001:db8:f53a::1')
+    test.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toString(), '::1')
+    test.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toString(), '2001:db8::')
+    test.done()
+
+  'returns correct kind for IPv6': (test) ->
+    addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1])
+    test.equal(addr.kind(), 'ipv6')
+    test.done()
+
+  'allows to access IPv6 address parts': (test) ->
+    addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 42, 0, 1])
+    test.equal(addr.parts[5], 42)
+    test.done()
+
+  'checks IPv6 address format': (test) ->
+    test.equal(ipaddr.IPv6.isIPv6('2001:db8:F53A::1'),     true)
+    test.equal(ipaddr.IPv6.isIPv6('200001::1'),            true)
+    test.equal(ipaddr.IPv6.isIPv6('::ffff:192.168.1.1'),   true)
+    test.equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1'),   true)
+    test.equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1:0'), false)
+    test.equal(ipaddr.IPv6.isIPv6('fe80::wtf'),            false)
+    test.done()
+
+  'validates IPv6 addresses': (test) ->
+    test.equal(ipaddr.IPv6.isValid('2001:db8:F53A::1'),    true)
+    test.equal(ipaddr.IPv6.isValid('200001::1'),           false)
+    test.equal(ipaddr.IPv6.isValid('::ffff:192.168.1.1'),   true)
+    test.equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1'),   false)
+    test.equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1:0'), false)
+    test.equal(ipaddr.IPv6.isValid('2001:db8::F53A::1'),   false)
+    test.equal(ipaddr.IPv6.isValid('fe80::wtf'),           false)
+    test.done()
+
+  'parses IPv6 in different formats': (test) ->
+    test.deepEqual(ipaddr.IPv6.parse('2001:db8:F53A:0:0:0:0:1').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1])
+    test.deepEqual(ipaddr.IPv6.parse('fe80::10').parts, [0xfe80, 0, 0, 0, 0, 0, 0, 0x10])
+    test.deepEqual(ipaddr.IPv6.parse('2001:db8:F53A::').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 0])
+    test.deepEqual(ipaddr.IPv6.parse('::1').parts, [0, 0, 0, 0, 0, 0, 0, 1])
+    test.deepEqual(ipaddr.IPv6.parse('::').parts, [0, 0, 0, 0, 0, 0, 0, 0])
+    test.done()
+
+  'barfs at invalid IPv6': (test) ->
+    test.throws ->
+      ipaddr.IPv6.parse('fe80::0::1')
+    test.done()
+
+  'matches IPv6 CIDR correctly': (test) ->
+    addr = ipaddr.IPv6.parse('2001:db8:f53a::1')
+    test.equal(addr.match(ipaddr.IPv6.parse('::'), 0),                  true)
+    test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53a::1:1'), 64), true)
+    test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53b::1:1'), 48), false)
+    test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f531::1:1'), 44), true)
+    test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f500::1'), 40),   true)
+    test.equal(addr.match(ipaddr.IPv6.parse('2001:db9:f500::1'), 40),   false)
+    test.equal(addr.match(addr, 128), true)
+    test.done()
+
+  'parses IPv6 CIDR correctly': (test) ->
+    addr = ipaddr.IPv6.parse('2001:db8:f53a::1')
+    test.equal(addr.match(ipaddr.IPv6.parseCIDR('::/0')),                  true)
+    test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1:1/64')), true)
+    test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53b::1:1/48')), false)
+    test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f531::1:1/44')), true)
+    test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f500::1/40')),   true)
+    test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db9:f500::1/40')),   false)
+    test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/128')),  true)
+    test.throws ->
+      ipaddr.IPv6.parseCIDR('2001:db8:f53a::1')
+    test.done()
+
+  'converts between IPv4-mapped IPv6 addresses and IPv4 addresses': (test) ->
+    addr = ipaddr.IPv4.parse('77.88.21.11')
+    mapped = addr.toIPv4MappedAddress()
+    test.deepEqual(mapped.parts, [0, 0, 0, 0, 0, 0xffff, 0x4d58, 0x150b])
+    test.deepEqual(mapped.toIPv4Address().octets, addr.octets)
+    test.done()
+
+  'refuses to convert non-IPv4-mapped IPv6 address to IPv4 address': (test) ->
+    test.throws ->
+      ipaddr.IPv6.parse('2001:db8::1').toIPv4Address()
+    test.done()
+
+  'detects reserved IPv6 networks': (test) ->
+    test.equal(ipaddr.IPv6.parse('::').range(),                        'unspecified')
+    test.equal(ipaddr.IPv6.parse('fe80::1234:5678:abcd:0123').range(), 'linkLocal')
+    test.equal(ipaddr.IPv6.parse('ff00::1234').range(),                'multicast')
+    test.equal(ipaddr.IPv6.parse('::1').range(),                       'loopback')
+    test.equal(ipaddr.IPv6.parse('fc00::').range(),                    'uniqueLocal')
+    test.equal(ipaddr.IPv6.parse('::ffff:192.168.1.10').range(),       'ipv4Mapped')
+    test.equal(ipaddr.IPv6.parse('::ffff:0:192.168.1.10').range(),     'rfc6145')
+    test.equal(ipaddr.IPv6.parse('64:ff9b::1234').range(),             'rfc6052')
+    test.equal(ipaddr.IPv6.parse('2002:1f63:45e8::1').range(),         '6to4')
+    test.equal(ipaddr.IPv6.parse('2001::4242').range(),                'teredo')
+    test.equal(ipaddr.IPv6.parse('2001:db8::3210').range(),            'reserved')
+    test.equal(ipaddr.IPv6.parse('2001:470:8:66::1').range(),          'unicast')
+    test.done()
+
+  'is able to determine IP address type': (test) ->
+    test.equal(ipaddr.parse('8.8.8.8').kind(), 'ipv4')
+    test.equal(ipaddr.parse('2001:db8:3312::1').kind(), 'ipv6')
+    test.done()
+
+  'throws an error if tried to parse an invalid address': (test) ->
+    test.throws ->
+      ipaddr.parse('::some.nonsense')
+    test.done()
+
+  'correctly processes IPv4-mapped addresses': (test) ->
+    test.equal(ipaddr.process('8.8.8.8').kind(), 'ipv4')
+    test.equal(ipaddr.process('2001:db8:3312::1').kind(), 'ipv6')
+    test.equal(ipaddr.process('::ffff:192.168.1.1').kind(), 'ipv4')
+    test.done()
+
+  'correctly converts IPv6 and IPv4 addresses to byte arrays': (test) ->
+    test.deepEqual(ipaddr.parse('1.2.3.4').toByteArray(),
+          [0x1, 0x2, 0x3, 0x4]);
+    # Fuck yeah. The first byte of Google's IPv6 address is 42. 42!
+    test.deepEqual(ipaddr.parse('2a00:1450:8007::68').toByteArray(),
+          [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68 ])
+    test.done()
+
+  'correctly parses 1 as an IPv4 address': (test) ->
+    test.equal(ipaddr.IPv6.isValid('1'), false)
+    test.equal(ipaddr.IPv4.isValid('1'), true)
+    test.deepEqual(new ipaddr.IPv4([0, 0, 0, 1]), ipaddr.parse('1'))
+    test.done()
+
+  'correctly detects IPv4 and IPv6 CIDR addresses': (test) ->
+    test.deepEqual([ipaddr.IPv6.parse('fc00::'), 64],
+                   ipaddr.parseCIDR('fc00::/64'))
+    test.deepEqual([ipaddr.IPv4.parse('1.2.3.4'), 5],
+                   ipaddr.parseCIDR('1.2.3.4/5'))
+    test.done()
+
+  'does not consider a very large or very small number a valid IP address': (test) ->
+    test.equal(ipaddr.isValid('4999999999'), false)
+    test.equal(ipaddr.isValid('-1'), false)
+    test.done()
+
+  'does not hang on ::8:8:8:8:8:8:8:8:8': (test) ->
+    test.equal(ipaddr.IPv6.isValid('::8:8:8:8:8:8:8:8:8'), false)
+    test.done()

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/package.json
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/package.json b/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/package.json
new file mode 100644
index 0000000..5c9c054
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/proxy-addr/package.json
@@ -0,0 +1,54 @@
+{
+  "name": "proxy-addr",
+  "description": "Determine address of proxied request",
+  "version": "1.0.8",
+  "author": {
+    "name": "Douglas Christopher Wilson",
+    "email": "doug@somethingdoug.com"
+  },
+  "license": "MIT",
+  "keywords": [
+    "ip",
+    "proxy",
+    "x-forwarded-for"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jshttp/proxy-addr.git"
+  },
+  "dependencies": {
+    "forwarded": "~0.1.0",
+    "ipaddr.js": "1.0.1"
+  },
+  "devDependencies": {
+    "benchmark": "1.0.0",
+    "beautify-benchmark": "0.2.4",
+    "istanbul": "0.3.9",
+    "mocha": "~1.21.5"
+  },
+  "files": [
+    "LICENSE",
+    "HISTORY.md",
+    "README.md",
+    "index.js"
+  ],
+  "engines": {
+    "node": ">= 0.6"
+  },
+  "scripts": {
+    "bench": "node benchmark/index.js",
+    "test": "mocha --reporter spec --bail --check-leaks test/",
+    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
+    "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
+  },
+  "readme": "# proxy-addr\n\n[![NPM Version][npm-image]][npm-url]\n[![NPM Downloads][downloads-image]][downloads-url]\n[![Node.js Version][node-version-image]][node-version-url]\n[![Build Status][travis-image]][travis-url]\n[![Test Coverage][coveralls-image]][coveralls-url]\n\nDetermine address of proxied request\n\n## Install\n\n```sh\n$ npm install proxy-addr\n```\n\n## API\n\n```js\nvar proxyaddr = require('proxy-addr')\n```\n\n### proxyaddr(req, trust)\n\nReturn the address of the request, using the given `trust` parameter.\n\nThe `trust` argument is a function that returns `true` if you trust\nthe address, `false` if you don't. The closest untrusted address is\nreturned.\n\n```js\nproxyaddr(req, function(addr){ return addr === '127.0.0.1' })\nproxyaddr(req, function(addr, i){ return i < 1 })\n```\n\nThe `trust` arugment may also be a single IP address string or an\narray of trusted addresses, as plain IP addresses, CIDR-formatted\nstrings, or IP/netmask strings.\n\n```js\nproxy
 addr(req, '127.0.0.1')\nproxyaddr(req, ['127.0.0.0/8', '10.0.0.0/8'])\nproxyaddr(req, ['127.0.0.0/255.0.0.0', '192.168.0.0/255.255.0.0'])\n```\n\nThis module also supports IPv6. Your IPv6 addresses will be normalized\nautomatically (i.e. `fe80::00ed:1` equals `fe80:0:0:0:0:0:ed:1`).\n\n```js\nproxyaddr(req, '::1')\nproxyaddr(req, ['::1/128', 'fe80::/10'])\nproxyaddr(req, ['fe80::/ffc0::'])\n```\n\nThis module will automatically work with IPv4-mapped IPv6 addresses\nas well to support node.js in IPv6-only mode. This means that you do\nnot have to specify both `::ffff:a00:1` and `10.0.0.1`.\n\nAs a convenience, this module also takes certain pre-defined names\nin addition to IP addresses, which expand into IP addresses:\n\n```js\nproxyaddr(req, 'loopback')\nproxyaddr(req, ['loopback', 'fc00:ac:1ab5:fff::1/64'])\n```\n\n  * `loopback`: IPv4 and IPv6 loopback addresses (like `::1` and\n    `127.0.0.1`).\n  * `linklocal`: IPv4 and IPv6 link-local addresses (like\n    `fe80::1:1:1:1` and 
 `169.254.0.1`).\n  * `uniquelocal`: IPv4 private addresses and IPv6 unique-local\n    addresses (like `fc00:ac:1ab5:fff::1` and `192.168.0.1`).\n\nWhen `trust` is specified as a function, it will be called for each\naddress to determine if it is a trusted address. The function is\ngiven two arguments: `addr` and `i`, where `addr` is a string of\nthe address to check and `i` is a number that represents the distance\nfrom the socket address.\n\n### proxyaddr.all(req, [trust])\n\nReturn all the addresses of the request, optionally stopping at the\nfirst untrusted. This array is ordered from closest to furthest\n(i.e. `arr[0] === req.connection.remoteAddress`).\n\n```js\nproxyaddr.all(req)\n```\n\nThe optional `trust` argument takes the same arguments as `trust`\ndoes in `proxyaddr(req, trust)`.\n\n```js\nproxyaddr.all(req, 'loopback')\n```\n\n### proxyaddr.compile(val)\n\nCompiles argument `val` into a `trust` function. This function takes\nthe same arguments as `trust` does in `proxya
 ddr(req, trust)` and\nreturns a function suitable for `proxyaddr(req, trust)`.\n\n```js\nvar trust = proxyaddr.compile('localhost')\nvar addr  = proxyaddr(req, trust)\n```\n\nThis function is meant to be optimized for use against every request.\nIt is recommend to compile a trust function up-front for the trusted\nconfiguration and pass that to `proxyaddr(req, trust)` for each request.\n\n## Testing\n\n```sh\n$ npm test\n```\n\n## Benchmarks\n\n```sh\n$ npm run-script bench\n```\n\n## License\n\n[MIT](LICENSE)\n\n[npm-image]: https://img.shields.io/npm/v/proxy-addr.svg\n[npm-url]: https://npmjs.org/package/proxy-addr\n[node-version-image]: https://img.shields.io/node/v/proxy-addr.svg\n[node-version-url]: http://nodejs.org/download/\n[travis-image]: https://img.shields.io/travis/jshttp/proxy-addr/master.svg\n[travis-url]: https://travis-ci.org/jshttp/proxy-addr\n[coveralls-image]: https://img.shields.io/coveralls/jshttp/proxy-addr/master.svg\n[coveralls-url]: https://coveralls.io/r/j
 shttp/proxy-addr?branch=master\n[downloads-image]: https://img.shields.io/npm/dm/proxy-addr.svg\n[downloads-url]: https://npmjs.org/package/proxy-addr\n",
+  "readmeFilename": "README.md",
+  "bugs": {
+    "url": "https://github.com/jshttp/proxy-addr/issues"
+  },
+  "homepage": "https://github.com/jshttp/proxy-addr#readme",
+  "_id": "proxy-addr@1.0.8",
+  "_shasum": "db54ec878bcc1053d57646609219b3715678bafe",
+  "_resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.8.tgz",
+  "_from": "proxy-addr@>=1.0.8 <1.1.0"
+}

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/.eslintignore
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/.eslintignore b/node_modules/cordova-serve/node_modules/express/node_modules/qs/.eslintignore
new file mode 100644
index 0000000..1521c8b
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/.eslintignore
@@ -0,0 +1 @@
+dist

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/.npmignore
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/.npmignore b/node_modules/cordova-serve/node_modules/express/node_modules/qs/.npmignore
new file mode 100644
index 0000000..2abba8d
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/.npmignore
@@ -0,0 +1,19 @@
+.idea
+*.iml
+npm-debug.log
+dump.rdb
+node_modules
+results.tap
+results.xml
+npm-shrinkwrap.json
+config.json
+.DS_Store
+*/.DS_Store
+*/*/.DS_Store
+._*
+*/._*
+*/*/._*
+coverage.*
+lib-cov
+complexity.md
+dist

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/.travis.yml
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/.travis.yml b/node_modules/cordova-serve/node_modules/express/node_modules/qs/.travis.yml
new file mode 100644
index 0000000..f502178
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+
+node_js:
+  - 0.10
+  - 0.12
+  - iojs

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/CHANGELOG.md
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/CHANGELOG.md b/node_modules/cordova-serve/node_modules/express/node_modules/qs/CHANGELOG.md
new file mode 100644
index 0000000..1fadc78
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/CHANGELOG.md
@@ -0,0 +1,88 @@
+
+## [**3.1.0**](https://github.com/hapijs/qs/issues?milestone=24&state=open)
+- [**#89**](https://github.com/hapijs/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
+
+## [**3.0.0**](https://github.com/hapijs/qs/issues?milestone=23&state=closed)
+- [**#77**](https://github.com/hapijs/qs/issues/77) Perf boost
+- [**#60**](https://github.com/hapijs/qs/issues/60) Add explicit option to disable array parsing
+- [**#80**](https://github.com/hapijs/qs/issues/80) qs.parse silently drops properties
+- [**#74**](https://github.com/hapijs/qs/issues/74) Bad parse when turning array into object
+- [**#81**](https://github.com/hapijs/qs/issues/81) Add a `filter` option
+- [**#68**](https://github.com/hapijs/qs/issues/68) Fixed issue with recursion and passing strings into objects.
+- [**#66**](https://github.com/hapijs/qs/issues/66) Add mixed array and object dot notation support Closes: #47
+- [**#76**](https://github.com/hapijs/qs/issues/76) RFC 3986
+- [**#85**](https://github.com/hapijs/qs/issues/85) No equal sign
+- [**#84**](https://github.com/hapijs/qs/issues/84) update license attribute
+
+## [**2.4.1**](https://github.com/hapijs/qs/issues?milestone=20&state=closed)
+- [**#73**](https://github.com/hapijs/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
+
+## [**2.4.0**](https://github.com/hapijs/qs/issues?milestone=19&state=closed)
+- [**#70**](https://github.com/hapijs/qs/issues/70) Add arrayFormat option
+
+## [**2.3.3**](https://github.com/hapijs/qs/issues?milestone=18&state=closed)
+- [**#59**](https://github.com/hapijs/qs/issues/59) make sure array indexes are >= 0, closes #57
+- [**#58**](https://github.com/hapijs/qs/issues/58) make qs usable for browser loader
+
+## [**2.3.2**](https://github.com/hapijs/qs/issues?milestone=17&state=closed)
+- [**#55**](https://github.com/hapijs/qs/issues/55) allow merging a string into an object
+
+## [**2.3.1**](https://github.com/hapijs/qs/issues?milestone=16&state=closed)
+- [**#52**](https://github.com/hapijs/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
+
+## [**2.3.0**](https://github.com/hapijs/qs/issues?milestone=15&state=closed)
+- [**#50**](https://github.com/hapijs/qs/issues/50) add option to omit array indices, closes #46
+
+## [**2.2.5**](https://github.com/hapijs/qs/issues?milestone=14&state=closed)
+- [**#39**](https://github.com/hapijs/qs/issues/39) Is there an alternative to Buffer.isBuffer?
+- [**#49**](https://github.com/hapijs/qs/issues/49) refactor utils.merge, fixes #45
+- [**#41**](https://github.com/hapijs/qs/issues/41) avoid browserifying Buffer, for #39
+
+## [**2.2.4**](https://github.com/hapijs/qs/issues?milestone=13&state=closed)
+- [**#38**](https://github.com/hapijs/qs/issues/38) how to handle object keys beginning with a number
+
+## [**2.2.3**](https://github.com/hapijs/qs/issues?milestone=12&state=closed)
+- [**#37**](https://github.com/hapijs/qs/issues/37) parser discards first empty value in array
+- [**#36**](https://github.com/hapijs/qs/issues/36) Update to lab 4.x
+
+## [**2.2.2**](https://github.com/hapijs/qs/issues?milestone=11&state=closed)
+- [**#33**](https://github.com/hapijs/qs/issues/33) Error when plain object in a value
+- [**#34**](https://github.com/hapijs/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
+- [**#24**](https://github.com/hapijs/qs/issues/24) Changelog? Semver?
+
+## [**2.2.1**](https://github.com/hapijs/qs/issues?milestone=10&state=closed)
+- [**#32**](https://github.com/hapijs/qs/issues/32) account for circular references properly, closes #31
+- [**#31**](https://github.com/hapijs/qs/issues/31) qs.parse stackoverflow on circular objects
+
+## [**2.2.0**](https://github.com/hapijs/qs/issues?milestone=9&state=closed)
+- [**#26**](https://github.com/hapijs/qs/issues/26) Don't use Buffer global if it's not present
+- [**#30**](https://github.com/hapijs/qs/issues/30) Bug when merging non-object values into arrays
+- [**#29**](https://github.com/hapijs/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
+- [**#23**](https://github.com/hapijs/qs/issues/23) Ability to not limit parameters?
+
+## [**2.1.0**](https://github.com/hapijs/qs/issues?milestone=8&state=closed)
+- [**#22**](https://github.com/hapijs/qs/issues/22) Enable using a RegExp as delimiter
+
+## [**2.0.0**](https://github.com/hapijs/qs/issues?milestone=7&state=closed)
+- [**#18**](https://github.com/hapijs/qs/issues/18) Why is there arrayLimit?
+- [**#20**](https://github.com/hapijs/qs/issues/20) Configurable parametersLimit
+- [**#21**](https://github.com/hapijs/qs/issues/21) make all limits optional, for #18, for #20
+
+## [**1.2.2**](https://github.com/hapijs/qs/issues?milestone=6&state=closed)
+- [**#19**](https://github.com/hapijs/qs/issues/19) Don't overwrite null values
+
+## [**1.2.1**](https://github.com/hapijs/qs/issues?milestone=5&state=closed)
+- [**#16**](https://github.com/hapijs/qs/issues/16) ignore non-string delimiters
+- [**#15**](https://github.com/hapijs/qs/issues/15) Close code block
+
+## [**1.2.0**](https://github.com/hapijs/qs/issues?milestone=4&state=closed)
+- [**#12**](https://github.com/hapijs/qs/issues/12) Add optional delim argument
+- [**#13**](https://github.com/hapijs/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
+
+## [**1.1.0**](https://github.com/hapijs/qs/issues?milestone=3&state=closed)
+- [**#7**](https://github.com/hapijs/qs/issues/7) Empty values of a POST array disappear after being submitted
+- [**#9**](https://github.com/hapijs/qs/issues/9) Should not omit equals signs (=) when value is null
+- [**#6**](https://github.com/hapijs/qs/issues/6) Minor grammar fix in README
+
+## [**1.0.2**](https://github.com/hapijs/qs/issues?milestone=2&state=closed)
+- [**#5**](https://github.com/hapijs/qs/issues/5) array holes incorrectly copied into object on large index

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/CONTRIBUTING.md
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/CONTRIBUTING.md b/node_modules/cordova-serve/node_modules/express/node_modules/qs/CONTRIBUTING.md
new file mode 100644
index 0000000..8928361
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/CONTRIBUTING.md
@@ -0,0 +1 @@
+Please view our [hapijs contributing guide](https://github.com/hapijs/hapi/blob/master/CONTRIBUTING.md).

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/LICENSE
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/LICENSE b/node_modules/cordova-serve/node_modules/express/node_modules/qs/LICENSE
new file mode 100644
index 0000000..d456948
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2014 Nathan LaFreniere and other contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * The names of any contributors may not be used to endorse or promote
+      products derived from this software without specific prior written
+      permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+                                  *   *   *
+
+The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/README.md
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/README.md b/node_modules/cordova-serve/node_modules/express/node_modules/qs/README.md
new file mode 100644
index 0000000..48a0de9
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/README.md
@@ -0,0 +1,317 @@
+# qs
+
+A querystring parsing and stringifying library with some added security.
+
+[![Build Status](https://secure.travis-ci.org/hapijs/qs.svg)](http://travis-ci.org/hapijs/qs)
+
+Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf)
+
+The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).
+
+## Usage
+
+```javascript
+var Qs = require('qs');
+
+var obj = Qs.parse('a=c');    // { a: 'c' }
+var str = Qs.stringify(obj);  // 'a=c'
+```
+
+### Parsing Objects
+
+```javascript
+Qs.parse(string, [options]);
+```
+
+**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`, or prefixing the sub-key with a dot `.`.
+For example, the string `'foo[bar]=baz'` converts to:
+
+```javascript
+{
+  foo: {
+    bar: 'baz'
+  }
+}
+```
+
+When using the `plainObjects` option the parsed value is returned as a plain object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:
+
+```javascript
+Qs.parse('a.hasOwnProperty=b', { plainObjects: true });
+// { a: { hasOwnProperty: 'b' } }
+```
+
+By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties. *WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.
+
+```javascript
+Qs.parse('a.hasOwnProperty=b', { allowPrototypes: true });
+// { a: { hasOwnProperty: 'b' } }
+```
+
+URI encoded strings work too:
+
+```javascript
+Qs.parse('a%5Bb%5D=c');
+// { a: { b: 'c' } }
+```
+
+You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
+
+```javascript
+{
+  foo: {
+    bar: {
+      baz: 'foobarbaz'
+    }
+  }
+}
+```
+
+By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like
+`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
+
+```javascript
+{
+  a: {
+    b: {
+      c: {
+        d: {
+          e: {
+            f: {
+              '[g][h][i]': 'j'
+            }
+          }
+        }
+      }
+    }
+  }
+}
+```
+
+This depth can be overridden by passing a `depth` option to `Qs.parse(string, [options])`:
+
+```javascript
+Qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
+// { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } }
+```
+
+The depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number.
+
+For similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option:
+
+```javascript
+Qs.parse('a=b&c=d', { parameterLimit: 1 });
+// { a: 'b' }
+```
+
+An optional delimiter can also be passed:
+
+```javascript
+Qs.parse('a=b;c=d', { delimiter: ';' });
+// { a: 'b', c: 'd' }
+```
+
+Delimiters can be a regular expression too:
+
+```javascript
+Qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
+// { a: 'b', c: 'd', e: 'f' }
+```
+
+Option `allowDots` can be used to disable dot notation:
+
+```javascript
+Qs.parse('a.b=c', { allowDots: false });
+// { 'a.b': 'c' } }
+```
+
+### Parsing Arrays
+
+**qs** can also parse arrays using a similar `[]` notation:
+
+```javascript
+Qs.parse('a[]=b&a[]=c');
+// { a: ['b', 'c'] }
+```
+
+You may specify an index as well:
+
+```javascript
+Qs.parse('a[1]=c&a[0]=b');
+// { a: ['b', 'c'] }
+```
+
+Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number
+to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving
+their order:
+
+```javascript
+Qs.parse('a[1]=b&a[15]=c');
+// { a: ['b', 'c'] }
+```
+
+Note that an empty string is also a value, and will be preserved:
+
+```javascript
+Qs.parse('a[]=&a[]=b');
+// { a: ['', 'b'] }
+Qs.parse('a[0]=b&a[1]=&a[2]=c');
+// { a: ['b', '', 'c'] }
+```
+
+**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will
+instead be converted to an object with the index as the key:
+
+```javascript
+Qs.parse('a[100]=b');
+// { a: { '100': 'b' } }
+```
+
+This limit can be overridden by passing an `arrayLimit` option:
+
+```javascript
+Qs.parse('a[1]=b', { arrayLimit: 0 });
+// { a: { '1': 'b' } }
+```
+
+To disable array parsing entirely, set `parseArrays` to `false`.
+
+```javascript
+Qs.parse('a[]=b', { parseArrays: false });
+// { a: { '0': 'b' } }
+```
+
+If you mix notations, **qs** will merge the two items into an object:
+
+```javascript
+Qs.parse('a[0]=b&a[b]=c');
+// { a: { '0': 'b', b: 'c' } }
+```
+
+You can also create arrays of objects:
+
+```javascript
+Qs.parse('a[][b]=c');
+// { a: [{ b: 'c' }] }
+```
+
+### Stringifying
+
+```javascript
+Qs.stringify(object, [options]);
+```
+
+When stringifying, **qs** always URI encodes output. Objects are stringified as you would expect:
+
+```javascript
+Qs.stringify({ a: 'b' });
+// 'a=b'
+Qs.stringify({ a: { b: 'c' } });
+// 'a%5Bb%5D=c'
+```
+
+Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
+
+When arrays are stringified, by default they are given explicit indices:
+
+```javascript
+Qs.stringify({ a: ['b', 'c', 'd'] });
+// 'a[0]=b&a[1]=c&a[2]=d'
+```
+
+You may override this by setting the `indices` option to `false`:
+
+```javascript
+Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
+// 'a=b&a=c&a=d'
+```
+
+You may use the `arrayFormat` option to specify the format of the output array
+
+```javascript
+Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
+// 'a[0]=b&a[1]=c'
+Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
+// 'a[]=b&a[]=c'
+Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
+// 'a=b&a=c'
+```
+
+Empty strings and null values will omit the value, but the equals sign (=) remains in place:
+
+```javascript
+Qs.stringify({ a: '' });
+// 'a='
+```
+
+Properties that are set to `undefined` will be omitted entirely:
+
+```javascript
+Qs.stringify({ a: null, b: undefined });
+// 'a='
+```
+
+The delimiter may be overridden with stringify as well:
+
+```javascript
+Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' });
+// 'a=b;c=d'
+```
+
+Finally, you can use the `filter` option to restrict which keys will be included in the stringified output.
+If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you
+pass an array, it will be used to select properties and array indices for stringification:
+
+```javascript
+function filterFunc(prefix, value) {
+  if (prefix == 'b') {
+    // Return an `undefined` value to omit a property.
+    return;
+  }
+  if (prefix == 'e[f]') {
+    return value.getTime();
+  }
+  if (prefix == 'e[g][0]') {
+    return value * 2;
+  }
+  return value;
+}
+Qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc })
+// 'a=b&c=d&e[f]=123&e[g][0]=4'
+Qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] })
+// 'a=b&e=f'
+Qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] })
+// 'a[0]=b&a[2]=d'
+```
+
+### Handling of `null` values
+
+By default, `null` values are treated like empty strings:
+
+```javascript
+Qs.stringify({ a: null, b: '' });
+// 'a=&b='
+```
+
+Parsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings.
+
+```javascript
+Qs.parse('a&b=')
+// { a: '', b: '' }
+```
+
+To distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null`
+values have no `=` sign:
+
+```javascript
+Qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
+// 'a&b='
+```
+
+To parse values without `=` back to `null` use the `strictNullHandling` flag:
+
+```javascript
+Qs.parse('a&b=', { strictNullHandling: true });
+// { a: null, b: '' }
+
+```

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/bower.json
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/bower.json b/node_modules/cordova-serve/node_modules/express/node_modules/qs/bower.json
new file mode 100644
index 0000000..ffd0641
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/bower.json
@@ -0,0 +1,22 @@
+{
+  "name": "qs",
+  "main": "dist/qs.js",
+  "version": "3.0.0",
+  "homepage": "https://github.com/hapijs/qs",
+  "authors": [
+    "Nathan LaFreniere <qu...@gmail.com>"
+  ],
+  "description": "A querystring parser that supports nesting and arrays, with a depth limit",
+  "keywords": [
+    "querystring",
+    "qs"
+  ],
+  "license": "BSD-3-Clause",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests"
+  ]
+}

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/index.js
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/index.js b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/index.js
new file mode 100644
index 0000000..0e09493
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/index.js
@@ -0,0 +1,15 @@
+// Load modules
+
+var Stringify = require('./stringify');
+var Parse = require('./parse');
+
+
+// Declare internals
+
+var internals = {};
+
+
+module.exports = {
+    stringify: Stringify,
+    parse: Parse
+};

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/parse.js
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/parse.js b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/parse.js
new file mode 100644
index 0000000..e7c56c5
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/parse.js
@@ -0,0 +1,186 @@
+// Load modules
+
+var Utils = require('./utils');
+
+
+// Declare internals
+
+var internals = {
+    delimiter: '&',
+    depth: 5,
+    arrayLimit: 20,
+    parameterLimit: 1000,
+    strictNullHandling: false,
+    plainObjects: false,
+    allowPrototypes: false
+};
+
+
+internals.parseValues = function (str, options) {
+
+    var obj = {};
+    var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
+
+    for (var i = 0, il = parts.length; i < il; ++i) {
+        var part = parts[i];
+        var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
+
+        if (pos === -1) {
+            obj[Utils.decode(part)] = '';
+
+            if (options.strictNullHandling) {
+                obj[Utils.decode(part)] = null;
+            }
+        }
+        else {
+            var key = Utils.decode(part.slice(0, pos));
+            var val = Utils.decode(part.slice(pos + 1));
+
+            if (!Object.prototype.hasOwnProperty.call(obj, key)) {
+                obj[key] = val;
+            }
+            else {
+                obj[key] = [].concat(obj[key]).concat(val);
+            }
+        }
+    }
+
+    return obj;
+};
+
+
+internals.parseObject = function (chain, val, options) {
+
+    if (!chain.length) {
+        return val;
+    }
+
+    var root = chain.shift();
+
+    var obj;
+    if (root === '[]') {
+        obj = [];
+        obj = obj.concat(internals.parseObject(chain, val, options));
+    }
+    else {
+        obj = options.plainObjects ? Object.create(null) : {};
+        var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
+        var index = parseInt(cleanRoot, 10);
+        var indexString = '' + index;
+        if (!isNaN(index) &&
+            root !== cleanRoot &&
+            indexString === cleanRoot &&
+            index >= 0 &&
+            (options.parseArrays &&
+             index <= options.arrayLimit)) {
+
+            obj = [];
+            obj[index] = internals.parseObject(chain, val, options);
+        }
+        else {
+            obj[cleanRoot] = internals.parseObject(chain, val, options);
+        }
+    }
+
+    return obj;
+};
+
+
+internals.parseKeys = function (key, val, options) {
+
+    if (!key) {
+        return;
+    }
+
+    // Transform dot notation to bracket notation
+
+    if (options.allowDots) {
+        key = key.replace(/\.([^\.\[]+)/g, '[$1]');
+    }
+
+    // The regex chunks
+
+    var parent = /^([^\[\]]*)/;
+    var child = /(\[[^\[\]]*\])/g;
+
+    // Get the parent
+
+    var segment = parent.exec(key);
+
+    // Stash the parent if it exists
+
+    var keys = [];
+    if (segment[1]) {
+        // If we aren't using plain objects, optionally prefix keys
+        // that would overwrite object prototype properties
+        if (!options.plainObjects &&
+            Object.prototype.hasOwnProperty(segment[1])) {
+
+            if (!options.allowPrototypes) {
+                return;
+            }
+        }
+
+        keys.push(segment[1]);
+    }
+
+    // Loop through children appending to the array until we hit depth
+
+    var i = 0;
+    while ((segment = child.exec(key)) !== null && i < options.depth) {
+
+        ++i;
+        if (!options.plainObjects &&
+            Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) {
+
+            if (!options.allowPrototypes) {
+                continue;
+            }
+        }
+        keys.push(segment[1]);
+    }
+
+    // If there's a remainder, just add whatever is left
+
+    if (segment) {
+        keys.push('[' + key.slice(segment.index) + ']');
+    }
+
+    return internals.parseObject(keys, val, options);
+};
+
+
+module.exports = function (str, options) {
+
+    options = options || {};
+    options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
+    options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
+    options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
+    options.parseArrays = options.parseArrays !== false;
+    options.allowDots = options.allowDots !== false;
+    options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
+    options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
+    options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
+    options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
+
+    if (str === '' ||
+        str === null ||
+        typeof str === 'undefined') {
+
+        return options.plainObjects ? Object.create(null) : {};
+    }
+
+    var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
+    var obj = options.plainObjects ? Object.create(null) : {};
+
+    // Iterate over the keys and setup the new object
+
+    var keys = Object.keys(tempObj);
+    for (var i = 0, il = keys.length; i < il; ++i) {
+        var key = keys[i];
+        var newObj = internals.parseKeys(key, tempObj[key], options);
+        obj = Utils.merge(obj, newObj, options);
+    }
+
+    return Utils.compact(obj);
+};

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/stringify.js
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/stringify.js b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/stringify.js
new file mode 100644
index 0000000..7414284
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/stringify.js
@@ -0,0 +1,121 @@
+// Load modules
+
+var Utils = require('./utils');
+
+
+// Declare internals
+
+var internals = {
+    delimiter: '&',
+    arrayPrefixGenerators: {
+        brackets: function (prefix, key) {
+
+            return prefix + '[]';
+        },
+        indices: function (prefix, key) {
+
+            return prefix + '[' + key + ']';
+        },
+        repeat: function (prefix, key) {
+
+            return prefix;
+        }
+    },
+    strictNullHandling: false
+};
+
+
+internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, filter) {
+
+    if (typeof filter === 'function') {
+        obj = filter(prefix, obj);
+    }
+    else if (Utils.isBuffer(obj)) {
+        obj = obj.toString();
+    }
+    else if (obj instanceof Date) {
+        obj = obj.toISOString();
+    }
+    else if (obj === null) {
+        if (strictNullHandling) {
+            return Utils.encode(prefix);
+        }
+
+        obj = '';
+    }
+
+    if (typeof obj === 'string' ||
+        typeof obj === 'number' ||
+        typeof obj === 'boolean') {
+
+        return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
+    }
+
+    var values = [];
+
+    if (typeof obj === 'undefined') {
+        return values;
+    }
+
+    var objKeys = Array.isArray(filter) ? filter : Object.keys(obj);
+    for (var i = 0, il = objKeys.length; i < il; ++i) {
+        var key = objKeys[i];
+
+        if (Array.isArray(obj)) {
+            values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, filter));
+        }
+        else {
+            values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, filter));
+        }
+    }
+
+    return values;
+};
+
+
+module.exports = function (obj, options) {
+
+    options = options || {};
+    var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
+    var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
+    var objKeys;
+    var filter;
+    if (typeof options.filter === 'function') {
+        filter = options.filter;
+        obj = filter('', obj);
+    }
+    else if (Array.isArray(options.filter)) {
+        objKeys = filter = options.filter;
+    }
+
+    var keys = [];
+
+    if (typeof obj !== 'object' ||
+        obj === null) {
+
+        return '';
+    }
+
+    var arrayFormat;
+    if (options.arrayFormat in internals.arrayPrefixGenerators) {
+        arrayFormat = options.arrayFormat;
+    }
+    else if ('indices' in options) {
+        arrayFormat = options.indices ? 'indices' : 'repeat';
+    }
+    else {
+        arrayFormat = 'indices';
+    }
+
+    var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
+
+    if (!objKeys) {
+        objKeys = Object.keys(obj);
+    }
+    for (var i = 0, il = objKeys.length; i < il; ++i) {
+        var key = objKeys[i];
+        keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, filter));
+    }
+
+    return keys.join(delimiter);
+};

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/utils.js
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/utils.js b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/utils.js
new file mode 100644
index 0000000..88f3147
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/lib/utils.js
@@ -0,0 +1,190 @@
+// Load modules
+
+
+// Declare internals
+
+var internals = {};
+internals.hexTable = new Array(256);
+for (var h = 0; h < 256; ++h) {
+    internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase();
+}
+
+
+exports.arrayToObject = function (source, options) {
+
+    var obj = options.plainObjects ? Object.create(null) : {};
+    for (var i = 0, il = source.length; i < il; ++i) {
+        if (typeof source[i] !== 'undefined') {
+
+            obj[i] = source[i];
+        }
+    }
+
+    return obj;
+};
+
+
+exports.merge = function (target, source, options) {
+
+    if (!source) {
+        return target;
+    }
+
+    if (typeof source !== 'object') {
+        if (Array.isArray(target)) {
+            target.push(source);
+        }
+        else if (typeof target === 'object') {
+            target[source] = true;
+        }
+        else {
+            target = [target, source];
+        }
+
+        return target;
+    }
+
+    if (typeof target !== 'object') {
+        target = [target].concat(source);
+        return target;
+    }
+
+    if (Array.isArray(target) &&
+        !Array.isArray(source)) {
+
+        target = exports.arrayToObject(target, options);
+    }
+
+    var keys = Object.keys(source);
+    for (var k = 0, kl = keys.length; k < kl; ++k) {
+        var key = keys[k];
+        var value = source[key];
+
+        if (!Object.prototype.hasOwnProperty.call(target, key)) {
+            target[key] = value;
+        }
+        else {
+            target[key] = exports.merge(target[key], value, options);
+        }
+    }
+
+    return target;
+};
+
+
+exports.decode = function (str) {
+
+    try {
+        return decodeURIComponent(str.replace(/\+/g, ' '));
+    } catch (e) {
+        return str;
+    }
+};
+
+exports.encode = function (str) {
+
+    // This code was originally written by Brian White (mscdex) for the io.js core querystring library.
+    // It has been adapted here for stricter adherence to RFC 3986
+    if (str.length === 0) {
+        return str;
+    }
+
+    if (typeof str !== 'string') {
+        str = '' + str;
+    }
+
+    var out = '';
+    for (var i = 0, il = str.length; i < il; ++i) {
+        var c = str.charCodeAt(i);
+
+        if (c === 0x2D || // -
+            c === 0x2E || // .
+            c === 0x5F || // _
+            c === 0x7E || // ~
+            (c >= 0x30 && c <= 0x39) || // 0-9
+            (c >= 0x41 && c <= 0x5A) || // a-z
+            (c >= 0x61 && c <= 0x7A)) { // A-Z
+
+            out += str[i];
+            continue;
+        }
+
+        if (c < 0x80) {
+            out += internals.hexTable[c];
+            continue;
+        }
+
+        if (c < 0x800) {
+            out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)];
+            continue;
+        }
+
+        if (c < 0xD800 || c >= 0xE000) {
+            out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
+            continue;
+        }
+
+        ++i;
+        c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
+        out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
+    }
+
+    return out;
+};
+
+exports.compact = function (obj, refs) {
+
+    if (typeof obj !== 'object' ||
+        obj === null) {
+
+        return obj;
+    }
+
+    refs = refs || [];
+    var lookup = refs.indexOf(obj);
+    if (lookup !== -1) {
+        return refs[lookup];
+    }
+
+    refs.push(obj);
+
+    if (Array.isArray(obj)) {
+        var compacted = [];
+
+        for (var i = 0, il = obj.length; i < il; ++i) {
+            if (typeof obj[i] !== 'undefined') {
+                compacted.push(obj[i]);
+            }
+        }
+
+        return compacted;
+    }
+
+    var keys = Object.keys(obj);
+    for (i = 0, il = keys.length; i < il; ++i) {
+        var key = keys[i];
+        obj[key] = exports.compact(obj[key], refs);
+    }
+
+    return obj;
+};
+
+
+exports.isRegExp = function (obj) {
+
+    return Object.prototype.toString.call(obj) === '[object RegExp]';
+};
+
+
+exports.isBuffer = function (obj) {
+
+    if (obj === null ||
+        typeof obj === 'undefined') {
+
+        return false;
+    }
+
+    return !!(obj.constructor &&
+              obj.constructor.isBuffer &&
+              obj.constructor.isBuffer(obj));
+};

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/package.json
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/package.json b/node_modules/cordova-serve/node_modules/express/node_modules/qs/package.json
new file mode 100644
index 0000000..1bae0ed
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/package.json
@@ -0,0 +1,57 @@
+{
+  "name": "qs",
+  "version": "4.0.0",
+  "description": "A querystring parser that supports nesting and arrays, with a depth limit",
+  "homepage": "https://github.com/hapijs/qs",
+  "main": "lib/index.js",
+  "dependencies": {},
+  "devDependencies": {
+    "browserify": "^10.2.1",
+    "code": "1.x.x",
+    "lab": "5.x.x"
+  },
+  "scripts": {
+    "test": "lab -a code -t 100 -L",
+    "test-cov-html": "lab -a code -r html -o coverage.html",
+    "dist": "browserify --standalone Qs lib/index.js > dist/qs.js"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/hapijs/qs.git"
+  },
+  "keywords": [
+    "querystring",
+    "qs"
+  ],
+  "license": "BSD-3-Clause",
+  "gitHead": "e573dd08eae6cce30d2202704691a102dfa3782a",
+  "bugs": {
+    "url": "https://github.com/hapijs/qs/issues"
+  },
+  "_id": "qs@4.0.0",
+  "_shasum": "c31d9b74ec27df75e543a86c78728ed8d4623607",
+  "_from": "qs@4.0.0",
+  "_npmVersion": "2.12.0",
+  "_nodeVersion": "0.12.4",
+  "_npmUser": {
+    "name": "nlf",
+    "email": "quitlahok@gmail.com"
+  },
+  "dist": {
+    "shasum": "c31d9b74ec27df75e543a86c78728ed8d4623607",
+    "tarball": "http://registry.npmjs.org/qs/-/qs-4.0.0.tgz"
+  },
+  "maintainers": [
+    {
+      "name": "nlf",
+      "email": "quitlahok@gmail.com"
+    },
+    {
+      "name": "hueniverse",
+      "email": "eran@hueniverse.com"
+    }
+  ],
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz",
+  "readme": "ERROR: No README data found!"
+}

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/parse.js
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/parse.js b/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/parse.js
new file mode 100644
index 0000000..a19d764
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/parse.js
@@ -0,0 +1,478 @@
+/* eslint no-extend-native:0 */
+// Load modules
+
+var Code = require('code');
+var Lab = require('lab');
+var Qs = require('../');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var expect = Code.expect;
+var describe = lab.experiment;
+var it = lab.test;
+
+
+describe('parse()', function () {
+
+    it('parses a simple string', function (done) {
+
+        expect(Qs.parse('0=foo')).to.deep.equal({ '0': 'foo' });
+        expect(Qs.parse('foo=c++')).to.deep.equal({ foo: 'c  ' });
+        expect(Qs.parse('a[>=]=23')).to.deep.equal({ a: { '>=': '23' } });
+        expect(Qs.parse('a[<=>]==23')).to.deep.equal({ a: { '<=>': '=23' } });
+        expect(Qs.parse('a[==]=23')).to.deep.equal({ a: { '==': '23' } });
+        expect(Qs.parse('foo', { strictNullHandling: true })).to.deep.equal({ foo: null });
+        expect(Qs.parse('foo' )).to.deep.equal({ foo: '' });
+        expect(Qs.parse('foo=')).to.deep.equal({ foo: '' });
+        expect(Qs.parse('foo=bar')).to.deep.equal({ foo: 'bar' });
+        expect(Qs.parse(' foo = bar = baz ')).to.deep.equal({ ' foo ': ' bar = baz ' });
+        expect(Qs.parse('foo=bar=baz')).to.deep.equal({ foo: 'bar=baz' });
+        expect(Qs.parse('foo=bar&bar=baz')).to.deep.equal({ foo: 'bar', bar: 'baz' });
+        expect(Qs.parse('foo2=bar2&baz2=')).to.deep.equal({ foo2: 'bar2', baz2: '' });
+        expect(Qs.parse('foo=bar&baz', { strictNullHandling: true })).to.deep.equal({ foo: 'bar', baz: null });
+        expect(Qs.parse('foo=bar&baz')).to.deep.equal({ foo: 'bar', baz: '' });
+        expect(Qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')).to.deep.equal({
+            cht: 'p3',
+            chd: 't:60,40',
+            chs: '250x100',
+            chl: 'Hello|World'
+        });
+        done();
+    });
+
+    it('allows disabling dot notation', function (done) {
+
+        expect(Qs.parse('a.b=c')).to.deep.equal({ a: { b: 'c' } });
+        expect(Qs.parse('a.b=c', { allowDots: false })).to.deep.equal({ 'a.b': 'c' });
+        done();
+    });
+
+    it('parses a single nested string', function (done) {
+
+        expect(Qs.parse('a[b]=c')).to.deep.equal({ a: { b: 'c' } });
+        done();
+    });
+
+    it('parses a double nested string', function (done) {
+
+        expect(Qs.parse('a[b][c]=d')).to.deep.equal({ a: { b: { c: 'd' } } });
+        done();
+    });
+
+    it('defaults to a depth of 5', function (done) {
+
+        expect(Qs.parse('a[b][c][d][e][f][g][h]=i')).to.deep.equal({ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } });
+        done();
+    });
+
+    it('only parses one level when depth = 1', function (done) {
+
+        expect(Qs.parse('a[b][c]=d', { depth: 1 })).to.deep.equal({ a: { b: { '[c]': 'd' } } });
+        expect(Qs.parse('a[b][c][d]=e', { depth: 1 })).to.deep.equal({ a: { b: { '[c][d]': 'e' } } });
+        done();
+    });
+
+    it('parses a simple array', function (done) {
+
+        expect(Qs.parse('a=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+        done();
+    });
+
+    it('parses an explicit array', function (done) {
+
+        expect(Qs.parse('a[]=b')).to.deep.equal({ a: ['b'] });
+        expect(Qs.parse('a[]=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] });
+        expect(Qs.parse('a[]=b&a[]=c&a[]=d')).to.deep.equal({ a: ['b', 'c', 'd'] });
+        done();
+    });
+
+    it('parses a mix of simple and explicit arrays', function (done) {
+
+        expect(Qs.parse('a=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] });
+        expect(Qs.parse('a[]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+        expect(Qs.parse('a[0]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+        expect(Qs.parse('a=b&a[0]=c')).to.deep.equal({ a: ['b', 'c'] });
+        expect(Qs.parse('a[1]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+        expect(Qs.parse('a=b&a[1]=c')).to.deep.equal({ a: ['b', 'c'] });
+        done();
+    });
+
+    it('parses a nested array', function (done) {
+
+        expect(Qs.parse('a[b][]=c&a[b][]=d')).to.deep.equal({ a: { b: ['c', 'd'] } });
+        expect(Qs.parse('a[>=]=25')).to.deep.equal({ a: { '>=': '25' } });
+        done();
+    });
+
+    it('allows to specify array indices', function (done) {
+
+        expect(Qs.parse('a[1]=c&a[0]=b&a[2]=d')).to.deep.equal({ a: ['b', 'c', 'd'] });
+        expect(Qs.parse('a[1]=c&a[0]=b')).to.deep.equal({ a: ['b', 'c'] });
+        expect(Qs.parse('a[1]=c')).to.deep.equal({ a: ['c'] });
+        done();
+    });
+
+    it('limits specific array indices to 20', function (done) {
+
+        expect(Qs.parse('a[20]=a')).to.deep.equal({ a: ['a'] });
+        expect(Qs.parse('a[21]=a')).to.deep.equal({ a: { '21': 'a' } });
+        done();
+    });
+
+    it('supports keys that begin with a number', function (done) {
+
+        expect(Qs.parse('a[12b]=c')).to.deep.equal({ a: { '12b': 'c' } });
+        done();
+    });
+
+    it('supports encoded = signs', function (done) {
+
+        expect(Qs.parse('he%3Dllo=th%3Dere')).to.deep.equal({ 'he=llo': 'th=ere' });
+        done();
+    });
+
+    it('is ok with url encoded strings', function (done) {
+
+        expect(Qs.parse('a[b%20c]=d')).to.deep.equal({ a: { 'b c': 'd' } });
+        expect(Qs.parse('a[b]=c%20d')).to.deep.equal({ a: { b: 'c d' } });
+        done();
+    });
+
+    it('allows brackets in the value', function (done) {
+
+        expect(Qs.parse('pets=["tobi"]')).to.deep.equal({ pets: '["tobi"]' });
+        expect(Qs.parse('operators=[">=", "<="]')).to.deep.equal({ operators: '[">=", "<="]' });
+        done();
+    });
+
+    it('allows empty values', function (done) {
+
+        expect(Qs.parse('')).to.deep.equal({});
+        expect(Qs.parse(null)).to.deep.equal({});
+        expect(Qs.parse(undefined)).to.deep.equal({});
+        done();
+    });
+
+    it('transforms arrays to objects', function (done) {
+
+        expect(Qs.parse('foo[0]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
+        expect(Qs.parse('foo[bad]=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
+        expect(Qs.parse('foo[bad]=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
+        expect(Qs.parse('foo[]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
+        expect(Qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
+        expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
+        expect(Qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c')).to.deep.equal({ a: { '0': 'b', t: 'u', c: true } });
+        expect(Qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y')).to.deep.equal({ a: { '0': 'b', '1': 'c', x: 'y' } });
+        done();
+    });
+
+    it('transforms arrays to objects (dot notation)', function (done) {
+
+        expect(Qs.parse('foo[0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
+        expect(Qs.parse('foo[0].baz=bar&fool.bad.boo=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
+        expect(Qs.parse('foo[0][0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
+        expect(Qs.parse('foo[0].baz[0]=15&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15'], bar: '2' }] });
+        expect(Qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15', '16'], bar: '2' }] });
+        expect(Qs.parse('foo.bad=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
+        expect(Qs.parse('foo.bad=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
+        expect(Qs.parse('foo[]=bar&foo.bad=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
+        expect(Qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
+        expect(Qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
+        done();
+    });
+
+    it('can add keys to objects', function (done) {
+
+        expect(Qs.parse('a[b]=c&a=d')).to.deep.equal({ a: { b: 'c', d: true } });
+        done();
+    });
+
+    it('correctly prunes undefined values when converting an array to an object', function (done) {
+
+        expect(Qs.parse('a[2]=b&a[99999999]=c')).to.deep.equal({ a: { '2': 'b', '99999999': 'c' } });
+        done();
+    });
+
+    it('supports malformed uri characters', function (done) {
+
+        expect(Qs.parse('{%:%}', { strictNullHandling: true })).to.deep.equal({ '{%:%}': null });
+        expect(Qs.parse('{%:%}=')).to.deep.equal({ '{%:%}': '' });
+        expect(Qs.parse('foo=%:%}')).to.deep.equal({ foo: '%:%}' });
+        done();
+    });
+
+    it('doesn\'t produce empty keys', function (done) {
+
+        expect(Qs.parse('_r=1&')).to.deep.equal({ '_r': '1' });
+        done();
+    });
+
+    it('cannot access Object prototype', function (done) {
+
+        Qs.parse('constructor[prototype][bad]=bad');
+        Qs.parse('bad[constructor][prototype][bad]=bad');
+        expect(typeof Object.prototype.bad).to.equal('undefined');
+        done();
+    });
+
+    it('parses arrays of objects', function (done) {
+
+        expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
+        expect(Qs.parse('a[0][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
+        done();
+    });
+
+    it('allows for empty strings in arrays', function (done) {
+
+        expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] });
+        expect(Qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true })).to.deep.equal({ a: ['b', null, 'c', ''] });
+        expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true })).to.deep.equal({ a: ['b', '', 'c', null] });
+        expect(Qs.parse('a[]=&a[]=b&a[]=c')).to.deep.equal({ a: ['', 'b', 'c'] });
+        done();
+    });
+
+    it('compacts sparse arrays', function (done) {
+
+        expect(Qs.parse('a[10]=1&a[2]=2')).to.deep.equal({ a: ['2', '1'] });
+        done();
+    });
+
+    it('parses semi-parsed strings', function (done) {
+
+        expect(Qs.parse({ 'a[b]': 'c' })).to.deep.equal({ a: { b: 'c' } });
+        expect(Qs.parse({ 'a[b]': 'c', 'a[d]': 'e' })).to.deep.equal({ a: { b: 'c', d: 'e' } });
+        done();
+    });
+
+    it('parses buffers correctly', function (done) {
+
+        var b = new Buffer('test');
+        expect(Qs.parse({ a: b })).to.deep.equal({ a: b });
+        done();
+    });
+
+    it('continues parsing when no parent is found', function (done) {
+
+        expect(Qs.parse('[]=&a=b')).to.deep.equal({ '0': '', a: 'b' });
+        expect(Qs.parse('[]&a=b', { strictNullHandling: true })).to.deep.equal({ '0': null, a: 'b' });
+        expect(Qs.parse('[foo]=bar')).to.deep.equal({ foo: 'bar' });
+        done();
+    });
+
+    it('does not error when parsing a very long array', function (done) {
+
+        var str = 'a[]=a';
+        while (Buffer.byteLength(str) < 128 * 1024) {
+            str += '&' + str;
+        }
+
+        expect(function () {
+
+            Qs.parse(str);
+        }).to.not.throw();
+
+        done();
+    });
+
+    it('should not throw when a native prototype has an enumerable property', { parallel: false }, function (done) {
+
+        Object.prototype.crash = '';
+        Array.prototype.crash = '';
+        expect(Qs.parse.bind(null, 'a=b')).to.not.throw();
+        expect(Qs.parse('a=b')).to.deep.equal({ a: 'b' });
+        expect(Qs.parse.bind(null, 'a[][b]=c')).to.not.throw();
+        expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
+        delete Object.prototype.crash;
+        delete Array.prototype.crash;
+        done();
+    });
+
+    it('parses a string with an alternative string delimiter', function (done) {
+
+        expect(Qs.parse('a=b;c=d', { delimiter: ';' })).to.deep.equal({ a: 'b', c: 'd' });
+        done();
+    });
+
+    it('parses a string with an alternative RegExp delimiter', function (done) {
+
+        expect(Qs.parse('a=b; c=d', { delimiter: /[;,] */ })).to.deep.equal({ a: 'b', c: 'd' });
+        done();
+    });
+
+    it('does not use non-splittable objects as delimiters', function (done) {
+
+        expect(Qs.parse('a=b&c=d', { delimiter: true })).to.deep.equal({ a: 'b', c: 'd' });
+        done();
+    });
+
+    it('allows overriding parameter limit', function (done) {
+
+        expect(Qs.parse('a=b&c=d', { parameterLimit: 1 })).to.deep.equal({ a: 'b' });
+        done();
+    });
+
+    it('allows setting the parameter limit to Infinity', function (done) {
+
+        expect(Qs.parse('a=b&c=d', { parameterLimit: Infinity })).to.deep.equal({ a: 'b', c: 'd' });
+        done();
+    });
+
+    it('allows overriding array limit', function (done) {
+
+        expect(Qs.parse('a[0]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '0': 'b' } });
+        expect(Qs.parse('a[-1]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '-1': 'b' } });
+        expect(Qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 })).to.deep.equal({ a: { '0': 'b', '1': 'c' } });
+        done();
+    });
+
+    it('allows disabling array parsing', function (done) {
+
+        expect(Qs.parse('a[0]=b&a[1]=c', { parseArrays: false })).to.deep.equal({ a: { '0': 'b', '1': 'c' } });
+        done();
+    });
+
+    it('parses an object', function (done) {
+
+        var input = {
+            'user[name]': { 'pop[bob]': 3 },
+            'user[email]': null
+        };
+
+        var expected = {
+            'user': {
+                'name': { 'pop[bob]': 3 },
+                'email': null
+            }
+        };
+
+        var result = Qs.parse(input);
+
+        expect(result).to.deep.equal(expected);
+        done();
+    });
+
+    it('parses an object in dot notation', function (done) {
+
+        var input = {
+            'user.name': { 'pop[bob]': 3 },
+            'user.email.': null
+        };
+
+        var expected = {
+            'user': {
+                'name': { 'pop[bob]': 3 },
+                'email': null
+            }
+        };
+
+        var result = Qs.parse(input);
+
+        expect(result).to.deep.equal(expected);
+        done();
+    });
+
+    it('parses an object and not child values', function (done) {
+
+        var input = {
+            'user[name]': { 'pop[bob]': { 'test': 3 } },
+            'user[email]': null
+        };
+
+        var expected = {
+            'user': {
+                'name': { 'pop[bob]': { 'test': 3 } },
+                'email': null
+            }
+        };
+
+        var result = Qs.parse(input);
+
+        expect(result).to.deep.equal(expected);
+        done();
+    });
+
+    it('does not blow up when Buffer global is missing', function (done) {
+
+        var tempBuffer = global.Buffer;
+        delete global.Buffer;
+        var result = Qs.parse('a=b&c=d');
+        global.Buffer = tempBuffer;
+        expect(result).to.deep.equal({ a: 'b', c: 'd' });
+        done();
+    });
+
+    it('does not crash when parsing circular references', function (done) {
+
+        var a = {};
+        a.b = a;
+
+        var parsed;
+
+        expect(function () {
+
+            parsed = Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a });
+        }).to.not.throw();
+
+        expect(parsed).to.contain('foo');
+        expect(parsed.foo).to.contain('bar', 'baz');
+        expect(parsed.foo.bar).to.equal('baz');
+        expect(parsed.foo.baz).to.deep.equal(a);
+        done();
+    });
+
+    it('parses plain objects correctly', function (done) {
+
+        var a = Object.create(null);
+        a.b = 'c';
+
+        expect(Qs.parse(a)).to.deep.equal({ b: 'c' });
+        var result = Qs.parse({ a: a });
+        expect(result).to.contain('a');
+        expect(result.a).to.deep.equal(a);
+        done();
+    });
+
+    it('parses dates correctly', function (done) {
+
+        var now = new Date();
+        expect(Qs.parse({ a: now })).to.deep.equal({ a: now });
+        done();
+    });
+
+    it('parses regular expressions correctly', function (done) {
+
+        var re = /^test$/;
+        expect(Qs.parse({ a: re })).to.deep.equal({ a: re });
+        done();
+    });
+
+    it('can allow overwriting prototype properties', function (done) {
+
+        expect(Qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true })).to.deep.equal({ a: { hasOwnProperty: 'b' } }, { prototype: false });
+        expect(Qs.parse('hasOwnProperty=b', { allowPrototypes: true })).to.deep.equal({ hasOwnProperty: 'b' }, { prototype: false });
+        done();
+    });
+
+    it('can return plain objects', function (done) {
+
+        var expected = Object.create(null);
+        expected.a = Object.create(null);
+        expected.a.b = 'c';
+        expected.a.hasOwnProperty = 'd';
+        expect(Qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true })).to.deep.equal(expected);
+        expect(Qs.parse(null, { plainObjects: true })).to.deep.equal(Object.create(null));
+        var expectedArray = Object.create(null);
+        expectedArray.a = Object.create(null);
+        expectedArray.a['0'] = 'b';
+        expectedArray.a.c = 'd';
+        expect(Qs.parse('a[]=b&a[c]=d', { plainObjects: true })).to.deep.equal(expectedArray);
+        done();
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/stringify.js
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/stringify.js b/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/stringify.js
new file mode 100644
index 0000000..48b7803
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/stringify.js
@@ -0,0 +1,259 @@
+/* eslint no-extend-native:0 */
+// Load modules
+
+var Code = require('code');
+var Lab = require('lab');
+var Qs = require('../');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var expect = Code.expect;
+var describe = lab.experiment;
+var it = lab.test;
+
+
+describe('stringify()', function () {
+
+    it('stringifies a querystring object', function (done) {
+
+        expect(Qs.stringify({ a: 'b' })).to.equal('a=b');
+        expect(Qs.stringify({ a: 1 })).to.equal('a=1');
+        expect(Qs.stringify({ a: 1, b: 2 })).to.equal('a=1&b=2');
+        expect(Qs.stringify({ a: 'A_Z' })).to.equal('a=A_Z');
+        expect(Qs.stringify({ a: '€' })).to.equal('a=%E2%82%AC');
+        expect(Qs.stringify({ a: '' })).to.equal('a=%EE%80%80');
+        expect(Qs.stringify({ a: 'א' })).to.equal('a=%D7%90');
+        expect(Qs.stringify({ a: '𐐷' })).to.equal('a=%F0%90%90%B7');
+        done();
+    });
+
+    it('stringifies a nested object', function (done) {
+
+        expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
+        expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a%5Bb%5D%5Bc%5D%5Bd%5D=e');
+        done();
+    });
+
+    it('stringifies an array value', function (done) {
+
+        expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d');
+        done();
+    });
+
+    it('omits array indices when asked', function (done) {
+
+        expect(Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false })).to.equal('a=b&a=c&a=d');
+        done();
+    });
+
+    it('stringifies a nested array value', function (done) {
+
+        expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
+        done();
+    });
+
+    it('stringifies an object inside an array', function (done) {
+
+        expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a%5B0%5D%5Bb%5D=c');
+        expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1');
+        done();
+    });
+
+    it('does not omit object keys when indices = false', function (done) {
+
+        expect(Qs.stringify({ a: [{ b: 'c' }] }, { indices: false })).to.equal('a%5Bb%5D=c');
+        done();
+    });
+
+    it('uses indices notation for arrays when indices=true', function (done) {
+
+        expect(Qs.stringify({ a: ['b', 'c'] }, { indices: true })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
+        done();
+    });
+
+    it('uses indices notation for arrays when no arrayFormat is specified', function (done) {
+
+        expect(Qs.stringify({ a: ['b', 'c'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
+        done();
+    });
+
+    it('uses indices notation for arrays when no arrayFormat=indices', function (done) {
+
+        expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
+        done();
+    });
+
+    it('uses repeat notation for arrays when no arrayFormat=repeat', function (done) {
+
+        expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })).to.equal('a=b&a=c');
+        done();
+    });
+
+    it('uses brackets notation for arrays when no arrayFormat=brackets', function (done) {
+
+        expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })).to.equal('a%5B%5D=b&a%5B%5D=c');
+        done();
+    });
+
+    it('stringifies a complicated object', function (done) {
+
+        expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a%5Bb%5D=c&a%5Bd%5D=e');
+        done();
+    });
+
+    it('stringifies an empty value', function (done) {
+
+        expect(Qs.stringify({ a: '' })).to.equal('a=');
+        expect(Qs.stringify({ a: null }, { strictNullHandling: true })).to.equal('a');
+
+        expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b=');
+        expect(Qs.stringify({ a: null, b: '' }, { strictNullHandling: true })).to.equal('a&b=');
+
+        expect(Qs.stringify({ a: { b: '' } })).to.equal('a%5Bb%5D=');
+        expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: true })).to.equal('a%5Bb%5D');
+        expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: false })).to.equal('a%5Bb%5D=');
+
+        done();
+    });
+
+    it('stringifies an empty object', function (done) {
+
+        var obj = Object.create(null);
+        obj.a = 'b';
+        expect(Qs.stringify(obj)).to.equal('a=b');
+        done();
+    });
+
+    it('returns an empty string for invalid input', function (done) {
+
+        expect(Qs.stringify(undefined)).to.equal('');
+        expect(Qs.stringify(false)).to.equal('');
+        expect(Qs.stringify(null)).to.equal('');
+        expect(Qs.stringify('')).to.equal('');
+        done();
+    });
+
+    it('stringifies an object with an empty object as a child', function (done) {
+
+        var obj = {
+            a: Object.create(null)
+        };
+
+        obj.a.b = 'c';
+        expect(Qs.stringify(obj)).to.equal('a%5Bb%5D=c');
+        done();
+    });
+
+    it('drops keys with a value of undefined', function (done) {
+
+        expect(Qs.stringify({ a: undefined })).to.equal('');
+
+        expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true })).to.equal('a%5Bc%5D');
+        expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false })).to.equal('a%5Bc%5D=');
+        expect(Qs.stringify({ a: { b: undefined, c: '' } })).to.equal('a%5Bc%5D=');
+        done();
+    });
+
+    it('url encodes values', function (done) {
+
+        expect(Qs.stringify({ a: 'b c' })).to.equal('a=b%20c');
+        done();
+    });
+
+    it('stringifies a date', function (done) {
+
+        var now = new Date();
+        var str = 'a=' + encodeURIComponent(now.toISOString());
+        expect(Qs.stringify({ a: now })).to.equal(str);
+        done();
+    });
+
+    it('stringifies the weird object from qs', function (done) {
+
+        expect(Qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F');
+        done();
+    });
+
+    it('skips properties that are part of the object prototype', function (done) {
+
+        Object.prototype.crash = 'test';
+        expect(Qs.stringify({ a: 'b' })).to.equal('a=b');
+        expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
+        delete Object.prototype.crash;
+        done();
+    });
+
+    it('stringifies boolean values', function (done) {
+
+        expect(Qs.stringify({ a: true })).to.equal('a=true');
+        expect(Qs.stringify({ a: { b: true } })).to.equal('a%5Bb%5D=true');
+        expect(Qs.stringify({ b: false })).to.equal('b=false');
+        expect(Qs.stringify({ b: { c: false } })).to.equal('b%5Bc%5D=false');
+        done();
+    });
+
+    it('stringifies buffer values', function (done) {
+
+        expect(Qs.stringify({ a: new Buffer('test') })).to.equal('a=test');
+        expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a%5Bb%5D=test');
+        done();
+    });
+
+    it('stringifies an object using an alternative delimiter', function (done) {
+
+        expect(Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' })).to.equal('a=b;c=d');
+        done();
+    });
+
+    it('doesn\'t blow up when Buffer global is missing', function (done) {
+
+        var tempBuffer = global.Buffer;
+        delete global.Buffer;
+        expect(Qs.stringify({ a: 'b', c: 'd' })).to.equal('a=b&c=d');
+        global.Buffer = tempBuffer;
+        done();
+    });
+
+    it('selects properties when filter=array', function (done) {
+
+        expect(Qs.stringify({ a: 'b' }, { filter: ['a'] })).to.equal('a=b');
+        expect(Qs.stringify({ a: 1 }, { filter: [] })).to.equal('');
+        expect(Qs.stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] })).to.equal('a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3');
+        done();
+
+    });
+
+    it('supports custom representations when filter=function', function (done) {
+
+        var calls = 0;
+        var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
+        var filterFunc = function (prefix, value) {
+
+            calls++;
+            if (calls === 1) {
+                expect(prefix).to.be.empty();
+                expect(value).to.equal(obj);
+            }
+            else if (prefix === 'c') {
+                return;
+            }
+            else if (value instanceof Date) {
+                expect(prefix).to.equal('e[f]');
+                return value.getTime();
+            }
+            return value;
+        };
+
+        expect(Qs.stringify(obj, { filter: filterFunc })).to.equal('a=b&e%5Bf%5D=1257894000000');
+        expect(calls).to.equal(5);
+        done();
+
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/utils.js
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/utils.js b/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/utils.js
new file mode 100644
index 0000000..a9a6b52
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/qs/test/utils.js
@@ -0,0 +1,28 @@
+// Load modules
+
+var Code = require('code');
+var Lab = require('lab');
+var Utils = require('../lib/utils');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var expect = Code.expect;
+var describe = lab.experiment;
+var it = lab.test;
+
+
+describe('merge()', function () {
+
+    it('can merge two objects with the same key', function (done) {
+
+        expect(Utils.merge({ a: 'b' }, { a: 'c' })).to.deep.equal({ a: ['b', 'c'] });
+        done();
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/HISTORY.md
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/HISTORY.md b/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/HISTORY.md
new file mode 100644
index 0000000..1bb53bd
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/HISTORY.md
@@ -0,0 +1,35 @@
+1.0.2 / 2014-09-08
+==================
+
+  * Support Node.js 0.6
+
+1.0.1 / 2014-09-07
+==================
+
+  * Move repository to jshttp
+
+1.0.0 / 2013-12-11
+==================
+
+  * Add repository to package.json
+  * Add MIT license
+
+0.0.4 / 2012-06-17
+==================
+
+  * Change ret -1 for unsatisfiable and -2 when invalid
+
+0.0.3 / 2012-06-17
+==================
+
+  * Fix last-byte-pos default to len - 1
+
+0.0.2 / 2012-06-14
+==================
+
+  * Add `.type`
+
+0.0.1 / 2012-06-11
+==================
+
+  * Initial release

http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/1d2725bf/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/LICENSE
----------------------------------------------------------------------
diff --git a/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/LICENSE b/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/LICENSE
new file mode 100644
index 0000000..a491841
--- /dev/null
+++ b/node_modules/cordova-serve/node_modules/express/node_modules/range-parser/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012-2014 TJ Holowaychuk <vision-media.ca>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org