Update to NPM version

This commit is contained in:
Lucas Patenaude
2024-04-11 04:23:19 -06:00
parent 886d197fa7
commit 6d6ef4f257
8225 changed files with 863748 additions and 1 deletions

94
ProjectSourceCode/node_modules/chai-http/History.md generated vendored Normal file
View File

@@ -0,0 +1,94 @@
### Note
As of 2.0.0, the History.md file has been deprecated. [Please refer to the full
commit logs available on GitHub](https://github.com/chaijs/chai-http/commits/master).
---
1.0.0 / 2014-10-08
==================
* update readme
* Merge pull request #14 from keithamus/fix-master
* Fix #13: Convert unroutable addresses to localhost
* Fix #13: Add proper detection of Promise capabilities in tests
* Merge pull request #12 from keithamus/patch-1
* add travis badge
* add travis support
* Fix typo
* Merge pull request #11 from keithamus/refactor-agent
* (chore) Regenerate README.md
* Add list of contributors to package.json
* Add simple usage docs for request api
* Fix typo in readme
* Add cookie assertions
* Add URL query string parameter assertions
* Add request.agent() which allows persisting of cookies
* Add Promises support
* Listen on random port in tests - not 4000 which may be taken
* Add Test class to exports, to enable extending
* Drastically simplify chai.request() by inheriting superagent
* Update SuperAgent to v0.19.x
* Drop harbor and pauli deps.
0.5.0 / 2014-08-19
==================
* Merge pull request #9 from hurrymaplelad/assert_redirect
* Add redirect assertions
* Merge pull request #8 from lxanders/improveReadme
* Fixed missing word and improved wording in documentation
0.4.0 / 2013-06-25
==================
* support: [readme] cleanup
* Merge pull request #7 from pezra/newer-superagent
* remove previous additions to the history
* bump superagent version dependency
* Merge pull request #5 from wookiehangover/patch-1
* Fixing typo in Readme
* readme typo
0.3.0 / 2012-10-24
==================
* minor tweak to http docs
* Readme auto generation support
* readme preview
* improve readme code comments
* add readme
* documentation for request api
* update http assertion docs
* add request test for existing url
* support and tests for header existence
* improve type detection error message
* add support for superagent based testing
* code cleanup
* convert to tdd
* clean up test runner
0.2.0 / 2012-05-15
==================
* chai 1.0.0 compatibility
* update package.json
0.1.0 / 2012-03-10
==================
* Merge branch 'refs/heads/dev'
* git/npm ignore
* Add `Asssertion#ip`.
* Docs.
* Add test for Assertion#headers.
* Add test for Assertion#json, Assertion#html Assertion#text.
* Add test for Assertion#header.
* Add test for Assertion#status.
0.0.1 / 2012-03-06
==================
* prepping for release 0.0.1
* first lib commit
* initial commit

446
ProjectSourceCode/node_modules/chai-http/README.md generated vendored Normal file
View File

@@ -0,0 +1,446 @@
# Chai HTTP [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![NPM version](https://img.shields.io/npm/v/chai-http.svg)](https://img.shields.io/npm/v/chai-http.svg) [![Dependency Status](https://img.shields.io/david/chaijs/chai-http.svg)](https://img.shields.io/david/chaijs/chai-http.svg) [![devDependencies](https://david-dm.org/chaijs/chai-http/dev-status.svg)](https://david-dm.org/chaijs/chai-http/dev-status.svg)
> HTTP integration testing with Chai assertions.
#### Features
- integration test request composition
- test http apps or external services
- assertions for common http tasks
- chai `expect` and `should` interfaces
#### Installation
This is an addon plugin for the [Chai Assertion Library](http://chaijs.com). Install via [npm](http://npmjs.org).
npm install chai-http
#### Plugin
Use this plugin as you would all other Chai plugins.
```js
const chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
```
To use Chai HTTP on a web page, just include the [`dist/chai-http.js`](dist/chai-http.js) file:
```html
<script src="chai.js"></script>
<script src="chai-http.js"></script>
<script>
chai.use(chaiHttp);
</script>
```
## Integration Testing
Chai HTTP provides an interface for live integration
testing via [superagent](https://github.com/visionmedia/superagent).
To do this, you must first
construct a request to an application or url.
Upon construction you are provided a chainable api that
allows you to specify the http VERB request (get, post, etc)
that you wish to invoke.
#### Application / Server
You may use a function (such as an express or connect app)
or a node.js http(s) server as the foundation for your request.
If the server is not running, chai-http will find a suitable
port to listen on for a given test.
__Note:__ This feature is only supported on Node.js, not in web browsers.
```js
chai.request(app)
.get('/')
```
When passing an `app` to `request`; it will automatically open the server for
incoming requests (by calling `listen()`) and, once a request has been made
the server will automatically shut down (by calling `.close()`). If you want to
keep the server open, perhaps if you're making multiple requests, you must call
`.keepOpen()` after `.request()`, and manually close the server down:
```js
const requester = chai.request(app).keepOpen()
Promise.all([
requester.get('/a'),
requester.get('/b'),
])
.then(responses => { /* ... */ })
.then(() => requester.close())
```
#### URL
You may also use a base url as the foundation of your request.
```js
chai.request('http://localhost:8080')
.get('/')
```
#### Setting up requests
Once a request is created with a given VERB (get, post, etc), you chain on these additional methods to create your request:
| Method | Purpose |
|---|---|
| `.set(key, value)` | Set request headers |
| `.send(data)` | Set request data (default type is JSON) |
| `.type(dataType)` | Change the type of the data sent from the `.send()` method (xml, form, etc) |
| `.attach(field, file, attachment)` | Attach a file |
| `.auth(username, password)` | Add auth headers for Basic Authentication |
| `.query(parmasObject)` | Chain on some GET parameters |
Examples:
`.set()`
```js
// Set a request header
chai.request(app)
.put('/user/me')
.set('Content-Type', 'application/json')
.send({ password: '123', confirmPassword: '123' })
```
`.send()`
```js
// Send some JSON
chai.request(app)
.put('/user/me')
.send({ password: '123', confirmPassword: '123' })
```
`.type()`
```js
// Send some Form Data
chai.request(app)
.post('/user/me')
.type('form')
.send({
'_method': 'put',
'password': '123',
'confirmPassword': '123'
})
```
`.attach()`
```js
// Attach a file
chai.request(app)
.post('/user/avatar')
.attach('imageField', fs.readFileSync('avatar.png'), 'avatar.png')
```
`.auth()`
```js
// Authenticate with Basic authentication
chai.request(app)
.get('/protected')
.auth('user', 'pass')
// Authenticate with Bearer Token
chai.request(app)
.get('/protected')
.auth(accessToken, { type: 'bearer' })
```
`.query()`
```js
// Chain some GET query parameters
chai.request(app)
.get('/search')
.query({name: 'foo', limit: 10}) // /search?name=foo&limit=10
```
#### Dealing with the response - traditional
In the following examples we use Chai's Expect assertion library:
```js
const { expect } = chai;
```
To make the request and assert on its response, the `end` method can be used:
```js
chai.request(app)
.put('/user/me')
.send({ password: '123', confirmPassword: '123' })
.end((err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(200);
});
```
##### Caveat
Because the `end` function is passed a callback, assertions are run
asynchronously. Therefore, a mechanism must be used to notify the testing
framework that the callback has completed. Otherwise, the test will pass before
the assertions are checked.
For example, in the [Mocha test framework](http://mochajs.org/), this is
accomplished using the
[`done` callback](https://mochajs.org/#asynchronous-code), which signal that the
callback has completed, and the assertions can be verified:
```js
it('fails, as expected', function(done) { // <= Pass in done callback
chai.request('http://localhost:8080')
.get('/')
.end((err, res) => {
expect(res).to.have.status(123);
done(); // <= Call done to signal callback end
});
});
it('succeeds silently!', () => { // <= No done callback
chai.request('http://localhost:8080')
.get('/')
.end((err, res) => {
expect(res).to.have.status(123); // <= Test completes before this runs
});
});
```
When `done` is passed in, Mocha will wait until the call to `done()`, or until
the [timeout](http://mochajs.org/#timeouts) expires. `done` also accepts an
error parameter when signaling completion.
#### Dealing with the response - Promises
If `Promise` is available, `request()` becomes a Promise capable library -
and chaining of `then`s becomes possible:
```js
chai.request(app)
.put('/user/me')
.send({ password: '123', confirmPassword: '123' })
.then((res) => {
expect(res).to.have.status(200);
})
.catch((err) => {
throw err;
});
```
__Note:__ Some older web browsers do not have native promise support. You can use any spec compliant library, such as:
- [kriskowal/q](https://github.com/kriskowal/q)
- [stefanpenner/es6-promise](https://github.com/stefanpenner/es6-promise)
- [petkaantonov/bluebird](https://github.com/petkaantonov/bluebird)
- [then/promise](https://github.com/then/promise)
You will need to set the library you use to `global.Promise`, before
requiring in chai-http. For example:
```js
// Add promise support if this does not exist natively.
if (!global.Promise) {
global.Promise = require('q');
}
const chai = require('chai');
chai.use(require('chai-http'));
```
#### Retaining cookies with each request
Sometimes you need to keep cookies from one request, and send them with the
next (for example, when you want to login with the first request, then access an authenticated-only resource later). For this, `.request.agent()` is available:
```js
// Log in
const agent = chai.request.agent(app)
agent
.post('/session')
.send({ username: 'me', password: '123' })
.then((res) => {
expect(res).to.have.cookie('sessionid');
// The `agent` now has the sessionid cookie saved, and will send it
// back to the server in the next request:
return agent.get('/user/me')
.then((res) => {
expect(res).to.have.status(200);
});
});
```
Note: The server started by `chai.request.agent(app)` will not automatically close following the test(s). You should call `agent.close()` after your tests to ensure your program exits.
## Assertions
The Chai HTTP module provides a number of assertions
for the `expect` and `should` interfaces.
### .status (code)
* **@param** _{Number}_ status number
Assert that a response has a supplied status.
```js
expect(res).to.have.status(200);
```
### .header (key[, value])
* **@param** _{String}_ header key (case insensitive)
* **@param** _{String|RegExp}_ header value (optional)
Assert that a `Response` or `Request` object has a header.
If a value is provided, equality to value will be asserted.
You may also pass a regular expression to check.
__Note:__ When running in a web browser, the
[same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
only allows Chai HTTP to read
[certain headers](https://www.w3.org/TR/cors/#simple-response-header),
which can cause assertions to fail.
```js
expect(req).to.have.header('x-api-key');
expect(req).to.have.header('content-type', 'text/plain');
expect(req).to.have.header('content-type', /^text/);
```
### .headers
Assert that a `Response` or `Request` object has headers.
__Note:__ When running in a web browser, the
[same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
only allows Chai HTTP to read
[certain headers](https://www.w3.org/TR/cors/#simple-response-header),
which can cause assertions to fail.
```js
expect(req).to.have.headers;
```
### .ip
Assert that a string represents valid ip address.
```js
expect('127.0.0.1').to.be.an.ip;
expect('2001:0db8:85a3:0000:0000:8a2e:0370:7334').to.be.an.ip;
```
### .json / .text / .html
Assert that a `Response` or `Request` object has a given content-type.
```js
expect(req).to.be.json;
expect(req).to.be.html;
expect(req).to.be.text;
```
### .charset
Assert that a `Response` or `Request` object has a given charset.
```js
expect(req).to.have.charset('utf-8');
```
### .redirect
Assert that a `Response` object has a redirect status code.
```js
expect(res).to.redirect;
expect(res).to.not.redirect;
```
### .redirectTo
* **@param** _{String|RegExp}_ location url
Assert that a `Response` object redirects to the supplied location.
```js
expect(res).to.redirectTo('http://example.com');
expect(res).to.redirectTo(/^\/search\/results\?orderBy=desc$/);
```
### .param
* **@param** _{String}_ parameter name
* **@param** _{String}_ parameter value
Assert that a `Request` object has a query string parameter with a given
key, (optionally) equal to value
```js
expect(req).to.have.param('orderby');
expect(req).to.have.param('orderby', 'date');
expect(req).to.not.have.param('limit');
```
### .cookie
* **@param** _{String}_ parameter name
* **@param** _{String}_ parameter value
Assert that a `Request` or `Response` object has a cookie header with a
given key, (optionally) equal to value
```js
expect(req).to.have.cookie('session_id');
expect(req).to.have.cookie('session_id', '1234');
expect(req).to.not.have.cookie('PHPSESSID');
expect(res).to.have.cookie('session_id');
expect(res).to.have.cookie('session_id', '1234');
expect(res).to.not.have.cookie('PHPSESSID');
```
## Releasing
`chai-http` is released with [`semantic-release`](https://github.com/semantic-release/semantic-release) using the plugins:
- [`commit-analyzer`](https://github.com/semantic-release/commit-analyzer) to determine the next version from commit messages.
- [`release-notes-generator`](https://github.com/semantic-release/release-notes-generator) to summarize release in
- [`changelog`](https://github.com/semantic-release/changelog) to update the CHANGELOG.md file.
- [`github`](https://github.com/semantic-release/github) to publish a [GitHub release](https://github.com/chaijs/chai-http/releases).
- [`git`](https://github.com/semantic-release/git) to commit release assets.
- [`npm`](https://github.com/semantic-release/npm) to publish to [npm](https://www.npmjs.com/package/chai-http).
## License
(The MIT License)
Copyright (c) Jake Luer <jake@alogicalparadox.com>
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.

File diff suppressed because it is too large Load Diff

1
ProjectSourceCode/node_modules/chai-http/index.js generated vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = require('./lib/http');

423
ProjectSourceCode/node_modules/chai-http/lib/http.js generated vendored Normal file
View File

@@ -0,0 +1,423 @@
/*!
* chai-http
* Copyright(c) 2011-2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/**
* ## Assertions
*
* The Chai HTTP module provides a number of assertions
* for the `expect` and `should` interfaces.
*/
module.exports = function (chai, _) {
/*!
* Module dependencies.
*/
var net = require('net');
var qs = require('qs');
var url = require('url');
var Cookie = require('cookiejar');
var charset = require("charset");
/*!
* Aliases.
*/
var Assertion = chai.Assertion
, i = _.inspect;
/*!
* Expose request builder
*/
chai.request = require('./request');
/*!
* Content types hash. Used to
* define `Assertion` properties.
*
* @type {Object}
*/
var contentTypes = {
json: 'application/json'
, text: 'text/plain'
, html: 'text/html'
};
/*!
* Return a header from `Request` or `Response` object.
*
* @param {Request|Response} object
* @param {String} Header
* @returns {String|Undefined}
*/
function getHeader(obj, key) {
if (key) key = key.toLowerCase();
if (obj.getHeader) return obj.getHeader(key);
if (obj.headers) return obj.headers[key];
};
/**
* ### .status (code)
*
* Assert that a response has a supplied status.
*
* ```js
* expect(res).to.have.status(200);
* ```
*
* @param {Number} status number
* @name status
* @api public
*/
Assertion.addMethod('status', function (code) {
var hasStatus = Boolean('status' in this._obj || 'statusCode' in this._obj);
new Assertion(hasStatus).assert(
hasStatus
, "expected #{act} to have keys 'status', or 'statusCode'"
, null // never negated
, hasStatus // expected
, this._obj // actual
, false // no diff
);
var status = this._obj.status || this._obj.statusCode;
this.assert(
status == code
, 'expected #{this} to have status code #{exp} but got #{act}'
, 'expected #{this} to not have status code #{act}'
, code
, status
);
});
/**
* ### .header (key[, value])
*
* Assert that a `Response` or `Request` object has a header.
* If a value is provided, equality to value will be asserted.
* You may also pass a regular expression to check.
*
* __Note:__ When running in a web browser, the
* [same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
* only allows Chai HTTP to read
* [certain headers](https://www.w3.org/TR/cors/#simple-response-header),
* which can cause assertions to fail.
*
* ```js
* expect(req).to.have.header('x-api-key');
* expect(req).to.have.header('content-type', 'text/plain');
* expect(req).to.have.header('content-type', /^text/);
* ```
*
* @param {String} header key (case insensitive)
* @param {String|RegExp} header value (optional)
* @name header
* @api public
*/
Assertion.addMethod('header', function (key, value) {
var header = getHeader(this._obj, key);
if (arguments.length < 2) {
this.assert(
'undefined' !== typeof header || null === header
, 'expected header \'' + key + '\' to exist'
, 'expected header \'' + key + '\' to not exist'
);
} else if (arguments[1] instanceof RegExp) {
this.assert(
value.test(header)
, 'expected header \'' + key + '\' to match ' + value + ' but got ' + i(header)
, 'expected header \'' + key + '\' not to match ' + value + ' but got ' + i(header)
, value
, header
);
} else {
this.assert(
header == value
, 'expected header \'' + key + '\' to have value ' + value + ' but got ' + i(header)
, 'expected header \'' + key + '\' to not have value ' + value
, value
, header
);
}
});
/**
* ### .headers
*
* Assert that a `Response` or `Request` object has headers.
*
* __Note:__ When running in a web browser, the
* [same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
* only allows Chai HTTP to read
* [certain headers](https://www.w3.org/TR/cors/#simple-response-header),
* which can cause assertions to fail.
*
* ```js
* expect(req).to.have.headers;
* ```
*
* @name headers
* @api public
*/
Assertion.addProperty('headers', function () {
this.assert(
this._obj.headers || this._obj.getHeader
, 'expected #{this} to have headers or getHeader method'
, 'expected #{this} to not have headers or getHeader method'
);
});
/**
* ### .ip
*
* Assert that a string represents valid ip address.
*
* ```js
* expect('127.0.0.1').to.be.an.ip;
* expect('2001:0db8:85a3:0000:0000:8a2e:0370:7334').to.be.an.ip;
* ```
*
* @name ip
* @api public
*/
Assertion.addProperty('ip', function () {
this.assert(
net.isIP(this._obj)
, 'expected #{this} to be an ip'
, 'expected #{this} to not be an ip'
);
});
/**
* ### .json / .text / .html
*
* Assert that a `Response` or `Request` object has a given content-type.
*
* ```js
* expect(req).to.be.json;
* expect(req).to.be.html;
* expect(req).to.be.text;
* ```
*
* @name json
* @name html
* @name text
* @api public
*/
function checkContentType (name) {
var val = contentTypes[name];
Assertion.addProperty(name, function () {
new Assertion(this._obj).to.have.headers;
var ct = getHeader(this._obj, 'content-type')
, ins = i(ct) === 'undefined'
? 'headers'
: i(ct);
this.assert(
ct && ~ct.indexOf(val)
, 'expected ' + ins + ' to include \'' + val + '\''
, 'expected ' + ins + ' to not include \'' + val + '\''
);
});
}
Object
.keys(contentTypes)
.forEach(checkContentType);
/**
* ### .charset
*
* Assert that a `Response` or `Request` object has a given charset.
*
* ```js
* expect(req).to.have.charset('utf-8');
* ```
*
* @name charset
* @api public
*/
Assertion.addMethod('charset', function (value) {
value = value.toLowerCase();
var headers = this._obj.headers;
var cs = charset(headers);
/*
* Fix charset() treating "utf8" as a special case
* See https://github.com/node-modules/charset/issues/12
*/
if (cs === "utf8") {
cs = "utf-8";
}
this.assert(
cs != null && value === cs
, 'expected content type to have ' + value + ' charset'
, 'expected content type to not have ' + value + ' charset'
)
});
/**
* ### .redirect
*
* Assert that a `Response` object has a redirect status code.
*
* ```js
* expect(res).to.redirect;
* ```
*
* @name redirect
* @api public
*/
Assertion.addProperty('redirect', function() {
var redirectCodes = [301, 302, 303, 307, 308]
, status = this._obj.status
, redirects = this._obj.redirects;
this.assert(
redirectCodes.indexOf(status) >= 0 || redirects && redirects.length
, "expected redirect with 30X status code but got " + status
, "expected not to redirect but got " + status + " status"
);
});
/**
* ### .redirectTo
*
* Assert that a `Response` object redirects to the supplied location.
*
* ```js
* expect(res).to.redirectTo('http://example.com');
* ```
*
* @param {String|RegExp} location url
* @name redirectTo
* @api public
*/
Assertion.addMethod('redirectTo', function(destination) {
var redirects = this._obj.redirects;
new Assertion(this._obj).to.redirect;
if(redirects && redirects.length) {
var hasRedirected;
if (Object.prototype.toString.call(destination) === '[object RegExp]') {
hasRedirected = redirects.some(redirect => destination.test(redirect));
} else {
hasRedirected = redirects.indexOf(destination) > -1;
}
this.assert(
hasRedirected
, 'expected redirect to ' + destination + ' but got ' + redirects.join(' then ')
, 'expected not to redirect to ' + destination + ' but got ' + redirects.join(' then ')
);
} else {
var assertion = new Assertion(this._obj);
_.transferFlags(this, assertion);
assertion.with.header('location', destination);
}
});
/**
* ### .param
*
* Assert that a `Request` object has a query string parameter with a given
* key, (optionally) equal to value
*
* ```js
* expect(req).to.have.param('orderby');
* expect(req).to.have.param('orderby', 'date');
* expect(req).to.not.have.param('limit');
* ```
*
* @param {String} parameter name
* @param {String} parameter value
* @name param
* @api public
*/
Assertion.addMethod('param', function(name, value) {
var assertion = new Assertion();
_.transferFlags(this, assertion);
assertion._obj = qs.parse(url.parse(this._obj.url).query);
assertion.property.apply(assertion, arguments);
});
/**
* ### .cookie
*
* Assert that a `Request`, `Response` or `Agent` object has a cookie header with a
* given key, (optionally) equal to value
*
* ```js
* expect(req).to.have.cookie('session_id');
* expect(req).to.have.cookie('session_id', '1234');
* expect(req).to.not.have.cookie('PHPSESSID');
* expect(res).to.have.cookie('session_id');
* expect(res).to.have.cookie('session_id', '1234');
* expect(res).to.not.have.cookie('PHPSESSID');
* expect(agent).to.have.cookie('session_id');
* expect(agent).to.have.cookie('session_id', '1234');
* expect(agent).to.not.have.cookie('PHPSESSID');
* ```
*
* @param {String} parameter name
* @param {String} parameter value
* @name param
* @api public
*/
Assertion.addMethod('cookie', function (key, value) {
var header = getHeader(this._obj, 'set-cookie')
, cookie;
if (!header) {
header = (getHeader(this._obj, 'cookie') || '').split(';');
}
if (this._obj instanceof chai.request.agent && this._obj.jar) {
cookie = this._obj.jar.getCookie(key, Cookie.CookieAccessInfo.All);
} else {
cookie = Cookie.CookieJar();
cookie.setCookies(header);
cookie = cookie.getCookie(key, Cookie.CookieAccessInfo.All);
}
if (arguments.length === 2) {
this.assert(
cookie.value == value
, 'expected cookie \'' + key + '\' to have value #{exp} but got #{act}'
, 'expected cookie \'' + key + '\' to not have value #{exp}'
, value
, cookie.value
);
} else {
this.assert(
'undefined' !== typeof cookie || null === cookie
, 'expected cookie \'' + key + '\' to exist'
, 'expected cookie \'' + key + '\' to not exist'
);
}
});
};

14
ProjectSourceCode/node_modules/chai-http/lib/net.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
/*!
* chai-http - request helper
* Copyright(c) 2011-2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/*!
* net.isIP shim for browsers
*/
var isIP = require('is-ip');
exports.isIP = isIP;
exports.isIPv4 = isIP.v4;
exports.isIPv6 = isIP.v6;

353
ProjectSourceCode/node_modules/chai-http/lib/request.js generated vendored Normal file
View File

@@ -0,0 +1,353 @@
/*!
* chai-http - request helper
* Copyright(c) 2011-2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/*!
* Module dependancies
*/
var http = require('http')
, https = require('https')
, methods = require('methods')
, superagent = require('superagent')
, Agent = superagent.agent
, Request = superagent.Request
, util = require('util');
/**
* ## Integration Testing
*
* Chai HTTP provides an interface for live integration
* testing via [superagent](https://github.com/visionmedia/superagent).
* To do this, you must first
* construct a request to an application or url.
*
* Upon construction you are provided a chainable api that
* allows you to specify the http VERB request (get, post, etc)
* that you wish to invoke.
*
* #### Application / Server
*
* You may use a function (such as an express or connect app)
* or a node.js http(s) server as the foundation for your request.
* If the server is not running, chai-http will find a suitable
* port to listen on for a given test.
*
* __Note:__ This feature is only supported on Node.js, not in web browsers.
*
* ```js
* chai.request(app)
* .get('/')
* ```
*
* #### URL
*
* You may also use a base url as the foundation of your request.
*
* ```js
* chai.request('http://localhost:8080')
* .get('/')
* ```
*
* #### Setting up requests
*
* Once a request is created with a given VERB, it can have headers, form data,
* json, or even file attachments added to it, all with a simple API:
*
* ```js
* // Send some JSON
* chai.request(app)
* .put('/user/me')
* .set('X-API-Key', 'foobar')
* .send({ password: '123', confirmPassword: '123' })
* ```
*
* ```js
* // Send some Form Data
* chai.request(app)
* .post('/user/me')
* .type('form')
* .send({'_method': 'put'})
* .send({'password': '123'})
* .send({'confirmPassword', '123'})
* ```
*
* ```js
* // Attach a file
* chai.request(app)
* .post('/user/avatar')
* .attach('imageField', fs.readFileSync('avatar.png'), 'avatar.png')
* ```
*
* ```js
* // Authenticate with Basic authentication
* chai.request(app)
* .get('/protected')
* .auth('user', 'pass')
* ```
*
* ```js
* // Chain some GET query parameters
* chai.request(app)
* .get('/search')
* .query({name: 'foo', limit: 10}) // /search?name=foo&limit=10
* ```
*
* #### Dealing with the response - traditional
*
* To make the request and assert on its response, the `end` method can be used:
*
* ```js
* chai.request(app)
* .put('/user/me')
* .send({ password: '123', confirmPassword: '123' })
* .end(function (err, res) {
* expect(err).to.be.null;
* expect(res).to.have.status(200);
* });
* ```
* ##### Caveat
* Because the `end` function is passed a callback, assertions are run
* asynchronously. Therefore, a mechanism must be used to notify the testing
* framework that the callback has completed. Otherwise, the test will pass before
* the assertions are checked.
*
* For example, in the [Mocha test framework](http://mochajs.org/), this is
* accomplished using the
* [`done` callback](https://mochajs.org/#asynchronous-code), which signal that the
* callback has completed, and the assertions can be verified:
*
* ```js
* it('fails, as expected', function(done) { // <= Pass in done callback
* chai.request('http://localhost:8080')
* .get('/')
* .end(function(err, res) {
* expect(res).to.have.status(123);
* done(); // <= Call done to signal callback end
* });
* }) ;
*
* it('succeeds silently!', function() { // <= No done callback
* chai.request('http://localhost:8080')
* .get('/')
* .end(function(err, res) {
* expect(res).to.have.status(123); // <= Test completes before this runs
* });
* }) ;
* ```
*
* When `done` is passed in, Mocha will wait until the call to `done()`, or until
* the [timeout](http://mochajs.org/#timeouts) expires. `done` also accepts an
* error parameter when signaling completion.
*
* #### Dealing with the response - Promises
*
* If `Promise` is available, `request()` becomes a Promise capable library -
* and chaining of `then`s becomes possible:
*
* ```js
* chai.request(app)
* .put('/user/me')
* .send({ password: '123', confirmPassword: '123' })
* .then(function (res) {
* expect(res).to.have.status(200);
* })
* .catch(function (err) {
* throw err;
* })
* ```
*
* __Note:__ Node.js version 0.10.x and some older web browsers do not have
* native promise support. You can use any spec compliant library, such as:
* - [kriskowal/q](https://github.com/kriskowal/q)
* - [stefanpenner/es6-promise](https://github.com/stefanpenner/es6-promise)
* - [petkaantonov/bluebird](https://github.com/petkaantonov/bluebird)
* - [then/promise](https://github.com/then/promise)
* You will need to set the library you use to `global.Promise`, before
* requiring in chai-http. For example:
*
* ```js
* // Add promise support if this does not exist natively.
* if (!global.Promise) {
* global.Promise = require('q');
* }
* var chai = require('chai');
* chai.use(require('chai-http'));
*
* ```
*
* #### Retaining cookies with each request
*
* Sometimes you need to keep cookies from one request, and send them with the
* next. For this, `.request.agent()` is available:
*
* ```js
* // Log in
* var agent = chai.request.agent(app)
* agent
* .post('/session')
* .send({ username: 'me', password: '123' })
* .then(function (res) {
* expect(res).to.have.cookie('sessionid');
* // The `agent` now has the sessionid cookie saved, and will send it
* // back to the server in the next request:
* return agent.get('/user/me')
* .then(function (res) {
* expect(res).to.have.status(200);
* })
* })
* ```
*
*/
module.exports = function (app) {
/*!
* @param {Mixed} function or server
* @returns {Object} API
*/
var server = ('function' === typeof app)
? http.createServer(app)
: app
, obj = {};
var keepOpen = false
if (typeof server !== 'string' && server && server.listen && server.address) {
if (!server.address()) {
server = server.listen(0)
}
}
obj.keepOpen = function() {
keepOpen = true
return this
}
obj.close = function(callback) {
if (server && server.close) {
server.close(callback);
}
else if(callback) {
callback();
}
return this
}
methods.forEach(function (method) {
obj[method] = function (path) {
return new Test(server, method, path)
.on('end', function() {
if(keepOpen === false) {
obj.close();
}
});
};
});
obj.del = obj.delete;
return obj;
};
module.exports.Test = Test;
module.exports.Request = Test;
module.exports.agent = TestAgent;
/*!
* Test
*
* An extension of superagent.Request,
* this provides the same chainable api
* as superagent so all things can be modified.
*
* @param {Object|String} server, app, or url
* @param {String} method
* @param {String} path
* @api private
*/
function Test (app, method, path) {
Request.call(this, method, path);
this.app = app;
this.url = typeof app === 'string' ? app + path : serverAddress(app, path);
this.ok(function() {
return true;
});
}
util.inherits(Test, Request);
function serverAddress (app, path) {
if ('string' === typeof app) {
return app + path;
}
var addr = app.address();
if (!addr) {
throw new Error('Server is not listening')
}
var protocol = (app instanceof https.Server) ? 'https' : 'http';
// If address is "unroutable" IPv4/6 address, then set to localhost
if (addr.address === '0.0.0.0' || addr.address === '::') {
addr.address = '127.0.0.1';
}
return protocol + '://' + addr.address + ':' + addr.port + path;
}
/*!
* agent
*
* Follows the same API as superagent.Request,
* but allows persisting of cookies between requests.
*
* @param {Object|String} server, app, or url
* @param {String} method
* @param {String} path
* @api private
*/
function TestAgent(app) {
if (!(this instanceof TestAgent)) return new TestAgent(app);
if (typeof app === 'function') app = http.createServer(app);
(Agent || Request).call(this);
this.app = app;
if (typeof app !== 'string' && app && app.listen && app.address && !app.address()) {
this.app = app.listen(0)
}
}
util.inherits(TestAgent, Agent || Request);
TestAgent.prototype.close = function close(callback) {
if (this.app && this.app.close) {
this.app.close(callback)
}
return this
}
TestAgent.prototype.keepOpen = function keepOpen() {
return this
}
// override HTTP verb methods
methods.forEach(function(method){
TestAgent.prototype[method] = function(url){
var req = new Test(this.app, method, url)
, self = this;
if (Agent) {
// When running in Node, cookies are managed via
// `Agent._saveCookies()` and `Agent._attachCookies()`.
req.on('response', function (res) { self._saveCookies(res); });
req.on('redirect', function (res) { self._saveCookies(res); });
req.on('redirect', function () { self._attachCookies(req); });
this._attachCookies(req);
}
else {
// When running in a web browser, cookies are managed via `Request.withCredentials()`.
// The browser will attach cookies based on same-origin policy.
// https://tools.ietf.org/html/rfc6454#section-3
req.withCredentials();
}
return req;
};
});
TestAgent.prototype.del = TestAgent.prototype.delete;

View File

@@ -0,0 +1,43 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 160
quote_type = single
[test/*]
max_line_length = off
[LICENSE.md]
indent_size = off
[*.md]
max_line_length = off
[*.json]
max_line_length = off
[Makefile]
max_line_length = off
[CHANGELOG.md]
indent_style = space
indent_size = 2
[LICENSE]
indent_size = 2
max_line_length = off
[coverage/**/*]
indent_size = off
indent_style = off
indent = off
max_line_length = off
[.nycrc]
indent_style = tab

View File

@@ -0,0 +1,38 @@
{
"root": true,
"extends": "@ljharb",
"ignorePatterns": [
"dist/",
],
"rules": {
"complexity": 0,
"consistent-return": 1,
"func-name-matching": 0,
"id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
"indent": [2, 4],
"max-lines-per-function": [2, { "max": 150 }],
"max-params": [2, 18],
"max-statements": [2, 100],
"multiline-comment-style": 0,
"no-continue": 1,
"no-magic-numbers": 0,
"no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
},
"overrides": [
{
"files": "test/**",
"rules": {
"function-paren-newline": 0,
"max-lines-per-function": 0,
"max-statements": 0,
"no-buffer-constructor": 0,
"no-extend-native": 0,
"no-throw-literal": 0,
},
},
],
}

View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/qs
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL

View File

@@ -0,0 +1,13 @@
{
"all": true,
"check-coverage": false,
"reporter": ["text-summary", "text", "html", "json"],
"lines": 86,
"statements": 85.93,
"functions": 82.43,
"branches": 76.06,
"exclude": [
"coverage",
"dist"
]
}

View File

@@ -0,0 +1,582 @@
## **6.12.0**
- [New] `parse`/`stringify`: add `decodeDotInKeys`/`encodeDotKeys` options (#488)
- [New] `parse`: add `duplicates` option
- [New] `parse`/`stringify`: add `allowEmptyArrays` option to allow [] in object values (#487)
- [Refactor] `parse`/`stringify`: move allowDots config logic to its own variable
- [Refactor] `stringify`: move option-handling code into `normalizeStringifyOptions`
- [readme] update readme, add logos (#484)
- [readme] `stringify`: clarify default `arrayFormat` behavior
- [readme] fix line wrapping
- [readme] remove dead badges
- [Deps] update `side-channel`
- [meta] make the dist build 50% smaller
- [meta] add `sideEffects` flag
- [meta] run build in prepack, not prepublish
- [Tests] `parse`: remove useless tests; add coverage
- [Tests] `stringify`: increase coverage
- [Tests] use `mock-property`
- [Tests] `stringify`: improve coverage
- [Dev Deps] update `@ljharb/eslint-config `, `aud`, `has-override-mistake`, `has-property-descriptors`, `mock-property`, `npmignore`, `object-inspect`, `tape`
- [Dev Deps] pin `glob`, since v10.3.8+ requires a broken `jackspeak`
- [Dev Deps] pin `jackspeak` since 2.1.2+ depends on npm aliases, which kill the install process in npm < 6
## **6.11.2**
- [Fix] `parse`: Fix parsing when the global Object prototype is frozen (#473)
- [Tests] add passing test cases with empty keys (#473)
## **6.11.1**
- [Fix] `stringify`: encode comma values more consistently (#463)
- [readme] add usage of `filter` option for injecting custom serialization, i.e. of custom types (#447)
- [meta] remove extraneous code backticks (#457)
- [meta] fix changelog markdown
- [actions] update checkout action
- [actions] restrict action permissions
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `object-inspect`, `tape`
## **6.11.0**
- [New] [Fix] `stringify`: revert 0e903c0; add `commaRoundTrip` option (#442)
- [readme] fix version badge
## **6.10.5**
- [Fix] `stringify`: with `arrayFormat: comma`, properly include an explicit `[]` on a single-item array (#434)
## **6.10.4**
- [Fix] `stringify`: with `arrayFormat: comma`, include an explicit `[]` on a single-item array (#441)
- [meta] use `npmignore` to autogenerate an npmignore file
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-symbol`, `object-inspect`, `tape`
## **6.10.3**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [actions] reuse common workflows
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `tape`
## **6.10.2**
- [Fix] `stringify`: actually fix cyclic references (#426)
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
- [readme] remove travis badge; add github actions/codecov badges; update URLs
- [Docs] add note and links for coercing primitive values (#408)
- [actions] update codecov uploader
- [actions] update workflows
- [Tests] clean up stringify tests slightly
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `object-inspect`, `safe-publish-latest`, `tape`
## **6.10.1**
- [Fix] `stringify`: avoid exception on repeated object values (#402)
## **6.10.0**
- [New] `stringify`: throw on cycles, instead of an infinite loop (#395, #394, #393)
- [New] `parse`: add `allowSparse` option for collapsing arrays with missing indices (#312)
- [meta] fix README.md (#399)
- [meta] only run `npm run dist` in publish, not install
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-symbols`, `tape`
- [Tests] fix tests on node v0.6
- [Tests] use `ljharb/actions/node/install` instead of `ljharb/actions/node/run`
- [Tests] Revert "[meta] ignore eclint transitive audit warning"
## **6.9.7**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [readme] remove travis badge; add github actions/codecov badges; update URLs
- [Docs] add note and links for coercing primitive values (#408)
- [Tests] clean up stringify tests slightly
- [meta] fix README.md (#399)
- Revert "[meta] ignore eclint transitive audit warning"
- [actions] backport actions from main
- [Dev Deps] backport updates from main
## **6.9.6**
- [Fix] restore `dist` dir; mistakenly removed in d4f6c32
## **6.9.5**
- [Fix] `stringify`: do not encode parens for RFC1738
- [Fix] `stringify`: fix arrayFormat comma with empty array/objects (#350)
- [Refactor] `format`: remove `util.assign` call
- [meta] add "Allow Edits" workflow; update rebase workflow
- [actions] switch Automatic Rebase workflow to `pull_request_target` event
- [Tests] `stringify`: add tests for #378
- [Tests] migrate tests to Github Actions
- [Tests] run `nyc` on all tests; use `tape` runner
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `mkdirp`, `object-inspect`, `tape`; add `aud`
## **6.9.4**
- [Fix] `stringify`: when `arrayFormat` is `comma`, respect `serializeDate` (#364)
- [Refactor] `stringify`: reduce branching (part of #350)
- [Refactor] move `maybeMap` to `utils`
- [Dev Deps] update `browserify`, `tape`
## **6.9.3**
- [Fix] proper comma parsing of URL-encoded commas (#361)
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
## **6.9.2**
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
- [meta] ignore eclint transitive audit warning
- [meta] fix indentation in package.json
- [meta] add tidelift marketing copy
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `has-symbols`, `tape`, `mkdirp`, `iconv-lite`
- [actions] add automatic rebasing / merge commit blocking
## **6.9.1**
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
- [Fix] `parse`: with comma true, do not split non-string values (#334)
- [meta] add `funding` field
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`
- [Tests] use shared travis-ci config
## **6.9.0**
- [New] `parse`/`stringify`: Pass extra key/value argument to `decoder` (#333)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `evalmd`
- [Tests] `parse`: add passing `arrayFormat` tests
- [Tests] add `posttest` using `npx aud` to run `npm audit` without a lockfile
- [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
## **6.8.3**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
- [readme] remove travis badge; add github actions/codecov badges; update URLs
- [Tests] clean up stringify tests slightly
- [Docs] add note and links for coercing primitive values (#408)
- [meta] fix README.md (#399)
- [actions] backport actions from main
- [Dev Deps] backport updates from main
- [Refactor] `stringify`: reduce branching
- [meta] do not publish workflow files
## **6.8.2**
- [Fix] proper comma parsing of URL-encoded commas (#361)
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
## **6.8.1**
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
- [fix] `parse`: with comma true, do not split non-string values (#334)
- [meta] add tidelift marketing copy
- [meta] add `funding` field
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `has-symbols`, `iconv-lite`, `mkdirp`, `object-inspect`
- [Tests] `parse`: add passing `arrayFormat` tests
- [Tests] use shared travis-ci configs
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
- [actions] add automatic rebasing / merge commit blocking
## **6.8.0**
- [New] add `depth=false` to preserve the original key; [Fix] `depth=0` should preserve the original key (#326)
- [New] [Fix] stringify symbols and bigints
- [Fix] ensure node 0.12 can stringify Symbols
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
- [Refactor] `formats`: tiny bit of cleanup.
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `safe-publish-latest`, `iconv-lite`, `tape`
- [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended (#326)
- [Tests] use `eclint` instead of `editorconfig-tools`
- [docs] readme: add security note
- [meta] add github sponsorship
- [meta] add FUNDING.yml
- [meta] Clean up license text so its properly detected as BSD-3-Clause
## **6.7.3**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [readme] remove travis badge; add github actions/codecov badges; update URLs
- [Docs] add note and links for coercing primitive values (#408)
- [meta] fix README.md (#399)
- [meta] do not publish workflow files
- [actions] backport actions from main
- [Dev Deps] backport updates from main
- [Tests] use `nyc` for coverage
- [Tests] clean up stringify tests slightly
## **6.7.2**
- [Fix] proper comma parsing of URL-encoded commas (#361)
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
## **6.7.1**
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
- [fix] `parse`: with comma true, do not split non-string values (#334)
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
- [Refactor] `formats`: tiny bit of cleanup.
- readme: add security note
- [meta] add tidelift marketing copy
- [meta] add `funding` field
- [meta] add FUNDING.yml
- [meta] Clean up license text so its properly detected as BSD-3-Clause
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `iconv-lite`, `mkdirp`, `object-inspect`, `browserify`
- [Tests] `parse`: add passing `arrayFormat` tests
- [Tests] use shared travis-ci configs
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
- [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended
- [Tests] use `eclint` instead of `editorconfig-tools`
- [actions] add automatic rebasing / merge commit blocking
## **6.7.0**
- [New] `stringify`/`parse`: add `comma` as an `arrayFormat` option (#276, #219)
- [Fix] correctly parse nested arrays (#212)
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source, also with an array source
- [Robustness] `stringify`: cache `Object.prototype.hasOwnProperty`
- [Refactor] `utils`: `isBuffer`: small tweak; add tests
- [Refactor] use cached `Array.isArray`
- [Refactor] `parse`/`stringify`: make a function to normalize the options
- [Refactor] `utils`: reduce observable [[Get]]s
- [Refactor] `stringify`/`utils`: cache `Array.isArray`
- [Tests] always use `String(x)` over `x.toString()`
- [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
- [Tests] temporarily allow coverage to fail
## **6.6.1**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
- [Fix] correctly parse nested arrays
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [Robustness] `stringify`: cache `Object.prototype.hasOwnProperty`
- [Refactor] `formats`: tiny bit of cleanup.
- [Refactor] `utils`: `isBuffer`: small tweak; add tests
- [Refactor]: `stringify`/`utils`: cache `Array.isArray`
- [Refactor] `utils`: reduce observable [[Get]]s
- [Refactor] use cached `Array.isArray`
- [Refactor] `parse`/`stringify`: make a function to normalize the options
- [readme] remove travis badge; add github actions/codecov badges; update URLs
- [Docs] Clarify the need for "arrayLimit" option
- [meta] fix README.md (#399)
- [meta] do not publish workflow files
- [meta] Clean up license text so its properly detected as BSD-3-Clause
- [meta] add FUNDING.yml
- [meta] Fixes typo in CHANGELOG.md
- [actions] backport actions from main
- [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
- [Tests] always use `String(x)` over `x.toString()`
- [Dev Deps] backport from main
## **6.6.0**
- [New] Add support for iso-8859-1, utf8 "sentinel" and numeric entities (#268)
- [New] move two-value combine to a `utils` function (#189)
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]` (#260)
- [Fix] `stringify`: do not crash in an obscure combo of `interpretNumericEntities`, a bad custom `decoder`, & `iso-8859-1`
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
- [refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
- [Refactor] `parse`: only need to reassign the var once
- [Refactor] `parse`/`stringify`: clean up `charset` options checking; fix defaults
- [Refactor] add missing defaults
- [Refactor] `parse`: one less `concat` call
- [Refactor] `utils`: `compactQueue`: make it explicitly side-effecting
- [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`, `iconv-lite`, `safe-publish-latest`, `tape`
- [Tests] up to `node` `v10.10`, `v9.11`, `v8.12`, `v6.14`, `v4.9`; pin included builds to LTS
## **6.5.3**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
- [Fix] correctly parse nested arrays
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
- [Refactor] `utils`: reduce observable [[Get]]s
- [Refactor] use cached `Array.isArray`
- [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
- [Refactor] `parse`: only need to reassign the var once
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [readme] remove travis badge; add github actions/codecov badges; update URLs
- [Docs] Clean up license text so its properly detected as BSD-3-Clause
- [Docs] Clarify the need for "arrayLimit" option
- [meta] fix README.md (#399)
- [meta] add FUNDING.yml
- [actions] backport actions from main
- [Tests] always use `String(x)` over `x.toString()`
- [Tests] remove nonexistent tape option
- [Dev Deps] backport from main
## **6.5.2**
- [Fix] use `safer-buffer` instead of `Buffer` constructor
- [Refactor] utils: `module.exports` one thing, instead of mutating `exports` (#230)
- [Dev Deps] update `browserify`, `eslint`, `iconv-lite`, `safer-buffer`, `tape`, `browserify`
## **6.5.1**
- [Fix] Fix parsing & compacting very deep objects (#224)
- [Refactor] name utils functions
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`
- [Tests] up to `node` `v8.4`; use `nvm install-latest-npm` so newer npm doesnt break older node
- [Tests] Use precise dist for Node.js 0.6 runtime (#225)
- [Tests] make 0.6 required, now that its passing
- [Tests] on `node` `v8.2`; fix npm on node 0.6
## **6.5.0**
- [New] add `utils.assign`
- [New] pass default encoder/decoder to custom encoder/decoder functions (#206)
- [New] `parse`/`stringify`: add `ignoreQueryPrefix`/`addQueryPrefix` options, respectively (#213)
- [Fix] Handle stringifying empty objects with addQueryPrefix (#217)
- [Fix] do not mutate `options` argument (#207)
- [Refactor] `parse`: cache index to reuse in else statement (#182)
- [Docs] add various badges to readme (#208)
- [Dev Deps] update `eslint`, `browserify`, `iconv-lite`, `tape`
- [Tests] up to `node` `v8.1`, `v7.10`, `v6.11`; npm v4.6 breaks on node < v1; npm v5+ breaks on node < v4
- [Tests] add `editorconfig-tools`
## **6.4.1**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
- [Fix] use `safer-buffer` instead of `Buffer` constructor
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [Refactor] use cached `Array.isArray`
- [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
- [readme] remove travis badge; add github actions/codecov badges; update URLs
- [Docs] Clarify the need for "arrayLimit" option
- [meta] fix README.md (#399)
- [meta] Clean up license text so its properly detected as BSD-3-Clause
- [meta] add FUNDING.yml
- [actions] backport actions from main
- [Tests] remove nonexistent tape option
- [Dev Deps] backport from main
## **6.4.0**
- [New] `qs.stringify`: add `encodeValuesOnly` option
- [Fix] follow `allowPrototypes` option during merge (#201, #201)
- [Fix] support keys starting with brackets (#202, #200)
- [Fix] chmod a-x
- [Dev Deps] update `eslint`
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
- [eslint] reduce warnings
## **6.3.3**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [Refactor] use cached `Array.isArray`
- [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
- [Docs] Clarify the need for "arrayLimit" option
- [meta] fix README.md (#399)
- [meta] Clean up license text so its properly detected as BSD-3-Clause
- [meta] add FUNDING.yml
- [actions] backport actions from main
- [Tests] use `safer-buffer` instead of `Buffer` constructor
- [Tests] remove nonexistent tape option
- [Dev Deps] backport from main
## **6.3.2**
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Dev Deps] update `eslint`
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.3.1**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties (thanks, @snyk!)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `iconv-lite`, `qs-iconv`, `tape`
- [Tests] on all node minors; improve test matrix
- [Docs] document stringify option `allowDots` (#195)
- [Docs] add empty object and array values example (#195)
- [Docs] Fix minor inconsistency/typo (#192)
- [Docs] document stringify option `sort` (#191)
- [Refactor] `stringify`: throw faster with an invalid encoder
- [Refactor] remove unnecessary escapes (#184)
- Remove contributing.md, since `qs` is no longer part of `hapi` (#183)
## **6.3.0**
- [New] Add support for RFC 1738 (#174, #173)
- [New] `stringify`: Add `serializeDate` option to customize Date serialization (#159)
- [Fix] ensure `utils.merge` handles merging two arrays
- [Refactor] only constructors should be capitalized
- [Refactor] capitalized var names are for constructors only
- [Refactor] avoid using a sparse array
- [Robustness] `formats`: cache `String#replace`
- [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`; add `safe-publish-latest`
- [Tests] up to `node` `v6.8`, `v4.6`; improve test matrix
- [Tests] flesh out arrayLimit/arrayFormat tests (#107)
- [Tests] skip Object.create tests when null objects are not available
- [Tests] Turn on eslint for test files (#175)
## **6.2.4**
- [Fix] `parse`: ignore `__proto__` keys (#428)
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
- [Refactor] use cached `Array.isArray`
- [Docs] Clarify the need for "arrayLimit" option
- [meta] fix README.md (#399)
- [meta] Clean up license text so its properly detected as BSD-3-Clause
- [meta] add FUNDING.yml
- [actions] backport actions from main
- [Tests] use `safer-buffer` instead of `Buffer` constructor
- [Tests] remove nonexistent tape option
- [Dev Deps] backport from main
## **6.2.3**
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.2.2**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
## **6.2.1**
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
- [Refactor] Be explicit and use `Object.prototype.hasOwnProperty.call`
- [Tests] remove `parallelshell` since it does not reliably report failures
- [Tests] up to `node` `v6.3`, `v5.12`
- [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`, `qs-iconv`
## [**6.2.0**](https://github.com/ljharb/qs/issues?milestone=36&state=closed)
- [New] pass Buffers to the encoder/decoder directly (#161)
- [New] add "encoder" and "decoder" options, for custom param encoding/decoding (#160)
- [Fix] fix compacting of nested sparse arrays (#150)
## **6.1.2**
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.1.1**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
## [**6.1.0**](https://github.com/ljharb/qs/issues?milestone=35&state=closed)
- [New] allowDots option for `stringify` (#151)
- [Fix] "sort" option should work at a depth of 3 or more (#151)
- [Fix] Restore `dist` directory; will be removed in v7 (#148)
## **6.0.4**
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.0.3**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
- [Fix] Restore `dist` directory; will be removed in v7 (#148)
## [**6.0.2**](https://github.com/ljharb/qs/issues?milestone=33&state=closed)
- Revert ES6 requirement and restore support for node down to v0.8.
## [**6.0.1**](https://github.com/ljharb/qs/issues?milestone=32&state=closed)
- [**#127**](https://github.com/ljharb/qs/pull/127) Fix engines definition in package.json
## [**6.0.0**](https://github.com/ljharb/qs/issues?milestone=31&state=closed)
- [**#124**](https://github.com/ljharb/qs/issues/124) Use ES6 and drop support for node < v4
## **5.2.1**
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
## [**5.2.0**](https://github.com/ljharb/qs/issues?milestone=30&state=closed)
- [**#64**](https://github.com/ljharb/qs/issues/64) Add option to sort object keys in the query string
## [**5.1.0**](https://github.com/ljharb/qs/issues?milestone=29&state=closed)
- [**#117**](https://github.com/ljharb/qs/issues/117) make URI encoding stringified results optional
- [**#106**](https://github.com/ljharb/qs/issues/106) Add flag `skipNulls` to optionally skip null values in stringify
## [**5.0.0**](https://github.com/ljharb/qs/issues?milestone=28&state=closed)
- [**#114**](https://github.com/ljharb/qs/issues/114) default allowDots to false
- [**#100**](https://github.com/ljharb/qs/issues/100) include dist to npm
## [**4.0.0**](https://github.com/ljharb/qs/issues?milestone=26&state=closed)
- [**#98**](https://github.com/ljharb/qs/issues/98) make returning plain objects and allowing prototype overwriting properties optional
## [**3.1.0**](https://github.com/ljharb/qs/issues?milestone=24&state=closed)
- [**#89**](https://github.com/ljharb/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
## [**3.0.0**](https://github.com/ljharb/qs/issues?milestone=23&state=closed)
- [**#80**](https://github.com/ljharb/qs/issues/80) qs.parse silently drops properties
- [**#77**](https://github.com/ljharb/qs/issues/77) Perf boost
- [**#60**](https://github.com/ljharb/qs/issues/60) Add explicit option to disable array parsing
- [**#74**](https://github.com/ljharb/qs/issues/74) Bad parse when turning array into object
- [**#81**](https://github.com/ljharb/qs/issues/81) Add a `filter` option
- [**#68**](https://github.com/ljharb/qs/issues/68) Fixed issue with recursion and passing strings into objects.
- [**#66**](https://github.com/ljharb/qs/issues/66) Add mixed array and object dot notation support Closes: #47
- [**#76**](https://github.com/ljharb/qs/issues/76) RFC 3986
- [**#85**](https://github.com/ljharb/qs/issues/85) No equal sign
- [**#84**](https://github.com/ljharb/qs/issues/84) update license attribute
## [**2.4.1**](https://github.com/ljharb/qs/issues?milestone=20&state=closed)
- [**#73**](https://github.com/ljharb/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
## [**2.4.0**](https://github.com/ljharb/qs/issues?milestone=19&state=closed)
- [**#70**](https://github.com/ljharb/qs/issues/70) Add arrayFormat option
## [**2.3.3**](https://github.com/ljharb/qs/issues?milestone=18&state=closed)
- [**#59**](https://github.com/ljharb/qs/issues/59) make sure array indexes are >= 0, closes #57
- [**#58**](https://github.com/ljharb/qs/issues/58) make qs usable for browser loader
## [**2.3.2**](https://github.com/ljharb/qs/issues?milestone=17&state=closed)
- [**#55**](https://github.com/ljharb/qs/issues/55) allow merging a string into an object
## [**2.3.1**](https://github.com/ljharb/qs/issues?milestone=16&state=closed)
- [**#52**](https://github.com/ljharb/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
## [**2.3.0**](https://github.com/ljharb/qs/issues?milestone=15&state=closed)
- [**#50**](https://github.com/ljharb/qs/issues/50) add option to omit array indices, closes #46
## [**2.2.5**](https://github.com/ljharb/qs/issues?milestone=14&state=closed)
- [**#39**](https://github.com/ljharb/qs/issues/39) Is there an alternative to Buffer.isBuffer?
- [**#49**](https://github.com/ljharb/qs/issues/49) refactor utils.merge, fixes #45
- [**#41**](https://github.com/ljharb/qs/issues/41) avoid browserifying Buffer, for #39
## [**2.2.4**](https://github.com/ljharb/qs/issues?milestone=13&state=closed)
- [**#38**](https://github.com/ljharb/qs/issues/38) how to handle object keys beginning with a number
## [**2.2.3**](https://github.com/ljharb/qs/issues?milestone=12&state=closed)
- [**#37**](https://github.com/ljharb/qs/issues/37) parser discards first empty value in array
- [**#36**](https://github.com/ljharb/qs/issues/36) Update to lab 4.x
## [**2.2.2**](https://github.com/ljharb/qs/issues?milestone=11&state=closed)
- [**#33**](https://github.com/ljharb/qs/issues/33) Error when plain object in a value
- [**#34**](https://github.com/ljharb/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
- [**#24**](https://github.com/ljharb/qs/issues/24) Changelog? Semver?
## [**2.2.1**](https://github.com/ljharb/qs/issues?milestone=10&state=closed)
- [**#32**](https://github.com/ljharb/qs/issues/32) account for circular references properly, closes #31
- [**#31**](https://github.com/ljharb/qs/issues/31) qs.parse stackoverflow on circular objects
## [**2.2.0**](https://github.com/ljharb/qs/issues?milestone=9&state=closed)
- [**#26**](https://github.com/ljharb/qs/issues/26) Don't use Buffer global if it's not present
- [**#30**](https://github.com/ljharb/qs/issues/30) Bug when merging non-object values into arrays
- [**#29**](https://github.com/ljharb/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
- [**#23**](https://github.com/ljharb/qs/issues/23) Ability to not limit parameters?
## [**2.1.0**](https://github.com/ljharb/qs/issues?milestone=8&state=closed)
- [**#22**](https://github.com/ljharb/qs/issues/22) Enable using a RegExp as delimiter
## [**2.0.0**](https://github.com/ljharb/qs/issues?milestone=7&state=closed)
- [**#18**](https://github.com/ljharb/qs/issues/18) Why is there arrayLimit?
- [**#20**](https://github.com/ljharb/qs/issues/20) Configurable parametersLimit
- [**#21**](https://github.com/ljharb/qs/issues/21) make all limits optional, for #18, for #20
## [**1.2.2**](https://github.com/ljharb/qs/issues?milestone=6&state=closed)
- [**#19**](https://github.com/ljharb/qs/issues/19) Don't overwrite null values
## [**1.2.1**](https://github.com/ljharb/qs/issues?milestone=5&state=closed)
- [**#16**](https://github.com/ljharb/qs/issues/16) ignore non-string delimiters
- [**#15**](https://github.com/ljharb/qs/issues/15) Close code block
## [**1.2.0**](https://github.com/ljharb/qs/issues?milestone=4&state=closed)
- [**#12**](https://github.com/ljharb/qs/issues/12) Add optional delim argument
- [**#13**](https://github.com/ljharb/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
## [**1.1.0**](https://github.com/ljharb/qs/issues?milestone=3&state=closed)
- [**#7**](https://github.com/ljharb/qs/issues/7) Empty values of a POST array disappear after being submitted
- [**#9**](https://github.com/ljharb/qs/issues/9) Should not omit equals signs (=) when value is null
- [**#6**](https://github.com/ljharb/qs/issues/6) Minor grammar fix in README
## [**1.0.2**](https://github.com/ljharb/qs/issues?milestone=2&state=closed)
- [**#5**](https://github.com/ljharb/qs/issues/5) array holes incorrectly copied into object on large index

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/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:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder nor the names of its
contributors may 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 HOLDER OR 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.

View File

@@ -0,0 +1,697 @@
<p align="center">
<img alt="qs" src="./logos/banner_default.png" width="800" />
</p>
# qs <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
[![github actions][actions-image]][actions-url]
[![coverage][codecov-image]][codecov-url]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]
[![npm badge][npm-badge-png]][package-url]
A querystring parsing and stringifying library with some added security.
Lead Maintainer: [Jordan Harband](https://github.com/ljharb)
The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).
## Usage
```javascript
var qs = require('qs');
var assert = require('assert');
var obj = qs.parse('a=c');
assert.deepEqual(obj, { a: 'c' });
var str = qs.stringify(obj);
assert.equal(str, 'a=c');
```
### Parsing Objects
[](#preventEval)
```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 `[]`.
For example, the string `'foo[bar]=baz'` converts to:
```javascript
assert.deepEqual(qs.parse('foo[bar]=baz'), {
foo: {
bar: 'baz'
}
});
```
When using the `plainObjects` option the parsed value is returned as a null 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
var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
assert.deepEqual(nullObject, { 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
var protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });
assert.deepEqual(protoObject, { a: { hasOwnProperty: 'b' } });
```
URI encoded strings work too:
```javascript
assert.deepEqual(qs.parse('a%5Bb%5D=c'), {
a: { b: 'c' }
});
```
You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
```javascript
assert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), {
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
var expected = {
a: {
b: {
c: {
d: {
e: {
f: {
'[g][h][i]': 'j'
}
}
}
}
}
}
};
var string = 'a[b][c][d][e][f][g][h][i]=j';
assert.deepEqual(qs.parse(string), expected);
```
This depth can be overridden by passing a `depth` option to `qs.parse(string, [options])`:
```javascript
var deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
assert.deepEqual(deep, { 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
var limited = qs.parse('a=b&c=d', { parameterLimit: 1 });
assert.deepEqual(limited, { a: 'b' });
```
To bypass the leading question mark, use `ignoreQueryPrefix`:
```javascript
var prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });
assert.deepEqual(prefixed, { a: 'b', c: 'd' });
```
An optional delimiter can also be passed:
```javascript
var delimited = qs.parse('a=b;c=d', { delimiter: ';' });
assert.deepEqual(delimited, { a: 'b', c: 'd' });
```
Delimiters can be a regular expression too:
```javascript
var regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
assert.deepEqual(regexed, { a: 'b', c: 'd', e: 'f' });
```
Option `allowDots` can be used to enable dot notation:
```javascript
var withDots = qs.parse('a.b=c', { allowDots: true });
assert.deepEqual(withDots, { a: { b: 'c' } });
```
Option `decodeDotInKeys` can be used to decode dots in keys
Note: it implies `allowDots`, so `parse` will error if you set `decodeDotInKeys` to `true`, and `allowDots` to `false`.
```javascript
var withDots = qs.parse('name%252Eobj.first=John&name%252Eobj.last=Doe', { decodeDotInKeys: true });
assert.deepEqual(withDots, { 'name.obj': { first: 'John', last: 'Doe' }});
```
Option `allowEmptyArrays` can be used to allowing empty array values in object
```javascript
var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
```
Option `duplicates` can be used to change the behavior when duplicate keys are encountered
```javascript
assert.deepEqual(qs.parse('foo=bar&foo=baz'), { foo: ['bar', 'baz'] });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'combine' }), { foo: ['bar', 'baz'] });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'first' }), { foo: 'bar' });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'last' }), { foo: 'baz' });
```
If you have to deal with legacy browsers or services, there's also support for decoding percent-encoded octets as iso-8859-1:
```javascript
var oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
assert.deepEqual(oldCharset, { a: '§' });
```
Some services add an initial `utf8=✓` value to forms so that old Internet Explorer versions are more likely to submit the form as utf-8.
Additionally, the server can check the value against wrong encodings of the checkmark character and detect that a query string or `application/x-www-form-urlencoded` body was *not* sent as utf-8, eg. if the form had an `accept-charset` parameter or the containing page had a different character set.
**qs** supports this mechanism via the `charsetSentinel` option.
If specified, the `utf8` parameter will be omitted from the returned object.
It will be used to switch to `iso-8859-1`/`utf-8` mode depending on how the checkmark is encoded.
**Important**: When you specify both the `charset` option and the `charsetSentinel` option, the `charset` will be overridden when the request contains a `utf8` parameter from which the actual charset can be deduced.
In that sense the `charset` will behave as the default charset rather than the authoritative charset.
```javascript
var detectedAsUtf8 = qs.parse('utf8=%E2%9C%93&a=%C3%B8', {
charset: 'iso-8859-1',
charsetSentinel: true
});
assert.deepEqual(detectedAsUtf8, { a: 'ø' });
// Browsers encode the checkmark as &#10003; when submitting as iso-8859-1:
var detectedAsIso8859_1 = qs.parse('utf8=%26%2310003%3B&a=%F8', {
charset: 'utf-8',
charsetSentinel: true
});
assert.deepEqual(detectedAsIso8859_1, { a: 'ø' });
```
If you want to decode the `&#...;` syntax to the actual character, you can specify the `interpretNumericEntities` option as well:
```javascript
var detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
charset: 'iso-8859-1',
interpretNumericEntities: true
});
assert.deepEqual(detectedAsIso8859_1, { a: '☺' });
```
It also works when the charset has been detected in `charsetSentinel` mode.
### Parsing Arrays
**qs** can also parse arrays using a similar `[]` notation:
```javascript
var withArray = qs.parse('a[]=b&a[]=c');
assert.deepEqual(withArray, { a: ['b', 'c'] });
```
You may specify an index as well:
```javascript
var withIndexes = qs.parse('a[1]=c&a[0]=b');
assert.deepEqual(withIndexes, { 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
var noSparse = qs.parse('a[1]=b&a[15]=c');
assert.deepEqual(noSparse, { a: ['b', 'c'] });
```
You may also use `allowSparse` option to parse sparse arrays:
```javascript
var sparseArray = qs.parse('a[1]=2&a[3]=5', { allowSparse: true });
assert.deepEqual(sparseArray, { a: [, '2', , '5'] });
```
Note that an empty string is also a value, and will be preserved:
```javascript
var withEmptyString = qs.parse('a[]=&a[]=b');
assert.deepEqual(withEmptyString, { a: ['', 'b'] });
var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');
assert.deepEqual(withIndexedEmptyString, { 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.
This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
```javascript
var withMaxIndex = qs.parse('a[100]=b');
assert.deepEqual(withMaxIndex, { a: { '100': 'b' } });
```
This limit can be overridden by passing an `arrayLimit` option:
```javascript
var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });
assert.deepEqual(withArrayLimit, { a: { '1': 'b' } });
```
To disable array parsing entirely, set `parseArrays` to `false`.
```javascript
var noParsingArrays = qs.parse('a[]=b', { parseArrays: false });
assert.deepEqual(noParsingArrays, { a: { '0': 'b' } });
```
If you mix notations, **qs** will merge the two items into an object:
```javascript
var mixedNotation = qs.parse('a[0]=b&a[b]=c');
assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });
```
You can also create arrays of objects:
```javascript
var arraysOfObjects = qs.parse('a[][b]=c');
assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
```
Some people use comma to join array, **qs** can parse it:
```javascript
var arraysOfObjects = qs.parse('a=b,c', { comma: true })
assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
```
(_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
### Parsing primitive/scalar values (numbers, booleans, null, etc)
By default, all values are parsed as strings.
This behavior will not change and is explained in [issue #91](https://github.com/ljharb/qs/issues/91).
```javascript
var primitiveValues = qs.parse('a=15&b=true&c=null');
assert.deepEqual(primitiveValues, { a: '15', b: 'true', c: 'null' });
```
If you wish to auto-convert values which look like numbers, booleans, and other values into their primitive counterparts, you can use the [query-types Express JS middleware](https://github.com/xpepermint/query-types) which will auto-convert all request query parameters.
### Stringifying
[](#preventEval)
```javascript
qs.stringify(object, [options]);
```
When stringifying, **qs** by default URI encodes output. Objects are stringified as you would expect:
```javascript
assert.equal(qs.stringify({ a: 'b' }), 'a=b');
assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
```
This encoding can be disabled by setting the `encode` option to `false`:
```javascript
var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });
assert.equal(unencoded, 'a[b]=c');
```
Encoding can be disabled for keys by setting the `encodeValuesOnly` option to `true`:
```javascript
var encodedValues = qs.stringify(
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
{ encodeValuesOnly: true }
);
assert.equal(encodedValues,'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h');
```
This encoding can also be replaced by a custom encoding method set as `encoder` option:
```javascript
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) {
// Passed in values `a`, `b`, `c`
return // Return encoded string
}})
```
_(Note: the `encoder` option does not apply if `encode` is `false`)_
Analogue to the `encoder` there is a `decoder` option for `parse` to override decoding of properties and values:
```javascript
var decoded = qs.parse('x=z', { decoder: function (str) {
// Passed in values `x`, `z`
return // Return decoded string
}})
```
You can encode keys and values using different logic by using the type argument provided to the encoder:
```javascript
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
if (type === 'key') {
return // Encoded key
} else if (type === 'value') {
return // Encoded value
}
}})
```
The type argument is also provided to the decoder:
```javascript
var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
if (type === 'key') {
return // Decoded key
} else if (type === 'value') {
return // Decoded value
}
}})
```
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, they follow the `arrayFormat` option, which defaults to `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`, or to be more explicit, the `arrayFormat` option to `repeat`:
```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'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
// 'a=b,c'
```
Note: when using `arrayFormat` set to `'comma'`, you can also pass the `commaRoundTrip` option set to `true` or `false`, to append `[]` on single-item arrays, so that they can round trip through a parse.
When objects are stringified, by default they use bracket notation:
```javascript
qs.stringify({ a: { b: { c: 'd', e: 'f' } } });
// 'a[b][c]=d&a[b][e]=f'
```
You may override this to use dot notation by setting the `allowDots` option to `true`:
```javascript
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });
// 'a.b.c=d&a.b.e=f'
```
You may encode the dot notation in the keys of object with option `encodeDotInKeys` by setting it to `true`:
Note: it implies `allowDots`, so `stringify` will error if you set `decodeDotInKeys` to `true`, and `allowDots` to `false`.
Caveat: when `encodeValuesOnly` is `true` as well as `encodeDotInKeys`, only dots in keys and nothing else will be encoded.
```javascript
qs.stringify({ "name.obj": { "first": "John", "last": "Doe" } }, { allowDots: true, encodeDotInKeys: true })
// 'name%252Eobj.first=John&name%252Eobj.last=Doe'
```
You may allow empty array values by setting the `allowEmptyArrays` option to `true`:
```javascript
qs.stringify({ foo: [], bar: 'baz' }, { allowEmptyArrays: true });
// 'foo[]&bar=baz'
```
Empty strings and null values will omit the value, but the equals sign (=) remains in place:
```javascript
assert.equal(qs.stringify({ a: '' }), 'a=');
```
Key with no values (such as an empty object or array) will return nothing:
```javascript
assert.equal(qs.stringify({ a: [] }), '');
assert.equal(qs.stringify({ a: {} }), '');
assert.equal(qs.stringify({ a: [{}] }), '');
assert.equal(qs.stringify({ a: { b: []} }), '');
assert.equal(qs.stringify({ a: { b: {}} }), '');
```
Properties that are set to `undefined` will be omitted entirely:
```javascript
assert.equal(qs.stringify({ a: null, b: undefined }), 'a=');
```
The query string may optionally be prepended with a question mark:
```javascript
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
```
The delimiter may be overridden with stringify as well:
```javascript
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
```
If you only want to override the serialization of `Date` objects, you can provide a `serializeDate` option:
```javascript
var date = new Date(7);
assert.equal(qs.stringify({ a: date }), 'a=1970-01-01T00:00:00.007Z'.replace(/:/g, '%3A'));
assert.equal(
qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } }),
'a=7'
);
```
You may use the `sort` option to affect the order of parameter keys:
```javascript
function alphabeticalSort(a, b) {
return a.localeCompare(b);
}
assert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }), 'a=c&b=f&z=y');
```
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'
```
You could also use `filter` to inject custom serialization for user defined types.
Consider you're working with some api that expects query strings of the format for ranges:
```
https://domain.com/endpoint?range=30...70
```
For which you model as:
```javascript
class Range {
constructor(from, to) {
this.from = from;
this.to = to;
}
}
```
You could _inject_ a custom serializer to handle values of this type:
```javascript
qs.stringify(
{
range: new Range(30, 70),
},
{
filter: (prefix, value) => {
if (value instanceof Range) {
return `${value.from}...${value.to}`;
}
// serialize the usual way
return value;
},
}
);
// range=30...70
```
### Handling of `null` values
By default, `null` values are treated like empty strings:
```javascript
var withNull = qs.stringify({ a: null, b: '' });
assert.equal(withNull, 'a=&b=');
```
Parsing does not distinguish between parameters with and without equal signs.
Both are converted to empty strings.
```javascript
var equalsInsensitive = qs.parse('a&b=');
assert.deepEqual(equalsInsensitive, { 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
var strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
assert.equal(strictNull, 'a&b=');
```
To parse values without `=` back to `null` use the `strictNullHandling` flag:
```javascript
var parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true });
assert.deepEqual(parsedStrictNull, { a: null, b: '' });
```
To completely skip rendering keys with `null` values, use the `skipNulls` flag:
```javascript
var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
assert.equal(nullsSkipped, 'a=b');
```
If you're communicating with legacy systems, you can switch to `iso-8859-1` using the `charset` option:
```javascript
var iso = qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' });
assert.equal(iso, '%E6=%E6');
```
Characters that don't exist in `iso-8859-1` will be converted to numeric entities, similar to what browsers do:
```javascript
var numeric = qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' });
assert.equal(numeric, 'a=%26%239786%3B');
```
You can use the `charsetSentinel` option to announce the character by including an `utf8=✓` parameter with the proper encoding if the checkmark, similar to what Ruby on Rails and others do when submitting forms.
```javascript
var sentinel = qs.stringify({ a: '☺' }, { charsetSentinel: true });
assert.equal(sentinel, 'utf8=%E2%9C%93&a=%E2%98%BA');
var isoSentinel = qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' });
assert.equal(isoSentinel, 'utf8=%26%2310003%3B&a=%E6');
```
### Dealing with special character sets
By default the encoding and decoding of characters is done in `utf-8`, and `iso-8859-1` support is also built in via the `charset` parameter.
If you wish to encode querystrings to a different character set (i.e.
[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the
[`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library:
```javascript
var encoder = require('qs-iconv/encoder')('shift_jis');
var shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder });
assert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I');
```
This also works for decoding of query strings:
```javascript
var decoder = require('qs-iconv/decoder')('shift_jis');
var obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder });
assert.deepEqual(obj, { a: 'こんにちは!' });
```
### RFC 3986 and RFC 1738 space encoding
RFC3986 used as default option and encodes ' ' to *%20* which is backward compatible.
In the same time, output can be stringified as per RFC1738 with ' ' equal to '+'.
```
assert.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
```
## Security
Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
## qs for enterprise
Available as part of the Tidelift Subscription
The maintainers of qs and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications.
Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.
[Learn more.](https://tidelift.com/subscription/pkg/npm-qs?utm_source=npm-qs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
[package-url]: https://npmjs.org/package/qs
[npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
[deps-svg]: https://david-dm.org/ljharb/qs.svg
[deps-url]: https://david-dm.org/ljharb/qs
[dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
[dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
[npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
[license-image]: https://img.shields.io/npm/l/qs.svg
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/qs.svg
[downloads-url]: https://npm-stat.com/charts.html?package=qs
[codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/ljharb/qs/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
[actions-url]: https://github.com/ljharb/qs/actions
## Acknowledgements
qs logo by [NUMI](https://github.com/numi-hq/open-design):
[<img src="https://raw.githubusercontent.com/numi-hq/open-design/main/assets/numi-lockup.png" alt="NUMI Logo" style="width: 200px;"/>](https://numi.tech/?ref=qs)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
'use strict';
var replace = String.prototype.replace;
var percentTwenties = /%20/g;
var Format = {
RFC1738: 'RFC1738',
RFC3986: 'RFC3986'
};
module.exports = {
'default': Format.RFC3986,
formatters: {
RFC1738: function (value) {
return replace.call(value, percentTwenties, '+');
},
RFC3986: function (value) {
return String(value);
}
},
RFC1738: Format.RFC1738,
RFC3986: Format.RFC3986
};

View File

@@ -0,0 +1,11 @@
'use strict';
var stringify = require('./stringify');
var parse = require('./parse');
var formats = require('./formats');
module.exports = {
formats: formats,
parse: parse,
stringify: stringify
};

View File

@@ -0,0 +1,288 @@
'use strict';
var utils = require('./utils');
var has = Object.prototype.hasOwnProperty;
var isArray = Array.isArray;
var defaults = {
allowDots: false,
allowEmptyArrays: false,
allowPrototypes: false,
allowSparse: false,
arrayLimit: 20,
charset: 'utf-8',
charsetSentinel: false,
comma: false,
decodeDotInKeys: true,
decoder: utils.decode,
delimiter: '&',
depth: 5,
duplicates: 'combine',
ignoreQueryPrefix: false,
interpretNumericEntities: false,
parameterLimit: 1000,
parseArrays: true,
plainObjects: false,
strictNullHandling: false
};
var interpretNumericEntities = function (str) {
return str.replace(/&#(\d+);/g, function ($0, numberStr) {
return String.fromCharCode(parseInt(numberStr, 10));
});
};
var parseArrayValue = function (val, options) {
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
return val.split(',');
}
return val;
};
// This is what browsers will submit when the ✓ character occurs in an
// application/x-www-form-urlencoded body and the encoding of the page containing
// the form is iso-8859-1, or when the submitted form has an accept-charset
// attribute of iso-8859-1. Presumably also with other charsets that do not contain
// the ✓ character, such as us-ascii.
var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('&#10003;')
// These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded.
var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
var parseValues = function parseQueryStringValues(str, options) {
var obj = { __proto__: null };
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
var parts = cleanStr.split(options.delimiter, limit);
var skipIndex = -1; // Keep track of where the utf8 sentinel was found
var i;
var charset = options.charset;
if (options.charsetSentinel) {
for (i = 0; i < parts.length; ++i) {
if (parts[i].indexOf('utf8=') === 0) {
if (parts[i] === charsetSentinel) {
charset = 'utf-8';
} else if (parts[i] === isoSentinel) {
charset = 'iso-8859-1';
}
skipIndex = i;
i = parts.length; // The eslint settings do not allow break;
}
}
}
for (i = 0; i < parts.length; ++i) {
if (i === skipIndex) {
continue;
}
var part = parts[i];
var bracketEqualsPos = part.indexOf(']=');
var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1;
var key, val;
if (pos === -1) {
key = options.decoder(part, defaults.decoder, charset, 'key');
val = options.strictNullHandling ? null : '';
} else {
key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
val = utils.maybeMap(
parseArrayValue(part.slice(pos + 1), options),
function (encodedVal) {
return options.decoder(encodedVal, defaults.decoder, charset, 'value');
}
);
}
if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
val = interpretNumericEntities(val);
}
if (part.indexOf('[]=') > -1) {
val = isArray(val) ? [val] : val;
}
var existing = has.call(obj, key);
if (existing && options.duplicates === 'combine') {
obj[key] = utils.combine(obj[key], val);
} else if (!existing || options.duplicates === 'last') {
obj[key] = val;
}
}
return obj;
};
var parseObject = function (chain, val, options, valuesParsed) {
var leaf = valuesParsed ? val : parseArrayValue(val, options);
for (var i = chain.length - 1; i >= 0; --i) {
var obj;
var root = chain[i];
if (root === '[]' && options.parseArrays) {
obj = options.allowEmptyArrays && leaf === '' ? [] : [].concat(leaf);
} else {
obj = options.plainObjects ? Object.create(null) : {};
var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
var decodedRoot = options.decodeDotInKeys ? cleanRoot.replace(/%2E/g, '.') : cleanRoot;
var index = parseInt(decodedRoot, 10);
if (!options.parseArrays && decodedRoot === '') {
obj = { 0: leaf };
} else if (
!isNaN(index)
&& root !== decodedRoot
&& String(index) === decodedRoot
&& index >= 0
&& (options.parseArrays && index <= options.arrayLimit)
) {
obj = [];
obj[index] = leaf;
} else if (decodedRoot !== '__proto__') {
obj[decodedRoot] = leaf;
}
}
leaf = obj;
}
return leaf;
};
var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
if (!givenKey) {
return;
}
// Transform dot notation to bracket notation
var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
// The regex chunks
var brackets = /(\[[^[\]]*])/;
var child = /(\[[^[\]]*])/g;
// Get the parent
var segment = options.depth > 0 && brackets.exec(key);
var parent = segment ? key.slice(0, segment.index) : key;
// Stash the parent if it exists
var keys = [];
if (parent) {
// If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties
if (!options.plainObjects && has.call(Object.prototype, parent)) {
if (!options.allowPrototypes) {
return;
}
}
keys.push(parent);
}
// Loop through children appending to the array until we hit depth
var i = 0;
while (options.depth > 0 && (segment = child.exec(key)) !== null && i < options.depth) {
i += 1;
if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) {
if (!options.allowPrototypes) {
return;
}
}
keys.push(segment[1]);
}
// If there's a remainder, just add whatever is left
if (segment) {
keys.push('[' + key.slice(segment.index) + ']');
}
return parseObject(keys, val, options, valuesParsed);
};
var normalizeParseOptions = function normalizeParseOptions(opts) {
if (!opts) {
return defaults;
}
if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') {
throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided');
}
if (typeof opts.decodeDotInKeys !== 'undefined' && typeof opts.decodeDotInKeys !== 'boolean') {
throw new TypeError('`decodeDotInKeys` option can only be `true` or `false`, when provided');
}
if (opts.decoder !== null && typeof opts.decoder !== 'undefined' && typeof opts.decoder !== 'function') {
throw new TypeError('Decoder has to be a function.');
}
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
}
var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset;
var duplicates = typeof opts.duplicates === 'undefined' ? defaults.duplicates : opts.duplicates;
if (duplicates !== 'combine' && duplicates !== 'first' && duplicates !== 'last') {
throw new TypeError('The duplicates option must be either combine, first, or last');
}
var allowDots = typeof opts.allowDots === 'undefined' ? opts.decodeDotInKeys === true ? true : defaults.allowDots : !!opts.allowDots;
return {
allowDots: allowDots,
allowEmptyArrays: typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays,
allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,
arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
charset: charset,
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,
decodeDotInKeys: typeof opts.decodeDotInKeys === 'boolean' ? opts.decodeDotInKeys : defaults.decodeDotInKeys,
decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,
delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
// eslint-disable-next-line no-implicit-coercion, no-extra-parens
depth: (typeof opts.depth === 'number' || opts.depth === false) ? +opts.depth : defaults.depth,
duplicates: duplicates,
ignoreQueryPrefix: opts.ignoreQueryPrefix === true,
interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities,
parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit,
parseArrays: opts.parseArrays !== false,
plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects,
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
};
};
module.exports = function (str, opts) {
var options = normalizeParseOptions(opts);
if (str === '' || str === null || typeof str === 'undefined') {
return options.plainObjects ? Object.create(null) : {};
}
var tempObj = typeof str === 'string' ? 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; i < keys.length; ++i) {
var key = keys[i];
var newObj = parseKeys(key, tempObj[key], options, typeof str === 'string');
obj = utils.merge(obj, newObj, options);
}
if (options.allowSparse === true) {
return obj;
}
return utils.compact(obj);
};

View File

@@ -0,0 +1,351 @@
'use strict';
var getSideChannel = require('side-channel');
var utils = require('./utils');
var formats = require('./formats');
var has = Object.prototype.hasOwnProperty;
var arrayPrefixGenerators = {
brackets: function brackets(prefix) {
return prefix + '[]';
},
comma: 'comma',
indices: function indices(prefix, key) {
return prefix + '[' + key + ']';
},
repeat: function repeat(prefix) {
return prefix;
}
};
var isArray = Array.isArray;
var push = Array.prototype.push;
var pushToArray = function (arr, valueOrArray) {
push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
};
var toISO = Date.prototype.toISOString;
var defaultFormat = formats['default'];
var defaults = {
addQueryPrefix: false,
allowDots: false,
allowEmptyArrays: false,
arrayFormat: 'indices',
charset: 'utf-8',
charsetSentinel: false,
delimiter: '&',
encode: true,
encodeDotInKeys: false,
encoder: utils.encode,
encodeValuesOnly: false,
format: defaultFormat,
formatter: formats.formatters[defaultFormat],
// deprecated
indices: false,
serializeDate: function serializeDate(date) {
return toISO.call(date);
},
skipNulls: false,
strictNullHandling: false
};
var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
return typeof v === 'string'
|| typeof v === 'number'
|| typeof v === 'boolean'
|| typeof v === 'symbol'
|| typeof v === 'bigint';
};
var sentinel = {};
var stringify = function stringify(
object,
prefix,
generateArrayPrefix,
commaRoundTrip,
allowEmptyArrays,
strictNullHandling,
skipNulls,
encodeDotInKeys,
encoder,
filter,
sort,
allowDots,
serializeDate,
format,
formatter,
encodeValuesOnly,
charset,
sideChannel
) {
var obj = object;
var tmpSc = sideChannel;
var step = 0;
var findFlag = false;
while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {
// Where object last appeared in the ref tree
var pos = tmpSc.get(object);
step += 1;
if (typeof pos !== 'undefined') {
if (pos === step) {
throw new RangeError('Cyclic object value');
} else {
findFlag = true; // Break while
}
}
if (typeof tmpSc.get(sentinel) === 'undefined') {
step = 0;
}
}
if (typeof filter === 'function') {
obj = filter(prefix, obj);
} else if (obj instanceof Date) {
obj = serializeDate(obj);
} else if (generateArrayPrefix === 'comma' && isArray(obj)) {
obj = utils.maybeMap(obj, function (value) {
if (value instanceof Date) {
return serializeDate(value);
}
return value;
});
}
if (obj === null) {
if (strictNullHandling) {
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;
}
obj = '';
}
if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
if (encoder) {
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];
}
return [formatter(prefix) + '=' + formatter(String(obj))];
}
var values = [];
if (typeof obj === 'undefined') {
return values;
}
var objKeys;
if (generateArrayPrefix === 'comma' && isArray(obj)) {
// we need to join elements in
if (encodeValuesOnly && encoder) {
obj = utils.maybeMap(obj, encoder);
}
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
} else if (isArray(filter)) {
objKeys = filter;
} else {
var keys = Object.keys(obj);
objKeys = sort ? keys.sort(sort) : keys;
}
var encodedPrefix = encodeDotInKeys ? prefix.replace(/\./g, '%2E') : prefix;
var adjustedPrefix = commaRoundTrip && isArray(obj) && obj.length === 1 ? encodedPrefix + '[]' : encodedPrefix;
if (allowEmptyArrays && isArray(obj) && obj.length === 0) {
return adjustedPrefix + '[]';
}
for (var j = 0; j < objKeys.length; ++j) {
var key = objKeys[j];
var value = typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key];
if (skipNulls && value === null) {
continue;
}
var encodedKey = allowDots && encodeDotInKeys ? key.replace(/\./g, '%2E') : key;
var keyPrefix = isArray(obj)
? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(adjustedPrefix, encodedKey) : adjustedPrefix
: adjustedPrefix + (allowDots ? '.' + encodedKey : '[' + encodedKey + ']');
sideChannel.set(object, step);
var valueSideChannel = getSideChannel();
valueSideChannel.set(sentinel, sideChannel);
pushToArray(values, stringify(
value,
keyPrefix,
generateArrayPrefix,
commaRoundTrip,
allowEmptyArrays,
strictNullHandling,
skipNulls,
encodeDotInKeys,
generateArrayPrefix === 'comma' && encodeValuesOnly && isArray(obj) ? null : encoder,
filter,
sort,
allowDots,
serializeDate,
format,
formatter,
encodeValuesOnly,
charset,
valueSideChannel
));
}
return values;
};
var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
if (!opts) {
return defaults;
}
if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') {
throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided');
}
if (typeof opts.encodeDotInKeys !== 'undefined' && typeof opts.encodeDotInKeys !== 'boolean') {
throw new TypeError('`encodeDotInKeys` option can only be `true` or `false`, when provided');
}
if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
throw new TypeError('Encoder has to be a function.');
}
var charset = opts.charset || defaults.charset;
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
}
var format = formats['default'];
if (typeof opts.format !== 'undefined') {
if (!has.call(formats.formatters, opts.format)) {
throw new TypeError('Unknown format option provided.');
}
format = opts.format;
}
var formatter = formats.formatters[format];
var filter = defaults.filter;
if (typeof opts.filter === 'function' || isArray(opts.filter)) {
filter = opts.filter;
}
var arrayFormat;
if (opts.arrayFormat in arrayPrefixGenerators) {
arrayFormat = opts.arrayFormat;
} else if ('indices' in opts) {
arrayFormat = opts.indices ? 'indices' : 'repeat';
} else {
arrayFormat = defaults.arrayFormat;
}
if ('commaRoundTrip' in opts && typeof opts.commaRoundTrip !== 'boolean') {
throw new TypeError('`commaRoundTrip` must be a boolean, or absent');
}
var allowDots = typeof opts.allowDots === 'undefined' ? opts.encodeDotInKeys === true ? true : defaults.allowDots : !!opts.allowDots;
return {
addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,
allowDots: allowDots,
allowEmptyArrays: typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays,
arrayFormat: arrayFormat,
charset: charset,
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
commaRoundTrip: opts.commaRoundTrip,
delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,
encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,
encodeDotInKeys: typeof opts.encodeDotInKeys === 'boolean' ? opts.encodeDotInKeys : defaults.encodeDotInKeys,
encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
filter: filter,
format: format,
formatter: formatter,
serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
sort: typeof opts.sort === 'function' ? opts.sort : null,
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
};
};
module.exports = function (object, opts) {
var obj = object;
var options = normalizeStringifyOptions(opts);
var objKeys;
var filter;
if (typeof options.filter === 'function') {
filter = options.filter;
obj = filter('', obj);
} else if (isArray(options.filter)) {
filter = options.filter;
objKeys = filter;
}
var keys = [];
if (typeof obj !== 'object' || obj === null) {
return '';
}
var generateArrayPrefix = arrayPrefixGenerators[options.arrayFormat];
var commaRoundTrip = generateArrayPrefix === 'comma' && options.commaRoundTrip;
if (!objKeys) {
objKeys = Object.keys(obj);
}
if (options.sort) {
objKeys.sort(options.sort);
}
var sideChannel = getSideChannel();
for (var i = 0; i < objKeys.length; ++i) {
var key = objKeys[i];
if (options.skipNulls && obj[key] === null) {
continue;
}
pushToArray(keys, stringify(
obj[key],
key,
generateArrayPrefix,
commaRoundTrip,
options.allowEmptyArrays,
options.strictNullHandling,
options.skipNulls,
options.encodeDotInKeys,
options.encode ? options.encoder : null,
options.filter,
options.sort,
options.allowDots,
options.serializeDate,
options.format,
options.formatter,
options.encodeValuesOnly,
options.charset,
sideChannel
));
}
var joined = keys.join(options.delimiter);
var prefix = options.addQueryPrefix === true ? '?' : '';
if (options.charsetSentinel) {
if (options.charset === 'iso-8859-1') {
// encodeURIComponent('&#10003;'), the "numeric entity" representation of a checkmark
prefix += 'utf8=%26%2310003%3B&';
} else {
// encodeURIComponent('✓')
prefix += 'utf8=%E2%9C%93&';
}
}
return joined.length > 0 ? prefix + joined : '';
};

View File

@@ -0,0 +1,252 @@
'use strict';
var formats = require('./formats');
var has = Object.prototype.hasOwnProperty;
var isArray = Array.isArray;
var hexTable = (function () {
var array = [];
for (var i = 0; i < 256; ++i) {
array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase());
}
return array;
}());
var compactQueue = function compactQueue(queue) {
while (queue.length > 1) {
var item = queue.pop();
var obj = item.obj[item.prop];
if (isArray(obj)) {
var compacted = [];
for (var j = 0; j < obj.length; ++j) {
if (typeof obj[j] !== 'undefined') {
compacted.push(obj[j]);
}
}
item.obj[item.prop] = compacted;
}
}
};
var arrayToObject = function arrayToObject(source, options) {
var obj = options && options.plainObjects ? Object.create(null) : {};
for (var i = 0; i < source.length; ++i) {
if (typeof source[i] !== 'undefined') {
obj[i] = source[i];
}
}
return obj;
};
var merge = function merge(target, source, options) {
/* eslint no-param-reassign: 0 */
if (!source) {
return target;
}
if (typeof source !== 'object') {
if (isArray(target)) {
target.push(source);
} else if (target && typeof target === 'object') {
if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
target[source] = true;
}
} else {
return [target, source];
}
return target;
}
if (!target || typeof target !== 'object') {
return [target].concat(source);
}
var mergeTarget = target;
if (isArray(target) && !isArray(source)) {
mergeTarget = arrayToObject(target, options);
}
if (isArray(target) && isArray(source)) {
source.forEach(function (item, i) {
if (has.call(target, i)) {
var targetItem = target[i];
if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
target[i] = merge(targetItem, item, options);
} else {
target.push(item);
}
} else {
target[i] = item;
}
});
return target;
}
return Object.keys(source).reduce(function (acc, key) {
var value = source[key];
if (has.call(acc, key)) {
acc[key] = merge(acc[key], value, options);
} else {
acc[key] = value;
}
return acc;
}, mergeTarget);
};
var assign = function assignSingleSource(target, source) {
return Object.keys(source).reduce(function (acc, key) {
acc[key] = source[key];
return acc;
}, target);
};
var decode = function (str, decoder, charset) {
var strWithoutPlus = str.replace(/\+/g, ' ');
if (charset === 'iso-8859-1') {
// unescape never throws, no try...catch needed:
return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape);
}
// utf-8
try {
return decodeURIComponent(strWithoutPlus);
} catch (e) {
return strWithoutPlus;
}
};
var encode = function encode(str, defaultEncoder, charset, kind, format) {
// 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;
}
var string = str;
if (typeof str === 'symbol') {
string = Symbol.prototype.toString.call(str);
} else if (typeof str !== 'string') {
string = String(str);
}
if (charset === 'iso-8859-1') {
return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) {
return '%26%23' + parseInt($0.slice(2), 16) + '%3B';
});
}
var out = '';
for (var i = 0; i < string.length; ++i) {
var c = string.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
|| (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( )
) {
out += string.charAt(i);
continue;
}
if (c < 0x80) {
out = out + hexTable[c];
continue;
}
if (c < 0x800) {
out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]);
continue;
}
if (c < 0xD800 || c >= 0xE000) {
out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]);
continue;
}
i += 1;
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
/* eslint operator-linebreak: [2, "before"] */
out += hexTable[0xF0 | (c >> 18)]
+ hexTable[0x80 | ((c >> 12) & 0x3F)]
+ hexTable[0x80 | ((c >> 6) & 0x3F)]
+ hexTable[0x80 | (c & 0x3F)];
}
return out;
};
var compact = function compact(value) {
var queue = [{ obj: { o: value }, prop: 'o' }];
var refs = [];
for (var i = 0; i < queue.length; ++i) {
var item = queue[i];
var obj = item.obj[item.prop];
var keys = Object.keys(obj);
for (var j = 0; j < keys.length; ++j) {
var key = keys[j];
var val = obj[key];
if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
queue.push({ obj: obj, prop: key });
refs.push(val);
}
}
}
compactQueue(queue);
return value;
};
var isRegExp = function isRegExp(obj) {
return Object.prototype.toString.call(obj) === '[object RegExp]';
};
var isBuffer = function isBuffer(obj) {
if (!obj || typeof obj !== 'object') {
return false;
}
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));
};
var combine = function combine(a, b) {
return [].concat(a, b);
};
var maybeMap = function maybeMap(val, fn) {
if (isArray(val)) {
var mapped = [];
for (var i = 0; i < val.length; i += 1) {
mapped.push(fn(val[i]));
}
return mapped;
}
return fn(val);
};
module.exports = {
arrayToObject: arrayToObject,
assign: assign,
combine: combine,
compact: compact,
decode: decode,
encode: encode,
isBuffer: isBuffer,
isRegExp: isRegExp,
maybeMap: maybeMap,
merge: merge
};

View File

@@ -0,0 +1,91 @@
{
"name": "qs",
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
"homepage": "https://github.com/ljharb/qs",
"version": "6.12.0",
"repository": {
"type": "git",
"url": "https://github.com/ljharb/qs.git"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
},
"main": "lib/index.js",
"sideEffects": false,
"contributors": [
{
"name": "Jordan Harband",
"email": "ljharb@gmail.com",
"url": "http://ljharb.codes"
}
],
"keywords": [
"querystring",
"qs",
"query",
"url",
"parse",
"stringify"
],
"engines": {
"node": ">=0.6"
},
"dependencies": {
"side-channel": "^1.0.6"
},
"devDependencies": {
"@browserify/envify": "^6.0.0",
"@browserify/uglifyify": "^6.0.0",
"@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.4",
"browserify": "^16.5.2",
"bundle-collapser": "^1.4.0",
"common-shakeify": "~1.0.0",
"eclint": "^2.8.1",
"es-value-fixtures": "^1.4.2",
"eslint": "=8.8.0",
"evalmd": "^0.0.19",
"for-each": "^0.3.3",
"glob": "=10.3.7",
"has-override-mistake": "^1.0.1",
"has-property-descriptors": "^1.0.2",
"has-symbols": "^1.0.3",
"iconv-lite": "^0.5.1",
"in-publish": "^2.0.1",
"jackspeak": "=2.1.1",
"mkdirp": "^0.5.5",
"mock-property": "^1.0.3",
"module-deps": "^6.2.3",
"npmignore": "^0.3.1",
"nyc": "^10.3.2",
"object-inspect": "^1.13.1",
"qs-iconv": "^1.0.4",
"safe-publish-latest": "^2.0.0",
"safer-buffer": "^2.1.2",
"tape": "^5.7.5",
"unassertify": "^3.0.1"
},
"scripts": {
"prepack": "npmignore --auto --commentLines=autogenerated && npm run dist",
"prepublishOnly": "safe-publish-latest",
"prepublish": "not-in-publish || npm run prepublishOnly",
"pretest": "npm run --silent readme && npm run --silent lint",
"test": "npm run tests-only",
"tests-only": "nyc tape 'test/**/*.js'",
"posttest": "aud --production",
"readme": "evalmd README.md",
"postlint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git' | grep -v dist/)",
"lint": "eslint --ext=js,mjs .",
"dist": "mkdirp dist && browserify --standalone Qs -g unassertify -g @browserify/envify -g [@browserify/uglifyify --mangle.keep_fnames --compress.keep_fnames --format.indent_level=1 --compress.arrows=false --compress.passes=4 --compress.typeofs=false] -p common-shakeify -p bundle-collapser/plugin lib/index.js > dist/qs.js"
},
"license": "BSD-3-Clause",
"publishConfig": {
"ignore": [
"!dist/*",
"bower.json",
"component.json",
".github/workflows",
"logos"
]
}
}

View File

@@ -0,0 +1,267 @@
'use strict';
module.exports = {
emptyTestCases: [
{
input: '&',
withEmptyKeys: {},
stringifyOutput: {
brackets: '',
indices: '',
repeat: ''
},
noEmptyKeys: {}
},
{
input: '&&',
withEmptyKeys: {},
stringifyOutput: {
brackets: '',
indices: '',
repeat: ''
},
noEmptyKeys: {}
},
{
input: '&=',
withEmptyKeys: { '': '' },
stringifyOutput: {
brackets: '=',
indices: '=',
repeat: '='
},
noEmptyKeys: {}
},
{
input: '&=&',
withEmptyKeys: { '': '' },
stringifyOutput: {
brackets: '=',
indices: '=',
repeat: '='
},
noEmptyKeys: {}
},
{
input: '&=&=',
withEmptyKeys: { '': ['', ''] },
stringifyOutput: {
brackets: '[]=&[]=',
indices: '[0]=&[1]=',
repeat: '=&='
},
noEmptyKeys: {}
},
{
input: '&=&=&',
withEmptyKeys: { '': ['', ''] },
stringifyOutput: {
brackets: '[]=&[]=',
indices: '[0]=&[1]=',
repeat: '=&='
},
noEmptyKeys: {}
},
{
input: '=',
withEmptyKeys: { '': '' },
noEmptyKeys: {},
stringifyOutput: {
brackets: '=',
indices: '=',
repeat: '='
}
},
{
input: '=&',
withEmptyKeys: { '': '' },
stringifyOutput: {
brackets: '=',
indices: '=',
repeat: '='
},
noEmptyKeys: {}
},
{
input: '=&&&',
withEmptyKeys: { '': '' },
stringifyOutput: {
brackets: '=',
indices: '=',
repeat: '='
},
noEmptyKeys: {}
},
{
input: '=&=&=&',
withEmptyKeys: { '': ['', '', ''] },
stringifyOutput: {
brackets: '[]=&[]=&[]=',
indices: '[0]=&[1]=&[2]=',
repeat: '=&=&='
},
noEmptyKeys: {}
},
{
input: '=&a[]=b&a[1]=c',
withEmptyKeys: { '': '', a: ['b', 'c'] },
stringifyOutput: {
brackets: '=&a[]=b&a[]=c',
indices: '=&a[0]=b&a[1]=c',
repeat: '=&a=b&a=c'
},
noEmptyKeys: { a: ['b', 'c'] }
},
{
input: '=a',
withEmptyKeys: { '': 'a' },
noEmptyKeys: {},
stringifyOutput: {
brackets: '=a',
indices: '=a',
repeat: '=a'
}
},
{
input: 'a==a',
withEmptyKeys: { a: '=a' },
noEmptyKeys: { a: '=a' },
stringifyOutput: {
brackets: 'a==a',
indices: 'a==a',
repeat: 'a==a'
}
},
{
input: '=&a[]=b',
withEmptyKeys: { '': '', a: ['b'] },
stringifyOutput: {
brackets: '=&a[]=b',
indices: '=&a[0]=b',
repeat: '=&a=b'
},
noEmptyKeys: { a: ['b'] }
},
{
input: '=&a[]=b&a[]=c&a[2]=d',
withEmptyKeys: { '': '', a: ['b', 'c', 'd'] },
stringifyOutput: {
brackets: '=&a[]=b&a[]=c&a[]=d',
indices: '=&a[0]=b&a[1]=c&a[2]=d',
repeat: '=&a=b&a=c&a=d'
},
noEmptyKeys: { a: ['b', 'c', 'd'] }
},
{
input: '=a&=b',
withEmptyKeys: { '': ['a', 'b'] },
stringifyOutput: {
brackets: '[]=a&[]=b',
indices: '[0]=a&[1]=b',
repeat: '=a&=b'
},
noEmptyKeys: {}
},
{
input: '=a&foo=b',
withEmptyKeys: { '': 'a', foo: 'b' },
noEmptyKeys: { foo: 'b' },
stringifyOutput: {
brackets: '=a&foo=b',
indices: '=a&foo=b',
repeat: '=a&foo=b'
}
},
{
input: 'a[]=b&a=c&=',
withEmptyKeys: { '': '', a: ['b', 'c'] },
stringifyOutput: {
brackets: '=&a[]=b&a[]=c',
indices: '=&a[0]=b&a[1]=c',
repeat: '=&a=b&a=c'
},
noEmptyKeys: { a: ['b', 'c'] }
},
{
input: 'a[]=b&a=c&=',
withEmptyKeys: { '': '', a: ['b', 'c'] },
stringifyOutput: {
brackets: '=&a[]=b&a[]=c',
indices: '=&a[0]=b&a[1]=c',
repeat: '=&a=b&a=c'
},
noEmptyKeys: { a: ['b', 'c'] }
},
{
input: 'a[0]=b&a=c&=',
withEmptyKeys: { '': '', a: ['b', 'c'] },
stringifyOutput: {
brackets: '=&a[]=b&a[]=c',
indices: '=&a[0]=b&a[1]=c',
repeat: '=&a=b&a=c'
},
noEmptyKeys: { a: ['b', 'c'] }
},
{
input: 'a=b&a[]=c&=',
withEmptyKeys: { '': '', a: ['b', 'c'] },
stringifyOutput: {
brackets: '=&a[]=b&a[]=c',
indices: '=&a[0]=b&a[1]=c',
repeat: '=&a=b&a=c'
},
noEmptyKeys: { a: ['b', 'c'] }
},
{
input: 'a=b&a[0]=c&=',
withEmptyKeys: { '': '', a: ['b', 'c'] },
stringifyOutput: {
brackets: '=&a[]=b&a[]=c',
indices: '=&a[0]=b&a[1]=c',
repeat: '=&a=b&a=c'
},
noEmptyKeys: { a: ['b', 'c'] }
},
{
input: '[]=a&[]=b& []=1',
withEmptyKeys: { '': ['a', 'b'], ' ': ['1'] },
stringifyOutput: {
brackets: '[]=a&[]=b& []=1',
indices: '[0]=a&[1]=b& [0]=1',
repeat: '=a&=b& =1'
},
noEmptyKeys: { 0: 'a', 1: 'b', ' ': ['1'] }
},
{
input: '[0]=a&[1]=b&a[0]=1&a[1]=2',
withEmptyKeys: { '': ['a', 'b'], a: ['1', '2'] },
noEmptyKeys: { 0: 'a', 1: 'b', a: ['1', '2'] },
stringifyOutput: {
brackets: '[]=a&[]=b&a[]=1&a[]=2',
indices: '[0]=a&[1]=b&a[0]=1&a[1]=2',
repeat: '=a&=b&a=1&a=2'
}
},
{
input: '[deep]=a&[deep]=2',
withEmptyKeys: { '': { deep: ['a', '2'] }
},
stringifyOutput: {
brackets: '[deep][]=a&[deep][]=2',
indices: '[deep][0]=a&[deep][1]=2',
repeat: '[deep]=a&[deep]=2'
},
noEmptyKeys: { deep: ['a', '2'] }
},
{
input: '%5B0%5D=a&%5B1%5D=b',
withEmptyKeys: { '': ['a', 'b'] },
stringifyOutput: {
brackets: '[]=a&[]=b',
indices: '[0]=a&[1]=b',
repeat: '=a&=b'
},
noEmptyKeys: { 0: 'a', 1: 'b' }
}
]
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
'use strict';
var test = require('tape');
var inspect = require('object-inspect');
var SaferBuffer = require('safer-buffer').Buffer;
var forEach = require('for-each');
var utils = require('../lib/utils');
test('merge()', function (t) {
t.deepEqual(utils.merge(null, true), [null, true], 'merges true into null');
t.deepEqual(utils.merge(null, [42]), [null, 42], 'merges null into an array');
t.deepEqual(utils.merge({ a: 'b' }, { a: 'c' }), { a: ['b', 'c'] }, 'merges two objects with the same key');
var oneMerged = utils.merge({ foo: 'bar' }, { foo: { first: '123' } });
t.deepEqual(oneMerged, { foo: ['bar', { first: '123' }] }, 'merges a standalone and an object into an array');
var twoMerged = utils.merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } });
t.deepEqual(twoMerged, { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, 'merges a standalone and two objects into an array');
var sandwiched = utils.merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' });
t.deepEqual(sandwiched, { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, 'merges an object sandwiched by two standalones into an array');
var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] });
t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] });
var noOptionsNonObjectSource = utils.merge({ foo: 'baz' }, 'bar');
t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });
t.test(
'avoids invoking array setters unnecessarily',
{ skip: typeof Object.defineProperty !== 'function' },
function (st) {
var setCount = 0;
var getCount = 0;
var observed = [];
Object.defineProperty(observed, 0, {
get: function () {
getCount += 1;
return { bar: 'baz' };
},
set: function () { setCount += 1; }
});
utils.merge(observed, [null]);
st.equal(setCount, 0);
st.equal(getCount, 1);
observed[0] = observed[0]; // eslint-disable-line no-self-assign
st.equal(setCount, 1);
st.equal(getCount, 2);
st.end();
}
);
t.end();
});
test('assign()', function (t) {
var target = { a: 1, b: 2 };
var source = { b: 3, c: 4 };
var result = utils.assign(target, source);
t.equal(result, target, 'returns the target');
t.deepEqual(target, { a: 1, b: 3, c: 4 }, 'target and source are merged');
t.deepEqual(source, { b: 3, c: 4 }, 'source is untouched');
t.end();
});
test('combine()', function (t) {
t.test('both arrays', function (st) {
var a = [1];
var b = [2];
var combined = utils.combine(a, b);
st.deepEqual(a, [1], 'a is not mutated');
st.deepEqual(b, [2], 'b is not mutated');
st.notEqual(a, combined, 'a !== combined');
st.notEqual(b, combined, 'b !== combined');
st.deepEqual(combined, [1, 2], 'combined is a + b');
st.end();
});
t.test('one array, one non-array', function (st) {
var aN = 1;
var a = [aN];
var bN = 2;
var b = [bN];
var combinedAnB = utils.combine(aN, b);
st.deepEqual(b, [bN], 'b is not mutated');
st.notEqual(aN, combinedAnB, 'aN + b !== aN');
st.notEqual(a, combinedAnB, 'aN + b !== a');
st.notEqual(bN, combinedAnB, 'aN + b !== bN');
st.notEqual(b, combinedAnB, 'aN + b !== b');
st.deepEqual([1, 2], combinedAnB, 'first argument is array-wrapped when not an array');
var combinedABn = utils.combine(a, bN);
st.deepEqual(a, [aN], 'a is not mutated');
st.notEqual(aN, combinedABn, 'a + bN !== aN');
st.notEqual(a, combinedABn, 'a + bN !== a');
st.notEqual(bN, combinedABn, 'a + bN !== bN');
st.notEqual(b, combinedABn, 'a + bN !== b');
st.deepEqual([1, 2], combinedABn, 'second argument is array-wrapped when not an array');
st.end();
});
t.test('neither is an array', function (st) {
var combined = utils.combine(1, 2);
st.notEqual(1, combined, '1 + 2 !== 1');
st.notEqual(2, combined, '1 + 2 !== 2');
st.deepEqual([1, 2], combined, 'both arguments are array-wrapped when not an array');
st.end();
});
t.end();
});
test('isBuffer()', function (t) {
forEach([null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], function () {}, /a/g], function (x) {
t.equal(utils.isBuffer(x), false, inspect(x) + ' is not a buffer');
});
var fakeBuffer = { constructor: Buffer };
t.equal(utils.isBuffer(fakeBuffer), false, 'fake buffer is not a buffer');
var saferBuffer = SaferBuffer.from('abc');
t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');
var buffer = Buffer.from && Buffer.alloc ? Buffer.from('abc') : new Buffer('abc');
t.equal(utils.isBuffer(buffer), true, 'real Buffer instance is a buffer');
t.end();
});

83
ProjectSourceCode/node_modules/chai-http/package.json generated vendored Normal file
View File

@@ -0,0 +1,83 @@
{
"name": "chai-http",
"version": "4.4.0",
"description": "Extend Chai Assertion library with tests for http apis",
"author": "Jake Luer <jake@alogicalparadox.com>",
"license": "MIT",
"keywords": [
"chai",
"chai-plugin",
"browser",
"http",
"request",
"vendor",
"supertest",
"superagent"
],
"contributors": [
"Jake Luer <jake@alogicalparadox.com>",
"Veselin Todorov <hi@vesln.com>",
"Keith Cirkel <oss@keithcirkel.co.uk> (http://keithcirkel.co.uk)"
],
"files": [
"dist/chai-http.js",
"lib/*.js",
"index.js",
"types/index.d.ts"
],
"main": "./index",
"types": "./types/index.d.ts",
"repository": {
"type": "git",
"url": "git@github.com:chaijs/chai-http.git"
},
"scripts": {
"build": "npm run build:js && npm run build:ts",
"prebuild:js": "rm -rf dist",
"build:js": "simplifyify lib/http.js --outfile dist/chai-http.js --bundle --minify --debug --standalone chaiHttp",
"build:ts": "cd types && tsc",
"start": "npm-run-all --parallel watch server",
"watch": "npm run build:js -- --watch",
"server": "http-server -o -c-1",
"test": "istanbul cover --report lcovonly _mocha",
"coverage": "if [ -z \"$COVERALLS_REPO_TOKEN\" ]; then cat coverage/lcov.info | coveralls; fi",
"release": "npm run build && semantic-release"
},
"browser": {
"http": false,
"https": false,
"net": "./lib/net.js",
"querystring": "qs"
},
"dependencies": {
"@types/chai": "4",
"@types/superagent": "4.1.13",
"charset": "^1.0.1",
"cookiejar": "^2.1.4",
"is-ip": "^2.0.0",
"methods": "^1.1.2",
"qs": "^6.11.2",
"superagent": "^8.0.9"
},
"devDependencies": {
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/commit-analyzer": "^8.0.1",
"@semantic-release/git": "^9.0.0",
"@semantic-release/npm": "^7.0.0",
"@semantic-release/release-notes-generator": "^7.1.4",
"chai": "4",
"coveralls": "^3.0.0",
"dox": "^0.9.0",
"es6-shim": "^0.35.1",
"http-server": "^0.10.0",
"istanbul": "^0.4.3",
"mocha": "^4.0.1",
"npm-run-all": "^4.1.1",
"semantic-release": "^17.3.9",
"simplifyify": "^4.0.0",
"typescript": "^3.0.1"
},
"engines": {
"node": ">=10"
}
}

View File

@@ -0,0 +1,63 @@
// Definitions by: Wim Looman <https://github.com/Nemo157>
// Liam Jones <https://github.com/G1itcher>
// Federico Caselli <https://github.com/CaselIT>
// Bas Luksenburg <https://github.com/bas-l>
// Austin Cawley-Edwards <https://github.com/austince>
// TypeScript Version: 3.0
/// <reference types="chai" />
import * as request from 'superagent';
// Merge namespace with global chai
declare global {
namespace Chai {
interface ChaiStatic {
request: ChaiHttpRequest;
}
interface ChaiHttpRequest {
(server: any): ChaiHttp.Agent;
agent(server: any): ChaiHttp.Agent;
addPromises(promiseConstructor: PromiseConstructorLike): void;
}
interface Assertion {
redirectTo(location: string|RegExp): Assertion;
param(key: string, value?: string): Assertion;
cookie(key: string, value?: string): Assertion;
status(code: number): Assertion;
statusCode(code: number): Assertion;
header(key: string, value?: string | RegExp): Assertion;
charset(charset: string): Assertion;
headers: Assertion;
json: Assertion;
text: Assertion;
html: Assertion;
redirect: Assertion;
}
interface TypeComparison {
ip: Assertion;
}
}
namespace ChaiHttp {
interface Response extends request.Response {}
interface Agent extends request.SuperAgentStatic {
keepOpen(): Agent;
close(callback?: (err: any) => void): Agent;
}
}
}
declare function chaiHttp(chai: any, utils: any): void;
export = chaiHttp;