diff --git a/.browserslistrc b/.browserslistrc
new file mode 100644
index 000000000..8b68bc78f
--- /dev/null
+++ b/.browserslistrc
@@ -0,0 +1 @@
+defaults, not ie 11
diff --git a/.commitlintrc.js b/.commitlintrc.js
new file mode 100644
index 000000000..c34aa79d0
--- /dev/null
+++ b/.commitlintrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: ['@commitlint/config-conventional']
+};
diff --git a/.dist.babelrc b/.dist.babelrc
new file mode 100644
index 000000000..4d2062da6
--- /dev/null
+++ b/.dist.babelrc
@@ -0,0 +1,12 @@
+{
+ "presets": [
+ ["@babel/env", {
+ "targets": {
+ "browsers": [ "defaults, not ie 11" ]
+ }
+ }]
+ ],
+ "sourceType": "script",
+ "sourceMaps": "inline",
+ "comments": false
+}
diff --git a/.dist.eslintrc b/.dist.eslintrc
new file mode 100644
index 000000000..6fa48a702
--- /dev/null
+++ b/.dist.eslintrc
@@ -0,0 +1,42 @@
+{
+ "extends": ["eslint:recommended", "plugin:compat/recommended"],
+ "env": {
+ "node": false,
+ "browser": true,
+ "amd": true,
+ "es6": true
+ },
+ "plugins": ["compat"],
+ "rules": {
+ "node/no-unsupported-features/es-builtins": "off",
+ "compat/compat": "error",
+ "no-console": "off",
+ "no-empty": "off",
+ "no-extra-semi": "off",
+ "no-func-assign": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-useless-escape": "off",
+ "no-obj-calls": "off",
+ "no-cond-assign": "off",
+ "no-redeclare": "off",
+ "node/no-exports-assign": "off",
+ "no-unsafe-finally": "off",
+ "complexity": ["error", 10000],
+ "max-statements": "off",
+ "no-constant-condition": "off",
+ "no-control-regex": "off",
+ "no-fallthrough": "off",
+ "operator-linebreak": "off",
+ "node/no-missing-require": "warn"
+ },
+ "globals": {
+ "regeneratorRuntime": "writable"
+ },
+ "settings": {
+ "polyfills": [
+ "WeakRef",
+ "BigInt"
+ ]
+ }
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..c6c8b3621
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 000000000..b7b06bce1
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,37 @@
+{
+ "extends": [
+ "eslint:recommended",
+ "plugin:node/recommended"
+ ],
+ "env": {
+ "node": true,
+ "browser": true,
+ "es6": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 2021
+ },
+ "overrides": [
+ {
+ "files": "test/**/*.js",
+ "env": {
+ "mocha": true
+ },
+ "rules": {
+ "no-prototype-builtins": "off",
+ "node/no-deprecated-api": "warn",
+ "node/no-extraneous-require": "warn",
+ "no-unused-vars": "warn",
+ "node/no-missing-require": "warn"
+ }
+ }
+ ],
+ "rules": {
+ "node/no-unsupported-features/node-builtins": "off",
+ "node/no-unsupported-features/es-syntax": "off",
+ "node/no-exports-assign": "off",
+ "no-unused-vars": "warn"
+ },
+ "globals": {
+ }
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..176a458f9
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..ec9106b87
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,48 @@
+name: CI
+on: [push, pull_request]
+
+env:
+ SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
+ SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
+
+permissions:
+ contents: read # to fetch code (actions/checkout)
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - node-version: 18.x
+ - node-version: 20.x
+ - node-version: 22.x
+ - node-version: 24.x
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Node - ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: 'npm'
+ - name: Install dependencies
+ run: npm ci
+ - name: Build
+ run: npm run build
+ - name: Test On Node ${{ matrix.node-version }}
+ env:
+ BROWSER: ${{ matrix.test-on-brower }}
+ HTTP2_TEST_DISABLED: ${{ matrix.http2-test-disabled }}
+ OLD_NODE_TEST: ${{ matrix.test-on-old-node }}
+ run: |
+ if [ "$OLD_NODE_TEST" = "1" ]; then
+ make test
+ else
+ npm run lint
+ make test
+ fi
+ - name: Coverage On Node ${{ matrix.node-version }}
+ run: npm run coverage
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v4
diff --git a/.gitignore b/.gitignore
index 1293f2078..03cc677ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.vscode
build
lib-cov
coverage.html
@@ -8,3 +9,11 @@ test.js
components
test/node/fixtures/tmp.json
.idea
+superagent.js
+*.log
+coverage
+.nyc_output
+lib
+dist
+*.swp
+yarn.lock
diff --git a/.husky/commit-msg b/.husky/commit-msg
new file mode 100755
index 000000000..a78cc751d
--- /dev/null
+++ b/.husky/commit-msg
@@ -0,0 +1 @@
+npx commitlint --edit $1
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 000000000..2312dc587
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+npx lint-staged
diff --git a/.lib.babelrc b/.lib.babelrc
new file mode 100644
index 000000000..a151f4078
--- /dev/null
+++ b/.lib.babelrc
@@ -0,0 +1,11 @@
+{
+ "presets": [
+ ["@babel/env", {
+ "targets": {
+ "node": "14.18.0",
+ "browsers": [ "defaults, not ie 11" ]
+ }
+ }]
+ ],
+ "sourceMaps": "inline"
+}
diff --git a/.lib.eslintrc b/.lib.eslintrc
new file mode 100644
index 000000000..5915045fb
--- /dev/null
+++ b/.lib.eslintrc
@@ -0,0 +1,37 @@
+{
+ "extends": ["eslint:recommended", "plugin:node/recommended"],
+ "env": {
+ "browser": true
+ },
+ "rules": {
+ "node/no-unsupported-features/es-builtins": ["error", {
+ "version": ">=6.4.0",
+ "ignores": [
+ ]
+ }],
+ "node/no-deprecated-api": "off",
+ "no-console": "off",
+ "no-unused-vars": "off",
+ "no-empty": "off",
+ "no-func-assign": "off",
+ "no-global-assign": ["error", {"exceptions": ["exports"]}],
+ "no-fallthrough": "off",
+ "no-constant-condition": "off",
+ "node/no-exports-assign": "off",
+ "no-unsafe-finally": "off"
+ },
+ "overrides": [
+ {
+ "files": [ "lib/client.js" ],
+ "globals": {
+ }
+ },
+ {
+ "files": [ "lib/node/http2wrapper.js" ],
+ "rules": {
+ "node/no-unsupported-features/es-builtins": "off",
+ "node/no-unsupported-features/node-builtins": "off"
+ }
+ }
+ ]
+}
diff --git a/.lintstagedrc.js b/.lintstagedrc.js
new file mode 100644
index 000000000..59cf142d8
--- /dev/null
+++ b/.lintstagedrc.js
@@ -0,0 +1,4 @@
+module.exports = {
+ "*.md": filenames => filenames.map(filename => `remark ${filename} -qfo`),
+ '*.js': 'xo --fix'
+};
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index 90d998b0e..000000000
--- a/.npmignore
+++ /dev/null
@@ -1,6 +0,0 @@
-support
-test
-examples
-*.sock
-lib-cov
-coverage.html
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 000000000..cafe685a1
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+package-lock=true
diff --git a/.prettierrc.js b/.prettierrc.js
new file mode 100644
index 000000000..97a92d420
--- /dev/null
+++ b/.prettierrc.js
@@ -0,0 +1,5 @@
+module.exports = {
+ singleQuote: true,
+ bracketSpacing: true,
+ trailingComma: 'none'
+};
diff --git a/.remarkignore b/.remarkignore
new file mode 100644
index 000000000..9e60a0859
--- /dev/null
+++ b/.remarkignore
@@ -0,0 +1,3 @@
+CONTRIBUTING.md
+HISTORY.md
+docs
diff --git a/.remarkrc.js b/.remarkrc.js
new file mode 100644
index 000000000..487138c4e
--- /dev/null
+++ b/.remarkrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ plugins: ['preset-github']
+};
diff --git a/.test.babelrc b/.test.babelrc
new file mode 100644
index 000000000..337b344be
--- /dev/null
+++ b/.test.babelrc
@@ -0,0 +1,17 @@
+{
+ "presets": [
+ ["@babel/env", {
+ "targets": {
+ "node": "14.18.0",
+ "browsers": [ "defaults, not ie 11" ]
+ }
+ }]
+ ],
+ "plugins": [
+ ["@babel/transform-runtime"]
+ ],
+ "parserOpts": {
+ "allowReturnOutsideFunction": true
+ },
+ "sourceMaps": "inline"
+}
diff --git a/.travis.yml b/.travis.yml
index 19af9c7f7..7ad5293e9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,15 +1,12 @@
sudo: false
-
language: node_js
+node_js:
+ - '18'
+ - '16'
+ - '14'
+after_success: npm run coverage
env:
global:
- SAUCE_USERNAME='shtylman-superagent'
- SAUCE_ACCESS_KEY='39a45464-cb1d-4b8d-aa1f-83c7c04fa673'
-
-matrix:
- include:
- - node_js: "0.10"
- - node_js: "0.12"
- - node_js: "0.10"
- env: BROWSER=1
diff --git a/.xo-config.js b/.xo-config.js
new file mode 100644
index 000000000..86401d682
--- /dev/null
+++ b/.xo-config.js
@@ -0,0 +1,95 @@
+module.exports = {
+ prettier: true,
+ space: true,
+ nodeVersion: false,
+ extends: [
+ 'xo-lass',
+ ],
+ envs: [
+ 'node',
+ 'browser',
+ ],
+ overrides: [
+ {
+ files: 'test/**/*.js',
+ envs: [
+ 'mocha',
+ ],
+ rules: {
+ 'block-scoped-var': 'warn',
+ complexity: 'warn',
+ 'default-case': 'warn',
+ eqeqeq: 'warn',
+ 'func-name-matching': 'warn',
+ 'func-names': 'warn',
+ 'guard-for-in': 'warn',
+ 'handle-callback-err': 'warn',
+ 'import/no-extraneous-dependencies': 'warn',
+ 'import/no-unassigned-import': 'warn',
+ 'import/order': 'warn',
+ 'max-nested-callbacks': 'warn',
+ 'new-cap': 'warn',
+ 'no-eq-null': 'warn',
+ 'no-extend-native': 'warn',
+ 'no-implicit-coercion': 'warn',
+ 'no-multi-assign': 'warn',
+ 'no-negated-condition': 'off',
+ 'no-prototype-builtins': 'warn',
+ 'no-redeclare': 'warn',
+ 'no-undef': 'warn',
+ 'no-unused-expressions': 'warn',
+ 'no-unused-vars': 'warn',
+ 'no-use-extend-native/no-use-extend-native': 'warn',
+ 'no-useless-escape': 'warn',
+ 'no-var': 'warn',
+ 'no-void': 'warn',
+ 'n/no-deprecated-api': 'warn',
+ 'prefer-rest-params': 'warn',
+ 'prefer-spread': 'warn',
+ 'unicorn/filename-case': 'warn',
+ 'valid-jsdoc': 'warn',
+ 'n/no-path-concat': 'warn',
+ 'unicorn/no-empty-file': 'warn',
+ 'unicorn/expiring-todo-comments': 'off',
+ 'n/prefer-global/buffer': 'off',
+ 'n/prefer-global/process': 'off',
+ },
+ },
+ ],
+ rules: {
+ 'unicorn/prevent-abbreviations': [
+ 'warn',
+ {
+ replacements: {
+ res: false,
+ args: false,
+ fn: false,
+ err: false,
+ e: false,
+ i: false,
+ },
+ },
+ ],
+ 'no-bitwise': 'warn',
+ 'n/prefer-global/buffer': 'off',
+ 'n/prefer-global/process': 'off',
+ 'unicorn/no-new-array': 'warn',
+ 'unicorn/no-this-assignment': 'warn',
+ 'unicorn/prefer-spread': 'warn',
+ 'unicorn/catch-error-name': 'warn',
+ 'unicorn/prefer-code-point': 'warn',
+ 'n/no-unsupported-features': [
+ 'error',
+ {
+ version: 8,
+ ignores: [
+ 'syntax',
+ ],
+ },
+ ],
+ 'unicorn/prefer-optional-catch-binding': 'off',
+ 'no-unused-vars': 'off',
+ 'unicorn/expiring-todo-comments': 'off'
+ },
+ globals: [],
+};
diff --git a/.zuul.yml b/.zuul.yml
index 031c56a81..8b229ec5f 100644
--- a/.zuul.yml
+++ b/.zuul.yml
@@ -1,5 +1,6 @@
ui: mocha-bdd
server: ./test/support/server.js
+tunnel_host: http://focusaurus.com
browsers:
- name: chrome
version: latest
@@ -7,9 +8,9 @@ browsers:
version: latest
- name: safari
version: latest
- - name: iphone
- version: latest
- - name: android
- version: latest
- name: ie
version: 9..latest
+browserify:
+ - transform:
+ name: babelify
+ configFile: './.dist.babelrc'
diff --git a/Contributing.md b/CONTRIBUTING.md
similarity index 100%
rename from Contributing.md
rename to CONTRIBUTING.md
diff --git a/HISTORY.md b/HISTORY.md
new file mode 100644
index 000000000..cc21c0213
--- /dev/null
+++ b/HISTORY.md
@@ -0,0 +1,692 @@
+# This HISTORY log is deprecated
+
+Please see [GitHub releases page](https://github.com/ladjs/superagent/releases) for the current changelog.
+
+# 4.1.0 (2018-12-26)
+
+ * `.connect()` IP/DNS override option (Kornel)
+ * `.trustLocalhost()` option for allowing broken HTTPS on `localhost`
+ * `.abort()` used with promises rejects the promise.
+
+# 4.0.0 (2018-11-17)
+
+## Breaking changes
+
+* Node.js v4 has reached it's end of life, so we no longer support it. It's v6+ or later. We recommend Node.js 10.
+* We now use ES6 in the browser code, too.
+ * If you're using Browserify or Webpack to package code for Internet Explorer, you will also have to use Babel.
+ * The pre-built node_modules/superagent.js is still ES5-compatible.
+* `.end(…)` returns `undefined` instead of the request. If you need the request object after calling `.end()` (and you probably don't), save it in a variable and call `request.end(…)`. Consider not using `.end()` at all, and migrating to promises by calling `.then()` instead.
+* In Node, responses with unknown MIME type are buffered by default. To get old behavior, if you use custom _unbuffered_ parsers, add `.buffer(false)` to requests or set `superagent.buffer[yourMimeType] = false`.
+* Invalid uses of `.pipe()` throw.
+
+
+## Minor changes
+
+* Throw if `req.abort().end()` is called
+* Throw if using unsupported mix of send and field
+* Reject `.end()` promise on all error events (Kornel Lesiński)
+* Set `https.servername` from the `Host` header (Kornel Lesiński)
+* Leave backticks unencoded in query strings where possible (Ethan Resnick)
+* Update node-mime to 2.x (Alexey Kucherenko)
+* Allow default buffer settings based on response-type (shrey)
+* `response.buffered` is more accurate.
+
+# 3.8.3 (2018-04-29)
+
+* Add flags for 201 & 422 responses (Nikhil Fadnis)
+* Emit progress event while uploading Node `Buffer` via send method (Sergey Akhalkov)
+* Fixed setting correct cookies for redirects (Damien Clark)
+* Replace .catch with ['catch'] for IE9 Support (Miguel Stevens)
+
+# 3.8.2 (2017-12-09)
+
+* Fixed handling of exceptions thrown from callbacks
+* Stricter matching of `+json` MIME types.
+
+# 3.8.1 (2017-11-08)
+
+* Clear authorization header on cross-domain redirect
+
+# 3.8.0
+
+* Added support for "globally" defined headers and event handlers via `superagent.agent()`. It now remembers default settings for all its requests.
+* Added optional callback to `.retry()` (Alexander Murphy)
+* Unified auth args handling in node/browser (Edmundo Alvarez)
+* Fixed error handling in zlib pipes (Kornel)
+* Documented that 3xx status codes are errors (Mickey Reiss)
+
+# 3.7.0 (2017-10-17)
+
+* Limit maximum response size. Prevents zip bombs (Kornel)
+* Catch and pass along errors in `.ok()` callback (Jeremy Ruppel)
+* Fixed parsing of XHR headers without a newline (nsf)
+
+# 3.6.2 (2017-10-02)
+
+* Upgrade MIME type dependency to a newer, secure version
+* Recognize PDF MIME as binary
+* Fix for error in subsequent require() calls (Steven de Salas)
+
+# 3.6.0 (2017-08-20)
+
+* Support disabling TCP_NODELAY option ([#1240](https://github.com/ladjs/superagent/issues/1240)) (xiamengyu)
+* Send payload in query string for GET and HEAD shorthand API (Peter Lyons)
+* Support passphrase with pfx certificate (Paul Westerdale (ABRS Limited))
+* Documentation improvements (Peter Lyons)
+* Fixed duplicated query string params ([#1200](https://github.com/ladjs/superagent/issues/1200)) (Kornel)
+
+# 3.5.1 (2017-03-18)
+
+* Allow crossDomain errors to be retried ([#1194](https://github.com/ladjs/superagent/issues/1194)) (Michael Olson)
+* Read responseType property from the correct object (Julien Dupouy)
+* Check for ownProperty before adding header (Lucas Vieira)
+
+# 3.5.0 (2017-02-23)
+
+* Add errno to distinguish between request timeout and body download timeout ([#1184](https://github.com/ladjs/superagent/issues/1184)) (Kornel Lesiński)
+* Warn about bogus timeout options ([#1185](https://github.com/ladjs/superagent/issues/1185)) (Kornel Lesiński)
+
+# 3.4.4 (2017-02-17)
+
+* Treat videos like images (Kornel Lesiński)
+* Avoid renaming module (Kornel Lesiński)
+
+# 3.4.3 (2017-02-14)
+
+* Fixed being able to define own parsers when their mime type starts with `text/` (Damien Clark)
+* `withCredentials(false)` (Andy Woods)
+* Use `formData.on` instead of `.once` (Kornel Lesiński)
+* Ignore `attach("file",null)` (Kornel Lesiński)
+
+# 3.4.1 (2017-01-29)
+
+* Allow `retry()` and `retry(0)` (Alexander Pope)
+* Allow optional body/data in DELETE requests (Alpha Shuro)
+* Fixed query string on retried requests (Kornel Lesiński)
+
+# 3.4.0 (2017-01-25)
+
+* New `.retry(n)` method and `err.retries` (Alexander Pope)
+* Docs for HTTPS request (Jun Wan Goh)
+
+# 3.3.1 (2016-12-17)
+
+* Fixed "double callback bug" warning on timeouts of gzipped responses
+
+# 3.3.0 (2016-12-14)
+
+* Added `.ok(callback)` that allows customizing which responses are errors (Kornel Lesiński)
+* Added `.responseType()` to Node version (Kornel Lesiński)
+* Added `.parse()` to browser version (jakepearson)
+* Fixed parse error when using `responseType('blob')` (Kornel Lesiński)
+
+# 3.2.0 (2016-12-11)
+
+* Added `.timeout({response:ms})`, which allows limiting maximum response time independently from total download time (Kornel Lesiński)
+* Added warnings when `.end()` is called more than once (Kornel Lesiński)
+* Added `response.links` to browser version (Lukas Eipert)
+* `btoa` is no longer required in IE9 (Kornel Lesiński)
+* Fixed `.sortQuery()` on URLs without query strings (Kornel Lesiński)
+* Refactored common response code into `ResponseBase` (Lukas Eipert)
+
+# 3.1.0 (2016-11-28)
+
+* Added `.sortQuery()` (vicanso)
+* Added support for arrays and bools in `.field()` (Kornel Lesiński)
+* Made `superagent.Request` subclassable without need to patch all static methods (Kornel Lesiński)
+
+# 3.0.0 (2016-11-19)
+
+* Dropped support for Node 0.x. Please upgrade to at least Node 4.
+* Dropped support for componentjs (Damien Caselli)
+* Removed deprecated `.part()`/`superagent.Part` APIs.
+* Removed unreliable `.body` property on internal response object used by unbuffered parsers.
+ Note: the normal `response.body` is unaffected.
+* Multiple `.send()` calls mixing `Buffer`/`Blob` and JSON data are not possible and will now throw instead of messing up the data.
+* Improved `.send()` data object type check (Fernando Mendes)
+* Added common prototype for Node and browser versions (Andreas Helmberger)
+* Added `http+unix:` schema to support Unix sockets (Yuki KAN)
+* Added full `attach` options parameter in the Node version (Lapo Luchini)
+* Added `pfx` TLS option with new `pfx()` method. (Reid Burke)
+* Internally changed `.on` to `.once` to prevent possible memory leaks (Matt Blair)
+* Made all errors reported as an event (Kornel Lesiński)
+
+# 2.3.0 (2016-09-20)
+
+* Enabled `.field()` to handle objects (Affan Shahid)
+* Added authentication with client certificates (terusus)
+* Added `.catch()` for more Promise-like interface (Maxim Samoilov, Kornel Lesiński)
+* Silenced errors from incomplete gzip streams for compatibility with web browsers (Kornel Lesiński)
+* Fixed `event.direction` in uploads (Kornel Lesiński)
+* Fixed returned value of overwritten response object's `on()` method (Juan Dopazo)
+
+# 2.2.0 (2016-08-13)
+
+* Added `timedout` property to node Request instance (Alexander Pope)
+* Unified `null` querystring values in node and browser environments. (George Chung)
+
+# 2.1.0 (2016-06-14)
+
+* Refactored async parsers. Now the `end` callback waits for async parsers to finish (Kornel Lesiński)
+* Errors thrown in `.end()` callback don't cause the callback to be called twice (Kornel Lesiński)
+* Added `headers` to `toJSON()` (Tao)
+
+# 2.0.0 (2016-05-29)
+
+
+## Breaking changes
+
+Breaking changes are in rarely used functionality, so we hope upgrade will be smooth for most users.
+
+* Browser: The `.parse()` method has been renamed to `.serialize()` for consistency with NodeJS version.
+* Browser: Query string keys without a value used to be parsed as `'undefined'`, now their value is `''` (empty string) (shura, Kornel Lesiński).
+* NodeJS: The `redirect` event is called after new query string and headers have been set and is allowed to override the request URL (Kornel Lesiński)
+* `.then()` returns a real `Promise`. Note that use of superagent with promises now requires a global `Promise` object.
+ If you target Internet Explorer or Node 0.10, you'll need `require('es6-promise').polyfill()` or similar.
+* Upgraded all dependencies (Peter Lyons)
+* Renamed properties documented as `@api private` to have `_prefixed` names (Kornel Lesiński)
+
+
+## Probably not breaking changes:
+
+* Extracted common functions to request-base (Peter Lyons)
+* Fixed race condition in pipe tests (Peter Lyons)
+* Handle `FormData` error events (scriptype)
+* Fixed wrong jsdoc of Request#attach (George Chung)
+* Updated and improved tests (Peter Lyons)
+* `request.head()` supports `.redirects(5)` call (Kornel Lesiński)
+* `response` event is also emitted when using `.pipe()`
+
+# 1.8.2 (2016-03-20)
+
+* Fixed handling of HTTP status 204 with content-encoding: gzip (Andrew Shelton)
+* Handling of FormData error events (scriptype)
+* Fixed parsing of `vnd+json` MIME types (Kornel Lesiński)
+* Aliased browser implementation of `.parse()` as `.serialize()` for forward compatibility
+
+# 1.8.1 (2016-03-14)
+
+* Fixed form-data incompatibility with IE9
+
+# 1.8.0 (2016-03-09)
+
+* Extracted common code into request-base class (Peter Lyons)
+ * It does not affect the public API, but please let us know if you notice any plugins/subclasses breaking!
+* Added option `{type:'auto'}` to `auth` method, which enables browser-native auth types (Jungle, Askar Yusupov)
+* Added `responseType()` to set XHR `responseType` (chris)
+* Switched to form-data for browserify-compatible `FormData` (Peter Lyons)
+* Added `statusCode` to error response when JSON response is malformed (mattdell)
+* Prevented TCP port conflicts in all tests (Peter Lyons)
+* Updated form-data dependency
+
+# 1.7.2 (2016-01-26)
+
+* Fix case-sensitivity of header fields introduced by [`a4ddd6a`](https://github.com/ladjs/superagent/commit/a4ddd6a). (Edward J. Jinotti)
+* bump extend dependency, as former version did not contain any license information (Lukas Eipert)
+
+# 1.7.1 (2016-01-21)
+
+* Fixed a conflict with express when using npm 3.x (Glenn)
+* Fixed redirects after a multipart/form-data POST request (cyclist2)
+
+# 1.7.0 (2016-01-18)
+
+* When attaching files, read default filename from the `File` object (JD Isaacks)
+* Add `direction` property to `progress` events (Joseph Dykstra)
+* Update component-emitter & formidable (Kornel Lesiński)
+* Don't re-encode query string needlessly (Ruben Verborgh)
+* ensure querystring is appended when doing `stream.pipe(request)` (Keith Grennan)
+* change set header function, not call `this.request()` until call `this.end()` (vicanso)
+* Add no-op `withCredentials` to Node API (markdalgleish)
+* fix `delete` breaking on ie8 (kenjiokabe)
+* Don't let request error override responses (Clay Reimann)
+* Increased number of tests shared between node and client (Kornel Lesiński)
+
+# 1.6.0/1.6.1 (2015-12-09)
+
+* avoid misleading CORS error message
+* added 'progress' event on file/form upload in Node (Olivier Lalonde)
+* return raw response if the response parsing fails (Rei Colina)
+* parse content-types ending with `+json` as JSON (Eiryyy)
+* fix to avoid throwing errors on aborted requests (gjurgens)
+* retain cookies on redirect when hosts match (Tom Conroy)
+* added Bower manifest (Johnny Freeman)
+* upgrade to latest cookiejar (Andy Burke)
+
+# 1.5.0 (2015-11-30)
+
+* encode array values as `key=1&key=2&key=3` etc... (aalpern, Davis Kim)
+* avoid the error which is omitted from 'socket hang up'
+* faster JSON parsing, handling of zlib errors (jbellenger)
+* fix IE11 sends 'undefined' string if data was undefined (Vadim Goncharov)
+* alias `del()` method as `delete()` (Aaron Krause)
+* revert Request#parse since it was actually Response#parse
+
+# 1.4.0 (2015-09-14)
+
+* add Request#parse method to client library
+* add missing statusCode in client response
+* don't apply JSON heuristics if a valid parser is found
+* fix detection of root object for webworkers
+
+# 1.3.0 (2015-08-05)
+
+* fix incorrect content-length of data set to buffer
+* serialize request data takes into account charsets
+* add basic promise support via a `then` function
+
+# 1.2.0 (2015-04-13)
+
+* add progress events to downlodas
+* make usable in webworkers
+* add support for 308 redirects
+* update node-form-data dependency
+* update to work in react native
+* update node-mime dependency
+
+# 1.1.0 (2015-03-13)
+
+* Fix responseType checks without xhr2 and ie9 tests (rase-)
+* errors have .status and .response fields if applicable (defunctzombie)
+* fix end callback called before saving cookies (rase-)
+
+# 1.0.0 / 2015-03-08
+
+* All non-200 responses are treated as errors now. (The callback is called with an error when the response has a status < 200 or >= 300 now. In previous versions this would not have raised an error and the client would have to check the `res` object. See [#283](https://github.com/ladjs/superagent/issues/283).
+* keep timeouts intact across redirects (hopkinsth)
+* handle falsy json values (themaarten)
+* fire response events in browser version (Schoonology)
+* getXHR exported in client version (KidsKilla)
+* remove arity check on `.end()` callbacks (defunctzombie)
+* avoid setting content-type for host objects (rexxars)
+* don't index array strings in querystring (travisjeffery)
+* fix pipe() with redirects (cyrilis)
+* add xhr2 file download (vstirbu)
+* set default response type to text/plain if not specified (warrenseine)
+
+# 0.21.0 / 2014-11-11
+
+* Trim text before parsing json (gjohnson)
+* Update tests to express 4 (gaastonsr)
+* Prevent double callback when error is thrown (pgn-vole)
+* Fix missing clearTimeout (nickdima)
+* Update debug (TooTallNate)
+
+# 0.20.0 / 2014-10-02
+
+* Add toJSON() to request and response instances. (yields)
+* Prevent HEAD requests from getting parsed. (gjohnson)
+* Update debug. (TooTallNate)
+
+# 0.19.1 / 2014-09-24
+
+* Fix basic auth issue when password is falsey value. (gjohnson)
+
+# 0.19.0 / 2014-09-24
+
+* Add unset() to browser. (shesek)
+* Prefer XHR over ActiveX. (omeid)
+* Catch parse errors. (jacwright)
+* Update qs dependency. (wercker)
+* Add use() to node. (Financial-Times)
+* Add response text to errors. (yields)
+* Don't send empty cookie headers. (undoZen)
+* Don't parse empty response bodies. (DveMac)
+* Use hostname when setting cookie host. (prasunsultania)
+
+# 0.18.2 / 2014-07-12
+
+* Handle parser errors. (kof)
+* Ensure not to use default parsers when there is a user defined one. (kof)
+
+# 0.18.1 / 2014-07-05
+
+* Upgrade cookiejar dependency (juanpin)
+* Support image mime types (nebulade)
+* Make .agent chainable (kof)
+* Upgrade debug (TooTallNate)
+* Fix docs (aheckmann)
+
+# 0.18.0 / 2014-04-29
+
+* Use "form-data" module for the multipart/form-data implementation. (TooTallNate)
+* Add basic `field()` and `attach()` functions for HTML5 FormData. (TooTallNate)
+* Deprecate `part()`. (TooTallNate)
+* Set default user-agent header. (bevacqua)
+* Add `unset()` method for removing headers. (bevacqua)
+* Update cookiejar. (missinglink)
+* Fix response error formatting. (shesek)
+
+# 0.17.0 / 2014-03-06
+
+* supply uri malformed error to the callback (yields)
+* add request event (yields)
+* allow simple auth (yields)
+* add request event (yields)
+* switch to component/reduce (visionmedia)
+* fix part content-disposition (mscdex)
+* add browser testing via zuul (defunctzombie)
+* adds request.use() (johntron)
+
+# 0.16.0 / 2014-01-07
+
+* remove support for 0.6 (superjoe30)
+* fix CORS withCredentials (wejendorp)
+* add "test" script (superjoe30)
+* add request .accept() method (nickl-)
+* add xml to mime types mappings (nickl-)
+* fix parse body error on HEAD requests (gjohnson)
+* fix documentation typos (matteofigus)
+* fix content-type + charset (bengourley)
+* fix null values on query parameters (cristiandouce)
+
+# 0.15.7 / 2013-10-19
+
+* pin should.js to 1.3.0 due to breaking change in 2.0.x
+* fix browserify regression
+
+# 0.15.5 / 2013-10-09
+
+* add browser field to support browserify
+* fix .field() value number support
+
+# 0.15.4 / 2013-07-09
+
+* node: add a Request#agent() function to set the http Agent to use
+
+# 0.15.3 / 2013-07-05
+
+* fix .pipe() unzipping on more recent nodes. Closes [#240](https://github.com/ladjs/superagent/issues/240)
+* fix passing an empty object to .query() no longer appends "?"
+* fix formidable error handling
+* update formidable
+
+# 0.15.2 / 2013-07-02
+
+* fix: emit 'end' when piping.
+
+# 0.15.1 / 2013-06-26
+
+* add try/catch around parseLinks
+
+# 0.15.0 / 2013-06-25
+
+* make `Response#toError()` have a more meaningful `message`
+
+# 0.14.9 / 2013-06-15
+
+* add debug()s to the node client
+* add .abort() method to node client
+
+# 0.14.8 / 2013-06-13
+
+* set .agent = false always
+* remove X-Requested-With. Closes [#189](https://github.com/ladjs/superagent/issues/189)
+
+# 0.14.7 / 2013-06-06
+
+* fix unzip error handling
+
+# 0.14.6 / 2013-05-23
+
+* fix HEAD unzip bug
+
+# 0.14.5 / 2013-05-23
+
+* add flag to ensure the callback is **never** invoked twice
+
+# 0.14.4 / 2013-05-22
+
+* add superagent.js build output
+* update qs
+* update emitter-component
+* revert "add browser field to support browserify" see [GH-221](https://github.com/ladjs/superagent/issues/221)
+
+# 0.14.3 / 2013-05-18
+
+* add browser field to support browserify
+
+# 0.14.2/ 2013-05-07
+
+* add host object check to fix serialization of File/Blobs etc as json
+
+# 0.14.1 / 2013-04-09
+
+* update qs
+
+# 0.14.0 / 2013-04-02
+
+* add client-side basic auth
+* fix retaining of .set() header field case
+
+# 0.13.0 / 2013-03-13
+
+* add progress events to client
+* add simple example
+* add res.headers as alias of res.header for browser client
+* add res.get(field) to node/client
+
+# 0.12.4 / 2013-02-11
+
+* fix get content-type even if can't get other headers in firefox. fixes [#181](https://github.com/ladjs/superagent/issues/181)
+
+# 0.12.3 / 2013-02-11
+
+* add quick "progress" event support
+
+# 0.12.2 / 2013-02-04
+
+* add test to check if response acts as a readable stream
+* add ReadableStream in the Response prototype.
+* add test to assert correct redirection when the host changes in the location header.
+* add default Accept-Encoding. Closes [#155](https://github.com/ladjs/superagent/issues/155)
+* fix req.pipe() return value of original stream for node parity. Closes [#171](https://github.com/ladjs/superagent/issues/171)
+* remove the host header when cleaning headers to properly follow the redirection.
+
+# 0.12.1 / 2013-01-10
+
+* add x-domain error handling
+
+# 0.12.0 / 2013-01-04
+
+* add header persistence on redirects
+
+# 0.11.0 / 2013-01-02
+
+* add .error Error object. Closes [#156](https://github.com/ladjs/superagent/issues/156)
+* add forcing of res.text removal for FF HEAD responses. Closes [#162](https://github.com/ladjs/superagent/issues/162)
+* add reduce component usage. Closes [#90](https://github.com/ladjs/superagent/issues/90)
+* move better-assert dep to development deps
+
+# 0.10.0 / 2012-11-14
+
+* add req.timeout(ms) support for the client
+
+# 0.9.10 / 2012-11-14
+
+* fix client-side .query(str) support
+
+# 0.9.9 / 2012-11-14
+
+* add .parse(fn) support
+* fix socket hangup with dates in querystring. Closes [#146](https://github.com/ladjs/superagent/issues/146)
+* fix socket hangup "error" event when a callback of arity 2 is provided
+
+# 0.9.8 / 2012-11-03
+
+* add emission of error from `Request#callback()`
+* add a better fix for nodes weird socket hang up error
+* add PUT/POST/PATCH data support to client short-hand functions
+* add .license property to component.json
+* change client portion to build using component(1)
+* fix GET body support [guille]
+
+# 0.9.7 / 2012-10-19
+
+* fix `.buffer()` `res.text` when no parser matches
+
+# 0.9.6 / 2012-10-17
+
+* change: use `this` when `window` is undefined
+* update to new component spec [juliangruber]
+* fix emission of "data" events for compressed responses without encoding. Closes [#125](https://github.com/ladjs/superagent/issues/125)
+
+# 0.9.5 / 2012-10-01
+
+* add field name to .attach()
+* add text "parser"
+* refactor isObject()
+* remove wtf isFunction() helper
+
+# 0.9.4 / 2012-09-20
+
+* fix `Buffer` responses [TooTallNate]
+* fix `res.type` when a "type" param is present [TooTallNate]
+
+# 0.9.3 / 2012-09-18
+
+* remove **GET** `.send()` == `.query()` special-case (**API** change !!!)
+
+# 0.9.2 / 2012-09-17
+
+* add `.aborted` prop
+* add `.abort()`. Closes [#115](https://github.com/ladjs/superagent/issues/115)
+
+# 0.9.1 / 2012-09-07
+
+* add `.forbidden` response property
+* add component.json
+* change emitter-component to 0.0.5
+* fix client-side tests
+
+# 0.9.0 / 2012-08-28
+
+* add `.timeout(ms)`. Closes [#17](https://github.com/ladjs/superagent/issues/17)
+
+# 0.8.2 / 2012-08-28
+
+* fix pathname relative redirects. Closes [#112](https://github.com/ladjs/superagent/issues/112)
+
+# 0.8.1 / 2012-08-21
+
+* fix redirects when schema is specified
+
+# 0.8.0 / 2012-08-19
+
+* add `res.buffered` flag
+* add buffering of text/\*, json and forms only by default. Closes [#61](https://github.com/ladjs/superagent/issues/61)
+* add `.buffer(false)` cancellation
+* add cookie jar support [hunterloftis]
+* add agent functionality [hunterloftis]
+
+# 0.7.0 / 2012-08-03
+
+* allow `query()` to be called after the internal `req` has been created [tootallnate]
+
+# 0.6.0 / 2012-07-17
+
+* add `res.send('foo=bar')` default of "application/x-www-form-urlencoded"
+
+# 0.5.1 / 2012-07-16
+
+* add "methods" dep
+* add `.end()` arity check to node callbacks
+* fix unzip support due to weird node internals
+
+# 0.5.0 / 2012-06-16
+
+* Added "Link" response header field parsing, exposing `res.links`
+
+# 0.4.3 / 2012-06-15
+
+* Added 303, 305 and 307 as redirect status codes [slaskis]
+* Fixed passing an object as the url
+
+# 0.4.2 / 2012-06-02
+
+* Added component support
+* Fixed redirect data
+
+# 0.4.1 / 2012-04-13
+
+* Added HTTP PATCH support
+* Fixed: GET / HEAD when following redirects. Closes [#86](https://github.com/ladjs/superagent/issues/86)
+* Fixed Content-Length detection for multibyte chars
+
+# 0.4.0 / 2012-03-04
+
+* Added `.head()` method [browser]. Closes [#78](https://github.com/ladjs/superagent/issues/78)
+* Added `make test-cov` support
+* Added multipart request support. Closes [#11](https://github.com/ladjs/superagent/issues/11)
+* Added all methods that node supports. Closes [#71](https://github.com/ladjs/superagent/issues/71)
+* Added "response" event providing a Response object. Closes [#28](https://github.com/ladjs/superagent/issues/28)
+* Added `.query(obj)`. Closes [#59](https://github.com/ladjs/superagent/issues/59)
+* Added `res.type` (browser). Closes [#54](https://github.com/ladjs/superagent/issues/54)
+* Changed: default `res.body` and `res.files` to {}
+* Fixed: port existing query-string fix (browser). Closes [#57](https://github.com/ladjs/superagent/issues/57)
+
+# 0.3.0 / 2012-01-24
+
+* Added deflate/gzip support [guillermo]
+* Added `res.type` (Content-Type void of params)
+* Added `res.statusCode` to mirror node
+* Added `res.headers` to mirror node
+* Changed: parsers take callbacks
+* Fixed optional schema support. Closes [#49](https://github.com/ladjs/superagent/issues/49)
+
+# 0.2.0 / 2012-01-05
+
+* Added url auth support
+* Added `.auth(username, password)`
+* Added basic auth support [node]. Closes [#41](https://github.com/ladjs/superagent/issues/41)
+* Added `make test-docs`
+* Added guillermo's EventEmitter. Closes [#16](https://github.com/ladjs/superagent/issues/16)
+* Removed `Request#data()` for SS, renamed to `send()`
+* Removed `Request#data()` from client, renamed to `send()`
+* Fixed array support. [browser]
+* Fixed array support. Closes [#35](https://github.com/ladjs/superagent/issues/35) [node]
+* Fixed `EventEmitter#emit()`
+
+# 0.1.3 / 2011-10-25
+
+* Added error to callback
+* Bumped node dep for 0.5.x
+
+# 0.1.2 / 2011-09-24
+
+* Added markdown documentation
+* Added `request(url[, fn])` support to the client
+* Added `qs` dependency to package.json
+* Added options for `Request#pipe()`
+* Added support for `request(url, callback)`
+* Added `request(url)` as shortcut for `request.get(url)`
+* Added `Request#pipe(stream)`
+* Added inherit from `Stream`
+* Added multipart support
+* Added ssl support (node)
+* Removed Content-Length field from client
+* Fixed buffering, `setEncoding()` to utf8 [reported by stagas]
+* Fixed "end" event when piping
+
+# 0.1.1 / 2011-08-20
+
+* Added `res.redirect` flag (node)
+* Added redirect support (node)
+* Added `Request#redirects(n)` (node)
+* Added `.set(object)` header field support
+* Fixed `Content-Length` support
+
+# 0.1.0 / 2011-08-09
+
+* Added support for multiple calls to `.data()`
+* Added support for `.get(uri, obj)`
+* Added GET `.data()` querystring support
+* Added IE{6,7,8} support [alexyoung]
+
+# 0.0.1 / 2011-08-05
+
+* Initial commit
+
+
+
diff --git a/History.md b/History.md
deleted file mode 100644
index 56abef28a..000000000
--- a/History.md
+++ /dev/null
@@ -1,476 +0,0 @@
-# 1.2.0 (2015-04-13)
-
- * add progress events to downlodas
- * make usable in webworkers
- * add support for 308 redirects
- * update node-form-data dependency
- * update to work in react native
- * update node-mime dependency
-
-# 1.1.0 (2015-03-13)
-
- * Fix responseType checks without xhr2 and ie9 tests (rase-)
- * errors have .status and .response fields if applicable (defunctzombie)
- * fix end callback called before saving cookies (rase-)
-
-1.0.0 / 2015-03-08
-==================
-
- * All non-200 responses are treated as errors now. (The callback is called with an error when the response has a status < 200 or >= 300 now. In previous versions this would not have raised an error and the client would have to check the `res` object. See [#283](https://github.com/visionmedia/superagent/issues/283).
- * keep timeouts intact across redirects (hopkinsth)
- * handle falsy json values (themaarten)
- * fire response events in browser version (Schoonology)
- * getXHR exported in client version (KidsKilla)
- * remove arity check on `.end()` callbacks (defunctzombie)
- * avoid setting content-type for host objects (rexxars)
- * don't index array strings in querystring (travisjeffery)
- * fix pipe() with redirects (cyrilis)
- * add xhr2 file download (vstirbu)
- * set default response type to text/plain if not specified (warrenseine)
-
-0.21.0 / 2014-11-11
-==================
-
- * Trim text before parsing json (gjohnson)
- * Update tests to express 4 (gaastonsr)
- * Prevent double callback when error is thrown (pgn-vole)
- * Fix missing clearTimeout (nickdima)
- * Update debug (TooTallNate)
-
-0.20.0 / 2014-10-02
-==================
-
- * Add toJSON() to request and response instances. (yields)
- * Prevent HEAD requests from getting parsed. (gjohnson)
- * Update debug. (TooTallNate)
-
-0.19.1 / 2014-09-24
-==================
-
- * Fix basic auth issue when password is falsey value. (gjohnson)
-
-0.19.0 / 2014-09-24
-==================
-
- * Add unset() to browser. (shesek)
- * Prefer XHR over ActiveX. (omeid)
- * Catch parse errors. (jacwright)
- * Update qs dependency. (wercker)
- * Add use() to node. (Financial-Times)
- * Add response text to errors. (yields)
- * Don't send empty cookie headers. (undoZen)
- * Don't parse empty response bodies. (DveMac)
- * Use hostname when setting cookie host. (prasunsultania)
-
-0.18.2 / 2014-07-12
-==================
-
- * Handle parser errors. (kof)
- * Ensure not to use default parsers when there is a user defined one. (kof)
-
-0.18.1 / 2014-07-05
-==================
-
- * Upgrade cookiejar dependency (juanpin)
- * Support image mime types (nebulade)
- * Make .agent chainable (kof)
- * Upgrade debug (TooTallNate)
- * Fix docs (aheckmann)
-
-0.18.0 / 2014-04-29
-===================
-
-* Use "form-data" module for the multipart/form-data implementation. (TooTallNate)
-* Add basic `field()` and `attach()` functions for HTML5 FormData. (TooTallNate)
-* Deprecate `part()`. (TooTallNate)
-* Set default user-agent header. (bevacqua)
-* Add `unset()` method for removing headers. (bevacqua)
-* Update cookiejar. (missinglink)
-* Fix response error formatting. (shesek)
-
-0.17.0 / 2014-03-06
-===================
-
- * supply uri malformed error to the callback (yields)
- * add request event (yields)
- * allow simple auth (yields)
- * add request event (yields)
- * switch to component/reduce (visionmedia)
- * fix part content-disposition (mscdex)
- * add browser testing via zuul (defunctzombie)
- * adds request.use() (johntron)
-
-0.16.0 / 2014-01-07
-==================
-
- * remove support for 0.6 (superjoe30)
- * fix CORS withCredentials (wejendorp)
- * add "test" script (superjoe30)
- * add request .accept() method (nickl-)
- * add xml to mime types mappings (nickl-)
- * fix parse body error on HEAD requests (gjohnson)
- * fix documentation typos (matteofigus)
- * fix content-type + charset (bengourley)
- * fix null values on query parameters (cristiandouce)
-
-0.15.7 / 2013-10-19
-==================
-
- * pin should.js to 1.3.0 due to breaking change in 2.0.x
- * fix browserify regression
-
-0.15.5 / 2013-10-09
-==================
-
- * add browser field to support browserify
- * fix .field() value number support
-
-0.15.4 / 2013-07-09
-==================
-
- * node: add a Request#agent() function to set the http Agent to use
-
-0.15.3 / 2013-07-05
-==================
-
- * fix .pipe() unzipping on more recent nodes. Closes #240
- * fix passing an empty object to .query() no longer appends "?"
- * fix formidable error handling
- * update formidable
-
-0.15.2 / 2013-07-02
-==================
-
- * fix: emit 'end' when piping.
-
-0.15.1 / 2013-06-26
-==================
-
- * add try/catch around parseLinks
-
-0.15.0 / 2013-06-25
-==================
-
- * make `Response#toError()` have a more meaningful `message`
-
-0.14.9 / 2013-06-15
-==================
-
- * add debug()s to the node client
- * add .abort() method to node client
-
-0.14.8 / 2013-06-13
-==================
-
- * set .agent = false always
- * remove X-Requested-With. Closes #189
-
-0.14.7 / 2013-06-06
-==================
-
- * fix unzip error handling
-
-0.14.6 / 2013-05-23
-==================
-
- * fix HEAD unzip bug
-
-0.14.5 / 2013-05-23
-==================
-
- * add flag to ensure the callback is __never__ invoked twice
-
-0.14.4 / 2013-05-22
-==================
-
- * add superagent.js build output
- * update qs
- * update emitter-component
- * revert "add browser field to support browserify" see GH-221
-
-0.14.3 / 2013-05-18
-==================
-
- * add browser field to support browserify
-
-0.14.2/ 2013-05-07
-==================
-
- * add host object check to fix serialization of File/Blobs etc as json
-
-0.14.1 / 2013-04-09
-==================
-
- * update qs
-
-0.14.0 / 2013-04-02
-==================
-
- * add client-side basic auth
- * fix retaining of .set() header field case
-
-0.13.0 / 2013-03-13
-==================
-
- * add progress events to client
- * add simple example
- * add res.headers as alias of res.header for browser client
- * add res.get(field) to node/client
-
-0.12.4 / 2013-02-11
-==================
-
- * fix get content-type even if can't get other headers in firefox. fixes #181
-
-0.12.3 / 2013-02-11
-==================
-
- * add quick "progress" event support
-
-0.12.2 / 2013-02-04
-==================
-
- * add test to check if response acts as a readable stream
- * add ReadableStream in the Response prototype.
- * add test to assert correct redirection when the host changes in the location header.
- * add default Accept-Encoding. Closes #155
- * fix req.pipe() return value of original stream for node parity. Closes #171
- * remove the host header when cleaning headers to properly follow the redirection.
-
-0.12.1 / 2013-01-10
-==================
-
- * add x-domain error handling
-
-0.12.0 / 2013-01-04
-==================
-
- * add header persistence on redirects
-
-0.11.0 / 2013-01-02
-==================
-
- * add .error Error object. Closes #156
- * add forcing of res.text removal for FF HEAD responses. Closes #162
- * add reduce component usage. Closes #90
- * move better-assert dep to development deps
-
-0.10.0 / 2012-11-14
-==================
-
- * add req.timeout(ms) support for the client
-
-0.9.10 / 2012-11-14
-==================
-
- * fix client-side .query(str) support
-
-0.9.9 / 2012-11-14
-==================
-
- * add .parse(fn) support
- * fix socket hangup with dates in querystring. Closes #146
- * fix socket hangup "error" event when a callback of arity 2 is provided
-
-0.9.8 / 2012-11-03
-==================
-
- * add emission of error from `Request#callback()`
- * add a better fix for nodes weird socket hang up error
- * add PUT/POST/PATCH data support to client short-hand functions
- * add .license property to component.json
- * change client portion to build using component(1)
- * fix GET body support [guille]
-
-0.9.7 / 2012-10-19
-==================
-
- * fix `.buffer()` `res.text` when no parser matches
-
-0.9.6 / 2012-10-17
-==================
-
- * change: use `this` when `window` is undefined
- * update to new component spec [juliangruber]
- * fix emission of "data" events for compressed responses without encoding. Closes #125
-
-0.9.5 / 2012-10-01
-==================
-
- * add field name to .attach()
- * add text "parser"
- * refactor isObject()
- * remove wtf isFunction() helper
-
-0.9.4 / 2012-09-20
-==================
-
- * fix `Buffer` responses [TooTallNate]
- * fix `res.type` when a "type" param is present [TooTallNate]
-
-0.9.3 / 2012-09-18
-==================
-
- * remove __GET__ `.send()` == `.query()` special-case (__API__ change !!!)
-
-0.9.2 / 2012-09-17
-==================
-
- * add `.aborted` prop
- * add `.abort()`. Closes #115
-
-0.9.1 / 2012-09-07
-==================
-
- * add `.forbidden` response property
- * add component.json
- * change emitter-component to 0.0.5
- * fix client-side tests
-
-0.9.0 / 2012-08-28
-==================
-
- * add `.timeout(ms)`. Closes #17
-
-0.8.2 / 2012-08-28
-==================
-
- * fix pathname relative redirects. Closes #112
-
-0.8.1 / 2012-08-21
-==================
-
- * fix redirects when schema is specified
-
-0.8.0 / 2012-08-19
-==================
-
- * add `res.buffered` flag
- * add buffering of text/*, json and forms only by default. Closes #61
- * add `.buffer(false)` cancellation
- * add cookie jar support [hunterloftis]
- * add agent functionality [hunterloftis]
-
-0.7.0 / 2012-08-03
-==================
-
- * allow `query()` to be called after the internal `req` has been created [tootallnate]
-
-0.6.0 / 2012-07-17
-==================
-
- * add `res.send('foo=bar')` default of "application/x-www-form-urlencoded"
-
-0.5.1 / 2012-07-16
-==================
-
- * add "methods" dep
- * add `.end()` arity check to node callbacks
- * fix unzip support due to weird node internals
-
-0.5.0 / 2012-06-16
-==================
-
- * Added "Link" response header field parsing, exposing `res.links`
-
-0.4.3 / 2012-06-15
-==================
-
- * Added 303, 305 and 307 as redirect status codes [slaskis]
- * Fixed passing an object as the url
-
-0.4.2 / 2012-06-02
-==================
-
- * Added component support
- * Fixed redirect data
-
-0.4.1 / 2012-04-13
-==================
-
- * Added HTTP PATCH support
- * Fixed: GET / HEAD when following redirects. Closes #86
- * Fixed Content-Length detection for multibyte chars
-
-0.4.0 / 2012-03-04
-==================
-
- * Added `.head()` method [browser]. Closes #78
- * Added `make test-cov` support
- * Added multipart request support. Closes #11
- * Added all methods that node supports. Closes #71
- * Added "response" event providing a Response object. Closes #28
- * Added `.query(obj)`. Closes #59
- * Added `res.type` (browser). Closes #54
- * Changed: default `res.body` and `res.files` to {}
- * Fixed: port existing query-string fix (browser). Closes #57
-
-0.3.0 / 2012-01-24
-==================
-
- * Added deflate/gzip support [guillermo]
- * Added `res.type` (Content-Type void of params)
- * Added `res.statusCode` to mirror node
- * Added `res.headers` to mirror node
- * Changed: parsers take callbacks
- * Fixed optional schema support. Closes #49
-
-0.2.0 / 2012-01-05
-==================
-
- * Added url auth support
- * Added `.auth(username, password)`
- * Added basic auth support [node]. Closes #41
- * Added `make test-docs`
- * Added guillermo's EventEmitter. Closes #16
- * Removed `Request#data()` for SS, renamed to `send()`
- * Removed `Request#data()` from client, renamed to `send()`
- * Fixed array support. [browser]
- * Fixed array support. Closes #35 [node]
- * Fixed `EventEmitter#emit()`
-
-0.1.3 / 2011-10-25
-==================
-
- * Added error to callback
- * Bumped node dep for 0.5.x
-
-0.1.2 / 2011-09-24
-==================
-
- * Added markdown documentation
- * Added `request(url[, fn])` support to the client
- * Added `qs` dependency to package.json
- * Added options for `Request#pipe()`
- * Added support for `request(url, callback)`
- * Added `request(url)` as shortcut for `request.get(url)`
- * Added `Request#pipe(stream)`
- * Added inherit from `Stream`
- * Added multipart support
- * Added ssl support (node)
- * Removed Content-Length field from client
- * Fixed buffering, `setEncoding()` to utf8 [reported by stagas]
- * Fixed "end" event when piping
-
-0.1.1 / 2011-08-20
-==================
-
- * Added `res.redirect` flag (node)
- * Added redirect support (node)
- * Added `Request#redirects(n)` (node)
- * Added `.set(object)` header field support
- * Fixed `Content-Length` support
-
-0.1.0 / 2011-08-09
-==================
-
- * Added support for multiple calls to `.data()`
- * Added support for `.get(uri, obj)`
- * Added GET `.data()` querystring support
- * Added IE{6,7,8} support [alexyoung]
-
-0.0.1 / 2011-08-05
-==================
-
- * Initial commit
-
diff --git a/LICENSE b/LICENSE
index 9051a2bdf..1b188ba5d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
(The MIT License)
-Copyright (c) 2014-2015 TJ Holowaychuk
+Copyright (c) 2014-2016 TJ Holowaychuk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/Makefile b/Makefile
index 47cd9ea0f..0acb194b5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,48 +1,77 @@
-
-TESTS = test/*.js test/node/*.js
+OLDNODETESTS ?= lib/node/test/*.js lib/node/test/node/*.js
+NODETESTS ?= test/*.js test/node/*.js
+BROWSERTESTS ?= test/*.js test/client/*.js
REPORTER = spec
-all: superagent.js
+ifeq ("$(OLD_NODE_TEST)", "1")
+ NODETESTS := $(OLDNODETESTS)
+endif
test:
- @if [ "x$(BROWSER)" = "x" ]; then make test-node; else make test-browser; fi
+ @if [ "$(BROWSER)" = "1" ]; then \
+ echo test on browser; \
+ make test-browser; \
+ fi \
+
+ @if [ "$(NODE_TEST)" = "1" ] || [ "x$(BROWSER)" = "x" ]; then \
+ echo test on node with http1; \
+ export HTTP2_TEST="" && make test-node; \
+ if [ "$(HTTP2_TEST_DISABLED)" != "1" ]; then \
+ echo test on node with http2; \
+ export HTTP2_TEST="1" && make test-node; \
+ fi \
+ fi
-test-node:
- @NODE_ENV=test NODE_TLS_REJECT_UNAUTHORIZED=0 ./node_modules/.bin/mocha \
+copy:
+ @if [ "$(OLD_NODE_TEST)" = "1" ]; then \
+ echo test on old node; \
+ cp test/node/fixtures lib/node/test/node -rf; \
+ else \
+ echo test on plain node; \
+ fi
+
+test-node:copy
+ @NODE_ENV=test HTTP2_TEST=$(HTTP2_TEST) ./node_modules/.bin/nyc ./node_modules/.bin/mocha \
--require should \
+ --trace-warnings \
+ --throw-deprecation \
--reporter $(REPORTER) \
+ --slow 2000 \
--timeout 5000 \
- --growl \
- $(TESTS)
+ --exit \
+ $(NODETESTS)
test-cov: lib-cov
SUPERAGENT_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
test-browser:
- ./node_modules/.bin/zuul -- test/*.js test/client/*.js
+ SAUCE_APPIUM_VERSION=1.7 ./node_modules/.bin/zuul -- $(BROWSERTESTS)
test-browser-local:
- ./node_modules/.bin/zuul --local 4000 -- test/*.js test/client/*.js
+ ./node_modules/.bin/zuul --no-coverage --local 4000 -- $(BROWSERTESTS)
lib-cov:
jscoverage lib lib-cov
-superagent.js: lib/node/*.js lib/node/parsers/*.js
- @./node_modules/.bin/browserify \
- --standalone superagent \
- --outfile superagent.js .
-
test-server:
@node test/server
-docs: test-docs
+docs: index.html test-docs docs/index.md
+
+index.html: docs/index.md docs/head.html docs/tail.html
+ marked < $< \
+ | cat docs/head.html - docs/tail.html \
+ > $@
+
+docclean:
+ rm -f index.html docs/test.html
-test-docs:
+test-docs: docs/head.html docs/tail.html
make test REPORTER=doc \
| cat docs/head.html - docs/tail.html \
> docs/test.html
clean:
- rm -fr superagent.js components
+ rm -fr components
-.PHONY: test-cov test docs test-docs clean test-browser-local
+.PHONY: copy test-cov test docs test-docs clean test-browser-local
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..4ce4234d2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,273 @@
+# superagent
+
+[](https://github.com/forwardemail/superagent/actions/workflows/ci.yml)
+[](https://codecov.io/gh/ladjs/superagent)
+[](https://github.com/sindresorhus/xo)
+[](https://github.com/prettier/prettier)
+[](https://lass.js.org)
+[](LICENSE)
+
+> Small progressive client-side HTTP request library, and Node.js module with the same API, supporting many high-level HTTP client features. Maintained for [Forward Email](https://github.com/forwardemail) and [Lad](https://github.com/ladjs).
+
+
+## Table of Contents
+
+* [Install](#install)
+* [Usage](#usage)
+ * [Node](#node)
+ * [Browser](#browser)
+* [Supported Platforms](#supported-platforms)
+ * [Required Browser Features](#required-browser-features)
+* [Plugins](#plugins)
+* [Upgrading from previous versions](#upgrading-from-previous-versions)
+* [Contributors](#contributors)
+* [License](#license)
+
+
+## Install
+
+[npm][]:
+
+```sh
+npm install superagent
+```
+
+[yarn][]:
+
+```sh
+yarn add superagent
+```
+
+
+## Usage
+
+### Node
+
+```js
+const superagent = require('superagent');
+
+// callback
+superagent
+ .post('/api/pet')
+ .send({ name: 'Manny', species: 'cat' }) // sends a JSON post body
+ .set('X-API-Key', 'foobar')
+ .set('accept', 'json')
+ .end((err, res) => {
+ // Calling the end function will send the request
+ });
+
+// promise with then/catch
+superagent.post('/api/pet').then(console.log).catch(console.error);
+
+// promise with async/await
+(async () => {
+ try {
+ const res = await superagent.post('/api/pet');
+ console.log(res);
+ } catch (err) {
+ console.error(err);
+ }
+})();
+```
+
+### Browser
+
+**The browser-ready, minified version of `superagent` is only 50 KB (minified and gzipped).**
+
+Browser-ready versions of this module are available via [jsdelivr][], [unpkg][], and also in the `node_modules/superagent/dist` folder in downloads of the `superagent` package.
+
+> Note that we also provide unminified versions with `.js` instead of `.min.js` file extensions.
+
+#### VanillaJS
+
+This is the solution for you if you're just using `
+
+
+
+
+```
+
+#### Bundler
+
+If you are using [browserify][], [webpack][], [rollup][], or another bundler, then you can follow the same usage as [Node](#node) above.
+
+
+## Supported Platforms
+
+* Node: v14.18.0+
+* Browsers (see [.browserslistrc](.browserslistrc)):
+
+ ```sh
+ npx browserslist
+ ```
+
+ ```sh
+ and_chr 102
+ and_ff 101
+ and_qq 10.4
+ and_uc 12.12
+ android 101
+ chrome 103
+ chrome 102
+ chrome 101
+ chrome 100
+ edge 103
+ edge 102
+ edge 101
+ firefox 101
+ firefox 100
+ firefox 91
+ ios_saf 15.5
+ ios_saf 15.4
+ ios_saf 15.2-15.3
+ ios_saf 15.0-15.1
+ ios_saf 14.5-14.8
+ ios_saf 14.0-14.4
+ ios_saf 12.2-12.5
+ kaios 2.5
+ op_mini all
+ op_mob 64
+ opera 86
+ opera 85
+ safari 15.5
+ safari 15.4
+ samsung 17.0
+ samsung 16.0
+ ```
+
+### Required Browser Features
+
+We recommend using (specifically with the bundle mentioned in [VanillaJS](#vanillajs) above):
+
+```html
+
+```
+
+* WeakRef is not supported in Opera 85, iOS Safari 12.2-12.5
+* BigInt is not supported in iOS Safari 12.2-12.5
+
+
+## Plugins
+
+SuperAgent is easily extended via plugins.
+
+```js
+const nocache = require('superagent-no-cache');
+const superagent = require('superagent');
+const prefix = require('superagent-prefix')('/static');
+
+superagent
+ .get('/some-url')
+ .query({ action: 'edit', city: 'London' }) // query string
+ .use(prefix) // Prefixes *only* this request
+ .use(nocache) // Prevents caching of *only* this request
+ .end((err, res) => {
+ // Do something
+ });
+```
+
+Existing plugins:
+
+* [superagent-no-cache](https://github.com/johntron/superagent-no-cache) - prevents caching by including Cache-Control header
+* [superagent-prefix](https://github.com/johntron/superagent-prefix) - prefixes absolute URLs (useful in test environment)
+* [superagent-suffix](https://github.com/timneutkens1/superagent-suffix) - suffix URLs with a given path
+* [superagent-mock](https://github.com/M6Web/superagent-mock) - simulate HTTP calls by returning data fixtures based on the requested URL
+* [superagent-mocker](https://github.com/shuvalov-anton/superagent-mocker) — simulate REST API
+* [superagent-cache](https://github.com/jpodwys/superagent-cache) - A global SuperAgent patch with built-in, flexible caching
+* [superagent-cache-plugin](https://github.com/jpodwys/superagent-cache-plugin) - A SuperAgent plugin with built-in, flexible caching
+* [superagent-jsonapify](https://github.com/alex94puchades/superagent-jsonapify) - A lightweight [json-api](http://jsonapi.org/format/) client addon for superagent
+* [superagent-serializer](https://github.com/zzarcon/superagent-serializer) - Converts server payload into different cases
+* [superagent-httpbackend](https://www.npmjs.com/package/superagent-httpbackend) - stub out requests using AngularJS' $httpBackend syntax
+* [superagent-throttle](https://github.com/leviwheatcroft/superagent-throttle) - queues and intelligently throttles requests
+* [superagent-charset](https://github.com/magicdawn/superagent-charset) - add charset support for node's SuperAgent
+* [superagent-verbose-errors](https://github.com/jcoreio/superagent-verbose-errors) - include response body in error messages for failed requests
+* [superagent-declare](https://github.com/damoclark/superagent-declare) - A simple [declarative](https://en.wikipedia.org/wiki/Declarative_programming) API for SuperAgent
+* [superagent-node-http-timings](https://github.com/webuniverseio/superagent-node-http-timings) - measure http timings in node.js
+* [superagent-cheerio](https://github.com/mmmmmrob/superagent-cheerio) - add [cheerio](https://www.npmjs.com/package/cheerio) to your response content automatically. Adds `res.$` for HTML and XML response bodies.
+* [@certible/superagent-aws-sign](https://github.com/certible/superagent-aws-sign) - Sign AWS endpoint requests, it uses the aws4 to authenticate the SuperAgent requests
+
+Please prefix your plugin with `superagent-*` so that it can easily be found by others.
+
+For SuperAgent extensions such as couchdb and oauth visit the [wiki](https://github.com/ladjs/superagent/wiki).
+
+
+## Upgrading from previous versions
+
+Please see [GitHub releases page](https://github.com/ladjs/superagent/releases) for the current changelog.
+
+Our breaking changes are mostly in rarely used functionality and from stricter error handling.
+
+* [6.0 to 6.1](https://github.com/ladjs/superagent/releases/tag/v6.1.0)
+ * Browser behaviour changed to match Node when serializing `application/x-www-form-urlencoded`, using `arrayFormat: 'indices'` semantics of `qs` library. (See: )
+* [5.x to 6.x](https://github.com/ladjs/superagent/releases/tag/v6.0.0):
+ * Retry behavior is still opt-in, however we now have a more fine-grained list of status codes and error codes that we retry against (see updated docs)
+ * A specific issue with Content-Type matching not being case-insensitive is fixed
+ * Set is now required for IE 9, see [Required Browser Features](#required-browser-features) for more insight
+* [4.x to 5.x](https://github.com/ladjs/superagent/releases/tag/v5.0.0):
+ * We've implemented the build setup of [Lass](https://lass.js.org) to simplify our stack and linting
+ * Unminified browserified build size has been reduced from 48KB to 20KB (via `tinyify` and the latest version of Babel using `@babel/preset-env` and `.browserslistrc`)
+ * Linting support has been added using `caniuse-lite` and `eslint-plugin-compat`
+ * We can now target what versions of Node we wish to support more easily using `.babelrc`
+* [3.x to 4.x](https://github.com/ladjs/superagent/releases/tag/v4.0.0-alpha.1):
+ * Ensure you're running Node 6 or later. We've dropped support for Node 4.
+ * We've started using ES6 and for compatibility with Internet Explorer you may need to use Babel.
+ * We suggest migrating from `.end()` callbacks to `.then()` or `await`.
+* [2.x to 3.x](https://github.com/ladjs/superagent/releases/tag/v3.0.0):
+ * Ensure you're running Node 4 or later. We've dropped support for Node 0.x.
+ * Test code that calls `.send()` multiple times. Invalid calls to `.send()` will now throw instead of sending garbage.
+* [1.x to 2.x](https://github.com/ladjs/superagent/releases/tag/v2.0.0):
+ * If you use `.parse()` in the *browser* version, rename it to `.serialize()`.
+ * If you rely on `undefined` in query-string values being sent literally as the text "undefined", switch to checking for missing value instead. `?key=undefined` is now `?key` (without a value).
+ * If you use `.then()` in Internet Explorer, ensure that you have a polyfill that adds a global `Promise` object.
+* 0.x to 1.x:
+ * Instead of 1-argument callback `.end(function(res){})` use `.then(res => {})`.
+
+
+## Contributors
+
+| Name |
+| ------------------- |
+| **Kornel Lesiński** |
+| **Peter Lyons** |
+| **Hunter Loftis** |
+| **Nick Baugh** |
+
+
+## License
+
+[MIT](LICENSE) © TJ Holowaychuk
+
+
+##
+
+[npm]: https://www.npmjs.com/
+
+[yarn]: https://yarnpkg.com/
+
+[jsdelivr]: https://www.jsdelivr.com/
+
+[unpkg]: https://unpkg.com/
+
+[browserify]: https://github.com/browserify/browserify
+
+[webpack]: https://github.com/webpack/webpack
+
+[rollup]: https://github.com/rollup/rollup
diff --git a/Readme.md b/Readme.md
deleted file mode 100644
index c26ac794e..000000000
--- a/Readme.md
+++ /dev/null
@@ -1,110 +0,0 @@
-# SuperAgent [](https://travis-ci.org/visionmedia/superagent)
-
-[](https://saucelabs.com/u/shtylman-superagent)
-
-SuperAgent is a small progressive __client-side__ HTTP request library, and __Node.js__ module with the same API, sporting many high-level HTTP client features. View the [docs](http://visionmedia.github.com/superagent/).
-
-
-
-## Installation
-
-node:
-
-```
-$ npm install superagent
-```
-
-component:
-
-```
-$ component install visionmedia/superagent
-```
-
-Works with [browserify](https://github.com/substack/node-browserify) and should work with [webpack](https://github.com/visionmedia/superagent/wiki/Superagent-for-Webpack)
-
-```js
-request
- .post('/api/pet')
- .send({ name: 'Manny', species: 'cat' })
- .set('X-API-Key', 'foobar')
- .set('Accept', 'application/json')
- .end(function(err, res){
- // Calling the end function will send the request
- });
-```
-
-## Supported browsers
-
-Tested browsers:
-
-- Latest Android
-- Latest Firefox
-- Latest Chrome
-- IE9 through latest
-- Latest iPhone
-- Latest Safari
-
-Even though IE9 is supported, a polyfill `window.btoa` is needed to use basic auth.
-
-# Plugins
-
-Superagent is easily extended via plugins.
-
-```js
-var nocache = require('no-cache');
-var request = require('superagent');
-var prefix = require('superagent-prefix')('/static');
-
-prefix(request); // Prefixes *all* requests
-
-request
-.get('/some-url')
-.use(nocache) // Prevents caching of *only* this request
-.end(function(err, res){
- // Do something
-});
-```
-
-Existing plugins:
- * [superagent-no-cache](https://github.com/johntron/superagent-no-cache) - prevents caching by including Cache-Control header
- * [superagent-prefix](https://github.com/johntron/superagent-prefix) - prefixes absolute URLs (useful in test environment)
- * [superagent-mock](https://github.com/M6Web/superagent-mock) - simulate HTTP calls by returning data fixtures based on the requested URL
-
-Please prefix your plugin with `superagent-*` so that it can easily be found by others.
-
-For superagent extensions such as couchdb and oauth visit the [wiki](https://github.com/visionmedia/superagent/wiki).
-
-## Running node tests
-
-Install dependencies:
-
-```shell
-$ npm install
-```
-Run em!
-
-```shell
-$ make test
-```
-
-## Running browser tests
-
-Install dependencies:
-
-```shell
-$ npm install
-```
-
-Start the test runner:
-
-```shell
-$ make test-browser-local
-```
-
-Visit `http://localhost:4000/__zuul` in your browser.
-
-Edit tests and refresh your browser. You do not have to restart the test runner.
-
-## License
-
-MIT
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..bff6e78ce
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,6 @@
+# Security Policy
+
+
+## Reporting a Vulnerability
+
+Please report security issues to `niftylettuce@gmail.com`
diff --git a/ci/remove-deps-4-old-node.js b/ci/remove-deps-4-old-node.js
new file mode 100644
index 000000000..4cf9deefe
--- /dev/null
+++ b/ci/remove-deps-4-old-node.js
@@ -0,0 +1,29 @@
+const fs = require('fs');
+const path = require('path');
+const package = require('../package.json');
+
+const UNSUPPORT_DEPS_4_OLD = new Set([
+ '@commitlint/cli',
+ '@commitlint/config-conventional',
+ 'eslint',
+ 'eslint-config-xo-lass',
+ 'eslint-plugin-compat',
+ 'eslint-plugin-node',
+ 'husky',
+ 'lint-staged',
+ 'marked',
+ 'remark-cli',
+ 'remark-preset-github',
+ 'xo'
+]);
+
+for (const item in package.devDependencies) {
+ if (UNSUPPORT_DEPS_4_OLD.has(item)) {
+ package.devDependencies[item] = undefined;
+ }
+}
+
+fs.writeFileSync(
+ path.join(__dirname, '../package.json'),
+ JSON.stringify(package, null, 2)
+);
diff --git a/component.json b/component.json
deleted file mode 100644
index e5ec7d42b..000000000
--- a/component.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "name": "superagent",
- "repo": "visionmedia/superagent",
- "description": "awesome http requests",
- "version": "1.2.0",
- "keywords": [
- "http",
- "ajax",
- "request",
- "agent"
- ],
- "scripts": [
- "lib/client.js"
- ],
- "main": "lib/client.js",
- "dependencies": {
- "component/emitter": "*",
- "component/reduce": "*"
- },
- "license": "MIT"
-}
diff --git a/docs/head.html b/docs/head.html
new file mode 100644
index 000000000..8a2d2d2a7
--- /dev/null
+++ b/docs/head.html
@@ -0,0 +1,11 @@
+
+
+
+
+ SuperAgent — elegant API for AJAX in Node and browsers
+
+
+
+
+
+
diff --git a/docs/images/bg.png b/docs/images/bg.png
new file mode 100644
index 000000000..ca3d2679c
Binary files /dev/null and b/docs/images/bg.png differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 000000000..b148cee8e
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,881 @@
+
+# SuperAgent
+
+SuperAgent is light-weight progressive ajax API crafted for flexibility, readability, and a low learning curve after being frustrated with many of the existing request APIs. It also works with Node.js!
+
+```javascript
+ request
+ .post('/api/pet')
+ .send({ name: 'Manny', species: 'cat' })
+ .set('X-API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(res => {
+ alert('yay got ' + JSON.stringify(res.body));
+ });
+```
+
+## Test documentation
+
+[**中文文档**](docs/zh_CN/index.html)
+
+The following [test documentation](docs/test.html) was generated with [Mocha's](https://mochajs.org/) "doc" reporter, and directly reflects the test suite. This provides an additional source of documentation.
+
+## Request basics
+
+A request can be initiated by invoking the appropriate method on the `request` object, then calling `.then()` (or `.end()` [or `await`](#promise-and-generator-support)) to send the request. For example a simple __GET__ request:
+
+```javascript
+ request
+ .get('/search')
+ .then(res => {
+ // res.body, res.headers, res.status
+ })
+ .catch(err => {
+ // err.message, err.response
+ });
+```
+
+HTTP method may also be passed as a string:
+
+```javascript
+ request('GET', '/search').then(success, failure);
+```
+
+Old-style callbacks are also supported, but not recommended. *Instead of* `.then()` you can call `.end()`:
+
+```javascript
+ request('GET', '/search').end(function(err, res){
+ if (res.ok) {}
+ });
+```
+
+Absolute URLs can be used. In web browsers absolute URLs work only if the server implements [CORS](#cors).
+
+```javascript
+ request
+ .get('https://example.com/search')
+ .then(res => {
+
+ });
+```
+
+The __Node__ client supports making requests to [Unix Domain Sockets](https://en.wikipedia.org/wiki/Unix_domain_socket):
+
+```javascript
+ // pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
+ // Use `%2F` as `/` in SOCKET_PATH
+ try {
+ const res = await request
+ .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search');
+ // res.body, res.headers, res.status
+ } catch(err) {
+ // err.message, err.response
+ }
+```
+
+__DELETE__, __HEAD__, __PATCH__, __POST__, and __PUT__ requests can also be used, simply change the method name:
+
+```javascript
+ request
+ .head('/favicon.ico')
+ .then(res => {
+
+ });
+```
+
+__DELETE__ can be also called as `.del()` for compatibility with old IE where `delete` is a reserved word.
+
+The HTTP method defaults to __GET__, so if you wish, the following is valid:
+
+```javascript
+ request('/search', (err, res) => {
+
+ });
+```
+
+## Using HTTP/2
+
+To make a request using HTTP/2 protocol only (with no HTTP/1.x fallback), use the `.http2()` method.
+
+```javascript
+ const request = require('superagent');
+ const res = await request
+ .get('https://example.com/h2')
+ .http2();
+```
+
+## Setting header fields
+
+Setting header fields is simple, invoke `.set()` with a field name and value:
+
+```javascript
+ request
+ .get('/search')
+ .set('API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(callback);
+```
+
+You may also pass an object to set several fields in a single call:
+
+```javascript
+ request
+ .get('/search')
+ .set({ 'API-Key': 'foobar', Accept: 'application/json' })
+ .then(callback);
+```
+
+## `GET` requests
+
+The `.query()` method accepts objects, which when used with the __GET__ method will form a query-string. The following will produce the path `/search?query=Manny&range=1..5&order=desc`.
+
+```javascript
+ request
+ .get('/search')
+ .query({ query: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .then(res => {
+
+ });
+```
+
+Or as a single object:
+
+```javascript
+ request
+ .get('/search')
+ .query({ query: 'Manny', range: '1..5', order: 'desc' })
+ .then(res => {
+
+ });
+```
+
+The `.query()` method accepts strings as well:
+
+```javascript
+ request
+ .get('/querystring')
+ .query('search=Manny&range=1..5')
+ .then(res => {
+
+ });
+```
+
+Or joined:
+
+```javascript
+ request
+ .get('/querystring')
+ .query('search=Manny')
+ .query('range=1..5')
+ .then(res => {
+
+ });
+```
+
+## `HEAD` requests
+
+You can also use the `.query()` method for HEAD requests. The following will produce the path `/users?email=joe@smith.com`.
+
+```javascript
+ request
+ .head('/users')
+ .query({ email: 'joe@smith.com' })
+ .then(res => {
+
+ });
+```
+
+## `POST` / `PUT` requests
+
+A typical JSON __POST__ request might look a little like the following, where we set the Content-Type header field appropriately, and "write" some data, in this case just a JSON string.
+
+```javascript
+ request.post('/user')
+ .set('Content-Type', 'application/json')
+ .send('{"name":"tj","pet":"tobi"}')
+ .then(callback)
+ .catch(errorCallback)
+```
+
+Since JSON is undoubtedly the most common, it's the _default_! The following example is equivalent to the previous.
+
+```javascript
+ request.post('/user')
+ .send({ name: 'tj', pet: 'tobi' })
+ .then(callback, errorCallback)
+```
+
+Or using multiple `.send()` calls:
+
+```javascript
+ request.post('/user')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+```
+
+By default sending strings will set the `Content-Type` to `application/x-www-form-urlencoded`,
+ multiple calls will be concatenated with `&`, here resulting in `name=tj&pet=tobi`:
+
+```javascript
+ request.post('/user')
+ .send('name=tj')
+ .send('pet=tobi')
+ .then(callback, errorCallback);
+```
+
+SuperAgent formats are extensible, however by default "json" and "form" are supported. To send the data as `application/x-www-form-urlencoded` simply invoke `.type()` with "form", where the default is "json". This request will __POST__ the body "name=tj&pet=tobi".
+
+```javascript
+ request.post('/user')
+ .type('form')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+```
+
+Sending a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData) object is also supported. The following example will __POST__ the content of the HTML form identified by id="myForm":
+
+```javascript
+ request.post('/user')
+ .send(new FormData(document.getElementById('myForm')))
+ .then(callback, errorCallback)
+```
+
+## Setting the `Content-Type`
+
+The obvious solution is to use the `.set()` method:
+
+```javascript
+ request.post('/user')
+ .set('Content-Type', 'application/json')
+```
+
+As a short-hand the `.type()` method is also available, accepting
+the canonicalized MIME type name complete with type/subtype, or
+simply the extension name such as "xml", "json", "png", etc:
+
+```javascript
+ request.post('/user')
+ .type('application/json')
+
+ request.post('/user')
+ .type('json')
+
+ request.post('/user')
+ .type('png')
+```
+
+## Serializing request body
+
+SuperAgent will automatically serialize JSON and forms.
+You can setup automatic serialization for other types as well:
+
+```js
+request.serialize['application/xml'] = function (obj) {
+ return 'string generated from obj';
+};
+
+// going forward, all requests with a Content-type of
+// 'application/xml' will be automatically serialized
+```
+If you want to send the payload in a custom format, you can replace
+the built-in serialization with the `.serialize()` method on a per-request basis:
+
+```js
+request
+ .post('/user')
+ .send({foo: 'bar'})
+ .serialize(obj => {
+ return 'string generated from obj';
+ });
+```
+## Retrying requests
+
+When given the `.retry()` method, SuperAgent will automatically retry requests, if they fail in a way that is transient or could be due to a flaky Internet connection.
+
+This method has two optional arguments: number of retries (default 1) and a callback. It calls `callback(err, res)` before each retry. The callback may return `true`/`false` to control whether the request should be retried (but the maximum number of retries is always applied).
+
+```javascript
+ request
+ .get('https://example.com/search')
+ .retry(2) // or:
+ .retry(2, callback)
+ .then(finished);
+ .catch(failed);
+```
+
+Use `.retry()` only with requests that are *idempotent* (i.e. multiple requests reaching the server won't cause undesirable side effects like duplicate purchases).
+
+All request methods are tried by default (which means if you do not want POST requests to be retried, you will need to pass a custom retry callback).
+
+By default the following status codes are retried:
+
+* `408`
+* `413`
+* `429`
+* `500`
+* `502`
+* `503`
+* `504`
+* `521`
+* `522`
+* `524`
+
+By default the following error codes are retried:
+
+* `'ETIMEDOUT'`
+* `'ECONNRESET'`
+* `'EADDRINUSE'`
+* `'ECONNREFUSED'`
+* `'EPIPE'`
+* `'ENOTFOUND'`
+* `'ENETUNREACH'`
+* `'EAI_AGAIN'`
+
+## Setting Accept
+
+In a similar fashion to the `.type()` method it is also possible to set the `Accept` header via the short hand method `.accept()`. Which references `request.types` as well allowing you to specify either the full canonicalized MIME type name as `type/subtype`, or the extension suffix form as "xml", "json", "png", etc. for convenience:
+
+```javascript
+ request.get('/user')
+ .accept('application/json')
+
+ request.get('/user')
+ .accept('json')
+
+ request.post('/user')
+ .accept('png')
+```
+
+### Facebook and Accept JSON
+
+If you are calling Facebook's API, be sure to send an `Accept: application/json` header in your request. If you don't do this, Facebook will respond with `Content-Type: text/javascript; charset=UTF-8`, which SuperAgent will not parse and thus `res.body` will be undefined. You can do this with either `req.accept('json')` or `req.set('Accept', 'application/json')`. See [issue 1078](https://github.com/ladjs/superagent/issues/1078) for details.
+
+## Query strings
+
+ `req.query(obj)` is a method which may be used to build up a query-string. For example populating `?format=json&dest=/login` on a __POST__:
+
+```javascript
+ request
+ .post('/')
+ .query({ format: 'json' })
+ .query({ dest: '/login' })
+ .send({ post: 'data', here: 'wahoo' })
+ .then(callback);
+```
+
+By default the query string is not assembled in any particular order. An asciibetically-sorted query string can be enabled with `req.sortQuery()`. You may also provide a custom sorting comparison function with `req.sortQuery(myComparisonFn)`. The comparison function should take 2 arguments and return a negative/zero/positive integer.
+
+```js
+ // default order
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery()
+ .then(callback)
+
+ // customized sort function
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery((a, b) => a.length - b.length)
+ .then(callback)
+```
+
+## TLS options
+
+In Node.js SuperAgent supports methods to configure HTTPS requests:
+
+- `.ca()`: Set the CA certificate(s) to trust
+- `.cert()`: Set the client certificate chain(s)
+- `.key()`: Set the client private key(s)
+- `.pfx()`: Set the client PFX or PKCS12 encoded private key and certificate chain
+- `.disableTLSCerts()`: Does not reject expired or invalid TLS certs. Sets internally `rejectUnauthorized=true`. *Be warned, this method allows MITM attacks.*
+
+For more information, see Node.js [https.request docs](https://nodejs.org/api/https.html#https_https_request_options_callback).
+
+```js
+var key = fs.readFileSync('key.pem'),
+ cert = fs.readFileSync('cert.pem');
+
+request
+ .post('/client-auth')
+ .key(key)
+ .cert(cert)
+ .then(callback);
+```
+
+```js
+var ca = fs.readFileSync('ca.cert.pem');
+
+request
+ .post('https://localhost/private-ca-server')
+ .ca(ca)
+ .then(res => {});
+```
+
+## Parsing response bodies
+
+SuperAgent will parse known response-body data for you,
+currently supporting `application/x-www-form-urlencoded`,
+`application/json`, and `multipart/form-data`. You can setup
+automatic parsing for other response-body data as well:
+
+```js
+//browser
+request.parse['application/xml'] = function (str) {
+ return {'object': 'parsed from str'};
+};
+
+//node
+request.parse['application/xml'] = function (res, cb) {
+ //parse response text and set res.body here
+
+ cb(null, res);
+};
+
+//going forward, responses of type 'application/xml'
+//will be parsed automatically
+```
+
+You can set a custom parser (that takes precedence over built-in parsers) with the `.buffer(true).parse(fn)` method. If response buffering is not enabled (`.buffer(false)`) then the `response` event will be emitted without waiting for the body parser to finish, so `response.body` won't be available.
+
+### JSON / Urlencoded
+
+The property `res.body` is the parsed object, for example if a request responded with the JSON string '{"user":{"name":"tobi"}}', `res.body.user.name` would be "tobi". Likewise the x-www-form-urlencoded value of "user[name]=tobi" would yield the same result. Only one level of nesting is supported. If you need more complex data, send JSON instead.
+
+Arrays are sent by repeating the key. `.send({color: ['red','blue']})` sends `color=red&color=blue`. If you want the array keys to contain `[]` in their name, you must add it yourself, as SuperAgent doesn't add it automatically.
+
+### Multipart
+
+The Node client supports _multipart/form-data_ via the [Formidable](https://github.com/felixge/node-formidable) module. When parsing multipart responses, the object `res.files` is also available to you. Suppose for example a request responds with the following multipart body:
+
+ --whoop
+ Content-Disposition: attachment; name="image"; filename="tobi.png"
+ Content-Type: image/png
+
+ ... data here ...
+ --whoop
+ Content-Disposition: form-data; name="name"
+ Content-Type: text/plain
+
+ Tobi
+ --whoop--
+
+You would have the values `res.body.name` provided as "Tobi", and `res.files.image` as a `File` object containing the path on disk, filename, and other properties.
+
+### Binary
+
+In browsers, you may use `.responseType('blob')` to request handling of binary response bodies. This API is unnecessary when running in node.js. The supported argument values for this method are
+
+- `'blob'` passed through to the XmlHTTPRequest `responseType` property
+- `'arraybuffer'` passed through to the XmlHTTPRequest `responseType` property
+
+```js
+req.get('/binary.data')
+ .responseType('blob')
+ .then(res => {
+ // res.body will be a browser native Blob type here
+ });
+```
+
+For more information, see the Mozilla Developer Network [xhr.responseType docs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType).
+
+## Response properties
+
+Many helpful flags and properties are set on the `Response` object, ranging from the response text, parsed response body, header fields, status flags and more.
+
+### Response text
+
+The `res.text` property contains the unparsed response body string. This property is always present for the client API, and only when the mime type matches "text/*", "*/json", or "x-www-form-urlencoded" by default for node. The reasoning is to conserve memory, as buffering text of large bodies such as multipart files or images is extremely inefficient. To force buffering see the "Buffering responses" section.
+
+### Response body
+
+Much like SuperAgent can auto-serialize request data, it can also automatically parse it. When a parser is defined for the Content-Type, it is parsed, which by default includes "application/json" and "application/x-www-form-urlencoded". The parsed object is then available via `res.body`.
+
+### Response header fields
+
+The `res.header` contains an object of parsed header fields, lowercasing field names much like node does. For example `res.header['content-length']`.
+
+### Response Content-Type
+
+The Content-Type response header is special-cased, providing `res.type`, which is void of the charset (if any). For example the Content-Type of "text/html; charset=utf8" will provide "text/html" as `res.type`, and the `res.charset` property would then contain "utf8".
+
+### Response status
+
+The response status flags help determine if the request was a success, among other useful information, making SuperAgent ideal for interacting with RESTful web services. These flags are currently defined as:
+
+```javascript
+ var type = status / 100 | 0;
+
+ // status / class
+ res.status = status;
+ res.statusType = type;
+
+ // basics
+ res.info = 1 == type;
+ res.ok = 2 == type;
+ res.clientError = 4 == type;
+ res.serverError = 5 == type;
+ res.error = 4 == type || 5 == type;
+
+ // sugar
+ res.accepted = 202 == status;
+ res.noContent = 204 == status || 1223 == status;
+ res.badRequest = 400 == status;
+ res.unauthorized = 401 == status;
+ res.notAcceptable = 406 == status;
+ res.notFound = 404 == status;
+ res.forbidden = 403 == status;
+```
+
+## Aborting requests
+
+To abort requests simply invoke the `req.abort()` method.
+
+## Timeouts
+
+Sometimes networks and servers get "stuck" and never respond after accepting a request. Set timeouts to avoid requests waiting forever.
+
+ * `req.timeout({deadline:ms})` or `req.timeout(ms)` (where `ms` is a number of milliseconds > 0) sets a deadline for the entire request (including all uploads, redirects, server processing time) to complete. If the response isn't fully downloaded within that time, the request will be aborted.
+
+ * `req.timeout({response:ms})` sets maximum time to wait for the first byte to arrive from the server, but it does not limit how long the entire download can take. Response timeout should be at least few seconds longer than just the time it takes the server to respond, because it also includes time to make DNS lookup, TCP/IP and TLS connections, and time to upload request data.
+
+You should use both `deadline` and `response` timeouts. This way you can use a short response timeout to detect unresponsive networks quickly, and a long deadline to give time for downloads on slow, but reliable, networks. Note that both of these timers limit how long *uploads* of attached files are allowed to take. Use long timeouts if you're uploading files.
+
+```javascript
+ request
+ .get('/big-file?network=slow')
+ .timeout({
+ response: 5000, // Wait 5 seconds for the server to start sending,
+ deadline: 60000, // but allow 1 minute for the file to finish loading.
+ })
+ .then(res => {
+ /* responded in time */
+ }, err => {
+ if (err.timeout) { /* timed out! */ } else { /* other error */ }
+ });
+```
+
+Timeout errors have a `.timeout` property.
+
+## Authentication
+
+In both Node and browsers auth available via the `.auth()` method:
+
+```javascript
+ request
+ .get('http://local')
+ .auth('tobi', 'learnboost')
+ .then(callback);
+```
+
+In the _Node_ client Basic auth can be in the URL as "user:pass":
+
+```javascript
+ request.get('http://tobi:learnboost@local').then(callback);
+```
+
+By default only `Basic` auth is used. In browser you can add `{type:'auto'}` to enable all methods built-in in the browser (Digest, NTLM, etc.):
+
+```javascript
+ request.auth('digest', 'secret', {type:'auto'})
+```
+
+The `auth` method also supports a `type` of `bearer`, to specify token-based authentication:
+
+```javascript
+ request.auth('my_token', { type: 'bearer' })
+```
+
+## Following redirects
+
+By default up to 5 redirects will be followed, however you may specify this with the `res.redirects(n)` method:
+
+```javascript
+ const response = await request.get('/some.png').redirects(2);
+```
+
+Redirects exceeding the limit are treated as errors. Use `.ok(res => res.status < 400)` to read them as successful responses.
+
+## Agents for global state
+
+### Saving cookies
+
+In Node SuperAgent does not save cookies by default, but you can use the `.agent()` method to create a copy of SuperAgent that saves cookies. Each copy has a separate cookie jar.
+
+```javascript
+ const agent = request.agent();
+ agent
+ .post('/login')
+ .then(() => {
+ return agent.get('/cookied-page');
+ });
+```
+
+In browsers cookies are managed automatically by the browser, so the `.agent()` does not isolate cookies.
+
+### Default options for multiple requests
+
+Regular request methods called on the agent will be used as defaults for all requests made by that agent.
+
+```javascript
+ const agent = request.agent()
+ .use(plugin)
+ .auth(shared);
+
+ await agent.get('/with-plugin-and-auth');
+ await agent.get('/also-with-plugin-and-auth');
+```
+
+The complete list of methods that the agent can use to set defaults is: `use`, `on`, `once`, `set`, `query`, `type`, `accept`, `auth`, `withCredentials`, `sortQuery`, `retry`, `ok`, `redirects`, `timeout`, `buffer`, `serialize`, `parse`, `ca`, `key`, `pfx`, `cert`.
+
+## Piping data
+
+The Node client allows you to pipe data to and from the request. Please note that `.pipe()` is used **instead of** `.end()`/`.then()` methods.
+
+For example piping a file's contents as the request:
+
+```javascript
+ const request = require('superagent');
+ const fs = require('fs');
+
+ const stream = fs.createReadStream('path/to/my.json');
+ const req = request.post('/somewhere');
+ req.type('json');
+ stream.pipe(req);
+```
+
+Note that when you pipe to a request, superagent sends the piped data with [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding), which isn't supported by all servers (for instance, Python WSGI servers).
+
+Or piping the response to a file:
+
+```javascript
+ const stream = fs.createWriteStream('path/to/my.json');
+ const req = request.get('/some.json');
+ req.pipe(stream);
+```
+
+ It's not possible to mix pipes and callbacks or promises. Note that you should **NOT** attempt to pipe the result of `.end()` or the `Response` object:
+
+```javascript
+ // Don't do either of these:
+ const stream = getAWritableStream();
+ const req = request
+ .get('/some.json')
+ // BAD: this pipes garbage to the stream and fails in unexpected ways
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
+ const req = request
+ .get('/some.json')
+ .end()
+ // BAD: this is also unsupported, .pipe calls .end for you.
+ .pipe(nope_its_too_late);
+```
+
+In a [future version](https://github.com/ladjs/superagent/issues/1188) of superagent, improper calls to `pipe()` will fail.
+
+## Multipart requests
+
+SuperAgent is also great for _building_ multipart requests for which it provides methods `.attach()` and `.field()`.
+
+When you use `.field()` or `.attach()` you can't use `.send()` and you *must not* set `Content-Type` (the correct type will be set for you).
+
+### Attaching files
+
+To send a file use `.attach(name, [file], [options])`. You can attach multiple files by calling `.attach` multiple times. The arguments are:
+
+ * `name` — field name in the form.
+ * `file` — either string with file path or `Blob`/`Buffer` object.
+ * `options` — (optional) either string with custom file name or `{filename: string}` object. In Node also `{contentType: 'mime/type'}` is supported. In browser create a `Blob` with an appropriate type instead.
+
+
+
+```javascript
+ request
+ .post('/upload')
+ .attach('image1', 'path/to/felix.jpeg')
+ .attach('image2', imageBuffer, 'luna.jpeg')
+ .field('caption', 'My cats')
+ .then(callback);
+```
+
+### Field values
+
+Much like form fields in HTML, you can set field values with `.field(name, value)` and `.field({name: value})`. Suppose you want to upload a few images with your name and email, your request might look something like this:
+
+```javascript
+ request
+ .post('/upload')
+ .field('user[name]', 'Tobi')
+ .field('user[email]', 'tobi@learnboost.com')
+ .field('friends[]', ['loki', 'jane'])
+ .attach('image', 'path/to/tobi.png')
+ .then(callback);
+```
+
+## Compression
+
+The node client supports compressed responses, best of all, you don't have to do anything! It just works.
+
+## Buffering responses
+
+To force buffering of response bodies as `res.text` you may invoke `req.buffer()`. To undo the default of buffering for text responses such as "text/plain", "text/html" etc you may invoke `req.buffer(false)`.
+
+When buffered the `res.buffered` flag is provided, you may use this to handle both buffered and unbuffered responses in the same callback.
+
+## CORS
+
+For security reasons, browsers will block cross-origin requests unless the server opts-in using CORS headers. Browsers will also make extra __OPTIONS__ requests to check what HTTP headers and methods are allowed by the server. [Read more about CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS).
+
+The `.withCredentials()` method enables the ability to send cookies from the origin, however only when `Access-Control-Allow-Origin` is _not_ a wildcard ("*"), and `Access-Control-Allow-Credentials` is "true".
+
+```javascript
+ request
+ .get('https://api.example.com:4001/')
+ .withCredentials()
+ .then(res => {
+ assert.equal(200, res.status);
+ assert.equal('tobi', res.text);
+ })
+```
+
+## Error handling
+
+Your callback function will always be passed two arguments: error and response. If no error occurred, the first argument will be null:
+
+```javascript
+ request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .then(res => {
+
+ });
+```
+
+An "error" event is also emitted, with you can listen for:
+
+```javascript
+ request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .on('error', handle)
+ .then(res => {
+
+ });
+```
+
+Note that **superagent considers 4xx and 5xx responses (as well as unhandled 3xx responses) errors by default**. For example, if you get a `304 Not modified`, `403 Forbidden` or `500 Internal server error` response, this status information will be available via `err.status`. Errors from such responses also contain an `err.response` field with all of the properties mentioned in "[Response properties](#response-properties)". The library behaves in this way to handle the common case of wanting success responses and treating HTTP error status codes as errors while still allowing for custom logic around specific error conditions.
+
+Network failures, timeouts, and other errors that produce no response will contain no `err.status` or `err.response` fields.
+
+If you wish to handle 404 or other HTTP error responses, you can query the `err.status` property. When an HTTP error occurs (4xx or 5xx response) the `res.error` property is an `Error` object, this allows you to perform checks such as:
+
+```javascript
+ if (err && err.status === 404) {
+ alert('oh no ' + res.body.message);
+ }
+ else if (err) {
+ // all other error types we handle generically
+ }
+```
+
+Alternatively, you can use the `.ok(callback)` method to decide whether a response is an error or not. The callback to the `ok` function gets a response and returns `true` if the response should be interpreted as success.
+
+```javascript
+ request.get('/404')
+ .ok(res => res.status < 500)
+ .then(response => {
+ // reads 404 page as a successful response
+ })
+```
+
+## Progress tracking
+
+SuperAgent fires `progress` events on upload and download of large files.
+
+```javascript
+ request.post(url)
+ .attach('field_name', file)
+ .on('progress', event => {
+ /* the event is:
+ {
+ direction: "upload" or "download"
+ percent: 0 to 100 // may be missing if file size is unknown
+ total: // total file size, may be missing
+ loaded: // bytes downloaded or uploaded so far
+ } */
+ })
+ .then()
+```
+
+## Testing on localhost
+
+### Forcing specific connection IP address
+
+In Node.js it's possible to ignore DNS resolution and direct all requests to a specific IP address using `.connect()` method. For example, this request will go to localhost instead of `example.com`:
+
+```javascript
+ const res = await request.get("http://example.com").connect("127.0.0.1");
+```
+
+Because the request may be redirected, it's possible to specify multiple hostnames and multiple IPs, as well as a special `*` as the fallback (note: other wildcards are not supported). The requests will keep their `Host` header with the original value. `.connect(undefined)` turns off the feature.
+
+```javascript
+ const res = await request.get("http://redir.example.com:555")
+ .connect({
+ "redir.example.com": "127.0.0.1", // redir.example.com:555 will use 127.0.0.1:555
+ "www.example.com": false, // don't override this one; use DNS as normal
+ "mapped.example.com": { host: "127.0.0.1", port: 8080}, // mapped.example.com:* will use 127.0.0.1:8080
+ "*": "proxy.example.com", // all other requests will go to this host
+ });
+```
+
+### Ignoring broken/insecure HTTPS on localhost
+
+In Node.js, when HTTPS is misconfigured and insecure (e.g. using self-signed certificate *without* specifying own `.ca()`), it's still possible to permit requests to `localhost` by calling `.trustLocalhost()`:
+
+```javascript
+ const res = await request.get("https://localhost").trustLocalhost()
+```
+
+Together with `.connect("127.0.0.1")` this may be used to force HTTPS requests to any domain to be re-routed to `localhost` instead.
+
+It's generally safe to ignore broken HTTPS on `localhost`, because the loopback interface is not exposed to untrusted networks. Trusting `localhost` may become the default in the future. Use `.trustLocalhost(false)` to force check of `127.0.0.1`'s authenticity.
+
+We intentionally don't support disabling of HTTPS security when making requests to any other IP, because such options end up abused as a quick "fix" for HTTPS problems. You can get free HTTPS certificates from [Let's Encrypt](https://certbot.eff.org) or set your own CA (`.ca(ca_public_pem)`) to make your self-signed certificates trusted.
+
+## Promise and Generator support
+
+SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and the `async`/`await` syntax.
+
+```javascript
+ const res = await request.get(url);
+```
+
+If you're using promises, **do not** call `.end()` or `.pipe()`. Any use of `.then()` or `await` disables all other ways of using the request.
+
+Libraries like [co](https://github.com/tj/co) or a web framework like [koa](https://github.com/koajs/koa) can `yield` on any SuperAgent method:
+
+```javascript
+ const req = request
+ .get('http://local')
+ .auth('tobi', 'learnboost');
+ const res = yield req;
+```
+
+Note that SuperAgent expects the global `Promise` object to be present. You'll need to use v7 and a polyfill to use promises in Internet Explorer or Node.js 0.10.
+
+We have dropped support in v8 for IE. You must add a polyfill for WeakRef and BigInt if you wish to support Opera 85, iOS Safari 12.2-12.5, for example using
:
+
+```html
+
+```
+
+## Browser and node versions
+
+SuperAgent has two implementations: one for web browsers (using XHR) and one for Node.JS (using core http module). By default Browserify and WebPack will pick the browser version.
+
+If want to use WebPack to compile code for Node.JS, you *must* specify [node target](https://webpack.github.io/docs/configuration.html#target) in its configuration.
diff --git a/docs/ko_KR/index.html b/docs/ko_KR/index.html
new file mode 100644
index 000000000..13b9262c8
--- /dev/null
+++ b/docs/ko_KR/index.html
@@ -0,0 +1,1181 @@
+
+
+
+
+
SuperAgent — elegant API for AJAX in Node and browsers
+
+
+
+
+
+
+
SuperAgent
+
+ SuperAgent는 기존의 복잡한 요청 API에 대한 불만에서 출발해 유연성,
+ 가독성, 그리고 낮은 학습 난이도를 목표로 설계된 경량 Ajax API입니다.
+ 또한 Node.js 환경에서도 동작합니다!
+
+
request
+ .post('/api/pet')
+ .send({ name: 'Manny', species: 'cat' })
+ .set('X-API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(res => {
+ alert('yay got ' + JSON.stringify(res.body));
+ });
+
+
테스트 문서
+
+ English
+
+
+ 다음의 테스트 문서 는
+ Mocha's "doc" 리포터를
+ 사용해 생성되었으며, 실제 테스트 스위트를 직접 반영합니다. 이 문서는
+ 추가적인 참고 자료로 활용할 수 있습니다.
+
+
기본 요청
+
+ 요청은 request 객체에서 적절한 메서드를 호출하여 시작되며,
+ 그 다음 .then() 또는 .end() 혹은
+ await 를 사용해 요청을 전송할 수 있습니다. 예를 들어, 간단한
+ GET 요청은 다음과 같습니다.
+
+
request
+ .get('/search')
+ .then(res => {
+ // res.body, res.headers, res.status
+ })
+ .catch(err => {
+ // err.message, err.response
+ });
+
+
HTTP 메서드는 문자열로도 전달할 수 있습니다.
+
request('GET', '/search').then(success, failure);
+
+
+ 예전 방식의 콜백도 지원되지만, 권장되지는 않습니다.
+ .then() 대신 .end()를 호출하여 요청을 전송할
+ 수 있습니다.
+
+
request('GET', '/search').end(function(err, res){
+ if (res.ok) {}
+ });
+
+
+ 절대 URL을 사용할 수 있습니다. 단, 웹 브라우저에서는 서버가
+ CORS 를 구현한 경우에만 절대 URL이 정상적으로
+ 작동합니다.
+
+
request
+ .get('https://example.com/search')
+ .then(res => {
+
+ });
+
+
+ Node 클라이언트는
+ 유닉스 도메인 소켓 을 통한 요청을 지원합니다.
+
+
// pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
+ // Use `%2F` as `/` in SOCKET_PATH
+ try {
+ const res = await request
+ .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search');
+ // res.body, res.headers, res.status
+ } catch(err) {
+ // err.message, err.response
+ }
+
+
+ DELETE , HEAD , PATCH ,
+ POST , and PUT 요청도 사용할 수 있으며,
+ 다음 예시에서 메서드 이름만 변경하면 됩니다.
+
+
request
+ .head('/favicon.ico')
+ .then(res => {
+
+ });
+
+
+ DELETE 는 delete가 예약어였던 구형 IE와의
+ 호환성을 위해 .del() 메서드로도 호출할 수 있습니다.
+
+
+ HTTP 메서드의 기본값은 GET 이므로, 다음과 같이 작성해도
+ 유효합니다.
+
+
request('/search', (err, res) => {
+
+ });
+
+
HTTP/2 사용하기
+
+ HTTP/1.x 폴백 없이 HTTP/2 프로토콜만 사용하려면
+ .http2() 메서드를 호출하여 요청을 전송할 수 있습니다.
+
+
const request = require('superagent');
+ const res = await request
+ .get('https://example.com/h2')
+ .http2();
+
+
+
+ 헤더 필드 설정은 간단합니다. 필드 이름과 값을
+ .set() 메서드에 전달하면 됩니다.
+
+
request
+ .get('/search')
+ .set('API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(callback);
+
+
여러 개의 헤더 필드를 한 번에 설정하려면 객체를 전달하면 됩니다.
+
request
+ .get('/search')
+ .set({ 'API-Key': 'foobar', Accept: 'application/json' })
+ .then(callback);
+
+
GET 요청
+
+ .query() 메서드는 객체를 인자로 받아
+ GET 요청 시 쿼리 문자열을 자동으로 생성합니다. 예를
+ 들어 다음 코드는
+ /search?query=Manny&range=1..5&order=desc 경로를
+ 생성합니다.
+
+
request
+ .get('/search')
+ .query({ query: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .then(res => {
+
+ });
+
+
또는 하나의 객체로 설정할 수 있습니다.
+
request
+ .get('/search')
+ .query({ query: 'Manny', range: '1..5', order: 'desc' })
+ .then(res => {
+
+ });
+
+
.query() 메서드는 문자열도 받습니다.
+
request
+ .get('/querystring')
+ .query('search=Manny&range=1..5')
+ .then(res => {
+
+ });
+
+
조인할 수도 있습니다.
+
request
+ .get('/querystring')
+ .query('search=Manny')
+ .query('range=1..5')
+ .then(res => {
+
+ });
+
+
HEAD 요청하기
+
+ HEAD 요청에서도 .query() 메서드를 사용할 수 있습니다. 예를
+ 들어 다음 코드는 /users?email=joe@smith.com 경로를
+ 생성합니다.
+
+
request
+ .head('/users')
+ .query({ email: 'joe@smith.com' })
+ .then(res => {
+
+ });
+
+
POST / PUT 요청
+
+ 전형적인 JSON POST 요청은 Content-Type 헤더를 적절히
+ 설정하고, 데이터를 JSON 형식으로 전송하는 방식입니다. 예를 들어 다음과
+ 같은 코드가 이에 해당합니다.
+
+
request.post('/user')
+ .set('Content-Type', 'application/json')
+ .send('{"name":"tj","pet":"tobi"}')
+ .then(callback)
+ .catch(errorCallback)
+
+
+ JSON은 가장 일반적으로 사용되므로 기본값으로 설정되어 있습니다. 다음
+ 예제는 앞선 예제와 동일한 동작을 수행합니다.
+
+
request.post('/user')
+ .send({ name: 'tj', pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
또는 .send() 여러 번 호출할 수 있습니다.
+
request.post('/user')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
+ 기본적으로 문자열을 전송하면 Content-Type이
+ application/x-www-form-urlencoded로 자동 설정됩니다. 여러
+ 번 .send()를 호출하면 각 문자열이 &로
+ 연결되어 최종적으로 name=tj&pet=tobi와 같은 결과가
+ 생성됩니다.
+
+
request.post('/user')
+ .send('name=tj')
+ .send('pet=tobi')
+ .then(callback, errorCallback);
+
+
+ SuperAgent는 다양한 형식으로 확장 가능하지만, 기본적으로
+ "json"과 "form" 형식을 지원합니다.
+ application/x-www-form-urlencoded 형식으로 데이터를
+ 전송하려면 .type('form')을 호출하면 됩니다. 기본 형식은
+ "json"입니다. 다음 요청은 본문에
+ "name=tj&pet=tobi"를 포함하여 POST 됩니다.
+
+
request.post('/user')
+ .type('form')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
+ FormData
+ 객체를 사용하는 것도 지원됩니다. 다음 예제는 id="myForm"인
+ HTML 폼의 내용을 POST 방식으로 전송합니다.
+
+
request.post('/user')
+ .send(new FormData(document.getElementById('myForm')))
+ .then(callback, errorCallback)
+
+
Content-Type 설정하기
+
+ 가장 명확한 해결책은 .set() 메서드를 사용하는 것입니다.
+
+
request.post('/user')
+ .set('Content-Type', 'application/json')
+
+
+ 간단하게 .type() 메서드를 사용할 수 있으며, 표준화된 MIME
+ 타입(type/subtype)을 직접 지정하거나 "xml",
+ "json", "png" 등과 같은 확장자 이름만으로도 설정할
+ 수 있습니다.
+
+
request.post('/user')
+ .type('application/json')
+
+ request.post('/user')
+ .type('json')
+
+ request.post('/user')
+ .type('png')
+
+
요청 본문 직렬화하기
+
+ SuperAgent는 기본적으로 JSON과 폼 데이터를 자동으로 직렬화합니다. 또한
+ 다른 콘텐츠 유형에 대해서도 자동 직렬화를 설정할 수 있습니다.
+
+
request.serialize['application/xml'] = function (obj) {
+ return 'string generated from obj';
+};
+
+// 'application/xml' Content-type을 가진 모든 요청은
+// 자동으로 직렬화 됩니다.
+
+
+ 사용자 정의 형식으로 페이로드를 전송하려면, 요청 단위로
+ .serialize()
+ 메서드를 사용해 SuperAgent의 기본 직렬화 방식을 교체할 수 있습니다.
+
+
request
+ .post('/user')
+ .send({foo: 'bar'})
+ .serialize(obj => {
+ return 'string generated from obj';
+ });
+
+
요청 재시도하기
+
+ .retry() 메서드를 사용하면, 일시적인 오류나 불안정한 인터넷
+ 연결로 인해 요청이 실패한 경우 SuperAgent가 자동으로 재시도합니다.
+
+
+ 이 메서드는 두 개의 선택적 인자를 받습니다. 재시도 횟수(기본값은 1)와
+ 콜백 함수입니다. 각 재시도 전에 callback(err, res)를
+ 호출합니다. 콜백 함수는 요청을 재시도할지 여부를 결정하기 위해
+ true 또는 false를 반환할 수 있습니다. 단, 최대
+ 재시도 횟수는 항상 적용됩니다.
+
+
request
+ .get('https://example.com/search')
+ .retry(2) // 혹은
+ .retry(2, callback)
+ .then(finished);
+ .catch(failed);
+
+
+ 멱등한 요청인 경우에만 .retry() 메서드를 사용하세요. 예를
+ 들어, 동일한 요청이 서버에 여러 번 도달하더라도 중복 구매와 같은
+ 바람직하지 않은 부작용이 발생하지 않아야 합니다.
+
+
+ 모든 요청 메서드는 기본적으로 재시도 대상에 포함됩니다. 따라서 POST
+ 요청을 재시도하지 않도록 하려면, 사용자 정의 재시도 콜백을 전달해야
+ 합니다.
+
+
기본적으로 다음과 같은 상태 코드는 자동으로 재시도됩니다.
+
+ 408
+ 413
+ 429
+ 500
+ 502
+ 503
+ 504
+ 521
+ 522
+ 524
+
+
기본적으로 다음과 같은 오류 코드가 자동으로 재시도됩니다.
+
+ 'ETIMEDOUT'
+ 'ECONNRESET'
+ 'EADDRINUSE'
+ 'ECONNREFUSED'
+ 'EPIPE'
+ 'ENOTFOUND'
+ 'ENETUNREACH'
+ 'EAI_AGAIN'
+
+
Accept 설정하기
+
+ .type() 메서드와 유사하게, .accept() 메서드를
+ 사용하면 Accept 헤더를 간편하게 설정할 수 있습니다. 이
+ 메서드는 request.types를 참조하며,
+ type/subtype 형식의 MIME 타입 전체 이름이나
+ "xml", "json", "png" 등의 확장자 형태로도
+ 지정할 수 있어 편리합니다.
+
+
request.get('/user')
+ .accept('application/json')
+
+ request.get('/user')
+ .accept('json')
+
+ request.post('/user')
+ .accept('png')
+
+
Facebook과 Accept JSON
+
+ Facebook API를 호출할 때는 반드시 요청 헤더에
+ Accept: application/json을 포함해야 합니다. 그렇지 않으면
+ Facebook은 Content-Type: text/javascript; charset=UTF-8으로
+ 응답하게 되며, SuperAgent는 이 형식을 파싱하지 못해
+ res.body가 undefined가 됩니다.
+ req.accept('json') 또는
+ req.set('Accept', 'application/json')을
+ 사용할 수 있습니다. 자세한 사항은
+ issue 1078
+ 에서 확인해보세요.
+
+
쿼리 문자열
+
+ req.query(obj)는 쿼리 문자열을 구성하는 데 사용되는
+ 메서드입니다. 예를 들어 POST 요청에서
+ ?format=json&dest=/login과 같은 쿼리 문자열을 추가할 수
+ 있습니다.
+
+
request
+ .post('/')
+ .query({ format: 'json' })
+ .query({ dest: '/login' })
+ .send({ post: 'data', here: 'wahoo' })
+ .then(callback);
+
+
+ 기본적으로 쿼리 문자열은 특정한 순서로 조립되지 않습니다. ASCII 순으로
+ 정렬된 쿼리 문자열을 사용하려면
+ req.sortQuery()를 호출하면 됩니다. 또한
+ req.sortQuery(myComparisonFn)을 통해 사용자 정의 정렬 비교
+ 함수를 전달할 수도 있습니다. 비교 함수는 두 개의 인자를 받아 음수, 0
+ 또는 양수를 반환해야 합니다.
+
+
// 기본 정렬 방식
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery()
+ .then(callback)
+
+ // 사용자 정의 정렬 함수
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery((a, b) => a.length - b.length)
+ .then(callback)
+
+
TLS 옵션
+
+ Node.js에서 SuperAgent는 HTTPS 요청을 구성할 수 있는 다양한 메서드를
+ 지원합니다.
+
+
+ .ca(): 신뢰할 CA 인증서를 설정합니다.
+ .cert(): 클라이언트 인증서 체인을 설정합니다.
+ .key(): 클라이언트의 개인 키를 설정합니다.
+
+ .pfx(): PKCS12 형식의 PFX 파일을 사용하여 클라이언트의
+ 개인 키와 인증서 체인을 설정합니다.
+
+
+ .disableTLSCerts(): 만료되었거나 유효하지 않은 TLS
+ 인증서를 거부하지 않도록 설정합니다. 내부적으로
+ rejectUnauthorized=true가 설정되며, 중간자 공격(MITM)에
+ 노출될 수 있으므로 주의가 필요합니다.
+
+
+
+ 더 자세한 내용은 Node.js
+ https.request 문서 에서 확인할 수 있습니다.
+
+
var key = fs.readFileSync('key.pem'),
+ cert = fs.readFileSync('cert.pem');
+
+request
+ .post('/client-auth')
+ .key(key)
+ .cert(cert)
+ .then(callback);
+
+
var ca = fs.readFileSync('ca.cert.pem');
+
+request
+ .post('https://localhost/private-ca-server')
+ .ca(ca)
+ .then(res => {});
+
+
응답 본문 파싱하기
+
+ SuperAgent는 응답 본문 데이터를 자동으로 파싱해줍니다. 현재
+ application/x-www-form-urlencoded,
+ application/json, multipart/form-data을
+ 지원합니다. 이외의 응답 본문 데이터에 대해서도 자동 파싱을 설정할 수
+ 있습니다.
+
+
// 브라우저
+request.parse['application/xml'] = function (str) {
+ return {'object': 'parsed from str'};
+};
+
+// node
+request.parse['application/xml'] = function (res, cb) {
+ //parse response text and set res.body here
+
+ cb(null, res);
+};
+
+// 앞으로 'application/xml' 유형의 반응은
+// 자동으로 파싱됩니다
+
+
+ .buffer(true).parse(fn) 메서드를 사용하면 내장된 파서보다
+ 우선적으로 적용되는 사용자 정의 파서를 설정할 수 있습니다.
+ .buffer(false)로 응답 버퍼링이 비활성화되어 있다면,
+ response 이벤트는 본문 파싱이 완료되기 전에 발생하므로
+ response.body를 사용할 수 없습니다.
+
+
JSON / Urlencoded
+
+ res.body 속성은 파싱된 객체를 나타냅니다. 예를 들어, 응답이
+ JSON 문자열
+ '{"user":{"name":"tobi"}}'를
+ 반환했다면, res.body.user.name은 "tobi" 값을 갖게
+ 됩니다. 마찬가지로 x-www-form-urlencoded 형식의
+ "user[name]=tobi"도 동일한 결과를 제공합니다. 단, 중첩은 한
+ 단계까지만 지원되므로 더 복잡한 구조의 데이터를 다루려면 JSON 형식을
+ 사용하는 것이 좋습니다.
+
+
+ 배열은 key를 반복해서 전달하는 방식으로 전송됩니다.
+ .send({color: ['red','blue']})는
+ color=red&color=blue로 변환되어 전송됩니다. 배열의
+ key에 []를 포함시키고 싶다면 SuperAgent는 이를 자동으로
+ 처리하지 않으므로, 직접 color[]와 같이 key 이름에 대괄호를
+ 추가해야 합니다.
+
+
다중 파트
+
+ Node 클라이언트는
+ Formidable
+ 모듈을 통해 multipart/form-data 를 지원합니다. 다중 파트 응답을
+ 파싱할 때 res.files 객체를 사용할 수 있으며, 이 객체에는
+ 업로드된 파일에 대한 정보가 포함됩니다. 예를 들어, 다음과 같은 multipart
+ 본문을 포함한 응답을 가정해볼 수 있습니다.
+
+
--whoop
+Content-Disposition: attachment; name="image"; filename="tobi.png"
+Content-Type: image/png
+
+... data here ...
+--whoop
+Content-Disposition: form-data; name="name"
+Content-Type: text/plain
+
+Tobi
+--whoop--
+
+
+ res.body.name은 "Tobi" 값을 가지고 있으며,
+ res.files.image는 디스크 경로, 파일 이름 및 기타 속성을
+ 포함한 File 객체입니다.
+
+
바이너리
+
+ 브라우저에서는 바이너리 응답 본문을 처리하기 위해
+ .responseType('blob')을 사용할 수 있습니다. 이
+ API는 Node.js 환경에서는 필요하지 않습니다. 이 메서드에서 지원되는 인자
+ 값은 다음과 같습니다.
+
+
+
+ 'blob'는 XMLHttpRequest의
+ responseType 속성에 그대로 전달됩니다.
+
+
+ 'arraybuffer'도 마찬가지로
+ responseType 속성에 전달됩니다.
+
+
+
req.get('/binary.data')
+ .responseType('blob')
+ .then(res => {
+ // 여기서 res.body는 브라우저 기본 Blob 타입입니다.
+ });
+
+
+ 더 자세한 내용은 Mozilla Developer Network의
+ XMLHttpRequest.responseType 문서 에서 확인할 수 있습니다.
+
+
응답 속성
+
+ Response 객체에는 응답 텍스트, 파싱된 응답 본문, 헤더 필드,
+ 상태 플래그 등 다양한 유용한 플래그와 속성이 설정되어 있습니다.
+
+
응답 문자
+
+ res.text 속성에는 파싱되지 않은 응답 본문 문자열이
+ 포함됩니다. 이 속성은 클라이언트 API에서는 항상 존재하며, Node
+ 환경에서는 MIME 타입이 "text/", " /json",
+ 또는 "x-www-form-urlencoded"와 일치할 경우에만 기본적으로
+ 제공됩니다. 이러한 제한은 대용량 multipart 파일이나 이미지 등의 본문을
+ 텍스트로 버퍼링하는 것이 매우 비효율적이기 때문에 메모리를 절약하기 위한
+ 목적입니다. 응답을 강제로 버퍼링하려면 "응답 버퍼링" 섹션을
+ 참조하세요.
+
+
응답 본문
+
+ SuperAgent는 요청 데이터를 자동으로 직렬화할 뿐만 아니라, 자동으로
+ 파싱할 수도 있습니다. Content-Type에 대해 파서가 정의되어 있는 경우,
+ 해당 타입에 맞게 응답이 파싱되며 기본적으로
+ "application/json"과
+ "application/x-www-form-urlencoded" 형식이 포함됩니다. 파싱된
+ 객체는 res.body를 통해 접근할 수 있습니다.
+
+
+
+ res.header는 파싱된 응답 헤더 필드를 담은 객체로, Node.js와
+ 마찬가지로 필드 이름을 소문자로 변환하여 저장합니다. 예를 들어,
+ res.header['content-length']와 같이 접근할 수
+ 있습니다.
+
+
응답 콘텐츠 타입
+
+ Content-Type 응답 헤더는 특별하게 처리되어 res.type 속성은
+ charset 정보를 제외한 콘텐츠 타입만을 제공합니다. 예를 들어
+ Content-Type이 "text/html; charset=utf8"인 경우,
+ res.type은 "text/html"을 반환하고,
+ res.charset 속성에는 "utf8"이 포함됩니다.
+
+
응답 상태
+
+ 응답 상태 플래그는 요청이 성공했는지 여부를 비롯한 다양한 유용한 정보를
+ 판단하는 데 도움을 줍니다. 이를 통해 SuperAgent는 RESTful 웹 서비스와
+ 효과적으로 상호작용할 수 있습니다. 현재 정의된 주요 플래그는 다음과
+ 같습니다.
+
+
var type = status / 100 | 0;
+
+ // 상태 / 클래스
+ res.status = status;
+ res.statusType = type;
+
+ // 기본
+ res.info = 1 == type;
+ res.ok = 2 == type;
+ res.clientError = 4 == type;
+ res.serverError = 5 == type;
+ res.error = 4 == type || 5 == type;
+
+ // 편의 기능
+ res.accepted = 202 == status;
+ res.noContent = 204 == status || 1223 == status;
+ res.badRequest = 400 == status;
+ res.unauthorized = 401 == status;
+ res.notAcceptable = 406 == status;
+ res.notFound = 404 == status;
+ res.forbidden = 403 == status;
+
+
요청 중단하기
+
+ 요청을 중단하려면 req.abort() 메서드를 호출하기만 하면
+ 됩니다.
+
+
타임아웃
+
+ 때때로 네트워크나 서버가 요청을 수신한 후 응답 없이 멈춰버리는 경우가
+ 있습니다. 이러한 무한 대기를 방지하려면 타임아웃을 설정해야 합니다.
+
+
+
+
+ req.timeout({deadline:ms}) 또는
+ req.timeout(ms)는 업로드, 리다이렉트, 서버 처리 시간을
+ 포함한 전체 요청이 완료되어야 하는 최종 시간 제한을 설정합니다.
+ ms는 0보다 큰 밀리초 단위의 숫자이며, 제한 시간 내에
+ 응답이 완료되지 않으면 요청은 중단됩니다.
+
+
+
+
+ req.timeout({response:ms})는 서버로부터 첫 번째
+ 바이트가 도착할 때까지의 최대 대기 시간을 설정합니다. 전체 다운로드
+ 소요 시간은 제한하지 않습니다. 응답 타임아웃은 DNS 조회, TCP/IP 및
+ TLS 연결, 요청 데이터 업로드 시간을 포함하므로, 서버의 실제 응답
+ 시간보다 몇 초 더 길게 설정하는 것이 좋습니다.
+
+
+
+
+ deadline과 response 타임아웃은 함께 사용하는
+ 것이 좋습니다. 짧은 응답 타임아웃은 응답하지 않는 네트워크를 빠르게
+ 감지하는 데 유용하고, 긴 데드라인은 느리지만 안정적인 네트워크 환경에서
+ 다운로드를 완료할 수 있도록 여유 시간을 제공합니다. 두 타이머 모두
+ 첨부된 파일 업로드에 허용되는 시간을 제한합니다. 파일을 업로드하는
+ 경우에는 충분히 긴 타임아웃을 설정하는 것이 좋습니다.
+
+
request
+ .get('/big-file?network=slow')
+ .timeout({
+ response: 5000, // 서버가 데이터를 보내기 시작할 때까지 최대 5초간 기다립니다.
+ deadline: 60000, // 파일 전체를 로드하는 데 최대 1분까지 허용합니다.
+ })
+ .then(res => {
+ /* 제시간 응답 수신 */
+ }, err => {
+ if (err.timeout) { /* 시간 초과! */ } else { /* 그 외 오류 */ }
+ });
+
+
타임아웃 오류에는 .timeout 속성이 포함되어 있습니다.
+
인증
+
+ Node와 브라우저 환경에서 .auth() 메서드를 사용하여 인증을
+ 수행할 수 있습니다.
+
+
request
+ .get('http://local')
+ .auth('tobi', 'learnboost')
+ .then(callback);
+
+
+ Node 클라이언트에서는 기본 인증을 URL 내에
+ "user:pass" 형식으로 포함시킬 수 있습니다.
+
+
request.get('http://tobi:learnboost@local').then(callback);
+
+
+ 기본적으로 Basic 인증만 사용됩니다. 브라우저에서는
+ {type:'auto'}를 추가하면 Digest, NTLM 등 브라우저에
+ 내장된 모든 인증 방식을 사용할 수 있습니다.
+
+
request.auth('digest', 'secret', {type:'auto'})
+
+
+ auth 메서드는 토큰 기반 인증을 위한 type의
+ bearer 옵션도 지원합니다.
+
+
request.auth('my_token', { type: 'bearer' })
+
+
다음 리다이렉션 따라가기
+
+ 기본적으로 최대 5번까지 리다이렉션이 자동으로 따라가며, 필요에 따라
+ res.redirects(n) 메서드를 사용하여 이 횟수를 지정할 수
+ 있습니다.
+
+
const response = await request.get('/some.png').redirects(2);
+
+
+ 리다이렉션 횟수가 제한을 초과하면 오류로 간주됩니다. 이를 성공적인
+ 응답으로 처리하려면
+ .ok(res => res.status < 400) 메서드를 사용하세요.
+
+
전역 상태를 위한 에이전트
+
쿠키 저장하기
+
+ Node에서 SuperAgent는 기본적으로 쿠키를 저장하지 않습니다. 하지만
+ .agent() 메서드를 사용하면 쿠키를 저장하는 SuperAgent
+ 인스턴스를 생성할 수 있습니다. 각 인스턴스는 독립적인 쿠키 저장소를
+ 가지고 있습니다.
+
+
const agent = request.agent();
+ agent
+ .post('/login')
+ .then(() => {
+ return agent.get('/cookied-page');
+ });
+
+
+ 브라우저에서는 쿠키가 자동으로 관리되므로
+ .agent()를 사용해도 쿠키가 분리되지는 않습니다.
+
+
+ 다중 요청을 위한 기본 옵션
+
+
+ 에이전트에서 호출된 일반 요청 메서드는 해당 에이전트가 처리하는 모든
+ 요청에 대해 기본값으로 적용됩니다.
+
+
const agent = request.agent()
+ .use(plugin)
+ .auth(shared);
+
+ await agent.get('/with-plugin-and-auth');
+ await agent.get('/also-with-plugin-and-auth');
+
+
+ 에이전트가 기본 옵션을 설정할 수 있도록 지원하는 메서드 목록입니다.
+ use, on, once, set,
+ query, type, accept,
+ auth, withCredentials, sortQuery,
+ retry, ok, redirects,
+ timeout, buffer, serialize,
+ parse, ca, key, pfx,
+ cert.
+
+
데이터 전달 방식
+
+ .pipe()는 .end() 또는
+ .then() 메서드 대신 사용되며, Node
+ 클라이언트는 요청과 응답 간에 데이터를 주고받도록 파이프 처리할 수
+ 있습니다.
+
+
+ 예를 들어, 파일의 콘텐츠를 요청 본문으로 전달하는 경우는 다음과
+ 같습니다.
+
+
const request = require('superagent');
+ const fs = require('fs');
+
+ const stream = fs.createReadStream('path/to/my.json');
+ const req = request.post('/somewhere');
+ req.type('json');
+ stream.pipe(req);
+
+
+ 요청에 데이터를 파이프할 경우, SuperAgent는 해당 데이터를
+ 청크 전송 인코딩
+ 방식으로 전송합니다. 이 방식은 Python WSGI 서버 등 모든 서버에서
+ 지원되지는 않습니다.
+
+
응답을 파일로 저장하려면 다음과 같이 파이프 처리할 수 있습니다.
+
const stream = fs.createWriteStream('path/to/my.json');
+ const req = request.get('/some.json');
+ req.pipe(stream);
+
+
+ 파이프와 콜백 또는 프로미스는 함께 사용할 수 없으며 ,
+ .end()나 Response 객체의 결과를 파이프
+ 처리해서는 안 됩니다.
+
+
// 이러한 방식으로 하지 마세요.
+ const stream = getAWritableStream();
+ const req = request
+ .get('/some.json')
+ // 나쁨: 이 방식은 스트림에 올바르지 않은 데이터를 전달하며 예기치 못한 방식으로 실패할 수 있습니다.
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
+ const req = request
+ .get('/some.json')
+ .end()
+ // 나쁨: 이 방식도 지원되지 않으며, .pipe는 자동으로 .end를 호출합니다.
+ .pipe(nope_its_too_late);
+
+
+ SuperAgent의
+ 향후 버전 에서는 pipe()를 부적절하게 호출하면 실패하게 됩니다.
+
+
다중 부분 요청
+
+ .attach()와 .field() 메서드를 제공하는
+ SuperAgent는 다중 부분 요청을 구성하는 데에도 매우 유용합니다.
+
+
+ .field() 또는 .attach()를 사용할 경우
+ .send()는 사용할 수 없으며,
+ Content-Type 헤더를 직접 설정해서는 안 됩니다. 올바른
+ 타입은 자동으로 지정됩니다.
+
+
파일 첨부하기
+
+ .attach(name, [file], [options])를 사용하여 파일을 전송할
+ 수 있습니다. 여러 파일을 첨부하려면 .attach를 반복 호출하면
+ 됩니다. 인자는 다음과 같습니다.
+
+
+ name — 폼 이름 필드.
+
+ file — 파일 경로의 문자열 또는 Blob/Buffer
+ 객체.
+
+
+ options — (선택) 사용자 정의 파일 이름의 문자열 또는
+ {filename: string} 형식의 객체. In Node also
+ {contentType: 'mime/type'} is supported. In
+ browser create a Blob with an appropriate type instead.
+
+
+
+
+
request
+ .post('/upload')
+ .attach('image1', 'path/to/felix.jpeg')
+ .attach('image2', imageBuffer, 'luna.jpeg')
+ .field('caption', 'My cats')
+ .then(callback);
+
+
필드 값
+
+ .field(name, value) 및 .field({name: value})를
+ 사용해 HTML 폼 필드처럼 값을 설정할 수 있습니다. 예를 들어 이름과 이메일
+ 정보를 함께 여러 이미지를 업로드하려면, 요청은 다음과 같이 구성될 수
+ 있습니다.
+
+
request
+ .post('/upload')
+ .field('user[name]', 'Tobi')
+ .field('user[email]', 'tobi@learnboost.com')
+ .field('friends[]', ['loki', 'jane'])
+ .attach('image', 'path/to/tobi.png')
+ .then(callback);
+
+
압축
+
+ node 클라이언트는 압축된 응답을 지원하며, 아무 것도 하지 않아도 됩니다!
+ 그냥 작동합니다.
+
+
응답 버퍼링
+
+ .req.buffer()를 호출하면 응답 본문을
+ res.text로 강제 버퍼링할 수 있습니다.
+ "text/plain", "text/html" 등 텍스트 응답의 기본
+ 버퍼링을 취소하려면 req.buffer(false)를 호출하세요.
+
+
+ res.buffered 플래그가 제공되면, 이를 활용하여 동일한 콜백
+ 함수에서 버퍼링된 응답과 버퍼링되지 않은 응답을 모두 처리할 수 있습니다.
+
+
CORS
+
+ 보안상의 이유로 브라우저는 서버가 CORS 헤더를 통해 명시적으로 허용하지
+ 않으면 교차 출처 요청(cross-origin requests)을 차단합니다. 브라우저는
+ 또한 서버가 어떤 HTTP 헤더와 메서드를 허용하는지 확인하기 위해 추가적인
+ OPTIONS 요청을 전송합니다.
+ CORS에 대해 더 알아보기 .
+
+
+ .withCredentials() 메서드는 origin(출처)에서 쿠키를 전송할
+ 수 있도록 활성화합니다. 단, 이 기능은
+ Access-Control-Allow-Origin 값이
+ 와일드카드("*")가 아니어야 하며,
+ Access-Control-Allow-Credentials 값이 "true"일
+ 경우에만 작동합니다.
+
+
request
+ .get('https://api.example.com:4001/')
+ .withCredentials()
+ .then(res => {
+ assert.equal(200, res.status);
+ assert.equal('tobi', res.text);
+ })
+
+
오류 처리하기
+
+ 콜백 함수는 항상 두 개의 인자를 전달합니다. 오류와 응답입니다. 오류가
+ 발생하지 않으면, 첫 번째 인자는 null 입니다.
+
+
request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .then(res => {
+
+ });
+
+
+ "error" 이벤트도 발생하며, 이를 통해 오류를 감지하고 처리할 수 있습니다.
+
+
request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .on('error', handle)
+ .then(res => {
+
+ });
+
+
+ SuperAgent는 기본적으로 4xx 및 5xx 응답(그리고 처리되지 않은 3xx
+ 응답도 포함)을 오류 로 간주합니다. 예를 들어 304 Not Modified,
+ 403 Forbidden 또는
+ 500 Internal Server Error 같은 응답을 받으면 해당 상태
+ 정보는 err.status를 통해 확인할 수 있습니다. 이러한
+ 응답으로부터 발생한 오류에는 "응답 요소 "에서 언급한 모든 속성을 포함한 err.response 필드도
+ 포함됩니다. 이 라이브러리는 일반적으로 성공 응답만을 원하고, HTTP 오류
+ 상태 코드를 오류로 처리하는 경우를 대비하여 이러한 방식으로 동작합니다.
+ 하지만 특정 오류 조건에 대해서는 사용자 정의 로직을 허용하도록 설계되어
+ 있습니다.
+
+
+ 네트워크 실패, 시간초과, 응답 없는 오류는 err.status 또는
+ err.response 필드를 포함하지 않습니다.
+
+
+ 404 또는 HTTP 오류 응답을 처리하고 싶다면,
+ error.status 요소를 사용할 수 있습니다. HTTP 오류(4xx 또는
+ 5xx 응답)가 발생했을 때 res.error 요소는
+ Error 객체이고 이는 다음과 같이 에러 확인을 수행할 수
+ 있습니다.
+
+
if (err && err.status === 404) {
+ alert('oh no ' + res.body.message);
+ }
+ else if (err) {
+ // 그 외 다른 모든 오류 유형은 일반적으로 처리합니다
+ }
+
+
+ 대안으로, .ok(callback) 메서드를 사용하여 응답이 오류인지
+ 아닌지 결정할 수 있습니다. ok 콜백은 응답을 받고 응답이
+ 성공으로 해석되면 true를 반환합니다.
+
+
request.get('/404')
+ .ok(res => res.status < 500)
+ .then(response => {
+ // 404 페이지를 성공적인 응답으로 처리합니다
+ })
+
+
진행과정 추적하기
+
+ SuperAgent는 업로드와 큰 파일 다운로드에서
+ progress 이벤트를 동작시킵니다.
+
+
request.post(url)
+ .attach('field_name', file)
+ .on('progress', event => {
+ /* the event is:
+ {
+ direction: "upload" or "download"
+ percent: 0 to 100 // 0에서 100까지 (파일 크기를 알 수 없는 경우 생략될 수 있습니다)
+ total: // 전체 파일 크기 (생략될 수 있습니다)
+ loaded: // 현재까지 다운로드되거나 업로드된 바이트 수
+ } */
+ })
+ .then()
+
+
로컬 호스트에서 테스트하기
+
+ 특정 IP 주소 연결 설정하기
+
+
+ Node.js에서는 DNS를 무시하고 .connect() 메서드를 사용하여
+ 모든 요청을 특정 IP 주소로 직접 연결할 수 있습니다. 예를 들어, 이 요청은
+ example.com 대신 로컬호스트로 전달됩니다.
+
+
const res = await request.get("http://example.com").connect("127.0.0.1");
+
+
+ 요청은 리다이렉트 되어, 여러 호스트명과 IP를 특정지을 수 있으며 특별한
+ *를 대체로 설정할 수 있습니다. (다른 와일드 카드는 지원되지
+ 않습니다). 요청은 원본 값을 가지며 본인의 Host 헤더를
+ 유지합니다. .connect(undefined)는 이러한 기능을 끕니다.
+
+
const res = await request.get("http://redir.example.com:555")
+ .connect({
+ "redir.example.com": "127.0.0.1", // redir.example.com:555는 127.0.0.1:555를 사용합니다.
+ "www.example.com": false, // 이 항목은 재정의하지 마세요. 일반적인 DNS 설정을 사용합니다.
+ "mapped.example.com": { host: "127.0.0.1", port: 8080}, // mapped.example.com의 모든 포트는 127.0.0.1:8080으로 매핑됩니다.
+ "*": "proxy.example.com", // 나머지 모든 요청은 이 호스트로 전달됩니다
+ });
+
+
+ 로컬 호스트에서 깨지거나 보안되지 않은 HTTPS 무시하기
+
+
+ Node.js에서 HTTPS 설정이 잘못되었거나 보안성이 떨어지는 경우(예: 자체
+ 서명된 인증서를 사용하면서
+ .ca()를 지정하지 않은 경우),
+ .trustLocalhost()를 호출하면 localhost로의
+ 요청을 허용할 수 있습니다.
+
+
const res = await request.get("https://localhost").trustLocalhost()
+
+
+ .connect("127.0.0.1")와 함께 사용하면 HTTPS
+ 요청을 어떤 도메인에서든 localhost로 강제로 리다이렉트할 수
+ 있습니다.
+
+
+ localhost는 신뢰되지 않은 네트워크에 노출되지 않는 루프백
+ 인터페이스이기 때문에, 깨진 HTTPS를 무시하는 것이 일반적으로 안전합니다.
+ localhost를 신뢰하도록 설정하는 것이 향후 기본값이 될 수
+ 있습니다. 127.0.0.1'의 진위 여부를 강제로 검사하려면
+ .trustLocalhost(false)를 사용하세요.
+
+
+ 다른 IP 주소로 요청을 보낼 때 HTTPS 보안을 비활성화하는 기능은
+ 의도적으로 지원하지 않습니다. 이러한 옵션은 HTTPS 문제를 빠르게
+ "해결"하려는 방식으로 오용되는 경우가 많기 때문입니다.
+ Let's Encrypt 를 통해 무료
+ HTTPS 인증서를 발급하거나, .ca(ca_public_pem)을 사용해 자체
+ 서명된 인증서를 신뢰할 수 있도록 직접 CA를 설정할 수 있습니다.
+
+
Promise 및 Generator 지원
+
+ SuperAgent의 요청은 "thenable" 객체이며, JavaScript의 Promise
+ 및 async/await
+ 문법과 호환됩니다.
+
+
const res = await request.get(url);
+
+
+ Promise를 사용할 경우,
+ .end() 또는 .pipe()를 호출하지
+ 마세요 . .then() 또는 await를 사용하면 요청을 처리할
+ 수 있는 다른 방식들이 모두 비활성화됩니다.
+
+
+ co 와 같은 라이브러리나
+ koa 와 같은 웹
+ 프레임워크에서는 SuperAgent의 모든 메서드에서 yield를
+ 사용할 수 있습니다.
+
+
const req = request
+ .get('http://local')
+ .auth('tobi', 'learnboost');
+ const res = yield req;
+
+
+ SuperAgent는 전역 Promise 객체가 존재하는 환경에서
+ 동작하도록 설계되어 있습니다. Internet Explorer나 Node.js 0.10에서
+ promise를 사용하려면 v7 버전과 폴리필이 필요합니다.
+
+
+ v8 버전부터는 IE에 대한 지원이 중단되었습니다. Opera 85나 iOS Safari
+ 12.2–12.5 등을 지원하려면 WeakRef와 BigInt에 대한 폴리필을 추가해야
+ 합니다. 예를 들어
+ https://cdnjs.cloudflare.com/polyfill/ 을 사용할 수 있습니다.
+
+
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=WeakRef,BigInt"></script>
+
+
브라우저와 node 버전
+
+ SuperAgent에는 두 가지 구현 방식이 있습니다. 하나는 웹 브라우저용(XHR
+ 사용)이고, 다른 하나는 Node.JS용(core http 모듈 사용)입니다. 기본적으로
+ Browserify와 WebPack은 브라우저 버전을 선택합니다.
+
+
+ Node.JS용 코드를 컴파일하려면 WebPack 설정에서 반드시
+ node target
+ 을 지정해야 합니다.
+
+
+
+
+
+
+
+
+
diff --git a/docs/ko_KR/index.md b/docs/ko_KR/index.md
new file mode 100644
index 000000000..8ecb8befc
--- /dev/null
+++ b/docs/ko_KR/index.md
@@ -0,0 +1,877 @@
+# SuperAgent
+
+SuperAgent는 기존의 복잡한 요청 API에 대한 불만에서 출발해 유연성, 가독성, 그리고 낮은 학습 난이도를 목표로 설계된 경량 Ajax API입니다. 또한 Node.js 환경에서도 동작합니다!
+
+```javascript
+request
+ .post('/api/pet')
+ .send({ name: 'Manny', species: 'cat' })
+ .set('X-API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then((res) => {
+ alert('yay got ' + JSON.stringify(res.body));
+ });
+```
+
+## 테스트 문서
+
+[**English**](/superagent/)
+
+다음의 [테스트 문서](../test.html)는 [Mocha](https://mochajs.org/)의 "doc" 리포터를 사용해 생성되었으며, 실제 테스트 스위트를 직접 반영합니다.
+이 문서는 추가적인 참고 자료로 활용할 수 있습니다.
+
+## 기본 요청
+
+요청은 `request` 객체에서 적절한 메서드를 호출하여 시작되며, 그 다음 `.then()` 또는 `.end()` 혹은 [`await`](#promise-and-generator-support)를 사용해 요청을 전송할 수 있습니다. 예를 들어, 간단한 **GET** 요청은 다음과 같습니다.
+
+```javascript
+request
+ .get('/search')
+ .then((res) => {
+ // res.body, res.headers, res.status
+ })
+ .catch((err) => {
+ // err.message, err.response
+ });
+```
+
+HTTP 메서드는 문자열로도 전달할 수 있습니다.
+
+```javascript
+request('GET', '/search').then(success, failure);
+```
+
+예전 방식의 콜백도 지원되지만, 권장되지는 않습니다. `.then()` 대신 `.end()`를 호출하여 요청을 전송할 수 있습니다.
+
+```javascript
+request('GET', '/search').end(function (err, res) {
+ if (res.ok) {
+ }
+});
+```
+
+절대 URL을 사용할 수 있습니다. 단, 웹 브라우저에서는 서버가 [CORS](#cors)를 구현한 경우에만 절대 URL이 정상적으로 작동합니다.
+
+```javascript
+request.get('https://example.com/search').then((res) => {});
+```
+
+**Node** 클라이언트는 [유닉스 도메인 소켓](https://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%89%EC%8A%A4_%EB%8F%84%EB%A9%94%EC%9D%B8_%EC%86%8C%EC%BC%93)을 통한 요청을 지원합니다.
+
+```javascript
+// pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
+// Use `%2F` as `/` in SOCKET_PATH
+try {
+ const res = await request.get(
+ 'http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search'
+ );
+ // res.body, res.headers, res.status
+} catch (err) {
+ // err.message, err.response
+}
+```
+
+**DELETE**, **HEAD**, **PATCH**, **POST**, **PUT** 요청도 사용할 수 있으며, 다음 예시에서 메서드 이름만 변경하면 됩니다.
+
+```javascript
+request.head('/favicon.ico').then((res) => {});
+```
+
+**DELETE**는 `delete`가 예약어였던 구형 IE와의 호환성을 위해 `.del()` 메서드로도 호출할 수 있습니다.
+
+HTTP 메서드의 기본값은 **GET**이므로, 다음과 같이 작성해도 유효합니다.
+
+```javascript
+request('/search', (err, res) => {});
+```
+
+## HTTP/2 사용하기
+
+HTTP/1.x 폴백 없이 HTTP/2 프로토콜만 사용하려면 `.http2()` 메서드를 호출하여 요청을 전송할 수 있습니다.
+
+```javascript
+const request = require('superagent');
+const res = await request.get('https://example.com/h2').http2();
+```
+
+## 헤더 필드 설정하기
+
+헤더 필드 설정은 간단합니다. 필드 이름과 값을 `.set()` 메서드에 전달하면 됩니다.
+
+```javascript
+request
+ .get('/search')
+ .set('API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(callback);
+```
+
+You may also pass an object to set several fields in a single call:
+
+여러 개의 헤더 필드를 한 번에 설정하려면 객체를 전달하면 됩니다.
+
+```javascript
+request
+ .get('/search')
+ .set({ 'API-Key': 'foobar', Accept: 'application/json' })
+ .then(callback);
+```
+
+## `GET` 요청
+
+`.query()` 메서드는 객체를 인자로 받아 **GET** 요청 시 쿼리 문자열을 자동으로 생성합니다. 예를 들어 다음 코드는 `/search?query=Manny&range=1..5&order=desc` 경로를 생성합니다.
+
+````js
+request
+ .get('/search')
+ .query({ query: 'Manny', range: '1..5', order: 'desc' });
+
+
+```javascript
+request
+ .get('/search')
+ .query({ query: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .then((res) => {});
+````
+
+또는 하나의 객체로 설정할 수 있습니다.
+
+```javascript
+request
+ .get('/search')
+ .query({ query: 'Manny', range: '1..5', order: 'desc' })
+ .then((res) => {});
+```
+
+`.query()` 메서드는 문자열도 받습니다.
+
+```javascript
+request
+ .get('/querystring')
+ .query('search=Manny&range=1..5')
+ .then((res) => {});
+```
+
+조인할 수도 있습니다.
+
+```javascript
+request
+ .get('/querystring')
+ .query('search=Manny')
+ .query('range=1..5')
+ .then((res) => {});
+```
+
+## `HEAD` 요청하기
+
+HEAD 요청에서도 `.query()` 메서드를 사용할 수 있습니다. 예를 들어 다음 코드는 `/users?email=joe@smith.com` 경로를 생성합니다.
+
+```javascript
+request
+ .head('/users')
+ .query({ email: 'joe@smith.com' })
+ .then((res) => {});
+```
+
+## `POST` / `PUT` 요청
+
+전형적인 JSON **POST** 요청은 Content-Type 헤더를 적절히 설정하고,
+데이터를 JSON 형식으로 전송하는 방식입니다. 예를 들어 다음과 같은 코드가 이에 해당합니다.
+
+```javascript
+request
+ .post('/user')
+ .set('Content-Type', 'application/json')
+ .send('{"name":"tj","pet":"tobi"}')
+ .then(callback)
+ .catch(errorCallback);
+```
+
+JSON은 가장 일반적으로 사용되므로 기본값으로 설정되어 있습니다. 다음 예제는 앞선 예제와 동일한 동작을 수행합니다.
+
+```javascript
+request
+ .post('/user')
+ .send({ name: 'tj', pet: 'tobi' })
+ .then(callback, errorCallback);
+```
+
+또는 `.send()` 여러 번 호출할 수 있습니다.
+
+```javascript
+request
+ .post('/user')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback);
+```
+
+기본적으로 문자열을 전송하면 `Content-Type`이 `application/x-www-form-urlencoded`로 자동 설정됩니다.
+여러 번 `.send()`를 호출하면 각 문자열이 `&`로 연결되어 최종적으로 `name=tj&pet=tobi`와 같은 결과가 생성됩니다.
+
+```javascript
+request
+ .post('/user')
+ .send('name=tj')
+ .send('pet=tobi')
+ .then(callback, errorCallback);
+```
+
+SuperAgent는 다양한 형식으로 확장 가능하지만, 기본적으로 "json"과 "form" 형식을 지원합니다. `application/x-www-form-urlencoded` 형식으로 데이터를 전송하려면 `.type('form')`을 호출하면 됩니다. 기본 형식은 `"json"`입니다. 다음 요청은 본문에 `"name=tj&pet=tobi"`를 포함하여 **POST**됩니다.
+
+```javascript
+request
+ .post('/user')
+ .type('form')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback);
+```
+
+[`FormData`](https://developer.mozilla.org/ko/docs/Web/API/FormData/FormData) 객체를 사용하는 것도 지원됩니다. 다음 예제는 `id="myForm"`인 HTML 폼의 내용을 **POST** 방식으로 전송합니다.
+
+```javascript
+request
+ .post('/user')
+ .send(new FormData(document.getElementById('myForm')))
+ .then(callback, errorCallback);
+```
+
+## `Content-Type` 설정하기
+
+가장 명확한 해결책은 `.set()` 메서드를 사용하는 것입니다.
+
+```javascript
+request.post('/user').set('Content-Type', 'application/json');
+```
+
+간단하게 `.type()` 메서드를 사용할 수 있으며,
+표준화된 MIME 타입(`type/subtype`)을 직접 지정하거나
+"xml", "json", "png" 등과 같은 확장자 이름만으로도 설정할 수 있습니다.
+
+```javascript
+request.post('/user').type('application/json');
+
+request.post('/user').type('json');
+
+request.post('/user').type('png');
+```
+
+## 요청 본문 직렬화하기
+
+SuperAgent는 기본적으로 JSON과 폼 데이터를 자동으로 직렬화합니다.
+또한 다른 콘텐츠 유형에 대해서도 자동 직렬화를 설정할 수 있습니다.
+
+```js
+request.serialize['application/xml'] = function (obj) {
+ return 'string generated from obj';
+};
+
+// 'application/xml' Content-type을 가진 모든 요청은
+// 자동으로 직렬화 됩니다.
+```
+
+사용자 정의 형식으로 페이로드를 전송하려면,
+요청 단위로 `.serialize()` 메서드를 사용해 SuperAgent의 기본 직렬화 방식을 교체할 수 있습니다.
+
+```js
+request
+ .post('/user')
+ .send({ foo: 'bar' })
+ .serialize((obj) => {
+ return 'string generated from obj';
+ });
+```
+
+## 요청 재시도하기
+
+`.retry()` 메서드를 사용하면, 일시적인 오류나 불안정한 인터넷 연결로 인해 요청이 실패한 경우 SuperAgent가 자동으로 재시도합니다.
+
+이 메서드는 두 개의 선택적 인자를 받습니다. 재시도 횟수(기본값은 1)와 콜백 함수입니다. 각 재시도 전에 `callback(err, res)`를 호출합니다. 콜백 함수는 요청을 재시도할지 여부를 결정하기 위해 `true` 또는 `false`를 반환할 수 있습니다. 단, 최대 재시도 횟수는 항상 적용됩니다.
+
+```javascript
+ request
+ .get('https://example.com/search')
+ .retry(2) // 혹은
+ .retry(2, callback)
+ .then(finished);
+ .catch(failed);
+```
+
+멱등한 요청인 경우에만 `.retry()` 메서드를 사용하세요. 예를 들어, 동일한 요청이 서버에 여러 번 도달하더라도 중복 구매와 같은 바람직하지 않은 부작용이 발생하지 않아야 합니다.
+
+모든 요청 메서드는 기본적으로 재시도 대상에 포함됩니다. 따라서 POST 요청을 재시도하지 않도록 하려면, 사용자 정의 재시도 콜백을 전달해야 합니다.
+
+기본적으로 다음과 같은 상태 코드는 자동으로 재시도됩니다.
+
+- `408`
+- `413`
+- `429`
+- `500`
+- `502`
+- `503`
+- `504`
+- `521`
+- `522`
+- `524`
+
+기본적으로 다음과 같은 오류 코드가 자동으로 재시도됩니다.
+
+- `'ETIMEDOUT'`
+- `'ECONNRESET'`
+- `'EADDRINUSE'`
+- `'ECONNREFUSED'`
+- `'EPIPE'`
+- `'ENOTFOUND'`
+- `'ENETUNREACH'`
+- `'EAI_AGAIN'`
+
+## Accept 설정하기
+
+`.type()` 메서드와 유사하게, `.accept()` 메서드를 사용하면 `Accept` 헤더를 간편하게 설정할 수 있습니다. 이 메서드는 `request.types`를 참조하며, `type/subtype` 형식의 MIME 타입 전체 이름이나 "xml", "json", "png" 등의 확장자 형태로도 지정할 수 있어 편리합니다.
+
+```javascript
+request.get('/user').accept('application/json');
+
+request.get('/user').accept('json');
+
+request.post('/user').accept('png');
+```
+
+### Facebook과 Accept JSON
+
+Facebook API를 호출할 때는 반드시 요청 헤더에 `Accept: application/json`을 포함해야 합니다. 그렇지 않으면 Facebook은 `Content-Type: text/javascript; charset=UTF-8`으로 응답하게 되며, SuperAgent는 이 형식을 파싱하지 못해 `res.body`가 `undefined`가 됩니다. `req.accept('json')` 또는 `req.set('Accept', 'application/json')`을 사용할 수 있습니다. 자세한 사항은 [issue 1078](https://github.com/ladjs/superagent/issues/1078)에서 확인해보세요.
+
+## 쿼리 문자열
+
+`req.query(obj)`는 쿼리 문자열을 구성하는 데 사용되는 메서드입니다. 예를 들어 **POST** 요청에서 `?format=json&dest=/login`과 같은 쿼리 문자열을 추가할 수 있습니다.
+
+```javascript
+request
+ .post('/')
+ .query({ format: 'json' })
+ .query({ dest: '/login' })
+ .send({ post: 'data', here: 'wahoo' })
+ .then(callback);
+```
+
+기본적으로 쿼리 문자열은 특정한 순서로 조립되지 않습니다. ASCII 순으로 정렬된 쿼리 문자열을 사용하려면 `req.sortQuery()`를 호출하면 됩니다. 또한 `req.sortQuery(myComparisonFn)`을 통해 사용자 정의 정렬 비교 함수를 전달할 수도 있습니다. 비교 함수는 두 개의 인자를 받아 음수, 0 또는 양수를 반환해야 합니다.
+
+```js
+// 기본 정렬 방식
+request
+ .get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery()
+ .then(callback);
+
+// 사용자 정의 정렬 함수
+request
+ .get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery((a, b) => a.length - b.length)
+ .then(callback);
+```
+
+## TLS 옵션
+
+Node.js에서 SuperAgent는 HTTPS 요청을 구성할 수 있는 다양한 메서드를 지원합니다.
+
+- `.ca()`: 신뢰할 CA 인증서를 설정합니다.
+- `.cert()`: 클라이언트 인증서 체인을 설정합니다.
+- `.key()`: 클라이언트의 개인 키를 설정합니다.
+- `.pfx()`: PKCS12 형식의 PFX 파일을 사용하여 클라이언트의 개인 키와 인증서 체인을 설정합니다.
+- `.disableTLSCerts()`: 만료되었거나 유효하지 않은 TLS 인증서를 거부하지 않도록 설정합니다. 내부적으로 `rejectUnauthorized=true`가 설정되며, 중간자 공격(MITM)에 노출될 수 있으므로 주의가 필요합니다.
+
+더 자세한 내용은 Node.js [https.request 문서](https://nodejs.org/api/https.html#https_https_request_options_callback)에서 확인할 수 있습니다.
+
+```js
+var key = fs.readFileSync('key.pem'),
+ cert = fs.readFileSync('cert.pem');
+
+request.post('/client-auth').key(key).cert(cert).then(callback);
+```
+
+```js
+var ca = fs.readFileSync('ca.cert.pem');
+
+request
+ .post('https://localhost/private-ca-server')
+ .ca(ca)
+ .then((res) => {});
+```
+
+## Parsing response bodies
+
+## 응답 본문 파싱하기
+
+SuperAgent will parse known response-body data for you,
+currently supporting `application/x-www-form-urlencoded`,
+`application/json`, and `multipart/form-data`. You can setup
+automatic parsing for other response-body data as well:
+
+SuperAgent는 응답 본문 데이터를 자동으로 파싱해줍니다.
+현재 `application/x-www-form-urlencoded`, `application/json`, `multipart/form-data`을 지원합니다. 이외의 응답 본문 데이터에 대해서도 자동 파싱을 설정할 수 있습니다.
+
+```js
+// 브라우저
+request.parse['application/xml'] = function (str) {
+ return { object: 'parsed from str' };
+};
+
+// node
+request.parse['application/xml'] = function (res, cb) {
+ // 응답 문자를 파싱하고 res.body를 여기서 설정하세요
+
+ cb(null, res);
+};
+
+// 앞으로 'application/xml' 유형의 반응은
+// 자동으로 파싱됩니다
+```
+
+`.buffer(true).parse(fn)` 메서드를 사용하면 내장된 파서보다 우선적으로 적용되는 사용자 정의 파서를 설정할 수 있습니다. `.buffer(false)`로 응답 버퍼링이 비활성화되어 있다면, `response` 이벤트는 본문 파싱이 완료되기 전에 발생하므로 `response.body`를 사용할 수 없습니다.
+
+### JSON / Urlencoded
+
+`res.body` 속성은 파싱된 객체를 나타냅니다. 예를 들어, 응답이 JSON 문자열 `{"user":{"name":"tobi"}}`를 반환했다면, `res.body.user.name`은 "tobi" 값을 갖게 됩니다. 마찬가지로 x-www-form-urlencoded 형식의 "user[name]=tobi"도 동일한 결과를 제공합니다. 단, 중첩은 한 단계까지만 지원되므로 더 복잡한 구조의 데이터를 다루려면 JSON 형식을 사용하는 것이 좋습니다.
+
+배열은 key를 반복해서 전달하는 방식으로 전송됩니다. 예를 들어, `.send({ color: ['red', 'blue'] })`는 `color=red&color=blue`로 변환되어 전송됩니다. 배열의 key에 `[]`를 포함시키고 싶다면 SuperAgent는 이를 자동으로 처리하지 않으므로, 직접 `color[]`와 같이 key 이름에 대괄호를 추가해야 합니다.
+
+### 다중 파트
+
+Node 클라이언트는 [Formidable](https://github.com/felixge/node-formidable) 모듈을 통해 *multipart/form-data*를 지원합니다.
+다중 파트 응답을 파싱할 때 `res.files` 객체를 사용할 수 있으며, 이 객체에는 업로드된 파일에 대한 정보가 포함됩니다. 예를 들어, 다음과 같은 multipart 본문을 포함한 응답을 가정해볼 수 있습니다.
+
+ --whoop
+ Content-Disposition: attachment; name="image"; filename="tobi.png"
+ Content-Type: image/png
+
+ ... data here ...
+ --whoop
+ Content-Disposition: form-data; name="name"
+ Content-Type: text/plain
+
+ Tobi
+ --whoop--
+
+`res.body.name`은 "Tobi" 값을 가지고 있으며, `res.files.image`는 디스크 경로, 파일 이름 및 기타 속성을 포함한 `File` 객체입니다.
+
+### 바이너리
+
+브라우저에서는 바이너리 응답 본문을 처리하기 위해 `.responseType('blob')`을 사용할 수 있습니다. 이 API는 Node.js 환경에서는 필요하지 않습니다. 이 메서드에서 지원되는 인자 값은 다음과 같습니다.
+
+- `'blob'`는 XMLHttpRequest의 `responseType` 속성에 그대로 전달됩니다.
+- `'arraybuffer'`도 마찬가지로 `responseType` 속성에 전달됩니다.
+
+```js
+req
+ .get('/binary.data')
+ .responseType('blob')
+ .then((res) => {
+ // 여기서 res.body는 브라우저 기본 Blob 타입입니다.
+ });
+```
+
+더 자세한 내용은 Mozilla Developer Network의 [XMLHttpRequest.responseType 문서](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType)에서 확인할 수 있습니다.
+
+## 응답 속성
+
+`Response` 객체에는 응답 텍스트, 파싱된 응답 본문, 헤더 필드, 상태 플래그 등 다양한 유용한 플래그와 속성이 설정되어 있습니다.
+
+### 응답 문자
+
+`res.text` 속성에는 파싱되지 않은 응답 본문 문자열이 포함됩니다. 이 속성은 클라이언트 API에서는 항상 존재하며, Node 환경에서는 MIME 타입이 "text/_", "_/json", "x-www-form-urlencoded"와 일치할 경우에만 기본적으로 제공됩니다. 이러한 제한은 대용량 multipart 파일이나 이미지 등의 본문을 텍스트로 버퍼링하는 것이 매우 비효율적이기 때문에 메모리를 절약하기 위한 목적입니다. 응답을 강제로 버퍼링하려면 "응답 버퍼링" 섹션을 참조하세요.
+
+### 응답 본문
+
+SuperAgent는 요청 데이터를 자동으로 직렬화할 뿐만 아니라, 자동으로 파싱할 수도 있습니다. Content-Type에 대해 파서가 정의되어 있는 경우, 해당 타입에 맞게 응답이 파싱되며 기본적으로 "application/json"과 "application/x-www-form-urlencoded" 형식이 포함됩니다. 파싱된 객체는 `res.body`를 통해 접근할 수 있습니다.
+
+### 응답 헤더 필드
+
+`res.header`는 파싱된 응답 헤더 필드를 담은 객체로, Node.js와 마찬가지로 필드 이름을 소문자로 변환하여 저장합니다.
+예를 들어, `res.header['content-length']`와 같이 접근할 수 있습니다.
+
+### 응답 콘텐츠 타입
+
+Content-Type 응답 헤더는 특별하게 처리되어 `res.type` 속성은 charset 정보를 제외한 콘텐츠 타입만을 제공합니다. 예를 들어 Content-Type이 "text/html; charset=utf8"인 경우, `res.type`은 "text/html"을 반환하고, `res.charset` 속성에는 "utf8"이 포함됩니다.
+
+### 응답 상태
+
+응답 상태 플래그는 요청이 성공했는지 여부를 비롯한 다양한 유용한 정보를 판단하는 데 도움을 줍니다. 이를 통해 SuperAgent는 RESTful 웹 서비스와 효과적으로 상호작용할 수 있습니다. 현재 정의된 주요 플래그는 다음과 같습니다.
+
+```javascript
+var type = (status / 100) | 0;
+
+// 상태 / 클래스
+res.status = status;
+res.statusType = type;
+
+// 기본
+res.info = 1 == type;
+res.ok = 2 == type;
+res.clientError = 4 == type;
+res.serverError = 5 == type;
+res.error = 4 == type || 5 == type;
+
+// 편의 기능
+res.accepted = 202 == status;
+res.noContent = 204 == status || 1223 == status;
+res.badRequest = 400 == status;
+res.unauthorized = 401 == status;
+res.notAcceptable = 406 == status;
+res.notFound = 404 == status;
+res.forbidden = 403 == status;
+```
+
+## 요청 중단하기
+
+요청을 중단하려면 `req.abort()` 메서드를 호출하기만 하면 됩니다.
+
+## 타임아웃
+
+때때로 네트워크나 서버가 요청을 수신한 후 응답 없이 멈춰버리는 경우가 있습니다. 이러한 무한 대기를 방지하려면 타임아웃을 설정해야 합니다.
+
+- `req.timeout({deadline: ms})` 또는 `req.timeout(ms)`는 업로드, 리다이렉트, 서버 처리 시간을 포함한 전체 요청이 완료되어야 하는 최종 시간 제한을 설정합니다. `ms`는 0보다 큰 밀리초 단위의 숫자이며, 제한 시간 내에 응답이 완료되지 않으면 요청은 중단됩니다.
+
+- `req.timeout({response: ms})`는 서버로부터 첫 번째 바이트가 도착할 때까지의 최대 대기 시간을 설정합니다. 전체 다운로드 소요 시간은 제한하지 않습니다. 응답 타임아웃은 DNS 조회, TCP/IP 및 TLS 연결, 요청 데이터 업로드 시간을 포함하므로, 서버의 실제 응답 시간보다 몇 초 더 길게 설정하는 것이 좋습니다.
+
+`deadline`과 `response` 타임아웃은 함께 사용하는 것이 좋습니다. 짧은 응답 타임아웃은 응답하지 않는 네트워크를 빠르게 감지하는 데 유용하고, 긴 데드라인은 느리지만 안정적인 네트워크 환경에서 다운로드를 완료할 수 있도록 여유 시간을 제공합니다. 두 타이머 모두 첨부된 파일 업로드에 허용되는 시간을 제한합니다. 파일을 업로드하는 경우에는 충분히 긴 타임아웃을 설정하는 것이 좋습니다.
+
+```javascript
+request
+ .get('/big-file?network=slow')
+ .timeout({
+ response: 5000, // 서버가 데이터를 보내기 시작할 때까지 최대 5초간 기다립니다.
+ deadline: 60000 // 파일 전체를 로드하는 데 최대 1분까지 허용합니다.
+ })
+ .then(
+ (res) => {
+ /* 제시간 응답 수신 */
+ },
+ (err) => {
+ if (err.timeout) {
+ /* 시간 초과! */
+ } else {
+ /* 그 외 오류 */
+ }
+ }
+ );
+```
+
+타임아웃 오류에는 `.timeout` 속성이 포함되어 있습니다.
+
+## 인증
+
+Node와 브라우저 환경에서 `.auth()` 메서드를 사용하여 인증을 수행할 수 있습니다.
+
+```javascript
+request.get('http://local').auth('tobi', 'learnboost').then(callback);
+```
+
+_Node_ 클라이언트에서는 기본 인증을 URL 내에 "user:pass" 형식으로 포함시킬 수 있습니다.
+
+```javascript
+request.get('http://tobi:learnboost@local').then(callback);
+```
+
+기본적으로 `Basic` 인증만 사용됩니다. 브라우저에서는 `{type: 'auto'}`를 추가하면 Digest, NTLM 등 브라우저에 내장된 모든 인증 방식을 사용할 수 있습니다.
+
+```javascript
+request.auth('digest', 'secret', { type: 'auto' });
+```
+
+`auth` 메서드는 토큰 기반 인증을 위한 `type: 'bearer'` 옵션도 지원합니다.
+
+```javascript
+request.auth('my_token', { type: 'bearer' });
+```
+
+## 다음 리다이렉션 따라가기
+
+기본적으로 최대 5번까지 리다이렉션이 자동으로 따라가며, 필요에 따라 `res.redirects(n)` 메서드를 사용하여 이 횟수를 지정할 수 있습니다.
+
+```javascript
+const response = await request.get('/some.png').redirects(2);
+```
+
+리다이렉션 횟수가 제한을 초과하면 오류로 간주됩니다. 이를 성공적인 응답으로 처리하려면 `.ok(res => res.status < 400)` 메서드를 사용하세요.
+
+## 전역 상태를 위한 에이전트
+
+### 쿠키 저장하기
+
+Node에서 SuperAgent는 기본적으로 쿠키를 저장하지 않습니다. 하지만 `.agent()` 메서드를 사용하면 쿠키를 저장하는 SuperAgent 인스턴스를 생성할 수 있습니다. 각 인스턴스는 독립적인 쿠키 저장소를 가지고 있습니다.
+
+```javascript
+const agent = request.agent();
+agent.post('/login').then(() => {
+ return agent.get('/cookied-page');
+});
+```
+
+브라우저에서는 쿠키가 자동으로 관리되므로 `.agent()`를 사용해도 쿠키가 분리되지는 않습니다.
+
+### 다중 요청을 위한 기본 옵션
+
+에이전트에서 호출된 일반 요청 메서드는 해당 에이전트가 처리하는 모든 요청에 대해 기본값으로 적용됩니다.
+
+```javascript
+const agent = request.agent().use(plugin).auth(shared);
+
+await agent.get('/with-plugin-and-auth');
+await agent.get('/also-with-plugin-and-auth');
+```
+
+에이전트가 기본 옵션을 설정할 수 있도록 지원하는 메서드 목록입니다. `use`, `on`, `once`, `set`, `query`, `type`, `accept`, `auth`, `withCredentials`, `sortQuery`, `retry`, `ok`, `redirects`, `timeout`, `buffer`, `serialize`, `parse`, `ca`, `key`, `pfx`, `cert`.
+
+## 데이터 전달 방식
+
+`.pipe()`는 `.end()` 또는 `.then()` 메서드 **대신** 사용되며, Node 클라이언트는 요청과 응답 간에 데이터를 주고받도록 파이프 처리할 수 있습니다.
+
+예를 들어, 파일의 콘텐츠를 요청 본문으로 전달하는 경우는 다음과 같습니다.
+
+```javascript
+const request = require('superagent');
+const fs = require('fs');
+
+const stream = fs.createReadStream('path/to/my.json');
+const req = request.post('/somewhere');
+req.type('json');
+stream.pipe(req);
+```
+
+요청에 데이터를 파이프할 경우, SuperAgent는 해당 데이터를 [청크 전송 인코딩](https://en.wikipedia.org/wiki/Chunked_transfer_encoding) 방식으로 전송합니다. 이 방식은 Python WSGI 서버 등 모든 서버에서 지원되지는 않습니다.
+
+응답을 파일로 저장하려면 다음과 같이 파이프 처리할 수 있습니다.
+
+```javascript
+const stream = fs.createWriteStream('path/to/my.json');
+const req = request.get('/some.json');
+req.pipe(stream);
+```
+
+파이프와 콜백 또는 프로미스는 **함께 사용할 수 없으며**, `.end()`나 `Response` 객체의 결과를 파이프 처리해서는 안 됩니다.
+
+```javascript
+// 이러한 방식으로 하지 마세요.
+const stream = getAWritableStream();
+const req = request
+ .get('/some.json')
+ // 나쁨: 이 방식은 스트림에 올바르지 않은 데이터를 전달하며 예기치 못한 방식으로 실패할 수 있습니다.
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream));
+const req = request
+ .get('/some.json')
+ .end()
+ // 나쁨: 이 방식도 지원되지 않으며, .pipe는 자동으로 .end를 호출합니다.
+ .pipe(nope_its_too_late);
+```
+
+SuperAgent의 [향후 버전](https://github.com/ladjs/superagent/issues/1188)에서는 `pipe()`를 부적절하게 호출하면 실패하게 됩니다.
+
+## 다중 부분 요청
+
+`.attach()`와 `.field()` 메서드를 제공하는 SuperAgent는 다중 부분 요청을 구성하는 데에도 매우 유용합니다.
+
+`.field()` 또는 `.attach()`를 사용할 경우 `.send()`는 사용할 수 없으며, `Content-Type` 헤더를 직접 설정해서는 안 됩니다. 올바른 타입은 자동으로 지정됩니다.
+
+### 파일 첨부하기
+
+`.attach(name, [file], [options])`를 사용하여 파일을 전송할 수 있습니다. 여러 파일을 첨부하려면 `.attach`를 반복 호출하면 됩니다. 인자는 다음과 같습니다.
+
+- `name` — 폼 이름 필드
+- `file` — 파일 경로의 문자열 또는 `Blob`/`Buffer` 객체.
+- `options` — (선택) 사용자 정의 파일 이름의 문자열 또는 `{filename: string}` 형식의 객체. Node 환경에서는 `{contentType: 'mime/type'}`도 지원하며 브라우저에서는 적절한 타입의 `Blob` 객체를 생성해야 합니다.
+
+
+
+```javascript
+request
+ .post('/upload')
+ .attach('image1', 'path/to/felix.jpeg')
+ .attach('image2', imageBuffer, 'luna.jpeg')
+ .field('caption', 'My cats')
+ .then(callback);
+```
+
+### 필드 값
+
+`.field(name, value)` 및 `.field({name: value})`를 사용해 HTML 폼 필드처럼 값을 설정할 수 있습니다. 예를 들어 이름과 이메일 정보를 함께 여러 이미지를 업로드하려면, 요청은 다음과 같이 구성될 수 있습니다.
+
+```javascript
+request
+ .post('/upload')
+ .field('user[name]', 'Tobi')
+ .field('user[email]', 'tobi@learnboost.com')
+ .field('friends[]', ['loki', 'jane'])
+ .attach('image', 'path/to/tobi.png')
+ .then(callback);
+```
+
+## 압축
+
+node 클라이언트는 압축된 응답을 지원하며, 아무 것도 하지 않아도 됩니다! 그냥 작동합니다.
+
+## 응답 버퍼링
+
+To force buffering of response bodies as `res.text` you may invoke `req.buffer()`. To undo the default of buffering for text responses such as "text/plain", "text/html" etc you may invoke `req.buffer(false)`.
+
+`.req.buffer()`를 호출하면 응답 본문을 `res.text`로 강제 버퍼링할 수 있습니다. "text/plain", "text/html" 등 텍스트 응답의 기본 버퍼링을 취소하려면 `.req.buffer(false)`를 호출하세요.
+
+`res.buffered` 플래그가 제공되면, 이를 활용하여 동일한 콜백 함수에서 버퍼링된 응답과 버퍼링되지 않은 응답을 모두 처리할 수 있습니다.
+
+## CORS
+
+보안상의 이유로 브라우저는 서버가 CORS 헤더를 통해 명시적으로 허용하지 않으면 교차 출처 요청(cross-origin requests)을 차단합니다. 브라우저는 또한 서버가 어떤 HTTP 헤더와 메서드를 허용하는지 확인하기 위해 추가적인 **OPTIONS** 요청을 전송합니다. [CORS에 대해 더 알아보기](https://developer.mozilla.org/ko/docs/Web/HTTP/Guides/CORS).
+
+`.withCredentials()` 메서드는 origin(출처)에서 쿠키를 전송할 수 있도록 활성화합니다. 단, 이 기능은 `Access-Control-Allow-Origin` 값이 와일드카드("\*")가 _아니어야_ 하며, `Access-Control-Allow-Credentials` 값이 `"true"`일 경우에만 작동합니다.
+
+```javascript
+request
+ .get('https://api.example.com:4001/')
+ .withCredentials()
+ .then((res) => {
+ assert.equal(200, res.status);
+ assert.equal('tobi', res.text);
+ });
+```
+
+## 오류 처리하기
+
+콜백 함수는 항상 두 개의 인자를 전달합니다. 오류와 응답입니다. 오류가 발생하지 않으면, 첫 번째 인자는 null 입니다.
+
+```javascript
+request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .then((res) => {});
+```
+
+"error" 이벤트도 발생하며, 이를 통해 오류를 감지하고 처리할 수 있습니다.
+
+```javascript
+request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .on('error', handle)
+ .then((res) => {});
+```
+
+**SuperAgent는 기본적으로 4xx 및 5xx 응답(그리고 처리되지 않은 3xx 응답도 포함)을 오류**로 간주합니다. 예를 들어 `304 Not Modified`, `403 Forbidden`, `500 Internal Server Error` 같은 응답을 받으면 해당 상태 정보는 `err.status`를 통해 확인할 수 있습니다. 이러한 응답으로부터 발생한 오류에는 "[응답 요소](#response-properties)"에서 언급한 모든 속성을 포함한 `err.response` 필드도 포함됩니다. 이 라이브러리는 일반적으로 성공 응답만을 원하고, HTTP 오류 상태 코드를 오류로 처리하는 경우를 대비하여 이러한 방식으로 동작합니다. 하지만 특정 오류 조건에 대해서는 사용자 정의 로직을 허용하도록 설계되어 있습니다.
+
+네트워크 실패, 시간초과, 응답 없는 오류는 `err.status` 또는 `err.response` 필드를 포함하지 않습니다.
+
+404 또는 HTTP 오류 응답을 처리하고 싶다면, `error.status` 요소를 사용할 수 있습니다. HTTP 오류(4xx 또는 5xx 응답)가 발생했을 때 `res.error` 요소는 `Error` 객체이고 이는 다음과 같이 에러 확인을 수행할 수 있습니다.
+
+```javascript
+if (err && err.status === 404) {
+ alert('oh no ' + res.body.message);
+} else if (err) {
+ // 그 외 다른 모든 오류 유형은 일반적으로 처리합니다
+}
+```
+
+대안으로, `.ok(callback)` 메서드를 사용하여 응답이 오류인지 아닌지 결정할 수 있습니다. `ok` 콜백은 응답을 받고 응답이 성공으로 해석되면 `true`를 반환합니다.
+
+```javascript
+request
+ .get('/404')
+ .ok((res) => res.status < 500)
+ .then((response) => {
+ // 404 페이지를 성공적인 응답으로 처리합니다
+ });
+```
+
+## 진행과정 추적하기
+
+SuperAgent는 업로드와 큰 파일 다운로드에서 `progress` 이벤트를 동작시킵니다.
+
+```javascript
+request
+ .post(url)
+ .attach('field_name', file)
+ .on('progress', (event) => {
+ /* 이벤트 객체는 다음과 같습니다.
+ {
+ direction: "upload" or "download"
+ percent: 0 to 100 // 0에서 100까지 (파일 크기를 알 수 없는 경우 생략될 수 있습니다)
+ total: // 전체 파일 크기 (생략될 수 있습니다)
+ loaded: // 현재까지 다운로드되거나 업로드된 바이트 수
+ } */
+ })
+ .then();
+```
+
+## 로컬 호스트에서 테스트하기
+
+### 특정 IP 주소 연결 설정하기
+
+In Node.js it's possible to ignore DNS resolution and direct all requests to a specific IP address using `.connect()` method. For example, this request will go to localhost instead of `example.com`:
+
+Node.js에서는 DNS를 무시하고 `.connect()` 메서드를 사용하여 모든 요청을 특정 IP 주소로 직접 연결할 수 있습니다. 예를 들어, 이 요청은 `example.com` 대신 로컬호스트로 전달됩니다.
+
+```javascript
+const res = await request.get('http://example.com').connect('127.0.0.1');
+```
+
+요청은 리다이렉트 되어, 여러 호스트명과 IP를 특정지을 수 있으며 특별한 `*`를 대체로 설정할 수 있습니다. (다른 와일드 카드는 지원되지 않습니다). 요청은 원본 값을 가지며 본인의 `Host` 헤더를 유지합니다. `.connect(undefined)`는 이러한 기능을 끕니다.
+
+```javascript
+const res = await request.get('http://redir.example.com:555').connect({
+ 'redir.example.com': '127.0.0.1', // redir.example.com:555는 127.0.0.1:555를 사용합니다.
+ 'www.example.com': false, // 이 항목은 재정의하지 마세요. 일반적인 DNS 설정을 사용합니다.
+ 'mapped.example.com': { host: '127.0.0.1', port: 8080 }, // mapped.example.com의 모든 포트는 127.0.0.1:8080으로 매핑됩니다.
+ '*': 'proxy.example.com' // 나머지 모든 요청은 이 호스트로 전달됩니다
+});
+```
+
+### 로컬 호스트에서 깨지거나 보안되지 않은 HTTPS 무시하기
+
+Node.js에서 HTTPS 설정이 잘못되었거나 보안성이 떨어지는 경우(예: 자체 서명된 인증서를 사용하면서 `.ca()`를 지정하지 않은 경우), `.trustLocalhost()`를 호출하면 `localhost`로의 요청을 허용할 수 있습니다.
+
+```javascript
+const res = await request.get('https://localhost').trustLocalhost();
+```
+
+`.connect("127.0.0.1")`와 함께 사용하면 HTTPS 요청을 어떤 도메인에서든 `localhost`로 강제로 리다이렉트할 수 있습니다.
+
+`localhost`는 신뢰되지 않은 네트워크에 노출되지 않는 루프백 인터페이스이기 때문에, 깨진 HTTPS를 무시하는 것이 일반적으로 안전합니다. `localhost`를 신뢰하도록 설정하는 것이 향후 기본값이 될 수 있습니다. `127.0.0.1`의 진위 여부를 강제로 검사하려면 `.trustLocalhost(false)`를 사용하세요.
+
+다른 IP 주소로 요청을 보낼 때 HTTPS 보안을 비활성화하는 기능은 의도적으로 지원하지 않습니다. 이러한 옵션은 HTTPS 문제를 빠르게 "해결"하려는 방식으로 오용되는 경우가 많기 때문입니다. [Let's Encrypt](https://certbot.eff.org)를 통해 무료 HTTPS 인증서를 발급받거나, `.ca(ca_public_pem)`을 사용해 자체 서명된 인증서를 신뢰할 수 있도록 직접 CA를 설정할 수 있습니다.
+
+## Promise 및 Generator 지원
+
+SuperAgent의 요청은 "thenable" 객체이며, JavaScript의 Promise 및 `async`/`await` 문법과 호환됩니다.
+
+```javascript
+const res = await request.get(url);
+```
+
+Promise를 사용할 경우, **`.end()` 또는 `.pipe()`를 호출하지 마세요**. `.then()` 또는 `await`를 사용하면 요청을 처리할 수 있는 다른 방식들이 모두 비활성화됩니다.
+
+[co](https://github.com/tj/co)와 같은 라이브러리나 [koa](https://github.com/koajs/koa)와 같은 웹 프레임워크에서는 SuperAgent의 모든 메서드에서 `yield`를 사용할 수 있습니다.
+
+```javascript
+ const req = request
+ .get('http://local')
+ .auth('tobi', 'learnboost');
+ const res = yield req;
+```
+
+SuperAgent는 전역 `Promise` 객체가 존재하는 환경에서 동작하도록 설계되어 있습니다. Internet Explorer나 Node.js 0.10에서 promise를 사용하려면 v7 버전과 폴리필이 필요합니다.
+
+v8 버전부터는 IE에 대한 지원이 중단되었습니다. Opera 85나 iOS Safari 12.2–12.5 등을 지원하려면 WeakRef와 BigInt에 대한 폴리필을 추가해야 합니다. 예를 들어
을 사용할 수 있습니다.
+
+```html
+
+```
+
+## 브라우저와 node 버전
+
+SuperAgent에는 두 가지 구현 방식이 있습니다. 하나는 웹 브라우저용(XHR 사용)이고, 다른 하나는 Node.JS용(core http 모듈 사용)입니다. 기본적으로 Browserify와 WebPack은 브라우저 버전을 선택합니다.
+
+Node.JS용 코드를 컴파일하려면 WebPack 설정에서 반드시 [node target](https://webpack.github.io/docs/configuration.html#target)을 지정해야 합니다.
diff --git a/docs/style.css b/docs/style.css
new file mode 100644
index 000000000..958428013
--- /dev/null
+++ b/docs/style.css
@@ -0,0 +1,88 @@
+body {
+ padding: 40px 80px;
+ font: 14px/1.5 "Helvetica Neue", Helvetica, sans-serif;
+ background: #181818 url(images/bg.png);
+ text-align: center;
+}
+
+#content {
+ margin: 0 auto;
+ padding: 10px 40px;
+ text-align: left;
+ background: white;
+ width: 50%;
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+ border-radius: 2px;
+ -webkit-box-shadow: 0 2px 5px 0 black;
+}
+
+#menu {
+ font-size: 13px;
+ margin: 0;
+ padding: 0;
+ text-align: left;
+ position: fixed;
+ top: 15px;
+ left: 15px;
+}
+
+#menu ul {
+ margin: 0;
+ padding: 0;
+}
+
+#menu li {
+ list-style: none;
+}
+
+#menu a {
+ color: rgba(255,255,255,.5);
+ text-decoration: none;
+}
+
+#menu a:hover {
+ color: white;
+}
+
+#menu .active a {
+ color: white;
+}
+
+pre {
+ padding: 10px;
+}
+
+code {
+ font-family: monaco, monospace, sans-serif;
+ font-size: 0.85em;
+}
+
+p code {
+ border: 1px solid #ECEA75;
+ padding: 1px 3px;
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+ border-radius: 2px;
+ background: #FDFCD1;
+}
+
+pre {
+ padding: 20px 25px;
+ border: 1px solid #ddd;
+ -webkit-box-shadow: inset 0 0 5px #eee;
+ -moz-box-shadow: inset 0 0 5px #eee;
+ box-shadow: inset 0 0 5px #eee;
+ overflow: scroll;
+}
+
+code .comment { color: #ddd }
+code .init { color: #2F6FAD }
+code .string { color: #5890AD }
+code .keyword { color: #8A6343 }
+code .number { color: #2F6FAD }
+
+/* override tocbot style to avoid vertical white line in table of content */
+.toc-link::before {
+ content: initial;
+}
diff --git a/docs/tail.html b/docs/tail.html
new file mode 100644
index 000000000..e88720bbe
--- /dev/null
+++ b/docs/tail.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
diff --git a/docs/test.html b/docs/test.html
new file mode 100644
index 000000000..75d5f45de
--- /dev/null
+++ b/docs/test.html
@@ -0,0 +1,9173 @@
+
+
+
+
+ SuperAgent — elegant API for AJAX in Node and browsers
+
+
+
+
+
+
+test on node with http1
+test on plain node
+
+ Agent
+
+ should remember defaults
+ if (typeof Promise === 'undefined') {
+ return;
+}
+let called = 0;
+let event_called = 0;
+const agent = request
+ .agent()
+ .accept('json')
+ .use(() => {
+ called++;
+ })
+ .once('request', () => {
+ event_called++;
+ })
+ .query({ hello: 'world' })
+ .set('X-test', 'testing');
+assert.equal(0, called);
+assert.equal(0, event_called);
+return agent
+ .get(`${base}/echo`)
+ .then((res) => {
+ assert.equal(1, called);
+ assert.equal(1, event_called);
+ assert.equal('application/json', res.headers.accept);
+ assert.equal('testing', res.headers['x-test']);
+ return agent.get(`${base}/querystring`);
+ })
+ .then((res) => {
+ assert.equal(2, called);
+ assert.equal(2, event_called);
+ assert.deepEqual({ hello: 'world' }, res.body);
+ });
+
+
+
+ request
+
+
+ res.statusCode
+
+ should set statusCode
+ request.get(`${uri}/login`, (error, res) => {
+ try {
+ assert.strictEqual(res.statusCode, 200);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ should allow the send shorthand
+
+ with callback in the method call
+ request.get(`${uri}/login`, (error, res) => {
+ assert.equal(res.status, 200);
+ done();
+});
+ with data in the method call
+ request.post(`${uri}/echo`, { foo: 'bar' }).end((error, res) => {
+ assert.equal('{"foo":"bar"}', res.text);
+ done();
+});
+ with callback and data in the method call
+ request.post(`${uri}/echo`, { foo: 'bar' }, (error, res) => {
+ assert.equal('{"foo":"bar"}', res.text);
+ done();
+});
+
+
+
+ with a callback
+
+ should invoke .end()
+ request.get(`${uri}/login`, (error, res) => {
+ try {
+ assert.equal(res.status, 200);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ .end()
+
+ should issue a request
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert.equal(res.status, 200);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+ is optional with a promise
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/login`)
+ .then((res) => res.status)
+ .then()
+ .then((status) => {
+ assert.equal(200, status, 'Real promises pass results through');
+ });
+ called only once with a promise
+ if (typeof Promise === 'undefined') {
+ return;
+}
+const request_ = request.get(`${uri}/unique`);
+return Promise.all([request_, request_, request_]).then((results) => {
+ for (const item of results) {
+ assert.deepEqual(
+ item.body,
+ results[0].body,
+ 'It should keep returning the same result after being called once'
+ );
+ }
+});
+
+
+
+ res.error
+
+ ok
+ let calledErrorEvent = false;
+let calledOKHandler = false;
+request
+ .get(`${uri}/error`)
+ .ok((res) => {
+ assert.strictEqual(500, res.status);
+ calledOKHandler = true;
+ return true;
+ })
+ .on('error', (error) => {
+ calledErrorEvent = true;
+ })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.strictEqual(res.status, 500);
+ assert(!calledErrorEvent);
+ assert(calledOKHandler);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should be an Error object
+ let calledErrorEvent = false;
+request
+ .get(`${uri}/error`)
+ .on('error', (error) => {
+ assert.strictEqual(error.status, 500);
+ calledErrorEvent = true;
+ })
+ .end((error, res) => {
+ try {
+ if (NODE) {
+ res.error.message.should.equal('cannot GET /error (500)');
+ } else {
+ res.error.message.should.equal(`cannot GET ${uri}/error (500)`);
+ }
+ assert.strictEqual(res.error.status, 500);
+ assert(error, 'should have an error for 500');
+ assert.equal(error.message, 'Internal Server Error');
+ assert(calledErrorEvent);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ with .then() promise
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request.get(`${uri}/error`).then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(err.message, 'Internal Server Error');
+ }
+);
+ with .ok() returning false
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/echo`)
+ .ok(() => false)
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'OK');
+ }
+ );
+ with .ok() throwing an Error
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/echo`)
+ .ok(() => {
+ throw new Error('boom');
+ })
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(200, err.status);
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'boom');
+ }
+ );
+ with .ok() throwing an Error with status
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/echo`)
+ .ok(() => {
+ const err = new Error('boom');
+ err.status = 404;
+ throw err;
+ })
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(404, err.status);
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'boom');
+ }
+ );
+
+
+
+ res.header
+
+ should be an object
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert.equal('Express', res.header['x-powered-by']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ set headers
+
+ should only set headers for ownProperties of header
+ try {
+ request
+ .get(`${uri}/echo-headers`)
+ .set('valid', 'ok')
+ .end((error, res) => {
+ if (
+ !error &&
+ res.body &&
+ res.body.valid &&
+ !res.body.hasOwnProperty('invalid')
+ ) {
+ return done();
+ }
+ done(error || new Error('fail'));
+ });
+} catch (err) {
+ done(err);
+}
+
+
+
+ res.charset
+
+ should be set when present
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ res.charset.should.equal('utf-8');
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ res.statusType
+
+ should provide the first digit
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert(!error, 'should not have an error for success responses');
+ assert.equal(200, res.status);
+ assert.equal(2, res.statusType);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ res.type
+
+ should provide the mime-type void of params
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ res.type.should.equal('text/html');
+ res.charset.should.equal('utf-8');
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ req.set(field, val)
+
+ should set the header field
+ request
+ .post(`${uri}/echo`)
+ .set('X-Foo', 'bar')
+ .set('X-Bar', 'baz')
+ .end((error, res) => {
+ try {
+ assert.equal('bar', res.header['x-foo']);
+ assert.equal('baz', res.header['x-bar']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.set(obj)
+
+ should set the header fields
+ request
+ .post(`${uri}/echo`)
+ .set({ 'X-Foo': 'bar', 'X-Bar': 'baz' })
+ .end((error, res) => {
+ try {
+ assert.equal('bar', res.header['x-foo']);
+ assert.equal('baz', res.header['x-bar']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.type(str)
+
+ should set the Content-Type
+ request
+ .post(`${uri}/echo`)
+ .type('text/x-foo')
+ .end((error, res) => {
+ try {
+ res.header['content-type'].should.equal('text/x-foo');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "json"
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('{"a": 1}')
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "html"
+ request
+ .post(`${uri}/echo`)
+ .type('html')
+ .end((error, res) => {
+ try {
+ res.header['content-type'].should.equal('text/html');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.accept(str)
+
+ should set Accept
+ request
+ .get(`${uri}/echo`)
+ .accept('text/x-foo')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('text/x-foo');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "json"
+ request
+ .get(`${uri}/echo`)
+ .accept('json')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('application/json');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "xml"
+ request
+ .get(`${uri}/echo`)
+ .accept('xml')
+ .end((error, res) => {
+ try {
+ // Mime module keeps changing this :(
+ assert(
+ res.header.accept == 'application/xml' ||
+ res.header.accept == 'text/xml'
+ );
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "html"
+ request
+ .get(`${uri}/echo`)
+ .accept('html')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('text/html');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.send(str)
+
+ should write the string
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('{"name":"tobi"}')
+ .end((error, res) => {
+ try {
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.send(Object)
+
+ should default to json
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ when called several times
+
+ should merge the objects
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .send({ age: 1 })
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ if (NODE) {
+ res.buffered.should.be.true();
+ }
+ res.text.should.equal('{"name":"tobi","age":1}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+
+
+ .end(fn)
+
+ should check arity
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should emit request
+ const request_ = request.post(`${uri}/echo`);
+request_.on('request', (request) => {
+ assert.equal(request_, request);
+ done();
+});
+request_.end();
+ should emit response
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .on('response', (res) => {
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ })
+ .end();
+
+
+
+ .then(fulfill, reject)
+
+ should support successful fulfills with .then(fulfill)
+ if (typeof Promise === 'undefined') {
+ return done();
+}
+request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .then((res) => {
+ res.type.should.equal('application/json');
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ });
+ should reject an error with .then(null, reject)
+ if (typeof Promise === 'undefined') {
+ return done();
+}
+request.get(`${uri}/error`).then(null, (err) => {
+ assert.equal(err.status, 500);
+ assert.equal(err.response.text, 'boom');
+ done();
+});
+
+
+
+ .catch(reject)
+
+ should reject an error with .catch(reject)
+ if (typeof Promise === 'undefined') {
+ return done();
+}
+request.get(`${uri}/error`).catch((err) => {
+ assert.equal(err.status, 500);
+ assert.equal(err.response.text, 'boom');
+ done();
+});
+
+
+
+ .abort()
+
+ should abort the request
+ const request_ = request.get(`${uri}/delay/3000`);
+request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+});
+request_.on('error', (error) => {
+ done(error);
+});
+request_.on('abort', done);
+setTimeout(() => {
+ request_.abort();
+}, 500);
+ should abort the promise
+ const request_ = request.get(`${uri}/delay/3000`);
+setTimeout(() => {
+ request_.abort();
+}, 10);
+return request_.then(
+ () => {
+ assert.fail('should not complete the request');
+ },
+ (err) => {
+ assert.equal('ABORTED', err.code);
+ }
+);
+ should allow chaining .abort() several times
+ const request_ = request.get(`${uri}/delay/3000`);
+request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+});
+// This also verifies only a single 'done' event is emitted
+request_.on('abort', done);
+setTimeout(() => {
+ request_.abort().abort().abort();
+}, 1000);
+ should not allow abort then end
+ request
+ .get(`${uri}/delay/3000`)
+ .abort()
+ .end((error, res) => {
+ done(error ? undefined : new Error('Expected abort error'));
+ });
+
+
+
+ req.toJSON()
+
+ should describe the request
+ const request_ = request.post(`${uri}/echo`).send({ foo: 'baz' });
+request_.end((error, res) => {
+ try {
+ const json = request_.toJSON();
+ assert.equal('POST', json.method);
+ assert(/\/echo$/.test(json.url));
+ assert.equal('baz', json.data.foo);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ req.options()
+
+ should allow request body
+ request
+ .options(`${uri}/options/echo/body`)
+ .send({ foo: 'baz' })
+ .end((error, res) => {
+ try {
+ assert.equal(error, null);
+ assert.strictEqual(res.body.foo, 'baz');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.sortQuery()
+
+ nop with no querystring
+ request
+ .get(`${uri}/url`)
+ .sortQuery()
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should sort the request querystring
+ request
+ .get(`${uri}/url`)
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery()
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?order=desc&search=Manny');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should allow disabling sorting
+ request
+ .get(`${uri}/url`)
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery() // take default of true
+ .sortQuery(false) // override it in later call
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?search=Manny&order=desc');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should sort the request querystring using customized function
+ request
+ .get(`${uri}/url`)
+ .query('name=Nick')
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery((a, b) => a.length - b.length)
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?name=Nick&order=desc&search=Manny');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+
+
+ req.set("Content-Type", contentType)
+
+ should work with just the contentType component
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json')
+ .send({ name: 'tobi' })
+ .end((error) => {
+ assert(!error);
+ done();
+ });
+ should work with the charset component
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json; charset=utf-8')
+ .send({ name: 'tobi' })
+ .end((error) => {
+ assert(!error);
+ done();
+ });
+
+
+
+ req.send(Object) as "form"
+
+
+ with req.type() set to form
+
+ should send x-www-form-urlencoded data
+ request
+ .post(`${base}/echo`)
+ .type('form')
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.text.should.equal('name=tobi');
+ done();
+ });
+
+
+
+ when called several times
+
+ should merge the objects
+ request
+ .post(`${base}/echo`)
+ .type('form')
+ .send({ name: { first: 'tobi', last: 'holowaychuk' } })
+ .send({ age: '1' })
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.text.should.equal(
+ 'name%5Bfirst%5D=tobi&name%5Blast%5D=holowaychuk&age=1'
+ );
+ done();
+ });
+
+
+
+
+
+ req.attach
+
+ ignores null file
+ request
+ .post('/echo')
+ .attach('image', null)
+ .end((error, res) => {
+ done();
+ });
+
+
+
+ req.field
+
+ allow bools
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field('bools', true)
+ .field('strings', 'true')
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { bools: 'true', strings: 'true' });
+ done();
+ });
+ allow objects
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field({ bools: true, strings: 'true' })
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { bools: 'true', strings: 'true' });
+ done();
+ });
+ works with arrays in objects
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field({ numbers: [1, 2, 3] })
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { numbers: ['1', '2', '3'] });
+ done();
+ });
+ works with arrays
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field('letters', ['a', 'b', 'c'])
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { letters: ['a', 'b', 'c'] });
+ done();
+ });
+ throw when empty
+ should.throws(() => {
+ request.post(`${base}/echo`).field();
+}, /name/);
+should.throws(() => {
+ request.post(`${base}/echo`).field('name');
+}, /val/);
+ cannot be mixed with send()
+ assert.throws(() => {
+ request.post('/echo').field('form', 'data').send('hi');
+});
+assert.throws(() => {
+ request.post('/echo').send('hi').field('form', 'data');
+});
+
+
+
+ req.send(Object) as "json"
+
+ should default to json
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ });
+ should work with arrays
+ request
+ .post(`${uri}/echo`)
+ .send([1, 2, 3])
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('[1,2,3]');
+ done();
+ });
+ should work with value null
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('null')
+ .end((error, res) => {
+ res.should.be.json();
+ assert.strictEqual(res.body, null);
+ done();
+ });
+ should work with value false
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('false')
+ .end((error, res) => {
+ res.should.be.json();
+ res.body.should.equal(false);
+ done();
+ });
+ should work with empty string value
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('""')
+ .end((error, res) => {
+ res.should.be.json();
+ res.body.should.equal('');
+ done();
+ });
+ should work with vendor MIME type
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/vnd.example+json')
+ .send({ name: 'vendor' })
+ .end((error, res) => {
+ res.text.should.equal('{"name":"vendor"}');
+ ({ name: 'vendor' }.should.eql(res.body));
+ done();
+ });
+
+ when called several times
+
+ should merge the objects
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .send({ age: 1 })
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi","age":1}');
+ ({ name: 'tobi', age: 1 }.should.eql(res.body));
+ done();
+ });
+
+
+
+
+
+ res.body
+
+
+ application/json
+
+ should parse the body
+ request.get(`${uri}/json`).end((error, res) => {
+ res.text.should.equal('{"name":"manny"}');
+ res.body.should.eql({ name: 'manny' });
+ done();
+});
+
+
+
+ Invalid JSON response
+
+ should return the raw response
+ request.get(`${uri}/invalid-json`).end((error, res) => {
+ assert.deepEqual(
+ error.rawResponse,
+ ")]}', {'header':{'code':200,'text':'OK','version':'1.0'},'data':'some data'}"
+ );
+ done();
+});
+ should return the http status code
+ request.get(`${uri}/invalid-json-forbidden`).end((error, res) => {
+ assert.equal(error.statusCode, 403);
+ done();
+});
+
+
+
+
+
+ request
+
+
+ on redirect
+
+ should retain header fields
+ request
+ .get(`${base}/header`)
+ .set('X-Foo', 'bar')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should preserve timeout across redirects
+ request
+ .get(`${base}/movies/random`)
+ .timeout(250)
+ .end((error, res) => {
+ try {
+ assert(error instanceof Error, 'expected an error');
+ error.should.have.property('timeout', 250);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should successfully redirect after retry on error
+ const id = Math.random() * 1_000_000 * Date.now();
+request
+ .get(`${base}/error/redirect/${id}`)
+ .retry(2)
+ .end((error, res) => {
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'first movie page');
+ done();
+ });
+ should preserve retries across redirects
+ const id = Math.random() * 1_000_000 * Date.now();
+request
+ .get(`${base}/error/redirect-error${id}`)
+ .retry(2)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ });
+
+
+
+ on 303
+
+ should redirect with same method
+ request
+ .put(`${base}/redirect-303`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+ res.text.should.equal('method=get');
+ done();
+ });
+
+
+
+ on 307
+
+ should redirect with same method
+ if (isMSIE) return done(); // IE9 broken
+request
+ .put(`${base}/redirect-307`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+ res.text.should.equal('method=put');
+ done();
+ });
+
+
+
+ on 308
+
+ should redirect with same method
+ if (isMSIE) return done(); // IE9 broken
+request
+ .put(`${base}/redirect-308`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+ res.text.should.equal('method=put');
+ done();
+ });
+
+
+
+
+
+ request
+
+ Request inheritance
+ assert(request.get(`${uri}/`) instanceof request.Request);
+ request() simple GET without callback
+ request('GET', 'test/test.request.js').end();
+next();
+ request() simple GET
+ request('GET', `${uri}/ok`).end((error, res) => {
+ try {
+ assert(res instanceof request.Response, 'respond with Response');
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'res.text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() simple HEAD
+ request.head(`${uri}/ok`).end((error, res) => {
+ try {
+ assert(res instanceof request.Response, 'respond with Response');
+ assert(res.ok, 'response should be ok');
+ assert(!res.text, 'res.text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 5xx
+ request('GET', `${uri}/error`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(error.message, 'Internal Server Error');
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(!res.clientError, 'response should not be a client error');
+ assert(res.serverError, 'response should be a server error');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 4xx
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(error.message, 'Not Found');
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(res.clientError, 'response should be a client error');
+ assert(!res.serverError, 'response should not be a server error');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 404 Not Found
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.notFound, 'response should be .notFound');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 400 Bad Request
+ request('GET', `${uri}/bad-request`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.badRequest, 'response should be .badRequest');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 401 Bad Request
+ request('GET', `${uri}/unauthorized`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.unauthorized, 'response should be .unauthorized');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 406 Not Acceptable
+ request('GET', `${uri}/not-acceptable`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.notAcceptable, 'response should be .notAcceptable');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 204 No Content
+ request('GET', `${uri}/no-content`).end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() DELETE 204 No Content
+ request('DELETE', `${uri}/no-content`).end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() header parsing
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal('text/html; charset=utf-8', res.header['content-type']);
+ assert.equal('Express', res.header['x-powered-by']);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() .status
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(404, res.status, 'response .status');
+ assert.equal(4, res.statusType, 'response .statusType');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ get()
+ request.get(`${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(404, res.status, 'response .status');
+ assert.equal(4, res.statusType, 'response .statusType');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ put()
+ request.put(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('updated', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ put().send()
+ request
+ .put(`${uri}/user/13/body`)
+ .send({ user: 'new' })
+ .end((error, res) => {
+ try {
+ assert.equal('received new', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ post()
+ request.post(`${uri}/user`).end((error, res) => {
+ try {
+ assert.equal('created', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ del()
+ request.del(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('deleted', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ delete()
+ request.delete(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('deleted', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ post() data
+ request
+ .post(`${uri}/todo/item`)
+ .type('application/octet-stream')
+ .send('tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .type()
+ request
+ .post(`${uri}/user/12/pet`)
+ .type('urlencoded')
+ .send('pet=tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .type() with alias
+ request
+ .post(`${uri}/user/12/pet`)
+ .type('application/x-www-form-urlencoded')
+ .send('pet=tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .get() with no data or callback
+ request.get(`${uri}/echo-header/content-type`);
+next();
+ request .send() with no data only
+ request.post(`${uri}/user/5/pet`).type('urlencoded').send('pet=tobi');
+next();
+ request .send() with callback only
+ request
+ .get(`${uri}/echo-header/accept`)
+ .set('Accept', 'foo/bar')
+ .end((error, res) => {
+ try {
+ assert.equal('foo/bar', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with json
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('json')
+ .end((error, res) => {
+ try {
+ assert.equal('application/json', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with application/json
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('application/json')
+ .end((error, res) => {
+ try {
+ assert.equal('application/json', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with xml
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('xml')
+ .end((error, res) => {
+ try {
+ // We can't depend on mime module to be consistent with this
+ assert(res.text == 'application/xml' || res.text == 'text/xml');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with application/xml
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('application/xml')
+ .end((error, res) => {
+ try {
+ assert.equal('application/xml', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .end()
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .send()
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .set()
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .set(object)
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set({ 'Content-Type': 'text/plain' })
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST urlencoded
+ request
+ .post(`${uri}/pet`)
+ .type('urlencoded')
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json
+ request
+ .post(`${uri}/pet`)
+ .type('json')
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json array
+ request
+ .post(`${uri}/echo`)
+ .send([1, 2, 3])
+ .end((error, res) => {
+ try {
+ assert.equal(
+ 'application/json',
+ res.header['content-type'].split(';')[0]
+ );
+ assert.equal('[1,2,3]', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json default
+ request
+ .post(`${uri}/pet`)
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json contentType charset
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json; charset=UTF-8')
+ .send({ data: ['data1', 'data2'] })
+ .end((error, res) => {
+ try {
+ assert.equal('{"data":["data1","data2"]}', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json contentType vendor
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/vnd.example+json')
+ .send({ data: ['data1', 'data2'] })
+ .end((error, res) => {
+ try {
+ assert.equal('{"data":["data1","data2"]}', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST multiple .send() calls
+ request
+ .post(`${uri}/pet`)
+ .send({ name: 'Manny' })
+ .send({ species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST multiple .send() strings
+ request
+ .post(`${uri}/echo`)
+ .send('user[name]=tj')
+ .send('user[email]=tj@vision-media.ca')
+ .end((error, res) => {
+ try {
+ assert.equal(
+ 'application/x-www-form-urlencoded',
+ res.header['content-type'].split(';')[0]
+ );
+ assert.equal(
+ res.text,
+ 'user[name]=tj&user[email]=tj@vision-media.ca'
+ );
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST with no data
+ request
+ .post(`${uri}/empty-body`)
+ .send()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET .type
+ request.get(`${uri}/pets`).end((error, res) => {
+ try {
+ assert.equal('application/json', res.type);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET Content-Type params
+ request.get(`${uri}/text`).end((error, res) => {
+ try {
+ assert.equal('utf-8', res.charset);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET json
+ request.get(`${uri}/pets`).end((error, res) => {
+ try {
+ assert.deepEqual(res.body, ['tobi', 'loki', 'jane']);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET json-seq
+ request
+ .get(`${uri}/json-seq`)
+ .buffer()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.deepEqual(res.text, '\u001E{"id":1}\n\u001E{"id":2}\n');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET binary data
+ request
+ .get(`${uri}/binary-data`)
+ .buffer()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.deepEqual(res.body, binData);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET x-www-form-urlencoded
+ request.get(`${uri}/foo`).end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { foo: 'bar' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET shorthand
+ request.get(`${uri}/foo`, (error, res) => {
+ try {
+ assert.equal('foo=bar', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ POST shorthand
+ request.post(`${uri}/user/0/pet`, { pet: 'tobi' }, (error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ POST shorthand without callback
+ request.post(`${uri}/user/0/pet`, { pet: 'tobi' }).end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET querystring object with array
+ request
+ .get(`${uri}/querystring`)
+ .query({ val: ['a', 'b', 'c'] })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { val: ['a', 'b', 'c'] });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring object with array and primitives
+ request
+ .get(`${uri}/querystring`)
+ .query({ array: ['a', 'b', 'c'], string: 'foo', number: 10 })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ array: ['a', 'b', 'c'],
+ string: 'foo',
+ number: 10
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring object with two arrays
+ request
+ .get(`${uri}/querystring`)
+ .query({ array1: ['a', 'b', 'c'], array2: [1, 2, 3] })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ array1: ['a', 'b', 'c'],
+ array2: [1, 2, 3]
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring object
+ request
+ .get(`${uri}/querystring`)
+ .query({ search: 'Manny' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { search: 'Manny' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring append original
+ request
+ .get(`${uri}/querystring?search=Manny`)
+ .query({ range: '1..5' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { search: 'Manny', range: '1..5' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring multiple objects
+ request
+ .get(`${uri}/querystring`)
+ .query({ search: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring with strings
+ request
+ .get(`${uri}/querystring`)
+ .query('search=Manny')
+ .query('range=1..5')
+ .query('order=desc')
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring with strings and objects
+ request
+ .get(`${uri}/querystring`)
+ .query('search=Manny')
+ .query({ order: 'desc', range: '1..5' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET shorthand payload goes to querystring
+ request.get(
+ `${uri}/querystring`,
+ { foo: 'FOO', bar: 'BAR' },
+ (error, res) => {
+ try {
+ assert.deepEqual(res.body, { foo: 'FOO', bar: 'BAR' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ }
+);
+ HEAD shorthand payload goes to querystring
+ request.head(
+ `${uri}/querystring-in-header`,
+ { foo: 'FOO', bar: 'BAR' },
+ (error, res) => {
+ try {
+ assert.deepEqual(JSON.parse(res.headers.query), {
+ foo: 'FOO',
+ bar: 'BAR'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ }
+);
+ request(method, url)
+ request('GET', `${uri}/foo`).end((error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request(url)
+ request(`${uri}/foo`).end((error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request(url, fn)
+ request(`${uri}/foo`, (error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ req.timeout(ms)
+ const request_ = request.get(`${uri}/delay/3000`).timeout(1000);
+request_.end((error, res) => {
+ try {
+ assert(error, 'error missing');
+ assert.equal(1000, error.timeout, 'err.timeout missing');
+ assert.equal(
+ 'Timeout of 1000ms exceeded',
+ error.message,
+ 'err.message incorrect'
+ );
+ assert.equal(null, res);
+ assert(request_.timedout, true);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ req.timeout(ms) with redirect
+ const request_ = request.get(`${uri}/delay/const`).timeout(1000);
+request_.end((error, res) => {
+ try {
+ assert(error, 'error missing');
+ assert.equal(1000, error.timeout, 'err.timeout missing');
+ assert.equal(
+ 'Timeout of 1000ms exceeded',
+ error.message,
+ 'err.message incorrect'
+ );
+ assert.equal(null, res);
+ assert(request_.timedout, true);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request event
+ request
+ .get(`${uri}/foo`)
+ .on('request', (request_) => {
+ try {
+ assert.equal(`${uri}/foo`, request_.url);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ })
+ .end();
+ response event
+ request
+ .get(`${uri}/foo`)
+ .on('response', (res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ })
+ .end();
+ response should set statusCode
+ request.get(`${uri}/ok`, (error, res) => {
+ try {
+ assert.strictEqual(res.statusCode, 200);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ req.toJSON()
+ request.get(`${uri}/ok`).end((error, res) => {
+ try {
+ const index = (res.request || res.req).toJSON();
+ for (const property of ['url', 'method', 'data', 'headers']) {
+ assert(index.hasOwnProperty(property));
+ }
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+
+
+
+ .retry(count)
+
+ should not retry if passed "0"
+ request
+ .get(`${base}/error`)
+ .retry(0)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not retry if passed an invalid number
+ request
+ .get(`${base}/error`)
+ .retry(-2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not retry if passed undefined
+ request
+ .get(`${base}/error`)
+ .retry(undefined)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle server error after repeat attempt
+ request
+ .get(`${base}/error`)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should retry if passed nothing
+ request
+ .get(`${base}/error`)
+ .retry()
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(1, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should retry if passed "true"
+ request
+ .get(`${base}/error`)
+ .retry(true)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(1, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle successful request after repeat attempt from server error
+ request
+ .get(`${base}/error/ok/${uniqid()}`)
+ .query({ qs: 'present' })
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'res.text');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle server timeout error after repeat attempt
+ request
+ .get(`${base}/delay/400`)
+ .timeout(200)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle successful request after repeat attempt from server timeout
+ const url = `/delay/1200/ok/${uniqid()}?built=in`;
+request
+ .get(base + url)
+ .query('string=ified')
+ .query({ json: 'ed' })
+ .timeout(600)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert.equal(res.text, `ok = ${url}&string=ified&json=ed`);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle successful request after repeat attempt from server timeout when using .then(fulfill, reject)
+ const url = `/delay/1200/ok/${uniqid()}?built=in`;
+request
+ .get(base + url)
+ .query('string=ified')
+ .query({ json: 'ed' })
+ .timeout(600)
+ .retry(1)
+ .then((res, error) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert.equal(res.text, `ok = ${url}&string=ified&json=ed`);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should correctly abort a retry attempt
+ let aborted = false;
+const request_ = request.get(`${base}/delay/400`).timeout(200).retry(2);
+request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+});
+request_.on('abort', () => {
+ aborted = true;
+});
+setTimeout(() => {
+ request_.abort();
+ setTimeout(() => {
+ try {
+ assert(aborted, 'should be aborted');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ }, 150);
+}, 150);
+ should correctly retain header fields
+ request
+ .get(`${base}/error/ok/${uniqid()}`)
+ .query({ qs: 'present' })
+ .retry(2)
+ .set('X-Foo', 'bar')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not retry on 4xx responses
+ request
+ .get(`${base}/bad-request`)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(0, error.retries, 'expected an error with 0 .retries');
+ assert.equal(400, error.status, 'expected an error status of 400');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should execute callback on retry if passed
+ let callbackCallCount = 0;
+function retryCallback(request) {
+ callbackCallCount++;
+}
+request
+ .get(`${base}/error`)
+ .retry(2, retryCallback)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ assert.equal(
+ 2,
+ callbackCallCount,
+ 'expected the callback to be called on each retry'
+ );
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ .timeout(ms)
+
+
+ when timeout is exceeded
+
+ should error
+ request
+ .get(`${base}/delay/500`)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should error in promise interface
+ request
+ .get(`${base}/delay/500`)
+ .timeout(150)
+ .catch((err) => {
+ assert(err, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof err.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', err.code, 'expected abort error code');
+ done();
+ });
+ should handle gzip timeout
+ request
+ .get(`${base}/delay/zip`)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should handle buffer timeout
+ request
+ .get(`${base}/delay/json`)
+ .buffer(true)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should error on deadline
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ deadline: 150 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should support setting individual options
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ deadline: 10 })
+ .timeout({ response: 99_999 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ assert.equal('ETIME', error.errno);
+ done();
+ });
+ should error on response
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ response: 150 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ assert.equal('ETIMEDOUT', error.errno);
+ done();
+ });
+ should accept slow body with fast response
+ request
+ .get(`${base}/delay/slowbody`)
+ .timeout({ response: 1000 })
+ .on('progress', () => {
+ // This only makes the test faster without relying on arbitrary timeouts
+ request.get(`${base}/delay/slowbody/finish`).end();
+ })
+ .end(done);
+
+
+
+
+
+ request
+
+
+ use
+
+ should use plugin success
+ const now = `${Date.now()}`;
+function uuid(request_) {
+ request_.set('X-UUID', now);
+ return request_;
+}
+function prefix(request_) {
+ request_.url = uri + request_.url;
+ return request_;
+}
+request
+ .get('/echo')
+ .use(uuid)
+ .use(prefix)
+ .end((error, res) => {
+ assert.strictEqual(res.statusCode, 200);
+ assert.equal(res.get('X-UUID'), now);
+ done();
+ });
+
+
+
+
+
+ subclass
+
+ should be an instance of Request
+ const request_ = request.get('/');
+assert(request_ instanceof request.Request);
+ should use patched subclass
+ assert(OriginalRequest);
+let constructorCalled;
+let sendCalled;
+function NewRequest(...args) {
+ constructorCalled = true;
+ OriginalRequest.apply(this, args);
+}
+NewRequest.prototype = Object.create(OriginalRequest.prototype);
+NewRequest.prototype.send = function () {
+ sendCalled = true;
+ return this;
+};
+request.Request = NewRequest;
+const request_ = request.get('/').send();
+assert(constructorCalled);
+assert(sendCalled);
+assert(request_ instanceof NewRequest);
+assert(request_ instanceof OriginalRequest);
+ should use patched subclass in agent too
+ if (!request.agent) return; // Node-only
+function NewRequest(...args) {
+ OriginalRequest.apply(this, args);
+}
+NewRequest.prototype = Object.create(OriginalRequest.prototype);
+request.Request = NewRequest;
+const request_ = request.agent().del('/');
+assert(request_ instanceof NewRequest);
+assert(request_ instanceof OriginalRequest);
+
+
+
+ request
+
+
+ persistent agent
+
+ should gain a session on POST
+ agent3.post(`${base}/signin`).then((res) => {
+ res.should.have.status(200);
+ should.not.exist(res.headers['set-cookie']);
+ res.text.should.containEql('dashboard');
+ })
+ should start with empty session (set cookies)
+ agent1.get(`${base}/dashboard`).end((error, res) => {
+ should.exist(error);
+ res.should.have.status(401);
+ should.exist(res.headers['set-cookie']);
+ done();
+});
+ should gain a session (cookies already set)
+ agent1.post(`${base}/signin`).then((res) => {
+ res.should.have.status(200);
+ should.not.exist(res.headers['set-cookie']);
+ res.text.should.containEql('dashboard');
+ })
+ should persist cookies across requests
+ agent1.get(`${base}/dashboard`).then((res) => {
+ res.should.have.status(200);
+ })
+ should have the cookie set in the end callback
+ agent4
+ .post(`${base}/setcookie`)
+ .then(() => agent4.get(`${base}/getcookie`))
+ .then((res) => {
+ res.should.have.status(200);
+ assert.strictEqual(res.text, 'jar');
+ })
+ should not share cookies
+ agent2.get(`${base}/dashboard`).end((error, res) => {
+ should.exist(error);
+ res.should.have.status(401);
+ done();
+});
+ should not lose cookies between agents
+ agent1.get(`${base}/dashboard`).then((res) => {
+ res.should.have.status(200);
+ })
+ should be able to follow redirects
+ agent1.get(base).then((res) => {
+ res.should.have.status(200);
+ res.text.should.containEql('dashboard');
+ })
+ should be able to post redirects
+ agent1
+ .post(`${base}/redirect`)
+ .send({ foo: 'bar', baz: 'blaaah' })
+ .then((res) => {
+ res.should.have.status(200);
+ res.text.should.containEql('simple');
+ res.redirects.should.eql([`${base}/simple`]);
+ })
+ should be able to limit redirects
+ agent1
+ .get(base)
+ .redirects(0)
+ .end((error, res) => {
+ should.exist(error);
+ res.should.have.status(302);
+ res.redirects.should.eql([]);
+ res.header.location.should.equal('/dashboard');
+ done();
+ });
+ should be able to create a new session (clear cookie)
+ agent1.post(`${base}/signout`).then((res) => {
+ res.should.have.status(200);
+ should.exist(res.headers['set-cookie']);
+ })
+ should regenerate with an empty session
+ agent1.get(`${base}/dashboard`).end((error, res) => {
+ should.exist(error);
+ res.should.have.status(401);
+ should.not.exist(res.headers['set-cookie']);
+ done();
+});
+
+
+
+
+
+ Basic auth
+
+
+ when credentials are present in url
+
+ should set Authorization
+ const new_url = URL.parse(base);
+new_url.auth = 'tobi:learnboost';
+new_url.pathname = '/basic-auth';
+request.get(URL.format(new_url)).end((error, res) => {
+ res.status.should.equal(200);
+ done();
+});
+
+
+
+ req.auth(user, pass)
+
+ should set Authorization
+ request
+ .get(`${base}/basic-auth`)
+ .auth('tobi', 'learnboost')
+ .end((error, res) => {
+ res.status.should.equal(200);
+ done();
+ });
+
+
+
+ req.auth(user + ":" + pass)
+
+ should set authorization
+ request
+ .get(`${base}/basic-auth/again`)
+ .auth('tobi')
+ .end((error, res) => {
+ res.status.should.eql(200);
+ done();
+ });
+
+
+
+
+
+ [node] request
+
+ should send body with .get().send()
+ request
+ .get(`${base}/echo`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('wahoo', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+
+ with an url
+
+ should preserve the encoding of the url
+ request.get(`${base}/url?a=(b%29`).end((error, res) => {
+ assert.equal('/url?a=(b%29', res.text);
+ done();
+});
+
+
+
+ with an object
+
+ should format the url
+ request.get(url.parse(`${base}/login`)).then((res) => {
+ assert(res.ok);
+ })
+
+
+
+ without a schema
+
+ should default to http
+ request.get(`${base}/login`).then((res) => {
+ assert.equal(res.status, 200);
+ })
+
+
+
+ res.toJSON()
+
+ should describe the response
+ request
+ .post(`${base}/echo`)
+ .send({ foo: 'baz' })
+ .then((res) => {
+ const object = res.toJSON();
+ assert.equal('object', typeof object.header);
+ assert.equal('object', typeof object.req);
+ assert.equal(200, object.status);
+ assert.equal('{"foo":"baz"}', object.text);
+ })
+
+
+
+ res.links
+
+ should default to an empty object
+ request.get(`${base}/login`).then((res) => {
+ res.links.should.eql({});
+ })
+ should parse the Link header field
+ request.get(`${base}/links`).end((error, res) => {
+ res.links.next.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=2'
+ );
+ done();
+});
+
+
+
+ req.unset(field)
+
+ should remove the header field
+ request
+ .post(`${base}/echo`)
+ .unset('User-Agent')
+ .end((error, res) => {
+ assert.equal(void 0, res.header['user-agent']);
+ done();
+ });
+
+
+
+ case-insensitive
+
+ should set/get header fields case-insensitively
+ const r = request.post(`${base}/echo`);
+r.set('MiXeD', 'helloes');
+assert.strictEqual(r.get('mixed'), 'helloes');
+ should unset header fields case-insensitively
+ const r = request.post(`${base}/echo`);
+r.set('MiXeD', 'helloes');
+r.unset('MIXED');
+assert.strictEqual(r.get('mixed'), undefined);
+
+
+
+ req.write(str)
+
+ should write the given data
+ const request_ = request.post(`${base}/echo`);
+request_.set('Content-Type', 'application/json');
+assert.equal('boolean', typeof request_.write('{"name"'));
+assert.equal('boolean', typeof request_.write(':"tobi"}'));
+request_.end((error, res) => {
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+});
+
+
+
+ req.pipe(stream)
+
+ should pipe the response to the given stream
+ const stream = new EventEmitter();
+stream.buf = '';
+stream.writable = true;
+stream.write = function (chunk) {
+ this.buf += chunk;
+};
+stream.end = function () {
+ this.buf.should.equal('{"name":"tobi"}');
+ done();
+};
+request.post(`${base}/echo`).send('{"name":"tobi"}').pipe(stream);
+
+
+
+ .buffer()
+
+ should enable buffering
+ request
+ .get(`${base}/custom`)
+ .buffer()
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal('custom stuff', res.text);
+ assert(res.buffered);
+ done();
+ });
+ should take precedence over request.buffer['someMimeType'] = false
+ const type = 'application/barbaz';
+const send = 'some text';
+request.buffer[type] = false;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .buffer()
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+
+
+
+ .buffer(false)
+
+ should disable buffering
+ request
+ .post(`${base}/echo`)
+ .type('application/x-dog')
+ .send('hello this is dog')
+ .buffer(false)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal('hello this is dog');
+ done();
+ });
+ });
+ should take precedence over request.buffer['someMimeType'] = true
+ const type = 'application/foobar';
+const send = 'hello this is a dog';
+request.buffer[type] = true;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .buffer(false)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+
+
+
+ .withCredentials()
+
+ should not throw an error when using the client-side "withCredentials" method
+ request
+ .get(`${base}/custom`)
+ .withCredentials()
+ .end((error, res) => {
+ assert.ifError(error);
+ done();
+ });
+
+
+
+ .agent()
+
+ should return the defaut agent
+ const request_ = request.post(`${base}/echo`);
+request_.agent().should.equal(false);
+done();
+
+
+
+ .agent(undefined)
+
+ should set an agent to undefined and ensure it is chainable
+ const request_ = request.get(`${base}/echo`);
+const returnValue = request_.agent(undefined);
+returnValue.should.equal(request_);
+assert.strictEqual(request_.agent(), undefined);
+done();
+
+
+
+ .agent(new http.Agent())
+
+ should set passed agent
+ const http = require('http');
+const request_ = request.get(`${base}/echo`);
+const agent = new http.Agent();
+const returnValue = request_.agent(agent);
+returnValue.should.equal(request_);
+request_.agent().should.equal(agent);
+done();
+
+
+
+ with a content type other than application/json or text/*
+
+ should still use buffering
+ return request
+ .post(`${base}/echo`)
+ .type('application/x-dog')
+ .send('hello this is dog')
+ .then((res) => {
+ assert.equal(null, res.text);
+ assert.equal(res.body.toString(), 'hello this is dog');
+ res.buffered.should.be.true;
+ });
+
+
+
+ content-length
+
+ should be set to the byte length of a non-buffer object
+ const decoder = new StringDecoder('utf8');
+let img = fs.readFileSync(`${__dirname}/fixtures/test.png`);
+img = decoder.write(img);
+request
+ .post(`${base}/echo`)
+ .type('application/x-image')
+ .send(img)
+ .buffer(false)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(!res.buffered);
+ assert.equal(res.header['content-length'], Buffer.byteLength(img));
+ done();
+ });
+ should be set to the length of a buffer object
+ const img = fs.readFileSync(`${__dirname}/fixtures/test.png`);
+request
+ .post(`${base}/echo`)
+ .type('application/x-image')
+ .send(img)
+ .buffer(true)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.buffered);
+ assert.equal(res.header['content-length'], img.length);
+ done();
+ });
+
+
+
+
+
+ req.buffer['someMimeType']
+
+ should respect that agent.buffer(true) takes precedent
+ const agent = request.agent();
+agent.buffer(true);
+const type = 'application/somerandomtype';
+const send = 'somerandomtext';
+request.buffer[type] = false;
+agent
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+ should respect that agent.buffer(false) takes precedent
+ const agent = request.agent();
+agent.buffer(false);
+const type = 'application/barrr';
+const send = 'some random text2';
+request.buffer[type] = true;
+agent
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+ should disable buffering for that mimetype when false
+ const type = 'application/bar';
+const send = 'some random text';
+request.buffer[type] = false;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+ should enable buffering for that mimetype when true
+ const type = 'application/baz';
+const send = 'woooo';
+request.buffer[type] = true;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+ should fallback to default handling for that mimetype when undefined
+ const type = 'application/bazzz';
+const send = 'woooooo';
+return request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .then((res) => {
+ assert.equal(res.type, type);
+ assert.equal(send, res.body.toString());
+ assert(res.buffered);
+ });
+
+
+
+ exports
+
+ should expose .protocols
+ Object.keys(request.protocols).should.eql(['http:', 'https:', 'http2:']);
+ should expose .serialize
+ Object.keys(request.serialize).should.eql([
+ 'application/x-www-form-urlencoded',
+ 'application/json'
+]);
+ should expose .parse
+ Object.keys(request.parse).should.eql([
+ 'application/x-www-form-urlencoded',
+ 'application/json',
+ 'text',
+ 'application/json-seq',
+ 'application/octet-stream',
+ 'application/pdf',
+ 'image'
+]);
+ should export .buffer
+ Object.keys(request.buffer).should.eql([]);
+
+
+
+ flags
+
+
+ with 4xx response
+
+ should set res.error and res.clientError
+ request.get(`${base}/notfound`).end((error, res) => {
+ assert(error);
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(res.clientError, 'response should be a client error');
+ assert(!res.serverError, 'response should not be a server error');
+ done();
+});
+
+
+
+ with 5xx response
+
+ should set res.error and res.serverError
+ request.get(`${base}/error`).end((error, res) => {
+ assert(error);
+ assert(!res.ok, 'response should not be ok');
+ assert(!res.notFound, 'response should not be notFound');
+ assert(res.error, 'response should be an error');
+ assert(!res.clientError, 'response should not be a client error');
+ assert(res.serverError, 'response should be a server error');
+ done();
+});
+
+
+
+ with 404 Not Found
+
+ should res.notFound
+ request.get(`${base}/notfound`).end((error, res) => {
+ assert(error);
+ assert(res.notFound, 'response should be .notFound');
+ done();
+});
+
+
+
+ with 400 Bad Request
+
+ should set req.badRequest
+ request.get(`${base}/bad-request`).end((error, res) => {
+ assert(error);
+ assert(res.badRequest, 'response should be .badRequest');
+ done();
+});
+
+
+
+ with 401 Bad Request
+
+ should set res.unauthorized
+ request.get(`${base}/unauthorized`).end((error, res) => {
+ assert(error);
+ assert(res.unauthorized, 'response should be .unauthorized');
+ done();
+});
+
+
+
+ with 406 Not Acceptable
+
+ should set res.notAcceptable
+ request.get(`${base}/not-acceptable`).end((error, res) => {
+ assert(error);
+ assert(res.notAcceptable, 'response should be .notAcceptable');
+ done();
+});
+
+
+
+ with 204 No Content
+
+ should set res.noContent
+ request.get(`${base}/no-content`).end((error, res) => {
+ assert(!error);
+ assert(res.noContent, 'response should be .noContent');
+ done();
+});
+
+
+
+ with 201 Created
+
+ should set res.created
+ request.post(`${base}/created`).end((error, res) => {
+ assert(!error);
+ assert(res.created, 'response should be .created');
+ done();
+});
+
+
+
+ with 422 Unprocessable Entity
+
+ should set res.unprocessableEntity
+ request.post(`${base}/unprocessable-entity`).end((error, res) => {
+ assert(error);
+ assert(
+ res.unprocessableEntity,
+ 'response should be .unprocessableEntity'
+ );
+ done();
+});
+
+
+
+
+
+ Merging objects
+
+ Don't mix Buffer and JSON
+ assert.throws(() => {
+ request
+ .post('/echo')
+ .send(Buffer.from('some buffer'))
+ .send({ allowed: false });
+});
+
+
+
+ req.send(String)
+
+ should default to "form"
+ request
+ .post(`${base}/echo`)
+ .send('user[name]=tj')
+ .send('user[email]=tj@vision-media.ca')
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.body.should.eql({
+ user: { name: 'tj', email: 'tj@vision-media.ca' }
+ });
+ done();
+ });
+
+
+
+ res.body
+
+
+ application/x-www-form-urlencoded
+
+ should parse the body
+ request.get(`${base}/form-data`).end((error, res) => {
+ res.text.should.equal('pet[name]=manny');
+ res.body.should.eql({ pet: { name: 'manny' } });
+ done();
+});
+
+
+
+
+
+ https
+
+
+ certificate authority
+
+
+ request
+
+ should give a good response
+ request
+ .get(testEndpoint)
+ .ca(ca)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ should reject unauthorized response
+ return request
+ .get(testEndpoint)
+ .trustLocalhost(false)
+ .then(
+ () => {
+ throw new Error('Allows MITM');
+ },
+ () => {}
+ );
+ should not reject unauthorized response
+ return request
+ .get(testEndpoint)
+ .disableTLSCerts()
+ .then(({ status }) => {
+ assert.strictEqual(status, 200);
+ });
+ should trust localhost unauthorized response
+ return request.get(testEndpoint).trustLocalhost(true);
+ should trust overriden localhost unauthorized response
+ return request
+ .get(`https://example.com:${server.address().port}`)
+ .connect('127.0.0.1')
+ .trustLocalhost();
+
+
+
+ .agent
+
+ should be able to make multiple requests without redefining the certificate
+ const agent = request.agent({ ca });
+agent.get(testEndpoint).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ agent.get(url.parse(testEndpoint)).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+});
+
+
+
+
+
+ client certificates
+
+
+
+
+
+
+
+
+ res.body
+
+
+ image/png
+
+ should parse the body
+ request.get(`${base}/image`).end((error, res) => {
+ res.type.should.equal('image/png');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+});
+
+
+
+ application/octet-stream
+
+ should parse the body
+ request
+ .get(`${base}/image-as-octets`)
+ .buffer(true) // that's tech debt :(
+ .end((error, res) => {
+ res.type.should.equal('application/octet-stream');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+ });
+
+
+
+ application/octet-stream
+
+ should parse the body (using responseType)
+ request
+ .get(`${base}/image-as-octets`)
+ .responseType('blob')
+ .end((error, res) => {
+ res.type.should.equal('application/octet-stream');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+ });
+
+
+
+
+
+ zlib
+
+ should deflate the content
+ request.get(base).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.equal(subject);
+ res.headers['content-length'].should.be.below(subject.length);
+ done();
+});
+ should protect from zip bombs
+ request
+ .get(base)
+ .buffer(true)
+ .maxResponseSize(1)
+ .end((error, res) => {
+ try {
+ assert.equal('Maximum response size reached', error && error.message);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should ignore trailing junk
+ request.get(`${base}/junk`).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.equal(subject);
+ done();
+});
+ should ignore missing data
+ request.get(`${base}/chopped`).end((error, res) => {
+ assert.equal(undefined, error);
+ res.should.have.status(200);
+ res.text.should.startWith(subject);
+ done();
+});
+ should handle corrupted responses
+ request.get(`${base}/corrupt`).end((error, res) => {
+ assert(error, 'missing error');
+ assert(!res, 'response should not be defined');
+ done();
+});
+ should handle no content with gzip header
+ request.get(`${base}/nocontent`).end((error, res) => {
+ assert.ifError(error);
+ assert(res);
+ res.should.have.status(204);
+ res.text.should.equal('');
+ res.headers.should.not.have.property('content-length');
+ done();
+});
+
+ without encoding set
+
+ should buffer if asked
+ return request
+ .get(`${base}/binary`)
+ .buffer(true)
+ .then((res) => {
+ res.should.have.status(200);
+ assert(res.headers['content-length']);
+ assert(res.body.byteLength);
+ assert.equal(subject, res.body.toString());
+ });
+ should emit buffers
+ request.get(`${base}/binary`).end((error, res) => {
+ res.should.have.status(200);
+ res.headers['content-length'].should.be.below(subject.length);
+ res.on('data', (chunk) => {
+ chunk.should.have.length(subject.length);
+ });
+ res.on('end', done);
+});
+
+
+
+
+
+ req.lookup()
+
+ should set a custom lookup
+ const r = request.get(`${base}/ok`).lookup(myLookup);
+assert(r.lookup() === myLookup);
+r.then((res) => {
+ res.text.should.equal('ok');
+ done();
+});
+
+
+
+ Multipart
+
+
+ #field(name, value)
+
+ should set a multipart field value
+ const request_ = request.post(`${base}/echo`);
+request_.field('user[name]', 'tobi');
+request_.field('user[age]', '2');
+request_.field('user[species]', 'ferret');
+return request_.then((res) => {
+ res.body['user[name]'].should.equal('tobi');
+ res.body['user[age]'].should.equal('2');
+ res.body['user[species]'].should.equal('ferret');
+});
+ should work with file attachments
+ const request_ = request.post(`${base}/echo`);
+request_.field('name', 'Tobi');
+request_.attach('document', 'test/node/fixtures/user.html');
+request_.field('species', 'ferret');
+return request_.then((res) => {
+ res.body.name.should.equal('Tobi');
+ res.body.species.should.equal('ferret');
+ const html = res.files.document;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+});
+
+
+
+ #attach(name, path)
+
+ should attach a file
+ const request_ = request.post(`${base}/echo`);
+request_.attach('one', 'test/node/fixtures/user.html');
+request_.attach('two', 'test/node/fixtures/user.json');
+request_.attach('three', 'test/node/fixtures/user.txt');
+return request_.then((res) => {
+ const html = res.files.one;
+ const json = res.files.two;
+ const text = res.files.three;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+ json.originalFilename.should.equal('user.json');
+ json.mimetype.should.equal('application/json');
+ read(json.filepath).should.equal('{"name":"tobi"}');
+ text.originalFilename.should.equal('user.txt');
+ text.mimetype.should.equal('text/plain');
+ read(text.filepath).should.equal('Tobi');
+});
+
+ when a file does not exist
+
+ should fail the request with an error
+ const request_ = request.post(`${base}/echo`);
+request_.attach('name', 'foo');
+// request_.attach('name2', 'bar');
+// request_.attach('name3', 'baz');
+request_.end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed.');
+ error.code.should.equal('ENOENT');
+ error.message.should.containEql('ENOENT');
+ if (IS_WINDOWS) {
+ error.path.toLowerCase().should.equal(
+ getFullPath('foo').toLowerCase()
+ );
+ } else {
+ error.path.should.equal(getFullPath('foo'));
+ }
+ done();
+});
+ promise should fail
+ return request
+ .post(`${base}/echo`)
+ .field({ a: 1, b: 2 })
+ .attach('c', 'does-not-exist.txt')
+ .then(
+ (res) => assert.fail('It should not allow this'),
+ (err) => {
+ err.code.should.equal('ENOENT');
+ if (IS_WINDOWS) {
+ err.path.toLowerCase().should.equal(
+ getFullPath('does-not-exist.txt').toLowerCase()
+ );
+ } else {
+ err.path.should.equal(getFullPath('does-not-exist.txt'));
+ }
+ }
+ );
+ should report ENOENT via the callback
+ request
+ .post(`${base}/echo`)
+ .attach('name', 'file-does-not-exist')
+ .end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed');
+ error.code.should.equal('ENOENT');
+ done();
+ });
+ should report ENOENT via Promise
+ return request
+ .post(`${base}/echo`)
+ .attach('name', 'file-does-not-exist')
+ .then(
+ (res) => assert.fail('Request should have failed'),
+ (err) => err.code.should.equal('ENOENT')
+ );
+
+
+
+
+
+ #attach(name, path, filename)
+
+ should use the custom filename
+ request
+ .post(`${base}/echo`)
+ .attach('document', 'test/node/fixtures/user.html', 'doc.html')
+ .then((res) => {
+ const html = res.files.document;
+ html.originalFilename.should.equal('doc.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+ })
+ should fire progress event
+ let loaded = 0;
+let total = 0;
+let uploadEventWasFired = false;
+request
+ .post(`${base}/echo`)
+ .attach('document', 'test/node/fixtures/user.html')
+ .on('progress', (event) => {
+ total = event.total;
+ loaded = event.loaded;
+ if (event.direction === 'upload') {
+ uploadEventWasFired = true;
+ }
+ })
+ .end((error, res) => {
+ if (error) return done(error);
+ const html = res.files.document;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+ total.should.equal(223);
+ loaded.should.equal(223);
+ uploadEventWasFired.should.equal(true);
+ done();
+ });
+ filesystem errors should be caught
+ request
+ .post(`${base}/echo`)
+ .attach('filedata', 'test/node/fixtures/non-existent-file.ext')
+ .end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed.');
+ error.code.should.equal('ENOENT');
+ if (IS_WINDOWS) {
+ error.path.toLowerCase().should.equal(
+ getFullPath('test/node/fixtures/non-existent-file.ext').toLowerCase()
+ );
+ } else {
+ error.path.should.equal(
+ getFullPath('test/node/fixtures/non-existent-file.ext')
+ );
+ }
+ done();
+ });
+
+
+
+ #field(name, val)
+
+ should set a multipart field value
+ request
+ .post(`${base}/echo`)
+ .field('first-name', 'foo')
+ .field('last-name', 'bar')
+ .end((error, res) => {
+ if (error) done(error);
+ res.should.be.ok();
+ res.body['first-name'].should.equal('foo');
+ res.body['last-name'].should.equal('bar');
+ done();
+ });
+
+
+
+ #field(object)
+
+ should set multiple multipart fields
+ request
+ .post(`${base}/echo`)
+ .field({ 'first-name': 'foo', 'last-name': 'bar' })
+ .end((error, res) => {
+ if (error) done(error);
+ res.should.be.ok();
+ res.body['first-name'].should.equal('foo');
+ res.body['last-name'].should.equal('bar');
+ done();
+ });
+
+
+
+
+
+ with network error
+
+ should error
+ request.get(`http://localhost:${this.port}/`).end((error, res) => {
+ assert(error, 'expected an error');
+ done();
+});
+
+
+
+ request
+
+
+ not modified
+
+ should start with 200
+ request.get(`${base}/if-mod`).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.match(/^\d+$/);
+ ts = Number(res.text);
+ done();
+});
+ should then be 304
+ request
+ .get(`${base}/if-mod`)
+ .set('If-Modified-Since', new Date(ts).toUTCString())
+ .end((error, res) => {
+ res.should.have.status(304);
+ // res.text.should.be.empty
+ done();
+ });
+
+
+
+
+
+ req.parse(fn)
+
+ should take precedence over default parsers
+ request
+ .get(`${base}/manny`)
+ .parse(request.parse['application/json'])
+ .end((error, res) => {
+ assert(res.ok);
+ assert.equal('{"name":"manny"}', res.text);
+ assert.equal('manny', res.body.name);
+ done();
+ });
+ should be the only parser
+ request
+ .get(`${base}/image`)
+ .buffer(false)
+ .parse((res, fn) => {
+ res.on('data', () => {});
+ })
+ .then((res) => {
+ assert(res.ok);
+ assert.strictEqual(res.text, undefined);
+ res.body.should.eql({});
+ })
+ should emit error if parser throws
+ request
+ .get(`${base}/manny`)
+ .parse(() => {
+ throw new Error('I am broken');
+ })
+ .on('error', (error) => {
+ error.message.should.equal('I am broken');
+ done();
+ })
+ .end();
+ should emit error if parser returns an error
+ request
+ .get(`${base}/manny`)
+ .parse((res, fn) => {
+ fn(new Error('I am broken'));
+ })
+ .on('error', (error) => {
+ error.message.should.equal('I am broken');
+ done();
+ })
+ .end();
+ should not emit error on chunked json
+ request.get(`${base}/chunked-json`).end((error) => {
+ assert.ifError(error);
+ done();
+});
+ should not emit error on aborted chunked json
+ const request_ = request.get(`${base}/chunked-json`);
+request_.end((error) => {
+ assert.ifError(error);
+ done();
+});
+setTimeout(() => {
+ request_.abort();
+}, 50);
+
+
+
+ pipe on redirect
+
+ should follow Location
+ const stream = fs.createWriteStream(destinationPath);
+const redirects = [];
+const request_ = request
+ .get(base)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .connect({
+ inapplicable: 'should be ignored'
+ });
+stream.on('finish', () => {
+ redirects.should.eql(['/movies', '/movies/all', '/movies/all/0']);
+ fs.readFileSync(destinationPath, 'utf8').should.eql('first movie page');
+ done();
+});
+request_.pipe(stream);
+
+
+
+ request pipe
+
+ should act as a writable stream
+ const request_ = request.post(base);
+const stream = fs.createReadStream('test/node/fixtures/user.json');
+request_.type('json');
+request_.on('response', (res) => {
+ res.body.should.eql({ name: 'tobi' });
+ done();
+});
+stream.pipe(request_);
+ end() stops piping
+ const stream = fs.createWriteStream(destinationPath);
+request.get(base).end((error, res) => {
+ try {
+ res.pipe(stream);
+ return done(new Error('Did not prevent nonsense pipe'));
+ } catch {
+ /* expected error */
+ }
+ done();
+});
+ should act as a readable stream
+ const stream = fs.createWriteStream(destinationPath);
+let responseCalled = false;
+const request_ = request.get(base);
+request_.type('json');
+request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+});
+stream.on('finish', () => {
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ responseCalled.should.be.true();
+ done();
+});
+request_.pipe(stream);
+ should follow redirects
+ const stream = fs.createWriteStream(destinationPath);
+let responseCalled = false;
+const request_ = request.get(base + '/redirect');
+request_.type('json');
+request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+});
+stream.on('finish', () => {
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ responseCalled.should.be.true();
+ done();
+});
+request_.pipe(stream);
+ should not throw on bad redirects
+ const stream = fs.createWriteStream(destinationPath);
+let responseCalled = false;
+let errorCalled = false;
+const request_ = request.get(base + '/badRedirectNoLocation');
+request_.type('json');
+request_.on('response', (res) => {
+ responseCalled = true;
+});
+request_.on('error', (error) => {
+ error.message.should.eql('No location header for redirect');
+ errorCalled = true;
+ stream.end();
+});
+stream.on('finish', () => {
+ responseCalled.should.be.false();
+ errorCalled.should.be.true();
+ done();
+});
+request_.pipe(stream);
+
+
+
+ req.query(String)
+
+ should support passing in a string
+ request
+ .del(base)
+ .query('name=t%F6bi')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi' });
+ done();
+ });
+ should work with url query-string and string for query
+ request
+ .del(`${base}/?name=tobi`)
+ .query('age=2%20')
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', age: '2 ' });
+ done();
+ });
+ should support compound elements in a string
+ request
+ .del(base)
+ .query('name=t%F6bi&age=2')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2' });
+ done();
+ });
+ should work when called multiple times with a string
+ request
+ .del(base)
+ .query('name=t%F6bi')
+ .query('age=2%F6')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2%F6' });
+ done();
+ });
+ should work with normal `query` object and query string
+ request
+ .del(base)
+ .query('name=t%F6bi')
+ .query({ age: '2' })
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2' });
+ done();
+ });
+ should not encode raw backticks, but leave encoded ones as is
+ return Promise.all([
+ request
+ .get(`${base}/raw-query`)
+ .query('name=`t%60bi`&age`=2')
+ .then((res) => {
+ res.text.should.eql('name=`t%60bi`&age`=2');
+ }),
+ request.get(base + '/raw-query?`age%60`=2%60`').then((res) => {
+ res.text.should.eql('`age%60`=2%60`');
+ }),
+ request
+ .get(`${base}/raw-query`)
+ .query('name=`t%60bi`')
+ .query('age`=2')
+ .then((res) => {
+ res.text.should.eql('name=`t%60bi`&age`=2');
+ })
+]);
+
+
+
+ req.query(Object)
+
+ should construct the query-string
+ request
+ .del(base)
+ .query({ name: 'tobi' })
+ .query({ order: 'asc' })
+ .query({ limit: ['1', '2'] })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
+ done();
+ });
+ should encode raw backticks
+ request
+ .get(`${base}/raw-query`)
+ .query({ name: '`tobi`' })
+ .query({ 'orde%60r': null })
+ .query({ '`limit`': ['%602`'] })
+ .end((error, res) => {
+ res.text.should.eql('name=%60tobi%60&orde%2560r&%60limit%60=%25602%60');
+ done();
+ });
+ should not error on dates
+ const date = new Date(0);
+request
+ .del(base)
+ .query({ at: date })
+ .end((error, res) => {
+ assert.equal(date.toISOString(), res.body.at);
+ done();
+ });
+ should work after setting header fields
+ request
+ .del(base)
+ .set('Foo', 'bar')
+ .set('Bar', 'baz')
+ .query({ name: 'tobi' })
+ .query({ order: 'asc' })
+ .query({ limit: ['1', '2'] })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
+ done();
+ });
+ should append to the original query-string
+ request
+ .del(`${base}/?name=tobi`)
+ .query({ order: 'asc' })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc' });
+ done();
+ });
+ should retain the original query-string
+ request.del(`${base}/?name=tobi`).end((error, res) => {
+ res.body.should.eql({ name: 'tobi' });
+ done();
+});
+ should keep only keys with null querystring values
+ request
+ .del(`${base}/url`)
+ .query({ nil: null })
+ .end((error, res) => {
+ res.text.should.equal('/url?nil');
+ done();
+ });
+ query-string should be sent on pipe
+ this.timeout(15_000);
+const request_ = request.put(`${base}/?name=tobi`);
+const stream = fs.createReadStream('test/node/fixtures/user.json');
+request_.on('response', (res) => {
+ res.body.should.eql({ name: 'tobi' });
+ done();
+});
+request_.on('error', (err) => {
+ done(err);
+});
+stream.on('error', function (err) {
+ done(err);
+});
+stream.pipe(request_);
+
+
+
+ request.get
+
+
+ on 301 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-301`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 302 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-302`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 303 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-303`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 307 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-307`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 308 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-308`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+
+
+ request.post
+
+
+ on 301 redirect
+
+ should follow Location with a GET request
+ const request_ = request.post(`${base}/test-301`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 302 redirect
+
+ should follow Location with a GET request
+ const request_ = request.post(`${base}/test-302`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 303 redirect
+
+ should follow Location with a GET request
+ const request_ = request.post(`${base}/test-303`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 307 redirect
+
+ should follow Location with a POST request
+ const request_ = request.post(`${base}/test-307`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('POST');
+ done();
+});
+
+
+
+ on 308 redirect
+
+ should follow Location with a POST request
+ const request_ = request.post(`${base}/test-308`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('POST');
+ done();
+});
+
+
+
+
+
+ request
+
+
+ on redirect
+
+ should merge cookies if agent is used
+ request
+ .agent()
+ .get(`${base}/cookie-redirect`)
+ .set('Cookie', 'orig=1; replaced=not')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), 'orig=1/.test');
+ assert(/replaced=yes/.test(res.text), 'replaced=yes/.test');
+ assert(/from-redir=1/.test(res.text), 'from-redir=1');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not merge cookies if agent is not used
+ request
+ .get(`${base}/cookie-redirect`)
+ .set('Cookie', 'orig=1; replaced=not')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), '/orig=1');
+ assert(/replaced=not/.test(res.text), '/replaced=not');
+ assert(!/replaced=yes/.test(res.text), '!/replaced=yes');
+ assert(!/from-redir/.test(res.text), '!/from-redir');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should have previously set cookie for subsquent requests when agent is used
+ const agent = request.agent();
+agent.get(`${base}/set-cookie`).end((error) => {
+ assert.ifError(error);
+ agent
+ .get(`${base}/show-cookies`)
+ .set({ Cookie: 'orig=1' })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), 'orig=1/.test');
+ assert(/persist=123/.test(res.text), 'persist=123');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+});
+ should follow Location
+ const redirects = [];
+request
+ .get(base)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should follow Location with IP override
+ const redirects = [];
+const url = URL.parse(base);
+return request
+ .get(`http://redir.example.com:${url.port || '80'}${url.pathname}`)
+ .connect({
+ '*': url.hostname
+ })
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ });
+ should follow Location with IP:port override
+ const redirects = [];
+const url = URL.parse(base);
+return request
+ .get(`http://redir.example.com:9999${url.pathname}`)
+ .connect({
+ '*': { host: url.hostname, port: url.port || 80 }
+ })
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ });
+ should not follow on HEAD by default
+ const redirects = [];
+return request
+ .head(base)
+ .ok(() => true)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql([]);
+ res.status.should.equal(302);
+ });
+ should follow on HEAD when redirects are set
+ const redirects = [];
+request
+ .head(base)
+ .redirects(10)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = [];
+ array.push('/movies', '/movies/all', '/movies/all/0');
+ redirects.should.eql(array);
+ assert(!res.text);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should remove Content-* fields
+ request
+ .post(`${base}/header`)
+ .type('txt')
+ .set('X-Foo', 'bar')
+ .set('X-Bar', 'baz')
+ .send('hey')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ res.body.should.have.property('x-bar', 'baz');
+ res.body.should.not.have.property('content-type');
+ res.body.should.not.have.property('content-length');
+ res.body.should.not.have.property('transfer-encoding');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should retain cookies
+ request
+ .get(`${base}/header`)
+ .set('Cookie', 'foo=bar;')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('cookie', 'foo=bar;');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not resend query parameters
+ const redirects = [];
+const query = [];
+request
+ .get(`${base}/?foo=bar`)
+ .on('redirect', (res) => {
+ query.push(res.headers.query);
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = [];
+ array.push('/movies', '/movies/all', '/movies/all/0');
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ query.should.eql(['{"foo":"bar"}', '{}', '{}']);
+ res.headers.query.should.eql('{}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle no location header
+ request.get(`${base}/bad-redirect`).end((error, res) => {
+ try {
+ error.message.should.equal('No location header for redirect');
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+ when relative
+
+ should redirect to a sibling path
+ const redirects = [];
+request
+ .get(`${base}/relative`)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ redirects.should.eql(['tobi']);
+ res.text.should.equal('tobi');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should redirect to a parent path
+ const redirects = [];
+request
+ .get(`${base}/relative/sub`)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ redirects.should.eql(['../tobi']);
+ res.text.should.equal('tobi');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+
+
+ req.redirects(n)
+
+ should alter the default number of redirects to follow
+ const redirects = [];
+request
+ .get(base)
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = [];
+ assert(res.redirect, 'res.redirect');
+ array.push('/movies', '/movies/all');
+ redirects.should.eql(array);
+ res.text.should.match(/Moved Temporarily|Found/);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ on POST
+
+ should redirect as GET
+ const redirects = [];
+return request
+ .post(`${base}/movie`)
+ .send({ name: 'Tobi' })
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql(['/movies/all/0']);
+ res.text.should.equal('first movie page');
+ });
+ using multipart/form-data should redirect as GET
+ const redirects = [];
+request
+ .post(`${base}/movie`)
+ .type('form')
+ .field('name', 'Tobi')
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql(['/movies/all/0']);
+ res.text.should.equal('first movie page');
+ });
+
+
+
+
+
+ response
+
+ should act as a readable stream
+ const request_ = request.get(base).buffer(false);
+request_.end((error, res) => {
+ if (error) return done(error);
+ let trackEndEvent = 0;
+ let trackCloseEvent = 0;
+ res.on('end', () => {
+ trackEndEvent++;
+ trackEndEvent.should.equal(1);
+ if (!process.env.HTTP2_TEST) {
+ trackCloseEvent.should.equal(0); // close should not have been called
+ }
+ done();
+ });
+ res.on('close', () => {
+ trackCloseEvent++;
+ });
+ setTimeout(() => {
+ (() => {
+ res.pause();
+ }).should.not.throw();
+ (() => {
+ res.resume();
+ }).should.not.throw();
+ (() => {
+ res.destroy();
+ }).should.not.throw();
+ }, 50);
+});
+
+
+
+ req.serialize(fn)
+
+ should take precedence over default parsers
+ request
+ .post(`${base}/echo`)
+ .send({ foo: 123 })
+ .serialize(() => '{"bar":456}')
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal('{"bar":456}', res.text);
+ assert.equal(456, res.body.bar);
+ done();
+ });
+
+
+
+ request.get().set()
+
+ should set host header after get()
+ app.get('/', (request_, res) => {
+ assert.equal(request_.hostname, 'example.com');
+ res.end();
+});
+server = http.createServer(app);
+server.listen(0, function listening() {
+ request
+ .get(`http://localhost:${server.address().port}`)
+ .set('host', 'example.com')
+ .then(() => {
+ return request
+ .get(`http://example.com:${server.address().port}`)
+ .connect({
+ 'example.com': 'localhost',
+ '*': 'fail'
+ });
+ })
+ .then(() => done(), done);
+});
+
+
+
+ res.toError()
+
+ should return an Error
+ request.get(base).end((err, res) => {
+ const error = res.toError();
+ assert.equal(error.status, 400);
+ assert.equal(error.method, 'GET');
+ assert.equal(error.path, '/');
+ assert.equal(error.message, 'cannot GET / (400)');
+ assert.equal(error.text, 'invalid json');
+ done();
+});
+
+
+
+ [unix-sockets] http
+
+
+ request
+
+ path: / (root)
+ request.get(`${base}/`).end((error, res) => {
+ assert(res.ok);
+ assert.strictEqual('root ok!', res.text);
+ done();
+});
+ path: /request/path
+ request.get(`${base}/request/path`).end((error, res) => {
+ assert(res.ok);
+ assert.strictEqual('request path ok!', res.text);
+ done();
+});
+
+
+
+
+
+ [unix-sockets] https
+
+
+ request
+
+ path: / (root)
+ request
+ .get(`${base}/`)
+ .ca(cacert)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('root ok!', res.text);
+ done();
+ });
+ path: /request/path
+ request
+ .get(`${base}/request/path`)
+ .ca(cacert)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('request path ok!', res.text);
+ done();
+ });
+
+
+
+
+
+ req.get()
+
+ should not set a default user-agent
+ request.get(`${base}/ua`).then((res) => {
+ assert(res.headers);
+ assert(!res.headers['user-agent']);
+ })
+
+
+
+ utils.type(str)
+
+ should return the mime type
+ utils
+ .type('application/json; charset=utf-8')
+ .should.equal('application/json');
+utils.type('application/json').should.equal('application/json');
+
+
+
+ utils.params(str)
+
+ should return the field parameters
+ const object = utils.params('application/json; charset=utf-8; foo = bar');
+object.charset.should.equal('utf-8');
+object.foo.should.equal('bar');
+utils.params('application/json').should.eql({});
+
+
+
+ utils.parseLinks(str)
+
+ should parse links
+ const string_ =
+ '<https://api.github.com/repos/visionmedia/mocha/issues?page=2>; rel="next", <https://api.github.com/repos/visionmedia/mocha/issues?page=5>; rel="last"';
+const returnValue = utils.parseLinks(string_);
+returnValue.next.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=2'
+);
+returnValue.last.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=5'
+);
+
+
+-------------------|---------|----------|---------|---------|---------------------------------------
+File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
+-------------------|---------|----------|---------|---------|---------------------------------------
+All files | 85.79 | 79.93 | 78.06 | 86.6 |
+ src | 92.5 | 83.83 | 91.83 | 93.84 |
+ agent-base.js | 100 | 100 | 100 | 100 |
+ request-base.js | 91.01 | 83.9 | 94.28 | 93.06 | ...21,262,316,501,525-533,579,757,768
+ response-base.js | 100 | 100 | 75 | 100 |
+ utils.js | 92.85 | 71.42 | 85.71 | 91.66 | 94-98
+ src/node | 82.28 | 78 | 68.42 | 82.94 |
+ agent.js | 89.79 | 66.66 | 100 | 88.63 | 39,43,47,51,101
+ http2wrapper.js | 24.8 | 4.34 | 0 | 21.49 | ...76,180-181,185-186,190-191,196-198
+ index.js | 92.49 | 83.03 | 87.71 | 93.1 | ...,977,1142,1182-1186,1220-1221,1301
+ response.js | 90 | 83.33 | 55.55 | 89.79 | 78,86,94,120-121
+ unzip.js | 100 | 92.85 | 100 | 100 | 47
+ src/node/parsers | 97.61 | 75 | 100 | 97.61 |
+ image.js | 100 | 100 | 100 | 100 |
+ index.js | 100 | 100 | 100 | 100 |
+ json.js | 100 | 75 | 100 | 100 | 15
+ text.js | 100 | 100 | 100 | 100 |
+ urlencoded.js | 90 | 100 | 100 | 90 | 17
+-------------------|---------|----------|---------|---------|---------------------------------------
+test on node with http2
+test on plain node
+
+ Agent
+
+ should remember defaults
+ if (typeof Promise === 'undefined') {
+ return;
+}
+let called = 0;
+let event_called = 0;
+const agent = request
+ .agent()
+ .accept('json')
+ .use(() => {
+ called++;
+ })
+ .once('request', () => {
+ event_called++;
+ })
+ .query({ hello: 'world' })
+ .set('X-test', 'testing');
+assert.equal(0, called);
+assert.equal(0, event_called);
+return agent
+ .get(`${base}/echo`)
+ .then((res) => {
+ assert.equal(1, called);
+ assert.equal(1, event_called);
+ assert.equal('application/json', res.headers.accept);
+ assert.equal('testing', res.headers['x-test']);
+ return agent.get(`${base}/querystring`);
+ })
+ .then((res) => {
+ assert.equal(2, called);
+ assert.equal(2, event_called);
+ assert.deepEqual({ hello: 'world' }, res.body);
+ });
+
+
+
+ request
+
+
+ res.statusCode
+
+ should set statusCode
+ request.get(`${uri}/login`, (error, res) => {
+ try {
+ assert.strictEqual(res.statusCode, 200);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ should allow the send shorthand
+
+ with callback in the method call
+ request.get(`${uri}/login`, (error, res) => {
+ assert.equal(res.status, 200);
+ done();
+});
+ with data in the method call
+ request.post(`${uri}/echo`, { foo: 'bar' }).end((error, res) => {
+ assert.equal('{"foo":"bar"}', res.text);
+ done();
+});
+ with callback and data in the method call
+ request.post(`${uri}/echo`, { foo: 'bar' }, (error, res) => {
+ assert.equal('{"foo":"bar"}', res.text);
+ done();
+});
+
+
+
+ with a callback
+
+ should invoke .end()
+ request.get(`${uri}/login`, (error, res) => {
+ try {
+ assert.equal(res.status, 200);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ .end()
+
+ should issue a request
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert.equal(res.status, 200);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+ is optional with a promise
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/login`)
+ .then((res) => res.status)
+ .then()
+ .then((status) => {
+ assert.equal(200, status, 'Real promises pass results through');
+ });
+ called only once with a promise
+ if (typeof Promise === 'undefined') {
+ return;
+}
+const request_ = request.get(`${uri}/unique`);
+return Promise.all([request_, request_, request_]).then((results) => {
+ for (const item of results) {
+ assert.deepEqual(
+ item.body,
+ results[0].body,
+ 'It should keep returning the same result after being called once'
+ );
+ }
+});
+
+
+
+ res.error
+
+ ok
+ let calledErrorEvent = false;
+let calledOKHandler = false;
+request
+ .get(`${uri}/error`)
+ .ok((res) => {
+ assert.strictEqual(500, res.status);
+ calledOKHandler = true;
+ return true;
+ })
+ .on('error', (error) => {
+ calledErrorEvent = true;
+ })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.strictEqual(res.status, 500);
+ assert(!calledErrorEvent);
+ assert(calledOKHandler);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should be an Error object
+ let calledErrorEvent = false;
+request
+ .get(`${uri}/error`)
+ .on('error', (error) => {
+ assert.strictEqual(error.status, 500);
+ calledErrorEvent = true;
+ })
+ .end((error, res) => {
+ try {
+ if (NODE) {
+ res.error.message.should.equal('cannot GET /error (500)');
+ } else {
+ res.error.message.should.equal(`cannot GET ${uri}/error (500)`);
+ }
+ assert.strictEqual(res.error.status, 500);
+ assert(error, 'should have an error for 500');
+ assert.equal(error.message, 'Internal Server Error');
+ assert(calledErrorEvent);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ with .then() promise
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request.get(`${uri}/error`).then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(err.message, 'Internal Server Error');
+ }
+);
+ with .ok() returning false
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/echo`)
+ .ok(() => false)
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'OK');
+ }
+ );
+ with .ok() throwing an Error
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/echo`)
+ .ok(() => {
+ throw new Error('boom');
+ })
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(200, err.status);
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'boom');
+ }
+ );
+ with .ok() throwing an Error with status
+ if (typeof Promise === 'undefined') {
+ return;
+}
+return request
+ .get(`${uri}/echo`)
+ .ok(() => {
+ const err = new Error('boom');
+ err.status = 404;
+ throw err;
+ })
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(404, err.status);
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'boom');
+ }
+ );
+
+
+
+ res.header
+
+ should be an object
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert.equal('Express', res.header['x-powered-by']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ set headers
+
+ should only set headers for ownProperties of header
+ try {
+ request
+ .get(`${uri}/echo-headers`)
+ .set('valid', 'ok')
+ .end((error, res) => {
+ if (
+ !error &&
+ res.body &&
+ res.body.valid &&
+ !res.body.hasOwnProperty('invalid')
+ ) {
+ return done();
+ }
+ done(error || new Error('fail'));
+ });
+} catch (err) {
+ done(err);
+}
+
+
+
+ res.charset
+
+ should be set when present
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ res.charset.should.equal('utf-8');
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ res.statusType
+
+ should provide the first digit
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert(!error, 'should not have an error for success responses');
+ assert.equal(200, res.status);
+ assert.equal(2, res.statusType);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ res.type
+
+ should provide the mime-type void of params
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ res.type.should.equal('text/html');
+ res.charset.should.equal('utf-8');
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ req.set(field, val)
+
+ should set the header field
+ request
+ .post(`${uri}/echo`)
+ .set('X-Foo', 'bar')
+ .set('X-Bar', 'baz')
+ .end((error, res) => {
+ try {
+ assert.equal('bar', res.header['x-foo']);
+ assert.equal('baz', res.header['x-bar']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.set(obj)
+
+ should set the header fields
+ request
+ .post(`${uri}/echo`)
+ .set({ 'X-Foo': 'bar', 'X-Bar': 'baz' })
+ .end((error, res) => {
+ try {
+ assert.equal('bar', res.header['x-foo']);
+ assert.equal('baz', res.header['x-bar']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.type(str)
+
+ should set the Content-Type
+ request
+ .post(`${uri}/echo`)
+ .type('text/x-foo')
+ .end((error, res) => {
+ try {
+ res.header['content-type'].should.equal('text/x-foo');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "json"
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('{"a": 1}')
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "html"
+ request
+ .post(`${uri}/echo`)
+ .type('html')
+ .end((error, res) => {
+ try {
+ res.header['content-type'].should.equal('text/html');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.accept(str)
+
+ should set Accept
+ request
+ .get(`${uri}/echo`)
+ .accept('text/x-foo')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('text/x-foo');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "json"
+ request
+ .get(`${uri}/echo`)
+ .accept('json')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('application/json');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "xml"
+ request
+ .get(`${uri}/echo`)
+ .accept('xml')
+ .end((error, res) => {
+ try {
+ // Mime module keeps changing this :(
+ assert(
+ res.header.accept == 'application/xml' ||
+ res.header.accept == 'text/xml'
+ );
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should map "html"
+ request
+ .get(`${uri}/echo`)
+ .accept('html')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('text/html');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.send(str)
+
+ should write the string
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('{"name":"tobi"}')
+ .end((error, res) => {
+ try {
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.send(Object)
+
+ should default to json
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ when called several times
+
+ should merge the objects
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .send({ age: 1 })
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ if (NODE) {
+ res.buffered.should.be.true();
+ }
+ res.text.should.equal('{"name":"tobi","age":1}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+
+
+ .end(fn)
+
+ should check arity
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should emit request
+ const request_ = request.post(`${uri}/echo`);
+request_.on('request', (request) => {
+ assert.equal(request_, request);
+ done();
+});
+request_.end();
+ should emit response
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .on('response', (res) => {
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ })
+ .end();
+
+
+
+ .then(fulfill, reject)
+
+ should support successful fulfills with .then(fulfill)
+ if (typeof Promise === 'undefined') {
+ return done();
+}
+request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .then((res) => {
+ res.type.should.equal('application/json');
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ });
+ should reject an error with .then(null, reject)
+ if (typeof Promise === 'undefined') {
+ return done();
+}
+request.get(`${uri}/error`).then(null, (err) => {
+ assert.equal(err.status, 500);
+ assert.equal(err.response.text, 'boom');
+ done();
+});
+
+
+
+ .catch(reject)
+
+ should reject an error with .catch(reject)
+ if (typeof Promise === 'undefined') {
+ return done();
+}
+request.get(`${uri}/error`).catch((err) => {
+ assert.equal(err.status, 500);
+ assert.equal(err.response.text, 'boom');
+ done();
+});
+
+
+
+ .abort()
+
+ should abort the request
+ const request_ = request.get(`${uri}/delay/3000`);
+request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+});
+request_.on('error', (error) => {
+ done(error);
+});
+request_.on('abort', done);
+setTimeout(() => {
+ request_.abort();
+}, 500);
+ should abort the promise
+ const request_ = request.get(`${uri}/delay/3000`);
+setTimeout(() => {
+ request_.abort();
+}, 10);
+return request_.then(
+ () => {
+ assert.fail('should not complete the request');
+ },
+ (err) => {
+ assert.equal('ABORTED', err.code);
+ }
+);
+ should allow chaining .abort() several times
+ const request_ = request.get(`${uri}/delay/3000`);
+request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+});
+// This also verifies only a single 'done' event is emitted
+request_.on('abort', done);
+setTimeout(() => {
+ request_.abort().abort().abort();
+}, 1000);
+ should not allow abort then end
+ request
+ .get(`${uri}/delay/3000`)
+ .abort()
+ .end((error, res) => {
+ done(error ? undefined : new Error('Expected abort error'));
+ });
+
+
+
+ req.toJSON()
+
+ should describe the request
+ const request_ = request.post(`${uri}/echo`).send({ foo: 'baz' });
+request_.end((error, res) => {
+ try {
+ const json = request_.toJSON();
+ assert.equal('POST', json.method);
+ assert(/\/echo$/.test(json.url));
+ assert.equal('baz', json.data.foo);
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+
+
+ req.options()
+
+ should allow request body
+ request
+ .options(`${uri}/options/echo/body`)
+ .send({ foo: 'baz' })
+ .end((error, res) => {
+ try {
+ assert.equal(error, null);
+ assert.strictEqual(res.body.foo, 'baz');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ req.sortQuery()
+
+ nop with no querystring
+ request
+ .get(`${uri}/url`)
+ .sortQuery()
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should sort the request querystring
+ request
+ .get(`${uri}/url`)
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery()
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?order=desc&search=Manny');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should allow disabling sorting
+ request
+ .get(`${uri}/url`)
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery() // take default of true
+ .sortQuery(false) // override it in later call
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?search=Manny&order=desc');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should sort the request querystring using customized function
+ request
+ .get(`${uri}/url`)
+ .query('name=Nick')
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery((a, b) => a.length - b.length)
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?name=Nick&order=desc&search=Manny');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+
+
+ req.set("Content-Type", contentType)
+
+ should work with just the contentType component
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json')
+ .send({ name: 'tobi' })
+ .end((error) => {
+ assert(!error);
+ done();
+ });
+ should work with the charset component
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json; charset=utf-8')
+ .send({ name: 'tobi' })
+ .end((error) => {
+ assert(!error);
+ done();
+ });
+
+
+
+ req.send(Object) as "form"
+
+
+ with req.type() set to form
+
+ should send x-www-form-urlencoded data
+ request
+ .post(`${base}/echo`)
+ .type('form')
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.text.should.equal('name=tobi');
+ done();
+ });
+
+
+
+ when called several times
+
+ should merge the objects
+ request
+ .post(`${base}/echo`)
+ .type('form')
+ .send({ name: { first: 'tobi', last: 'holowaychuk' } })
+ .send({ age: '1' })
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.text.should.equal(
+ 'name%5Bfirst%5D=tobi&name%5Blast%5D=holowaychuk&age=1'
+ );
+ done();
+ });
+
+
+
+
+
+ req.attach
+
+ ignores null file
+ request
+ .post('/echo')
+ .attach('image', null)
+ .end((error, res) => {
+ done();
+ });
+
+
+
+ req.field
+
+ allow bools
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field('bools', true)
+ .field('strings', 'true')
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { bools: 'true', strings: 'true' });
+ done();
+ });
+ allow objects
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field({ bools: true, strings: 'true' })
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { bools: 'true', strings: 'true' });
+ done();
+ });
+ works with arrays in objects
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field({ numbers: [1, 2, 3] })
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { numbers: ['1', '2', '3'] });
+ done();
+ });
+ works with arrays
+ if (!formDataSupported) {
+ return done();
+}
+request
+ .post(`${base}/formecho`)
+ .field('letters', ['a', 'b', 'c'])
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { letters: ['a', 'b', 'c'] });
+ done();
+ });
+ throw when empty
+ should.throws(() => {
+ request.post(`${base}/echo`).field();
+}, /name/);
+should.throws(() => {
+ request.post(`${base}/echo`).field('name');
+}, /val/);
+ cannot be mixed with send()
+ assert.throws(() => {
+ request.post('/echo').field('form', 'data').send('hi');
+});
+assert.throws(() => {
+ request.post('/echo').send('hi').field('form', 'data');
+});
+
+
+
+ req.send(Object) as "json"
+
+ should default to json
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ });
+ should work with arrays
+ request
+ .post(`${uri}/echo`)
+ .send([1, 2, 3])
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('[1,2,3]');
+ done();
+ });
+ should work with value null
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('null')
+ .end((error, res) => {
+ res.should.be.json();
+ assert.strictEqual(res.body, null);
+ done();
+ });
+ should work with value false
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('false')
+ .end((error, res) => {
+ res.should.be.json();
+ res.body.should.equal(false);
+ done();
+ });
+ should work with empty string value
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('""')
+ .end((error, res) => {
+ res.should.be.json();
+ res.body.should.equal('');
+ done();
+ });
+ should work with vendor MIME type
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/vnd.example+json')
+ .send({ name: 'vendor' })
+ .end((error, res) => {
+ res.text.should.equal('{"name":"vendor"}');
+ ({ name: 'vendor' }.should.eql(res.body));
+ done();
+ });
+
+ when called several times
+
+ should merge the objects
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .send({ age: 1 })
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi","age":1}');
+ ({ name: 'tobi', age: 1 }.should.eql(res.body));
+ done();
+ });
+
+
+
+
+
+ res.body
+
+
+ application/json
+
+ should parse the body
+ request.get(`${uri}/json`).end((error, res) => {
+ res.text.should.equal('{"name":"manny"}');
+ res.body.should.eql({ name: 'manny' });
+ done();
+});
+
+
+
+ Invalid JSON response
+
+ should return the raw response
+ request.get(`${uri}/invalid-json`).end((error, res) => {
+ assert.deepEqual(
+ error.rawResponse,
+ ")]}', {'header':{'code':200,'text':'OK','version':'1.0'},'data':'some data'}"
+ );
+ done();
+});
+ should return the http status code
+ request.get(`${uri}/invalid-json-forbidden`).end((error, res) => {
+ assert.equal(error.statusCode, 403);
+ done();
+});
+
+
+
+
+
+ request
+
+
+ on redirect
+
+ should retain header fields
+ request
+ .get(`${base}/header`)
+ .set('X-Foo', 'bar')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should preserve timeout across redirects
+ request
+ .get(`${base}/movies/random`)
+ .timeout(250)
+ .end((error, res) => {
+ try {
+ assert(error instanceof Error, 'expected an error');
+ error.should.have.property('timeout', 250);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should successfully redirect after retry on error
+ const id = Math.random() * 1_000_000 * Date.now();
+request
+ .get(`${base}/error/redirect/${id}`)
+ .retry(2)
+ .end((error, res) => {
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'first movie page');
+ done();
+ });
+ should preserve retries across redirects
+ const id = Math.random() * 1_000_000 * Date.now();
+request
+ .get(`${base}/error/redirect-error${id}`)
+ .retry(2)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ });
+
+
+
+ on 303
+
+ should redirect with same method
+ request
+ .put(`${base}/redirect-303`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+ res.text.should.equal('method=get');
+ done();
+ });
+
+
+
+ on 307
+
+ should redirect with same method
+ if (isMSIE) return done(); // IE9 broken
+request
+ .put(`${base}/redirect-307`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+ res.text.should.equal('method=put');
+ done();
+ });
+
+
+
+ on 308
+
+ should redirect with same method
+ if (isMSIE) return done(); // IE9 broken
+request
+ .put(`${base}/redirect-308`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+ res.text.should.equal('method=put');
+ done();
+ });
+
+
+
+
+
+ request
+
+ Request inheritance
+ assert(request.get(`${uri}/`) instanceof request.Request);
+ request() simple GET without callback
+ request('GET', 'test/test.request.js').end();
+next();
+ request() simple GET
+ request('GET', `${uri}/ok`).end((error, res) => {
+ try {
+ assert(res instanceof request.Response, 'respond with Response');
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'res.text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() simple HEAD
+ request.head(`${uri}/ok`).end((error, res) => {
+ try {
+ assert(res instanceof request.Response, 'respond with Response');
+ assert(res.ok, 'response should be ok');
+ assert(!res.text, 'res.text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 5xx
+ request('GET', `${uri}/error`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(error.message, 'Internal Server Error');
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(!res.clientError, 'response should not be a client error');
+ assert(res.serverError, 'response should be a server error');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 4xx
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(error.message, 'Not Found');
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(res.clientError, 'response should be a client error');
+ assert(!res.serverError, 'response should not be a server error');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 404 Not Found
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.notFound, 'response should be .notFound');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 400 Bad Request
+ request('GET', `${uri}/bad-request`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.badRequest, 'response should be .badRequest');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 401 Bad Request
+ request('GET', `${uri}/unauthorized`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.unauthorized, 'response should be .unauthorized');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 406 Not Acceptable
+ request('GET', `${uri}/not-acceptable`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.notAcceptable, 'response should be .notAcceptable');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() GET 204 No Content
+ request('GET', `${uri}/no-content`).end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() DELETE 204 No Content
+ request('DELETE', `${uri}/no-content`).end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() header parsing
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal('text/html; charset=utf-8', res.header['content-type']);
+ assert.equal('Express', res.header['x-powered-by']);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request() .status
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(404, res.status, 'response .status');
+ assert.equal(4, res.statusType, 'response .statusType');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ get()
+ request.get(`${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(404, res.status, 'response .status');
+ assert.equal(4, res.statusType, 'response .statusType');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ put()
+ request.put(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('updated', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ put().send()
+ request
+ .put(`${uri}/user/13/body`)
+ .send({ user: 'new' })
+ .end((error, res) => {
+ try {
+ assert.equal('received new', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ post()
+ request.post(`${uri}/user`).end((error, res) => {
+ try {
+ assert.equal('created', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ del()
+ request.del(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('deleted', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ delete()
+ request.delete(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('deleted', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ post() data
+ request
+ .post(`${uri}/todo/item`)
+ .type('application/octet-stream')
+ .send('tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .type()
+ request
+ .post(`${uri}/user/12/pet`)
+ .type('urlencoded')
+ .send('pet=tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .type() with alias
+ request
+ .post(`${uri}/user/12/pet`)
+ .type('application/x-www-form-urlencoded')
+ .send('pet=tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .get() with no data or callback
+ request.get(`${uri}/echo-header/content-type`);
+next();
+ request .send() with no data only
+ request.post(`${uri}/user/5/pet`).type('urlencoded').send('pet=tobi');
+next();
+ request .send() with callback only
+ request
+ .get(`${uri}/echo-header/accept`)
+ .set('Accept', 'foo/bar')
+ .end((error, res) => {
+ try {
+ assert.equal('foo/bar', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with json
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('json')
+ .end((error, res) => {
+ try {
+ assert.equal('application/json', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with application/json
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('application/json')
+ .end((error, res) => {
+ try {
+ assert.equal('application/json', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with xml
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('xml')
+ .end((error, res) => {
+ try {
+ // We can't depend on mime module to be consistent with this
+ assert(res.text == 'application/xml' || res.text == 'text/xml');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .accept() with application/xml
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('application/xml')
+ .end((error, res) => {
+ try {
+ assert.equal('application/xml', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .end()
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .send()
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .set()
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ request .set(object)
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set({ 'Content-Type': 'text/plain' })
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST urlencoded
+ request
+ .post(`${uri}/pet`)
+ .type('urlencoded')
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json
+ request
+ .post(`${uri}/pet`)
+ .type('json')
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json array
+ request
+ .post(`${uri}/echo`)
+ .send([1, 2, 3])
+ .end((error, res) => {
+ try {
+ assert.equal(
+ 'application/json',
+ res.header['content-type'].split(';')[0]
+ );
+ assert.equal('[1,2,3]', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json default
+ request
+ .post(`${uri}/pet`)
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json contentType charset
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json; charset=UTF-8')
+ .send({ data: ['data1', 'data2'] })
+ .end((error, res) => {
+ try {
+ assert.equal('{"data":["data1","data2"]}', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST json contentType vendor
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/vnd.example+json')
+ .send({ data: ['data1', 'data2'] })
+ .end((error, res) => {
+ try {
+ assert.equal('{"data":["data1","data2"]}', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST multiple .send() calls
+ request
+ .post(`${uri}/pet`)
+ .send({ name: 'Manny' })
+ .send({ species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST multiple .send() strings
+ request
+ .post(`${uri}/echo`)
+ .send('user[name]=tj')
+ .send('user[email]=tj@vision-media.ca')
+ .end((error, res) => {
+ try {
+ assert.equal(
+ 'application/x-www-form-urlencoded',
+ res.header['content-type'].split(';')[0]
+ );
+ assert.equal(
+ res.text,
+ 'user[name]=tj&user[email]=tj@vision-media.ca'
+ );
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ POST with no data
+ request
+ .post(`${uri}/empty-body`)
+ .send()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET .type
+ request.get(`${uri}/pets`).end((error, res) => {
+ try {
+ assert.equal('application/json', res.type);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET Content-Type params
+ request.get(`${uri}/text`).end((error, res) => {
+ try {
+ assert.equal('utf-8', res.charset);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET json
+ request.get(`${uri}/pets`).end((error, res) => {
+ try {
+ assert.deepEqual(res.body, ['tobi', 'loki', 'jane']);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET json-seq
+ request
+ .get(`${uri}/json-seq`)
+ .buffer()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.deepEqual(res.text, '\u001E{"id":1}\n\u001E{"id":2}\n');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET binary data
+ request
+ .get(`${uri}/binary-data`)
+ .buffer()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.deepEqual(res.body, binData);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET x-www-form-urlencoded
+ request.get(`${uri}/foo`).end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { foo: 'bar' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET shorthand
+ request.get(`${uri}/foo`, (error, res) => {
+ try {
+ assert.equal('foo=bar', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ POST shorthand
+ request.post(`${uri}/user/0/pet`, { pet: 'tobi' }, (error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ POST shorthand without callback
+ request.post(`${uri}/user/0/pet`, { pet: 'tobi' }).end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ GET querystring object with array
+ request
+ .get(`${uri}/querystring`)
+ .query({ val: ['a', 'b', 'c'] })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { val: ['a', 'b', 'c'] });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring object with array and primitives
+ request
+ .get(`${uri}/querystring`)
+ .query({ array: ['a', 'b', 'c'], string: 'foo', number: 10 })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ array: ['a', 'b', 'c'],
+ string: 'foo',
+ number: 10
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring object with two arrays
+ request
+ .get(`${uri}/querystring`)
+ .query({ array1: ['a', 'b', 'c'], array2: [1, 2, 3] })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ array1: ['a', 'b', 'c'],
+ array2: [1, 2, 3]
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring object
+ request
+ .get(`${uri}/querystring`)
+ .query({ search: 'Manny' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { search: 'Manny' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring append original
+ request
+ .get(`${uri}/querystring?search=Manny`)
+ .query({ range: '1..5' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { search: 'Manny', range: '1..5' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring multiple objects
+ request
+ .get(`${uri}/querystring`)
+ .query({ search: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring with strings
+ request
+ .get(`${uri}/querystring`)
+ .query('search=Manny')
+ .query('range=1..5')
+ .query('order=desc')
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET querystring with strings and objects
+ request
+ .get(`${uri}/querystring`)
+ .query('search=Manny')
+ .query({ order: 'desc', range: '1..5' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ GET shorthand payload goes to querystring
+ request.get(
+ `${uri}/querystring`,
+ { foo: 'FOO', bar: 'BAR' },
+ (error, res) => {
+ try {
+ assert.deepEqual(res.body, { foo: 'FOO', bar: 'BAR' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ }
+);
+ HEAD shorthand payload goes to querystring
+ request.head(
+ `${uri}/querystring-in-header`,
+ { foo: 'FOO', bar: 'BAR' },
+ (error, res) => {
+ try {
+ assert.deepEqual(JSON.parse(res.headers.query), {
+ foo: 'FOO',
+ bar: 'BAR'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ }
+);
+ request(method, url)
+ request('GET', `${uri}/foo`).end((error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request(url)
+ request(`${uri}/foo`).end((error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request(url, fn)
+ request(`${uri}/foo`, (error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ req.timeout(ms)
+ const request_ = request.get(`${uri}/delay/3000`).timeout(1000);
+request_.end((error, res) => {
+ try {
+ assert(error, 'error missing');
+ assert.equal(1000, error.timeout, 'err.timeout missing');
+ assert.equal(
+ 'Timeout of 1000ms exceeded',
+ error.message,
+ 'err.message incorrect'
+ );
+ assert.equal(null, res);
+ assert(request_.timedout, true);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ req.timeout(ms) with redirect
+ const request_ = request.get(`${uri}/delay/const`).timeout(1000);
+request_.end((error, res) => {
+ try {
+ assert(error, 'error missing');
+ assert.equal(1000, error.timeout, 'err.timeout missing');
+ assert.equal(
+ 'Timeout of 1000ms exceeded',
+ error.message,
+ 'err.message incorrect'
+ );
+ assert.equal(null, res);
+ assert(request_.timedout, true);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ request event
+ request
+ .get(`${uri}/foo`)
+ .on('request', (request_) => {
+ try {
+ assert.equal(`${uri}/foo`, request_.url);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ })
+ .end();
+ response event
+ request
+ .get(`${uri}/foo`)
+ .on('response', (res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ })
+ .end();
+ response should set statusCode
+ request.get(`${uri}/ok`, (error, res) => {
+ try {
+ assert.strictEqual(res.statusCode, 200);
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+ req.toJSON()
+ request.get(`${uri}/ok`).end((error, res) => {
+ try {
+ const index = (res.request || res.req).toJSON();
+ for (const property of ['url', 'method', 'data', 'headers']) {
+ assert(index.hasOwnProperty(property));
+ }
+ next();
+ } catch (err) {
+ next(err);
+ }
+});
+
+
+
+ .retry(count)
+
+ should not retry if passed "0"
+ request
+ .get(`${base}/error`)
+ .retry(0)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not retry if passed an invalid number
+ request
+ .get(`${base}/error`)
+ .retry(-2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not retry if passed undefined
+ request
+ .get(`${base}/error`)
+ .retry(undefined)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle server error after repeat attempt
+ request
+ .get(`${base}/error`)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should retry if passed nothing
+ request
+ .get(`${base}/error`)
+ .retry()
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(1, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should retry if passed "true"
+ request
+ .get(`${base}/error`)
+ .retry(true)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(1, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle successful request after repeat attempt from server error
+ request
+ .get(`${base}/error/ok/${uniqid()}`)
+ .query({ qs: 'present' })
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'res.text');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle server timeout error after repeat attempt
+ request
+ .get(`${base}/delay/400`)
+ .timeout(200)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle successful request after repeat attempt from server timeout
+ const url = `/delay/1200/ok/${uniqid()}?built=in`;
+request
+ .get(base + url)
+ .query('string=ified')
+ .query({ json: 'ed' })
+ .timeout(600)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert.equal(res.text, `ok = ${url}&string=ified&json=ed`);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle successful request after repeat attempt from server timeout when using .then(fulfill, reject)
+ const url = `/delay/1200/ok/${uniqid()}?built=in`;
+request
+ .get(base + url)
+ .query('string=ified')
+ .query({ json: 'ed' })
+ .timeout(600)
+ .retry(1)
+ .then((res, error) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert.equal(res.text, `ok = ${url}&string=ified&json=ed`);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should correctly abort a retry attempt
+ let aborted = false;
+const request_ = request.get(`${base}/delay/400`).timeout(200).retry(2);
+request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+});
+request_.on('abort', () => {
+ aborted = true;
+});
+setTimeout(() => {
+ request_.abort();
+ setTimeout(() => {
+ try {
+ assert(aborted, 'should be aborted');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ }, 150);
+}, 150);
+ should correctly retain header fields
+ request
+ .get(`${base}/error/ok/${uniqid()}`)
+ .query({ qs: 'present' })
+ .retry(2)
+ .set('X-Foo', 'bar')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not retry on 4xx responses
+ request
+ .get(`${base}/bad-request`)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(0, error.retries, 'expected an error with 0 .retries');
+ assert.equal(400, error.status, 'expected an error status of 400');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should execute callback on retry if passed
+ let callbackCallCount = 0;
+function retryCallback(request) {
+ callbackCallCount++;
+}
+request
+ .get(`${base}/error`)
+ .retry(2, retryCallback)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ assert.equal(
+ 2,
+ callbackCallCount,
+ 'expected the callback to be called on each retry'
+ );
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ .timeout(ms)
+
+
+ when timeout is exceeded
+
+ should error
+ request
+ .get(`${base}/delay/500`)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should error in promise interface
+ request
+ .get(`${base}/delay/500`)
+ .timeout(150)
+ .catch((err) => {
+ assert(err, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof err.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', err.code, 'expected abort error code');
+ done();
+ });
+ should handle gzip timeout
+ request
+ .get(`${base}/delay/zip`)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should handle buffer timeout
+ request
+ .get(`${base}/delay/json`)
+ .buffer(true)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should error on deadline
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ deadline: 150 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ should support setting individual options
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ deadline: 10 })
+ .timeout({ response: 99_999 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ assert.equal('ETIME', error.errno);
+ done();
+ });
+ should error on response
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ response: 150 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ assert.equal('ETIMEDOUT', error.errno);
+ done();
+ });
+ should accept slow body with fast response
+ request
+ .get(`${base}/delay/slowbody`)
+ .timeout({ response: 1000 })
+ .on('progress', () => {
+ // This only makes the test faster without relying on arbitrary timeouts
+ request.get(`${base}/delay/slowbody/finish`).end();
+ })
+ .end(done);
+
+
+
+
+
+ request
+
+
+ use
+
+ should use plugin success
+ const now = `${Date.now()}`;
+function uuid(request_) {
+ request_.set('X-UUID', now);
+ return request_;
+}
+function prefix(request_) {
+ request_.url = uri + request_.url;
+ return request_;
+}
+request
+ .get('/echo')
+ .use(uuid)
+ .use(prefix)
+ .end((error, res) => {
+ assert.strictEqual(res.statusCode, 200);
+ assert.equal(res.get('X-UUID'), now);
+ done();
+ });
+
+
+
+
+
+ subclass
+
+ should be an instance of Request
+ const request_ = request.get('/');
+assert(request_ instanceof request.Request);
+ should use patched subclass
+ assert(OriginalRequest);
+let constructorCalled;
+let sendCalled;
+function NewRequest(...args) {
+ constructorCalled = true;
+ OriginalRequest.apply(this, args);
+}
+NewRequest.prototype = Object.create(OriginalRequest.prototype);
+NewRequest.prototype.send = function () {
+ sendCalled = true;
+ return this;
+};
+request.Request = NewRequest;
+const request_ = request.get('/').send();
+assert(constructorCalled);
+assert(sendCalled);
+assert(request_ instanceof NewRequest);
+assert(request_ instanceof OriginalRequest);
+ should use patched subclass in agent too
+ if (!request.agent) return; // Node-only
+function NewRequest(...args) {
+ OriginalRequest.apply(this, args);
+}
+NewRequest.prototype = Object.create(OriginalRequest.prototype);
+request.Request = NewRequest;
+const request_ = request.agent().del('/');
+assert(request_ instanceof NewRequest);
+assert(request_ instanceof OriginalRequest);
+
+
+
+ request
+
+
+ persistent agent
+
+ should gain a session on POST
+ agent3.post(`${base}/signin`).then((res) => {
+ res.should.have.status(200);
+ should.not.exist(res.headers['set-cookie']);
+ res.text.should.containEql('dashboard');
+ })
+ should start with empty session (set cookies)
+ agent1.get(`${base}/dashboard`).end((error, res) => {
+ should.exist(error);
+ res.should.have.status(401);
+ should.exist(res.headers['set-cookie']);
+ done();
+});
+ should gain a session (cookies already set)
+ agent1.post(`${base}/signin`).then((res) => {
+ res.should.have.status(200);
+ should.not.exist(res.headers['set-cookie']);
+ res.text.should.containEql('dashboard');
+ })
+ should persist cookies across requests
+ agent1.get(`${base}/dashboard`).then((res) => {
+ res.should.have.status(200);
+ })
+ should have the cookie set in the end callback
+ agent4
+ .post(`${base}/setcookie`)
+ .then(() => agent4.get(`${base}/getcookie`))
+ .then((res) => {
+ res.should.have.status(200);
+ assert.strictEqual(res.text, 'jar');
+ })
+ should not share cookies
+ agent2.get(`${base}/dashboard`).end((error, res) => {
+ should.exist(error);
+ res.should.have.status(401);
+ done();
+});
+ should not lose cookies between agents
+ agent1.get(`${base}/dashboard`).then((res) => {
+ res.should.have.status(200);
+ })
+ should be able to follow redirects
+ agent1.get(base).then((res) => {
+ res.should.have.status(200);
+ res.text.should.containEql('dashboard');
+ })
+ should be able to post redirects
+ agent1
+ .post(`${base}/redirect`)
+ .send({ foo: 'bar', baz: 'blaaah' })
+ .then((res) => {
+ res.should.have.status(200);
+ res.text.should.containEql('simple');
+ res.redirects.should.eql([`${base}/simple`]);
+ })
+ should be able to limit redirects
+ agent1
+ .get(base)
+ .redirects(0)
+ .end((error, res) => {
+ should.exist(error);
+ res.should.have.status(302);
+ res.redirects.should.eql([]);
+ res.header.location.should.equal('/dashboard');
+ done();
+ });
+ should be able to create a new session (clear cookie)
+ agent1.post(`${base}/signout`).then((res) => {
+ res.should.have.status(200);
+ should.exist(res.headers['set-cookie']);
+ })
+ should regenerate with an empty session
+ agent1.get(`${base}/dashboard`).end((error, res) => {
+ should.exist(error);
+ res.should.have.status(401);
+ should.not.exist(res.headers['set-cookie']);
+ done();
+});
+
+
+
+
+
+ Basic auth
+
+
+ when credentials are present in url
+
+ should set Authorization
+ const new_url = URL.parse(base);
+new_url.auth = 'tobi:learnboost';
+new_url.pathname = '/basic-auth';
+request.get(URL.format(new_url)).end((error, res) => {
+ res.status.should.equal(200);
+ done();
+});
+
+
+
+ req.auth(user, pass)
+
+ should set Authorization
+ request
+ .get(`${base}/basic-auth`)
+ .auth('tobi', 'learnboost')
+ .end((error, res) => {
+ res.status.should.equal(200);
+ done();
+ });
+
+
+
+ req.auth(user + ":" + pass)
+
+ should set authorization
+ request
+ .get(`${base}/basic-auth/again`)
+ .auth('tobi')
+ .end((error, res) => {
+ res.status.should.eql(200);
+ done();
+ });
+
+
+
+
+
+ [node] request
+
+
+ with an url
+
+ should preserve the encoding of the url
+ request.get(`${base}/url?a=(b%29`).end((error, res) => {
+ assert.equal('/url?a=(b%29', res.text);
+ done();
+});
+
+
+
+ with an object
+
+ should format the url
+ request.get(url.parse(`${base}/login`)).then((res) => {
+ assert(res.ok);
+ })
+
+
+
+ without a schema
+
+ should default to http
+ request.get(`${base}/login`).then((res) => {
+ assert.equal(res.status, 200);
+ })
+
+
+
+ res.toJSON()
+
+ should describe the response
+ request
+ .post(`${base}/echo`)
+ .send({ foo: 'baz' })
+ .then((res) => {
+ const object = res.toJSON();
+ assert.equal('object', typeof object.header);
+ assert.equal('object', typeof object.req);
+ assert.equal(200, object.status);
+ assert.equal('{"foo":"baz"}', object.text);
+ })
+
+
+
+ res.links
+
+ should default to an empty object
+ request.get(`${base}/login`).then((res) => {
+ res.links.should.eql({});
+ })
+ should parse the Link header field
+ request.get(`${base}/links`).end((error, res) => {
+ res.links.next.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=2'
+ );
+ done();
+});
+
+
+
+ req.unset(field)
+
+ should remove the header field
+ request
+ .post(`${base}/echo`)
+ .unset('User-Agent')
+ .end((error, res) => {
+ assert.equal(void 0, res.header['user-agent']);
+ done();
+ });
+
+
+
+ case-insensitive
+
+ should set/get header fields case-insensitively
+ const r = request.post(`${base}/echo`);
+r.set('MiXeD', 'helloes');
+assert.strictEqual(r.get('mixed'), 'helloes');
+ should unset header fields case-insensitively
+ const r = request.post(`${base}/echo`);
+r.set('MiXeD', 'helloes');
+r.unset('MIXED');
+assert.strictEqual(r.get('mixed'), undefined);
+
+
+
+ req.write(str)
+
+ should write the given data
+ const request_ = request.post(`${base}/echo`);
+request_.set('Content-Type', 'application/json');
+assert.equal('boolean', typeof request_.write('{"name"'));
+assert.equal('boolean', typeof request_.write(':"tobi"}'));
+request_.end((error, res) => {
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+});
+
+
+
+ req.pipe(stream)
+
+ should pipe the response to the given stream
+ const stream = new EventEmitter();
+stream.buf = '';
+stream.writable = true;
+stream.write = function (chunk) {
+ this.buf += chunk;
+};
+stream.end = function () {
+ this.buf.should.equal('{"name":"tobi"}');
+ done();
+};
+request.post(`${base}/echo`).send('{"name":"tobi"}').pipe(stream);
+
+
+
+ .buffer()
+
+ should enable buffering
+ request
+ .get(`${base}/custom`)
+ .buffer()
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal('custom stuff', res.text);
+ assert(res.buffered);
+ done();
+ });
+ should take precedence over request.buffer['someMimeType'] = false
+ const type = 'application/barbaz';
+const send = 'some text';
+request.buffer[type] = false;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .buffer()
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+
+
+
+ .buffer(false)
+
+ should disable buffering
+ request
+ .post(`${base}/echo`)
+ .type('application/x-dog')
+ .send('hello this is dog')
+ .buffer(false)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal('hello this is dog');
+ done();
+ });
+ });
+ should take precedence over request.buffer['someMimeType'] = true
+ const type = 'application/foobar';
+const send = 'hello this is a dog';
+request.buffer[type] = true;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .buffer(false)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+
+
+
+ .withCredentials()
+
+ should not throw an error when using the client-side "withCredentials" method
+ request
+ .get(`${base}/custom`)
+ .withCredentials()
+ .end((error, res) => {
+ assert.ifError(error);
+ done();
+ });
+
+
+
+ .agent()
+
+ should return the defaut agent
+ const request_ = request.post(`${base}/echo`);
+request_.agent().should.equal(false);
+done();
+
+
+
+ .agent(undefined)
+
+ should set an agent to undefined and ensure it is chainable
+ const request_ = request.get(`${base}/echo`);
+const returnValue = request_.agent(undefined);
+returnValue.should.equal(request_);
+assert.strictEqual(request_.agent(), undefined);
+done();
+
+
+
+ .agent(new http.Agent())
+
+ should set passed agent
+ const http = require('http');
+const request_ = request.get(`${base}/echo`);
+const agent = new http.Agent();
+const returnValue = request_.agent(agent);
+returnValue.should.equal(request_);
+request_.agent().should.equal(agent);
+done();
+
+
+
+ with a content type other than application/json or text/*
+
+ should still use buffering
+ return request
+ .post(`${base}/echo`)
+ .type('application/x-dog')
+ .send('hello this is dog')
+ .then((res) => {
+ assert.equal(null, res.text);
+ assert.equal(res.body.toString(), 'hello this is dog');
+ res.buffered.should.be.true;
+ });
+
+
+
+ content-length
+
+ should be set to the byte length of a non-buffer object
+ const decoder = new StringDecoder('utf8');
+let img = fs.readFileSync(`${__dirname}/fixtures/test.png`);
+img = decoder.write(img);
+request
+ .post(`${base}/echo`)
+ .type('application/x-image')
+ .send(img)
+ .buffer(false)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(!res.buffered);
+ assert.equal(res.header['content-length'], Buffer.byteLength(img));
+ done();
+ });
+ should be set to the length of a buffer object
+ const img = fs.readFileSync(`${__dirname}/fixtures/test.png`);
+request
+ .post(`${base}/echo`)
+ .type('application/x-image')
+ .send(img)
+ .buffer(true)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.buffered);
+ assert.equal(res.header['content-length'], img.length);
+ done();
+ });
+
+
+
+
+
+ req.buffer['someMimeType']
+
+ should respect that agent.buffer(true) takes precedent
+ const agent = request.agent();
+agent.buffer(true);
+const type = 'application/somerandomtype';
+const send = 'somerandomtext';
+request.buffer[type] = false;
+agent
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+ should respect that agent.buffer(false) takes precedent
+ const agent = request.agent();
+agent.buffer(false);
+const type = 'application/barrr';
+const send = 'some random text2';
+request.buffer[type] = true;
+agent
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+ should disable buffering for that mimetype when false
+ const type = 'application/bar';
+const send = 'some random text';
+request.buffer[type] = false;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+ should enable buffering for that mimetype when true
+ const type = 'application/baz';
+const send = 'woooo';
+request.buffer[type] = true;
+request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+ should fallback to default handling for that mimetype when undefined
+ const type = 'application/bazzz';
+const send = 'woooooo';
+return request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .then((res) => {
+ assert.equal(res.type, type);
+ assert.equal(send, res.body.toString());
+ assert(res.buffered);
+ });
+
+
+
+ exports
+
+ should expose .protocols
+ Object.keys(request.protocols).should.eql(['http:', 'https:', 'http2:']);
+ should expose .serialize
+ Object.keys(request.serialize).should.eql([
+ 'application/x-www-form-urlencoded',
+ 'application/json'
+]);
+ should expose .parse
+ Object.keys(request.parse).should.eql([
+ 'application/x-www-form-urlencoded',
+ 'application/json',
+ 'text',
+ 'application/json-seq',
+ 'application/octet-stream',
+ 'application/pdf',
+ 'image'
+]);
+ should export .buffer
+ Object.keys(request.buffer).should.eql([]);
+
+
+
+ flags
+
+
+ with 4xx response
+
+ should set res.error and res.clientError
+ request.get(`${base}/notfound`).end((error, res) => {
+ assert(error);
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(res.clientError, 'response should be a client error');
+ assert(!res.serverError, 'response should not be a server error');
+ done();
+});
+
+
+
+ with 5xx response
+
+ should set res.error and res.serverError
+ request.get(`${base}/error`).end((error, res) => {
+ assert(error);
+ assert(!res.ok, 'response should not be ok');
+ assert(!res.notFound, 'response should not be notFound');
+ assert(res.error, 'response should be an error');
+ assert(!res.clientError, 'response should not be a client error');
+ assert(res.serverError, 'response should be a server error');
+ done();
+});
+
+
+
+ with 404 Not Found
+
+ should res.notFound
+ request.get(`${base}/notfound`).end((error, res) => {
+ assert(error);
+ assert(res.notFound, 'response should be .notFound');
+ done();
+});
+
+
+
+ with 400 Bad Request
+
+ should set req.badRequest
+ request.get(`${base}/bad-request`).end((error, res) => {
+ assert(error);
+ assert(res.badRequest, 'response should be .badRequest');
+ done();
+});
+
+
+
+ with 401 Bad Request
+
+ should set res.unauthorized
+ request.get(`${base}/unauthorized`).end((error, res) => {
+ assert(error);
+ assert(res.unauthorized, 'response should be .unauthorized');
+ done();
+});
+
+
+
+ with 406 Not Acceptable
+
+ should set res.notAcceptable
+ request.get(`${base}/not-acceptable`).end((error, res) => {
+ assert(error);
+ assert(res.notAcceptable, 'response should be .notAcceptable');
+ done();
+});
+
+
+
+ with 204 No Content
+
+ should set res.noContent
+ request.get(`${base}/no-content`).end((error, res) => {
+ assert(!error);
+ assert(res.noContent, 'response should be .noContent');
+ done();
+});
+
+
+
+ with 201 Created
+
+ should set res.created
+ request.post(`${base}/created`).end((error, res) => {
+ assert(!error);
+ assert(res.created, 'response should be .created');
+ done();
+});
+
+
+
+ with 422 Unprocessable Entity
+
+ should set res.unprocessableEntity
+ request.post(`${base}/unprocessable-entity`).end((error, res) => {
+ assert(error);
+ assert(
+ res.unprocessableEntity,
+ 'response should be .unprocessableEntity'
+ );
+ done();
+});
+
+
+
+
+
+ Merging objects
+
+ Don't mix Buffer and JSON
+ assert.throws(() => {
+ request
+ .post('/echo')
+ .send(Buffer.from('some buffer'))
+ .send({ allowed: false });
+});
+
+
+
+ req.send(String)
+
+ should default to "form"
+ request
+ .post(`${base}/echo`)
+ .send('user[name]=tj')
+ .send('user[email]=tj@vision-media.ca')
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.body.should.eql({
+ user: { name: 'tj', email: 'tj@vision-media.ca' }
+ });
+ done();
+ });
+
+
+
+ res.body
+
+
+ application/x-www-form-urlencoded
+
+ should parse the body
+ request.get(`${base}/form-data`).end((error, res) => {
+ res.text.should.equal('pet[name]=manny');
+ res.body.should.eql({ pet: { name: 'manny' } });
+ done();
+});
+
+
+
+
+
+ request.get().http2()
+
+ should preserve the encoding of the url
+ request
+ .get(`${base}/url?a=(b%29`)
+ .http2()
+ .end((error, res) => {
+ assert.equal('/url?a=(b%29', res.text);
+ done();
+ });
+ should format the url
+ request
+ .get(url.parse(`${base}/login`))
+ .http2()
+ .then((res) => {
+ assert(res.ok);
+ })
+
+
+
+ https
+
+
+ certificate authority
+
+
+ request
+
+ should give a good response
+ request
+ .get(testEndpoint)
+ .ca(ca)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ should reject unauthorized response
+ return request
+ .get(testEndpoint)
+ .trustLocalhost(false)
+ .then(
+ () => {
+ throw new Error('Allows MITM');
+ },
+ () => {}
+ );
+ should not reject unauthorized response
+ return request
+ .get(testEndpoint)
+ .disableTLSCerts()
+ .then(({ status }) => {
+ assert.strictEqual(status, 200);
+ });
+ should trust localhost unauthorized response
+ return request.get(testEndpoint).trustLocalhost(true);
+ should trust overriden localhost unauthorized response
+ return request
+ .get(`https://example.com:${server.address().port}`)
+ .connect('127.0.0.1')
+ .trustLocalhost();
+
+
+
+ .agent
+
+ should be able to make multiple requests without redefining the certificate
+ const agent = request.agent({ ca });
+agent.get(testEndpoint).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ agent.get(url.parse(testEndpoint)).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+});
+
+
+
+
+
+ client certificates
+
+
+
+
+
+
+
+
+ res.body
+
+
+ image/png
+
+ should parse the body
+ request.get(`${base}/image`).end((error, res) => {
+ res.type.should.equal('image/png');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+});
+
+
+
+ application/octet-stream
+
+ should parse the body
+ request
+ .get(`${base}/image-as-octets`)
+ .buffer(true) // that's tech debt :(
+ .end((error, res) => {
+ res.type.should.equal('application/octet-stream');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+ });
+
+
+
+ application/octet-stream
+
+ should parse the body (using responseType)
+ request
+ .get(`${base}/image-as-octets`)
+ .responseType('blob')
+ .end((error, res) => {
+ res.type.should.equal('application/octet-stream');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+ });
+
+
+
+
+
+ zlib
+
+ should deflate the content
+ request.get(base).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.equal(subject);
+ res.headers['content-length'].should.be.below(subject.length);
+ done();
+});
+ should protect from zip bombs
+ request
+ .get(base)
+ .buffer(true)
+ .maxResponseSize(1)
+ .end((error, res) => {
+ try {
+ assert.equal('Maximum response size reached', error && error.message);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should ignore trailing junk
+ request.get(`${base}/junk`).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.equal(subject);
+ done();
+});
+ should ignore missing data
+ request.get(`${base}/chopped`).end((error, res) => {
+ assert.equal(undefined, error);
+ res.should.have.status(200);
+ res.text.should.startWith(subject);
+ done();
+});
+ should handle corrupted responses
+ request.get(`${base}/corrupt`).end((error, res) => {
+ assert(error, 'missing error');
+ assert(!res, 'response should not be defined');
+ done();
+});
+ should handle no content with gzip header
+ request.get(`${base}/nocontent`).end((error, res) => {
+ assert.ifError(error);
+ assert(res);
+ res.should.have.status(204);
+ res.text.should.equal('');
+ res.headers.should.not.have.property('content-length');
+ done();
+});
+
+ without encoding set
+
+ should buffer if asked
+ return request
+ .get(`${base}/binary`)
+ .buffer(true)
+ .then((res) => {
+ res.should.have.status(200);
+ assert(res.headers['content-length']);
+ assert(res.body.byteLength);
+ assert.equal(subject, res.body.toString());
+ });
+ should emit buffers
+ request.get(`${base}/binary`).end((error, res) => {
+ res.should.have.status(200);
+ res.headers['content-length'].should.be.below(subject.length);
+ res.on('data', (chunk) => {
+ chunk.should.have.length(subject.length);
+ });
+ res.on('end', done);
+});
+
+
+
+
+
+ req.lookup()
+
+ should set a custom lookup
+ const r = request.get(`${base}/ok`).lookup(myLookup);
+assert(r.lookup() === myLookup);
+r.then((res) => {
+ res.text.should.equal('ok');
+ done();
+});
+
+
+
+ Multipart
+
+
+ #field(name, value)
+
+ should set a multipart field value
+ const request_ = request.post(`${base}/echo`);
+request_.field('user[name]', 'tobi');
+request_.field('user[age]', '2');
+request_.field('user[species]', 'ferret');
+return request_.then((res) => {
+ res.body['user[name]'].should.equal('tobi');
+ res.body['user[age]'].should.equal('2');
+ res.body['user[species]'].should.equal('ferret');
+});
+ should work with file attachments
+ const request_ = request.post(`${base}/echo`);
+request_.field('name', 'Tobi');
+request_.attach('document', 'test/node/fixtures/user.html');
+request_.field('species', 'ferret');
+return request_.then((res) => {
+ res.body.name.should.equal('Tobi');
+ res.body.species.should.equal('ferret');
+ const html = res.files.document;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+});
+
+
+
+ #attach(name, path)
+
+ should attach a file
+ const request_ = request.post(`${base}/echo`);
+request_.attach('one', 'test/node/fixtures/user.html');
+request_.attach('two', 'test/node/fixtures/user.json');
+request_.attach('three', 'test/node/fixtures/user.txt');
+return request_.then((res) => {
+ const html = res.files.one;
+ const json = res.files.two;
+ const text = res.files.three;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+ json.originalFilename.should.equal('user.json');
+ json.mimetype.should.equal('application/json');
+ read(json.filepath).should.equal('{"name":"tobi"}');
+ text.originalFilename.should.equal('user.txt');
+ text.mimetype.should.equal('text/plain');
+ read(text.filepath).should.equal('Tobi');
+});
+
+ when a file does not exist
+
+ should fail the request with an error
+ const request_ = request.post(`${base}/echo`);
+request_.attach('name', 'foo');
+// request_.attach('name2', 'bar');
+// request_.attach('name3', 'baz');
+request_.end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed.');
+ error.code.should.equal('ENOENT');
+ error.message.should.containEql('ENOENT');
+ if (IS_WINDOWS) {
+ error.path.toLowerCase().should.equal(
+ getFullPath('foo').toLowerCase()
+ );
+ } else {
+ error.path.should.equal(getFullPath('foo'));
+ }
+ done();
+});
+ promise should fail
+ return request
+ .post(`${base}/echo`)
+ .field({ a: 1, b: 2 })
+ .attach('c', 'does-not-exist.txt')
+ .then(
+ (res) => assert.fail('It should not allow this'),
+ (err) => {
+ err.code.should.equal('ENOENT');
+ if (IS_WINDOWS) {
+ err.path.toLowerCase().should.equal(
+ getFullPath('does-not-exist.txt').toLowerCase()
+ );
+ } else {
+ err.path.should.equal(getFullPath('does-not-exist.txt'));
+ }
+ }
+ );
+ should report ENOENT via the callback
+ request
+ .post(`${base}/echo`)
+ .attach('name', 'file-does-not-exist')
+ .end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed');
+ error.code.should.equal('ENOENT');
+ done();
+ });
+ should report ENOENT via Promise
+ return request
+ .post(`${base}/echo`)
+ .attach('name', 'file-does-not-exist')
+ .then(
+ (res) => assert.fail('Request should have failed'),
+ (err) => err.code.should.equal('ENOENT')
+ );
+
+
+
+
+
+ #attach(name, path, filename)
+
+ should use the custom filename
+ request
+ .post(`${base}/echo`)
+ .attach('document', 'test/node/fixtures/user.html', 'doc.html')
+ .then((res) => {
+ const html = res.files.document;
+ html.originalFilename.should.equal('doc.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+ })
+ should fire progress event
+ let loaded = 0;
+let total = 0;
+let uploadEventWasFired = false;
+request
+ .post(`${base}/echo`)
+ .attach('document', 'test/node/fixtures/user.html')
+ .on('progress', (event) => {
+ total = event.total;
+ loaded = event.loaded;
+ if (event.direction === 'upload') {
+ uploadEventWasFired = true;
+ }
+ })
+ .end((error, res) => {
+ if (error) return done(error);
+ const html = res.files.document;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('<h1>name</h1>');
+ total.should.equal(223);
+ loaded.should.equal(223);
+ uploadEventWasFired.should.equal(true);
+ done();
+ });
+ filesystem errors should be caught
+ request
+ .post(`${base}/echo`)
+ .attach('filedata', 'test/node/fixtures/non-existent-file.ext')
+ .end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed.');
+ error.code.should.equal('ENOENT');
+ if (IS_WINDOWS) {
+ error.path.toLowerCase().should.equal(
+ getFullPath('test/node/fixtures/non-existent-file.ext').toLowerCase()
+ );
+ } else {
+ error.path.should.equal(
+ getFullPath('test/node/fixtures/non-existent-file.ext')
+ );
+ }
+ done();
+ });
+
+
+
+ #field(name, val)
+
+ should set a multipart field value
+ request
+ .post(`${base}/echo`)
+ .field('first-name', 'foo')
+ .field('last-name', 'bar')
+ .end((error, res) => {
+ if (error) done(error);
+ res.should.be.ok();
+ res.body['first-name'].should.equal('foo');
+ res.body['last-name'].should.equal('bar');
+ done();
+ });
+
+
+
+ #field(object)
+
+ should set multiple multipart fields
+ request
+ .post(`${base}/echo`)
+ .field({ 'first-name': 'foo', 'last-name': 'bar' })
+ .end((error, res) => {
+ if (error) done(error);
+ res.should.be.ok();
+ res.body['first-name'].should.equal('foo');
+ res.body['last-name'].should.equal('bar');
+ done();
+ });
+
+
+
+
+
+ with network error
+
+ should error
+ request.get(`http://localhost:${this.port}/`).end((error, res) => {
+ assert(error, 'expected an error');
+ done();
+});
+
+
+
+ request
+
+
+ not modified
+
+ should start with 200
+ request.get(`${base}/if-mod`).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.match(/^\d+$/);
+ ts = Number(res.text);
+ done();
+});
+ should then be 304
+ request
+ .get(`${base}/if-mod`)
+ .set('If-Modified-Since', new Date(ts).toUTCString())
+ .end((error, res) => {
+ res.should.have.status(304);
+ // res.text.should.be.empty
+ done();
+ });
+
+
+
+
+
+ req.parse(fn)
+
+ should take precedence over default parsers
+ request
+ .get(`${base}/manny`)
+ .parse(request.parse['application/json'])
+ .end((error, res) => {
+ assert(res.ok);
+ assert.equal('{"name":"manny"}', res.text);
+ assert.equal('manny', res.body.name);
+ done();
+ });
+ should be the only parser
+ request
+ .get(`${base}/image`)
+ .buffer(false)
+ .parse((res, fn) => {
+ res.on('data', () => {});
+ })
+ .then((res) => {
+ assert(res.ok);
+ assert.strictEqual(res.text, undefined);
+ res.body.should.eql({});
+ })
+ should emit error if parser throws
+ request
+ .get(`${base}/manny`)
+ .parse(() => {
+ throw new Error('I am broken');
+ })
+ .on('error', (error) => {
+ error.message.should.equal('I am broken');
+ done();
+ })
+ .end();
+ should emit error if parser returns an error
+ request
+ .get(`${base}/manny`)
+ .parse((res, fn) => {
+ fn(new Error('I am broken'));
+ })
+ .on('error', (error) => {
+ error.message.should.equal('I am broken');
+ done();
+ })
+ .end();
+
+
+
+ pipe on redirect
+
+ should follow Location
+ const stream = fs.createWriteStream(destinationPath);
+const redirects = [];
+const request_ = request
+ .get(base)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .connect({
+ inapplicable: 'should be ignored'
+ });
+stream.on('finish', () => {
+ redirects.should.eql(['/movies', '/movies/all', '/movies/all/0']);
+ fs.readFileSync(destinationPath, 'utf8').should.eql('first movie page');
+ done();
+});
+request_.pipe(stream);
+
+
+
+ request pipe
+
+ should act as a writable stream
+ const request_ = request.post(base);
+const stream = fs.createReadStream('test/node/fixtures/user.json');
+request_.type('json');
+request_.on('response', (res) => {
+ res.body.should.eql({ name: 'tobi' });
+ done();
+});
+stream.pipe(request_);
+ end() stops piping
+ const stream = fs.createWriteStream(destinationPath);
+request.get(base).end((error, res) => {
+ try {
+ res.pipe(stream);
+ return done(new Error('Did not prevent nonsense pipe'));
+ } catch {
+ /* expected error */
+ }
+ done();
+});
+ should act as a readable stream
+ const stream = fs.createWriteStream(destinationPath);
+let responseCalled = false;
+const request_ = request.get(base);
+request_.type('json');
+request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+});
+stream.on('finish', () => {
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ responseCalled.should.be.true();
+ done();
+});
+request_.pipe(stream);
+ should follow redirects
+ const stream = fs.createWriteStream(destinationPath);
+let responseCalled = false;
+const request_ = request.get(base + '/redirect');
+request_.type('json');
+request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+});
+stream.on('finish', () => {
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ responseCalled.should.be.true();
+ done();
+});
+request_.pipe(stream);
+ should not throw on bad redirects
+ const stream = fs.createWriteStream(destinationPath);
+let responseCalled = false;
+let errorCalled = false;
+const request_ = request.get(base + '/badRedirectNoLocation');
+request_.type('json');
+request_.on('response', (res) => {
+ responseCalled = true;
+});
+request_.on('error', (error) => {
+ error.message.should.eql('No location header for redirect');
+ errorCalled = true;
+ stream.end();
+});
+stream.on('finish', () => {
+ responseCalled.should.be.false();
+ errorCalled.should.be.true();
+ done();
+});
+request_.pipe(stream);
+
+
+
+ req.query(String)
+
+ should support passing in a string
+ request
+ .del(base)
+ .query('name=t%F6bi')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi' });
+ done();
+ });
+ should work with url query-string and string for query
+ request
+ .del(`${base}/?name=tobi`)
+ .query('age=2%20')
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', age: '2 ' });
+ done();
+ });
+ should support compound elements in a string
+ request
+ .del(base)
+ .query('name=t%F6bi&age=2')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2' });
+ done();
+ });
+ should work when called multiple times with a string
+ request
+ .del(base)
+ .query('name=t%F6bi')
+ .query('age=2%F6')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2%F6' });
+ done();
+ });
+ should work with normal `query` object and query string
+ request
+ .del(base)
+ .query('name=t%F6bi')
+ .query({ age: '2' })
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2' });
+ done();
+ });
+ should not encode raw backticks, but leave encoded ones as is
+ return Promise.all([
+ request
+ .get(`${base}/raw-query`)
+ .query('name=`t%60bi`&age`=2')
+ .then((res) => {
+ res.text.should.eql('name=`t%60bi`&age`=2');
+ }),
+ request.get(base + '/raw-query?`age%60`=2%60`').then((res) => {
+ res.text.should.eql('`age%60`=2%60`');
+ }),
+ request
+ .get(`${base}/raw-query`)
+ .query('name=`t%60bi`')
+ .query('age`=2')
+ .then((res) => {
+ res.text.should.eql('name=`t%60bi`&age`=2');
+ })
+]);
+
+
+
+ req.query(Object)
+
+ should construct the query-string
+ request
+ .del(base)
+ .query({ name: 'tobi' })
+ .query({ order: 'asc' })
+ .query({ limit: ['1', '2'] })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
+ done();
+ });
+ should encode raw backticks
+ request
+ .get(`${base}/raw-query`)
+ .query({ name: '`tobi`' })
+ .query({ 'orde%60r': null })
+ .query({ '`limit`': ['%602`'] })
+ .end((error, res) => {
+ res.text.should.eql('name=%60tobi%60&orde%2560r&%60limit%60=%25602%60');
+ done();
+ });
+ should not error on dates
+ const date = new Date(0);
+request
+ .del(base)
+ .query({ at: date })
+ .end((error, res) => {
+ assert.equal(date.toISOString(), res.body.at);
+ done();
+ });
+ should work after setting header fields
+ request
+ .del(base)
+ .set('Foo', 'bar')
+ .set('Bar', 'baz')
+ .query({ name: 'tobi' })
+ .query({ order: 'asc' })
+ .query({ limit: ['1', '2'] })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
+ done();
+ });
+ should append to the original query-string
+ request
+ .del(`${base}/?name=tobi`)
+ .query({ order: 'asc' })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc' });
+ done();
+ });
+ should retain the original query-string
+ request.del(`${base}/?name=tobi`).end((error, res) => {
+ res.body.should.eql({ name: 'tobi' });
+ done();
+});
+ should keep only keys with null querystring values
+ request
+ .del(`${base}/url`)
+ .query({ nil: null })
+ .end((error, res) => {
+ res.text.should.equal('/url?nil');
+ done();
+ });
+ query-string should be sent on pipe
+ this.timeout(15_000);
+const request_ = request.put(`${base}/?name=tobi`);
+const stream = fs.createReadStream('test/node/fixtures/user.json');
+request_.on('response', (res) => {
+ res.body.should.eql({ name: 'tobi' });
+ done();
+});
+request_.on('error', (err) => {
+ done(err);
+});
+stream.on('error', function (err) {
+ done(err);
+});
+stream.pipe(request_);
+
+
+
+ request.get
+
+
+ on 301 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-301`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 302 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-302`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 303 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-303`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 307 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-307`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 308 redirect
+
+ should follow Location with a GET request
+ const request_ = request.get(`${base}/test-308`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+
+
+ request.post
+
+
+ on 301 redirect
+
+ should follow Location with a GET request
+ const request_ = request.post(`${base}/test-301`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 302 redirect
+
+ should follow Location with a GET request
+ const request_ = request.post(`${base}/test-302`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 303 redirect
+
+ should follow Location with a GET request
+ const request_ = request.post(`${base}/test-303`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+});
+
+
+
+ on 307 redirect
+
+ should follow Location with a POST request
+ const request_ = request.post(`${base}/test-307`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('POST');
+ done();
+});
+
+
+
+ on 308 redirect
+
+ should follow Location with a POST request
+ const request_ = request.post(`${base}/test-308`).redirects(1);
+request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('POST');
+ done();
+});
+
+
+
+
+
+ request
+
+
+ on redirect
+
+ should merge cookies if agent is used
+ request
+ .agent()
+ .get(`${base}/cookie-redirect`)
+ .set('Cookie', 'orig=1; replaced=not')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), 'orig=1/.test');
+ assert(/replaced=yes/.test(res.text), 'replaced=yes/.test');
+ assert(/from-redir=1/.test(res.text), 'from-redir=1');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not merge cookies if agent is not used
+ request
+ .get(`${base}/cookie-redirect`)
+ .set('Cookie', 'orig=1; replaced=not')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), '/orig=1');
+ assert(/replaced=not/.test(res.text), '/replaced=not');
+ assert(!/replaced=yes/.test(res.text), '!/replaced=yes');
+ assert(!/from-redir/.test(res.text), '!/from-redir');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should have previously set cookie for subsquent requests when agent is used
+ const agent = request.agent();
+agent.get(`${base}/set-cookie`).end((error) => {
+ assert.ifError(error);
+ agent
+ .get(`${base}/show-cookies`)
+ .set({ Cookie: 'orig=1' })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), 'orig=1/.test');
+ assert(/persist=123/.test(res.text), 'persist=123');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+});
+ should follow Location
+ const redirects = [];
+request
+ .get(base)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should follow Location with IP override
+ const redirects = [];
+const url = URL.parse(base);
+return request
+ .get(`http://redir.example.com:${url.port || '80'}${url.pathname}`)
+ .connect({
+ '*': url.hostname
+ })
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ });
+ should follow Location with IP:port override
+ const redirects = [];
+const url = URL.parse(base);
+return request
+ .get(`http://redir.example.com:9999${url.pathname}`)
+ .connect({
+ '*': { host: url.hostname, port: url.port || 80 }
+ })
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ });
+ should not follow on HEAD by default
+ const redirects = [];
+return request
+ .head(base)
+ .ok(() => true)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql([]);
+ res.status.should.equal(302);
+ });
+ should follow on HEAD when redirects are set
+ const redirects = [];
+request
+ .head(base)
+ .redirects(10)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = [];
+ array.push('/movies', '/movies/all', '/movies/all/0');
+ redirects.should.eql(array);
+ assert(!res.text);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should remove Content-* fields
+ request
+ .post(`${base}/header`)
+ .type('txt')
+ .set('X-Foo', 'bar')
+ .set('X-Bar', 'baz')
+ .send('hey')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ res.body.should.have.property('x-bar', 'baz');
+ res.body.should.not.have.property('content-type');
+ res.body.should.not.have.property('content-length');
+ res.body.should.not.have.property('transfer-encoding');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should retain cookies
+ request
+ .get(`${base}/header`)
+ .set('Cookie', 'foo=bar;')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('cookie', 'foo=bar;');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should not resend query parameters
+ const redirects = [];
+const query = [];
+request
+ .get(`${base}/?foo=bar`)
+ .on('redirect', (res) => {
+ query.push(res.headers.query);
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = [];
+ array.push('/movies', '/movies/all', '/movies/all/0');
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ query.should.eql(['{"foo":"bar"}', '{}', '{}']);
+ res.headers.query.should.eql('{}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should handle no location header
+ request.get(`${base}/bad-redirect`).end((error, res) => {
+ try {
+ error.message.should.equal('No location header for redirect');
+ done();
+ } catch (err) {
+ done(err);
+ }
+});
+
+ when relative
+
+ should redirect to a sibling path
+ const redirects = [];
+request
+ .get(`${base}/relative`)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ redirects.should.eql(['tobi']);
+ res.text.should.equal('tobi');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ should redirect to a parent path
+ const redirects = [];
+request
+ .get(`${base}/relative/sub`)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ redirects.should.eql(['../tobi']);
+ res.text.should.equal('tobi');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+
+
+ req.redirects(n)
+
+ should alter the default number of redirects to follow
+ const redirects = [];
+request
+ .get(base)
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = [];
+ assert(res.redirect, 'res.redirect');
+ array.push('/movies', '/movies/all');
+ redirects.should.eql(array);
+ res.text.should.match(/Moved Temporarily|Found/);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+
+
+ on POST
+
+ should redirect as GET
+ const redirects = [];
+return request
+ .post(`${base}/movie`)
+ .send({ name: 'Tobi' })
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql(['/movies/all/0']);
+ res.text.should.equal('first movie page');
+ });
+ using multipart/form-data should redirect as GET
+ const redirects = [];
+request
+ .post(`${base}/movie`)
+ .type('form')
+ .field('name', 'Tobi')
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql(['/movies/all/0']);
+ res.text.should.equal('first movie page');
+ });
+
+
+
+
+
+ response
+
+ should act as a readable stream
+ const request_ = request.get(base).buffer(false);
+request_.end((error, res) => {
+ if (error) return done(error);
+ let trackEndEvent = 0;
+ let trackCloseEvent = 0;
+ res.on('end', () => {
+ trackEndEvent++;
+ trackEndEvent.should.equal(1);
+ if (!process.env.HTTP2_TEST) {
+ trackCloseEvent.should.equal(0); // close should not have been called
+ }
+ done();
+ });
+ res.on('close', () => {
+ trackCloseEvent++;
+ });
+ setTimeout(() => {
+ (() => {
+ res.pause();
+ }).should.not.throw();
+ (() => {
+ res.resume();
+ }).should.not.throw();
+ (() => {
+ res.destroy();
+ }).should.not.throw();
+ }, 50);
+});
+
+
+
+ req.serialize(fn)
+
+ should take precedence over default parsers
+ request
+ .post(`${base}/echo`)
+ .send({ foo: 123 })
+ .serialize(() => '{"bar":456}')
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal('{"bar":456}', res.text);
+ assert.equal(456, res.body.bar);
+ done();
+ });
+
+
+
+ res.toError()
+
+ should return an Error
+ request.get(base).end((err, res) => {
+ const error = res.toError();
+ assert.equal(error.status, 400);
+ assert.equal(error.method, 'GET');
+ assert.equal(error.path, '/');
+ assert.equal(error.message, 'cannot GET / (400)');
+ assert.equal(error.text, 'invalid json');
+ done();
+});
+
+
+
+ [unix-sockets] http
+
+
+ request
+
+ path: / (root)
+ request.get(`${base}/`).end((error, res) => {
+ assert(res.ok);
+ assert.strictEqual('root ok!', res.text);
+ done();
+});
+ path: /request/path
+ request.get(`${base}/request/path`).end((error, res) => {
+ assert(res.ok);
+ assert.strictEqual('request path ok!', res.text);
+ done();
+});
+
+
+
+
+
+ [unix-sockets] https
+
+
+ request
+
+ path: / (root)
+ request
+ .get(`${base}/`)
+ .ca(cacert)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('root ok!', res.text);
+ done();
+ });
+ path: /request/path
+ request
+ .get(`${base}/request/path`)
+ .ca(cacert)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('request path ok!', res.text);
+ done();
+ });
+
+
+
+
+
+ req.get()
+
+ should not set a default user-agent
+ request.get(`${base}/ua`).then((res) => {
+ assert(res.headers);
+ assert(!res.headers['user-agent']);
+ })
+
+
+
+ utils.type(str)
+
+ should return the mime type
+ utils
+ .type('application/json; charset=utf-8')
+ .should.equal('application/json');
+utils.type('application/json').should.equal('application/json');
+
+
+
+ utils.params(str)
+
+ should return the field parameters
+ const object = utils.params('application/json; charset=utf-8; foo = bar');
+object.charset.should.equal('utf-8');
+object.foo.should.equal('bar');
+utils.params('application/json').should.eql({});
+
+
+
+ utils.parseLinks(str)
+
+ should parse links
+ const string_ =
+ '<https://api.github.com/repos/visionmedia/mocha/issues?page=2>; rel="next", <https://api.github.com/repos/visionmedia/mocha/issues?page=5>; rel="last"';
+const returnValue = utils.parseLinks(string_);
+returnValue.next.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=2'
+);
+returnValue.last.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=5'
+);
+
+
+-------------------|---------|----------|---------|---------|---------------------------------------
+File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
+-------------------|---------|----------|---------|---------|---------------------------------------
+All files | 93.42 | 83.3 | 89.67 | 93.92 |
+ src | 92.5 | 83.83 | 91.83 | 93.84 |
+ agent-base.js | 100 | 100 | 100 | 100 |
+ request-base.js | 91.01 | 83.9 | 94.28 | 93.06 | ...21,262,316,501,525-533,579,757,768
+ response-base.js | 100 | 100 | 75 | 100 |
+ utils.js | 92.85 | 71.42 | 85.71 | 91.66 | 94-98
+ src/node | 93.61 | 83.12 | 87.36 | 93.75 |
+ agent.js | 89.79 | 66.66 | 100 | 88.63 | 39,43,47,51,101
+ http2wrapper.js | 96 | 82.6 | 89.47 | 96.26 | 14,83,185-186
+ index.js | 93.36 | 83.63 | 89.47 | 93.66 | ...,977,1182-1186,1220-1221,1275,1301
+ response.js | 90 | 83.33 | 55.55 | 89.79 | 78,86,94,120-121
+ unzip.js | 100 | 92.85 | 100 | 100 | 47
+ src/node/parsers | 97.61 | 75 | 100 | 97.61 |
+ image.js | 100 | 100 | 100 | 100 |
+ index.js | 100 | 100 | 100 | 100 |
+ json.js | 100 | 75 | 100 | 100 | 15
+ text.js | 100 | 100 | 100 | 100 |
+ urlencoded.js | 90 | 100 | 100 | 90 | 17
+-------------------|---------|----------|---------|---------|---------------------------------------
+
+
+
+
+
+
+
+
diff --git a/docs/zh_CN/index.html b/docs/zh_CN/index.html
new file mode 100644
index 000000000..fa0704db4
--- /dev/null
+++ b/docs/zh_CN/index.html
@@ -0,0 +1,659 @@
+
+
+
+
+ SuperAgent — elegant API for AJAX in Node and browsers
+
+
+
+
+
+
+
SuperAgent
+
SuperAgent是轻量级的渐进式ajax API,具有灵活性、可读性和较低的学习曲线。 它也适用于Node.js!
+
request
+ .post('/api/pet')
+ .send({ name: 'Manny', species: 'cat' })
+ .set('X-API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(res => {
+ alert('yay got ' + JSON.stringify(res.body));
+ });
+
+
测试文档
+
English
+
下面的测试文档 是用Mocha 的"文档"报告器生成的,并直接反映了测试套件。 这提供了额外的文档来源。
+
基本请求
+
可以通过调用 request 对象上的适当方法来发起请求,然后调用 .then() ( 或 .end() 或 await )发送请求。例如一个简单的 GET 请求:
+
request
+ .get('/search')
+ .then(res => {
+ // res.body, res.headers, res.status
+ })
+ .catch(err => {
+ // err.message, err.response
+ });
+
+
HTTP 方法也可以作为字符串传递: 译者注:大小写皆可。
+
request('GET', '/search').then(success, failure);
+
+
旧式回调也受支持,但不推荐使用。您可以调用 .end() 代替 .then():
+
request('GET', '/search').end(function(err, res){
+ if (res.ok) {}
+});
+
+
可以使用绝对 URL。在 Web 浏览器中,绝对 URL 仅在服务器实现 CORS 时才有效。
+
request
+ .get('https://example.com/search')
+ .then(res => {
+
+ });
+
+
Node 客户端支持向 Unix 域套接字 发出请求:
+
// pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
+//在套接字路径中将 `%2F` 用作 `/`
+try {
+ const res = await request
+ .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search');
+ // res.body, res.headers, res.status
+} catch(err) {
+ // err.message, err.response
+}
+
+
DELETE__、__HEAD__、__PATCH__、__POST 和 PUT 请求也可以使用,只需更改方法名称:
+
request
+ .head('/favicon.ico')
+ .then(res => {
+
+ });
+
+
DELETE 也可以用 .del() 调用以与旧版 IE 兼容,其中 delete 是保留字。
+
HTTP 方法默认为 __GET__,因此如果您愿意,以下代码是有效的:
+
request('/search', (err, res) => {
+
+ });
+
+
使用 HTTP/2
+
要使用 HTTP/2 协议(没有 HTTP/1.x 后备),请使用 .http2() 方法。
+
const request = require('superagent');
+ const res = await request
+ .get('https://example.com/h2')
+ .http2();
+
+
设置请求头字段
+
设置请求头字段很简单,调用 .set() 时传入字段名称和值:
+
request
+ .get('/search')
+ .set('API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(callback);
+
+
您还可以在一次调用中传入一个对象来设置多个字段:
+
request
+ .get('/search')
+ .set({ 'API-Key': 'foobar', Accept: 'application/json' })
+ .then(callback);
+
+
GET 请求
+
.query() 方法接受对象,当与 GET 方法一起使用时将形成一个查询字符串。以下将产生路径 /search?query=Manny&range=1..5&order=desc。
+译者注:.query() 方法的参数不需要提前进行url编码。
+
request
+ .get('/search')
+ .query({ query: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .then(res => {
+
+ });
+
+
或传入单个对象:
+
request
+ .get('/search')
+ .query({ query: 'Manny', range: '1..5', order: 'desc' })
+ .then(res => {
+
+ });
+
+
.query() 方法也可以接受字符串。
+
request
+ .get('/querystring')
+ .query('search=Manny&range=1..5')
+ .then(res => {
+
+ });
+
+
或者一个个加入:
+
request
+ .get('/querystring')
+ .query('search=Manny')
+ .query('range=1..5')
+ .then(res => {
+
+ });
+
+
HEAD 请求
+
您还可以对 HEAD 请求使用 .query() 方法。以下将生成路径 /users?email=joe@smith.com。
+
request
+ .head('/users')
+ .query({ email: 'joe@smith.com' })
+ .then(res => {
+
+ });
+
+
POST / PUT 请求
+
一个典型的 JSON POST 请求可能如下所示,我们适当地设置 Content-Type 请求头字段,并"写入"一些数据,在本例中只是一个 JSON 字符串。
+
request.post('/user')
+ .set('Content-Type', 'application/json')
+ .send('{"name":"tj","pet":"tobi"}')
+ .then(callback)
+ .catch(errorCallback)
+
+
由于 JSON 无疑是最常见的,所以它是 默认 的!下面的例子与前面的例子是等价的。
+
request.post('/user')
+ .send({ name: 'tj', pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
或者调用多个 .send():
+
request.post('/user')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
默认情况下,发送字符串会将 Content-Type 设置为 application/x-www-form-urlencoded,多个调用将用 & 连接,这里产生 name=tj&pet=tobi:
+
request.post('/user')
+ .send('name=tj')
+ .send('pet=tobi')
+ .then(callback, errorCallback);
+
+
SuperAgent 格式是可扩展的,但默认情况下支持 "json" 和 "form"。要将数据作为 application/x-www-form-urlencoded 发送,只需在调用 .type() 时传入 "form",默认为 "json"。此 POST 请求的请求体将是 "name=tj&pet=tobi"。
+
request.post('/user')
+ .type('form')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
还支持发送 FormData 对象。以下示例将 POST 请求由 id="myForm" 标识的 HTML 表单的内容:
+
request.post('/user')
+ .send(new FormData(document.getElementById('myForm')))
+ .then(callback, errorCallback)
+
+
设置 Content-Type
+
显而易见的解决方案是使用 .set() 方法:
+
request.post('/user')
+ .set('Content-Type', 'application/json')
+
+
.type() 方法也可以作为简写,接受带有类型/子类型的规范化 MIME 类型 名称,或者只是扩展名,例如"xml"、"json"、"png"等:
+
request.post('/user')
+ .type('application/json')
+
+ request.post('/user')
+ .type('json')
+
+ request.post('/user')
+ .type('png')
+
+
序列化请求体
+
SuperAgent 将自动序列化 JSON 和表单。您也可以为其他类型设置自动序列化:
+
request.serialize['application/xml'] = function (obj) {
+ return '从obj生成的字符串';
+};
+
+// 接下来,内容类型为 "application/xml" 的所有请求都将自动序列化
+
+
如果您想以自定义格式发送 数据体(payload),您可以根据每个请求将内置序列化替换为 .serialize() 方法:
+
request
+ .post('/user')
+ .send({foo: 'bar'})
+ .serialize(obj => {
+ return '从obj生成的字符串';
+ });
+
+
重试请求
+
如果请求暂时失败或可能是网络连接不稳定造成的失败,且当给定 .retry() 方法时,SuperAgent 将自动重试请求。
+
此方法有两个可选参数:重试次数(默认为 1)和回调函数。它在每次重试之前调用 callback(err, res) 。回调可以返回 true/false 以控制是否应重试请求(但始终应该用最大重试次数)。
+ request
+ .get('https://example.com/search' )
+ .retry(2) // 或者:
+ .retry(2, callback) // 二选一
+ .then(finished);
+ .catch(failed);
+
.retry() 仅用于幂等 请求(即到达服务器的多个请求不会导致重复购买等不良副作用)。
+
默认情况下会尝试所有请求方法(这意味着如果您不希望重试 POST 请求,则需要传递自定义的重试回调函数)。
+
默认情况下会重试以下状态代码:
+
+408
+413
+429
+500
+502
+503
+504
+521
+522
+524
+
+
默认情况下会重试以下错误代码:
+
+'ETIMEDOUT'
+'ECONNRESET'
+'EADDRINUSE'
+'ECONNREFUSED'
+'EPIPE'
+'ENOTFOUND'
+'ENETUNREACH'
+'EAI_AGAIN'
+
+
设置 Accept
+
与 .type() 方法类似,也可以通过简写方法 .accept() 设置 Accept 请求头。方便起见,其中还引用了 request.types,允许您将完整的规范化 MIME 类型 名称指定为类型/子类型,或将扩展后缀形式指定为"xml"、"json"、"png"等:
+
request.get('/user')
+ .accept('application/json')
+
+ request.get('/user')
+ .accept('json')
+
+ request.post('/user')
+ .accept('png')
+
+
Facebook 和 Accept JSON
+
如果您正在调用 Facebook 的 API,请务必在您的请求中发送 Accept: application/json 请求头。如果你不这样做,Facebook 会回复 Content-Type: text/javascript; charset=UTF-8,SuperAgent 将不会解析,因此 res.body 将是未定义的。您可以使用 req.accept('json') 或 req.header('Accept', 'application/json') 来执行此操作。有关详细信息,请参阅 issue 1078 。
+
查询字符串(Query strings)
+
req.query(obj) 是一种可用于构建查询字符串的方法。例如在 POST 上增加 ?format=json&dest=/login:
+
request
+ .post('/')
+ .query({ format: 'json' })
+ .query({ dest: '/login' })
+ .send({ post: 'data', here: 'wahoo' })
+ .then(callback);
+
+
默认情况下,查询字符串不按任何特定顺序组装。可以使用 req.sortQuery() 启用 ASCIIbetically 排序的查询字符串。您还可以使用 req.sortQuery(myComparisonFn) 提供自定义排序比较函数。比较函数应该接受 2 个参数并返回一个负/零/正整数。
+
// 默认顺序
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery()
+ .then(callback)
+
+ // 自定义排序函数
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery((a, b) => a.length - b.length)
+ .then(callback)
+
+
TLS 选项
+
在 Node.js 中,SuperAgent 支持配置 HTTPS 请求的方法:
+
+.ca(): 将 CA 证书设置为信任
+.cert(): 设置客户端证书链
+.key(): 设置客户端私钥
+.pfx(): 设置客户端 PFX 或 PKCS12 编码的私钥和证书链
+.disableTLSCerts(): 不拒绝过期或无效的 TLS 证书。在内部设置 rejectUnauthorized=true。请注意,此方法允许中间人攻击。
+
+
有关更多信息,请参阅 Node.js https.request 文档 。
+
var key = fs.readFileSync('key.pem'),
+ cert = fs.readFileSync('cert.pem');
+
+request
+ .post('/client-auth')
+ .key(key)
+ .cert(cert)
+ .then(callback);
+
+
var ca = fs.readFileSync('ca.cert.pem');
+
+request
+ .post('https://localhost/private-ca-server')
+ .ca(ca)
+ .then(res => {});
+
+
解析响应体
+
SuperAgent将为您解析已知的响应主体数据,目前支持application/x-www-form-urlencoded,application/json,以及multipart/form data。您可以设置自动解析其他响应主体数据:
+
//浏览器
+request.parse['application/xml'] = function (str) {
+ return {'object': '从str解析的'};
+};
+
+//node
+request.parse['application/xml'] = function (res, cb) {
+ //解析响应文本并在此处设置res.body
+
+ cb(null, res);
+};
+
+//接下来,将自动解析 'application/xml' 类型的响应
+
+
您可以使用 .buffer(true).parse(fn) 方法设置自定义解析器(优先于内置解析器)。如果未启用响应缓冲 (.buffer(false)),则将触发响应(response)事件而无需等待正文解析器完成,因此 response.body 将不可用。
+
JSON / Urlencoded
+
属性 res.body 是解析后的对象,例如,如果请求以 JSON 字符串 '{"user":{"name":"tobi"}}' 响应,则 res.body.user.name 将为 "tobi" .同样,"user[name]=tobi" 的 x-www-form-urlencoded 值将产生相同的结果。仅支持一级嵌套。如果您需要更复杂的数据,请改为发送 JSON。
+
通过重复的键发送数组。 .send({color: ['red','blue']}) 会发送 color=red&color=blue。如果您希望数组键的名称中包含 [],您必须自己添加它,因为 SuperAgent 不会自动添加它。
+
Multipart
+
Node 客户端通过 Formidable 模块支持 _multipart/form-data_。解析 multipart 响应时,对象 res.files 也可供您使用。例如,假设一个请求使用以下 multipart 请求体进行响应:
+
--whoop
+Content-Disposition: attachment; name="image"; filename="tobi.png"
+Content-Type: image/png
+
+... data here ...
+--whoop
+Content-Disposition: form-data; name="name"
+Content-Type: text/plain
+
+Tobi
+--whoop--
+
+
res.body.name的值将为 "Tobi",并且 res.files.image 将作为包含磁盘路径、文件名和其他属性的 File 对象。
+
二进制数据
+
在浏览器中,您可以使用 .responseType('blob') 来请求处理二进制响应体。在 node.js 中运行时不需要此 API。此方法支持的参数值为
+
+'blob' 赋值给 XmlHTTPRequest 的 responseType 属性
+'arraybuffer' 赋值给 XmlHTTPRequest 的 responseType 属性
+
+
req.get('/binary.data')
+ .responseType('blob')
+ .then(res => {
+ // res.body 将是浏览器原生 Blob 类型
+ });
+
+
有关更多信息,请参阅 Mozilla 开发人员网络 xhr.responseType 文档 。
+
响应属性
+
在 Response 对象上设置了许多有用的标志和属性,包括响应文本、解析的响应正文、响应头字段、状态标志等等。
+
响应文本
+
res.text 属性包含未解析的响应正文字符串。此属性始终存在于客户端 API 中,并且仅当默认情况下节点的 mime 类型与 "text/"、" /json" 或 "x-www-form-urlencoded" 匹配时。原因是为了节省内存,因为缓冲大型正文(例如 multipart 文件或图像)的文本效率极低。要强制缓冲,请参阅"缓冲响应 "部分。
+
响应体
+
就像 SuperAgent 可以自动序列化请求数据一样,它也可以自动解析响应体。为 Content-Type 定义解析器时,会对其进行解析,默认情况下包括 "application/json" 和 "application/x-www-form-urlencoded"。然后可以通过 res.body 获得解析的对象。
+
响应头字段
+
res.header 包含已解析的响应头字段的对象,字段名称小写,就像 node 做的一样。例如 res.header['content-length']。
+
响应内容类型(Content-Type)
+
Content-Type 响应头是特殊情况,提供 res.type,它没有字符集(也可以有)。例如,"text/html; charset=utf8" 的 Content-Type 将提供 "text/html" 作为 res.type,然后 res.charset 属性将包含 "utf8"。
+
响应状态
+
响应状态标志有助于确定请求是否成功,以及其他有用的信息,使 SuperAgent 成为与 RESTful Web 服务交互的理想选择。这些标志当前定义为:
+
var type = status / 100 | 0;
+
+ // status / class
+ res.status = status;
+ res.statusType = type;
+
+ // basics
+ res.info = 1 == type;
+ res.ok = 2 == type;
+ res.clientError = 4 == type;
+ res.serverError = 5 == type;
+ res.error = 4 == type || 5 == type;
+
+ // 语法糖
+ res.accepted = 202 == status;
+ res.noContent = 204 == status || 1223 == status;
+ res.badRequest = 400 == status;
+ res.unauthorized = 401 == status;
+ res.notAcceptable = 406 == status;
+ res.notFound = 404 == status;
+ res.forbidden = 403 == status;
+
+
中止请求
+
要中止请求,只需调用 req.abort() 方法。
+
超时设定
+
有时网络和服务器会 "卡住" 并且在接受请求后从不响应。设置超时以避免请求永远等待。
+
+req.timeout({deadline:ms}) 或 req.timeout(ms)(其中 ms 是毫秒数 > 0)设置完成整个请求(包括所有上传、重定向、服务器处理时间)的最后期限。如果在这段时间内没有完全下载响应,则请求将被中止。
+
+req.timeout({response:ms}) 设置等待第一个字节从服务器到达的最长时间,但它不限制整个下载需要多长时间。响应超时应该至少比服务器响应的时间长几秒钟,因为它还包括进行 DNS 查找、TCP/IP 和 TLS 连接的时间,以及上传请求数据的时间。
+
+
+
您应该同时使用 deadline 和 response 超时。通过这种方式,您可以使用较短的响应超时来快速检测无响应的网络,并使用较长的截止时间来为缓慢但可靠的网络上的下载留出时间。请注意,这两个计时器都限制了允许上传 附件的时间。如果您要上传文件,请使用长超时。
+
request
+ .get('/big-file?network=slow')
+ .timeout({
+ response: 5000, // 等待 5 秒让服务器开始发送
+ deadline: 60000, // 但允许文件用 1 分钟完成加载。
+ })
+ .then(res => {
+ /* 及时响应 */
+ }, err => {
+ if (err.timeout) { /* 超时! */ } else { /* 其他错误 */ }
+ });
+
+
超时错误有个 .timeout 属性。
+
验证
+
在 Node 和浏览器中都可以通过 .auth() 方法进行身份验证:
+
request
+ .get('http://local')
+ .auth('tobi', 'learnboost')
+ .then(callback);
+
+
在 Node 客户端中,基本身份验证可以在 URL 中写成 "user:pass":
+
request.get('http://tobi:learnboost@local').then(callback);
+
+
默认情况下,仅使用基本(Basic)身份验证。在浏览器中,您可以添加 {type:'auto'} 以启用浏览器中内置的所有方法(Digest、NTLM 等):
+
request.auth('digest', 'secret', {type:'auto'})
+
+
auth 方法还支持一种承载类型,以指定基于令牌的身份验证:
+
request.auth('my_token', { type: 'bearer' })
+
+
跟随重定向
+
默认情况下将跟随最多 5 个重定向,但是您可以使用 res.redirects(n) 方法指定它:
+
const response = await request.get('/some.png').redirects(2);
+
+
超出限制的重定向被视为错误。使用 .ok(res => res.status < 400) 将它们读取为成功响应。
+
全局状态代理程序
+
保存 cookie
+
在 Node 中 SuperAgent 默认不保存 cookie,但您可以使用 .agent() 方法创建保存 cookie 的 SuperAgent 副本。每个副本都有一个单独的 cookie 储存器。
+
const agent = request.agent();
+agent
+ .post('/login')
+ .then(() => {
+ return agent.get('/cookied-page');
+ });
+
+
在浏览器中,cookie 由浏览器自动管理,因此 .agent() 不会隔离 cookie。
+
多个请求的默认选项
+
代理程序上调用的常规请求方法将用作该代理发出的所有请求的默认值。
+
const agent = request.agent()
+ .use(plugin)
+ .auth(shared);
+
+await agent.get('/with-plugin-and-auth'); // 带有插件和身份验证
+await agent.get('/also-with-plugin-and-auth'); // 也带有插件和身份验证
+
+
代理可以用来设置默认值的完整方法列表是:use、 on、 once、 set、 query、 type、 accept、 auth、 withCredentials、 sortQuery、 retry、 ok、 redirects、 timeout、 buffer、 serialize、 parse、 ca、 key、 pfx、 cert.
+
管道数据
+
Node 客户端允许您通过管道将数据传入和传出请求。请注意,使用 .pipe() 代替 .end()/.then() 方法。
+
管道文件的内容作为请求的例子:
+
const request = require('superagent');
+const fs = require('fs');
+
+const stream = fs.createReadStream('path/to/my.json');
+const req = request.post('/somewhere');
+req.type('json');
+stream.pipe(req);
+
+
请注意,当您通过管道发送请求时,superagent 使用分块传输编码 发送管道数据,并非所有服务器(例如 Python WSGI 服务器)都支持。
+
或将响应传送到文件:
+
const stream = fs.createWriteStream('path/to/my.json');
+const req = request.get('/some.json');
+req.pipe(stream);
+
+
不能混合使用管道和回调函数或 promises。请注意,您不应 尝试通过管道传输 .end() 或 Response 对象的结果:
+
// 别特么这么写:
+const stream = getAWritableStream();
+const req = request
+ .get('/some.json')
+ // BAD: 这会将无用信息管道传输到流中并以意想不到的方式失败
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
+const req = request
+ .get('/some.json')
+ .end()
+ // BAD: 这也不支持,调用 .end 之后调用 .pipe。
+ .pipe(nope_its_too_late);
+
+
在 superagent 的未来版本 中,对 pipe() 的不当调用将失败。
+
多部分请求
+
SuperAgent 也非常适合 构建 它提供方法 .attach() 和 .field() 的多部分请求。
+
当您使用 .field() 或 .attach() 时,您不能使用 .send() 并且您不能 设置 Content-Type(将为您设置正确的类型)。
+
附加文件
+
要发送文件,请使用 .attach(name, [file], [options])。您可以通过多次调用 .attach 来附加多个文件。参数是:
+
+name — form 表单中的字段名。
+file — 带有文件路径的字符串或 Blob/Buffer 对象。
+options — (可选)自定义文件名的字符串或 {filename: string} 对象。在 Node 中也支持 {contentType: 'mime/type'}。在浏览器中创建一个具有适当类型的 Blob。
+
+
+
+
request
+ .post('/upload')
+ .attach('image1', 'path/to/felix.jpeg')
+ .attach('image2', imageBuffer, 'luna.jpeg')
+ .field('caption', 'My cats')
+ .then(callback);
+
+
字段值
+
与 HTML 中的表单字段非常相似,您可以使用 .field(name, value) 和 .field({name: value}) 设置字段值。假设您想上传一些带有您的姓名和电子邮件的图片,您的请求可能如下所示:
+
request
+ .post('/upload')
+ .field('user[name]', 'Tobi')
+ .field('user[email]', 'tobi@learnboost.com')
+ .field('friends[]', ['loki', 'jane'])
+ .attach('image', 'path/to/tobi.png')
+ .then(callback);
+
+
压缩
+
node 客户端支持压缩过的响应,最重要的是,您无需执行任何操作!它就能用。
+
缓冲响应
+
要强制将响应主体缓冲为 res.text,您可以调用 req.buffer()。要取消对文本响应(例如 "text/plain"、"text/html" 等)的默认缓冲,您可以调用 req.buffer(false)。
+
当缓冲提供 res.buffered 标志时,您可以使用它在同一个回调中处理缓冲和非缓冲响应。
+
CORS
+
出于安全原因,浏览器将阻止跨域请求,除非服务器选择使用 CORS 标头。浏览器还会发出额外的 OPTIONS 请求来检查服务器允许哪些 HTTP 标头和方法。阅读有关 CORS 的更多信息 。
+
.withCredentials() 方法支持从源发送 cookie,但仅当 Access-Control-Allow-Origin 不是 通配符 ("*") 且 Access-Control-Allow-Credentials 为 "true" 时。
+
request
+ .get('https://api.example.com:4001/')
+ .withCredentials()
+ .then(res => {
+ assert.equal(200, res.status);
+ assert.equal('tobi', res.text);
+ })
+
+
错误处理
+
您的回调函数将始终传递两个参数:错误和响应。如果没有发生错误,第一个参数将为null:
+
request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .then(res => {
+
+ });
+
+
还会触发"错误"事件,您可以监听:
+
request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .on('error', handle)
+ .then(res => {
+
+ });
+
+
请注意,默认情况下,superagent 会考虑 4xx 和 5xx 响应(以及未处理的 3xx 响应)视为错误 。例如,如果您收到 304 Not modified、403 Forbidden 或 500 Internal server 错误响应,则此状态信息将通过 err.status 提供。来自此类响应的错误还包含一个 err.response 字段,其中包含"响应属性 "中提到的所有属性。该库以这种方式运行以处理需要成功响应并将 HTTP 错误状态代码视为错误的常见情况,同时仍允许围绕特定错误条件进行自定义逻辑。
+
网络故障、超时和其他不产生响应的错误将不包含 err.status 或 err.response 字段。
+
如果您希望处理 404 或其他 HTTP 错误响应,您可以查询 err.status 属性。当发生 HTTP 错误(4xx 或 5xx 响应)时, res.error 属性是一个 Error 对象,这允许您执行以下检查:
+
if (err && err.status === 404) {
+ alert('oh no ' + res.body.message);
+}
+else if (err) {
+ // 所有其他需要处理的错误类型
+}
+
+
或者,您可以使用 .ok(callback) 方法来确定响应是否为错误。 ok 函数的回调函数获得响应,如果响应应该被解释为成功,则返回 true。
+
request.get('/404')
+ .ok(res => res.status < 500)
+ .then(response => {
+ // 将 404 页面作为成功响应
+ })
+
+
进度跟踪
+
SuperAgent 在上传和下载大文件时触发 progress 事件。
+
request.post(url)
+ .attach('field_name', file)
+ .on('progress', event => {
+ /* event的值:
+ {
+ direction: "upload" or "download"
+ percent: 0 to 100 // 如果文件大小未知,可能会没有
+ total: // 总文件大小,可能没有
+ loaded: // 到目前为止下载或上传的字节数
+ } */
+ })
+ .then()
+
+
在本地主机上测试
+
强制连接特定 IP 地址
+
在 Node.js 中,可以忽略 DNS 解析并使用 .connect() 方法将所有请求定向到特定 IP 地址。例如,此请求将转到 localhost 而不是 example.com:
+
const res = await request.get("http://example.com").connect("127.0.0.1");
+
+
因为请求可能被重定向,所以可以指定多个主机名和多个 IP,以及一个特殊的 * 作为后备(注意:不支持其他通配符)。请求将保留其 Host 请求头的原始值。
+
const res = await request.get("http://redir.example.com:555")
+ .connect({
+ "redir.example.com": "127.0.0.1", // redir.example.com:555 将使用 127.0.0.1:555
+ "www.example.com": false, // 不覆盖这个;正常使用 DNS
+ "mapped.example.com": { host: "127.0.0.1", port: 8080}, // mapped.example.com:* 将使用 127.0.0.1:8080
+ "*": "proxy.example.com", // 所有其他请求都将发送到该主机
+ });
+
+
忽略本地主机上损坏/不安全的 HTTPS
+
在 Node.js 中,当 HTTPS 配置错误且不安全(例如,使用自签名证书而不指定 自己的 .ca())时,仍然可以通过调用 .trustLocalhost() 来允许对 localhost 的请求:
+
const res = await request.get("https://localhost").trustLocalhost()
+
+
与 .connect("127.0.0.1") 一起,这可用于强制将对任何域的 HTTPS 请求重新路由到 localhost。
+
忽略本地主机上损坏的 HTTPS 通常是安全的,因为环回接口不会暴露给不受信任的网络。信任 localhost 可能会成为未来的默认设置。使用 .trustLocalhost(false) 强制检查 127.0.0.1 的可靠性。
+
当向任何其他 IP 发出请求时,我们故意不支持禁用 HTTPS 安全性,因为这些选项最终被滥用为 HTTPS 问题的快速"修复"。您可以从 Let's Encrypt 获得免费的 HTTPS 证书或设置您自己的 CA (.ca(ca_public_pem)) 以使您的自签名证书受信任。
+
Promise 和生成器函数支持
+
SuperAgent 的请求是一个 "thenable" 对象(带有then方法的对象),它与 JavaScript Promise 和 async/await 语法兼容。
+
const res = await request.get(url);
+
+
如果你使用 Promise,不要 调用 .end() 或 .pipe()。任何使用 .then() 或 await 都会禁用所有其他使用请求的方式。 像 co 这样的库或像 koa 这样的 web 框架可以在任何 SuperAgent 方法上 yield:
+
const req = request
+ .get('http://local')
+ .auth('tobi', 'learnboost');
+const res = yield req;
+
+
请注意,SuperAgent 期望全局 Promise 对象存在。您需要一个 polyfill 才能在 Internet Explorer 或 Node.js 0.10 中使用 Promise。
+
浏览器和 node 版本
+
SuperAgent 有两种实现:一种用于 Web 浏览器(使用 XHR),另一种用于 Node.JS(使用核心 http 模块)。默认情况下,Browserify 和 WebPack 将选择浏览器版本。
+
如果要使用 WebPack 为 Node.JS 编译代码,您必须 在其配置中指定node target 。
+
在 electron 中使用浏览器版本
+
Electron 开发人员报告说,如果您希望使用浏览器版本的 SuperAgent 而不是 Node 版本,您可以 require('superagent/superagent')。这样您的请求将显示在 Chrome 开发者工具的"网络(Network)"选项卡中。请注意,自动化测试套件未涵盖此环境,也未得到官方支持。
+
使用代理发送请求
+
可以使用另一个作者的 superagent-proxy 模块
+
翻译说明
+
文档全部内容都是根据原英文文档翻译的,译者也没水平,所以如果有错误还请指出
+
+
+
+
+
+
+
+
diff --git a/docs/zh_CN/index.md b/docs/zh_CN/index.md
new file mode 100644
index 000000000..efe1a7732
--- /dev/null
+++ b/docs/zh_CN/index.md
@@ -0,0 +1,774 @@
+# SuperAgent
+
+SuperAgent是轻量级的渐进式ajax API,具有灵活性、可读性和较低的学习曲线。 它也适用于Node.js!
+
+ request
+ .post('/api/pet')
+ .send({ name: 'Manny', species: 'cat' })
+ .set('X-API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(res => {
+ alert('yay got ' + JSON.stringify(res.body));
+ });
+
+## 测试文档
+
+[**English**](/superagent/)
+
+下面的[测试文档](../test.html)是用[Mocha](https://mochajs.org/)的"文档"报告器生成的,并直接反映了测试套件。 这提供了额外的文档来源。
+
+## 基本请求
+
+可以通过调用 `request` 对象上的适当方法来发起请求,然后调用 `.then()` ( 或 `.end()` 或 [`await`](#promise-and-generator-support) )发送请求。例如一个简单的 __GET__ 请求:
+
+ request
+ .get('/search')
+ .then(res => {
+ // res.body, res.headers, res.status
+ })
+ .catch(err => {
+ // err.message, err.response
+ });
+
+HTTP 方法也可以作为字符串传递:
+译者注:大小写皆可。
+
+ request('GET', '/search').then(success, failure);
+
+旧式回调也受支持,但不推荐使用。您可以调用 `.end()` *代替* `.then()`:
+
+ request('GET', '/search').end(function(err, res){
+ if (res.ok) {}
+ });
+
+可以使用绝对 URL。在 Web 浏览器中,绝对 URL 仅在服务器实现 [CORS](#cors) 时才有效。
+
+ request
+ .get('https://example.com/search')
+ .then(res => {
+
+ });
+
+__Node__ 客户端支持向 [Unix 域套接字](https://zh.wikipedia.org/wiki/Unix%E5%9F%9F%E5%A5%97%E6%8E%A5%E5%AD%97) 发出请求:
+
+ // pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
+ //在套接字路径中将 `%2F` 用作 `/`
+ try {
+ const res = await request
+ .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search');
+ // res.body, res.headers, res.status
+ } catch(err) {
+ // err.message, err.response
+ }
+
+__DELETE__、__HEAD__、__PATCH__、__POST__ 和 __PUT__ 请求也可以使用,只需更改方法名称:
+
+ request
+ .head('/favicon.ico')
+ .then(res => {
+
+ });
+
+__DELETE__ 也可以用 `.del()` 调用以与旧版 IE 兼容,其中 `delete` 是保留字。
+
+HTTP 方法默认为 __GET__,因此如果您愿意,以下代码是有效的:
+
+ request('/search', (err, res) => {
+
+ });
+
+## 使用 HTTP/2
+
+要使用 HTTP/2 协议(没有 HTTP/1.x 后备),请使用 `.http2()` 方法。
+
+ const request = require('superagent');
+ const res = await request
+ .get('https://example.com/h2')
+ .http2();
+
+## 设置请求头字段
+
+设置请求头字段很简单,调用 `.set()` 时传入字段名称和值:
+
+ request
+ .get('/search')
+ .set('API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(callback);
+
+您还可以在一次调用中传入一个对象来设置多个字段:
+
+ request
+ .get('/search')
+ .set({ 'API-Key': 'foobar', Accept: 'application/json' })
+ .then(callback);
+
+## `GET` 请求
+
+`.query()` 方法接受对象,当与 __GET__ 方法一起使用时将形成一个查询字符串。以下将产生路径 `/search?query=Manny&range=1..5&order=desc`。
+译者注:`.query()` 方法的参数不需要提前进行url编码。
+
+ request
+ .get('/search')
+ .query({ query: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .then(res => {
+
+ });
+
+或传入单个对象:
+
+ request
+ .get('/search')
+ .query({ query: 'Manny', range: '1..5', order: 'desc' })
+ .then(res => {
+
+ });
+
+`.query()` 方法也可以接受字符串。
+
+ request
+ .get('/querystring')
+ .query('search=Manny&range=1..5')
+ .then(res => {
+
+ });
+
+或者一个个加入:
+
+ request
+ .get('/querystring')
+ .query('search=Manny')
+ .query('range=1..5')
+ .then(res => {
+
+ });
+
+## `HEAD` 请求
+
+您还可以对 __HEAD__ 请求使用 .query() 方法。以下将生成路径 `/users?email=joe@smith.com`。
+
+ request
+ .head('/users')
+ .query({ email: 'joe@smith.com' })
+ .then(res => {
+
+ });
+
+## `POST` / `PUT` 请求
+
+一个典型的 JSON __POST__ 请求可能如下所示,我们适当地设置 `Content-Type` 请求头字段,并"写入"一些数据,在本例中只是一个 JSON 字符串。
+
+ request.post('/user')
+ .set('Content-Type', 'application/json')
+ .send('{"name":"tj","pet":"tobi"}')
+ .then(callback)
+ .catch(errorCallback)
+
+由于 JSON 无疑是最常见的,所以它是 _默认_ 的!下面的例子与前面的例子是等价的。
+
+ request.post('/user')
+ .send({ name: 'tj', pet: 'tobi' })
+ .then(callback, errorCallback)
+
+或者调用多个 `.send()`:
+
+ request.post('/user')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+默认情况下,发送字符串会将 `Content-Type` 设置为 `application/x-www-form-urlencoded`,多个调用将用 `&` 连接,这里产生 `name=tj&pet=tobi`:
+
+ request.post('/user')
+ .send('name=tj')
+ .send('pet=tobi')
+ .then(callback, errorCallback);
+
+SuperAgent 格式是可扩展的,但默认情况下支持 "json" 和 "form"。要将数据作为 `application/x-www-form-urlencoded` 发送,只需在调用 `.type()` 时传入 "form",默认为 "json"。此 __POST__ 请求的请求体将是 "name=tj&pet=tobi"。
+
+ request.post('/user')
+ .type('form')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+还支持发送 [`FormData`](https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/FormData) 对象。以下示例将 __POST__ 请求由 id="myForm" 标识的 HTML 表单的内容:
+
+ request.post('/user')
+ .send(new FormData(document.getElementById('myForm')))
+ .then(callback, errorCallback)
+
+## 设置 `Content-Type`
+
+显而易见的解决方案是使用 `.set()` 方法:
+
+ request.post('/user')
+ .set('Content-Type', 'application/json')
+
+`.type()` 方法也可以作为简写,接受带有类型/子类型的规范化 [MIME 类型](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types) 名称,或者只是扩展名,例如"xml"、"json"、"png"等:
+
+ request.post('/user')
+ .type('application/json')
+
+ request.post('/user')
+ .type('json')
+
+ request.post('/user')
+ .type('png')
+
+## 序列化请求体
+
+SuperAgent 将自动序列化 JSON 和表单。您也可以为其他类型设置自动序列化:
+
+```js
+request.serialize['application/xml'] = function (obj) {
+ return '从obj生成的字符串';
+};
+
+// 接下来,内容类型为 "application/xml" 的所有请求都将自动序列化
+```
+如果您想以自定义格式发送 数据体(payload),您可以根据每个请求将内置序列化替换为 `.serialize()` 方法:
+
+```js
+request
+ .post('/user')
+ .send({foo: 'bar'})
+ .serialize(obj => {
+ return '从obj生成的字符串';
+ });
+```
+## 重试请求
+
+如果请求暂时失败或可能是网络连接不稳定造成的失败,且当给定 `.retry()` 方法时,SuperAgent 将自动重试请求。
+
+此方法有两个可选参数:重试次数(默认为 `1`)和回调函数。它在每次重试之前调用 callback(err, res) 。回调可以返回 `true`/`false` 以控制是否应重试请求(但始终应该用最大重试次数)。
+ request
+ .get('https://example.com/search')
+ .retry(2) // 或者:
+ .retry(2, callback) // 二选一
+ .then(finished);
+ .catch(failed);
+
+`.retry()` 仅用于[*幂等*](https://baike.baidu.com/item/%E5%B9%82%E7%AD%89/8600688?fr=aladdin)请求(即到达服务器的多个请求不会导致重复购买等不良副作用)。
+
+默认情况下会尝试所有请求方法(这意味着如果您不希望重试 POST 请求,则需要传递自定义的重试回调函数)。
+
+默认情况下会重试以下状态代码:
+
+* `408`
+* `413`
+* `429`
+* `500`
+* `502`
+* `503`
+* `504`
+* `521`
+* `522`
+* `524`
+
+默认情况下会重试以下错误代码:
+
+* `'ETIMEDOUT'`
+* `'ECONNRESET'`
+* `'EADDRINUSE'`
+* `'ECONNREFUSED'`
+* `'EPIPE'`
+* `'ENOTFOUND'`
+* `'ENETUNREACH'`
+* `'EAI_AGAIN'`
+
+## 设置 Accept
+
+与 `.type()` 方法类似,也可以通过简写方法 `.accept()` 设置 `Accept` 请求头。方便起见,其中还引用了 `request.types`,允许您将完整的规范化 [MIME 类型](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types) 名称指定为`类型/子类型`,或将扩展后缀形式指定为"xml"、"json"、"png"等:
+
+ request.get('/user')
+ .accept('application/json')
+
+ request.get('/user')
+ .accept('json')
+
+ request.post('/user')
+ .accept('png')
+
+### Facebook 和 Accept JSON
+
+如果您正在调用 Facebook 的 API,请务必在您的请求中发送 `Accept: application/json` 请求头。如果你不这样做,Facebook 会回复 `Content-Type: text/javascript; charset=UTF-8`,SuperAgent 将不会解析,因此 `res.body` 将是未定义的。您可以使用 `req.accept('json')` 或 `req.header('Accept', 'application/json')` 来执行此操作。有关详细信息,请参阅 [issue 1078](https://github.com/ladjs/superagent/issues/1078)。
+
+## 查询字符串(Query strings)
+
+`req.query(obj)` 是一种可用于构建查询字符串的方法。例如在 __POST__ 上增加 `?format=json&dest=/login`:
+
+ request
+ .post('/')
+ .query({ format: 'json' })
+ .query({ dest: '/login' })
+ .send({ post: 'data', here: 'wahoo' })
+ .then(callback);
+
+默认情况下,查询字符串不按任何特定顺序组装。可以使用 `req.sortQuery()` 启用 ASCIIbetically 排序的查询字符串。您还可以使用 `req.sortQuery(myComparisonFn)` 提供自定义排序比较函数。比较函数应该接受 2 个参数并返回一个负/零/正整数。
+
+```js
+ // 默认顺序
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery()
+ .then(callback)
+
+ // 自定义排序函数
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery((a, b) => a.length - b.length)
+ .then(callback)
+```
+
+## TLS 选项
+
+在 Node.js 中,SuperAgent 支持配置 HTTPS 请求的方法:
+
+- `.ca()`: 将 CA 证书设置为信任
+- `.cert()`: 设置客户端证书链
+- `.key()`: 设置客户端私钥
+- `.pfx()`: 设置客户端 PFX 或 PKCS12 编码的私钥和证书链
+- `.disableTLSCerts()`: 不拒绝过期或无效的 TLS 证书。在内部设置 `rejectUnauthorized=true`。*请注意,此方法允许中间人攻击。*
+
+有关更多信息,请参阅 Node.js [https.request 文档](http://nodejs.cn/api/https.html#httpsrequesturl-options-callback)。
+
+```js
+var key = fs.readFileSync('key.pem'),
+ cert = fs.readFileSync('cert.pem');
+
+request
+ .post('/client-auth')
+ .key(key)
+ .cert(cert)
+ .then(callback);
+```
+
+```js
+var ca = fs.readFileSync('ca.cert.pem');
+
+request
+ .post('https://localhost/private-ca-server')
+ .ca(ca)
+ .then(res => {});
+```
+
+## 解析响应体
+
+SuperAgent将为您解析已知的响应主体数据,目前支持`application/x-www-form-urlencoded`,`application/json`,以及`multipart/form data`。您可以设置自动解析其他响应主体数据:
+
+```js
+//浏览器
+request.parse['application/xml'] = function (str) {
+ return {'object': '从str解析的'};
+};
+
+//node
+request.parse['application/xml'] = function (res, cb) {
+ //解析响应文本并在此处设置res.body
+
+ cb(null, res);
+};
+
+//接下来,将自动解析 'application/xml' 类型的响应
+```
+
+您可以使用 `.buffer(true).parse(fn)` 方法设置自定义解析器(优先于内置解析器)。如果未启用响应缓冲 (`.buffer(false)`),则将触发`响应(response)`事件而无需等待正文解析器完成,因此 `response.body` 将不可用。
+
+### JSON / Urlencoded
+
+属性 `res.body` 是解析后的对象,例如,如果请求以 JSON 字符串 '{"user":{"name":"tobi"}}' 响应,则 `res.body.user.name` 将为 "tobi" .同样,"user[name]=tobi" 的 x-www-form-urlencoded 值将产生相同的结果。仅支持一级嵌套。如果您需要更复杂的数据,请改为发送 JSON。
+
+通过重复的键发送数组。 `.send({color: ['red','blue']})` 会发送 `color=red&color=blue`。如果您希望数组键的名称中包含 `[]`,您必须自己添加它,因为 SuperAgent 不会自动添加它。
+
+### Multipart
+
+Node 客户端通过 [Formidable](https://github.com/felixge/node-formidable) 模块支持 _multipart/form-data_。解析 multipart 响应时,对象 `res.files` 也可供您使用。例如,假设一个请求使用以下 multipart 请求体进行响应:
+
+ --whoop
+ Content-Disposition: attachment; name="image"; filename="tobi.png"
+ Content-Type: image/png
+
+ ... data here ...
+ --whoop
+ Content-Disposition: form-data; name="name"
+ Content-Type: text/plain
+
+ Tobi
+ --whoop--
+
+`res.body.name`的值将为 "Tobi",并且 `res.files.image` 将作为包含磁盘路径、文件名和其他属性的 `File` 对象。
+
+
+### 二进制数据
+
+在浏览器中,您可以使用 `.responseType('blob')` 来请求处理二进制响应体。在 node.js 中运行时不需要此 API。此方法支持的参数值为
+
+- `'blob'` 赋值给 XmlHTTPRequest 的 `responseType` 属性
+- `'arraybuffer'` 赋值给 XmlHTTPRequest 的 responseType 属性
+
+```js
+req.get('/binary.data')
+ .responseType('blob')
+ .then(res => {
+ // res.body 将是浏览器原生 Blob 类型
+ });
+```
+
+有关更多信息,请参阅 Mozilla 开发人员网络 [xhr.responseType 文档](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseType)。
+
+## 响应属性
+
+在 `Response` 对象上设置了许多有用的标志和属性,包括响应文本、解析的响应正文、响应头字段、状态标志等等。
+
+### 响应文本
+
+`res.text` 属性包含未解析的响应正文字符串。此属性始终存在于客户端 API 中,并且仅当默认情况下节点的 mime 类型与 "text/*"、"*/json" 或 "x-www-form-urlencoded" 匹配时。原因是为了节省内存,因为缓冲大型正文(例如 multipart 文件或图像)的文本效率极低。要强制缓冲,请参阅"[缓冲响应](#缓冲响应)"部分。
+
+### 响应体
+
+就像 SuperAgent 可以自动序列化请求数据一样,它也可以自动解析响应体。为 Content-Type 定义解析器时,会对其进行解析,默认情况下包括 "application/json" 和 "application/x-www-form-urlencoded"。然后可以通过 `res.body` 获得解析的对象。
+
+### 响应头字段
+
+`res.header` 包含已解析的响应头字段的对象,字段名称小写,就像 node 做的一样。例如 `res.header['content-length']`。
+
+### 响应内容类型(Content-Type)
+
+Content-Type 响应头是特殊情况,提供 `res.type`,它没有字符集(也可以有)。例如,"text/html; charset=utf8" 的 `Content-Type` 将提供 "text/html" 作为 `res.type`,然后 `res.charset` 属性将包含 "utf8"。
+
+### 响应状态
+
+响应状态标志有助于确定请求是否成功,以及其他有用的信息,使 SuperAgent 成为与 RESTful Web 服务交互的理想选择。这些标志当前定义为:
+
+ var type = status / 100 | 0;
+
+ // status / class
+ res.status = status;
+ res.statusType = type;
+
+ // basics
+ res.info = 1 == type;
+ res.ok = 2 == type;
+ res.clientError = 4 == type;
+ res.serverError = 5 == type;
+ res.error = 4 == type || 5 == type;
+
+ // 语法糖
+ res.accepted = 202 == status;
+ res.noContent = 204 == status || 1223 == status;
+ res.badRequest = 400 == status;
+ res.unauthorized = 401 == status;
+ res.notAcceptable = 406 == status;
+ res.notFound = 404 == status;
+ res.forbidden = 403 == status;
+
+## 中止请求
+
+要中止请求,只需调用 `req.abort()` 方法。
+
+## 超时设定
+
+有时网络和服务器会 "卡住" 并且在接受请求后从不响应。设置超时以避免请求永远等待。
+
+ * `req.timeout({deadline:ms})` 或 `req.timeout(ms)`(其中 `ms` 是毫秒数 > 0)设置完成整个请求(包括所有上传、重定向、服务器处理时间)的最后期限。如果在这段时间内没有完全下载响应,则请求将被中止。
+
+ * `req.timeout({response:ms})` 设置等待第一个字节从服务器到达的最长时间,但它不限制整个下载需要多长时间。响应超时应该至少比服务器响应的时间长几秒钟,因为它还包括进行 DNS 查找、TCP/IP 和 TLS 连接的时间,以及上传请求数据的时间。
+
+您应该同时使用 `deadline` 和 `response` 超时。通过这种方式,您可以使用较短的响应超时来快速检测无响应的网络,并使用较长的截止时间来为缓慢但可靠的网络上的下载留出时间。请注意,这两个计时器都限制了允许*上传*附件的时间。如果您要上传文件,请使用长超时。
+
+ request
+ .get('/big-file?network=slow')
+ .timeout({
+ response: 5000, // 等待 5 秒让服务器开始发送
+ deadline: 60000, // 但允许文件用 1 分钟完成加载。
+ })
+ .then(res => {
+ /* 及时响应 */
+ }, err => {
+ if (err.timeout) { /* 超时! */ } else { /* 其他错误 */ }
+ });
+
+超时错误有个 `.timeout` 属性。
+
+## 验证
+
+在 Node 和浏览器中都可以通过 `.auth()` 方法进行身份验证:
+
+ request
+ .get('http://local')
+ .auth('tobi', 'learnboost')
+ .then(callback);
+
+
+在 _Node_ 客户端中,基本身份验证可以在 URL 中写成 "user:pass":
+
+ request.get('http://tobi:learnboost@local').then(callback);
+
+默认情况下,仅使用`基本(Basic)`身份验证。在浏览器中,您可以添加 `{type:'auto'}` 以启用浏览器中内置的所有方法(Digest、NTLM 等):
+
+ request.auth('digest', 'secret', {type:'auto'})
+
+`auth` 方法还支持一种`承载类型`,以指定基于令牌的身份验证:
+
+ request.auth('my_token', { type: 'bearer' })
+
+## 跟随重定向
+
+默认情况下将跟随最多 5 个重定向,但是您可以使用 `res.redirects(n)` 方法指定它:
+
+ const response = await request.get('/some.png').redirects(2);
+
+超出限制的重定向被视为错误。使用 `.ok(res => res.status < 400)` 将它们读取为成功响应。
+
+## 全局状态代理程序
+
+### 保存 cookie
+
+在 Node 中 SuperAgent 默认不保存 cookie,但您可以使用 `.agent()` 方法创建保存 cookie 的 SuperAgent 副本。每个副本都有一个单独的 cookie 储存器。
+
+ const agent = request.agent();
+ agent
+ .post('/login')
+ .then(() => {
+ return agent.get('/cookied-page');
+ });
+
+在浏览器中,cookie 由浏览器自动管理,因此 `.agent()` 不会隔离 cookie。
+
+### 多个请求的默认选项
+
+代理程序上调用的常规请求方法将用作该代理发出的所有请求的默认值。
+
+ const agent = request.agent()
+ .use(plugin)
+ .auth(shared);
+
+ await agent.get('/with-plugin-and-auth'); // 带有插件和身份验证
+ await agent.get('/also-with-plugin-and-auth'); // 也带有插件和身份验证
+
+代理可以用来设置默认值的完整方法列表是:`use`、 `on`、 `once`、 `set`、 `query`、 `type`、 `accept`、 `auth`、 `withCredentials`、 `sortQuery`、 `retry`、 `ok`、 `redirects`、 `timeout`、 `buffer`、 `serialize`、 `parse`、 `ca`、 `key`、 `pfx`、 `cert`.
+
+## 管道数据
+
+Node 客户端允许您通过管道将数据传入和传出请求。请注意,使用 `.pipe()` **代替** `.end()/.then()` 方法。
+
+管道文件的内容作为请求的例子:
+
+ const request = require('superagent');
+ const fs = require('fs');
+
+ const stream = fs.createReadStream('path/to/my.json');
+ const req = request.post('/somewhere');
+ req.type('json');
+ stream.pipe(req);
+
+请注意,当您通过管道发送请求时,superagent 使用[分块传输编码](https://baike.baidu.com/item/%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81/8359216?fr=aladdin)发送管道数据,并非所有服务器(例如 Python WSGI 服务器)都支持。
+
+或将响应传送到文件:
+
+ const stream = fs.createWriteStream('path/to/my.json');
+ const req = request.get('/some.json');
+ req.pipe(stream);
+
+ 不能混合使用管道和回调函数或 promises。请注意,您**不应**尝试通过管道传输 `.end()` 或 `Response` 对象的结果:
+
+ // 别特么这么写:
+ const stream = getAWritableStream();
+ const req = request
+ .get('/some.json')
+ // BAD: 这会将无用信息管道传输到流中并以意想不到的方式失败
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
+ const req = request
+ .get('/some.json')
+ .end()
+ // BAD: 这也不支持,调用 .end 之后调用 .pipe。
+ .pipe(nope_its_too_late);
+
+在 superagent 的[未来版本](https://github.com/ladjs/superagent/issues/1188)中,对 `pipe()` 的不当调用将失败。
+
+## 多部分请求
+
+SuperAgent 也非常适合 _构建_ 它提供方法 `.attach()` 和 `.field()` 的多部分请求。
+
+当您使用 `.field()` 或 `.attach()` 时,您不能使用 `.send()` 并且您*不能*设置 `Content-Type`(将为您设置正确的类型)。
+
+### 附加文件
+
+要发送文件,请使用 `.attach(name, [file], [options])`。您可以通过多次调用 `.attach` 来附加多个文件。参数是:
+
+ * `name` — form 表单中的字段名。
+ * `file` — 带有文件路径的字符串或 `Blob/Buffer` 对象。
+ * `options` — (可选)自定义文件名的字符串或 `{filename: string}` 对象。在 Node 中也支持 `{contentType: 'mime/type'}`。在浏览器中创建一个具有适当类型的 `Blob`。
+
+
+
+ request
+ .post('/upload')
+ .attach('image1', 'path/to/felix.jpeg')
+ .attach('image2', imageBuffer, 'luna.jpeg')
+ .field('caption', 'My cats')
+ .then(callback);
+
+### 字段值
+
+与 HTML 中的表单字段非常相似,您可以使用 `.field(name, value)` 和 `.field({name: value})` 设置字段值。假设您想上传一些带有您的姓名和电子邮件的图片,您的请求可能如下所示:
+
+ request
+ .post('/upload')
+ .field('user[name]', 'Tobi')
+ .field('user[email]', 'tobi@learnboost.com')
+ .field('friends[]', ['loki', 'jane'])
+ .attach('image', 'path/to/tobi.png')
+ .then(callback);
+
+## 压缩
+
+node 客户端支持压缩过的响应,最重要的是,您无需执行任何操作!它就能用。
+
+## 缓冲响应
+
+要强制将响应主体缓冲为 `res.text`,您可以调用 `req.buffer()`。要取消对文本响应(例如 "text/plain"、"text/html" 等)的默认缓冲,您可以调用 `req.buffer(false)`。
+
+当缓冲提供 `res.buffered` 标志时,您可以使用它在同一个回调中处理缓冲和非缓冲响应。
+
+## CORS
+
+出于安全原因,浏览器将阻止跨域请求,除非服务器选择使用 CORS 标头。浏览器还会发出额外的 __OPTIONS__ 请求来检查服务器允许哪些 HTTP 标头和方法。[阅读有关 CORS 的更多信息](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS)。
+
+`.withCredentials()` 方法支持从源发送 cookie,但仅当 Access-Control-Allow-Origin _不是_ 通配符 ("*") 且 `Access-Control-Allow-Credentials` 为 "true" 时。
+
+ request
+ .get('https://api.example.com:4001/')
+ .withCredentials()
+ .then(res => {
+ assert.equal(200, res.status);
+ assert.equal('tobi', res.text);
+ })
+
+## 错误处理
+
+您的回调函数将始终传递两个参数:错误和响应。如果没有发生错误,第一个参数将为`null`:
+
+ request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .then(res => {
+
+ });
+
+还会触发"错误"事件,您可以监听:
+
+ request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .on('error', handle)
+ .then(res => {
+
+ });
+
+请注意,**默认情况下,superagent 会考虑 4xx 和 5xx 响应(以及未处理的 3xx 响应)视为错误**。例如,如果您收到 `304 Not modified`、`403 Forbidden` 或 `500 Internal server` 错误响应,则此状态信息将通过 `err.status` 提供。来自此类响应的错误还包含一个 `err.response` 字段,其中包含"[响应属性](#响应属性)"中提到的所有属性。该库以这种方式运行以处理需要成功响应并将 HTTP 错误状态代码视为错误的常见情况,同时仍允许围绕特定错误条件进行自定义逻辑。
+
+网络故障、超时和其他不产生响应的错误将不包含 `err.status` 或 `err.response` 字段。
+
+如果您希望处理 404 或其他 HTTP 错误响应,您可以查询 `err.status` 属性。当发生 HTTP 错误(4xx 或 5xx 响应)时, `res.error` 属性是一个 `Error` 对象,这允许您执行以下检查:
+
+ if (err && err.status === 404) {
+ alert('oh no ' + res.body.message);
+ }
+ else if (err) {
+ // 所有其他需要处理的错误类型
+ }
+
+或者,您可以使用 `.ok(callback)` 方法来确定响应是否为错误。 `ok` 函数的回调函数获得响应,如果响应应该被解释为成功,则返回 true。
+
+ request.get('/404')
+ .ok(res => res.status < 500)
+ .then(response => {
+ // 将 404 页面作为成功响应
+ })
+
+## 进度跟踪
+
+SuperAgent 在上传和下载大文件时触发 `progress` 事件。
+
+ request.post(url)
+ .attach('field_name', file)
+ .on('progress', event => {
+ /* event的值:
+ {
+ direction: "upload" or "download"
+ percent: 0 to 100 // 如果文件大小未知,可能会没有
+ total: // 总文件大小,可能没有
+ loaded: // 到目前为止下载或上传的字节数
+ } */
+ })
+ .then()
+
+
+## 在本地主机上测试
+
+### 强制连接特定 IP 地址
+
+在 Node.js 中,可以忽略 DNS 解析并使用 `.connect()` 方法将所有请求定向到特定 IP 地址。例如,此请求将转到 localhost 而不是 `example.com`:
+
+ const res = await request.get("http://example.com").connect("127.0.0.1");
+
+因为请求可能被重定向,所以可以指定多个主机名和多个 IP,以及一个特殊的 `*` 作为后备(注意:不支持其他通配符)。请求将保留其 `Host` 请求头的原始值。
+
+ const res = await request.get("http://redir.example.com:555")
+ .connect({
+ "redir.example.com": "127.0.0.1", // redir.example.com:555 将使用 127.0.0.1:555
+ "www.example.com": false, // 不覆盖这个;正常使用 DNS
+ "mapped.example.com": { host: "127.0.0.1", port: 8080}, // mapped.example.com:* 将使用 127.0.0.1:8080
+ "*": "proxy.example.com", // 所有其他请求都将发送到该主机
+ });
+
+### 忽略本地主机上损坏/不安全的 HTTPS
+
+在 Node.js 中,当 HTTPS 配置错误且不安全(例如,使用自签名证书而*不指定*自己的 `.ca()`)时,仍然可以通过调用 `.trustLocalhost()` 来允许对 `localhost` 的请求:
+
+ const res = await request.get("https://localhost").trustLocalhost()
+
+与 `.connect("127.0.0.1")` 一起,这可用于强制将对任何域的 HTTPS 请求重新路由到 `localhost`。
+
+忽略本地主机上损坏的 HTTPS 通常是安全的,因为环回接口不会暴露给不受信任的网络。信任 `localhost` 可能会成为未来的默认设置。使用 `.trustLocalhost(false)` 强制检查 `127.0.0.1` 的可靠性。
+
+当向任何其他 IP 发出请求时,我们故意不支持禁用 HTTPS 安全性,因为这些选项最终被滥用为 HTTPS 问题的快速"修复"。您可以从 [Let's Encrypt](https://certbot.eff.org) 获得免费的 HTTPS 证书或设置您自己的 CA (`.ca(ca_public_pem)`) 以使您的自签名证书受信任。
+
+## Promise 和生成器函数支持
+
+SuperAgent 的请求是一个 "thenable" 对象(带有then方法的对象),它与 JavaScript Promise 和 `async/await` 语法兼容。
+
+ const res = await request.get(url);
+
+如果你使用 Promise,**不要**调用 `.end()` 或 `.pipe()`。任何使用 `.then()` 或 `await` 都会禁用所有其他使用请求的方式。 像 [co](https://github.com/tj/co) 这样的库或像 [koa](https://github.com/koajs/koa) 这样的 web 框架可以在任何 SuperAgent 方法上 `yield`:
+
+ const req = request
+ .get('http://local')
+ .auth('tobi', 'learnboost');
+ const res = yield req;
+
+请注意,SuperAgent 期望全局 `Promise` 对象存在。您需要一个 polyfill 才能在 Internet Explorer 或 Node.js 0.10 中使用 Promise。
+
+## 浏览器和 node 版本
+
+SuperAgent 有两种实现:一种用于 Web 浏览器(使用 XHR),另一种用于 Node.JS(使用核心 http 模块)。默认情况下,Browserify 和 WebPack 将选择浏览器版本。
+
+如果要使用 WebPack 为 Node.JS 编译代码,您*必须*在其配置中指定[node target](https://webpack.github.io/docs/configuration.html#target)。
+
+### 在 electron 中使用浏览器版本
+
+[Electron](https://electron.atom.io/) 开发人员报告说,如果您希望使用浏览器版本的 SuperAgent 而不是 Node 版本,您可以 `require('superagent/superagent')`。这样您的请求将显示在 Chrome 开发者工具的"网络(Network)"选项卡中。请注意,自动化测试套件未涵盖此环境,也未得到官方支持。
+
+## 使用代理发送请求
+
+可以使用另一个作者的 [superagent-proxy](https://www.npmjs.com/package/superagent-proxy) 模块
+
+## 翻译说明
+
+文档全部内容都是根据原英文文档翻译的,译者也没水平,所以如果有错误还请指出
\ No newline at end of file
diff --git a/examples/simple-get.js b/examples/simple-get.js
index 417e45890..a286fd885 100644
--- a/examples/simple-get.js
+++ b/examples/simple-get.js
@@ -1,13 +1,13 @@
-
/**
* Module dependencies.
*/
-var request = require('..');
+const request = require('..');
-var url = 'https://gist.github.com/visionmedia/9fff5b23c1bf1791c349/raw/3e588e0c4f762f15538cdaf9882df06b3f5b3db6/works.js';
+const url =
+ 'https://gist.githubusercontent.com/reinaldo13/cdbb4d663ba23410a77b/raw/0345267767d50790051951ddc460e2699649de2b/it-works.txt';
-request.get(url, function(err, res){
- if (err) throw err;
+request.get(url, (error, res) => {
+ if (error) throw error;
console.log(res.text);
});
diff --git a/index.html b/index.html
new file mode 100644
index 000000000..42d3f3eb3
--- /dev/null
+++ b/index.html
@@ -0,0 +1,668 @@
+
+
+
+
+ SuperAgent — elegant API for AJAX in Node and browsers
+
+
+
+
+
+
+
SuperAgent
+
SuperAgent is light-weight progressive ajax API crafted for flexibility, readability, and a low learning curve after being frustrated with many of the existing request APIs. It also works with Node.js!
+
request
+ .post('/api/pet')
+ .send({ name: 'Manny', species: 'cat' })
+ .set('X-API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(res => {
+ alert('yay got ' + JSON.stringify(res.body));
+ });
+
+
Test documentation
+
中文文档
+
The following test documentation was generated with Mocha's "doc" reporter, and directly reflects the test suite. This provides an additional source of documentation.
+
Request basics
+
A request can be initiated by invoking the appropriate method on the request object, then calling .then() (or .end() or await ) to send the request. For example a simple GET request:
+
request
+ .get('/search')
+ .then(res => {
+ // res.body, res.headers, res.status
+ })
+ .catch(err => {
+ // err.message, err.response
+ });
+
+
HTTP method may also be passed as a string:
+
request('GET', '/search').then(success, failure);
+
+
Old-style callbacks are also supported, but not recommended. Instead of .then() you can call .end():
+
request('GET', '/search').end(function(err, res){
+ if (res.ok) {}
+ });
+
+
Absolute URLs can be used. In web browsers absolute URLs work only if the server implements CORS .
+
request
+ .get('https://example.com/search')
+ .then(res => {
+
+ });
+
+
The Node client supports making requests to Unix Domain Sockets :
+
// pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
+ // Use `%2F` as `/` in SOCKET_PATH
+ try {
+ const res = await request
+ .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search');
+ // res.body, res.headers, res.status
+ } catch(err) {
+ // err.message, err.response
+ }
+
+
DELETE , HEAD , PATCH , POST , and PUT requests can also be used, simply change the method name:
+
request
+ .head('/favicon.ico')
+ .then(res => {
+
+ });
+
+
DELETE can be also called as .del() for compatibility with old IE where delete is a reserved word.
+
The HTTP method defaults to GET , so if you wish, the following is valid:
+
request('/search', (err, res) => {
+
+ });
+
+
Using HTTP/2
+
To make a request using HTTP/2 protocol only (with no HTTP/1.x fallback), use the .http2() method.
+
const request = require('superagent');
+ const res = await request
+ .get('https://example.com/h2')
+ .http2();
+
+
+
Setting header fields is simple, invoke .set() with a field name and value:
+
request
+ .get('/search')
+ .set('API-Key', 'foobar')
+ .set('Accept', 'application/json')
+ .then(callback);
+
+
You may also pass an object to set several fields in a single call:
+
request
+ .get('/search')
+ .set({ 'API-Key': 'foobar', Accept: 'application/json' })
+ .then(callback);
+
+
GET requests
+
The .query() method accepts objects, which when used with the GET method will form a query-string. The following will produce the path /search?query=Manny&range=1..5&order=desc.
+
request
+ .get('/search')
+ .query({ query: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .then(res => {
+
+ });
+
+
Or as a single object:
+
request
+ .get('/search')
+ .query({ query: 'Manny', range: '1..5', order: 'desc' })
+ .then(res => {
+
+ });
+
+
The .query() method accepts strings as well:
+
request
+ .get('/querystring')
+ .query('search=Manny&range=1..5')
+ .then(res => {
+
+ });
+
+
Or joined:
+
request
+ .get('/querystring')
+ .query('search=Manny')
+ .query('range=1..5')
+ .then(res => {
+
+ });
+
+
HEAD requests
+
You can also use the .query() method for HEAD requests. The following will produce the path /users?email=joe@smith.com.
+
request
+ .head('/users')
+ .query({ email: 'joe@smith.com' })
+ .then(res => {
+
+ });
+
+
POST / PUT requests
+
A typical JSON POST request might look a little like the following, where we set the Content-Type header field appropriately, and "write" some data, in this case just a JSON string.
+
request.post('/user')
+ .set('Content-Type', 'application/json')
+ .send('{"name":"tj","pet":"tobi"}')
+ .then(callback)
+ .catch(errorCallback)
+
+
Since JSON is undoubtedly the most common, it's the default ! The following example is equivalent to the previous.
+
request.post('/user')
+ .send({ name: 'tj', pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
Or using multiple .send() calls:
+
request.post('/user')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
By default sending strings will set the Content-Type to application/x-www-form-urlencoded,
+ multiple calls will be concatenated with &, here resulting in name=tj&pet=tobi:
+
request.post('/user')
+ .send('name=tj')
+ .send('pet=tobi')
+ .then(callback, errorCallback);
+
+
SuperAgent formats are extensible, however by default "json" and "form" are supported. To send the data as application/x-www-form-urlencoded simply invoke .type() with "form", where the default is "json". This request will POST the body "name=tj&pet=tobi".
+
request.post('/user')
+ .type('form')
+ .send({ name: 'tj' })
+ .send({ pet: 'tobi' })
+ .then(callback, errorCallback)
+
+
Sending a FormData object is also supported. The following example will POST the content of the HTML form identified by id="myForm":
+
request.post('/user')
+ .send(new FormData(document.getElementById('myForm')))
+ .then(callback, errorCallback)
+
+
Setting the Content-Type
+
The obvious solution is to use the .set() method:
+
request.post('/user')
+ .set('Content-Type', 'application/json')
+
+
As a short-hand the .type() method is also available, accepting
+the canonicalized MIME type name complete with type/subtype, or
+simply the extension name such as "xml", "json", "png", etc:
+
request.post('/user')
+ .type('application/json')
+
+ request.post('/user')
+ .type('json')
+
+ request.post('/user')
+ .type('png')
+
+
Serializing request body
+
SuperAgent will automatically serialize JSON and forms.
+You can setup automatic serialization for other types as well:
+
request.serialize['application/xml'] = function (obj) {
+ return 'string generated from obj';
+};
+
+// going forward, all requests with a Content-type of
+// 'application/xml' will be automatically serialized
+
+
If you want to send the payload in a custom format, you can replace
+the built-in serialization with the .serialize() method on a per-request basis:
+
request
+ .post('/user')
+ .send({foo: 'bar'})
+ .serialize(obj => {
+ return 'string generated from obj';
+ });
+
+
Retrying requests
+
When given the .retry() method, SuperAgent will automatically retry requests, if they fail in a way that is transient or could be due to a flaky Internet connection.
+
This method has two optional arguments: number of retries (default 1) and a callback. It calls callback(err, res) before each retry. The callback may return true/false to control whether the request should be retried (but the maximum number of retries is always applied).
+
request
+ .get('https://example.com/search')
+ .retry(2) // or:
+ .retry(2, callback)
+ .then(finished);
+ .catch(failed);
+
+
Use .retry() only with requests that are idempotent (i.e. multiple requests reaching the server won't cause undesirable side effects like duplicate purchases).
+
All request methods are tried by default (which means if you do not want POST requests to be retried, you will need to pass a custom retry callback).
+
By default the following status codes are retried:
+
+408
+413
+429
+500
+502
+503
+504
+521
+522
+524
+
+
By default the following error codes are retried:
+
+'ETIMEDOUT'
+'ECONNRESET'
+'EADDRINUSE'
+'ECONNREFUSED'
+'EPIPE'
+'ENOTFOUND'
+'ENETUNREACH'
+'EAI_AGAIN'
+
+
Setting Accept
+
In a similar fashion to the .type() method it is also possible to set the Accept header via the short hand method .accept(). Which references request.types as well allowing you to specify either the full canonicalized MIME type name as type/subtype, or the extension suffix form as "xml", "json", "png", etc. for convenience:
+
request.get('/user')
+ .accept('application/json')
+
+ request.get('/user')
+ .accept('json')
+
+ request.post('/user')
+ .accept('png')
+
+
Facebook and Accept JSON
+
If you are calling Facebook's API, be sure to send an Accept: application/json header in your request. If you don't do this, Facebook will respond with Content-Type: text/javascript; charset=UTF-8, which SuperAgent will not parse and thus res.body will be undefined. You can do this with either req.accept('json') or req.set('Accept', 'application/json'). See issue 1078 for details.
+
Query strings
+
req.query(obj) is a method which may be used to build up a query-string. For example populating ?format=json&dest=/login on a POST :
+
request
+ .post('/')
+ .query({ format: 'json' })
+ .query({ dest: '/login' })
+ .send({ post: 'data', here: 'wahoo' })
+ .then(callback);
+
+
By default the query string is not assembled in any particular order. An asciibetically-sorted query string can be enabled with req.sortQuery(). You may also provide a custom sorting comparison function with req.sortQuery(myComparisonFn). The comparison function should take 2 arguments and return a negative/zero/positive integer.
+
// default order
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery()
+ .then(callback)
+
+ // customized sort function
+ request.get('/user')
+ .query('name=Nick')
+ .query('search=Manny')
+ .sortQuery((a, b) => a.length - b.length)
+ .then(callback)
+
+
TLS options
+
In Node.js SuperAgent supports methods to configure HTTPS requests:
+
+.ca(): Set the CA certificate(s) to trust
+.cert(): Set the client certificate chain(s)
+.key(): Set the client private key(s)
+.pfx(): Set the client PFX or PKCS12 encoded private key and certificate chain
+.disableTLSCerts(): Does not reject expired or invalid TLS certs. Sets internally rejectUnauthorized=true. Be warned, this method allows MITM attacks.
+
+
For more information, see Node.js https.request docs .
+
var key = fs.readFileSync('key.pem'),
+ cert = fs.readFileSync('cert.pem');
+
+request
+ .post('/client-auth')
+ .key(key)
+ .cert(cert)
+ .then(callback);
+
+
var ca = fs.readFileSync('ca.cert.pem');
+
+request
+ .post('https://localhost/private-ca-server')
+ .ca(ca)
+ .then(res => {});
+
+
Parsing response bodies
+
SuperAgent will parse known response-body data for you,
+currently supporting application/x-www-form-urlencoded,
+application/json, and multipart/form-data. You can setup
+automatic parsing for other response-body data as well:
+
//browser
+request.parse['application/xml'] = function (str) {
+ return {'object': 'parsed from str'};
+};
+
+//node
+request.parse['application/xml'] = function (res, cb) {
+ //parse response text and set res.body here
+
+ cb(null, res);
+};
+
+//going forward, responses of type 'application/xml'
+//will be parsed automatically
+
+
You can set a custom parser (that takes precedence over built-in parsers) with the .buffer(true).parse(fn) method. If response buffering is not enabled (.buffer(false)) then the response event will be emitted without waiting for the body parser to finish, so response.body won't be available.
+
JSON / Urlencoded
+
The property res.body is the parsed object, for example if a request responded with the JSON string '{"user":{"name":"tobi"}}', res.body.user.name would be "tobi". Likewise the x-www-form-urlencoded value of "user[name]=tobi" would yield the same result. Only one level of nesting is supported. If you need more complex data, send JSON instead.
+
Arrays are sent by repeating the key. .send({color: ['red','blue']}) sends color=red&color=blue. If you want the array keys to contain [] in their name, you must add it yourself, as SuperAgent doesn't add it automatically.
+
Multipart
+
The Node client supports multipart/form-data via the Formidable module. When parsing multipart responses, the object res.files is also available to you. Suppose for example a request responds with the following multipart body:
+
--whoop
+Content-Disposition: attachment; name="image"; filename="tobi.png"
+Content-Type: image/png
+
+... data here ...
+--whoop
+Content-Disposition: form-data; name="name"
+Content-Type: text/plain
+
+Tobi
+--whoop--
+
+
You would have the values res.body.name provided as "Tobi", and res.files.image as a File object containing the path on disk, filename, and other properties.
+
Binary
+
In browsers, you may use .responseType('blob') to request handling of binary response bodies. This API is unnecessary when running in node.js. The supported argument values for this method are
+
+'blob' passed through to the XmlHTTPRequest responseType property
+'arraybuffer' passed through to the XmlHTTPRequest responseType property
+
+
req.get('/binary.data')
+ .responseType('blob')
+ .then(res => {
+ // res.body will be a browser native Blob type here
+ });
+
+
For more information, see the Mozilla Developer Network xhr.responseType docs .
+
Response properties
+
Many helpful flags and properties are set on the Response object, ranging from the response text, parsed response body, header fields, status flags and more.
+
Response text
+
The res.text property contains the unparsed response body string. This property is always present for the client API, and only when the mime type matches "text/", " /json", or "x-www-form-urlencoded" by default for node. The reasoning is to conserve memory, as buffering text of large bodies such as multipart files or images is extremely inefficient. To force buffering see the "Buffering responses" section.
+
Response body
+
Much like SuperAgent can auto-serialize request data, it can also automatically parse it. When a parser is defined for the Content-Type, it is parsed, which by default includes "application/json" and "application/x-www-form-urlencoded". The parsed object is then available via res.body.
+
+
The res.header contains an object of parsed header fields, lowercasing field names much like node does. For example res.header['content-length'].
+
Response Content-Type
+
The Content-Type response header is special-cased, providing res.type, which is void of the charset (if any). For example the Content-Type of "text/html; charset=utf8" will provide "text/html" as res.type, and the res.charset property would then contain "utf8".
+
Response status
+
The response status flags help determine if the request was a success, among other useful information, making SuperAgent ideal for interacting with RESTful web services. These flags are currently defined as:
+
var type = status / 100 | 0;
+
+ // status / class
+ res.status = status;
+ res.statusType = type;
+
+ // basics
+ res.info = 1 == type;
+ res.ok = 2 == type;
+ res.clientError = 4 == type;
+ res.serverError = 5 == type;
+ res.error = 4 == type || 5 == type;
+
+ // sugar
+ res.accepted = 202 == status;
+ res.noContent = 204 == status || 1223 == status;
+ res.badRequest = 400 == status;
+ res.unauthorized = 401 == status;
+ res.notAcceptable = 406 == status;
+ res.notFound = 404 == status;
+ res.forbidden = 403 == status;
+
+
Aborting requests
+
To abort requests simply invoke the req.abort() method.
+
Timeouts
+
Sometimes networks and servers get "stuck" and never respond after accepting a request. Set timeouts to avoid requests waiting forever.
+
+req.timeout({deadline:ms}) or req.timeout(ms) (where ms is a number of milliseconds > 0) sets a deadline for the entire request (including all uploads, redirects, server processing time) to complete. If the response isn't fully downloaded within that time, the request will be aborted.
+
+req.timeout({response:ms}) sets maximum time to wait for the first byte to arrive from the server, but it does not limit how long the entire download can take. Response timeout should be at least few seconds longer than just the time it takes the server to respond, because it also includes time to make DNS lookup, TCP/IP and TLS connections, and time to upload request data.
+
+
+
You should use both deadline and response timeouts. This way you can use a short response timeout to detect unresponsive networks quickly, and a long deadline to give time for downloads on slow, but reliable, networks. Note that both of these timers limit how long uploads of attached files are allowed to take. Use long timeouts if you're uploading files.
+
request
+ .get('/big-file?network=slow')
+ .timeout({
+ response: 5000, // Wait 5 seconds for the server to start sending,
+ deadline: 60000, // but allow 1 minute for the file to finish loading.
+ })
+ .then(res => {
+ /* responded in time */
+ }, err => {
+ if (err.timeout) { /* timed out! */ } else { /* other error */ }
+ });
+
+
Timeout errors have a .timeout property.
+
Authentication
+
In both Node and browsers auth available via the .auth() method:
+
request
+ .get('http://local')
+ .auth('tobi', 'learnboost')
+ .then(callback);
+
+
In the Node client Basic auth can be in the URL as "user:pass":
+
request.get('http://tobi:learnboost@local').then(callback);
+
+
By default only Basic auth is used. In browser you can add {type:'auto'} to enable all methods built-in in the browser (Digest, NTLM, etc.):
+
request.auth('digest', 'secret', {type:'auto'})
+
+
The auth method also supports a type of bearer, to specify token-based authentication:
+
request.auth('my_token', { type: 'bearer' })
+
+
Following redirects
+
By default up to 5 redirects will be followed, however you may specify this with the res.redirects(n) method:
+
const response = await request.get('/some.png').redirects(2);
+
+
Redirects exceeding the limit are treated as errors. Use .ok(res => res.status < 400) to read them as successful responses.
+
Agents for global state
+
Saving cookies
+
In Node SuperAgent does not save cookies by default, but you can use the .agent() method to create a copy of SuperAgent that saves cookies. Each copy has a separate cookie jar.
+
const agent = request.agent();
+ agent
+ .post('/login')
+ .then(() => {
+ return agent.get('/cookied-page');
+ });
+
+
In browsers cookies are managed automatically by the browser, so the .agent() does not isolate cookies.
+
Default options for multiple requests
+
Regular request methods called on the agent will be used as defaults for all requests made by that agent.
+
const agent = request.agent()
+ .use(plugin)
+ .auth(shared);
+
+ await agent.get('/with-plugin-and-auth');
+ await agent.get('/also-with-plugin-and-auth');
+
+
The complete list of methods that the agent can use to set defaults is: use, on, once, set, query, type, accept, auth, withCredentials, sortQuery, retry, ok, redirects, timeout, buffer, serialize, parse, ca, key, pfx, cert.
+
Piping data
+
The Node client allows you to pipe data to and from the request. Please note that .pipe() is used instead of .end()/.then() methods.
+
For example piping a file's contents as the request:
+
const request = require('superagent');
+ const fs = require('fs');
+
+ const stream = fs.createReadStream('path/to/my.json');
+ const req = request.post('/somewhere');
+ req.type('json');
+ stream.pipe(req);
+
+
Note that when you pipe to a request, superagent sends the piped data with chunked transfer encoding , which isn't supported by all servers (for instance, Python WSGI servers).
+
Or piping the response to a file:
+
const stream = fs.createWriteStream('path/to/my.json');
+ const req = request.get('/some.json');
+ req.pipe(stream);
+
+
It's not possible to mix pipes and callbacks or promises. Note that you should NOT attempt to pipe the result of .end() or the Response object:
+
// Don't do either of these:
+ const stream = getAWritableStream();
+ const req = request
+ .get('/some.json')
+ // BAD: this pipes garbage to the stream and fails in unexpected ways
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
+ const req = request
+ .get('/some.json')
+ .end()
+ // BAD: this is also unsupported, .pipe calls .end for you.
+ .pipe(nope_its_too_late);
+
+
In a future version of superagent, improper calls to pipe() will fail.
+
Multipart requests
+
SuperAgent is also great for building multipart requests for which it provides methods .attach() and .field().
+
When you use .field() or .attach() you can't use .send() and you must not set Content-Type (the correct type will be set for you).
+
Attaching files
+
To send a file use .attach(name, [file], [options]). You can attach multiple files by calling .attach multiple times. The arguments are:
+
+name — field name in the form.
+file — either string with file path or Blob/Buffer object.
+options — (optional) either string with custom file name or {filename: string} object. In Node also {contentType: 'mime/type'} is supported. In browser create a Blob with an appropriate type instead.
+
+
+
+
request
+ .post('/upload')
+ .attach('image1', 'path/to/felix.jpeg')
+ .attach('image2', imageBuffer, 'luna.jpeg')
+ .field('caption', 'My cats')
+ .then(callback);
+
+
Field values
+
Much like form fields in HTML, you can set field values with .field(name, value) and .field({name: value}). Suppose you want to upload a few images with your name and email, your request might look something like this:
+
request
+ .post('/upload')
+ .field('user[name]', 'Tobi')
+ .field('user[email]', 'tobi@learnboost.com')
+ .field('friends[]', ['loki', 'jane'])
+ .attach('image', 'path/to/tobi.png')
+ .then(callback);
+
+
Compression
+
The node client supports compressed responses, best of all, you don't have to do anything! It just works.
+
Buffering responses
+
To force buffering of response bodies as res.text you may invoke req.buffer(). To undo the default of buffering for text responses such as "text/plain", "text/html" etc you may invoke req.buffer(false).
+
When buffered the res.buffered flag is provided, you may use this to handle both buffered and unbuffered responses in the same callback.
+
CORS
+
For security reasons, browsers will block cross-origin requests unless the server opts-in using CORS headers. Browsers will also make extra OPTIONS requests to check what HTTP headers and methods are allowed by the server. Read more about CORS .
+
The .withCredentials() method enables the ability to send cookies from the origin, however only when Access-Control-Allow-Origin is not a wildcard ("*"), and Access-Control-Allow-Credentials is "true".
+
request
+ .get('https://api.example.com:4001/')
+ .withCredentials()
+ .then(res => {
+ assert.equal(200, res.status);
+ assert.equal('tobi', res.text);
+ })
+
+
Error handling
+
Your callback function will always be passed two arguments: error and response. If no error occurred, the first argument will be null:
+
request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .then(res => {
+
+ });
+
+
An "error" event is also emitted, with you can listen for:
+
request
+ .post('/upload')
+ .attach('image', 'path/to/tobi.png')
+ .on('error', handle)
+ .then(res => {
+
+ });
+
+
Note that superagent considers 4xx and 5xx responses (as well as unhandled 3xx responses) errors by default . For example, if you get a 304 Not modified, 403 Forbidden or 500 Internal server error response, this status information will be available via err.status. Errors from such responses also contain an err.response field with all of the properties mentioned in "Response properties ". The library behaves in this way to handle the common case of wanting success responses and treating HTTP error status codes as errors while still allowing for custom logic around specific error conditions.
+
Network failures, timeouts, and other errors that produce no response will contain no err.status or err.response fields.
+
If you wish to handle 404 or other HTTP error responses, you can query the err.status property. When an HTTP error occurs (4xx or 5xx response) the res.error property is an Error object, this allows you to perform checks such as:
+
if (err && err.status === 404) {
+ alert('oh no ' + res.body.message);
+ }
+ else if (err) {
+ // all other error types we handle generically
+ }
+
+
Alternatively, you can use the .ok(callback) method to decide whether a response is an error or not. The callback to the ok function gets a response and returns true if the response should be interpreted as success.
+
request.get('/404')
+ .ok(res => res.status < 500)
+ .then(response => {
+ // reads 404 page as a successful response
+ })
+
+
Progress tracking
+
SuperAgent fires progress events on upload and download of large files.
+
request.post(url)
+ .attach('field_name', file)
+ .on('progress', event => {
+ /* the event is:
+ {
+ direction: "upload" or "download"
+ percent: 0 to 100 // may be missing if file size is unknown
+ total: // total file size, may be missing
+ loaded: // bytes downloaded or uploaded so far
+ } */
+ })
+ .then()
+
+
Testing on localhost
+
Forcing specific connection IP address
+
In Node.js it's possible to ignore DNS resolution and direct all requests to a specific IP address using .connect() method. For example, this request will go to localhost instead of example.com:
+
const res = await request.get("http://example.com").connect("127.0.0.1");
+
+
Because the request may be redirected, it's possible to specify multiple hostnames and multiple IPs, as well as a special * as the fallback (note: other wildcards are not supported). The requests will keep their Host header with the original value. .connect(undefined) turns off the feature.
+
const res = await request.get("http://redir.example.com:555")
+ .connect({
+ "redir.example.com": "127.0.0.1", // redir.example.com:555 will use 127.0.0.1:555
+ "www.example.com": false, // don't override this one; use DNS as normal
+ "mapped.example.com": { host: "127.0.0.1", port: 8080}, // mapped.example.com:* will use 127.0.0.1:8080
+ "*": "proxy.example.com", // all other requests will go to this host
+ });
+
+
Ignoring broken/insecure HTTPS on localhost
+
In Node.js, when HTTPS is misconfigured and insecure (e.g. using self-signed certificate without specifying own .ca()), it's still possible to permit requests to localhost by calling .trustLocalhost():
+
const res = await request.get("https://localhost").trustLocalhost()
+
+
Together with .connect("127.0.0.1") this may be used to force HTTPS requests to any domain to be re-routed to localhost instead.
+
It's generally safe to ignore broken HTTPS on localhost, because the loopback interface is not exposed to untrusted networks. Trusting localhost may become the default in the future. Use .trustLocalhost(false) to force check of 127.0.0.1's authenticity.
+
We intentionally don't support disabling of HTTPS security when making requests to any other IP, because such options end up abused as a quick "fix" for HTTPS problems. You can get free HTTPS certificates from Let's Encrypt or set your own CA (.ca(ca_public_pem)) to make your self-signed certificates trusted.
+
Promise and Generator support
+
SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and the async/await syntax.
+
const res = await request.get(url);
+
+
If you're using promises, do not call .end() or .pipe(). Any use of .then() or await disables all other ways of using the request.
+
Libraries like co or a web framework like koa can yield on any SuperAgent method:
+
const req = request
+ .get('http://local')
+ .auth('tobi', 'learnboost');
+ const res = yield req;
+
+
Note that SuperAgent expects the global Promise object to be present. You'll need to use v7 and a polyfill to use promises in Internet Explorer or Node.js 0.10.
+
We have dropped support in v8 for IE. You must add a polyfill for WeakRef and BigInt if you wish to support Opera 85, iOS Safari 12.2-12.5, for example using https://cdnjs.cloudflare.com/polyfill/ :
+
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=WeakRef,BigInt"></script>
+
+
Browser and node versions
+
SuperAgent has two implementations: one for web browsers (using XHR) and one for Node.JS (using core http module). By default Browserify and WebPack will pick the browser version.
+
If want to use WebPack to compile code for Node.JS, you must specify node target in its configuration.
+
+
+
+
+
+
+
+
+
diff --git a/lib/client.js b/lib/client.js
deleted file mode 100644
index 33d033a6e..000000000
--- a/lib/client.js
+++ /dev/null
@@ -1,1123 +0,0 @@
-/**
- * Module dependencies.
- */
-
-var Emitter = require('emitter');
-var reduce = require('reduce');
-
-/**
- * Root reference for iframes.
- */
-
-var root = 'undefined' == typeof window
- ? (this || self)
- : window;
-
-/**
- * Noop.
- */
-
-function noop(){};
-
-/**
- * Check if `obj` is a host object,
- * we don't want to serialize these :)
- *
- * TODO: future proof, move to compoent land
- *
- * @param {Object} obj
- * @return {Boolean}
- * @api private
- */
-
-function isHost(obj) {
- var str = {}.toString.call(obj);
-
- switch (str) {
- case '[object File]':
- case '[object Blob]':
- case '[object FormData]':
- return true;
- default:
- return false;
- }
-}
-
-/**
- * Determine XHR.
- */
-
-request.getXHR = function () {
- if (root.XMLHttpRequest
- && (!root.location || 'file:' != root.location.protocol
- || !root.ActiveXObject)) {
- return new XMLHttpRequest;
- } else {
- try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}
- try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {}
- try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {}
- try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {}
- }
- return false;
-};
-
-/**
- * Removes leading and trailing whitespace, added to support IE.
- *
- * @param {String} s
- * @return {String}
- * @api private
- */
-
-var trim = ''.trim
- ? function(s) { return s.trim(); }
- : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); };
-
-/**
- * Check if `obj` is an object.
- *
- * @param {Object} obj
- * @return {Boolean}
- * @api private
- */
-
-function isObject(obj) {
- return obj === Object(obj);
-}
-
-/**
- * Serialize the given `obj`.
- *
- * @param {Object} obj
- * @return {String}
- * @api private
- */
-
-function serialize(obj) {
- if (!isObject(obj)) return obj;
- var pairs = [];
- for (var key in obj) {
- if (null != obj[key]) {
- pairs.push(encodeURIComponent(key)
- + '=' + encodeURIComponent(obj[key]));
- }
- }
- return pairs.join('&');
-}
-
-/**
- * Expose serialization method.
- */
-
- request.serializeObject = serialize;
-
- /**
- * Parse the given x-www-form-urlencoded `str`.
- *
- * @param {String} str
- * @return {Object}
- * @api private
- */
-
-function parseString(str) {
- var obj = {};
- var pairs = str.split('&');
- var parts;
- var pair;
-
- for (var i = 0, len = pairs.length; i < len; ++i) {
- pair = pairs[i];
- parts = pair.split('=');
- obj[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
- }
-
- return obj;
-}
-
-/**
- * Expose parser.
- */
-
-request.parseString = parseString;
-
-/**
- * Default MIME type map.
- *
- * superagent.types.xml = 'application/xml';
- *
- */
-
-request.types = {
- html: 'text/html',
- json: 'application/json',
- xml: 'application/xml',
- urlencoded: 'application/x-www-form-urlencoded',
- 'form': 'application/x-www-form-urlencoded',
- 'form-data': 'application/x-www-form-urlencoded'
-};
-
-/**
- * Default serialization map.
- *
- * superagent.serialize['application/xml'] = function(obj){
- * return 'generated xml here';
- * };
- *
- */
-
- request.serialize = {
- 'application/x-www-form-urlencoded': serialize,
- 'application/json': JSON.stringify
- };
-
- /**
- * Default parsers.
- *
- * superagent.parse['application/xml'] = function(str){
- * return { object parsed from str };
- * };
- *
- */
-
-request.parse = {
- 'application/x-www-form-urlencoded': parseString,
- 'application/json': JSON.parse
-};
-
-/**
- * Parse the given header `str` into
- * an object containing the mapped fields.
- *
- * @param {String} str
- * @return {Object}
- * @api private
- */
-
-function parseHeader(str) {
- var lines = str.split(/\r?\n/);
- var fields = {};
- var index;
- var line;
- var field;
- var val;
-
- lines.pop(); // trailing CRLF
-
- for (var i = 0, len = lines.length; i < len; ++i) {
- line = lines[i];
- index = line.indexOf(':');
- field = line.slice(0, index).toLowerCase();
- val = trim(line.slice(index + 1));
- fields[field] = val;
- }
-
- return fields;
-}
-
-/**
- * Return the mime type for the given `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-function type(str){
- return str.split(/ *; */).shift();
-};
-
-/**
- * Return header field parameters.
- *
- * @param {String} str
- * @return {Object}
- * @api private
- */
-
-function params(str){
- return reduce(str.split(/ *; */), function(obj, str){
- var parts = str.split(/ *= */)
- , key = parts.shift()
- , val = parts.shift();
-
- if (key && val) obj[key] = val;
- return obj;
- }, {});
-};
-
-/**
- * Initialize a new `Response` with the given `xhr`.
- *
- * - set flags (.ok, .error, etc)
- * - parse header
- *
- * Examples:
- *
- * Aliasing `superagent` as `request` is nice:
- *
- * request = superagent;
- *
- * We can use the promise-like API, or pass callbacks:
- *
- * request.get('/').end(function(res){});
- * request.get('/', function(res){});
- *
- * Sending data can be chained:
- *
- * request
- * .post('/user')
- * .send({ name: 'tj' })
- * .end(function(res){});
- *
- * Or passed to `.send()`:
- *
- * request
- * .post('/user')
- * .send({ name: 'tj' }, function(res){});
- *
- * Or passed to `.post()`:
- *
- * request
- * .post('/user', { name: 'tj' })
- * .end(function(res){});
- *
- * Or further reduced to a single call for simple cases:
- *
- * request
- * .post('/user', { name: 'tj' }, function(res){});
- *
- * @param {XMLHTTPRequest} xhr
- * @param {Object} options
- * @api private
- */
-
-function Response(req, options) {
- options = options || {};
- this.req = req;
- this.xhr = this.req.xhr;
- // responseText is accessible only if responseType is '' or 'text' and on older browsers
- this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined')
- ? this.xhr.responseText
- : null;
- this.statusText = this.req.xhr.statusText;
- this.setStatusProperties(this.xhr.status);
- this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders());
- // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but
- // getResponseHeader still works. so we get content-type even if getting
- // other headers fails.
- this.header['content-type'] = this.xhr.getResponseHeader('content-type');
- this.setHeaderProperties(this.header);
- this.body = this.req.method != 'HEAD'
- ? this.parseBody(this.text ? this.text : this.xhr.response)
- : null;
-}
-
-/**
- * Get case-insensitive `field` value.
- *
- * @param {String} field
- * @return {String}
- * @api public
- */
-
-Response.prototype.get = function(field){
- return this.header[field.toLowerCase()];
-};
-
-/**
- * Set header related properties:
- *
- * - `.type` the content type without params
- *
- * A response of "Content-Type: text/plain; charset=utf-8"
- * will provide you with a `.type` of "text/plain".
- *
- * @param {Object} header
- * @api private
- */
-
-Response.prototype.setHeaderProperties = function(header){
- // content-type
- var ct = this.header['content-type'] || '';
- this.type = type(ct);
-
- // params
- var obj = params(ct);
- for (var key in obj) this[key] = obj[key];
-};
-
-/**
- * Parse the given body `str`.
- *
- * Used for auto-parsing of bodies. Parsers
- * are defined on the `superagent.parse` object.
- *
- * @param {String} str
- * @return {Mixed}
- * @api private
- */
-
-Response.prototype.parseBody = function(str){
- var parse = request.parse[this.type];
- return parse && str && (str.length || str instanceof Object)
- ? parse(str)
- : null;
-};
-
-/**
- * Set flags such as `.ok` based on `status`.
- *
- * For example a 2xx response will give you a `.ok` of __true__
- * whereas 5xx will be __false__ and `.error` will be __true__. The
- * `.clientError` and `.serverError` are also available to be more
- * specific, and `.statusType` is the class of error ranging from 1..5
- * sometimes useful for mapping respond colors etc.
- *
- * "sugar" properties are also defined for common cases. Currently providing:
- *
- * - .noContent
- * - .badRequest
- * - .unauthorized
- * - .notAcceptable
- * - .notFound
- *
- * @param {Number} status
- * @api private
- */
-
-Response.prototype.setStatusProperties = function(status){
- // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
- if (status === 1223) {
- status = 204;
- }
-
- var type = status / 100 | 0;
-
- // status / class
- this.status = status;
- this.statusType = type;
-
- // basics
- this.info = 1 == type;
- this.ok = 2 == type;
- this.clientError = 4 == type;
- this.serverError = 5 == type;
- this.error = (4 == type || 5 == type)
- ? this.toError()
- : false;
-
- // sugar
- this.accepted = 202 == status;
- this.noContent = 204 == status;
- this.badRequest = 400 == status;
- this.unauthorized = 401 == status;
- this.notAcceptable = 406 == status;
- this.notFound = 404 == status;
- this.forbidden = 403 == status;
-};
-
-/**
- * Return an `Error` representative of this response.
- *
- * @return {Error}
- * @api public
- */
-
-Response.prototype.toError = function(){
- var req = this.req;
- var method = req.method;
- var url = req.url;
-
- var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')';
- var err = new Error(msg);
- err.status = this.status;
- err.method = method;
- err.url = url;
-
- return err;
-};
-
-/**
- * Expose `Response`.
- */
-
-request.Response = Response;
-
-/**
- * Initialize a new `Request` with the given `method` and `url`.
- *
- * @param {String} method
- * @param {String} url
- * @api public
- */
-
-function Request(method, url) {
- var self = this;
- Emitter.call(this);
- this._query = this._query || [];
- this.method = method;
- this.url = url;
- this.header = {};
- this._header = {};
- this.on('end', function(){
- var err = null;
- var res = null;
-
- try {
- res = new Response(self);
- } catch(e) {
- err = new Error('Parser is unable to parse the response');
- err.parse = true;
- err.original = e;
- return self.callback(err);
- }
-
- self.emit('response', res);
-
- if (err) {
- return self.callback(err, res);
- }
-
- if (res.status >= 200 && res.status < 300) {
- return self.callback(err, res);
- }
-
- var new_err = new Error(res.statusText || 'Unsuccessful HTTP response');
- new_err.original = err;
- new_err.response = res;
- new_err.status = res.status;
-
- self.callback(err || new_err, res);
- });
-}
-
-/**
- * Mixin `Emitter`.
- */
-
-Emitter(Request.prototype);
-
-/**
- * Allow for extension
- */
-
-Request.prototype.use = function(fn) {
- fn(this);
- return this;
-}
-
-/**
- * Set timeout to `ms`.
- *
- * @param {Number} ms
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.timeout = function(ms){
- this._timeout = ms;
- return this;
-};
-
-/**
- * Clear previous timeout.
- *
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.clearTimeout = function(){
- this._timeout = 0;
- clearTimeout(this._timer);
- return this;
-};
-
-/**
- * Abort the request, and clear potential timeout.
- *
- * @return {Request}
- * @api public
- */
-
-Request.prototype.abort = function(){
- if (this.aborted) return;
- this.aborted = true;
- this.xhr.abort();
- this.clearTimeout();
- this.emit('abort');
- return this;
-};
-
-/**
- * Set header `field` to `val`, or multiple fields with one object.
- *
- * Examples:
- *
- * req.get('/')
- * .set('Accept', 'application/json')
- * .set('X-API-Key', 'foobar')
- * .end(callback);
- *
- * req.get('/')
- * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' })
- * .end(callback);
- *
- * @param {String|Object} field
- * @param {String} val
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.set = function(field, val){
- if (isObject(field)) {
- for (var key in field) {
- this.set(key, field[key]);
- }
- return this;
- }
- this._header[field.toLowerCase()] = val;
- this.header[field] = val;
- return this;
-};
-
-/**
- * Remove header `field`.
- *
- * Example:
- *
- * req.get('/')
- * .unset('User-Agent')
- * .end(callback);
- *
- * @param {String} field
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.unset = function(field){
- delete this._header[field.toLowerCase()];
- delete this.header[field];
- return this;
-};
-
-/**
- * Get case-insensitive header `field` value.
- *
- * @param {String} field
- * @return {String}
- * @api private
- */
-
-Request.prototype.getHeader = function(field){
- return this._header[field.toLowerCase()];
-};
-
-/**
- * Set Content-Type to `type`, mapping values from `request.types`.
- *
- * Examples:
- *
- * superagent.types.xml = 'application/xml';
- *
- * request.post('/')
- * .type('xml')
- * .send(xmlstring)
- * .end(callback);
- *
- * request.post('/')
- * .type('application/xml')
- * .send(xmlstring)
- * .end(callback);
- *
- * @param {String} type
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.type = function(type){
- this.set('Content-Type', request.types[type] || type);
- return this;
-};
-
-/**
- * Set Accept to `type`, mapping values from `request.types`.
- *
- * Examples:
- *
- * superagent.types.json = 'application/json';
- *
- * request.get('/agent')
- * .accept('json')
- * .end(callback);
- *
- * request.get('/agent')
- * .accept('application/json')
- * .end(callback);
- *
- * @param {String} accept
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.accept = function(type){
- this.set('Accept', request.types[type] || type);
- return this;
-};
-
-/**
- * Set Authorization field value with `user` and `pass`.
- *
- * @param {String} user
- * @param {String} pass
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.auth = function(user, pass){
- var str = btoa(user + ':' + pass);
- this.set('Authorization', 'Basic ' + str);
- return this;
-};
-
-/**
-* Add query-string `val`.
-*
-* Examples:
-*
-* request.get('/shoes')
-* .query('size=10')
-* .query({ color: 'blue' })
-*
-* @param {Object|String} val
-* @return {Request} for chaining
-* @api public
-*/
-
-Request.prototype.query = function(val){
- if ('string' != typeof val) val = serialize(val);
- if (val) this._query.push(val);
- return this;
-};
-
-/**
- * Write the field `name` and `val` for "multipart/form-data"
- * request bodies.
- *
- * ``` js
- * request.post('/upload')
- * .field('foo', 'bar')
- * .end(callback);
- * ```
- *
- * @param {String} name
- * @param {String|Blob|File} val
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.field = function(name, val){
- if (!this._formData) this._formData = new root.FormData();
- this._formData.append(name, val);
- return this;
-};
-
-/**
- * Queue the given `file` as an attachment to the specified `field`,
- * with optional `filename`.
- *
- * ``` js
- * request.post('/upload')
- * .attach(new Blob(['hey! '], { type: "text/html"}))
- * .end(callback);
- * ```
- *
- * @param {String} field
- * @param {Blob|File} file
- * @param {String} filename
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.attach = function(field, file, filename){
- if (!this._formData) this._formData = new root.FormData();
- this._formData.append(field, file, filename);
- return this;
-};
-
-/**
- * Send `data`, defaulting the `.type()` to "json" when
- * an object is given.
- *
- * Examples:
- *
- * // querystring
- * request.get('/search')
- * .end(callback)
- *
- * // multiple data "writes"
- * request.get('/search')
- * .send({ search: 'query' })
- * .send({ range: '1..5' })
- * .send({ order: 'desc' })
- * .end(callback)
- *
- * // manual json
- * request.post('/user')
- * .type('json')
- * .send('{"name":"tj"})
- * .end(callback)
- *
- * // auto json
- * request.post('/user')
- * .send({ name: 'tj' })
- * .end(callback)
- *
- * // manual x-www-form-urlencoded
- * request.post('/user')
- * .type('form')
- * .send('name=tj')
- * .end(callback)
- *
- * // auto x-www-form-urlencoded
- * request.post('/user')
- * .type('form')
- * .send({ name: 'tj' })
- * .end(callback)
- *
- * // defaults to x-www-form-urlencoded
- * request.post('/user')
- * .send('name=tobi')
- * .send('species=ferret')
- * .end(callback)
- *
- * @param {String|Object} data
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.send = function(data){
- var obj = isObject(data);
- var type = this.getHeader('Content-Type');
-
- // merge
- if (obj && isObject(this._data)) {
- for (var key in data) {
- this._data[key] = data[key];
- }
- } else if ('string' == typeof data) {
- if (!type) this.type('form');
- type = this.getHeader('Content-Type');
- if ('application/x-www-form-urlencoded' == type) {
- this._data = this._data
- ? this._data + '&' + data
- : data;
- } else {
- this._data = (this._data || '') + data;
- }
- } else {
- this._data = data;
- }
-
- if (!obj || isHost(data)) return this;
- if (!type) this.type('json');
- return this;
-};
-
-/**
- * Invoke the callback with `err` and `res`
- * and handle arity check.
- *
- * @param {Error} err
- * @param {Response} res
- * @api private
- */
-
-Request.prototype.callback = function(err, res){
- var fn = this._callback;
- this.clearTimeout();
- fn(err, res);
-};
-
-/**
- * Invoke callback with x-domain error.
- *
- * @api private
- */
-
-Request.prototype.crossDomainError = function(){
- var err = new Error('Origin is not allowed by Access-Control-Allow-Origin');
- err.crossDomain = true;
- this.callback(err);
-};
-
-/**
- * Invoke callback with timeout error.
- *
- * @api private
- */
-
-Request.prototype.timeoutError = function(){
- var timeout = this._timeout;
- var err = new Error('timeout of ' + timeout + 'ms exceeded');
- err.timeout = timeout;
- this.callback(err);
-};
-
-/**
- * Enable transmission of cookies with x-domain requests.
- *
- * Note that for this to work the origin must not be
- * using "Access-Control-Allow-Origin" with a wildcard,
- * and also must set "Access-Control-Allow-Credentials"
- * to "true".
- *
- * @api public
- */
-
-Request.prototype.withCredentials = function(){
- this._withCredentials = true;
- return this;
-};
-
-/**
- * Initiate request, invoking callback `fn(res)`
- * with an instanceof `Response`.
- *
- * @param {Function} fn
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.end = function(fn){
- var self = this;
- var xhr = this.xhr = request.getXHR();
- var query = this._query.join('&');
- var timeout = this._timeout;
- var data = this._formData || this._data;
-
- // store callback
- this._callback = fn || noop;
-
- // state change
- xhr.onreadystatechange = function(){
- if (4 != xhr.readyState) return;
-
- // In IE9, reads to any property (e.g. status) off of an aborted XHR will
- // result in the error "Could not complete the operation due to error c00c023f"
- var status;
- try { status = xhr.status } catch(e) { status = 0; }
-
- if (0 == status) {
- if (self.timedout) return self.timeoutError();
- if (self.aborted) return;
- return self.crossDomainError();
- }
- self.emit('end');
- };
-
- // progress
- var handleProgress = function(e){
- if (e.total > 0) {
- e.percent = e.loaded / e.total * 100;
- }
- self.emit('progress', e);
- };
- if (this.hasListeners('progress')) {
- xhr.onprogress = handleProgress;
- }
- try {
- if (xhr.upload && this.hasListeners('progress')) {
- xhr.upload.onprogress = handleProgress;
- }
- } catch(e) {
- // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
- // Reported here:
- // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
- }
-
- // timeout
- if (timeout && !this._timer) {
- this._timer = setTimeout(function(){
- self.timedout = true;
- self.abort();
- }, timeout);
- }
-
- // querystring
- if (query) {
- query = request.serializeObject(query);
- this.url += ~this.url.indexOf('?')
- ? '&' + query
- : '?' + query;
- }
-
- // initiate request
- xhr.open(this.method, this.url, true);
-
- // CORS
- if (this._withCredentials) xhr.withCredentials = true;
-
- // body
- if ('GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !isHost(data)) {
- // serialize stuff
- var serialize = request.serialize[this.getHeader('Content-Type')];
- if (serialize) data = serialize(data);
- }
-
- // set header fields
- for (var field in this.header) {
- if (null == this.header[field]) continue;
- xhr.setRequestHeader(field, this.header[field]);
- }
-
- // send stuff
- this.emit('request', this);
- xhr.send(data);
- return this;
-};
-
-/**
- * Expose `Request`.
- */
-
-request.Request = Request;
-
-/**
- * Issue a request:
- *
- * Examples:
- *
- * request('GET', '/users').end(callback)
- * request('/users').end(callback)
- * request('/users', callback)
- *
- * @param {String} method
- * @param {String|Function} url or callback
- * @return {Request}
- * @api public
- */
-
-function request(method, url) {
- // callback
- if ('function' == typeof url) {
- return new Request('GET', method).end(url);
- }
-
- // url first
- if (1 == arguments.length) {
- return new Request('GET', method);
- }
-
- return new Request(method, url);
-}
-
-/**
- * GET `url` with optional callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed|Function} data or fn
- * @param {Function} fn
- * @return {Request}
- * @api public
- */
-
-request.get = function(url, data, fn){
- var req = request('GET', url);
- if ('function' == typeof data) fn = data, data = null;
- if (data) req.query(data);
- if (fn) req.end(fn);
- return req;
-};
-
-/**
- * HEAD `url` with optional callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed|Function} data or fn
- * @param {Function} fn
- * @return {Request}
- * @api public
- */
-
-request.head = function(url, data, fn){
- var req = request('HEAD', url);
- if ('function' == typeof data) fn = data, data = null;
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
-};
-
-/**
- * DELETE `url` with optional callback `fn(res)`.
- *
- * @param {String} url
- * @param {Function} fn
- * @return {Request}
- * @api public
- */
-
-request.del = function(url, fn){
- var req = request('DELETE', url);
- if (fn) req.end(fn);
- return req;
-};
-
-/**
- * PATCH `url` with optional `data` and callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed} data
- * @param {Function} fn
- * @return {Request}
- * @api public
- */
-
-request.patch = function(url, data, fn){
- var req = request('PATCH', url);
- if ('function' == typeof data) fn = data, data = null;
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
-};
-
-/**
- * POST `url` with optional `data` and callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed} data
- * @param {Function} fn
- * @return {Request}
- * @api public
- */
-
-request.post = function(url, data, fn){
- var req = request('POST', url);
- if ('function' == typeof data) fn = data, data = null;
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
-};
-
-/**
- * PUT `url` with optional `data` and callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed|Function} data or fn
- * @param {Function} fn
- * @return {Request}
- * @api public
- */
-
-request.put = function(url, data, fn){
- var req = request('PUT', url);
- if ('function' == typeof data) fn = data, data = null;
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
-};
-
-/**
- * Expose `request`.
- */
-
-module.exports = request;
diff --git a/lib/node/agent.js b/lib/node/agent.js
deleted file mode 100644
index 92afd74f2..000000000
--- a/lib/node/agent.js
+++ /dev/null
@@ -1,75 +0,0 @@
-
-/**
- * Module dependencies.
- */
-
-var CookieJar = require('cookiejar').CookieJar;
-var CookieAccess = require('cookiejar').CookieAccessInfo;
-var parse = require('url').parse;
-var request = require('./index');
-var methods = require('methods');
-
-/**
- * Expose `Agent`.
- */
-
-module.exports = Agent;
-
-/**
- * Initialize a new `Agent`.
- *
- * @api public
- */
-
-function Agent(options) {
- if (!(this instanceof Agent)) return new Agent(options);
- if (options) this._ca = options.ca;
- this.jar = new CookieJar;
-}
-
-/**
- * Save the cookies in the given `res` to
- * the agent's cookie jar for persistence.
- *
- * @param {Response} res
- * @api private
- */
-
-Agent.prototype.saveCookies = function(res){
- var cookies = res.headers['set-cookie'];
- if (cookies) this.jar.setCookies(cookies);
-};
-
-/**
- * Attach cookies when available to the given `req`.
- *
- * @param {Request} req
- * @api private
- */
-
-Agent.prototype.attachCookies = function(req){
- var url = parse(req.url);
- var access = CookieAccess(url.hostname, url.pathname, 'https:' == url.protocol);
- var cookies = this.jar.getCookies(access).toValueString();
- req.cookies = cookies;
-};
-
-// generate HTTP verb methods
-
-methods.forEach(function(method){
- var name = 'delete' == method ? 'del' : method;
-
- method = method.toUpperCase();
- Agent.prototype[name] = function(url, fn){
- var req = request(method, url);
- req.ca(this._ca);
-
- req.on('response', this.saveCookies.bind(this));
- req.on('redirect', this.saveCookies.bind(this));
- req.on('redirect', this.attachCookies.bind(this, req));
- this.attachCookies(req);
-
- fn && req.end(fn);
- return req;
- };
-});
diff --git a/lib/node/index.js b/lib/node/index.js
deleted file mode 100644
index 5da40a0be..000000000
--- a/lib/node/index.js
+++ /dev/null
@@ -1,1149 +0,0 @@
-
-/**
- * Module dependencies.
- */
-
-var debug = require('debug')('superagent');
-var formidable = require('formidable');
-var FormData = require('form-data');
-var Response = require('./response');
-var parse = require('url').parse;
-var format = require('url').format;
-var resolve = require('url').resolve;
-var methods = require('methods');
-var Stream = require('stream');
-var utils = require('./utils');
-var extend = require('extend');
-var Part = require('./part');
-var mime = require('mime');
-var https = require('https');
-var http = require('http');
-var fs = require('fs');
-var qs = require('qs');
-var zlib = require('zlib');
-var util = require('util');
-var pkg = require('../../package.json');
-
-/**
- * Expose the request function.
- */
-
-exports = module.exports = request;
-
-/**
- * Expose the agent function
- */
-
-exports.agent = require('./agent');
-
-/**
- * Expose `Part`.
- */
-
-exports.Part = Part;
-
-/**
- * Noop.
- */
-
-function noop(){};
-
-/**
- * Expose `Response`.
- */
-
-exports.Response = Response;
-
-/**
- * Define "form" mime type.
- */
-
-mime.define({
- 'application/x-www-form-urlencoded': ['form', 'urlencoded', 'form-data']
-});
-
-/**
- * Protocol map.
- */
-
-exports.protocols = {
- 'http:': http,
- 'https:': https
-};
-
-/**
- * Check if `obj` is an object.
- *
- * @param {Object} obj
- * @return {Boolean}
- * @api private
- */
-
-function isObject(obj) {
- return null != obj && 'object' == typeof obj;
-}
-
-/**
- * Default serialization map.
- *
- * superagent.serialize['application/xml'] = function(obj){
- * return 'generated xml here';
- * };
- *
- */
-
-exports.serialize = {
- 'application/x-www-form-urlencoded': qs.stringify,
- 'application/json': JSON.stringify
-};
-
-/**
- * Default parsers.
- *
- * superagent.parse['application/xml'] = function(res, fn){
- * fn(null, result);
- * };
- *
- */
-
-exports.parse = require('./parsers');
-
-/**
- * Initialize a new `Request` with the given `method` and `url`.
- *
- * @param {String} method
- * @param {String|Object} url
- * @api public
- */
-
-function Request(method, url) {
- Stream.call(this);
- var self = this;
- if ('string' != typeof url) url = format(url);
- this._agent = false;
- this._formData = null;
- this.method = method;
- this.url = url;
- this.header = {};
- this.writable = true;
- this._redirects = 0;
- this.redirects(5);
- this.cookies = '';
- this.qs = {};
- this.qsRaw = [];
- this._redirectList = [];
- this.on('end', this.clearTimeout.bind(this));
-}
-
-/**
- * Inherit from `Stream`.
- */
-
-util.inherits(Request, Stream);
-
-/**
- * Write the field `name` and `val` for "multipart/form-data"
- * request bodies.
- *
- * ``` js
- * request.post('http://localhost/upload')
- * .field('foo', 'bar')
- * .end(callback);
- * ```
- *
- * @param {String} name
- * @param {String|Buffer|fs.ReadStream} val
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.field = function(name, val){
- debug('field', name, val);
- if (!this._formData) this._formData = new FormData();
- this._formData.append(name, val);
- return this;
-};
-
-/**
- * Queue the given `file` as an attachment to the specified `field`,
- * with optional `filename`.
- *
- * ``` js
- * request.post('http://localhost/upload')
- * .attach(new Buffer('Hello world '), 'hello.html')
- * .end(callback);
- * ```
- *
- * A filename may also be used:
- *
- * ``` js
- * request.post('http://localhost/upload')
- * .attach('files', 'image.jpg')
- * .end(callback);
- * ```
- *
- * @param {String} field
- * @param {String|fs.ReadStream|Buffer} file
- * @param {String} filename
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.attach = function(field, file, filename){
- if (!this._formData) this._formData = new FormData();
- if ('string' == typeof file) {
- if (!filename) filename = file;
- debug('creating `fs.ReadStream` instance for file: %s', file);
- file = fs.createReadStream(file);
- }
- this._formData.append(field, file, { filename: filename });
- return this;
-};
-
-/**
- * Set the max redirects to `n`.
- *
- * @param {Number} n
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.redirects = function(n){
- debug('max redirects %s', n);
- this._maxRedirects = n;
- return this;
-};
-
-/**
- * Return a new `Part` for this request.
- *
- * @return {Part}
- * @api public
- * @deprecated pass a readable stream in to `Request#attach()` instead
- */
-
-Request.prototype.part = util.deprecate(function(){
- return new Part(this);
-}, '`Request#part()` is deprecated. ' +
- 'Pass a readable stream in to `Request#attach()` instead.');
-
-/**
- * Gets/sets the `Agent` to use for this HTTP request. The default (if this
- * function is not called) is to opt out of connection pooling (`agent: false`).
- *
- * @param {http.Agent} agent
- * @return {http.Agent}
- * @api public
- */
-
-Request.prototype.agent = function(agent){
- if (!arguments.length) return this._agent;
- this._agent = agent;
- return this;
-};
-
-/**
- * Set header `field` to `val`, or multiple fields with one object.
- *
- * Examples:
- *
- * req.get('/')
- * .set('Accept', 'application/json')
- * .set('X-API-Key', 'foobar')
- * .end(callback);
- *
- * req.get('/')
- * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' })
- * .end(callback);
- *
- * @param {String|Object} field
- * @param {String} val
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.set = function(field, val){
- if (isObject(field)) {
- for (var key in field) {
- this.set(key, field[key]);
- }
- return this;
- }
-
- debug('set %s "%s"', field, val);
- this.request().setHeader(field, val);
- return this;
-};
-
-/**
- * Remove header `field`.
- *
- * Example:
- *
- * req.get('/')
- * .unset('User-Agent')
- * .end(callback);
- *
- * @param {String} field
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.unset = function(field){
- debug('unset %s', field);
- this.request().removeHeader(field);
- return this;
-};
-
-/**
- * Get request header `field`.
- *
- * @param {String} field
- * @return {String}
- * @api public
- */
-
-Request.prototype.get = function(field){
- return this.request().getHeader(field);
-};
-
-/**
- * Set _Content-Type_ response header passed through `mime.lookup()`.
- *
- * Examples:
- *
- * request.post('/')
- * .type('xml')
- * .send(xmlstring)
- * .end(callback);
- *
- * request.post('/')
- * .type('json')
- * .send(jsonstring)
- * .end(callback);
- *
- * request.post('/')
- * .type('application/json')
- * .send(jsonstring)
- * .end(callback);
- *
- * @param {String} type
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.type = function(type){
- return this.set('Content-Type', ~type.indexOf('/')
- ? type
- : mime.lookup(type));
-};
-
-/**
- * Set _Accept_ response header passed through `mime.lookup()`.
- *
- * Examples:
- *
- * superagent.types.json = 'application/json';
- *
- * request.get('/agent')
- * .accept('json')
- * .end(callback);
- *
- * request.get('/agent')
- * .accept('application/json')
- * .end(callback);
- *
- * @param {String} accept
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.accept = function(type){
- return this.set('Accept', ~type.indexOf('/')
- ? type
- : mime.lookup(type));
-};
-
-/**
- * Add query-string `val`.
- *
- * Examples:
- *
- * request.get('/shoes')
- * .query('size=10')
- * .query({ color: 'blue' })
- *
- * @param {Object|String} val
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.query = function(val){
- if ('string' == typeof val) {
- this.qsRaw.push(val);
- return this;
- }
-
- extend(this.qs, val);
- return this;
-};
-
-/**
- * Send `data`, defaulting the `.type()` to "json" when
- * an object is given.
- *
- * Examples:
- *
- * // manual json
- * request.post('/user')
- * .type('json')
- * .send('{"name":"tj"}')
- * .end(callback)
- *
- * // auto json
- * request.post('/user')
- * .send({ name: 'tj' })
- * .end(callback)
- *
- * // manual x-www-form-urlencoded
- * request.post('/user')
- * .type('form')
- * .send('name=tj')
- * .end(callback)
- *
- * // auto x-www-form-urlencoded
- * request.post('/user')
- * .type('form')
- * .send({ name: 'tj' })
- * .end(callback)
- *
- * // string defaults to x-www-form-urlencoded
- * request.post('/user')
- * .send('name=tj')
- * .send('foo=bar')
- * .send('bar=baz')
- * .end(callback)
- *
- * @param {String|Object} data
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.send = function(data){
- var obj = isObject(data);
- var req = this.request();
- var type = req.getHeader('Content-Type');
-
- // merge
- if (obj && isObject(this._data)) {
- for (var key in data) {
- this._data[key] = data[key];
- }
- // string
- } else if ('string' == typeof data) {
- // default to x-www-form-urlencoded
- if (!type) this.type('form');
- type = req.getHeader('Content-Type');
-
- // concat &
- if ('application/x-www-form-urlencoded' == type) {
- this._data = this._data
- ? this._data + '&' + data
- : data;
- } else {
- this._data = (this._data || '') + data;
- }
- } else {
- this._data = data;
- }
-
- if (!obj) return this;
-
- // default to json
- if (!type) this.type('json');
- return this;
-};
-
-/**
- * Write raw `data` / `encoding` to the socket.
- *
- * @param {Buffer|String} data
- * @param {String} encoding
- * @return {Boolean}
- * @api public
- */
-
-Request.prototype.write = function(data, encoding){
- return this.request().write(data, encoding);
-};
-
-/**
- * Pipe the request body to `stream`.
- *
- * @param {Stream} stream
- * @param {Object} options
- * @return {Stream}
- * @api public
- */
-
-Request.prototype.pipe = function(stream, options){
- this.piped = true; // HACK...
- this.buffer(false);
- var self = this;
- this.end().req.on('response', function(res){
- // redirect
- var redirect = isRedirect(res.statusCode);
- if (redirect && self._redirects++ != self._maxRedirects) {
- return self.redirect(res).pipe(stream, options);
- }
-
- if (/^(deflate|gzip)$/.test(res.headers['content-encoding'])) {
- res.pipe(zlib.createUnzip()).pipe(stream, options);
- } else {
- res.pipe(stream, options);
- }
- res.on('end', function(){
- self.emit('end');
- });
- });
- return stream;
-};
-
-/**
- * Enable / disable buffering.
- *
- * @return {Boolean} [val]
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.buffer = function(val){
- this._buffer = false === val
- ? false
- : true;
- return this;
-};
-
-/**
- * Set timeout to `ms`.
- *
- * @param {Number} ms
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.timeout = function(ms){
- this._timeout = ms;
- return this;
-};
-
-/**
- * Clear previous timeout.
- *
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.clearTimeout = function(){
- debug('clear timeout %s %s', this.method, this.url);
- this._timeout = 0;
- clearTimeout(this._timer);
- return this;
-};
-
-/**
- * Abort and clear timeout.
- *
- * @api public
- */
-
-Request.prototype.abort = function(){
- debug('abort %s %s', this.method, this.url);
- this._aborted = true;
- this.clearTimeout();
- this.req.abort();
- this.emit('abort');
-};
-
-/**
- * Define the parser to be used for this response.
- *
- * @param {Function} fn
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.parse = function(fn){
- this._parser = fn;
- return this;
-};
-
-/**
- * Redirect to `url
- *
- * @param {IncomingMessage} res
- * @return {Request} for chaining
- * @api private
- */
-
-Request.prototype.redirect = function(res){
- var url = res.headers.location;
- if (!url) {
- return this.callback(new Error('No location header for redirect'), res);
- }
-
- debug('redirect %s -> %s', this.url, url);
-
- // location
- url = resolve(this.url, url);
-
- // ensure the response is being consumed
- // this is required for Node v0.10+
- res.resume();
-
- var headers = this.req._headers;
-
- // implementation of 302 following defacto standard
- if (res.statusCode == 301 || res.statusCode == 302){
- // strip Content-* related fields
- // in case of POST etc
- headers = utils.cleanHeader(this.req._headers);
-
- // force GET
- this.method = 'HEAD' == this.method
- ? 'HEAD'
- : 'GET';
-
- // clear data
- this._data = null;
- }
- // 303 is always GET
- if (res.statusCode == 303) {
- // strip Content-* related fields
- // in case of POST etc
- headers = utils.cleanHeader(this.req._headers);
-
- // force method
- this.method = 'GET';
-
- // clear data
- this._data = null;
- }
- // 307 preserves method
- // 308 preserves method
- delete headers.host;
-
- delete this.req;
-
- // redirect
- this.url = url;
- this._redirectList.push(url);
- this.emit('redirect', res);
- this.qs = {};
- this.set(headers);
- this.end(this._callback);
- return this;
-};
-
-/**
- * Set Authorization field value with `user` and `pass`.
- *
- * Examples:
- *
- * .auth('tobi', 'learnboost')
- * .auth('tobi:learnboost')
- * .auth('tobi')
- *
- * @param {String} user
- * @param {String} pass
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.auth = function(user, pass){
- if (1 === arguments.length) pass = '';
- if (!~user.indexOf(':')) user = user + ':';
- var str = new Buffer(user + pass).toString('base64');
- return this.set('Authorization', 'Basic ' + str);
-};
-
-/**
- * Set the certificate authority option for https request.
- *
- * @param {Buffer | Array} cert
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.ca = function(cert){
- this._ca = cert;
- return this;
-};
-
-/**
- * Allow for extension
- */
-
-Request.prototype.use = function(fn) {
- fn(this);
- return this;
-};
-
-/**
- * Return an http[s] request.
- *
- * @return {OutgoingMessage}
- * @api private
- */
-
-Request.prototype.request = function(){
- if (this.req) return this.req;
-
- var self = this;
- var options = {};
- var data = this._data;
- var url = this.url;
-
- // default to http://
- if (0 != url.indexOf('http')) url = 'http://' + url;
- url = parse(url, true);
-
- // options
- options.method = this.method;
- options.port = url.port;
- options.path = url.pathname;
- options.host = url.hostname;
- options.ca = this._ca;
- options.agent = this._agent;
-
- // initiate request
- var mod = exports.protocols[url.protocol];
-
- // request
- var req = this.req = mod.request(options);
- if ('HEAD' != options.method) req.setHeader('Accept-Encoding', 'gzip, deflate');
- this.protocol = url.protocol;
- this.host = url.host;
-
- // expose events
- req.on('drain', function(){ self.emit('drain'); });
-
- req.on('error', function(err){
- // flag abortion here for out timeouts
- // because node will emit a faux-error "socket hang up"
- // when request is aborted before a connection is made
- if (self._aborted) return;
- self.callback(err);
- });
-
- // auth
- if (url.auth) {
- var auth = url.auth.split(':');
- this.auth(auth[0], auth[1]);
- }
-
- // query
- this.query(url.query);
-
- // add cookies
- if (this.cookies) req.setHeader('Cookie', this.cookies);
-
- // set default UA
- req.setHeader('User-Agent', 'node-superagent/' + pkg.version);
-
- return req;
-};
-
-/**
- * Invoke the callback with `err` and `res`
- * and handle arity check.
- *
- * @param {Error} err
- * @param {Response} res
- * @api private
- */
-
-Request.prototype.callback = function(err, res){
- var fn = this._callback;
- this.clearTimeout();
- if (this.called) return console.warn('double callback!');
- this.called = true;
-
- if (err) {
- err.response = res;
- }
-
- // only emit error event if there is a listener
- // otherwise we assume the callback to `.end()` will get the error
- if (err && this.listeners('error').length > 0) this.emit('error', err);
-
- if (err) {
- return fn(err, res);
- }
-
- if (res && res.status >= 200 && res.status < 300) {
- return fn(err, res);
- }
-
- var msg = 'Unsuccessful HTTP response';
- if (res) {
- msg = http.STATUS_CODES[res.status] || msg;
- }
- var new_err = new Error(msg);
- new_err.original = err;
- new_err.response = res;
- new_err.status = (res) ? res.status : undefined;
-
- fn(err || new_err, res);
-};
-
-/**
- * Initiate request, invoking callback `fn(err, res)`
- * with an instanceof `Response`.
- *
- * @param {Function} fn
- * @return {Request} for chaining
- * @api public
- */
-
-Request.prototype.end = function(fn){
- var self = this;
- var data = this._data;
- var req = this.request();
- var buffer = this._buffer;
- var method = this.method;
- var timeout = this._timeout;
- debug('%s %s', this.method, this.url);
-
- // store callback
- this._callback = fn || noop;
-
- // querystring
- try {
- var querystring = qs.stringify(this.qs, { indices: false });
- querystring += ((querystring.length && this.qsRaw.length) ? '&' : '') + this.qsRaw.join('&');
- req.path += querystring.length
- ? (~req.path.indexOf('?') ? '&' : '?') + querystring
- : '';
- } catch (e) {
- return this.callback(e);
- }
-
- // timeout
- if (timeout && !this._timer) {
- debug('timeout %sms %s %s', timeout, this.method, this.url);
- this._timer = setTimeout(function(){
- var err = new Error('timeout of ' + timeout + 'ms exceeded');
- err.timeout = timeout;
- err.code = 'ECONNABORTED';
- self.abort();
- self.callback(err);
- }, timeout);
- }
-
- // body
- if ('HEAD' != method && !req._headerSent) {
- // serialize stuff
- if ('string' != typeof data) {
- var contentType = req.getHeader('Content-Type')
- // Parse out just the content type from the header (ignore the charset)
- if (contentType) contentType = contentType.split(';')[0]
- var serialize = exports.serialize[contentType];
- if (serialize) data = serialize(data);
- }
-
- // content-length
- if (data && !req.getHeader('Content-Length')) {
- this.set('Content-Length', Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data));
- }
- }
-
- // response
- req.on('response', function(res){
- debug('%s %s -> %s', self.method, self.url, res.statusCode);
- var max = self._maxRedirects;
- var mime = utils.type(res.headers['content-type'] || '') || 'text/plain';
- var len = res.headers['content-length'];
- var type = mime.split('/');
- var subtype = type[1];
- var type = type[0];
- var multipart = 'multipart' == type;
- var redirect = isRedirect(res.statusCode);
- var parser = self._parser;
-
- self.res = res;
-
- if ('HEAD' == self.method) {
- var response = new Response(self);
- self.response = response;
- response.redirects = self._redirectList;
- self.emit('response', response);
- self.callback(null, response);
- self.emit('end');
- return;
- }
-
- if (self.piped) {
- return;
- }
-
- // redirect
- if (redirect && self._redirects++ != max) {
- return self.redirect(res);
- }
-
- // zlib support
- if (/^(deflate|gzip)$/.test(res.headers['content-encoding'])) {
- utils.unzip(req, res);
- }
-
- // don't buffer multipart
- if (multipart) buffer = false;
-
- // TODO: make all parsers take callbacks
- if (!parser && multipart) {
- var form = new formidable.IncomingForm;
-
- form.parse(res, function(err, fields, files){
- if (err) return self.callback(err);
- var response = new Response(self);
- self.response = response;
- response.body = fields;
- response.files = files;
- response.redirects = self._redirectList;
- self.emit('end');
- self.callback(null, response);
- });
- return;
- }
-
- // check for images, one more special treatment
- if (!parser && isImage(mime)) {
- exports.parse.image(res, function(err, obj){
- if (err) return self.callback(err);
- var response = new Response(self);
- self.response = response;
- response.body = obj;
- response.redirects = self._redirectList;
- self.emit('end');
- self.callback(null, response);
- });
- return;
- }
-
- // by default only buffer text/*, json and messed up thing from hell
- if (null == buffer && isText(mime) || isJSON(mime)) buffer = true;
-
- // parser
- var parse = 'text' == type
- ? exports.parse.text
- : exports.parse[mime];
-
- // everyone wants their own white-labeled json
- if (isJSON(mime)) parse = exports.parse['application/json'];
-
- // buffered response
- if (buffer) parse = parse || exports.parse.text;
-
- // explicit parser
- if (parser) parse = parser;
-
- // parse
- if (parse) {
- try {
- parse(res, function(err, obj){
- if (err) self.callback(err);
- res.body = obj;
- });
- } catch (err) {
- self.callback(err);
- return;
- }
- }
-
- // unbuffered
- if (!buffer) {
- debug('unbuffered %s %s', self.method, self.url);
- self.res = res;
- var response = new Response(self);
- self.response = response;
- response.redirects = self._redirectList;
- self.emit('response', response);
- self.callback(null, response);
- if (multipart) return // allow multipart to handle end event
- res.on('end', function(){
- debug('end %s %s', self.method, self.url);
- self.emit('end');
- })
- return;
- }
-
- // end event
- self.res = res;
- res.on('end', function(){
- debug('end %s %s', self.method, self.url);
- // TODO: unless buffering emit earlier to stream
- var response = new Response(self);
- self.response = response;
- response.redirects = self._redirectList;
- self.emit('response', response);
- self.callback(null, response);
- self.emit('end');
- });
- });
-
- this.emit('request', this);
-
- // if a FormData instance got created, then we send that as the request body
- var formData = this._formData;
- if (formData) {
-
- // set headers
- var headers = formData.getHeaders();
- for (var i in headers) {
- debug('setting FormData header: "%s: %s"', i, headers[i]);
- req.setHeader(i, headers[i]);
- }
-
- // attempt to get "Content-Length" header
- formData.getLength(function(err, length) {
- // TODO: Add chunked encoding when no length (if err)
-
- debug('got FormData Content-Length: %s', length);
- if ('number' == typeof length) {
- req.setHeader('Content-Length', length);
- }
-
- formData.pipe(req);
- });
- } else {
- req.end(data);
- }
-
- return this;
-};
-
-/**
- * To json.
- *
- * @return {Object}
- * @api public
- */
-
-Request.prototype.toJSON = function(){
- return {
- method: this.method,
- url: this.url,
- data: this._data
- };
-};
-
-/**
- * Expose `Request`.
- */
-
-exports.Request = Request;
-
-/**
- * Issue a request:
- *
- * Examples:
- *
- * request('GET', '/users').end(callback)
- * request('/users').end(callback)
- * request('/users', callback)
- *
- * @param {String} method
- * @param {String|Function} url or callback
- * @return {Request}
- * @api public
- */
-
-function request(method, url) {
- // callback
- if ('function' == typeof url) {
- return new Request('GET', method).end(url);
- }
-
- // url first
- if (1 == arguments.length) {
- return new Request('GET', method);
- }
-
- return new Request(method, url);
-}
-
-// generate HTTP verb methods
-
-methods.forEach(function(method){
- var name = 'delete' == method ? 'del' : method;
- method = method.toUpperCase();
- request[name] = function(url, data, fn){
- var req = request(method, url);
- if ('function' == typeof data) fn = data, data = null;
- if (data) req.send(data);
- fn && req.end(fn);
- return req;
- };
-});
-
-/**
- * Check if `mime` is text and should be buffered.
- *
- * @param {String} mime
- * @return {Boolean}
- * @api public
- */
-
-function isText(mime) {
- var parts = mime.split('/');
- var type = parts[0];
- var subtype = parts[1];
-
- return 'text' == type
- || 'x-www-form-urlencoded' == subtype;
-}
-
-/**
- * Check if `mime` is image
- *
- * @param {String} mime
- * @return {Boolean}
- * @api public
- */
-
-function isImage(mime) {
- var parts = mime.split('/');
- var type = parts[0];
- var subtype = parts[1];
-
- return 'image' == type;
-}
-
-/**
- * Check if `mime` is json or has +json structured syntax suffix.
- *
- * @param {String} mime
- * @return {Boolean}
- * @api private
- */
-
-function isJSON(mime) {
- if (!mime) return false;
- var parts = mime.split('/');
- var type = parts[0];
- var subtype = parts[1];
- return subtype && subtype.match(/json/i);
-}
-
-/**
- * Check if we should follow the redirect `code`.
- *
- * @param {Number} code
- * @return {Boolean}
- * @api private
- */
-
-function isRedirect(code) {
- return ~[301, 302, 303, 305, 307, 308].indexOf(code);
-}
diff --git a/lib/node/parsers/image.js b/lib/node/parsers/image.js
deleted file mode 100644
index b3e0ebc4b..000000000
--- a/lib/node/parsers/image.js
+++ /dev/null
@@ -1,10 +0,0 @@
-module.exports = function(res, fn){
- var data = []; // Binary data needs binary storage
-
- res.on('data', function(chunk){
- data.push(chunk);
- });
- res.on('end', function () {
- fn(null, Buffer.concat(data));
- });
-};
\ No newline at end of file
diff --git a/lib/node/parsers/index.js b/lib/node/parsers/index.js
deleted file mode 100644
index 61a98cd29..000000000
--- a/lib/node/parsers/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-
-exports['application/x-www-form-urlencoded'] = require('./urlencoded');
-exports['application/json'] = require('./json');
-exports.text = require('./text');
-exports.image = require('./image');
diff --git a/lib/node/parsers/json.js b/lib/node/parsers/json.js
deleted file mode 100644
index aee28bb90..000000000
--- a/lib/node/parsers/json.js
+++ /dev/null
@@ -1,16 +0,0 @@
-
-module.exports = function parseJSON(res, fn){
- res.text = '';
- res.setEncoding('utf8');
- res.on('data', function(chunk){ res.text += chunk;});
- res.on('end', function(){
- try {
- var text = res.text && res.text.replace(/^\s*|\s*$/g, '');
- var body = text && JSON.parse(text);
- } catch (e) {
- var err = e;
- } finally {
- fn(err, body);
- }
- });
-};
diff --git a/lib/node/parsers/text.js b/lib/node/parsers/text.js
deleted file mode 100644
index 03575c698..000000000
--- a/lib/node/parsers/text.js
+++ /dev/null
@@ -1,7 +0,0 @@
-
-module.exports = function(res, fn){
- res.text = '';
- res.setEncoding('utf8');
- res.on('data', function(chunk){ res.text += chunk; });
- res.on('end', fn);
-};
\ No newline at end of file
diff --git a/lib/node/part.js b/lib/node/part.js
deleted file mode 100644
index 82ed56535..000000000
--- a/lib/node/part.js
+++ /dev/null
@@ -1,150 +0,0 @@
-
-/**
- * Module dependencies.
- */
-
-var util = require('util');
-var mime = require('mime');
-var FormData = require('form-data');
-var PassThrough = require('readable-stream/passthrough');
-
-/**
- * Initialize a new `Part` for the given `req`.
- *
- * @param {Request} req
- * @api public
- * @deprecated pass a readable stream in to `Request#attach()` instead
- */
-
-var Part = function (req) {
- PassThrough.call(this);
- this._req = req;
- this._attached = false;
- this._name = null;
- this._type = null;
- this._header = null;
- this._filename = null;
-
- this.once('pipe', this._attach.bind(this));
-};
-Part = util.deprecate(Part, 'The `Part()` constructor is deprecated. ' +
- 'Pass a readable stream in to `Request#attach()` instead.');
-
-/**
- * Inherit from `PassThrough`.
- */
-
-util.inherits(Part, PassThrough);
-
-/**
- * Expose `Part`.
- */
-
-module.exports = Part;
-
-/**
- * Set header `field` to `val`.
- *
- * @param {String} field
- * @param {String} val
- * @return {Part} for chaining
- * @api public
- */
-
-Part.prototype.set = function(field, val){
- //if (!this._header) this._header = {};
- //this._header[field] = val;
- //return this;
- throw new TypeError('setting custom form-data part headers is unsupported');
-};
-
-/**
- * Set _Content-Type_ response header passed through `mime.lookup()`.
- *
- * Examples:
- *
- * res.type('html');
- * res.type('.html');
- *
- * @param {String} type
- * @return {Part} for chaining
- * @api public
- */
-
-Part.prototype.type = function(type){
- var lookup = mime.lookup(type);
- this._type = lookup;
- //this.set('Content-Type', lookup);
- return this;
-};
-
-/**
- * Set the "name" portion for the _Content-Disposition_ header field.
- *
- * @param {String} name
- * @return {Part} for chaining
- * @api public
- */
-
-Part.prototype.name = function(name){
- this._name = name;
- return this;
-};
-
-/**
- * Set _Content-Disposition_ header field to _attachment_ with `filename`
- * and field `name`.
- *
- * @param {String} name
- * @param {String} filename
- * @return {Part} for chaining
- * @api public
- */
-
-Part.prototype.attachment = function(name, filename){
- this.name(name);
- if (filename) {
- this.type(filename);
- this._filename = filename;
- }
- return this;
-};
-
-/**
- * Calls `FormData#append()` on the Request instance's FormData object.
- *
- * Gets called implicitly upon the first `write()` call, or the "pipe" event.
- *
- * @api private
- */
-
-Part.prototype._attach = function(){
- if (this._attached) return;
- this._attached = true;
-
- if (!this._name) throw new Error('must call `Part#name()` first!');
-
- // add `this` Stream's readable side as a stream for this Part
- if (!this._req._formData) this._req._formData = new FormData();
- this._req._formData.append(this._name, this, {
- contentType: this._type,
- filename: this._filename
- });
-
- // restore PassThrough's default `write()` function now that we're setup
- this.write = PassThrough.prototype.write;
-};
-
-/**
- * Write `data` with `encoding`.
- *
- * @param {Buffer|String} data
- * @param {String} encoding
- * @return {Boolean}
- * @api public
- */
-
-Part.prototype.write = function(){
- this._attach();
- return this.write.apply(this, arguments);
-};
diff --git a/lib/node/response.js b/lib/node/response.js
deleted file mode 100644
index eb91494f4..000000000
--- a/lib/node/response.js
+++ /dev/null
@@ -1,220 +0,0 @@
-
-/**
- * Module dependencies.
- */
-
-var util = require('util');
-var utils = require('./utils');
-var Stream = require('stream');
-
-/**
- * Expose `Response`.
- */
-
-module.exports = Response;
-
-/**
- * Initialize a new `Response` with the given `xhr`.
- *
- * - set flags (.ok, .error, etc)
- * - parse header
- *
- * @param {Request} req
- * @param {Object} options
- * @constructor
- * @extends {Stream}
- * @implements {ReadableStream}
- * @api private
- */
-
-function Response(req, options) {
- Stream.call(this);
- options = options || {};
- var res = this.res = req.res;
- this.request = req;
- this.req = req.req;
- this.links = {};
- this.text = res.text;
- this.body = res.body !== undefined ? res.body : {};
- this.files = res.files || {};
- this.buffered = 'string' == typeof this.text;
- this.header = this.headers = res.headers;
- this.setStatusProperties(res.statusCode);
- this.setHeaderProperties(this.header);
- this.setEncoding = res.setEncoding.bind(res);
- res.on('data', this.emit.bind(this, 'data'));
- res.on('end', this.emit.bind(this, 'end'));
- res.on('close', this.emit.bind(this, 'close'));
- res.on('error', this.emit.bind(this, 'error'));
-}
-
-/**
- * Inherit from `Stream`.
- */
-
-util.inherits(Response, Stream);
-
-/**
- * Get case-insensitive `field` value.
- *
- * @param {String} field
- * @return {String}
- * @api public
- */
-
-Response.prototype.get = function(field){
- return this.header[field.toLowerCase()];
-};
-
-/**
- * Implements methods of a `ReadableStream`
- */
-
-Response.prototype.destroy = function(err){
- this.res.destroy(err);
-};
-
-/**
- * Pause.
- */
-
-Response.prototype.pause = function(){
- this.res.pause();
-};
-
-/**
- * Resume.
- */
-
-Response.prototype.resume = function(){
- this.res.resume();
-};
-
-/**
- * Return an `Error` representative of this response.
- *
- * @return {Error}
- * @api public
- */
-
-Response.prototype.toError = function(){
- var req = this.req;
- var method = req.method;
- var path = req.path;
-
- var msg = 'cannot ' + method + ' ' + path + ' (' + this.status + ')';
- var err = new Error(msg);
- err.status = this.status;
- err.text = this.text;
- err.method = method;
- err.path = path;
-
- return err;
-};
-
-/**
- * Set header related properties:
- *
- * - `.type` the content type without params
- *
- * A response of "Content-Type: text/plain; charset=utf-8"
- * will provide you with a `.type` of "text/plain".
- *
- * @param {Object} header
- * @api private
- */
-
-Response.prototype.setHeaderProperties = function(header){
- // TODO: moar!
- // TODO: make this a util
-
- // content-type
- var ct = this.header['content-type'] || '';
-
- // params
- var params = utils.params(ct);
- for (var key in params) this[key] = params[key];
-
- this.type = utils.type(ct);
-
- // links
- try {
- if (header.link) this.links = utils.parseLinks(header.link);
- } catch (err) {
- // ignore
- }
-};
-
-/**
- * Parse cookies from the header into an array.
- */
-
-function parseCookies(header) {
- return Array.isArray(header)
- ? header.map(Cookie.parse)
- : [Cookie.parse(header)];
-}
-
-/**
- * Set flags such as `.ok` based on `status`.
- *
- * For example a 2xx response will give you a `.ok` of __true__
- * whereas 5xx will be __false__ and `.error` will be __true__. The
- * `.clientError` and `.serverError` are also available to be more
- * specific, and `.statusType` is the class of error ranging from 1..5
- * sometimes useful for mapping respond colors etc.
- *
- * "sugar" properties are also defined for common cases. Currently providing:
- *
- * - .noContent
- * - .badRequest
- * - .unauthorized
- * - .notAcceptable
- * - .notFound
- *
- * @param {Number} status
- * @api private
- */
-
-Response.prototype.setStatusProperties = function(status){
- var type = status / 100 | 0;
-
- // status / class
- this.status = this.statusCode = status;
- this.statusType = type;
-
- // basics
- this.info = 1 == type;
- this.ok = 2 == type;
- this.redirect = 3 == type;
- this.clientError = 4 == type;
- this.serverError = 5 == type;
- this.error = (4 == type || 5 == type)
- ? this.toError()
- : false;
-
- // sugar
- this.accepted = 202 == status;
- this.noContent = 204 == status;
- this.badRequest = 400 == status;
- this.unauthorized = 401 == status;
- this.notAcceptable = 406 == status;
- this.forbidden = 403 == status;
- this.notFound = 404 == status;
-};
-
-/**
- * To json.
- *
- * @return {Object}
- * @api public
- */
-
-Response.prototype.toJSON = function(){
- return {
- req: this.request.toJSON(),
- header: this.header,
- status: this.status,
- text: this.text
- };
-};
diff --git a/lib/node/utils.js b/lib/node/utils.js
deleted file mode 100644
index 874211802..000000000
--- a/lib/node/utils.js
+++ /dev/null
@@ -1,140 +0,0 @@
-
-/**
- * Module dependencies.
- */
-
-var StringDecoder = require('string_decoder').StringDecoder;
-var Stream = require('stream');
-var zlib;
-
-/**
- * Require zlib module for Node 0.6+
- */
-
-try {
- zlib = require('zlib');
-} catch (e) { }
-
-/**
- * Return the mime type for the given `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-exports.type = function(str){
- return str.split(/ *; */).shift();
-};
-
-/**
- * Return header field parameters.
- *
- * @param {String} str
- * @return {Object}
- * @api private
- */
-
-exports.params = function(str){
- return str.split(/ *; */).reduce(function(obj, str){
- var parts = str.split(/ *= */);
- var key = parts.shift();
- var val = parts.shift();
-
- if (key && val) obj[key] = val;
- return obj;
- }, {});
-};
-
-/**
- * Parse Link header fields.
- *
- * @param {String} str
- * @return {Object}
- * @api private
- */
-
-exports.parseLinks = function(str){
- return str.split(/ *, */).reduce(function(obj, str){
- var parts = str.split(/ *; */);
- var url = parts[0].slice(1, -1);
- var rel = parts[1].split(/ *= */)[1].slice(1, -1);
- obj[rel] = url;
- return obj;
- }, {});
-};
-
-/**
- * Buffers response data events and re-emits when they're unzipped.
- *
- * @param {Request} req
- * @param {Response} res
- * @api private
- */
-
-exports.unzip = function(req, res){
- if (!zlib) return;
-
- var unzip = zlib.createUnzip();
- var stream = new Stream;
- var decoder;
-
- // make node responseOnEnd() happy
- stream.req = req;
-
- unzip.on('error', function(err){
- stream.emit('error', err);
- });
-
- // pipe to unzip
- res.pipe(unzip);
-
- // override `setEncoding` to capture encoding
- res.setEncoding = function(type){
- decoder = new StringDecoder(type);
- };
-
- // decode upon decompressing with captured encoding
- unzip.on('data', function(buf){
- if (decoder) {
- var str = decoder.write(buf);
- if (str.length) stream.emit('data', str);
- } else {
- stream.emit('data', buf);
- }
- });
-
- unzip.on('end', function(){
- stream.emit('end');
- });
-
- // override `on` to capture data listeners
- var _on = res.on;
- res.on = function(type, fn){
- if ('data' == type || 'end' == type) {
- stream.on(type, fn);
- } else if ('error' == type) {
- stream.on(type, fn);
- _on.call(res, type, fn);
- } else {
- _on.call(res, type, fn);
- }
- };
-};
-
-/**
- * Strip content related fields from `header`.
- *
- * @param {Object} header
- * @return {Object} header
- * @api private
- */
-
-exports.cleanHeader = function(header){
- delete header['content-type'];
- delete header['content-length'];
- delete header['transfer-encoding'];
- delete header['cookie'];
- delete header['host'];
- return header;
-};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..30e960fba
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,23485 @@
+{
+ "name": "superagent",
+ "version": "10.3.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "superagent",
+ "version": "10.3.0",
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "^1.3.1",
+ "cookiejar": "^2.1.4",
+ "debug": "^4.3.7",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.5",
+ "formidable": "^3.5.4",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.14.1"
+ },
+ "devDependencies": {
+ "@babel/cli": "^7.20.7",
+ "@babel/core": "^7.20.12",
+ "@babel/plugin-transform-runtime": "^7.19.6",
+ "@babel/preset-env": "^7.20.2",
+ "@babel/runtime": "^7.20.13",
+ "@commitlint/cli": "^19.8.1",
+ "@commitlint/config-conventional": "^19.8.1",
+ "babelify": "^10.0.0",
+ "Base64": "^1.1.0",
+ "basic-auth-connect": "^1.0.0",
+ "body-parser": "^1.20.4",
+ "browserify": "^17.0.0",
+ "cookie-parser": "^1.4.7",
+ "cross-env": "^7.0.3",
+ "eslint": "^8.32.0",
+ "eslint-config-xo-lass": "2",
+ "eslint-plugin-compat": "4.0.2",
+ "eslint-plugin-node": "^11.1.0",
+ "express": "^4.18.3",
+ "express-session": "^1.17.3",
+ "fixpack": "^4.0.0",
+ "get-port": "4.2.0",
+ "husky": "^9.1.7",
+ "lint-staged": "^15.5.2",
+ "marked": "^4.2.12",
+ "mocha": "^6.2.3",
+ "multer": "1.4.5-lts.1",
+ "nyc": "^15.1.0",
+ "remark-cli": "^11.0.0",
+ "remark-preset-github": "4.0.4",
+ "rimraf": "3",
+ "should": "^13.2.3",
+ "should-http": "^0.1.1",
+ "tinyify": "3.0.0",
+ "xo": "^0.53.1",
+ "zuul": "^3.12.0"
+ },
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
+ "node_modules/@babel/cli": {
+ "version": "7.28.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "commander": "^6.2.0",
+ "convert-source-map": "^2.0.0",
+ "fs-readdir-recursive": "^1.1.0",
+ "glob": "^7.2.0",
+ "make-dir": "^2.1.0",
+ "slash": "^2.0.0"
+ },
+ "bin": {
+ "babel": "bin/babel.js",
+ "babel-external-helpers": "bin/babel-external-helpers.js"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "optionalDependencies": {
+ "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
+ "chokidar": "^3.6.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.27.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/traverse": "^7.28.5",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "regexpu-core": "^6.3.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.6.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "debug": "^4.4.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.22.10"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-wrap-function": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.27.1",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.28.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.3",
+ "@babel/types": "^7.28.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.28.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-generator-functions": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-remap-async-to-generator": "^7.27.1",
+ "@babel/traverse": "^7.28.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-remap-async-to-generator": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-properties": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-static-block": {
+ "version": "7.28.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.3",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.28.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1",
+ "@babel/traverse": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/template": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-explicit-resource-management": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-transform-destructuring": "^7.28.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-export-namespace-from": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-json-strings": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-numeric-separator": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-rest-spread": {
+ "version": "7.28.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-transform-destructuring": "^7.28.0",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/traverse": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-chaining": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.27.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-methods": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-property-in-object": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.28.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-runtime": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "babel-plugin-polyfill-corejs2": "^0.4.14",
+ "babel-plugin-polyfill-corejs3": "^0.13.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.5",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-import-assertions": "^7.27.1",
+ "@babel/plugin-syntax-import-attributes": "^7.27.1",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.27.1",
+ "@babel/plugin-transform-async-generator-functions": "^7.28.0",
+ "@babel/plugin-transform-async-to-generator": "^7.27.1",
+ "@babel/plugin-transform-block-scoped-functions": "^7.27.1",
+ "@babel/plugin-transform-block-scoping": "^7.28.5",
+ "@babel/plugin-transform-class-properties": "^7.27.1",
+ "@babel/plugin-transform-class-static-block": "^7.28.3",
+ "@babel/plugin-transform-classes": "^7.28.4",
+ "@babel/plugin-transform-computed-properties": "^7.27.1",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
+ "@babel/plugin-transform-dotall-regex": "^7.27.1",
+ "@babel/plugin-transform-duplicate-keys": "^7.27.1",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1",
+ "@babel/plugin-transform-dynamic-import": "^7.27.1",
+ "@babel/plugin-transform-explicit-resource-management": "^7.28.0",
+ "@babel/plugin-transform-exponentiation-operator": "^7.28.5",
+ "@babel/plugin-transform-export-namespace-from": "^7.27.1",
+ "@babel/plugin-transform-for-of": "^7.27.1",
+ "@babel/plugin-transform-function-name": "^7.27.1",
+ "@babel/plugin-transform-json-strings": "^7.27.1",
+ "@babel/plugin-transform-literals": "^7.27.1",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.28.5",
+ "@babel/plugin-transform-member-expression-literals": "^7.27.1",
+ "@babel/plugin-transform-modules-amd": "^7.27.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.27.1",
+ "@babel/plugin-transform-modules-systemjs": "^7.28.5",
+ "@babel/plugin-transform-modules-umd": "^7.27.1",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1",
+ "@babel/plugin-transform-new-target": "^7.27.1",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
+ "@babel/plugin-transform-numeric-separator": "^7.27.1",
+ "@babel/plugin-transform-object-rest-spread": "^7.28.4",
+ "@babel/plugin-transform-object-super": "^7.27.1",
+ "@babel/plugin-transform-optional-catch-binding": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.28.5",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/plugin-transform-private-methods": "^7.27.1",
+ "@babel/plugin-transform-private-property-in-object": "^7.27.1",
+ "@babel/plugin-transform-property-literals": "^7.27.1",
+ "@babel/plugin-transform-regenerator": "^7.28.4",
+ "@babel/plugin-transform-regexp-modifiers": "^7.27.1",
+ "@babel/plugin-transform-reserved-words": "^7.27.1",
+ "@babel/plugin-transform-shorthand-properties": "^7.27.1",
+ "@babel/plugin-transform-spread": "^7.27.1",
+ "@babel/plugin-transform-sticky-regex": "^7.27.1",
+ "@babel/plugin-transform-template-literals": "^7.27.1",
+ "@babel/plugin-transform-typeof-symbol": "^7.27.1",
+ "@babel/plugin-transform-unicode-escapes": "^7.27.1",
+ "@babel/plugin-transform-unicode-property-regex": "^7.27.1",
+ "@babel/plugin-transform-unicode-regex": "^7.27.1",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.27.1",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.14",
+ "babel-plugin-polyfill-corejs3": "^0.13.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.5",
+ "core-js-compat": "^3.43.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@commitlint/cli": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/format": "^19.8.1",
+ "@commitlint/lint": "^19.8.1",
+ "@commitlint/load": "^19.8.1",
+ "@commitlint/read": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "tinyexec": "^1.0.0",
+ "yargs": "^17.0.0"
+ },
+ "bin": {
+ "commitlint": "cli.js"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-conventional": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "conventional-changelog-conventionalcommits": "^7.0.2"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-validator": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "ajv": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/ensure": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "lodash.camelcase": "^4.3.0",
+ "lodash.kebabcase": "^4.1.1",
+ "lodash.snakecase": "^4.1.1",
+ "lodash.startcase": "^4.4.0",
+ "lodash.upperfirst": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/execute-rule": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/format": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "chalk": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored/node_modules/semver": {
+ "version": "7.7.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@commitlint/lint": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/is-ignored": "^19.8.1",
+ "@commitlint/parse": "^19.8.1",
+ "@commitlint/rules": "^19.8.1",
+ "@commitlint/types": "^19.8.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/load": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^19.8.1",
+ "@commitlint/execute-rule": "^19.8.1",
+ "@commitlint/resolve-extends": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "chalk": "^5.3.0",
+ "cosmiconfig": "^9.0.0",
+ "cosmiconfig-typescript-loader": "^6.1.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.merge": "^4.6.2",
+ "lodash.uniq": "^4.5.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/message": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/parse": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "conventional-changelog-angular": "^7.0.0",
+ "conventional-commits-parser": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/read": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/top-level": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "git-raw-commits": "^4.0.0",
+ "minimist": "^1.2.8",
+ "tinyexec": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/resolve-extends": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "global-directory": "^4.0.1",
+ "import-meta-resolve": "^4.0.0",
+ "lodash.mergewith": "^4.6.2",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/rules": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/ensure": "^19.8.1",
+ "@commitlint/message": "^19.8.1",
+ "@commitlint/to-lines": "^19.8.1",
+ "@commitlint/types": "^19.8.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/to-lines": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/top-level": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/types": {
+ "version": "19.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/conventional-commits-parser": "^5.0.0",
+ "chalk": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@goto-bus-stop/common-shake": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn-walk": "^7.0.0",
+ "debug": "^3.2.6",
+ "escope": "^3.6.0"
+ }
+ },
+ "node_modules/@goto-bus-stop/common-shake/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/@goto-bus-stop/envify": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn-node": "^2.0.1",
+ "dash-ast": "^2.0.1",
+ "multisplice": "^1.0.0",
+ "through2": "^2.0.5"
+ },
+ "bin": {
+ "envify": "bin/envify"
+ }
+ },
+ "node_modules/@goto-bus-stop/envify/node_modules/acorn": {
+ "version": "7.4.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/@goto-bus-stop/envify/node_modules/acorn-node": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "acorn": "^7.0.0",
+ "acorn-walk": "^7.0.0",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/@goto-bus-stop/envify/node_modules/dash-ast": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.13.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.3",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/esprima": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
+ "version": "3.14.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.11",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mdn/browser-compat-data": {
+ "version": "4.2.1",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/@nicolo-ribaudo/chokidar-2": {
+ "version": "2.1.8-no-fsevents.3",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@npmcli/config": {
+ "version": "6.4.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/map-workspaces": "^3.0.2",
+ "ci-info": "^4.0.0",
+ "ini": "^4.1.0",
+ "nopt": "^7.0.0",
+ "proc-log": "^3.0.0",
+ "read-package-json-fast": "^3.0.2",
+ "semver": "^7.3.5",
+ "walk-up-path": "^3.0.1"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@npmcli/config/node_modules/semver": {
+ "version": "7.7.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@npmcli/map-workspaces": {
+ "version": "3.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/name-from-folder": "^2.0.0",
+ "glob": "^10.2.2",
+ "minimatch": "^9.0.0",
+ "read-package-json-fast": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@npmcli/map-workspaces/node_modules/foreground-child": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@npmcli/map-workspaces/node_modules/glob": {
+ "version": "10.5.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@npmcli/map-workspaces/node_modules/minimatch": {
+ "version": "9.0.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@npmcli/name-from-folder": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@paralleldrive/cuid2": {
+ "version": "2.3.1",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "^1.1.5"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/concat-stream": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/conventional-commits-parser": {
+ "version": "5.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/eslint": {
+ "version": "7.29.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.7",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/is-empty": {
+ "version": "1.2.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/mdast": {
+ "version": "3.0.15",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2"
+ }
+ },
+ "node_modules/@types/minimist": {
+ "version": "1.2.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "25.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/normalize-package-data": {
+ "version": "2.4.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/supports-color": {
+ "version": "8.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/text-table": {
+ "version": "0.2.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/unist": {
+ "version": "2.0.11",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.13.2",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.13.2",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.13.2",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.13.2",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/wasm-gen": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.13.2",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.13.2",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.13.2",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/helper-wasm-section": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-opt": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1",
+ "@webassemblyjs/wast-printer": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "peer": true
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true
+ },
+ "node_modules/abbrev": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-import-phases": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "peerDependencies": {
+ "acorn": "^8.14.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-node": {
+ "version": "1.8.2",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "acorn": "^7.0.0",
+ "acorn-walk": "^7.0.0",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/acorn-node/node_modules/acorn": {
+ "version": "7.4.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/adm-zip": {
+ "version": "0.4.16",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.3.0"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/alce": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esprima": "^1.2.0",
+ "estraverse": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/alce/node_modules/estraverse": {
+ "version": "1.9.3",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/amdefine": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "BSD-3-Clause OR MIT",
+ "engines": {
+ "node": ">=0.4.2"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "3.2.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/append-field": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/append-transform": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "default-require-extensions": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/archiver": {
+ "version": "0.7.1",
+ "dev": true,
+ "dependencies": {
+ "file-utils": "~0.1.5",
+ "lazystream": "~0.1.0",
+ "lodash": "~2.4.1",
+ "readable-stream": "~1.0.24",
+ "zip-stream": "~0.2.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/archiver/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/archiver/node_modules/lodash": {
+ "version": "2.4.2",
+ "dev": true,
+ "engines": [
+ "node",
+ "rhino"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/archiver/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/archiver/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/archy": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/arr-diff": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arr-flatten": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-flatten": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-union": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-filter": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-from": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-ify": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-iterate": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/array-map": {
+ "version": "0.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-reduce": {
+ "version": "0.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-uniq": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-unique": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.reduce": {
+ "version": "1.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "is-string": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arrify": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "license": "MIT"
+ },
+ "node_modules/asn1": {
+ "version": "0.1.11",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.9"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "4.10.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/asn1.js/node_modules/bn.js": {
+ "version": "4.12.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/assert": {
+ "version": "1.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object.assign": "^4.1.4",
+ "util": "^0.10.4"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "0.1.5",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/assert/node_modules/inherits": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/assert/node_modules/util": {
+ "version": "0.10.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/assign-symbols": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ast-metadata-inferer": {
+ "version": "0.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@mdn/browser-compat-data": "^3.3.14"
+ }
+ },
+ "node_modules/ast-metadata-inferer/node_modules/@mdn/browser-compat-data": {
+ "version": "3.3.14",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/async": {
+ "version": "0.2.10",
+ "dev": true
+ },
+ "node_modules/async-each": {
+ "version": "1.0.6",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "license": "MIT"
+ },
+ "node_modules/asyncreduce": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "runnel": "~0.5.0"
+ }
+ },
+ "node_modules/atob": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "(MIT OR Apache-2.0)",
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/author-regex": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.5.0",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.4.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.7",
+ "@babel/helper-define-polyfill-provider": "^0.6.5",
+ "semver": "^6.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.13.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.5",
+ "core-js-compat": "^3.43.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.6.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.5"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babelify": {
+ "version": "10.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base": {
+ "version": "0.11.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/base/node_modules/define-property": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/base/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/Base64": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "(Apache-2.0 OR WTFPL)"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.11",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/basic-auth-connect": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tsscmp": "^1.0.6"
+ }
+ },
+ "node_modules/batch": {
+ "version": "0.5.0",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "0.9.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "~1.0.26"
+ }
+ },
+ "node_modules/bl/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/bl/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.14.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/boom": {
+ "version": "0.4.2",
+ "dev": true,
+ "dependencies": {
+ "hoek": "0.9.x"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/browser-pack": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "combine-source-map": "~0.8.0",
+ "defined": "^1.0.0",
+ "JSONStream": "^1.0.3",
+ "safe-buffer": "^5.1.1",
+ "through2": "^2.0.0",
+ "umd": "^3.0.0"
+ },
+ "bin": {
+ "browser-pack": "bin/cmd.js"
+ }
+ },
+ "node_modules/browser-pack-flat": {
+ "version": "3.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "combine-source-map": "^0.8.0",
+ "convert-source-map": "^1.5.1",
+ "count-lines": "^0.1.2",
+ "dedent": "^0.7.0",
+ "estree-is-member-expression": "^1.0.0",
+ "estree-is-require": "^1.0.0",
+ "esutils": "^2.0.2",
+ "JSONStream": "^1.3.2",
+ "path-parse": "^1.0.5",
+ "scope-analyzer": "^2.0.0",
+ "stream-combiner": "^0.2.2",
+ "through2": "^3.0.1",
+ "transform-ast": "^2.4.2",
+ "umd": "^3.0.3",
+ "wrap-comment": "^1.0.0"
+ },
+ "bin": {
+ "browser-pack-flat": "cli.js"
+ }
+ },
+ "node_modules/browser-pack-flat/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/browser-pack-flat/node_modules/through2": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "2 || 3"
+ }
+ },
+ "node_modules/browser-process-hrtime": {
+ "version": "0.1.3",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/browser-resolve": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve": "^1.17.0"
+ }
+ },
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/browser-unpack": {
+ "version": "1.4.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn-node": "^1.5.2",
+ "concat-stream": "^1.5.0",
+ "minimist": "^1.1.1"
+ },
+ "bin": {
+ "browser-unpack": "bin/cmd.js"
+ }
+ },
+ "node_modules/browserify": {
+ "version": "17.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert": "^1.4.0",
+ "browser-pack": "^6.0.1",
+ "browser-resolve": "^2.0.0",
+ "browserify-zlib": "~0.2.0",
+ "buffer": "~5.2.1",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "^1.6.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "~1.0.0",
+ "crypto-browserify": "^3.0.0",
+ "defined": "^1.0.0",
+ "deps-sort": "^2.0.1",
+ "domain-browser": "^1.2.0",
+ "duplexer2": "~0.1.2",
+ "events": "^3.0.0",
+ "glob": "^7.1.0",
+ "hasown": "^2.0.0",
+ "htmlescape": "^1.1.0",
+ "https-browserify": "^1.0.0",
+ "inherits": "~2.0.1",
+ "insert-module-globals": "^7.2.1",
+ "JSONStream": "^1.0.3",
+ "labeled-stream-splicer": "^2.0.0",
+ "mkdirp-classic": "^0.5.2",
+ "module-deps": "^6.2.3",
+ "os-browserify": "~0.3.0",
+ "parents": "^1.0.1",
+ "path-browserify": "^1.0.0",
+ "process": "~0.11.0",
+ "punycode": "^1.3.2",
+ "querystring-es3": "~0.2.0",
+ "read-only-stream": "^2.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.1.4",
+ "shasum-object": "^1.0.0",
+ "shell-quote": "^1.6.1",
+ "stream-browserify": "^3.0.0",
+ "stream-http": "^3.0.0",
+ "string_decoder": "^1.1.1",
+ "subarg": "^1.0.0",
+ "syntax-error": "^1.1.1",
+ "through2": "^2.0.0",
+ "timers-browserify": "^1.0.1",
+ "tty-browserify": "0.0.1",
+ "url": "~0.11.0",
+ "util": "~0.12.0",
+ "vm-browserify": "^1.0.0",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "browserify": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/browserify-cipher": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "node_modules/browserify-des": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/browserify-istanbul": {
+ "version": "0.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "istanbul": "^0.2.8",
+ "minimatch": "^0.2.14",
+ "through": "^2.3.4"
+ }
+ },
+ "node_modules/browserify-istanbul/node_modules/lru-cache": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/browserify-istanbul/node_modules/minimatch": {
+ "version": "0.2.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/browserify-rsa": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^5.2.1",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/browserify-sign": {
+ "version": "4.2.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "bn.js": "^5.2.2",
+ "browserify-rsa": "^4.1.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.6.1",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.9",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/builtin-modules": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/builtin-status-codes": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/builtins": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.0.0"
+ }
+ },
+ "node_modules/builtins/node_modules/semver": {
+ "version": "7.7.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/bundle-collapser": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browser-pack": "^6.0.2",
+ "browser-unpack": "^1.1.0",
+ "concat-stream": "^1.5.0",
+ "falafel": "^2.1.0",
+ "minimist": "^1.1.1",
+ "through2": "^2.0.0"
+ },
+ "bin": {
+ "bundle-collapser": "bin/cmd.js"
+ }
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "dev": true,
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cache-base": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/cache-base/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/cached-path-relative": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/caching-transform": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasha": "^5.0.0",
+ "make-dir": "^3.0.0",
+ "package-hash": "^4.0.0",
+ "write-file-atomic": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/caching-transform/node_modules/make-dir": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-matcher": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-equal": "^1.0.0",
+ "espurify": "^2.0.0",
+ "estraverse": "^4.0.0"
+ }
+ },
+ "node_modules/call-matcher/node_modules/estraverse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase-keys": {
+ "version": "8.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camelcase": "^7.0.0",
+ "map-obj": "^4.3.0",
+ "quick-lru": "^6.1.1",
+ "type-fest": "^2.13.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/camelcase-keys/node_modules/camelcase": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/camelcase-keys/node_modules/type-fest": {
+ "version": "2.19.0",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001762",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/caseless": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "BSD"
+ },
+ "node_modules/ccount": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "5.6.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/char-split": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "zlib",
+ "dependencies": {
+ "through": "2.3.4"
+ }
+ },
+ "node_modules/char-split/node_modules/through": {
+ "version": "2.3.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.3.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cipher-base": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/class-utils": {
+ "version": "0.3.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/define-property": {
+ "version": "0.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-descriptor": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.1",
+ "is-data-descriptor": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/class-utils/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/clean-regexp": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/clean-regexp/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/co": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/collapse-white-space": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/collection-visit": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colors": {
+ "version": "0.6.2",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/combine-source-map": {
+ "version": "0.8.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "convert-source-map": "~1.1.0",
+ "inline-source-map": "~0.6.0",
+ "lodash.memoize": "~3.0.3",
+ "source-map": "~0.5.3"
+ }
+ },
+ "node_modules/combine-source-map/node_modules/convert-source-map": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "6.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/common-path-prefix": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/common-shakeify": {
+ "version": "0.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@goto-bus-stop/common-shake": "^2.2.0",
+ "convert-source-map": "^1.5.1",
+ "through2": "^2.0.3",
+ "transform-ast": "^2.4.3",
+ "wrap-comment": "^1.0.1"
+ }
+ },
+ "node_modules/common-shakeify/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/compare-func": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-ify": "^1.0.0",
+ "dot-prop": "^5.1.0"
+ }
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.1",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/compress-commons": {
+ "version": "0.2.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.1",
+ "crc32-stream": "~0.3.1",
+ "node-int64": "~0.3.0",
+ "readable-stream": "~1.0.26"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compress-commons/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/compress-commons/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/compress-commons/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.2.9",
+ "bytes": "2.1.0",
+ "compressible": "~2.0.3",
+ "debug": "~2.2.0",
+ "on-headers": "~1.0.0",
+ "vary": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compression/node_modules/accepts": {
+ "version": "1.2.13",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.6",
+ "negotiator": "0.5.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression/node_modules/bytes": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "0.7.1"
+ }
+ },
+ "node_modules/compression/node_modules/ms": {
+ "version": "0.7.1",
+ "dev": true
+ },
+ "node_modules/compression/node_modules/negotiator": {
+ "version": "0.5.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression/node_modules/on-headers": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/compression/node_modules/vary": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "dev": true,
+ "engines": [
+ "node >= 0.8"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/confusing-browser-globals": {
+ "version": "1.0.11",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/connect": {
+ "version": "2.12.0",
+ "dev": true,
+ "dependencies": {
+ "batch": "0.5.0",
+ "buffer-crc32": "0.2.1",
+ "bytes": "0.2.1",
+ "cookie": "0.1.0",
+ "cookie-signature": "1.0.1",
+ "debug": ">= 0.7.3 < 1",
+ "fresh": "0.2.0",
+ "methods": "0.1.0",
+ "multiparty": "2.2.0",
+ "negotiator": "0.3.0",
+ "pause": "0.0.1",
+ "qs": "0.6.6",
+ "raw-body": "1.1.2",
+ "send": "0.1.4",
+ "uid2": "0.0.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/connect/node_modules/buffer-crc32": {
+ "version": "0.2.1",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/connect/node_modules/bytes": {
+ "version": "0.2.1",
+ "dev": true
+ },
+ "node_modules/connect/node_modules/cookie": {
+ "version": "0.1.0",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/connect/node_modules/cookie-signature": {
+ "version": "1.0.1",
+ "dev": true
+ },
+ "node_modules/connect/node_modules/debug": {
+ "version": "0.8.1",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/connect/node_modules/fresh": {
+ "version": "0.2.0",
+ "dev": true
+ },
+ "node_modules/connect/node_modules/methods": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/connect/node_modules/mime": {
+ "version": "1.2.11",
+ "dev": true
+ },
+ "node_modules/connect/node_modules/negotiator": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/connect/node_modules/qs": {
+ "version": "0.6.6",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/connect/node_modules/range-parser": {
+ "version": "0.0.4",
+ "dev": true
+ },
+ "node_modules/connect/node_modules/raw-body": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~0.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/connect/node_modules/send": {
+ "version": "0.1.4",
+ "dev": true,
+ "dependencies": {
+ "debug": "*",
+ "fresh": "0.2.0",
+ "mime": "~1.2.9",
+ "range-parser": "0.0.4"
+ }
+ },
+ "node_modules/console-browserify": {
+ "version": "1.2.0",
+ "dev": true
+ },
+ "node_modules/constants-browserify": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/conventional-changelog-angular": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/conventional-changelog-conventionalcommits": {
+ "version": "7.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/conventional-commits-parser": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-text-path": "^2.0.0",
+ "JSONStream": "^1.3.5",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "conventional-commits-parser": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-parser": {
+ "version": "1.4.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "0.7.2",
+ "cookie-signature": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookiejar": {
+ "version": "2.1.4",
+ "license": "MIT"
+ },
+ "node_modules/copy-descriptor": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/core-js": {
+ "version": "3.47.0",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.47.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cosmiconfig": {
+ "version": "9.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cosmiconfig-typescript-loader": {
+ "version": "6.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jiti": "^2.6.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "cosmiconfig": ">=9",
+ "typescript": ">=5"
+ }
+ },
+ "node_modules/count-lines": {
+ "version": "0.1.2",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "0.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.1",
+ "readable-stream": "~1.0.24"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/crc32-stream/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/crc32-stream/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/crc32-stream/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-ecdh": {
+ "version": "4.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.5.3"
+ }
+ },
+ "node_modules/create-ecdh/node_modules/bn.js": {
+ "version": "4.12.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-hash": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/create-hmac": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cryptiles": {
+ "version": "0.2.2",
+ "dev": true,
+ "dependencies": {
+ "boom": "0.4.x"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/crypto-browserify": {
+ "version": "3.12.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserify-cipher": "^1.0.1",
+ "browserify-sign": "^4.2.3",
+ "create-ecdh": "^4.0.4",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "diffie-hellman": "^5.0.3",
+ "hash-base": "~3.0.4",
+ "inherits": "^2.0.4",
+ "pbkdf2": "^3.1.2",
+ "public-encrypt": "^4.0.3",
+ "randombytes": "^2.1.0",
+ "randomfill": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ctype": {
+ "version": "0.5.3",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/d": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "es5-ext": "^0.10.64",
+ "type": "^2.7.2"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/dargs": {
+ "version": "8.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dash-ast": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decamelize-keys": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "decamelize": "^1.1.0",
+ "map-obj": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decamelize-keys/node_modules/map-obj": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/decode-uri-component": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/dedent": {
+ "version": "0.7.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deep-equal": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arguments": "^1.1.1",
+ "is-date-object": "^1.0.5",
+ "is-regex": "^1.1.4",
+ "object-is": "^1.1.5",
+ "object-keys": "^1.1.1",
+ "regexp.prototype.flags": "^1.5.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/default-require-extensions": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "strip-bom": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-property": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/define-property/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/defined": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/deps-sort": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "JSONStream": "^1.0.3",
+ "shasum-object": "^1.0.0",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0"
+ },
+ "bin": {
+ "deps-sort": "bin/cmd.js"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/des.js": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-indent": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detect-newline": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detective": {
+ "version": "5.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn-node": "^1.8.2",
+ "defined": "^1.0.0",
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "detective": "bin/detective.js"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/dezalgo": {
+ "version": "1.0.4",
+ "license": "ISC",
+ "dependencies": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/diff": {
+ "version": "3.5.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/diffie-hellman": {
+ "version": "5.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "node_modules/diffie-hellman/node_modules/bn.js": {
+ "version": "4.12.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/domain-browser": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4",
+ "npm": ">=1.2"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/duplexer2": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/duplexify": {
+ "version": "4.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.2"
+ }
+ },
+ "node_modules/duplexify/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/elliptic": {
+ "version": "6.6.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emitter-component": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/enhance-visitors": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.13.1"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "0.9.1",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.2.0",
+ "tapable": "^0.1.8"
+ },
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/env-editor": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/error-stack-parser": {
+ "version": "1.3.6",
+ "dev": true,
+ "license": "Unlicense",
+ "dependencies": {
+ "stackframe": "^0.3.1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es5-ext": {
+ "version": "0.10.64",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "es6-iterator": "^2.0.3",
+ "es6-symbol": "^3.1.3",
+ "esniff": "^2.0.1",
+ "next-tick": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/es6-error": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es6-iterator": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "node_modules/es6-map": {
+ "version": "0.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "d": "1",
+ "es5-ext": "~0.10.14",
+ "es6-iterator": "~2.0.1",
+ "es6-set": "~0.1.5",
+ "es6-symbol": "~3.1.1",
+ "event-emitter": "~0.3.5"
+ }
+ },
+ "node_modules/es6-set": {
+ "version": "0.1.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d": "^1.0.1",
+ "es5-ext": "^0.10.62",
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "^3.1.3",
+ "event-emitter": "^0.3.5",
+ "type": "^2.7.2"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/es6-symbol": {
+ "version": "3.1.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d": "^1.0.2",
+ "ext": "^1.7.0"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/es6-weak-map": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d": "1",
+ "es5-ext": "^0.10.46",
+ "es6-iterator": "^2.0.3",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "1.14.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/escodegen/node_modules/esprima": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/escodegen/node_modules/estraverse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/levn": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/optionator": {
+ "version": "0.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/type-check": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escope": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "es6-map": "^0.1.3",
+ "es6-weak-map": "^2.0.1",
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/escope/node_modules/estraverse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "8.10.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-config-xo": {
+ "version": "0.43.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "confusing-browser-globals": "1.0.11"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.27.0"
+ }
+ },
+ "node_modules/eslint-config-xo-lass": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/eslint-formatter-pretty": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint": "^7.2.13",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.0",
+ "eslint-rule-docs": "^1.1.5",
+ "log-symbols": "^4.0.0",
+ "plur": "^4.0.0",
+ "string-width": "^4.2.0",
+ "supports-hyperlinks": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/log-symbols": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/type-fest": {
+ "version": "0.21.3",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-webpack": {
+ "version": "0.13.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "enhanced-resolve": "^0.9.1",
+ "find-root": "^1.1.0",
+ "hasown": "^2.0.2",
+ "interpret": "^1.4.0",
+ "is-core-module": "^2.15.1",
+ "is-regex": "^1.2.0",
+ "lodash": "^4.17.21",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^5.7.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "eslint-plugin-import": ">=1.4.0",
+ "webpack": ">=1.11.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-webpack/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-webpack/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-import-resolver-webpack/node_modules/semver": {
+ "version": "5.7.2",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-ava": {
+ "version": "13.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "enhance-visitors": "^1.0.0",
+ "eslint-utils": "^3.0.0",
+ "espree": "^9.0.0",
+ "espurify": "^2.1.1",
+ "import-modules": "^2.1.0",
+ "micro-spelling-correcter": "^1.1.1",
+ "pkg-dir": "^5.0.0",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.22 <13 || >=14.17 <15 || >=16.4"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.22.0"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/find-up": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/locate-path": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/p-limit": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/p-locate": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/pkg-dir": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-ava/node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-compat": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@mdn/browser-compat-data": "^4.1.5",
+ "ast-metadata-inferer": "^0.7.0",
+ "browserslist": "^4.16.8",
+ "caniuse-lite": "^1.0.30001304",
+ "core-js": "^3.16.2",
+ "find-up": "^5.0.0",
+ "lodash.memoize": "4.1.2",
+ "semver": "7.3.5"
+ },
+ "engines": {
+ "node": ">=9.x"
+ },
+ "peerDependencies": {
+ "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/find-up": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/locate-path": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint-plugin-compat/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/p-limit": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/p-locate": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/eslint-plugin-compat/node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-es": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=4.19.1"
+ }
+ },
+ "node_modules/eslint-plugin-eslint-comments": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5",
+ "ignore": "^5.0.5"
+ },
+ "engines": {
+ "node": ">=6.5.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=4.19.1"
+ }
+ },
+ "node_modules/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-n": {
+ "version": "15.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtins": "^5.0.1",
+ "eslint-plugin-es": "^4.1.0",
+ "eslint-utils": "^3.0.0",
+ "ignore": "^5.1.1",
+ "is-core-module": "^2.11.0",
+ "minimatch": "^3.1.2",
+ "resolve": "^1.22.1",
+ "semver": "^7.3.8"
+ },
+ "engines": {
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=4.19.1"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es/node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/semver": {
+ "version": "7.7.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-no-use-extend-native": {
+ "version": "0.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-get-set-prop": "^1.0.0",
+ "is-js-type": "^2.0.0",
+ "is-obj-prop": "^1.0.0",
+ "is-proto-prop": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-node": {
+ "version": "11.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-plugin-es": "^3.0.0",
+ "eslint-utils": "^2.0.0",
+ "ignore": "^5.1.1",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.10.1",
+ "semver": "^6.1.0"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=5.16.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "4.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.28.0",
+ "prettier": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-unicorn": {
+ "version": "44.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "ci-info": "^3.4.0",
+ "clean-regexp": "^1.0.0",
+ "eslint-utils": "^3.0.0",
+ "esquery": "^1.4.0",
+ "indent-string": "^4.0.0",
+ "is-builtin-module": "^3.2.0",
+ "lodash": "^4.17.21",
+ "pluralize": "^8.0.0",
+ "read-pkg-up": "^7.0.1",
+ "regexp-tree": "^0.1.24",
+ "safe-regex": "^2.1.1",
+ "semver": "^7.3.7",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.23.1"
+ }
+ },
+ "node_modules/eslint-plugin-unicorn/node_modules/ci-info": {
+ "version": "3.9.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-plugin-unicorn/node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/eslint-plugin-unicorn/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-unicorn/node_modules/semver": {
+ "version": "7.7.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-rule-docs": {
+ "version": "1.1.235",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/find-up": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/locate-path": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-limit": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-locate": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/esm-utils": {
+ "version": "4.4.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "import-meta-resolve": "^4.1.0",
+ "url-or-path": "^2.6.1"
+ },
+ "funding": {
+ "url": "https://github.com/fisker/esm-utils?sponsor=1"
+ }
+ },
+ "node_modules/esniff": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d": "^1.0.1",
+ "es5-ext": "^0.10.62",
+ "event-emitter": "^0.3.5",
+ "type": "^2.7.2"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "1.2.5",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/espurify": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-is-function": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/estree-is-identifier": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/estree-is-member-expression": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/estree-is-require": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "estree-is-identifier": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-emitter": {
+ "version": "0.3.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "d": "1",
+ "es5-ext": "~0.10.14"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/expand-brackets": {
+ "version": "0.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-posix-bracket": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-range": {
+ "version": "1.8.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-range/node_modules/fill-range": {
+ "version": "2.2.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^2.1.0",
+ "isobject": "^2.0.0",
+ "randomatic": "^3.0.0",
+ "repeat-element": "^1.1.2",
+ "repeat-string": "^1.5.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-range/node_modules/is-number": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-session": {
+ "version": "1.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "0.7.2",
+ "cookie-signature": "1.0.7",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-headers": "~1.1.0",
+ "parseurl": "~1.3.3",
+ "safe-buffer": "5.2.1",
+ "uid-safe": "~2.1.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/express-session/node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/express-session/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express-session/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ext": {
+ "version": "1.7.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "type": "^2.7.2"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/extend-object": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extend-shallow/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/is-extglob": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/falafel": {
+ "version": "2.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^7.1.1",
+ "isarray": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/falafel/node_modules/acorn": {
+ "version": "7.4.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/falafel/node_modules/isarray": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fault": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "format": "^0.2.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/file-utils": {
+ "version": "0.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "findup-sync": "~0.1.2",
+ "glob": "~3.2.6",
+ "iconv-lite": "~0.2.11",
+ "isbinaryfile": "~0.1.9",
+ "lodash": "~2.1.0",
+ "minimatch": "~0.2.12",
+ "rimraf": "~2.2.2"
+ }
+ },
+ "node_modules/file-utils/node_modules/glob": {
+ "version": "3.2.11",
+ "dev": true,
+ "license": "BSD",
+ "dependencies": {
+ "inherits": "2",
+ "minimatch": "0.3"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/file-utils/node_modules/glob/node_modules/minimatch": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/file-utils/node_modules/iconv-lite": {
+ "version": "0.2.11",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/file-utils/node_modules/lodash": {
+ "version": "2.1.0",
+ "dev": true,
+ "engines": [
+ "node",
+ "rhino"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/file-utils/node_modules/lru-cache": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/file-utils/node_modules/minimatch": {
+ "version": "0.2.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/file-utils/node_modules/rimraf": {
+ "version": "2.2.8",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/filename-regex": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fileset": {
+ "version": "0.1.8",
+ "dev": true,
+ "dependencies": {
+ "glob": "3.x",
+ "minimatch": "0.x"
+ }
+ },
+ "node_modules/fileset/node_modules/glob": {
+ "version": "3.2.11",
+ "dev": true,
+ "license": "BSD",
+ "dependencies": {
+ "inherits": "2",
+ "minimatch": "0.3"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/fileset/node_modules/glob/node_modules/minimatch": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/fileset/node_modules/lru-cache": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fileset/node_modules/minimatch": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/find-cache-dir": {
+ "version": "3.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/make-dir": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/find-nearest-file": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/find-up": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^7.2.0",
+ "path-exists": "^5.0.0",
+ "unicorn-magic": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/findup-sync": {
+ "version": "0.1.3",
+ "dev": true,
+ "dependencies": {
+ "glob": "~3.2.9",
+ "lodash": "~2.4.1"
+ },
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/findup-sync/node_modules/glob": {
+ "version": "3.2.11",
+ "dev": true,
+ "license": "BSD",
+ "dependencies": {
+ "inherits": "2",
+ "minimatch": "0.3"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/findup-sync/node_modules/lodash": {
+ "version": "2.4.2",
+ "dev": true,
+ "engines": [
+ "node",
+ "rhino"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/findup-sync/node_modules/lru-cache": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/findup-sync/node_modules/minimatch": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/firefox-profile": {
+ "version": "0.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "adm-zip": "~0.4.3",
+ "archiver": "~0.7.1",
+ "async": "~0.2.9",
+ "fs-extra": "~0.8.1",
+ "lazystream": "~0.1.0",
+ "node-uuid": "~1.4.1",
+ "wrench": "~1.5.1",
+ "xml2js": "~0.4.0"
+ }
+ },
+ "node_modules/fixpack": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "alce": "1.2.0",
+ "chalk": "^3.0.0",
+ "detect-indent": "^6.0.0",
+ "detect-newline": "^3.1.0",
+ "extend-object": "^1.0.0",
+ "rc": "^1.2.8"
+ },
+ "bin": {
+ "fixpack": "bin/fixpack"
+ }
+ },
+ "node_modules/fixpack/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/fixpack/node_modules/chalk": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fixpack/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fixpack/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/flat": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "is-buffer": "~2.0.3"
+ },
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flat/node_modules/is-buffer": {
+ "version": "2.0.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/for-own": {
+ "version": "0.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "for-in": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/foreach-shim": {
+ "version": "0.1.1",
+ "dev": true
+ },
+ "node_modules/forEachAsync": {
+ "version": "2.2.1",
+ "dev": true,
+ "dependencies": {
+ "sequence": "2.x"
+ },
+ "engines": {
+ "ender": ">= 0.5.0",
+ "node": "*"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/foreground-child/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/forever-agent": {
+ "version": "0.5.2",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/format": {
+ "version": "0.2.2",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/formidable": {
+ "version": "3.5.4",
+ "license": "MIT",
+ "dependencies": {
+ "@paralleldrive/cuid2": "^2.2.2",
+ "dezalgo": "^1.0.4",
+ "once": "^1.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fragment-cache": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "map-cache": "^0.2.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/from2": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "node_modules/from2-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "from2": "^2.0.3"
+ }
+ },
+ "node_modules/fromentries": {
+ "version": "1.3.2",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/fs-extra": {
+ "version": "0.8.1",
+ "dev": true,
+ "dependencies": {
+ "jsonfile": "~1.1.0",
+ "mkdirp": "0.3.x",
+ "ncp": "~0.4.2",
+ "rimraf": "~2.2.0"
+ }
+ },
+ "node_modules/fs-extra/node_modules/mkdirp": {
+ "version": "0.3.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs-extra/node_modules/rimraf": {
+ "version": "2.2.8",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/fs-readdir-recursive": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-assigned-identifiers": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-port": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-set-props": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/get-stdin": {
+ "version": "9.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-value": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/git-raw-commits": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dargs": "^8.0.0",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "git-raw-commits": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/github-slugger": {
+ "version": "1.5.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-base": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob-parent": "^2.0.0",
+ "is-glob": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/glob-base/node_modules/glob-parent": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^2.0.0"
+ }
+ },
+ "node_modules/glob-base/node_modules/is-extglob": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/glob-base/node_modules/is-glob": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true
+ },
+ "node_modules/global-directory": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "4.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/globby": {
+ "version": "13.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.3.0",
+ "ignore": "^5.2.4",
+ "merge2": "^1.4.1",
+ "slash": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby/node_modules/slash": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globs-to-files": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-uniq": "~1.0.2",
+ "asyncreduce": "~0.1.4",
+ "glob": "^5.0.10",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/globs-to-files/node_modules/glob": {
+ "version": "5.0.15",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/growl": {
+ "version": "1.10.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.x"
+ }
+ },
+ "node_modules/handlebars": {
+ "version": "1.0.12",
+ "dev": true,
+ "dependencies": {
+ "optimist": "~0.3",
+ "uglify-js": "~2.3"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ }
+ },
+ "node_modules/hard-rejection": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/has": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-ansi/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-value": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-value/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/is-number": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/is-number/node_modules/kind-of": {
+ "version": "3.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/kind-of": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/hasha": {
+ "version": "5.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-stream": "^2.0.0",
+ "type-fest": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hasha/node_modules/is-stream": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hasha/node_modules/type-fest": {
+ "version": "0.8.1",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hawk": {
+ "version": "1.1.1",
+ "dev": true,
+ "dependencies": {
+ "boom": "0.4.x",
+ "cryptiles": "0.2.x",
+ "hoek": "0.9.x",
+ "sntp": "0.2.x"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/hbs": {
+ "version": "2.4.0",
+ "dev": true,
+ "dependencies": {
+ "handlebars": "1.0.12",
+ "walk": "2.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "7.5.0",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/hoek": {
+ "version": "0.9.1",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/hosted-git-info": {
+ "version": "2.8.9",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/htmlescape": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/http-proxy": {
+ "version": "1.11.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "1.x.x",
+ "requires-port": "0.x.x"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/http-proxy/node_modules/eventemitter3": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http-signature": {
+ "version": "0.10.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asn1": "0.1.11",
+ "assert-plus": "^0.1.5",
+ "ctype": "0.5.3"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/https-browserify": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/humanize-duration": {
+ "version": "2.4.0",
+ "dev": true,
+ "license": "WTFPL"
+ },
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/import-modules": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/indexof": {
+ "version": "0.0.1",
+ "dev": true
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/inline-source-map": {
+ "version": "0.6.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "source-map": "~0.5.3"
+ }
+ },
+ "node_modules/insert-module-globals": {
+ "version": "7.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn-node": "^1.5.2",
+ "combine-source-map": "^0.8.0",
+ "concat-stream": "^1.6.1",
+ "is-buffer": "^1.1.0",
+ "JSONStream": "^1.0.3",
+ "path-is-absolute": "^1.0.1",
+ "process": "~0.11.0",
+ "through2": "^2.0.0",
+ "undeclared-identifiers": "^1.1.2",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "insert-module-globals": "bin/cmd.js"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/interpret": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/irregular-plurals": {
+ "version": "3.5.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-absolute": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-relative": "^1.0.0",
+ "is-windows": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-accessor-descriptor": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^1.0.0",
+ "is-decimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-arguments": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-builtin-module": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-modules": "^3.3.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-descriptor": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-descriptor": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.1",
+ "is-data-descriptor": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-dotfile": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-empty": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-equal-shallow": {
+ "version": "0.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-primitive": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-get-set-prop": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-set-props": "^0.1.0",
+ "lowercase-keys": "^1.0.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-js-type": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-types": "^1.0.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negated-glob": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-obj-prop": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^1.0.0",
+ "obj-props": "^1.0.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-plain-object/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-posix-bracket": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-primitive": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-proto-prop": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^1.0.0",
+ "proto-props": "^2.0.0"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-relative": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-unc-path": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-text-path": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "text-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-unc-path": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unc-path-regex": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-url": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isbinaryfile": {
+ "version": "0.1.9",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/isobject": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul": {
+ "version": "0.2.16",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1.0.x",
+ "async": "0.9.x",
+ "escodegen": "1.3.x",
+ "esprima": "1.2.x",
+ "fileset": "0.1.x",
+ "handlebars": "1.3.x",
+ "js-yaml": "3.x",
+ "mkdirp": "0.5.x",
+ "nopt": "3.x",
+ "resolve": "0.7.x",
+ "which": "1.0.x",
+ "wordwrap": "0.0.x"
+ },
+ "bin": {
+ "istanbul": "lib/cli.js"
+ }
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.2",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-hook": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "append-transform": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/core": "^7.7.5",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.0.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-processinfo": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "archy": "^1.0.0",
+ "cross-spawn": "^7.0.3",
+ "istanbul-lib-coverage": "^3.2.0",
+ "p-map": "^3.0.0",
+ "rimraf": "^3.0.0",
+ "uuid": "^8.3.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/make-dir": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/semver": {
+ "version": "7.7.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-middleware": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "BSD",
+ "dependencies": {
+ "archiver": "0.14.x",
+ "body-parser": "~1.12.3",
+ "express": "4.x",
+ "istanbul": "0.4.x"
+ },
+ "engines": {
+ "node": ">=0.6.x"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/abbrev": {
+ "version": "1.0.9",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/istanbul-middleware/node_modules/archiver": {
+ "version": "0.14.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async": "~0.9.0",
+ "buffer-crc32": "~0.2.1",
+ "glob": "~4.3.0",
+ "lazystream": "~0.1.0",
+ "lodash": "~3.2.0",
+ "readable-stream": "~1.0.26",
+ "tar-stream": "~1.1.0",
+ "zip-stream": "~0.5.0"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/async": {
+ "version": "0.9.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/body-parser": {
+ "version": "1.12.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "1.0.0",
+ "content-type": "~1.0.1",
+ "debug": "~2.2.0",
+ "depd": "~1.0.1",
+ "iconv-lite": "0.4.8",
+ "on-finished": "~2.2.1",
+ "qs": "2.4.2",
+ "raw-body": "~2.0.1",
+ "type-is": "~1.6.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/bytes": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "node_modules/istanbul-middleware/node_modules/debug": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "0.7.1"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/depd": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/ee-first": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/escodegen": {
+ "version": "1.8.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^2.7.1",
+ "estraverse": "^1.9.1",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=0.12.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.2.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/esprima": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/estraverse": {
+ "version": "1.9.3",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/glob": {
+ "version": "4.3.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^2.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/handlebars": {
+ "version": "4.7.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.2",
+ "source-map": "^0.6.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "^3.1.4"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/handlebars/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/has-flag": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/iconv-lite": {
+ "version": "0.4.8",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/istanbul": {
+ "version": "0.4.5",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "abbrev": "1.0.x",
+ "async": "1.x",
+ "escodegen": "1.8.x",
+ "esprima": "2.7.x",
+ "glob": "^5.0.15",
+ "handlebars": "^4.0.1",
+ "js-yaml": "3.x",
+ "mkdirp": "0.5.x",
+ "nopt": "3.x",
+ "once": "1.x",
+ "resolve": "1.1.x",
+ "supports-color": "^3.1.0",
+ "which": "^1.1.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "istanbul": "lib/cli.js"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/istanbul/node_modules/async": {
+ "version": "1.5.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/istanbul/node_modules/glob": {
+ "version": "5.0.15",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/js-yaml": {
+ "version": "3.14.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/js-yaml/node_modules/esprima": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/levn": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/lodash": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/minimatch": {
+ "version": "2.0.10",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/ms": {
+ "version": "0.7.1",
+ "dev": true
+ },
+ "node_modules/istanbul-middleware/node_modules/nopt": {
+ "version": "3.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/on-finished": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/optionator": {
+ "version": "0.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/qs": {
+ "version": "2.4.2",
+ "dev": true
+ },
+ "node_modules/istanbul-middleware/node_modules/raw-body": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "2.1.0",
+ "iconv-lite": "0.4.8"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/raw-body/node_modules/bytes": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/resolve": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/source-map": {
+ "version": "0.2.0",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "amdefine": ">=0.0.4"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/supports-color": {
+ "version": "3.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/type-check": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/uglify-js": {
+ "version": "3.19.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/which": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/istanbul-middleware/node_modules/wordwrap": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-middleware/node_modules/zip-stream": {
+ "version": "0.5.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "compress-commons": "~0.2.0",
+ "lodash": "~3.2.0",
+ "readable-stream": "~1.0.26"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul/node_modules/abbrev": {
+ "version": "1.0.9",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/istanbul/node_modules/argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/istanbul/node_modules/async": {
+ "version": "0.9.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul/node_modules/escodegen": {
+ "version": "1.3.3",
+ "dev": true,
+ "dependencies": {
+ "esprima": "~1.1.1",
+ "estraverse": "~1.5.0",
+ "esutils": "~1.0.0"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.1.33"
+ }
+ },
+ "node_modules/istanbul/node_modules/escodegen/node_modules/esprima": {
+ "version": "1.1.1",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/istanbul/node_modules/estraverse": {
+ "version": "1.5.1",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/istanbul/node_modules/esutils": {
+ "version": "1.0.0",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul/node_modules/handlebars": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "optimist": "~0.3"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "~2.3"
+ }
+ },
+ "node_modules/istanbul/node_modules/js-yaml": {
+ "version": "3.14.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/istanbul/node_modules/js-yaml/node_modules/esprima": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/istanbul/node_modules/nopt": {
+ "version": "3.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ }
+ },
+ "node_modules/istanbul/node_modules/resolve": {
+ "version": "0.7.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul/node_modules/source-map": {
+ "version": "0.1.43",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "amdefine": ">=0.0.4"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/istanbul/node_modules/which": {
+ "version": "1.0.9",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.5.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-types": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/JSON2": {
+ "version": "0.1.0",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "1.1.1",
+ "dev": true
+ },
+ "node_modules/jsonify": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "Public Domain",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/JSONStream": {
+ "version": "1.3.5",
+ "dev": true,
+ "license": "(MIT OR Apache-2.0)",
+ "dependencies": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ },
+ "bin": {
+ "JSONStream": "bin.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/keypress": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "3.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/labeled-stream-splicer": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "stream-splicer": "^2.0.0"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "0.1.0",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "~1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/line-column-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/line-column-path/node_modules/type-fest": {
+ "version": "2.19.0",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lint-staged": {
+ "version": "15.5.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.4.1",
+ "commander": "^13.1.0",
+ "debug": "^4.4.0",
+ "execa": "^8.0.1",
+ "lilconfig": "^3.1.3",
+ "listr2": "^8.2.5",
+ "micromatch": "^4.0.8",
+ "pidtree": "^0.6.0",
+ "string-argv": "^0.3.2",
+ "yaml": "^2.7.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/commander": {
+ "version": "13.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/load-plugin": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@npmcli/config": "^6.0.0",
+ "import-meta-resolve": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/load-plugin/node_modules/import-meta-resolve": {
+ "version": "2.2.2",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/load-script": {
+ "version": "0.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=6.11.5"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/localtunnel": {
+ "version": "1.5.0",
+ "dev": true,
+ "dependencies": {
+ "debug": "0.7.4",
+ "optimist": "0.3.4",
+ "request": "2.11.4"
+ },
+ "bin": {
+ "lt": "bin/client"
+ }
+ },
+ "node_modules/localtunnel/node_modules/debug": {
+ "version": "0.7.4",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/localtunnel/node_modules/optimist": {
+ "version": "0.3.4",
+ "dev": true,
+ "license": "MIT/X11",
+ "dependencies": {
+ "wordwrap": "~0.0.2"
+ }
+ },
+ "node_modules/localtunnel/node_modules/request": {
+ "version": "2.11.4",
+ "bundleDependencies": [
+ "form-data",
+ "mime"
+ ],
+ "dev": true,
+ "engines": [
+ "node >= 0.3.6"
+ ],
+ "dependencies": {
+ "form-data": "~0.0.3",
+ "mime": "~1.2.7"
+ }
+ },
+ "node_modules/localtunnel/node_modules/request/node_modules/form-data": {
+ "version": "0.0.3",
+ "dev": true,
+ "inBundle": true,
+ "dependencies": {
+ "async": "~0.1.9",
+ "combined-stream": "0.0.3",
+ "mime": "~1.2.2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/localtunnel/node_modules/request/node_modules/form-data/node_modules/async": {
+ "version": "0.1.9",
+ "dev": true,
+ "inBundle": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/localtunnel/node_modules/request/node_modules/form-data/node_modules/combined-stream": {
+ "version": "0.0.3",
+ "dev": true,
+ "inBundle": true,
+ "dependencies": {
+ "delayed-stream": "0.0.5"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/localtunnel/node_modules/request/node_modules/form-data/node_modules/combined-stream/node_modules/delayed-stream": {
+ "version": "0.0.5",
+ "dev": true,
+ "inBundle": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/localtunnel/node_modules/request/node_modules/mime": {
+ "version": "1.2.7",
+ "dev": true,
+ "inBundle": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^6.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash-es": {
+ "version": "4.17.22",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash._isnative": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash._objecttypes": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash._shimkeys": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash._objecttypes": "~2.4.1"
+ }
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.defaults": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash._objecttypes": "~2.4.1",
+ "lodash.keys": "~2.4.1"
+ }
+ },
+ "node_modules/lodash.flattendeep": {
+ "version": "4.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.isobject": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash._objecttypes": "~2.4.1"
+ }
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.kebabcase": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.keys": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash._isnative": "~2.4.1",
+ "lodash._shimkeys": "~2.4.1",
+ "lodash.isobject": "~2.4.1"
+ }
+ },
+ "node_modules/lodash.memoize": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.mergewith": {
+ "version": "4.6.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.startcase": {
+ "version": "4.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.upperfirst": {
+ "version": "4.3.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-symbols": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "2.4.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-convert": {
+ "version": "1.9.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-symbols/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "5.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/longest-streak": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/lowercase-keys": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.23.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sourcemap-codec": "^1.4.1"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "5.7.2",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/map-cache": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/map-obj": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/map-visit": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/markdown-table": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "repeat-string": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/marked": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/match-casing": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/math-random": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/md5.js": {
+ "version": "1.3.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/mdast-comment-marker": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0",
+ "unist-util-is": "^4.0.0",
+ "unist-util-visit-parents": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "@types/unist": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "mdast-util-to-string": "^3.1.0",
+ "micromark": "^3.0.0",
+ "micromark-util-decode-numeric-character-reference": "^1.0.0",
+ "micromark-util-decode-string": "^1.0.0",
+ "micromark-util-normalize-identifier": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "unist-util-stringify-position": "^3.0.0",
+ "uvu": "^0.5.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-gfm-autolink-literal": "^0.1.0",
+ "mdast-util-gfm-strikethrough": "^0.2.0",
+ "mdast-util-gfm-table": "^0.1.0",
+ "mdast-util-gfm-task-list-item": "^0.1.0",
+ "mdast-util-to-markdown": "^0.6.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "0.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ccount": "^1.0.0",
+ "mdast-util-find-and-replace": "^1.1.0",
+ "micromark": "^2.11.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark": {
+ "version": "2.11.4",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.0.0",
+ "parse-entities": "^2.0.0"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "0.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-markdown": "^0.6.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "0.1.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "markdown-table": "^2.0.0",
+ "mdast-util-to-markdown": "~0.6.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "0.1.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-markdown": "~0.6.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-heading-range": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-string": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-heading-range/node_modules/mdast-util-to-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-heading-style": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "unist-util-is": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing/node_modules/unist-util-is": {
+ "version": "5.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "0.6.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "longest-streak": "^2.0.0",
+ "mdast-util-to-string": "^2.0.0",
+ "parse-entities": "^2.0.0",
+ "repeat-string": "^1.0.0",
+ "zwitch": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown/node_modules/mdast-util-to-string": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-nlcst": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0",
+ "repeat-string": "^1.0.0",
+ "unist-util-position": "^3.0.0",
+ "vfile-location": "^3.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-toc": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.3",
+ "@types/unist": "^2.0.3",
+ "extend": "^3.0.2",
+ "github-slugger": "^1.2.1",
+ "mdast-util-to-string": "^2.0.0",
+ "unist-util-is": "^4.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-toc/node_modules/mdast-util-to-string": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/memory-fs": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/meow": {
+ "version": "12.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-source-map": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micro-spelling-correcter": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/micromark": {
+ "version": "3.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-core-commonmark": "^1.0.1",
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-chunked": "^1.0.0",
+ "micromark-util-combine-extensions": "^1.0.0",
+ "micromark-util-decode-numeric-character-reference": "^1.0.0",
+ "micromark-util-encode": "^1.0.0",
+ "micromark-util-normalize-identifier": "^1.0.0",
+ "micromark-util-resolve-all": "^1.0.0",
+ "micromark-util-sanitize-uri": "^1.0.0",
+ "micromark-util-subtokenize": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.1",
+ "uvu": "^0.5.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-factory-destination": "^1.0.0",
+ "micromark-factory-label": "^1.0.0",
+ "micromark-factory-space": "^1.0.0",
+ "micromark-factory-title": "^1.0.0",
+ "micromark-factory-whitespace": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-chunked": "^1.0.0",
+ "micromark-util-classify-character": "^1.0.0",
+ "micromark-util-html-tag-name": "^1.0.0",
+ "micromark-util-normalize-identifier": "^1.0.0",
+ "micromark-util-resolve-all": "^1.0.0",
+ "micromark-util-subtokenize": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.1",
+ "uvu": "^0.5.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "0.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark": "~2.11.0",
+ "micromark-extension-gfm-autolink-literal": "~0.5.0",
+ "micromark-extension-gfm-strikethrough": "~0.6.5",
+ "micromark-extension-gfm-table": "~0.4.0",
+ "micromark-extension-gfm-tagfilter": "~0.3.0",
+ "micromark-extension-gfm-task-list-item": "~0.3.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "0.5.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark": "~2.11.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark": {
+ "version": "2.11.4",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.0.0",
+ "parse-entities": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "0.6.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark": "~2.11.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark": {
+ "version": "2.11.4",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.0.0",
+ "parse-entities": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "0.4.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark": "~2.11.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table/node_modules/micromark": {
+ "version": "2.11.4",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.0.0",
+ "parse-entities": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "0.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark": "~2.11.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark": {
+ "version": "2.11.4",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.0.0",
+ "parse-entities": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm/node_modules/micromark": {
+ "version": "2.11.4",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.0.0",
+ "parse-entities": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-decode-numeric-character-reference": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-encode": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "1.1.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/miller-rabin": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ },
+ "bin": {
+ "miller-rabin": "bin/miller-rabin"
+ }
+ },
+ "node_modules/miller-rabin/node_modules/bn.js": {
+ "version": "4.12.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mime": {
+ "version": "2.6.0",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minify-stream": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "concat-stream": "^2.0.0",
+ "convert-source-map": "^1.5.0",
+ "duplexify": "^4.1.1",
+ "from2-string": "^1.1.0",
+ "terser": "^4.7.0",
+ "xtend": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/minify-stream/node_modules/concat-stream": {
+ "version": "2.0.0",
+ "dev": true,
+ "engines": [
+ "node >= 6.0"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.0.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/minify-stream/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/minify-stream/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minimist-options": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arrify": "^1.0.1",
+ "is-plain-obj": "^1.1.0",
+ "kind-of": "^6.0.3"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/minimist-options/node_modules/arrify": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/minimist-options/node_modules/is-plain-obj": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/minimist-options/node_modules/kind-of": {
+ "version": "6.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mixin-deep": {
+ "version": "1.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mixin-deep/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.5"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mocha": {
+ "version": "6.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "2.2.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.4",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.5",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/mocha/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mocha/node_modules/argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/mocha/node_modules/cliui": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "node_modules/mocha/node_modules/color-convert": {
+ "version": "1.9.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/mocha/node_modules/color-name": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mocha/node_modules/debug": {
+ "version": "3.2.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/mocha/node_modules/emoji-regex": {
+ "version": "7.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mocha/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/mocha/node_modules/esprima": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mocha/node_modules/find-up": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/glob": {
+ "version": "7.1.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mocha/node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mocha/node_modules/js-yaml": {
+ "version": "3.13.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/mocha/node_modules/locate-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mocha/node_modules/ms": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mocha/node_modules/object.assign": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mocha/node_modules/p-limit": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/p-locate": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/path-exists": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mocha/node_modules/string-width": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mocha/node_modules/which": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/mocha/node_modules/wrap-ansi": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/y18n": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/mocha/node_modules/yargs": {
+ "version": "13.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "node_modules/module-deps": {
+ "version": "6.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browser-resolve": "^2.0.0",
+ "cached-path-relative": "^1.0.2",
+ "concat-stream": "~1.6.0",
+ "defined": "^1.0.0",
+ "detective": "^5.2.0",
+ "duplexer2": "^0.1.2",
+ "inherits": "^2.0.1",
+ "JSONStream": "^1.0.3",
+ "parents": "^1.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.4.0",
+ "stream-combiner2": "^1.1.1",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "module-deps": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/mri": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "license": "MIT"
+ },
+ "node_modules/multer": {
+ "version": "1.4.5-lts.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "append-field": "^1.0.0",
+ "busboy": "^1.0.0",
+ "concat-stream": "^1.5.2",
+ "mkdirp": "^0.5.4",
+ "object-assign": "^4.1.1",
+ "type-is": "^1.6.4",
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/multi-stage-sourcemap": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "source-map": "^0.1.34"
+ }
+ },
+ "node_modules/multi-stage-sourcemap/node_modules/source-map": {
+ "version": "0.1.43",
+ "dev": true,
+ "dependencies": {
+ "amdefine": ">=0.0.4"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/multiparty": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "~1.1.9",
+ "stream-counter": "~0.2.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/multiparty/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/multiparty/node_modules/readable-stream": {
+ "version": "1.1.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/multiparty/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/multisplice": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/mutexify": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "queue-tick": "^1.0.0"
+ }
+ },
+ "node_modules/nan": {
+ "version": "2.24.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/nanobench": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browser-process-hrtime": "^0.1.2",
+ "chalk": "^1.1.3",
+ "mutexify": "^1.1.0",
+ "pretty-hrtime": "^1.0.2"
+ },
+ "bin": {
+ "nanobench": "run.js",
+ "nanobench-compare": "compare.js"
+ }
+ },
+ "node_modules/nanobench/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanobench/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanobench/node_modules/chalk": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanobench/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/nanobench/node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanobench/node_modules/supports-color": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/nanomatch": {
+ "version": "1.2.13",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanomatch/node_modules/arr-diff": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanomatch/node_modules/array-unique": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanomatch/node_modules/kind-of": {
+ "version": "6.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/natives": {
+ "version": "1.1.6",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ncp": {
+ "version": "0.4.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "ncp": "bin/ncp"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/next-tick": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/nlcst-is-literal": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/nlcst-normalize": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/nlcst-search": {
+ "version": "1.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-is-literal": "^1.1.0",
+ "nlcst-normalize": "^2.1.0",
+ "unist-util-visit": "^1.0.0"
+ }
+ },
+ "node_modules/nlcst-search/node_modules/unist-util-is": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nlcst-search/node_modules/unist-util-visit": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-visit-parents": "^2.0.0"
+ }
+ },
+ "node_modules/nlcst-search/node_modules/unist-util-visit-parents": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-is": "^3.0.0"
+ }
+ },
+ "node_modules/nlcst-to-string": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/node-environment-flags": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "object.getownpropertydescriptors": "^2.0.3",
+ "semver": "^5.7.0"
+ }
+ },
+ "node_modules/node-environment-flags/node_modules/semver": {
+ "version": "5.7.2",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/node-int64": {
+ "version": "0.3.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-preload": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "process-on-spawn": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-uuid": {
+ "version": "1.4.8",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/nopt": {
+ "version": "7.2.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^2.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/normalize-package-data": {
+ "version": "2.5.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "node_modules/normalize-package-data/node_modules/semver": {
+ "version": "5.7.2",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-normalize-package-bin": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/number-to-words": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nyc": {
+ "version": "15.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "caching-transform": "^4.0.0",
+ "convert-source-map": "^1.7.0",
+ "decamelize": "^1.2.0",
+ "find-cache-dir": "^3.2.0",
+ "find-up": "^4.1.0",
+ "foreground-child": "^2.0.0",
+ "get-package-type": "^0.1.0",
+ "glob": "^7.1.6",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-hook": "^3.0.0",
+ "istanbul-lib-instrument": "^4.0.0",
+ "istanbul-lib-processinfo": "^2.0.2",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.0.2",
+ "make-dir": "^3.0.0",
+ "node-preload": "^0.2.1",
+ "p-map": "^3.0.0",
+ "process-on-spawn": "^1.0.0",
+ "resolve-from": "^5.0.0",
+ "rimraf": "^3.0.0",
+ "signal-exit": "^3.0.2",
+ "spawn-wrap": "^2.0.0",
+ "test-exclude": "^6.0.0",
+ "yargs": "^15.0.2"
+ },
+ "bin": {
+ "nyc": "bin/nyc.js"
+ },
+ "engines": {
+ "node": ">=8.9"
+ }
+ },
+ "node_modules/nyc/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/nyc/node_modules/cliui": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "node_modules/nyc/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nyc/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nyc/node_modules/find-up": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/locate-path": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/make-dir": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/nyc/node_modules/p-limit": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/nyc/node_modules/p-locate": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/nyc/node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/y18n": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/nyc/node_modules/yargs": {
+ "version": "15.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/oauth-sign": {
+ "version": "0.4.0",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/obj-props": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/define-property": {
+ "version": "0.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.1",
+ "is-data-descriptor": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object-visit": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-visit/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.getownpropertydescriptors": {
+ "version": "2.1.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array.prototype.reduce": "^1.0.8",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "gopd": "^1.2.0",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.omit": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "for-own": "^0.1.4",
+ "is-extendable": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.pick": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.pick/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open": {
+ "version": "8.4.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-editor": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-editor": "^1.1.0",
+ "execa": "^5.1.1",
+ "line-column-path": "^3.0.0",
+ "open": "^8.4.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-editor/node_modules/execa": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/open-editor/node_modules/get-stream": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-editor/node_modules/human-signals": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/open-editor/node_modules/is-stream": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-editor/node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/open-editor/node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/open-editor/node_modules/onetime": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-editor/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/open-editor/node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/open/node_modules/define-lazy-prop": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/opener": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "WTFPL",
+ "bin": {
+ "opener": "opener.js"
+ }
+ },
+ "node_modules/optimist": {
+ "version": "0.3.7",
+ "dev": true,
+ "license": "MIT/X11",
+ "dependencies": {
+ "wordwrap": "~0.0.2"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/os-browserify": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/osenv": {
+ "version": "0.0.3",
+ "dev": true,
+ "license": "BSD"
+ },
+ "node_modules/outpipe": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shell-quote": "^1.4.2"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-map": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/package-hash": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "graceful-fs": "^4.1.15",
+ "hasha": "^5.0.0",
+ "lodash.flattendeep": "^4.4.0",
+ "release-zalgo": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "dev": true,
+ "license": "(MIT AND Zlib)"
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parents": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-platform": "~0.11.15"
+ }
+ },
+ "node_modules/parse-asn1": {
+ "version": "5.1.9",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "asn1.js": "^4.10.1",
+ "browserify-aes": "^1.2.0",
+ "evp_bytestokey": "^1.0.3",
+ "pbkdf2": "^3.1.5",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/parse-author": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "author-regex": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-english": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0",
+ "parse-latin": "^4.0.0",
+ "unist-util-modify-children": "^2.0.0",
+ "unist-util-visit-children": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^1.0.0",
+ "character-entities-legacy": "^1.0.0",
+ "character-reference-invalid": "^1.0.0",
+ "is-alphanumerical": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-hexadecimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/character-entities": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-glob": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob-base": "^0.3.0",
+ "is-dotfile": "^1.0.0",
+ "is-extglob": "^1.0.0",
+ "is-glob": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-glob/node_modules/is-extglob": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-glob/node_modules/is-glob": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-latin": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0",
+ "unist-util-modify-children": "^2.0.0",
+ "unist-util-visit-children": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/pascalcase": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-exists": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-platform": {
+ "version": "0.11.15",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.12",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pause": {
+ "version": "0.0.1",
+ "dev": true
+ },
+ "node_modules/pbkdf2": {
+ "version": "3.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "ripemd160": "^2.0.3",
+ "safe-buffer": "^5.2.1",
+ "sha.js": "^2.4.12",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pidtree": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/find-up": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/locate-path": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-limit": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-locate": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/plur": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "irregular-plurals": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pluralize": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/posix-character-classes": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/preserve": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.8.8",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/pretty-hrtime": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/proc-log": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/process-on-spawn": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fromentries": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/proto-props": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/public-encrypt": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/public-encrypt/node_modules/bn.js": {
+ "version": "4.12.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/q": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.0",
+ "teleport": ">=0.2.0"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.1",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/querystring-es3": {
+ "version": "0.2.1",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/queue-tick": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/quick-lru": {
+ "version": "6.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/quotation": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/random-bytes": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/randomatic": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^4.0.0",
+ "kind-of": "^6.0.0",
+ "math-random": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/randomatic/node_modules/is-number": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/randomatic/node_modules/kind-of": {
+ "version": "6.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/randomfill": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/ini": {
+ "version": "1.3.8",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/read-only-stream": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/read-package-json-fast": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "json-parse-even-better-errors": "^3.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/read-pkg": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/normalize-package-data": "^2.4.0",
+ "normalize-package-data": "^2.5.0",
+ "parse-json": "^5.0.0",
+ "type-fest": "^0.6.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/read-pkg-up": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^4.1.0",
+ "read-pkg": "^5.2.0",
+ "type-fest": "^0.8.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/read-pkg-up/node_modules/find-up": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/read-pkg-up/node_modules/locate-path": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/read-pkg-up/node_modules/p-limit": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/read-pkg-up/node_modules/p-locate": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/read-pkg-up/node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/read-pkg-up/node_modules/type-fest": {
+ "version": "0.8.1",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/read-pkg/node_modules/type-fest": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/readable-stream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/redent": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "indent-string": "^5.0.0",
+ "strip-indent": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/redent/node_modules/indent-string": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/redent/node_modules/strip-indent": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/reduce-component": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "Apache, Version 2.0"
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regenerate": {
+ "version": "1.4.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regex-cache": {
+ "version": "0.4.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-equal-shallow": "^0.1.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/regex-not": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/regex-not/node_modules/safe-regex": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ret": "~0.1.10"
+ }
+ },
+ "node_modules/regexp-tree": {
+ "version": "0.1.27",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "regexp-tree": "bin/regexp-tree"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexpp": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/regexpu-core": {
+ "version": "6.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.2.2",
+ "regjsgen": "^0.8.0",
+ "regjsparser": "^0.13.0",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regjsgen": {
+ "version": "0.8.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regjsparser": {
+ "version": "0.13.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "jsesc": "~3.1.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/release-zalgo": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "es6-error": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/remark": {
+ "version": "14.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "remark-parse": "^10.0.0",
+ "remark-stringify": "^10.0.0",
+ "unified": "^10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-cli": {
+ "version": "11.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "remark": "^14.0.0",
+ "unified-args": "^10.0.0"
+ },
+ "bin": {
+ "remark": "cli.js"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-comment-config": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-comment-marker": "^1.0.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-contributors": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-url": "^1.2.2",
+ "mdast-util-heading-range": "^2.1.2",
+ "parse-author": "^2.0.0",
+ "unist-builder": "^1.0.3"
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-gfm": "^0.1.0",
+ "micromark-extension-gfm": "^0.3.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-heading-gap": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-license": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-heading-range": "^2.0.0",
+ "parse-author": "^2.0.0",
+ "spdx-license-list": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "remark-message-control": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-blockquote-indentation": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-string": "^1.0.2",
+ "pluralize": "^8.0.0",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-blockquote-indentation/node_modules/mdast-util-to-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-checkbox-character-style": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-checkbox-content-indent": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0",
+ "vfile-location": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-code-block-style": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-definition-case": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-definition-spacing": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-emphasis-marker": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-fenced-code-flag": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-fenced-code-marker": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-file-extension": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-final-definition": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-final-newline": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-first-heading-level": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-hard-break-spaces": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-heading-style": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-heading-style": "^1.0.2",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-list-item-bullet-indent": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pluralize": "^8.0.0",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-list-item-indent": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pluralize": "^8.0.0",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-auto-link-without-protocol": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-string": "^1.0.2",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-auto-link-without-protocol/node_modules/mdast-util-to-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-blockquote-without-marker": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.0.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0",
+ "vfile-location": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-consecutive-blank-lines": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pluralize": "^8.0.0",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-duplicate-definitions": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-stringify-position": "^2.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-duplicate-definitions/node_modules/unist-util-stringify-position": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-emphasis-as-heading": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-file-name-articles": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-file-name-consecutive-dashes": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-file-name-irregular-characters": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-file-name-mixed-case": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-file-name-outer-dashes": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-heading-content-indent": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-heading-style": "^1.0.2",
+ "pluralize": "^8.0.0",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-heading-indent": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pluralize": "^8.0.0",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-heading-punctuation": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-string": "^1.0.2",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-heading-punctuation/node_modules/mdast-util-to-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-inline-padding": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-string": "^1.0.2",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-inline-padding/node_modules/mdast-util-to-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-literal-urls": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-string": "^1.0.2",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-literal-urls/node_modules/mdast-util-to-string": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-missing-blank-lines": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-multiple-toplevel-headings": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-stringify-position": "^2.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-multiple-toplevel-headings/node_modules/unist-util-stringify-position": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-shell-dollars": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-shortcut-reference-image": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-shortcut-reference-link": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-table-indentation": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0",
+ "vfile-location": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-tabs": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "vfile-location": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-undefined-references": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "collapse-white-space": "^1.0.4",
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.1.0",
+ "unist-util-visit": "^2.0.0",
+ "vfile-location": "^3.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-no-unused-definitions": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-ordered-list-marker-style": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-ordered-list-marker-value": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-rule-style": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-strong-marker": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-table-cell-padding": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-table-pipe-alignment": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-table-pipes": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-lint-unordered-list-marker-style": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unified-lint-rule": "^1.0.0",
+ "unist-util-generated": "^1.1.0",
+ "unist-util-position": "^3.0.0",
+ "unist-util-visit": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-message-control": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-comment-marker": "^1.0.0",
+ "unified-message-control": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "10.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-from-markdown": "^1.0.0",
+ "unified": "^10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-preset-github": {
+ "version": "4.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "remark-comment-config": "^6.0.0",
+ "remark-contributors": "4",
+ "remark-gfm": "^1.0.0",
+ "remark-heading-gap": "^4.0.0",
+ "remark-license": "5",
+ "remark-lint-blockquote-indentation": "^2.0.1",
+ "remark-lint-checkbox-character-style": "^3.0.0",
+ "remark-lint-checkbox-content-indent": "^3.0.0",
+ "remark-lint-code-block-style": "^2.0.1",
+ "remark-lint-definition-case": "^2.0.1",
+ "remark-lint-definition-spacing": "^2.0.1",
+ "remark-lint-emphasis-marker": "^2.0.1",
+ "remark-lint-fenced-code-flag": "^2.0.1",
+ "remark-lint-fenced-code-marker": "^2.0.1",
+ "remark-lint-file-extension": "^1.0.5",
+ "remark-lint-final-definition": "^2.1.0",
+ "remark-lint-first-heading-level": "^2.0.1",
+ "remark-lint-heading-style": "^2.0.1",
+ "remark-lint-list-item-indent": "2.0.1",
+ "remark-lint-no-consecutive-blank-lines": "^3.0.0",
+ "remark-lint-no-duplicate-definitions": "^2.0.1",
+ "remark-lint-no-emphasis-as-heading": "^2.0.1",
+ "remark-lint-no-file-name-articles": "^1.0.5",
+ "remark-lint-no-file-name-consecutive-dashes": "^1.0.5",
+ "remark-lint-no-file-name-irregular-characters": "^1.0.5",
+ "remark-lint-no-file-name-mixed-case": "^1.0.5",
+ "remark-lint-no-file-name-outer-dashes": "^1.0.6",
+ "remark-lint-no-heading-content-indent": "^3.0.0",
+ "remark-lint-no-heading-indent": "^3.0.0",
+ "remark-lint-no-heading-punctuation": "^2.0.1",
+ "remark-lint-no-missing-blank-lines": "^2.0.1",
+ "remark-lint-no-multiple-toplevel-headings": "^2.0.1",
+ "remark-lint-no-shell-dollars": "^2.0.2",
+ "remark-lint-no-table-indentation": "^3.0.0",
+ "remark-lint-no-tabs": "^2.0.1",
+ "remark-lint-ordered-list-marker-value": "^2.0.1",
+ "remark-lint-rule-style": "^2.0.1",
+ "remark-lint-strong-marker": "^2.0.1",
+ "remark-lint-table-cell-padding": "^3.0.0",
+ "remark-lint-table-pipe-alignment": "^2.0.1",
+ "remark-lint-table-pipes": "^3.0.0",
+ "remark-lint-unordered-list-marker-style": "^2.0.1",
+ "remark-preset-lint-recommended": "^5.0.0",
+ "remark-retext": "^4.0.0",
+ "remark-toc": "^7.0.0",
+ "retext-english": "^3.0.4",
+ "retext-preset-github": "^0.0.6",
+ "unified": "^9.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/bail": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/is-buffer": {
+ "version": "2.0.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/trough": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/unified": {
+ "version": "9.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bail": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-buffer": "^2.0.0",
+ "is-plain-obj": "^2.0.0",
+ "trough": "^1.0.0",
+ "vfile": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/unist-util-stringify-position": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/vfile": {
+ "version": "4.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "is-buffer": "^2.0.0",
+ "unist-util-stringify-position": "^2.0.0",
+ "vfile-message": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-preset-github/node_modules/vfile-message": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-preset-lint-recommended": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "remark-lint": "^8.0.0",
+ "remark-lint-final-newline": "^1.0.0",
+ "remark-lint-hard-break-spaces": "^2.0.0",
+ "remark-lint-list-item-bullet-indent": "^3.0.0",
+ "remark-lint-list-item-indent": "^2.0.0",
+ "remark-lint-no-auto-link-without-protocol": "^2.0.0",
+ "remark-lint-no-blockquote-without-marker": "^4.0.0",
+ "remark-lint-no-duplicate-definitions": "^2.0.0",
+ "remark-lint-no-heading-content-indent": "^3.0.0",
+ "remark-lint-no-inline-padding": "^3.0.0",
+ "remark-lint-no-literal-urls": "^2.0.0",
+ "remark-lint-no-shortcut-reference-image": "^2.0.0",
+ "remark-lint-no-shortcut-reference-link": "^2.0.0",
+ "remark-lint-no-undefined-references": "^3.0.0",
+ "remark-lint-no-unused-definitions": "^2.0.0",
+ "remark-lint-ordered-list-marker-style": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-retext": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-to-nlcst": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify": {
+ "version": "10.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-markdown": "^1.0.0",
+ "unified": "^10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify/node_modules/longest-streak": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/remark-stringify/node_modules/mdast-util-to-markdown": {
+ "version": "1.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "@types/unist": "^2.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^3.0.0",
+ "mdast-util-to-string": "^3.0.0",
+ "micromark-util-decode-string": "^1.0.0",
+ "unist-util-visit": "^4.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify/node_modules/unist-util-is": {
+ "version": "5.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify/node_modules/unist-util-visit": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0",
+ "unist-util-visit-parents": "^5.1.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify/node_modules/unist-util-visit-parents": {
+ "version": "5.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify/node_modules/zwitch": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/remark-toc": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.3",
+ "mdast-util-toc": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remove-trailing-separator": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/repeat-element": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/repeat-string": {
+ "version": "1.6.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/request": {
+ "version": "2.46.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "aws-sign2": "~0.5.0",
+ "bl": "~0.9.0",
+ "caseless": "~0.6.0",
+ "forever-agent": "~0.5.0",
+ "form-data": "~0.1.0",
+ "hawk": "1.1.1",
+ "http-signature": "~0.10.0",
+ "json-stringify-safe": "~5.0.0",
+ "mime-types": "~1.0.1",
+ "node-uuid": "~1.4.0",
+ "oauth-sign": "~0.4.0",
+ "qs": "~1.2.0",
+ "stringstream": "~0.0.4",
+ "tough-cookie": ">=0.12.0",
+ "tunnel-agent": "~0.4.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/request/node_modules/async": {
+ "version": "0.9.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/request/node_modules/combined-stream": {
+ "version": "0.0.7",
+ "dev": true,
+ "dependencies": {
+ "delayed-stream": "0.0.5"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/request/node_modules/delayed-stream": {
+ "version": "0.0.5",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/request/node_modules/form-data": {
+ "version": "0.1.4",
+ "dev": true,
+ "dependencies": {
+ "async": "~0.9.0",
+ "combined-stream": "~0.0.4",
+ "mime": "~1.2.11"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/request/node_modules/mime": {
+ "version": "1.2.11",
+ "dev": true
+ },
+ "node_modules/request/node_modules/mime-types": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/request/node_modules/qs": {
+ "version": "1.2.2",
+ "dev": true
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-main-filename": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/requires-port": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-url": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/onetime": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/retext-contractions": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-is-literal": "^1.0.0",
+ "nlcst-to-string": "^2.0.0",
+ "unist-util-visit": "^1.1.0"
+ }
+ },
+ "node_modules/retext-contractions/node_modules/unist-util-is": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/retext-contractions/node_modules/unist-util-visit": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-visit-parents": "^2.0.0"
+ }
+ },
+ "node_modules/retext-contractions/node_modules/unist-util-visit-parents": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-is": "^3.0.0"
+ }
+ },
+ "node_modules/retext-diacritics": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "match-casing": "^1.0.0",
+ "nlcst-search": "^1.0.0",
+ "nlcst-to-string": "^2.0.0",
+ "quotation": "^1.0.1",
+ "unist-util-position": "^3.0.0"
+ }
+ },
+ "node_modules/retext-english": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse-english": "^4.0.0",
+ "unherit": "^1.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext-indefinite-article": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "format": "^0.2.2",
+ "nlcst-to-string": "^2.0.0",
+ "number-to-words": "^1.2.3",
+ "unist-util-is": "^3.0.0",
+ "unist-util-visit": "^1.1.0"
+ }
+ },
+ "node_modules/retext-indefinite-article/node_modules/unist-util-is": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/retext-indefinite-article/node_modules/unist-util-visit": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-visit-parents": "^2.0.0"
+ }
+ },
+ "node_modules/retext-indefinite-article/node_modules/unist-util-visit-parents": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-is": "^3.0.0"
+ }
+ },
+ "node_modules/retext-preset-github": {
+ "version": "0.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "retext-contractions": "^3.0.0",
+ "retext-diacritics": "^2.0.0",
+ "retext-indefinite-article": "^1.1.7",
+ "retext-quotes": "^3.0.0",
+ "retext-redundant-acronyms": "^2.0.0",
+ "retext-repeated-words": "^2.0.0",
+ "retext-sentence-spacing": "^3.0.0"
+ }
+ },
+ "node_modules/retext-quotes": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0",
+ "unist-util-is": "^3.0.0",
+ "unist-util-visit": "^1.1.0"
+ }
+ },
+ "node_modules/retext-quotes/node_modules/unist-util-is": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/retext-quotes/node_modules/unist-util-visit": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-visit-parents": "^2.0.0"
+ }
+ },
+ "node_modules/retext-quotes/node_modules/unist-util-visit-parents": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-is": "^3.0.0"
+ }
+ },
+ "node_modules/retext-redundant-acronyms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-normalize": "^2.0.0",
+ "nlcst-search": "^1.0.0",
+ "nlcst-to-string": "^2.0.0",
+ "pluralize": "^8.0.0",
+ "quotation": "^1.0.0",
+ "unist-util-find-after": "^2.0.0",
+ "unist-util-position": "^3.0.0"
+ }
+ },
+ "node_modules/retext-repeated-words": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0",
+ "unist-util-is": "^3.0.0",
+ "unist-util-visit": "^1.1.0"
+ }
+ },
+ "node_modules/retext-repeated-words/node_modules/unist-util-is": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/retext-repeated-words/node_modules/unist-util-visit": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-visit-parents": "^2.0.0"
+ }
+ },
+ "node_modules/retext-repeated-words/node_modules/unist-util-visit-parents": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-is": "^3.0.0"
+ }
+ },
+ "node_modules/retext-sentence-spacing": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nlcst-to-string": "^2.0.0",
+ "unist-util-is": "^3.0.0",
+ "unist-util-visit": "^1.1.0"
+ }
+ },
+ "node_modules/retext-sentence-spacing/node_modules/unist-util-is": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/retext-sentence-spacing/node_modules/unist-util-visit": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-visit-parents": "^2.0.0"
+ }
+ },
+ "node_modules/retext-sentence-spacing/node_modules/unist-util-visit-parents": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-is": "^3.0.0"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ripemd160": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.1.2",
+ "inherits": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/ripemd160/node_modules/hash-base": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/runnel": {
+ "version": "0.5.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/sade": {
+ "version": "1.8.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mri": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-array-concat/node_modules/isarray": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply/node_modules/isarray": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safe-regex": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regexp-tree": "~0.1.1"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sax": {
+ "version": "1.4.3",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/schema-utils": {
+ "version": "4.3.3",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.9.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.1.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/scope-analyzer": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "array-from": "^2.1.1",
+ "dash-ast": "^2.0.1",
+ "es6-map": "^0.1.5",
+ "es6-set": "^0.1.5",
+ "es6-symbol": "^3.1.1",
+ "estree-is-function": "^1.0.0",
+ "get-assigned-identifiers": "^1.1.0"
+ }
+ },
+ "node_modules/scope-analyzer/node_modules/dash-ast": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/send/node_modules/mime": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/sequence": {
+ "version": "2.2.1",
+ "dev": true,
+ "engines": {
+ "ender": ">= 0.5.0",
+ "node": "*"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-value": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.12",
+ "dev": true,
+ "license": "(MIT AND BSD-3-Clause)",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/shallow-copy": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/shasum": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-stable-stringify": "~0.0.0",
+ "sha.js": "~2.4.4"
+ }
+ },
+ "node_modules/shasum-object": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fast-safe-stringify": "^2.0.7"
+ },
+ "bin": {
+ "shasum-object": "bin.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shell-quote": {
+ "version": "1.8.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/should": {
+ "version": "13.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "should-equal": "^2.0.0",
+ "should-format": "^3.0.3",
+ "should-type": "^1.4.0",
+ "should-type-adaptors": "^1.0.1",
+ "should-util": "^1.0.0"
+ }
+ },
+ "node_modules/should-equal": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "should-type": "^1.4.0"
+ }
+ },
+ "node_modules/should-format": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "should-type": "^1.3.0",
+ "should-type-adaptors": "^1.0.1"
+ }
+ },
+ "node_modules/should-http": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.2"
+ },
+ "peerDependencies": {
+ "should": ">= 4.x"
+ }
+ },
+ "node_modules/should-type": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/should-type-adaptors": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "should-type": "^1.3.0",
+ "should-util": "^1.0.0"
+ }
+ },
+ "node_modules/should-util": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sigmund": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/slash": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/sliced": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/snapdragon": {
+ "version": "0.8.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node/node_modules/define-property": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/define-property": {
+ "version": "0.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-descriptor": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.1",
+ "is-data-descriptor": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/snapdragon/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sntp": {
+ "version": "0.2.4",
+ "dev": true,
+ "dependencies": {
+ "hoek": "0.9.x"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-cjs": {
+ "version": "0.1.32",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/source-map-resolve": {
+ "version": "0.5.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-url": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sourcemap-codec": {
+ "version": "1.4.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/spawn-wrap": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^2.0.0",
+ "is-windows": "^1.0.2",
+ "make-dir": "^3.0.0",
+ "rimraf": "^3.0.0",
+ "signal-exit": "^3.0.2",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/spawn-wrap/node_modules/make-dir": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/spawn-wrap/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-exceptions": {
+ "version": "2.5.0",
+ "dev": true,
+ "license": "CC-BY-3.0"
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.22",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/spdx-license-list": {
+ "version": "6.10.0",
+ "dev": true,
+ "license": "CC0-1.0",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/split": {
+ "version": "0.1.2",
+ "dev": true,
+ "dependencies": {
+ "through": "1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/split-string": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split/node_modules/through": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/stack-generator": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "Unlicense",
+ "dependencies": {
+ "stackframe": "^1.0.2"
+ }
+ },
+ "node_modules/stack-generator/node_modules/stackframe": {
+ "version": "1.3.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stack-mapper": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-map": "0.0.0",
+ "foreach-shim": "~0.1.1",
+ "isarray": "0.0.1",
+ "source-map-cjs": "~0.1.31"
+ }
+ },
+ "node_modules/stack-mapper/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stackframe": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE"
+ },
+ "node_modules/stacktrace-gps": {
+ "version": "2.4.4",
+ "dev": true,
+ "license": "Unlicense",
+ "dependencies": {
+ "source-map": "0.5.6",
+ "stackframe": "~0.3"
+ }
+ },
+ "node_modules/stacktrace-gps/node_modules/source-map": {
+ "version": "0.5.6",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stacktrace-js": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "Unlicense",
+ "dependencies": {
+ "error-stack-parser": "^1.3.6",
+ "stack-generator": "^1.0.7",
+ "stacktrace-gps": "^2.4.3"
+ }
+ },
+ "node_modules/static-extend": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/define-property": {
+ "version": "0.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-descriptor": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.1",
+ "is-data-descriptor": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/stream-browserify": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.4",
+ "readable-stream": "^3.5.0"
+ }
+ },
+ "node_modules/stream-browserify/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/stream-combiner": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "duplexer": "~0.1.1",
+ "through": "~2.3.4"
+ }
+ },
+ "node_modules/stream-combiner2": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "duplexer2": "~0.1.0",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/stream-counter": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "BSD",
+ "dependencies": {
+ "readable-stream": "~1.1.8"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/stream-counter/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stream-counter/node_modules/readable-stream": {
+ "version": "1.1.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/stream-counter/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stream-http": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/stream-http/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stream-splicer": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/stringstream": {
+ "version": "0.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-indent": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "min-indent": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/subarg": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.1.0"
+ }
+ },
+ "node_modules/superagent": {
+ "version": "0.15.7",
+ "dev": true,
+ "dependencies": {
+ "cookiejar": "1.3.0",
+ "debug": "~0.7.2",
+ "emitter-component": "1.0.0",
+ "formidable": "1.0.14",
+ "methods": "0.0.1",
+ "mime": "1.2.5",
+ "qs": "0.6.5",
+ "reduce-component": "1.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/superagent/node_modules/cookiejar": {
+ "version": "1.3.0",
+ "dev": true
+ },
+ "node_modules/superagent/node_modules/debug": {
+ "version": "0.7.4",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/superagent/node_modules/formidable": {
+ "version": "1.0.14",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/superagent/node_modules/methods": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/superagent/node_modules/mime": {
+ "version": "1.2.5",
+ "dev": true
+ },
+ "node_modules/superagent/node_modules/qs": {
+ "version": "0.6.5",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/supports-hyperlinks": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-hyperlinks/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-hyperlinks/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/syntax-error": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn-node": "^1.2.0"
+ }
+ },
+ "node_modules/tap-finished": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tap-parser": "~0.2.0",
+ "through": "~2.3.4"
+ }
+ },
+ "node_modules/tap-finished/node_modules/tap-parser": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "split": "~0.1.2"
+ }
+ },
+ "node_modules/tap-parser": {
+ "version": "0.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.1",
+ "minimist": "^0.2.0",
+ "readable-stream": "~1.1.11"
+ },
+ "bin": {
+ "tap-parser": "bin/cmd.js"
+ }
+ },
+ "node_modules/tap-parser/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tap-parser/node_modules/minimist": {
+ "version": "0.2.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tap-parser/node_modules/readable-stream": {
+ "version": "1.1.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/tap-parser/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "0.1.10",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "1.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^0.9.0",
+ "end-of-stream": "^1.0.0",
+ "readable-stream": "~1.0.33",
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/tar-stream/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tar-stream/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/tar-stream/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/terser": {
+ "version": "4.8.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "5.3.16",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^4.3.0",
+ "serialize-javascript": "^6.0.2",
+ "terser": "^5.31.1"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "uglify-js": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/commander": {
+ "version": "2.20.3",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/terser-webpack-plugin/node_modules/terser": {
+ "version": "5.44.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.15.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/terser/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/test-exclude": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-extensions": {
+ "version": "2.4.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/through2": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "node_modules/timers-browserify": {
+ "version": "1.4.2",
+ "dev": true,
+ "dependencies": {
+ "process": "~0.11.0"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/tinyexec": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyify": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@goto-bus-stop/envify": "^5.0.0",
+ "acorn-node": "^1.8.2",
+ "browser-pack-flat": "^3.0.9",
+ "bundle-collapser": "^1.3.0",
+ "common-shakeify": "^0.6.0",
+ "dash-ast": "^1.0.0",
+ "minify-stream": "^2.0.1",
+ "multisplice": "^1.0.0",
+ "through2": "^3.0.1",
+ "uglifyify": "^5.0.0",
+ "unassertify": "^2.1.1"
+ }
+ },
+ "node_modules/tinyify/node_modules/through2": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "2 || 3"
+ }
+ },
+ "node_modules/tldts": {
+ "version": "7.0.19",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^7.0.19"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/tldts-core": {
+ "version": "7.0.19",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/to-absolute-glob": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-absolute": "^1.0.0",
+ "is-negated-glob": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-arraybuffer": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/to-buffer": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/to-buffer/node_modules/isarray": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/to-object-path": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/to-regex/node_modules/safe-regex": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ret": "~0.1.10"
+ }
+ },
+ "node_modules/to-vfile": {
+ "version": "7.2.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-buffer": "^2.0.0",
+ "vfile": "^5.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/to-vfile/node_modules/is-buffer": {
+ "version": "2.0.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^7.0.5"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/transform-ast": {
+ "version": "2.4.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn-node": "^1.3.0",
+ "convert-source-map": "^1.5.1",
+ "dash-ast": "^1.0.0",
+ "is-buffer": "^2.0.0",
+ "magic-string": "^0.23.2",
+ "merge-source-map": "1.0.4",
+ "nanobench": "^2.1.1"
+ }
+ },
+ "node_modules/transform-ast/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/transform-ast/node_modules/is-buffer": {
+ "version": "2.0.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/trim-newlines": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/strip-bom": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/tsscmp": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.x"
+ }
+ },
+ "node_modules/tty-browserify": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.4.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/type": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/typedarray-to-buffer": {
+ "version": "3.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/uglify-js": {
+ "version": "2.3.6",
+ "dev": true,
+ "dependencies": {
+ "async": "~0.2.6",
+ "optimist": "~0.3.5",
+ "source-map": "~0.1.7"
+ },
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/uglify-js/node_modules/source-map": {
+ "version": "0.1.43",
+ "dev": true,
+ "dependencies": {
+ "amdefine": ">=0.0.4"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/uglifyify": {
+ "version": "5.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "convert-source-map": "~1.1.0",
+ "minimatch": "^3.0.2",
+ "terser": "^3.7.5",
+ "through": "~2.3.4",
+ "xtend": "^4.0.1"
+ }
+ },
+ "node_modules/uglifyify/node_modules/commander": {
+ "version": "2.20.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uglifyify/node_modules/convert-source-map": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uglifyify/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/uglifyify/node_modules/terser": {
+ "version": "3.17.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "commander": "^2.19.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.10"
+ },
+ "bin": {
+ "terser": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/uid-safe": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "random-bytes": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/uid2": {
+ "version": "0.0.3",
+ "dev": true
+ },
+ "node_modules/umd": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "umd": "bin/cli.js"
+ }
+ },
+ "node_modules/unassert": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^7.0.0",
+ "call-matcher": "^2.0.0",
+ "deep-equal": "^1.0.0",
+ "espurify": "^2.0.1",
+ "estraverse": "^4.1.0",
+ "esutils": "^2.0.2",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "node_modules/unassert/node_modules/acorn": {
+ "version": "7.4.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/unassert/node_modules/estraverse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/unassertify": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^5.1.0",
+ "convert-source-map": "^1.1.1",
+ "escodegen": "^1.6.1",
+ "multi-stage-sourcemap": "^0.2.1",
+ "through": "^2.3.7",
+ "unassert": "^1.3.1"
+ }
+ },
+ "node_modules/unassertify/node_modules/acorn": {
+ "version": "5.7.4",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/unassertify/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unc-path-regex": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/undeclared-identifiers": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "acorn-node": "^1.3.0",
+ "dash-ast": "^1.0.0",
+ "get-assigned-identifiers": "^1.2.0",
+ "simple-concat": "^1.0.0",
+ "xtend": "^4.0.1"
+ },
+ "bin": {
+ "undeclared-identifiers": "bin.js"
+ }
+ },
+ "node_modules/underscore.string": {
+ "version": "2.3.3",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unherit": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.0",
+ "xtend": "^4.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicorn-magic": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unified": {
+ "version": "10.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "bail": "^2.0.0",
+ "extend": "^3.0.0",
+ "is-buffer": "^2.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unified-args": {
+ "version": "10.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/text-table": "^0.2.0",
+ "camelcase": "^7.0.0",
+ "chalk": "^5.0.0",
+ "chokidar": "^3.0.0",
+ "fault": "^2.0.0",
+ "json5": "^2.0.0",
+ "minimist": "^1.0.0",
+ "text-table": "^0.2.0",
+ "unified-engine": "^10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unified-args/node_modules/camelcase": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unified-engine": {
+ "version": "10.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/concat-stream": "^2.0.0",
+ "@types/debug": "^4.0.0",
+ "@types/is-empty": "^1.0.0",
+ "@types/node": "^18.0.0",
+ "@types/unist": "^2.0.0",
+ "concat-stream": "^2.0.0",
+ "debug": "^4.0.0",
+ "fault": "^2.0.0",
+ "glob": "^8.0.0",
+ "ignore": "^5.0.0",
+ "is-buffer": "^2.0.0",
+ "is-empty": "^1.0.0",
+ "is-plain-obj": "^4.0.0",
+ "load-plugin": "^5.0.0",
+ "parse-json": "^6.0.0",
+ "to-vfile": "^7.0.0",
+ "trough": "^2.0.0",
+ "unist-util-inspect": "^7.0.0",
+ "vfile-message": "^3.0.0",
+ "vfile-reporter": "^7.0.0",
+ "vfile-statistics": "^2.0.0",
+ "yaml": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unified-engine/node_modules/@types/node": {
+ "version": "18.19.130",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/unified-engine/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/unified-engine/node_modules/concat-stream": {
+ "version": "2.0.0",
+ "dev": true,
+ "engines": [
+ "node >= 6.0"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.0.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/unified-engine/node_modules/glob": {
+ "version": "8.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/unified-engine/node_modules/is-buffer": {
+ "version": "2.0.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unified-engine/node_modules/lines-and-columns": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/unified-engine/node_modules/minimatch": {
+ "version": "5.1.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/unified-engine/node_modules/parse-json": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.16.0",
+ "error-ex": "^1.3.2",
+ "json-parse-even-better-errors": "^2.3.1",
+ "lines-and-columns": "^2.0.2"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unified-engine/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/unified-engine/node_modules/undici-types": {
+ "version": "5.26.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unified-lint-rule": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "wrapped": "^1.0.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unified-message-control": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-visit": "^2.0.0",
+ "vfile-location": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unified/node_modules/is-buffer": {
+ "version": "2.0.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/union-value": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unist-builder": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4.1.0"
+ }
+ },
+ "node_modules/unist-util-find-after": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unist-util-is": "^3.0.0"
+ }
+ },
+ "node_modules/unist-util-find-after/node_modules/unist-util-is": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unist-util-generated": {
+ "version": "1.1.6",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-inspect": {
+ "version": "7.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-modify-children": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-iterate": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^4.0.0",
+ "unist-util-visit-parents": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-children": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/unset-value": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-values": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/uri-js/node_modules/punycode": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/urix": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/url": {
+ "version": "0.11.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^1.4.1",
+ "qs": "^6.12.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/url-or-path": {
+ "version": "2.6.1",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/fisker/url-or-path?sponsor=1"
+ }
+ },
+ "node_modules/use": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/uvu": {
+ "version": "0.5.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0",
+ "diff": "^5.0.0",
+ "kleur": "^4.0.3",
+ "sade": "^1.7.3"
+ },
+ "bin": {
+ "uvu": "bin.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/uvu/node_modules/diff": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/vargs": {
+ "version": "0.1.0",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.93"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "5.3.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "is-buffer": "^2.0.0",
+ "unist-util-stringify-position": "^3.0.0",
+ "vfile-message": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-location": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "3.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-reporter": {
+ "version": "7.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/supports-color": "^8.0.0",
+ "string-width": "^5.0.0",
+ "supports-color": "^9.0.0",
+ "unist-util-stringify-position": "^3.0.0",
+ "vfile": "^5.0.0",
+ "vfile-message": "^3.0.0",
+ "vfile-sort": "^3.0.0",
+ "vfile-statistics": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-reporter/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/vfile-reporter/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vfile-reporter/node_modules/string-width": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/vfile-reporter/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/vfile-reporter/node_modules/supports-color": {
+ "version": "9.4.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/vfile-sort": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vfile": "^5.0.0",
+ "vfile-message": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-statistics": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vfile": "^5.0.0",
+ "vfile-message": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile/node_modules/is-buffer": {
+ "version": "2.0.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/vm-browserify": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/walk": {
+ "version": "2.2.1",
+ "dev": true,
+ "dependencies": {
+ "forEachAsync": "~2.2"
+ }
+ },
+ "node_modules/walk-up-path": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/watchify": {
+ "version": "3.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "^1.3.0",
+ "browserify": "^13.0.0",
+ "chokidar": "^1.0.0",
+ "defined": "^1.0.0",
+ "outpipe": "^1.1.0",
+ "through2": "^2.0.0",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "watchify": "bin/cmd.js"
+ }
+ },
+ "node_modules/watchify/node_modules/acorn": {
+ "version": "5.7.4",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/watchify/node_modules/anymatch": {
+ "version": "1.3.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "micromatch": "^2.1.5",
+ "normalize-path": "^2.0.0"
+ }
+ },
+ "node_modules/watchify/node_modules/binary-extensions": {
+ "version": "1.13.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/braces": {
+ "version": "1.8.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "expand-range": "^1.8.1",
+ "preserve": "^0.2.0",
+ "repeat-element": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/browser-resolve": {
+ "version": "1.11.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve": "1.1.7"
+ }
+ },
+ "node_modules/watchify/node_modules/browserify": {
+ "version": "13.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert": "^1.4.0",
+ "browser-pack": "^6.0.1",
+ "browser-resolve": "^1.11.0",
+ "browserify-zlib": "~0.1.2",
+ "buffer": "^4.1.0",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "~1.5.1",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "~1.0.0",
+ "crypto-browserify": "^3.0.0",
+ "defined": "^1.0.0",
+ "deps-sort": "^2.0.0",
+ "domain-browser": "~1.1.0",
+ "duplexer2": "~0.1.2",
+ "events": "~1.1.0",
+ "glob": "^7.1.0",
+ "has": "^1.0.0",
+ "htmlescape": "^1.1.0",
+ "https-browserify": "~0.0.0",
+ "inherits": "~2.0.1",
+ "insert-module-globals": "^7.0.0",
+ "JSONStream": "^1.0.3",
+ "labeled-stream-splicer": "^2.0.0",
+ "module-deps": "^4.0.8",
+ "os-browserify": "~0.1.1",
+ "parents": "^1.0.1",
+ "path-browserify": "~0.0.0",
+ "process": "~0.11.0",
+ "punycode": "^1.3.2",
+ "querystring-es3": "~0.2.0",
+ "read-only-stream": "^2.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.1.4",
+ "shasum": "^1.0.0",
+ "shell-quote": "^1.6.1",
+ "stream-browserify": "^2.0.0",
+ "stream-http": "^2.0.0",
+ "string_decoder": "~0.10.0",
+ "subarg": "^1.0.0",
+ "syntax-error": "^1.1.1",
+ "through2": "^2.0.0",
+ "timers-browserify": "^1.0.1",
+ "tty-browserify": "~0.0.0",
+ "url": "~0.11.0",
+ "util": "~0.10.1",
+ "vm-browserify": "~0.0.1",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "browserify": "bin/cmd.js"
+ }
+ },
+ "node_modules/watchify/node_modules/browserify-zlib": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~0.2.0"
+ }
+ },
+ "node_modules/watchify/node_modules/buffer": {
+ "version": "4.9.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "node_modules/watchify/node_modules/chokidar": {
+ "version": "1.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "^1.3.0",
+ "async-each": "^1.0.0",
+ "glob-parent": "^2.0.0",
+ "inherits": "^2.0.1",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^2.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^1.0.0"
+ }
+ },
+ "node_modules/watchify/node_modules/concat-stream": {
+ "version": "1.5.2",
+ "dev": true,
+ "engines": [
+ "node >= 0.8"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.1",
+ "readable-stream": "~2.0.0",
+ "typedarray": "~0.0.5"
+ }
+ },
+ "node_modules/watchify/node_modules/concat-stream/node_modules/readable-stream": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/watchify/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/watchify/node_modules/detective": {
+ "version": "4.7.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^5.2.1",
+ "defined": "^1.0.0"
+ }
+ },
+ "node_modules/watchify/node_modules/domain-browser": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4",
+ "npm": ">=1.2"
+ }
+ },
+ "node_modules/watchify/node_modules/events": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/watchify/node_modules/fill-range": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/fill-range/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/fsevents": {
+ "version": "1.2.13",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ },
+ "engines": {
+ "node": ">= 4.0"
+ }
+ },
+ "node_modules/watchify/node_modules/glob-parent": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^2.0.0"
+ }
+ },
+ "node_modules/watchify/node_modules/https-browserify": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/inherits": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/watchify/node_modules/is-binary-path": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/is-extglob": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/is-glob": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/is-number": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/micromatch": {
+ "version": "2.3.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arr-diff": "^2.0.0",
+ "array-unique": "^0.2.1",
+ "braces": "^1.8.2",
+ "expand-brackets": "^0.1.4",
+ "extglob": "^0.3.1",
+ "filename-regex": "^2.0.0",
+ "is-extglob": "^1.0.0",
+ "is-glob": "^2.0.1",
+ "kind-of": "^3.0.2",
+ "normalize-path": "^2.0.1",
+ "object.omit": "^2.0.0",
+ "parse-glob": "^3.0.4",
+ "regex-cache": "^0.4.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/module-deps": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browser-resolve": "^1.7.0",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "~1.5.0",
+ "defined": "^1.0.0",
+ "detective": "^4.0.0",
+ "duplexer2": "^0.1.2",
+ "inherits": "^2.0.1",
+ "JSONStream": "^1.0.3",
+ "parents": "^1.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.1.3",
+ "stream-combiner2": "^1.1.1",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "module-deps": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/watchify/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/normalize-path": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "remove-trailing-separator": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/os-browserify": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/pako": {
+ "version": "0.2.9",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/path-browserify": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/process-nextick-args": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/readdirp": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/arr-diff": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/array-unique": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/braces": {
+ "version": "2.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/braces/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/expand-brackets": {
+ "version": "2.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/expand-brackets/node_modules/define-property": {
+ "version": "0.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/expand-brackets/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/expand-brackets/node_modules/is-descriptor": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.1",
+ "is-data-descriptor": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/extglob": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/extglob/node_modules/define-property": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/extglob/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/kind-of": {
+ "version": "6.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/readdirp/node_modules/micromatch": {
+ "version": "3.1.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/resolve": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/stream-browserify": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/watchify/node_modules/stream-http": {
+ "version": "2.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/watchify/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchify/node_modules/to-regex-range": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchify/node_modules/util": {
+ "version": "0.10.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/watchify/node_modules/vm-browserify": {
+ "version": "0.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "indexof": "0.0.1"
+ }
+ },
+ "node_modules/watchpack": {
+ "version": "2.5.0",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/wd": {
+ "version": "0.3.11",
+ "dev": true,
+ "engines": [
+ "node"
+ ],
+ "dependencies": {
+ "archiver": "~0.12.0",
+ "async": "~0.9.0",
+ "lodash": "~2.4.1",
+ "q": "~1.0.1",
+ "request": "~2.46.0",
+ "underscore.string": "~2.3.3",
+ "vargs": "~0.1.0"
+ },
+ "bin": {
+ "wd": "lib/bin.js"
+ }
+ },
+ "node_modules/wd/node_modules/archiver": {
+ "version": "0.12.0",
+ "dev": true,
+ "dependencies": {
+ "async": "~0.9.0",
+ "buffer-crc32": "~0.2.1",
+ "glob": "~4.0.6",
+ "lazystream": "~0.1.0",
+ "lodash": "~2.4.1",
+ "readable-stream": "~1.0.26",
+ "tar-stream": "~1.0.0",
+ "zip-stream": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/wd/node_modules/async": {
+ "version": "0.9.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wd/node_modules/compress-commons": {
+ "version": "0.1.6",
+ "dev": true,
+ "dependencies": {
+ "buffer-crc32": "~0.2.1",
+ "crc32-stream": "~0.3.1",
+ "readable-stream": "~1.0.26"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/wd/node_modules/glob": {
+ "version": "4.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "graceful-fs": "^3.0.2",
+ "inherits": "2",
+ "minimatch": "^1.0.0",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/wd/node_modules/graceful-fs": {
+ "version": "3.0.12",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "natives": "^1.1.3"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/wd/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wd/node_modules/lodash": {
+ "version": "2.4.2",
+ "dev": true,
+ "engines": [
+ "node",
+ "rhino"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/wd/node_modules/lru-cache": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/wd/node_modules/minimatch": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/wd/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/wd/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wd/node_modules/tar-stream": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^0.9.0",
+ "end-of-stream": "^1.0.0",
+ "readable-stream": "^1.0.27-1",
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/wd/node_modules/zip-stream": {
+ "version": "0.4.1",
+ "dev": true,
+ "dependencies": {
+ "compress-commons": "~0.1.0",
+ "lodash": "~2.4.1",
+ "readable-stream": "~1.0.26"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/webpack": {
+ "version": "5.104.1",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.7",
+ "@types/estree": "^1.0.8",
+ "@types/json-schema": "^7.0.15",
+ "@webassemblyjs/ast": "^1.14.1",
+ "@webassemblyjs/wasm-edit": "^1.14.1",
+ "@webassemblyjs/wasm-parser": "^1.14.1",
+ "acorn": "^8.15.0",
+ "acorn-import-phases": "^1.0.3",
+ "browserslist": "^4.28.1",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.17.4",
+ "es-module-lexer": "^2.0.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.11",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.3.1",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^4.3.3",
+ "tapable": "^2.3.0",
+ "terser-webpack-plugin": "^5.3.16",
+ "watchpack": "^2.4.4",
+ "webpack-sources": "^3.3.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.3.3",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack/node_modules/enhanced-resolve": {
+ "version": "5.18.4",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack/node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/webpack/node_modules/estraverse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/webpack/node_modules/tapable": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type/node_modules/isarray": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-module": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/wide-align": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "node_modules/wide-align/node_modules/ansi-regex": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/wide-align/node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/wide-align/node_modules/string-width": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/wide-align/node_modules/strip-ansi": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wordwrap": {
+ "version": "0.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-comment": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrapped": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "co": "3.1.0",
+ "sliced": "^1.0.1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "license": "ISC"
+ },
+ "node_modules/wrench": {
+ "version": "1.5.9",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.97"
+ }
+ },
+ "node_modules/write-file-atomic": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "node_modules/write-file-atomic/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/xml2js": {
+ "version": "0.4.23",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xo": {
+ "version": "0.53.1",
+ "bundleDependencies": [
+ "@typescript-eslint/eslint-plugin",
+ "@typescript-eslint/parser",
+ "eslint-config-xo-typescript"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint/eslintrc": "^1.3.3",
+ "@typescript-eslint/eslint-plugin": "^5.43.0",
+ "@typescript-eslint/parser": "^5.43.0",
+ "arrify": "^3.0.0",
+ "cosmiconfig": "^7.1.0",
+ "define-lazy-prop": "^3.0.0",
+ "eslint": "^8.27.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-config-xo": "^0.43.1",
+ "eslint-config-xo-typescript": "^0.55.0",
+ "eslint-formatter-pretty": "^4.1.0",
+ "eslint-import-resolver-webpack": "^0.13.2",
+ "eslint-plugin-ava": "^13.2.0",
+ "eslint-plugin-eslint-comments": "^3.2.0",
+ "eslint-plugin-import": "^2.26.0",
+ "eslint-plugin-n": "^15.5.1",
+ "eslint-plugin-no-use-extend-native": "^0.5.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "eslint-plugin-unicorn": "^44.0.2",
+ "esm-utils": "^4.1.0",
+ "find-cache-dir": "^4.0.0",
+ "find-up": "^6.3.0",
+ "get-stdin": "^9.0.0",
+ "globby": "^13.1.2",
+ "imurmurhash": "^0.1.4",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "json5": "^2.2.1",
+ "lodash-es": "^4.17.21",
+ "meow": "^11.0.0",
+ "micromatch": "^4.0.5",
+ "open-editor": "^4.0.0",
+ "prettier": "^2.7.1",
+ "semver": "^7.3.8",
+ "slash": "^5.0.0",
+ "to-absolute-glob": "^2.0.2",
+ "typescript": "^4.9.3"
+ },
+ "bin": {
+ "xo": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/@eslint/eslintrc": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.4.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/xo/node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/xo/node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/xo/node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/xo/node_modules/@types/json-schema": {
+ "version": "7.0.11",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/xo/node_modules/@types/semver": {
+ "version": "7.3.13",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.43.0",
+ "@typescript-eslint/type-utils": "5.43.0",
+ "@typescript-eslint/utils": "5.43.0",
+ "debug": "^4.3.4",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "regexpp": "^3.2.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^5.0.0",
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/parser": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.43.0",
+ "@typescript-eslint/types": "5.43.0",
+ "@typescript-eslint/typescript-estree": "5.43.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.43.0",
+ "@typescript-eslint/visitor-keys": "5.43.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/type-utils": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "5.43.0",
+ "@typescript-eslint/utils": "5.43.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/types": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "5.43.0",
+ "@typescript-eslint/visitor-keys": "5.43.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/typescript-estree/node_modules/globby": {
+ "version": "11.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/typescript-estree/node_modules/slash": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/utils": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.43.0",
+ "@typescript-eslint/types": "5.43.0",
+ "@typescript-eslint/typescript-estree": "5.43.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/xo/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.43.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.43.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/xo/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/xo/node_modules/array-union": {
+ "version": "2.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xo/node_modules/braces": {
+ "version": "3.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xo/node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/xo/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xo/node_modules/debug/node_modules/ms": {
+ "version": "2.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/xo/node_modules/decamelize": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/dir-glob": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xo/node_modules/eslint-config-xo-typescript": {
+ "version": "0.55.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": ">=5.43.0",
+ "@typescript-eslint/parser": ">=5.43.0",
+ "eslint": ">=8.0.0",
+ "typescript": ">=4.4"
+ }
+ },
+ "node_modules/xo/node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/xo/node_modules/eslint-scope/node_modules/estraverse": {
+ "version": "4.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xo/node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/xo/node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/xo/node_modules/eslint-visitor-keys": {
+ "version": "3.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/xo/node_modules/esrecurse": {
+ "version": "4.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xo/node_modules/estraverse": {
+ "version": "5.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xo/node_modules/fast-glob": {
+ "version": "3.2.12",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/xo/node_modules/fastq": {
+ "version": "1.13.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/xo/node_modules/fill-range": {
+ "version": "7.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xo/node_modules/find-cache-dir": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "common-path-prefix": "^3.0.0",
+ "pkg-dir": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/find-up": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^7.1.0",
+ "path-exists": "^5.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/xo/node_modules/hosted-git-info": {
+ "version": "5.2.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^7.5.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/xo/node_modules/ignore": {
+ "version": "5.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/xo/node_modules/is-extglob": {
+ "version": "2.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/xo/node_modules/is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/xo/node_modules/is-number": {
+ "version": "7.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/xo/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/xo/node_modules/lru-cache": {
+ "version": "7.18.3",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/xo/node_modules/meow": {
+ "version": "11.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/minimist": "^1.2.2",
+ "camelcase-keys": "^8.0.2",
+ "decamelize": "^6.0.0",
+ "decamelize-keys": "^1.1.0",
+ "hard-rejection": "^2.1.0",
+ "minimist-options": "4.1.0",
+ "normalize-package-data": "^4.0.1",
+ "read-pkg-up": "^9.1.0",
+ "redent": "^4.0.0",
+ "trim-newlines": "^4.0.2",
+ "type-fest": "^3.1.0",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/merge2": {
+ "version": "1.4.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/xo/node_modules/micromatch": {
+ "version": "4.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/xo/node_modules/natural-compare-lite": {
+ "version": "1.4.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/xo/node_modules/normalize-package-data": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "hosted-git-info": "^5.0.0",
+ "is-core-module": "^2.8.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/xo/node_modules/path-type": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xo/node_modules/picomatch": {
+ "version": "2.3.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/xo/node_modules/pkg-dir": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/xo/node_modules/read-pkg": {
+ "version": "7.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/normalize-package-data": "^2.4.1",
+ "normalize-package-data": "^3.0.2",
+ "parse-json": "^5.2.0",
+ "type-fest": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/read-pkg-up": {
+ "version": "9.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^6.3.0",
+ "read-pkg": "^7.1.0",
+ "type-fest": "^2.5.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/read-pkg-up/node_modules/type-fest": {
+ "version": "2.19.0",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/read-pkg/node_modules/hosted-git-info": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/xo/node_modules/read-pkg/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/xo/node_modules/read-pkg/node_modules/normalize-package-data": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "hosted-git-info": "^4.0.1",
+ "is-core-module": "^2.5.0",
+ "semver": "^7.3.4",
+ "validate-npm-package-license": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/xo/node_modules/read-pkg/node_modules/type-fest": {
+ "version": "2.19.0",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/regexpp": {
+ "version": "3.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/xo/node_modules/reusify": {
+ "version": "1.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/xo/node_modules/run-parallel": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/xo/node_modules/semver": {
+ "version": "7.3.8",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/xo/node_modules/semver/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/xo/node_modules/slash": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/xo/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "0BSD"
+ },
+ "node_modules/xo/node_modules/tsutils": {
+ "version": "3.21.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "node_modules/xo/node_modules/type-fest": {
+ "version": "3.13.1",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xo/node_modules/typescript": {
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/xo/node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/xo/node_modules/yaml": {
+ "version": "1.10.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/xo/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.8.2",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yamljs": {
+ "version": "0.2.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "glob": "^7.0.5"
+ },
+ "bin": {
+ "json2yaml": "bin/json2yaml",
+ "yaml2json": "bin/yaml2json"
+ }
+ },
+ "node_modules/yamljs/node_modules/argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "13.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "node_modules/yargs-unparser": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat": "^4.1.0",
+ "lodash": "^4.17.15",
+ "yargs": "^13.3.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/cliui": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/color-convert": {
+ "version": "1.9.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/color-name": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs-unparser/node_modules/emoji-regex": {
+ "version": "7.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs-unparser/node_modules/find-up": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/locate-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/p-limit": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/p-locate": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/path-exists": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/string-width": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/wrap-ansi": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/y18n": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs-unparser/node_modules/yargs": {
+ "version": "13.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "node_modules/yargs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zip-stream": {
+ "version": "0.2.3",
+ "dev": true,
+ "dependencies": {
+ "debug": "~0.7.4",
+ "lodash.defaults": "~2.4.1",
+ "readable-stream": "~1.0.24"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/zip-stream/node_modules/debug": {
+ "version": "0.7.4",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/zip-stream/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zip-stream/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/zip-stream/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul": {
+ "version": "3.12.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "batch": "0.5.0",
+ "browserify": "13.0.0",
+ "browserify-istanbul": "0.1.5",
+ "char-split": "0.2.0",
+ "colors": "0.6.2",
+ "commander": "2.1.0",
+ "compression": "1.5.0",
+ "convert-source-map": "1.0.0",
+ "debug": "2.1.0",
+ "express": "3.4.8",
+ "express-state": "1.0.3",
+ "find-nearest-file": "1.0.0",
+ "firefox-profile": "0.2.7",
+ "globs-to-files": "1.0.0",
+ "hbs": "2.4.0",
+ "highlight.js": "7.5.0",
+ "http-proxy": "1.11.2",
+ "humanize-duration": "2.4.0",
+ "istanbul-middleware": "0.2.2",
+ "JSON2": "0.1.0",
+ "load-script": "0.0.5",
+ "lodash": "3.10.1",
+ "opener": "1.4.0",
+ "osenv": "0.0.3",
+ "shallow-copy": "0.0.1",
+ "shell-quote": "1.4.1",
+ "stack-mapper": "0.2.2",
+ "stacktrace-js": "1.3.1",
+ "superagent": "0.15.7",
+ "tap-finished": "0.0.1",
+ "tap-parser": "0.7.0",
+ "watchify": "3.7.0",
+ "wd": "0.3.11",
+ "xtend": "2.1.2",
+ "yamljs": "0.2.8",
+ "zuul-localtunnel": "1.1.0"
+ },
+ "bin": {
+ "zuul": "bin/zuul"
+ }
+ },
+ "node_modules/zuul-localtunnel": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "localtunnel": "1.5.0"
+ }
+ },
+ "node_modules/zuul/node_modules/acorn": {
+ "version": "5.7.4",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/zuul/node_modules/assert": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "util": "0.10.3"
+ }
+ },
+ "node_modules/zuul/node_modules/assert/node_modules/inherits": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/zuul/node_modules/assert/node_modules/util": {
+ "version": "0.10.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "2.0.1"
+ }
+ },
+ "node_modules/zuul/node_modules/browser-resolve": {
+ "version": "1.11.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve": "1.1.7"
+ }
+ },
+ "node_modules/zuul/node_modules/browserify": {
+ "version": "13.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert": "~1.3.0",
+ "browser-pack": "^6.0.1",
+ "browser-resolve": "^1.11.0",
+ "browserify-zlib": "~0.1.2",
+ "buffer": "^4.1.0",
+ "concat-stream": "~1.5.1",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "~1.0.0",
+ "crypto-browserify": "^3.0.0",
+ "defined": "^1.0.0",
+ "deps-sort": "^2.0.0",
+ "domain-browser": "~1.1.0",
+ "duplexer2": "~0.1.2",
+ "events": "~1.1.0",
+ "glob": "^5.0.15",
+ "has": "^1.0.0",
+ "htmlescape": "^1.1.0",
+ "https-browserify": "~0.0.0",
+ "inherits": "~2.0.1",
+ "insert-module-globals": "^7.0.0",
+ "isarray": "0.0.1",
+ "JSONStream": "^1.0.3",
+ "labeled-stream-splicer": "^2.0.0",
+ "module-deps": "^4.0.2",
+ "os-browserify": "~0.1.1",
+ "parents": "^1.0.1",
+ "path-browserify": "~0.0.0",
+ "process": "~0.11.0",
+ "punycode": "^1.3.2",
+ "querystring-es3": "~0.2.0",
+ "read-only-stream": "^2.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.1.4",
+ "shasum": "^1.0.0",
+ "shell-quote": "^1.4.3",
+ "stream-browserify": "^2.0.0",
+ "stream-http": "^2.0.0",
+ "string_decoder": "~0.10.0",
+ "subarg": "^1.0.0",
+ "syntax-error": "^1.1.1",
+ "through2": "^2.0.0",
+ "timers-browserify": "^1.0.1",
+ "tty-browserify": "~0.0.0",
+ "url": "~0.11.0",
+ "util": "~0.10.1",
+ "vm-browserify": "~0.0.1",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "browserify": "bin/cmd.js"
+ }
+ },
+ "node_modules/zuul/node_modules/browserify-zlib": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~0.2.0"
+ }
+ },
+ "node_modules/zuul/node_modules/browserify/node_modules/isarray": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/browserify/node_modules/shell-quote": {
+ "version": "1.8.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/zuul/node_modules/browserify/node_modules/xtend": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/zuul/node_modules/buffer": {
+ "version": "4.9.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "node_modules/zuul/node_modules/buffer-crc32": {
+ "version": "0.2.1",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/zuul/node_modules/commander": {
+ "version": "2.1.0",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.x"
+ }
+ },
+ "node_modules/zuul/node_modules/concat-stream": {
+ "version": "1.5.2",
+ "dev": true,
+ "engines": [
+ "node >= 0.8"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.1",
+ "readable-stream": "~2.0.0",
+ "typedarray": "~0.0.5"
+ }
+ },
+ "node_modules/zuul/node_modules/concat-stream/node_modules/readable-stream": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/zuul/node_modules/convert-source-map": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/cookie": {
+ "version": "0.1.0",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/zuul/node_modules/cookie-signature": {
+ "version": "1.0.1",
+ "dev": true
+ },
+ "node_modules/zuul/node_modules/debug": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "0.6.2"
+ }
+ },
+ "node_modules/zuul/node_modules/detective": {
+ "version": "4.7.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^5.2.1",
+ "defined": "^1.0.0"
+ }
+ },
+ "node_modules/zuul/node_modules/domain-browser": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4",
+ "npm": ">=1.2"
+ }
+ },
+ "node_modules/zuul/node_modules/events": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/zuul/node_modules/express": {
+ "version": "3.4.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "0.2.1",
+ "commander": "1.3.2",
+ "connect": "2.12.0",
+ "cookie": "0.1.0",
+ "cookie-signature": "1.0.1",
+ "debug": ">= 0.7.3 < 1",
+ "fresh": "0.2.0",
+ "merge-descriptors": "0.0.1",
+ "methods": "0.1.0",
+ "mkdirp": "0.3.5",
+ "range-parser": "0.0.4",
+ "send": "0.1.4"
+ },
+ "bin": {
+ "express": "bin/express"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/zuul/node_modules/express-state": {
+ "version": "1.0.3",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.x",
+ "npm": ">=1.2.10"
+ },
+ "peerDependencies": {
+ "express": "3.x"
+ }
+ },
+ "node_modules/zuul/node_modules/express/node_modules/commander": {
+ "version": "1.3.2",
+ "dev": true,
+ "dependencies": {
+ "keypress": "0.1.x"
+ },
+ "engines": {
+ "node": ">= 0.6.x"
+ }
+ },
+ "node_modules/zuul/node_modules/express/node_modules/debug": {
+ "version": "0.8.1",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/zuul/node_modules/fresh": {
+ "version": "0.2.0",
+ "dev": true
+ },
+ "node_modules/zuul/node_modules/glob": {
+ "version": "5.0.15",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/zuul/node_modules/https-browserify": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/inherits": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/zuul/node_modules/lodash": {
+ "version": "3.10.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/merge-descriptors": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/methods": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/mime": {
+ "version": "1.2.11",
+ "dev": true
+ },
+ "node_modules/zuul/node_modules/mkdirp": {
+ "version": "0.3.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/module-deps": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browser-resolve": "^1.7.0",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "~1.5.0",
+ "defined": "^1.0.0",
+ "detective": "^4.0.0",
+ "duplexer2": "^0.1.2",
+ "inherits": "^2.0.1",
+ "JSONStream": "^1.0.3",
+ "parents": "^1.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.1.3",
+ "stream-combiner2": "^1.1.1",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0",
+ "xtend": "^4.0.0"
+ },
+ "bin": {
+ "module-deps": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/zuul/node_modules/module-deps/node_modules/xtend": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/zuul/node_modules/ms": {
+ "version": "0.6.2",
+ "dev": true
+ },
+ "node_modules/zuul/node_modules/object-keys": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/os-browserify": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/pako": {
+ "version": "0.2.9",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/path-browserify": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/process-nextick-args": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/range-parser": {
+ "version": "0.0.4",
+ "dev": true
+ },
+ "node_modules/zuul/node_modules/resolve": {
+ "version": "1.1.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/send": {
+ "version": "0.1.4",
+ "dev": true,
+ "dependencies": {
+ "debug": "*",
+ "fresh": "0.2.0",
+ "mime": "~1.2.9",
+ "range-parser": "0.0.4"
+ }
+ },
+ "node_modules/zuul/node_modules/shell-quote": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-filter": "~0.0.0",
+ "array-map": "~0.0.0",
+ "array-reduce": "~0.0.0",
+ "jsonify": "~0.0.0"
+ }
+ },
+ "node_modules/zuul/node_modules/stream-browserify": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/zuul/node_modules/stream-http": {
+ "version": "2.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/zuul/node_modules/stream-http/node_modules/xtend": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/zuul/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zuul/node_modules/util": {
+ "version": "0.10.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/zuul/node_modules/vm-browserify": {
+ "version": "0.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "indexof": "0.0.1"
+ }
+ },
+ "node_modules/zuul/node_modules/xtend": {
+ "version": "2.1.2",
+ "dev": true,
+ "dependencies": {
+ "object-keys": "~0.4.0"
+ },
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 3b1d47553..6485ae89d 100644
--- a/package.json
+++ b/package.json
@@ -1,63 +1,132 @@
{
"name": "superagent",
- "version": "1.2.0",
"description": "elegant & feature rich browser / node HTTP with a fluent API",
- "scripts": {
- "test": "make test"
- },
- "keywords": [
- "http",
- "ajax",
- "request",
- "agent"
- ],
+ "version": "10.3.0",
"author": "TJ Holowaychuk ",
+ "browser": {
+ "./src/node/index.js": "./src/client.js",
+ "./lib/node/index.js": "./lib/client.js",
+ "./test/support/server.js": "./test/support/blank.js"
+ },
+ "bugs": {
+ "url": "https://github.com/ladjs/superagent/issues"
+ },
"contributors": [
- "Hunter Loftis "
+ "Kornel Lesiński ",
+ "Peter Lyons ",
+ "Hunter Loftis ",
+ "Nick Baugh "
],
- "repository": {
- "type": "git",
- "url": "git://github.com/visionmedia/superagent.git"
- },
"dependencies": {
- "qs": "2.3.3",
- "formidable": "1.0.14",
- "mime": "1.3.4",
- "component-emitter": "1.1.2",
- "methods": "1.0.1",
- "cookiejar": "2.0.1",
- "debug": "2",
- "reduce-component": "1.0.1",
- "extend": "1.2.1",
- "form-data": "0.2.0",
- "readable-stream": "1.0.27-1"
+ "component-emitter": "^1.3.1",
+ "cookiejar": "^2.1.4",
+ "debug": "^4.3.7",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.5",
+ "formidable": "^3.5.4",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.14.1"
},
"devDependencies": {
- "basic-auth-connect": "~1.0.0",
- "better-assert": "~1.0.1",
- "body-parser": "~1.9.2",
- "cookie-parser": "~1.3.3",
- "express": "~4.9.8",
- "express-session": "~1.9.1",
- "mocha": "~2.0.1",
- "should": "~3.1.3",
- "zuul": "~1.19.0",
- "browserify": "~6.3.2",
- "Base64": "~0.3.0"
+ "@babel/cli": "^7.20.7",
+ "@babel/core": "^7.20.12",
+ "@babel/plugin-transform-runtime": "^7.19.6",
+ "@babel/preset-env": "^7.20.2",
+ "@babel/runtime": "^7.20.13",
+ "@commitlint/cli": "^19.8.1",
+ "@commitlint/config-conventional": "^19.8.1",
+ "babelify": "^10.0.0",
+ "Base64": "^1.1.0",
+ "basic-auth-connect": "^1.0.0",
+ "body-parser": "^1.20.4",
+ "browserify": "^17.0.0",
+ "cookie-parser": "^1.4.7",
+ "cross-env": "^7.0.3",
+ "eslint": "^8.32.0",
+ "eslint-config-xo-lass": "2",
+ "eslint-plugin-compat": "4.0.2",
+ "eslint-plugin-node": "^11.1.0",
+ "express": "^4.18.3",
+ "express-session": "^1.17.3",
+ "fixpack": "^4.0.0",
+ "get-port": "4.2.0",
+ "husky": "^9.1.7",
+ "lint-staged": "^15.5.2",
+ "marked": "^4.2.12",
+ "mocha": "^6.2.3",
+ "multer": "1.4.5-lts.1",
+ "nyc": "^15.1.0",
+ "remark-cli": "^11.0.0",
+ "remark-preset-github": "4.0.4",
+ "rimraf": "3",
+ "should": "^13.2.3",
+ "should-http": "^0.1.1",
+ "tinyify": "3.0.0",
+ "xo": "^0.53.1",
+ "zuul": "^3.12.0"
},
- "browser": {
- "./lib/node/index.js": "./lib/client.js",
- "emitter": "component-emitter",
- "reduce": "reduce-component",
- "./test/support/server.js": "./test/support/blank.js"
+ "engines": {
+ "node": ">=14.18.0"
+ },
+ "files": [
+ "dist/*.js",
+ "lib/**/*.js"
+ ],
+ "homepage": "https://github.com/ladjs/superagent",
+ "jsdelivr": "dist/superagent.min.js",
+ "keywords": [
+ "agent",
+ "ajax",
+ "ajax",
+ "api",
+ "async",
+ "await",
+ "axios",
+ "cancel",
+ "client",
+ "frisbee",
+ "got",
+ "http",
+ "http",
+ "https",
+ "ky",
+ "promise",
+ "promise",
+ "promises",
+ "request",
+ "request",
+ "requests",
+ "response",
+ "rest",
+ "retry",
+ "super",
+ "superagent",
+ "timeout",
+ "transform",
+ "xhr",
+ "xmlhttprequest"
+ ],
+ "license": "MIT",
+ "main": "lib/node/index.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/ladjs/superagent.git"
},
- "component": {
- "scripts": {
- "superagent": "lib/client.js"
- }
+ "scripts": {
+ "browserify": "browserify src/node/index.js -o dist/superagent.js -s superagent -g [ babelify --configFile ./.dist.babelrc ]",
+ "build": "npm run build:clean && npm run build:lib && npm run build:dist",
+ "build:clean": "rimraf lib dist",
+ "build:dist": "npm run browserify && npm run minify",
+ "build:lib": "babel --config-file ./.lib.babelrc src --out-dir lib",
+ "build:test": "babel --config-file ./.test.babelrc test --out-dir lib/node/test",
+ "coverage": "nyc report --reporter=text-lcov > coverage.lcov",
+ "lint": "eslint -c .eslintrc src test && remark . -qfo && eslint -c .lib.eslintrc lib/**/*.js && eslint -c .dist.eslintrc dist/**/*.js",
+ "minify": "cross-env NODE_ENV=production browserify src/node/index.js -o dist/superagent.min.js -s superagent -g [ babelify --configFile ./.dist.babelrc ] -p tinyify",
+ "nyc": "cross-env NODE_ENV=test nyc ava",
+ "test": "npm run build && npm run lint && make test",
+ "test-http2": "npm run build && npm run lint && make test-node-http2",
+ "prepare": "husky"
},
- "main": "./lib/node/index.js",
- "engines": {
- "node": ">= 0.8"
- }
-}
\ No newline at end of file
+ "unpkg": "dist/superagent.min.js"
+}
diff --git a/src/agent-base.js b/src/agent-base.js
new file mode 100644
index 000000000..af94779e7
--- /dev/null
+++ b/src/agent-base.js
@@ -0,0 +1,47 @@
+const defaults = [
+ 'use',
+ 'on',
+ 'once',
+ 'set',
+ 'query',
+ 'type',
+ 'accept',
+ 'auth',
+ 'withCredentials',
+ 'sortQuery',
+ 'retry',
+ 'ok',
+ 'redirects',
+ 'timeout',
+ 'buffer',
+ 'serialize',
+ 'parse',
+ 'ca',
+ 'key',
+ 'pfx',
+ 'cert',
+ 'disableTLSCerts'
+]
+
+class Agent {
+ constructor () {
+ this._defaults = [];
+ }
+
+ _setDefaults (request) {
+ for (const def of this._defaults) {
+ request[def.fn](...def.args);
+ }
+ }
+}
+
+for (const fn of defaults) {
+ // Default setting for all requests from this agent
+ Agent.prototype[fn] = function (...args) {
+ this._defaults.push({ fn, args });
+ return this;
+ };
+}
+
+
+module.exports = Agent;
diff --git a/src/client.js b/src/client.js
new file mode 100644
index 000000000..e85e59bda
--- /dev/null
+++ b/src/client.js
@@ -0,0 +1,1053 @@
+/**
+ * Root reference for iframes.
+ */
+
+let root;
+if (typeof window !== 'undefined') {
+ // Browser window
+ root = window;
+} else if (typeof self === 'undefined') {
+ // Other environments
+ console.warn(
+ 'Using browser-only version of superagent in non-browser environment'
+ );
+ root = this;
+} else {
+ // Web Worker
+ root = self;
+}
+
+const Emitter = require('component-emitter');
+const safeStringify = require('fast-safe-stringify');
+const qs = require('qs');
+const RequestBase = require('./request-base');
+const { isObject, mixin, hasOwn } = require('./utils');
+const ResponseBase = require('./response-base');
+const Agent = require('./agent-base');
+
+/**
+ * Noop.
+ */
+
+function noop() {}
+
+/**
+ * Expose `request`.
+ */
+
+module.exports = function (method, url) {
+ // callback
+ if (typeof url === 'function') {
+ return new exports.Request('GET', method).end(url);
+ }
+
+ // url first
+ if (arguments.length === 1) {
+ return new exports.Request('GET', method);
+ }
+
+ return new exports.Request(method, url);
+};
+
+exports = module.exports;
+
+const request = exports;
+
+exports.Request = Request;
+
+/**
+ * Determine XHR.
+ */
+
+request.getXHR = () => {
+ if (root.XMLHttpRequest) {
+ return new root.XMLHttpRequest();
+ }
+
+ throw new Error('Browser-only version of superagent could not find XHR');
+};
+
+/**
+ * Removes leading and trailing whitespace, added to support IE.
+ *
+ * @param {String} s
+ * @return {String}
+ * @api private
+ */
+
+const trim = ''.trim ? (s) => s.trim() : (s) => s.replace(/(^\s*|\s*$)/g, '');
+
+/**
+ * Serialize the given `obj`.
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api private
+ */
+
+function serialize(object) {
+ if (!isObject(object)) return object;
+ const pairs = [];
+ for (const key in object) {
+ if (hasOwn(object, key)) pushEncodedKeyValuePair(pairs, key, object[key]);
+ }
+
+ return pairs.join('&');
+}
+
+/**
+ * Helps 'serialize' with serializing arrays.
+ * Mutates the pairs array.
+ *
+ * @param {Array} pairs
+ * @param {String} key
+ * @param {Mixed} val
+ */
+
+function pushEncodedKeyValuePair(pairs, key, value) {
+ if (value === undefined) return;
+ if (value === null) {
+ pairs.push(encodeURI(key));
+ return;
+ }
+
+ if (Array.isArray(value)) {
+ for (const v of value) {
+ pushEncodedKeyValuePair(pairs, key, v);
+ }
+ } else if (isObject(value)) {
+ for (const subkey in value) {
+ if (hasOwn(value, subkey))
+ pushEncodedKeyValuePair(pairs, `${key}[${subkey}]`, value[subkey]);
+ }
+ } else {
+ pairs.push(encodeURI(key) + '=' + encodeURIComponent(value));
+ }
+}
+
+/**
+ * Expose serialization method.
+ */
+
+request.serializeObject = serialize;
+
+/**
+ * Parse the given x-www-form-urlencoded `str`.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+function parseString(string_) {
+ const object = {};
+ const pairs = string_.split('&');
+ let pair;
+ let pos;
+
+ for (let i = 0, length_ = pairs.length; i < length_; ++i) {
+ pair = pairs[i];
+ pos = pair.indexOf('=');
+ if (pos === -1) {
+ object[decodeURIComponent(pair)] = '';
+ } else {
+ object[decodeURIComponent(pair.slice(0, pos))] = decodeURIComponent(
+ pair.slice(pos + 1)
+ );
+ }
+ }
+
+ return object;
+}
+
+/**
+ * Expose parser.
+ */
+
+request.parseString = parseString;
+
+/**
+ * Default MIME type map.
+ *
+ * superagent.types.xml = 'application/xml';
+ *
+ */
+
+request.types = {
+ html: 'text/html',
+ json: 'application/json',
+ xml: 'text/xml',
+ urlencoded: 'application/x-www-form-urlencoded',
+ form: 'application/x-www-form-urlencoded',
+ 'form-data': 'application/x-www-form-urlencoded'
+};
+
+/**
+ * Default serialization map.
+ *
+ * superagent.serialize['application/xml'] = function(obj){
+ * return 'generated xml here';
+ * };
+ *
+ */
+
+request.serialize = {
+ 'application/x-www-form-urlencoded': (obj) => {
+ return qs.stringify(obj, { indices: false, strictNullHandling: true });
+ },
+ 'application/json': safeStringify
+};
+
+/**
+ * Default parsers.
+ *
+ * superagent.parse['application/xml'] = function(str){
+ * return { object parsed from str };
+ * };
+ *
+ */
+
+request.parse = {
+ 'application/x-www-form-urlencoded': parseString,
+ 'application/json': JSON.parse
+};
+
+/**
+ * Parse the given header `str` into
+ * an object containing the mapped fields.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+function parseHeader(string_) {
+ const lines = string_.split(/\r?\n/);
+ const fields = {};
+ let index;
+ let line;
+ let field;
+ let value;
+
+ for (let i = 0, length_ = lines.length; i < length_; ++i) {
+ line = lines[i];
+ index = line.indexOf(':');
+ if (index === -1) {
+ // could be empty line, just skip it
+ continue;
+ }
+
+ field = line.slice(0, index).toLowerCase();
+ value = trim(line.slice(index + 1));
+ fields[field] = value;
+ }
+
+ return fields;
+}
+
+/**
+ * Check if `mime` is json or has +json structured syntax suffix.
+ *
+ * @param {String} mime
+ * @return {Boolean}
+ * @api private
+ */
+
+function isJSON(mime) {
+ // should match /json or +json
+ // but not /json-seq
+ return /[/+]json($|[^-\w])/i.test(mime);
+}
+
+/**
+ * Initialize a new `Response` with the given `xhr`.
+ *
+ * - set flags (.ok, .error, etc)
+ * - parse header
+ *
+ * Examples:
+ *
+ * Aliasing `superagent` as `request` is nice:
+ *
+ * request = superagent;
+ *
+ * We can use the promise-like API, or pass callbacks:
+ *
+ * request.get('/').end(function(res){});
+ * request.get('/', function(res){});
+ *
+ * Sending data can be chained:
+ *
+ * request
+ * .post('/user')
+ * .send({ name: 'tj' })
+ * .end(function(res){});
+ *
+ * Or passed to `.send()`:
+ *
+ * request
+ * .post('/user')
+ * .send({ name: 'tj' }, function(res){});
+ *
+ * Or passed to `.post()`:
+ *
+ * request
+ * .post('/user', { name: 'tj' })
+ * .end(function(res){});
+ *
+ * Or further reduced to a single call for simple cases:
+ *
+ * request
+ * .post('/user', { name: 'tj' }, function(res){});
+ *
+ * @param {XMLHTTPRequest} xhr
+ * @param {Object} options
+ * @api private
+ */
+
+function Response(request_) {
+ this.req = request_;
+ this.xhr = this.req.xhr;
+ // responseText is accessible only if responseType is '' or 'text' and on older browsers
+ this.text =
+ (this.req.method !== 'HEAD' &&
+ (this.xhr.responseType === '' || this.xhr.responseType === 'text')) ||
+ typeof this.xhr.responseType === 'undefined'
+ ? this.xhr.responseText
+ : null;
+ this.statusText = this.req.xhr.statusText;
+ let { status } = this.xhr;
+ // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
+ if (status === 1223) {
+ status = 204;
+ }
+
+ this._setStatusProperties(status);
+ this.headers = parseHeader(this.xhr.getAllResponseHeaders());
+ this.header = this.headers;
+ // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but
+ // getResponseHeader still works. so we get content-type even if getting
+ // other headers fails.
+ this.header['content-type'] = this.xhr.getResponseHeader('content-type');
+ this._setHeaderProperties(this.header);
+
+ if (this.text === null && request_._responseType) {
+ this.body = this.xhr.response;
+ } else {
+ this.body =
+ this.req.method === 'HEAD'
+ ? null
+ : this._parseBody(this.text ? this.text : this.xhr.response);
+ }
+}
+
+mixin(Response.prototype, ResponseBase.prototype);
+
+/**
+ * Parse the given body `str`.
+ *
+ * Used for auto-parsing of bodies. Parsers
+ * are defined on the `superagent.parse` object.
+ *
+ * @param {String} str
+ * @return {Mixed}
+ * @api private
+ */
+
+Response.prototype._parseBody = function (string_) {
+ let parse = request.parse[this.type];
+ if (this.req._parser) {
+ return this.req._parser(this, string_);
+ }
+
+ if (!parse && isJSON(this.type)) {
+ parse = request.parse['application/json'];
+ }
+
+ return parse && string_ && (string_.length > 0 || string_ instanceof Object)
+ ? parse(string_)
+ : null;
+};
+
+/**
+ * Return an `Error` representative of this response.
+ *
+ * @return {Error}
+ * @api public
+ */
+
+Response.prototype.toError = function () {
+ const { req } = this;
+ const { method } = req;
+ const { url } = req;
+
+ const message = `cannot ${method} ${url} (${this.status})`;
+ const error = new Error(message);
+ error.status = this.status;
+ error.method = method;
+ error.url = url;
+
+ return error;
+};
+
+/**
+ * Expose `Response`.
+ */
+
+request.Response = Response;
+
+/**
+ * Initialize a new `Request` with the given `method` and `url`.
+ *
+ * @param {String} method
+ * @param {String} url
+ * @api public
+ */
+
+function Request(method, url) {
+ const self = this;
+ this._query = this._query || [];
+ this.method = method;
+ this.url = url;
+ this.header = {}; // preserves header name case
+ this._header = {}; // coerces header names to lowercase
+ this.on('end', () => {
+ let error = null;
+ let res = null;
+
+ try {
+ res = new Response(self);
+ } catch (err) {
+ error = new Error('Parser is unable to parse the response');
+ error.parse = true;
+ error.original = err;
+ // issue #675: return the raw response if the response parsing fails
+ if (self.xhr) {
+ // ie9 doesn't have 'response' property
+ error.rawResponse =
+ typeof self.xhr.responseType === 'undefined'
+ ? self.xhr.responseText
+ : self.xhr.response;
+ // issue #876: return the http status code if the response parsing fails
+ error.status = self.xhr.status ? self.xhr.status : null;
+ error.statusCode = error.status; // backwards-compat only
+ } else {
+ error.rawResponse = null;
+ error.status = null;
+ }
+
+ return self.callback(error);
+ }
+
+ self.emit('response', res);
+
+ let new_error;
+ try {
+ if (!self._isResponseOK(res)) {
+ new_error = new Error(
+ res.statusText || res.text || 'Unsuccessful HTTP response'
+ );
+ }
+ } catch (err) {
+ new_error = err; // ok() callback can throw
+ }
+
+ // #1000 don't catch errors from the callback to avoid double calling it
+ if (new_error) {
+ new_error.original = error;
+ new_error.response = res;
+ new_error.status = new_error.status || res.status;
+ self.callback(new_error, res);
+ } else {
+ self.callback(null, res);
+ }
+ });
+}
+
+/**
+ * Mixin `Emitter` and `RequestBase`.
+ */
+
+// eslint-disable-next-line new-cap
+Emitter(Request.prototype);
+
+mixin(Request.prototype, RequestBase.prototype);
+
+/**
+ * Set Content-Type to `type`, mapping values from `request.types`.
+ *
+ * Examples:
+ *
+ * superagent.types.xml = 'application/xml';
+ *
+ * request.post('/')
+ * .type('xml')
+ * .send(xmlstring)
+ * .end(callback);
+ *
+ * request.post('/')
+ * .type('application/xml')
+ * .send(xmlstring)
+ * .end(callback);
+ *
+ * @param {String} type
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.type = function (type) {
+ this.set('Content-Type', request.types[type] || type);
+ return this;
+};
+
+/**
+ * Set Accept to `type`, mapping values from `request.types`.
+ *
+ * Examples:
+ *
+ * superagent.types.json = 'application/json';
+ *
+ * request.get('/agent')
+ * .accept('json')
+ * .end(callback);
+ *
+ * request.get('/agent')
+ * .accept('application/json')
+ * .end(callback);
+ *
+ * @param {String} accept
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.accept = function (type) {
+ this.set('Accept', request.types[type] || type);
+ return this;
+};
+
+/**
+ * Set Authorization field value with `user` and `pass`.
+ *
+ * @param {String} user
+ * @param {String} [pass] optional in case of using 'bearer' as type
+ * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic')
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.auth = function (user, pass, options) {
+ if (arguments.length === 1) pass = '';
+ if (typeof pass === 'object' && pass !== null) {
+ // pass is optional and can be replaced with options
+ options = pass;
+ pass = '';
+ }
+
+ if (!options) {
+ options = {
+ type: typeof btoa === 'function' ? 'basic' : 'auto'
+ };
+ }
+
+ const encoder = options.encoder
+ ? options.encoder
+ : (string) => {
+ if (typeof btoa === 'function') {
+ return btoa(string);
+ }
+
+ throw new Error('Cannot use basic auth, btoa is not a function');
+ };
+
+ return this._auth(user, pass, options, encoder);
+};
+
+/**
+ * Add query-string `val`.
+ *
+ * Examples:
+ *
+ * request.get('/shoes')
+ * .query('size=10')
+ * .query({ color: 'blue' })
+ *
+ * @param {Object|String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.query = function (value) {
+ if (typeof value !== 'string') value = serialize(value);
+ if (value) this._query.push(value);
+ return this;
+};
+
+/**
+ * Queue the given `file` as an attachment to the specified `field`,
+ * with optional `options` (or filename).
+ *
+ * ``` js
+ * request.post('/upload')
+ * .attach('content', new Blob(['hey! '], { type: "text/html"}))
+ * .end(callback);
+ * ```
+ *
+ * @param {String} field
+ * @param {Blob|File} file
+ * @param {String|Object} options
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.attach = function (field, file, options) {
+ if (file) {
+ if (this._data) {
+ throw new Error("superagent can't mix .send() and .attach()");
+ }
+
+ this._getFormData().append(field, file, options || file.name);
+ }
+
+ return this;
+};
+
+Request.prototype._getFormData = function () {
+ if (!this._formData) {
+ this._formData = new root.FormData();
+ }
+
+ return this._formData;
+};
+
+/**
+ * Invoke the callback with `err` and `res`
+ * and handle arity check.
+ *
+ * @param {Error} err
+ * @param {Response} res
+ * @api private
+ */
+
+Request.prototype.callback = function (error, res) {
+ if (this._shouldRetry(error, res)) {
+ return this._retry();
+ }
+
+ const fn = this._callback;
+ this.clearTimeout();
+
+ if (error) {
+ if (this._maxRetries) error.retries = this._retries - 1;
+ this.emit('error', error);
+ }
+
+ fn(error, res);
+};
+
+/**
+ * Invoke callback with x-domain error.
+ *
+ * @api private
+ */
+
+Request.prototype.crossDomainError = function () {
+ const error = new Error(
+ 'Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.'
+ );
+ error.crossDomain = true;
+
+ error.status = this.status;
+ error.method = this.method;
+ error.url = this.url;
+
+ this.callback(error);
+};
+
+// This only warns, because the request is still likely to work
+Request.prototype.agent = function () {
+ console.warn('This is not supported in browser version of superagent');
+ return this;
+};
+
+Request.prototype.ca = Request.prototype.agent;
+Request.prototype.buffer = Request.prototype.ca;
+
+// This throws, because it can't send/receive data as expected
+Request.prototype.write = () => {
+ throw new Error(
+ 'Streaming is not supported in browser version of superagent'
+ );
+};
+
+Request.prototype.pipe = Request.prototype.write;
+
+/**
+ * Check if `obj` is a host object,
+ * we don't want to serialize these :)
+ *
+ * @param {Object} obj host object
+ * @return {Boolean} is a host object
+ * @api private
+ */
+Request.prototype._isHost = function (object) {
+ // Native objects stringify to [object File], [object Blob], [object FormData], etc.
+ return (
+ object &&
+ typeof object === 'object' &&
+ !Array.isArray(object) &&
+ Object.prototype.toString.call(object) !== '[object Object]'
+ );
+};
+
+/**
+ * Initiate request, invoking callback `fn(res)`
+ * with an instanceof `Response`.
+ *
+ * @param {Function} fn
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.end = function (fn) {
+ if (this._endCalled) {
+ console.warn(
+ 'Warning: .end() was called twice. This is not supported in superagent'
+ );
+ }
+
+ this._endCalled = true;
+
+ // store callback
+ this._callback = fn || noop;
+
+ // querystring
+ this._finalizeQueryString();
+
+ this._end();
+};
+
+Request.prototype._setUploadTimeout = function () {
+ const self = this;
+
+ // upload timeout it's wokrs only if deadline timeout is off
+ if (this._uploadTimeout && !this._uploadTimeoutTimer) {
+ this._uploadTimeoutTimer = setTimeout(() => {
+ self._timeoutError(
+ 'Upload timeout of ',
+ self._uploadTimeout,
+ 'ETIMEDOUT'
+ );
+ }, this._uploadTimeout);
+ }
+};
+
+// eslint-disable-next-line complexity
+Request.prototype._end = function () {
+ if (this._aborted)
+ return this.callback(
+ new Error('The request has been aborted even before .end() was called')
+ );
+
+ const self = this;
+ this.xhr = request.getXHR();
+ const { xhr } = this;
+ let data = this._formData || this._data;
+
+ this._setTimeouts();
+
+ // state change
+ xhr.addEventListener('readystatechange', () => {
+ const { readyState } = xhr;
+ if (readyState >= 2 && self._responseTimeoutTimer) {
+ clearTimeout(self._responseTimeoutTimer);
+ }
+
+ if (readyState !== 4) {
+ return;
+ }
+
+ // In IE9, reads to any property (e.g. status) off of an aborted XHR will
+ // result in the error "Could not complete the operation due to error c00c023f"
+ let status;
+ try {
+ status = xhr.status;
+ } catch (err) {
+ status = 0;
+ }
+
+ if (!status) {
+ if (self.timedout || self._aborted) return;
+ return self.crossDomainError();
+ }
+
+ self.emit('end');
+ });
+
+ // progress
+ const handleProgress = (direction, e) => {
+ if (e.total > 0) {
+ e.percent = (e.loaded / e.total) * 100;
+
+ if (e.percent === 100) {
+ clearTimeout(self._uploadTimeoutTimer);
+ }
+ }
+
+ e.direction = direction;
+ self.emit('progress', e);
+ };
+
+ if (this.hasListeners('progress')) {
+ try {
+ xhr.addEventListener('progress', handleProgress.bind(null, 'download'));
+ if (xhr.upload) {
+ xhr.upload.addEventListener(
+ 'progress',
+ handleProgress.bind(null, 'upload')
+ );
+ }
+ } catch (err) {
+ // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
+ // Reported here:
+ // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
+ }
+ }
+
+ if (xhr.upload) {
+ this._setUploadTimeout();
+ }
+
+ // initiate request
+ try {
+ if (this.username && this.password) {
+ xhr.open(this.method, this.url, true, this.username, this.password);
+ } else {
+ xhr.open(this.method, this.url, true);
+ }
+ } catch (err) {
+ // see #1149
+ return this.callback(err);
+ }
+
+ // CORS
+ if (this._withCredentials) xhr.withCredentials = true;
+
+ // body
+ if (
+ !this._formData &&
+ this.method !== 'GET' &&
+ this.method !== 'HEAD' &&
+ typeof data !== 'string' &&
+ !this._isHost(data)
+ ) {
+ // serialize stuff
+ const contentType = this._header['content-type'];
+ let serialize =
+ this._serializer ||
+ request.serialize[contentType ? contentType.split(';')[0] : ''];
+ if (!serialize && isJSON(contentType)) {
+ serialize = request.serialize['application/json'];
+ }
+
+ if (serialize) data = serialize(data);
+ }
+
+ // set header fields
+ for (const field in this.header) {
+ if (this.header[field] === null) continue;
+
+ if (hasOwn(this.header, field))
+ xhr.setRequestHeader(field, this.header[field]);
+ }
+
+ if (this._responseType) {
+ xhr.responseType = this._responseType;
+ }
+
+ // send stuff
+ this.emit('request', this);
+
+ // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing)
+ // We need null here if data is undefined
+ xhr.send(typeof data === 'undefined' ? null : data);
+};
+
+// create a Proxy that can instantiate a new Agent without using `new` keyword
+// (for backward compatibility and chaining)
+const proxyAgent = new Proxy(Agent, {
+ apply(target, thisArg, argumentsList) {
+ return new target(...argumentsList);
+ }
+});
+request.agent = proxyAgent;
+
+for (const method of ['GET', 'POST', 'OPTIONS', 'PATCH', 'PUT', 'DELETE']) {
+ Agent.prototype[method.toLowerCase()] = function (url, fn) {
+ const request_ = new request.Request(method, url);
+ this._setDefaults(request_);
+ if (fn) {
+ request_.end(fn);
+ }
+
+ return request_;
+ };
+}
+
+Agent.prototype.del = Agent.prototype.delete;
+
+/**
+ * GET `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+request.get = (url, data, fn) => {
+ const request_ = request('GET', url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) request_.query(data);
+ if (fn) request_.end(fn);
+ return request_;
+};
+
+/**
+ * HEAD `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+request.head = (url, data, fn) => {
+ const request_ = request('HEAD', url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) request_.query(data);
+ if (fn) request_.end(fn);
+ return request_;
+};
+
+/**
+ * OPTIONS query to `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+request.options = (url, data, fn) => {
+ const request_ = request('OPTIONS', url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) request_.send(data);
+ if (fn) request_.end(fn);
+ return request_;
+};
+
+/**
+ * DELETE `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} [data]
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+function del(url, data, fn) {
+ const request_ = request('DELETE', url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) request_.send(data);
+ if (fn) request_.end(fn);
+ return request_;
+}
+
+request.del = del;
+request.delete = del;
+
+/**
+ * PATCH `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} [data]
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+request.patch = (url, data, fn) => {
+ const request_ = request('PATCH', url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) request_.send(data);
+ if (fn) request_.end(fn);
+ return request_;
+};
+
+/**
+ * POST `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} [data]
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+request.post = (url, data, fn) => {
+ const request_ = request('POST', url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) request_.send(data);
+ if (fn) request_.end(fn);
+ return request_;
+};
+
+/**
+ * PUT `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+request.put = (url, data, fn) => {
+ const request_ = request('PUT', url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) request_.send(data);
+ if (fn) request_.end(fn);
+ return request_;
+};
diff --git a/src/node/agent.js b/src/node/agent.js
new file mode 100644
index 000000000..e27f89188
--- /dev/null
+++ b/src/node/agent.js
@@ -0,0 +1,109 @@
+/**
+ * Module dependencies.
+ */
+
+const { CookieJar } = require('cookiejar');
+const { CookieAccessInfo } = require('cookiejar');
+const methods = require('methods');
+const request = require('../..');
+const AgentBase = require('../agent-base');
+
+/**
+ * Initialize a new `Agent`.
+ *
+ * @api public
+ */
+
+class Agent extends AgentBase {
+ constructor (options) {
+ super();
+
+ this.jar = new CookieJar();
+
+ if (options) {
+ if (options.ca) {
+ this.ca(options.ca);
+ }
+
+ if (options.key) {
+ this.key(options.key);
+ }
+
+ if (options.pfx) {
+ this.pfx(options.pfx);
+ }
+
+ if (options.cert) {
+ this.cert(options.cert);
+ }
+
+ if (options.rejectUnauthorized === false) {
+ this.disableTLSCerts();
+ }
+ }
+ }
+
+ /**
+ * Save the cookies in the given `res` to
+ * the agent's cookie jar for persistence.
+ *
+ * @param {Response} res
+ * @api private
+ */
+ _saveCookies (res) {
+ const cookies = res.headers['set-cookie'];
+ if (cookies) {
+ const url = new URL(res.request?.url || '');
+ this.jar.setCookies(cookies, url.hostname, url.pathname);
+ }
+ }
+
+ /**
+ * Attach cookies when available to the given `req`.
+ *
+ * @param {Request} req
+ * @api private
+ */
+ _attachCookies (request_) {
+ const url = new URL(request_.url);
+ const access = new CookieAccessInfo(
+ url.hostname,
+ url.pathname,
+ url.protocol === 'https:'
+ );
+ const cookies = this.jar.getCookies(access).toValueString();
+ request_.cookies = cookies;
+ }
+}
+
+for (const name of methods) {
+ const method = name.toUpperCase();
+ if (method === "QUERY") continue;
+ Agent.prototype[name] = function (url, fn) {
+ const request_ = new request.Request(method, url);
+
+ request_.on('response', this._saveCookies.bind(this));
+ request_.on('redirect', this._saveCookies.bind(this));
+ request_.on('redirect', this._attachCookies.bind(this, request_));
+ this._setDefaults(request_);
+ this._attachCookies(request_);
+
+ if (fn) {
+ request_.end(fn);
+ }
+
+ return request_;
+ };
+}
+
+Agent.prototype.del = Agent.prototype.delete;
+
+// create a Proxy that can instantiate a new Agent without using `new` keyword
+// (for backward compatibility and chaining)
+const proxyAgent = new Proxy(Agent, {
+ apply (target, thisArg, argumentsList) {
+ return new target(...argumentsList);
+ }
+});
+
+module.exports = proxyAgent;
diff --git a/src/node/decompress.js b/src/node/decompress.js
new file mode 100644
index 000000000..81de84412
--- /dev/null
+++ b/src/node/decompress.js
@@ -0,0 +1,15 @@
+const zlib = require('zlib');
+const utils = require('../utils');
+const { isGzipOrDeflateEncoding, isBrotliEncoding } = utils;
+
+exports.chooseDecompresser = (res) => {
+ let decompresser;
+ if (isGzipOrDeflateEncoding(res)) {
+ decompresser = zlib.createUnzip();
+ } else if (isBrotliEncoding(res)) {
+ decompresser = zlib.createBrotliDecompress();
+ } else {
+ throw new Error('unknown content-encoding');
+ }
+ return decompresser;
+}
diff --git a/src/node/http2wrapper.js b/src/node/http2wrapper.js
new file mode 100644
index 000000000..0605abd82
--- /dev/null
+++ b/src/node/http2wrapper.js
@@ -0,0 +1,194 @@
+const http2 = require('http2');
+const Stream = require('stream');
+const net = require('net');
+const tls = require('tls');
+
+const {
+ HTTP2_HEADER_PATH,
+ HTTP2_HEADER_STATUS,
+ HTTP2_HEADER_METHOD,
+ HTTP2_HEADER_AUTHORITY,
+ HTTP2_HEADER_HOST,
+ HTTP2_HEADER_SET_COOKIE,
+ NGHTTP2_CANCEL
+} = http2.constants;
+
+function setProtocol(protocol) {
+ return {
+ request(options) {
+ return new Request(protocol, options);
+ }
+ };
+}
+
+function normalizeIpv6Host(host) {
+ return net.isIP(host) === 6 ? `[${host}]` : host;
+}
+
+class Request extends Stream {
+ constructor(protocol, options) {
+ super();
+ const defaultPort = protocol === 'https:' ? 443 : 80;
+ const defaultHost = 'localhost';
+ const port = options.port || defaultPort;
+ const host = options.host || defaultHost;
+
+ delete options.port;
+ delete options.host;
+
+ this.method = options.method;
+ this.path = options.path;
+ this.protocol = protocol;
+ this.host = host;
+
+ delete options.method;
+ delete options.path;
+
+ const sessionOptions = { ...options };
+ if (options.socketPath) {
+ sessionOptions.socketPath = options.socketPath;
+ sessionOptions.createConnection = this.createUnixConnection.bind(this);
+ }
+
+ this._headers = {};
+
+ const normalizedHost = normalizeIpv6Host(host);
+ const session = http2.connect(
+ `${protocol}//${normalizedHost}:${port}`,
+ sessionOptions
+ );
+ this.setHeader('host', `${normalizedHost}:${port}`);
+
+ session.on('error', (error) => this.emit('error', error));
+
+ this.session = session;
+ }
+
+ createUnixConnection(authority, options) {
+ switch (this.protocol) {
+ case 'http:':
+ return net.connect(options.socketPath);
+ case 'https:':
+ options.ALPNProtocols = ['h2'];
+ options.servername = this.host;
+ options.allowHalfOpen = true;
+ return tls.connect(options.socketPath, options);
+ default:
+ throw new Error('Unsupported protocol', this.protocol);
+ }
+ }
+
+ setNoDelay(bool) {
+ // We can not use setNoDelay with HTTP/2.
+ // Node 10 limits http2session.socket methods to ones safe to use with HTTP/2.
+ // See also https://nodejs.org/api/http2.html#http2_http2session_socket
+ }
+
+ getFrame() {
+ if (this.frame) {
+ return this.frame;
+ }
+
+ const method = {
+ [HTTP2_HEADER_PATH]: this.path,
+ [HTTP2_HEADER_METHOD]: this.method
+ };
+
+ let headers = this.mapToHttp2Header(this._headers);
+
+ headers = Object.assign(headers, method);
+
+ const frame = this.session.request(headers);
+
+ frame.once('response', (headers, flags) => {
+ headers = this.mapToHttpHeader(headers);
+ frame.headers = headers;
+ frame.statusCode = headers[HTTP2_HEADER_STATUS];
+ frame.status = frame.statusCode;
+ this.emit('response', frame);
+ });
+
+ this._headerSent = true;
+
+ frame.once('drain', () => this.emit('drain'));
+ frame.on('error', (error) => this.emit('error', error));
+ frame.on('close', () => this.session.close());
+
+ this.frame = frame;
+ return frame;
+ }
+
+ mapToHttpHeader(headers) {
+ const keys = Object.keys(headers);
+ const http2Headers = {};
+ for (let key of keys) {
+ let value = headers[key];
+ key = key.toLowerCase();
+ switch (key) {
+ case HTTP2_HEADER_SET_COOKIE:
+ value = Array.isArray(value) ? value : [value];
+ break;
+ default:
+ break;
+ }
+
+ http2Headers[key] = value;
+ }
+
+ return http2Headers;
+ }
+
+ mapToHttp2Header(headers) {
+ const keys = Object.keys(headers);
+ const http2Headers = {};
+ for (let key of keys) {
+ let value = headers[key];
+ key = key.toLowerCase();
+ switch (key) {
+ case HTTP2_HEADER_HOST:
+ key = HTTP2_HEADER_AUTHORITY;
+ value = /^http:\/\/|^https:\/\//.test(value)
+ ? new URL(value).host
+ : value;
+ break;
+ default:
+ break;
+ }
+
+ http2Headers[key] = value;
+ }
+
+ return http2Headers;
+ }
+
+ setHeader(name, value) {
+ this._headers[name.toLowerCase()] = value;
+ }
+
+ getHeader(name) {
+ return this._headers[name.toLowerCase()];
+ }
+
+ write(data, encoding) {
+ const frame = this.getFrame();
+ return frame.write(data, encoding);
+ }
+
+ pipe(stream, options) {
+ const frame = this.getFrame();
+ return frame.pipe(stream, options);
+ }
+
+ end(data) {
+ const frame = this.getFrame();
+ frame.end(data);
+ }
+
+ abort(data) {
+ const frame = this.getFrame();
+ frame.close(NGHTTP2_CANCEL);
+ this.session.destroy();
+ }
+}
+
+exports.setProtocol = setProtocol;
diff --git a/src/node/index.js b/src/node/index.js
new file mode 100644
index 000000000..c21231ad7
--- /dev/null
+++ b/src/node/index.js
@@ -0,0 +1,1435 @@
+/**
+ * Module dependencies.
+ */
+
+const { format } = require('url');
+const Stream = require('stream');
+const https = require('https');
+const http = require('http');
+const fs = require('fs');
+const zlib = require('zlib');
+const util = require('util');
+const qs = require('qs');
+const mime = require('mime');
+let methods = require('methods');
+const FormData = require('form-data');
+const formidable = require('formidable');
+const debug = require('debug')('superagent');
+const CookieJar = require('cookiejar');
+const safeStringify = require('fast-safe-stringify');
+
+const utils = require('../utils');
+const RequestBase = require('../request-base');
+const http2 = require('./http2wrapper');
+const { decompress } = require('./unzip');
+const Response = require('./response');
+
+const { mixin, hasOwn, isBrotliEncoding, isGzipOrDeflateEncoding } = utils;
+const { chooseDecompresser } = require('./decompress');
+
+function request(method, url) {
+ // callback
+ if (typeof url === 'function') {
+ return new exports.Request('GET', method).end(url);
+ }
+
+ // url first
+ if (arguments.length === 1) {
+ return new exports.Request('GET', method);
+ }
+
+ return new exports.Request(method, url);
+}
+
+module.exports = request;
+exports = module.exports;
+
+/**
+ * Expose `Request`.
+ */
+
+exports.Request = Request;
+
+/**
+ * Expose the agent function
+ */
+
+exports.agent = require('./agent');
+
+/**
+ * Noop.
+ */
+
+function noop() {}
+
+/**
+ * Expose `Response`.
+ */
+
+exports.Response = Response;
+
+/**
+ * Define "form" mime type.
+ */
+
+mime.define(
+ {
+ 'application/x-www-form-urlencoded': ['form', 'urlencoded', 'form-data']
+ },
+ true
+);
+
+/**
+ * Protocol map.
+ */
+
+exports.protocols = {
+ 'http:': http,
+ 'https:': https,
+ 'http2:': http2
+};
+
+/**
+ * Default serialization map.
+ *
+ * superagent.serialize['application/xml'] = function(obj){
+ * return 'generated xml here';
+ * };
+ *
+ */
+
+exports.serialize = {
+ 'application/x-www-form-urlencoded': (obj) => {
+ return qs.stringify(obj, { indices: false, strictNullHandling: true });
+ },
+ 'application/json': safeStringify
+};
+
+/**
+ * Default parsers.
+ *
+ * superagent.parse['application/xml'] = function(res, fn){
+ * fn(null, res);
+ * };
+ *
+ */
+
+exports.parse = require('./parsers');
+
+/**
+ * Default buffering map. Can be used to set certain
+ * response types to buffer/not buffer.
+ *
+ * superagent.buffer['application/xml'] = true;
+ */
+exports.buffer = {};
+
+/**
+ * Initialize internal header tracking properties on a request instance.
+ *
+ * @param {Object} req the instance
+ * @api private
+ */
+function _initHeaders(request_) {
+ request_._header = {
+ // coerces header names to lowercase
+ };
+ request_.header = {
+ // preserves header name case
+ };
+}
+
+/**
+ * Initialize a new `Request` with the given `method` and `url`.
+ *
+ * @param {String} method
+ * @param {String|Object} url
+ * @api public
+ */
+
+function Request(method, url) {
+ Stream.call(this);
+ if (typeof url !== 'string') url = format(url);
+ this._enableHttp2 = Boolean(process.env.HTTP2_TEST); // internal only
+ this._agent = false;
+ this._formData = null;
+ this.method = method;
+ this.url = url;
+ _initHeaders(this);
+ this.writable = true;
+ this._redirects = 0;
+ this.redirects(method === 'HEAD' ? 0 : 5);
+ this.cookies = '';
+ this.qs = {};
+ this._query = [];
+ this.qsRaw = this._query; // Unused, for backwards compatibility only
+ this._redirectList = [];
+ this._streamRequest = false;
+ this._lookup = undefined;
+ this.once('end', this.clearTimeout.bind(this));
+}
+
+/**
+ * Inherit from `Stream` (which inherits from `EventEmitter`).
+ * Mixin `RequestBase`.
+ */
+util.inherits(Request, Stream);
+
+mixin(Request.prototype, RequestBase.prototype);
+
+/**
+ * Enable or Disable http2.
+ *
+ * Enable http2.
+ *
+ * ``` js
+ * request.get('http://localhost/')
+ * .http2()
+ * .end(callback);
+ *
+ * request.get('http://localhost/')
+ * .http2(true)
+ * .end(callback);
+ * ```
+ *
+ * Disable http2.
+ *
+ * ``` js
+ * request = request.http2();
+ * request.get('http://localhost/')
+ * .http2(false)
+ * .end(callback);
+ * ```
+ *
+ * @param {Boolean} enable
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.http2 = function (bool) {
+ if (exports.protocols['http2:'] === undefined) {
+ throw new Error(
+ 'superagent: this version of Node.js does not support http2'
+ );
+ }
+
+ this._enableHttp2 = bool === undefined ? true : bool;
+ return this;
+};
+
+/**
+ * Queue the given `file` as an attachment to the specified `field`,
+ * with optional `options` (or filename).
+ *
+ * ``` js
+ * request.post('http://localhost/upload')
+ * .attach('field', Buffer.from('Hello world '), 'hello.html')
+ * .end(callback);
+ * ```
+ *
+ * A filename may also be used:
+ *
+ * ``` js
+ * request.post('http://localhost/upload')
+ * .attach('files', 'image.jpg')
+ * .end(callback);
+ * ```
+ *
+ * @param {String} field
+ * @param {String|fs.ReadStream|Buffer} file
+ * @param {String|Object} options
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.attach = function (field, file, options) {
+ if (file) {
+ if (this._data) {
+ throw new Error("superagent can't mix .send() and .attach()");
+ }
+
+ let o = options || {};
+ if (typeof options === 'string') {
+ o = { filename: options };
+ }
+
+ if (typeof file === 'string') {
+ if (!o.filename) o.filename = file;
+ debug('creating `fs.ReadStream` instance for file: %s', file);
+ file = fs.createReadStream(file);
+ file.on('error', (error) => {
+ const formData = this._getFormData();
+ formData.emit('error', error);
+ });
+ } else if (!o.filename && file.path) {
+ o.filename = file.path;
+ }
+
+ this._getFormData().append(field, file, o);
+ }
+
+ return this;
+};
+
+Request.prototype._getFormData = function () {
+ if (!this._formData) {
+ this._formData = new FormData();
+ this._formData.on('error', (error) => {
+ debug('FormData error', error);
+ if (this.called) {
+ // The request has already finished and the callback was called.
+ // Silently ignore the error.
+ return;
+ }
+
+ this.callback(error);
+ this.abort();
+ });
+ }
+
+ return this._formData;
+};
+
+/**
+ * Gets/sets the `Agent` to use for this HTTP request. The default (if this
+ * function is not called) is to opt out of connection pooling (`agent: false`).
+ *
+ * @param {http.Agent} agent
+ * @return {http.Agent}
+ * @api public
+ */
+
+Request.prototype.agent = function (agent) {
+ if (arguments.length === 0) return this._agent;
+ this._agent = agent;
+ return this;
+};
+
+/**
+ * Gets/sets the `lookup` function to use custom DNS resolver.
+ *
+ * @param {Function} lookup
+ * @return {Function}
+ * @api public
+ */
+
+Request.prototype.lookup = function (lookup) {
+ if (arguments.length === 0) return this._lookup;
+ this._lookup = lookup;
+ return this;
+};
+
+/**
+ * Set _Content-Type_ response header passed through `mime.getType()`.
+ *
+ * Examples:
+ *
+ * request.post('/')
+ * .type('xml')
+ * .send(xmlstring)
+ * .end(callback);
+ *
+ * request.post('/')
+ * .type('json')
+ * .send(jsonstring)
+ * .end(callback);
+ *
+ * request.post('/')
+ * .type('application/json')
+ * .send(jsonstring)
+ * .end(callback);
+ *
+ * @param {String} type
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.type = function (type) {
+ return this.set(
+ 'Content-Type',
+ type.includes('/') ? type : mime.getType(type)
+ );
+};
+
+/**
+ * Set _Accept_ response header passed through `mime.getType()`.
+ *
+ * Examples:
+ *
+ * superagent.types.json = 'application/json';
+ *
+ * request.get('/agent')
+ * .accept('json')
+ * .end(callback);
+ *
+ * request.get('/agent')
+ * .accept('application/json')
+ * .end(callback);
+ *
+ * @param {String} accept
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.accept = function (type) {
+ return this.set('Accept', type.includes('/') ? type : mime.getType(type));
+};
+
+/**
+ * Add query-string `val`.
+ *
+ * Examples:
+ *
+ * request.get('/shoes')
+ * .query('size=10')
+ * .query({ color: 'blue' })
+ *
+ * @param {Object|String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.query = function (value) {
+ if (typeof value === 'string') {
+ this._query.push(value);
+ } else {
+ Object.assign(this.qs, value);
+ }
+
+ return this;
+};
+
+/**
+ * Write raw `data` / `encoding` to the socket.
+ *
+ * @param {Buffer|String} data
+ * @param {String} encoding
+ * @return {Boolean}
+ * @api public
+ */
+
+Request.prototype.write = function (data, encoding) {
+ const request_ = this.request();
+ if (!this._streamRequest) {
+ this._streamRequest = true;
+ }
+
+ return request_.write(data, encoding);
+};
+
+/**
+ * Pipe the request body to `stream`.
+ *
+ * @param {Stream} stream
+ * @param {Object} options
+ * @return {Stream}
+ * @api public
+ */
+
+Request.prototype.pipe = function (stream, options) {
+ this.piped = true; // HACK...
+ this.buffer(false);
+ this.end();
+ return this._pipeContinue(stream, options);
+};
+
+Request.prototype._pipeContinue = function (stream, options) {
+ this.req.once('response', (res) => {
+ // redirect
+ if (
+ isRedirect(res.statusCode) &&
+ this._redirects++ !== this._maxRedirects
+ ) {
+ return this._redirect(res) === this
+ ? this._pipeContinue(stream, options)
+ : undefined;
+ }
+
+ this.res = res;
+ this._emitResponse();
+ if (this._aborted) return;
+
+ if (this._shouldDecompress(res)) {
+
+ let decompresser = chooseDecompresser(res);
+
+ decompresser.on('error', (error) => {
+ if (error && error.code === 'Z_BUF_ERROR') {
+ // unexpected end of file is ignored by browsers and curl
+ stream.emit('end');
+ return;
+ }
+
+ stream.emit('error', error);
+ });
+ res.pipe(decompresser).pipe(stream, options);
+ // don't emit 'end' until decompresser has completed writing all its data.
+ decompresser.once('end', () => this.emit('end'));
+ } else {
+ res.pipe(stream, options);
+ res.once('end', () => this.emit('end'));
+ }
+ });
+ return stream;
+};
+
+/**
+ * Enable / disable buffering.
+ *
+ * @return {Boolean} [val]
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.buffer = function (value) {
+ this._buffer = value !== false;
+ return this;
+};
+
+/**
+ * Redirect to `url
+ *
+ * @param {IncomingMessage} res
+ * @return {Request} for chaining
+ * @api private
+ */
+
+Request.prototype._redirect = function (res) {
+ let url = res.headers.location;
+ if (!url) {
+ return this.callback(new Error('No location header for redirect'), res);
+ }
+
+ debug('redirect %s -> %s', this.url, url);
+
+ // location
+ url = new URL(url, this.url).href;
+
+ // ensure the response is being consumed
+ // this is required for Node v0.10+
+ res.resume();
+
+ let headers = this.req.getHeaders ? this.req.getHeaders() : this.req._headers;
+
+ const changesOrigin = new URL(url).host !== new URL(this.url).host;
+
+ // implementation of 302 following defacto standard
+ if (res.statusCode === 301 || res.statusCode === 302) {
+ // strip Content-* related fields
+ // in case of POST etc
+ headers = utils.cleanHeader(headers, changesOrigin);
+
+ // force GET
+ this.method = this.method === 'HEAD' ? 'HEAD' : 'GET';
+
+ // clear data
+ this._data = null;
+ }
+
+ // 303 is always GET
+ if (res.statusCode === 303) {
+ // strip Content-* related fields
+ // in case of POST etc
+ headers = utils.cleanHeader(headers, changesOrigin);
+
+ // force method
+ this.method = 'GET';
+
+ // clear data
+ this._data = null;
+ }
+
+ // 307 preserves method
+ // 308 preserves method
+ delete headers.host;
+
+ delete this.req;
+ delete this._formData;
+
+ // remove all add header except User-Agent
+ _initHeaders(this);
+
+ // redirect
+ this.res = res;
+ this._endCalled = false;
+ this.url = url;
+ this.qs = {};
+ this._query.length = 0;
+ this.set(headers);
+ this._emitRedirect();
+ this._redirectList.push(this.url);
+ this.end(this._callback);
+ return this;
+};
+
+/**
+ * Set Authorization field value with `user` and `pass`.
+ *
+ * Examples:
+ *
+ * .auth('tobi', 'learnboost')
+ * .auth('tobi:learnboost')
+ * .auth('tobi')
+ * .auth(accessToken, { type: 'bearer' })
+ *
+ * @param {String} user
+ * @param {String} [pass]
+ * @param {Object} [options] options with authorization type 'basic' or 'bearer' ('basic' is default)
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.auth = function (user, pass, options) {
+ if (arguments.length === 1) pass = '';
+ if (typeof pass === 'object' && pass !== null) {
+ // pass is optional and can be replaced with options
+ options = pass;
+ pass = '';
+ }
+
+ if (!options) {
+ options = { type: 'basic' };
+ }
+
+ const encoder = (string) => Buffer.from(string).toString('base64');
+
+ return this._auth(user, pass, options, encoder);
+};
+
+/**
+ * Set the certificate authority option for https request.
+ *
+ * @param {Buffer | Array} cert
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.ca = function (cert) {
+ this._ca = cert;
+ return this;
+};
+
+/**
+ * Set the client certificate key option for https request.
+ *
+ * @param {Buffer | String} cert
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.key = function (cert) {
+ this._key = cert;
+ return this;
+};
+
+/**
+ * Set the key, certificate, and CA certs of the client in PFX or PKCS12 format.
+ *
+ * @param {Buffer | String} cert
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.pfx = function (cert) {
+ if (typeof cert === 'object' && !Buffer.isBuffer(cert)) {
+ this._pfx = cert.pfx;
+ this._passphrase = cert.passphrase;
+ } else {
+ this._pfx = cert;
+ }
+
+ return this;
+};
+
+/**
+ * Set the client certificate option for https request.
+ *
+ * @param {Buffer | String} cert
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.cert = function (cert) {
+ this._cert = cert;
+ return this;
+};
+
+/**
+ * Do not reject expired or invalid TLS certs.
+ * sets `rejectUnauthorized=true`. Be warned that this allows MITM attacks.
+ *
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.disableTLSCerts = function () {
+ this._disableTLSCerts = true;
+ return this;
+};
+
+/**
+ * Return an http[s] request.
+ *
+ * @return {OutgoingMessage}
+ * @api private
+ */
+
+// eslint-disable-next-line complexity
+Request.prototype.request = function () {
+ if (this.req) return this.req;
+
+ const options = {};
+
+ try {
+ const query = qs.stringify(this.qs, {
+ indices: false,
+ strictNullHandling: true
+ });
+ if (query) {
+ this.qs = {};
+ this._query.push(query);
+ }
+
+ this._finalizeQueryString();
+ } catch (err) {
+ return this.emit('error', err);
+ }
+
+ let { url: urlString } = this;
+ const retries = this._retries;
+
+ // default to http://
+ if (urlString.indexOf('http') !== 0) urlString = `http://${urlString}`;
+ const url = new URL(urlString);
+ let { protocol } = url;
+ let path = `${url.pathname}${url.search}`;
+
+ // support unix sockets
+ if (/^https?\+unix:/.test(protocol) === true) {
+ // get the protocol
+ protocol = `${protocol.split('+')[0]}:`;
+
+ // get the socket path
+ options.socketPath = url.hostname.replace(/%2F/g, '/');
+ url.host = '';
+ url.hostname = '';
+ }
+
+ // Override IP address of a hostname
+ if (this._connectOverride) {
+ const { hostname } = url;
+ const match =
+ hostname in this._connectOverride
+ ? this._connectOverride[hostname]
+ : this._connectOverride['*'];
+ if (match) {
+ // backup the real host
+ if (!this._header.host) {
+ this.set('host', url.host);
+ }
+
+ let newHost;
+ let newPort;
+
+ if (typeof match === 'object') {
+ newHost = match.host;
+ newPort = match.port;
+ } else {
+ newHost = match;
+ newPort = url.port;
+ }
+
+ // wrap [ipv6]
+ url.host = /:/.test(newHost) ? `[${newHost}]` : newHost;
+ if (newPort) {
+ url.host += `:${newPort}`;
+ url.port = newPort;
+ }
+
+ url.hostname = newHost;
+ }
+ }
+
+ // options
+ options.method = this.method;
+ options.port = url.port;
+ options.path = path;
+ options.host = utils.normalizeHostname(url.hostname); // ex: [::1] -> ::1
+ options.ca = this._ca;
+ options.key = this._key;
+ options.pfx = this._pfx;
+ options.cert = this._cert;
+ options.passphrase = this._passphrase;
+ options.agent = this._agent;
+ options.lookup = this._lookup;
+ options.rejectUnauthorized =
+ typeof this._disableTLSCerts === 'boolean'
+ ? !this._disableTLSCerts
+ : process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0';
+
+ // Allows request.get('https://1.2.3.4/').set('Host', 'example.com')
+ if (this._header.host) {
+ options.servername = this._header.host.replace(/:\d+$/, '');
+ }
+
+ if (
+ this._trustLocalhost &&
+ /^(?:localhost|127\.0\.0\.\d+|(0*:)+:0*1)$/.test(url.hostname)
+ ) {
+ options.rejectUnauthorized = false;
+ }
+
+ // initiate request
+ const module_ = this._enableHttp2
+ ? exports.protocols['http2:'].setProtocol(protocol)
+ : exports.protocols[protocol];
+
+ // request
+ this.req = module_.request(options);
+ const { req } = this;
+
+ // set tcp no delay
+ req.setNoDelay(true);
+
+ if (options.method !== 'HEAD') {
+ req.setHeader('Accept-Encoding', 'gzip, deflate');
+ }
+
+ this.protocol = protocol;
+ this.host = url.host;
+
+ // expose events
+ req.once('drain', () => {
+ this.emit('drain');
+ });
+
+ req.on('error', (error) => {
+ // flag abortion here for out timeouts
+ // because node will emit a faux-error "socket hang up"
+ // when request is aborted before a connection is made
+ if (this._aborted) return;
+ // if not the same, we are in the **old** (cancelled) request,
+ // so need to continue (same as for above)
+ if (this._retries !== retries) return;
+ // if we've received a response then we don't want to let
+ // an error in the request blow up the response
+ if (this.response) return;
+ this.callback(error);
+ });
+
+ // auth
+ if (url.username || url.password) {
+ this.auth(url.username, url.password);
+ }
+
+ if (this.username && this.password) {
+ this.auth(this.username, this.password);
+ }
+
+ for (const key in this.header) {
+ if (hasOwn(this.header, key)) req.setHeader(key, this.header[key]);
+ }
+
+ // add cookies
+ if (this.cookies) {
+ if (hasOwn(this._header, 'cookie')) {
+ // merge
+ const temporaryJar = new CookieJar.CookieJar();
+ temporaryJar.setCookies(this._header.cookie.split('; '));
+ temporaryJar.setCookies(this.cookies.split('; '));
+ req.setHeader(
+ 'Cookie',
+ temporaryJar.getCookies(CookieJar.CookieAccessInfo.All).toValueString()
+ );
+ } else {
+ req.setHeader('Cookie', this.cookies);
+ }
+ }
+
+ return req;
+};
+
+/**
+ * Invoke the callback with `err` and `res`
+ * and handle arity check.
+ *
+ * @param {Error} err
+ * @param {Response} res
+ * @api private
+ */
+
+Request.prototype.callback = function (error, res) {
+ if (this._shouldRetry(error, res)) {
+ return this._retry();
+ }
+
+ // Avoid the error which is emitted from 'socket hang up' to cause the fn undefined error on JS runtime.
+ const fn = this._callback || noop;
+ this.clearTimeout();
+ if (this.called) return console.warn('superagent: double callback bug');
+ this.called = true;
+
+ if (!error) {
+ try {
+ if (!this._isResponseOK(res)) {
+ let message = 'Unsuccessful HTTP response';
+ if (res) {
+ message = http.STATUS_CODES[res.status] || message;
+ }
+
+ error = new Error(message);
+ error.status = res ? res.status : undefined;
+ }
+ } catch (err) {
+ error = err;
+ error.status = error.status || (res ? res.status : undefined);
+ }
+ }
+
+ // It's important that the callback is called outside try/catch
+ // to avoid double callback
+ if (!error) {
+ return fn(null, res);
+ }
+
+ error.response = res;
+ if (this._maxRetries) error.retries = this._retries - 1;
+
+ // only emit error event if there is a listener
+ // otherwise we assume the callback to `.end()` will get the error
+ if (error && this.listeners('error').length > 0) {
+ this.emit('error', error);
+ }
+
+ fn(error, res);
+};
+
+/**
+ * Check if `obj` is a host object,
+ *
+ * @param {Object} obj host object
+ * @return {Boolean} is a host object
+ * @api private
+ */
+Request.prototype._isHost = function (object) {
+ return (
+ Buffer.isBuffer(object) ||
+ object instanceof Stream ||
+ object instanceof FormData
+ );
+};
+
+/**
+ * Initiate request, invoking callback `fn(err, res)`
+ * with an instanceof `Response`.
+ *
+ * @param {Function} fn
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype._emitResponse = function (body, files) {
+ const response = new Response(this);
+ this.response = response;
+ response.redirects = this._redirectList;
+ if (undefined !== body) {
+ response.body = body;
+ }
+
+ response.files = files;
+ if (this._endCalled) {
+ response.pipe = function () {
+ throw new Error(
+ "end() has already been called, so it's too late to start piping"
+ );
+ };
+ }
+
+ this.emit('response', response);
+ return response;
+};
+
+/**
+ * Emit `redirect` event, passing an instanceof `Response`.
+ *
+ * @api private
+ */
+
+Request.prototype._emitRedirect = function () {
+ const response = new Response(this);
+ response.redirects = this._redirectList;
+ this.emit('redirect', response);
+};
+
+Request.prototype.end = function (fn) {
+ this.request();
+ debug('%s %s', this.method, this.url);
+
+ if (this._endCalled) {
+ throw new Error(
+ '.end() was called twice. This is not supported in superagent'
+ );
+ }
+
+ this._endCalled = true;
+
+ // store callback
+ this._callback = fn || noop;
+
+ this._end();
+};
+
+Request.prototype._end = function () {
+ if (this._aborted)
+ return this.callback(
+ new Error('The request has been aborted even before .end() was called')
+ );
+
+ let data = this._data;
+ const { req } = this;
+ const { method } = this;
+
+ this._setTimeouts();
+
+ // body
+ if (method !== 'HEAD' && !req._headerSent) {
+ // serialize stuff
+ if (typeof data !== 'string') {
+ let contentType = req.getHeader('Content-Type');
+ // Parse out just the content type from the header (ignore the charset)
+ if (contentType) contentType = contentType.split(';')[0];
+ let serialize = this._serializer || exports.serialize[contentType];
+ if (!serialize && isJSON(contentType)) {
+ serialize = exports.serialize['application/json'];
+ }
+
+ if (serialize) data = serialize(data);
+ }
+
+ // content-length
+ if (data && !req.getHeader('Content-Length')) {
+ req.setHeader(
+ 'Content-Length',
+ Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data)
+ );
+ }
+ }
+
+ // response
+ // eslint-disable-next-line complexity
+ req.once('response', (res) => {
+ debug('%s %s -> %s', this.method, this.url, res.statusCode);
+
+ if (this._responseTimeoutTimer) {
+ clearTimeout(this._responseTimeoutTimer);
+ }
+
+ if (this.piped) {
+ return;
+ }
+
+ const max = this._maxRedirects;
+ const mime = utils.type(res.headers['content-type'] || '') || 'text/plain';
+ let type = mime.split('/')[0];
+ if (type) type = type.toLowerCase().trim();
+ const multipart = type === 'multipart';
+ const redirect = isRedirect(res.statusCode);
+ const responseType = this._responseType;
+
+ this.res = res;
+
+ // redirect
+ if (redirect && this._redirects++ !== max) {
+ return this._redirect(res);
+ }
+
+ if (this.method === 'HEAD') {
+ this.emit('end');
+ this.callback(null, this._emitResponse());
+ return;
+ }
+
+ // zlib support
+ if (this._shouldDecompress(res)) {
+ decompress(req, res);
+ }
+
+ let buffer = this._buffer;
+ if (buffer === undefined && mime in exports.buffer) {
+ buffer = Boolean(exports.buffer[mime]);
+ }
+
+ let parser = this._parser;
+ if (undefined === buffer && parser) {
+ console.warn(
+ "A custom superagent parser has been set, but buffering strategy for the parser hasn't been configured. Call `req.buffer(true or false)` or set `superagent.buffer[mime] = true or false`"
+ );
+ buffer = true;
+ }
+
+ if (!parser) {
+ if (responseType) {
+ parser = exports.parse.image; // It's actually a generic Buffer
+ buffer = true;
+ } else if (multipart) {
+ const form = formidable.formidable();
+ parser = (res, callback) => {
+ // Create a PassThrough stream that acts as a proper HTTP request
+ const bridgeStream = new Stream.PassThrough();
+
+ // Add HTTP request properties from the current request context
+ bridgeStream.method = this.method || 'POST';
+ bridgeStream.url = this.url || '/';
+ bridgeStream.httpVersion = res.httpVersion || '1.1';
+ bridgeStream.headers = res.headers || {};
+ bridgeStream.socket = res.socket || { readable: true };
+
+ // Pipe the response data through the bridge stream
+ res.pipe(bridgeStream);
+
+ form.parse(bridgeStream, (err, fields, files) => {
+ if (err) return callback(err);
+
+ // Formidable v3 always returns arrays, but SuperAgent expects single values
+ // Flatten single-item arrays to maintain backward compatibility
+ const flattenedFields = {};
+ if (fields) {
+ for (const key in fields) {
+ const value = fields[key];
+ flattenedFields[key] = Array.isArray(value) && value.length === 1 ? value[0] : value;
+ }
+ }
+
+ const flattenedFiles = {};
+ if (files) {
+ for (const key in files) {
+ const value = files[key];
+ flattenedFiles[key] = Array.isArray(value) && value.length === 1 ? value[0] : value;
+ }
+ }
+
+ // Return flattened fields as the object parameter to match SuperAgent's expected format
+ callback(null, flattenedFields, flattenedFiles);
+ });
+ };
+ buffer = true;
+ } else if (isBinary(mime)) {
+ parser = exports.parse.image;
+ buffer = true; // For backwards-compatibility buffering default is ad-hoc MIME-dependent
+ } else if (exports.parse[mime]) {
+ parser = exports.parse[mime];
+ } else if (type === 'text') {
+ parser = exports.parse.text;
+ buffer = buffer !== false;
+ // everyone wants their own white-labeled json
+ } else if (isJSON(mime)) {
+ parser = exports.parse['application/json'];
+ buffer = buffer !== false;
+ } else if (buffer) {
+ parser = exports.parse.text;
+ } else if (undefined === buffer) {
+ parser = exports.parse.image; // It's actually a generic Buffer
+ buffer = true;
+ }
+ }
+
+ // by default only buffer text/*, json and messed up thing from hell
+ if ((undefined === buffer && isText(mime)) || isJSON(mime)) {
+ buffer = true;
+ }
+
+ this._resBuffered = buffer;
+ let parserHandlesEnd = false;
+ if (buffer) {
+ // Protectiona against zip bombs and other nuisance
+ let responseBytesLeft = this._maxResponseSize || 200000000;
+ res.on('data', (buf) => {
+ responseBytesLeft -= buf.byteLength || buf.length > 0 ? buf.length : 0;
+ if (responseBytesLeft < 0) {
+ // This will propagate through error event
+ const error = new Error('Maximum response size reached');
+ error.code = 'ETOOLARGE';
+ // Parsers aren't required to observe error event,
+ // so would incorrectly report success
+ parserHandlesEnd = false;
+ // Will not emit error event
+ res.destroy(error);
+ // so we do callback now
+ this.callback(error, null);
+ }
+ });
+ }
+
+ if (parser) {
+ try {
+ // Unbuffered parsers are supposed to emit response early,
+ // which is weird BTW, because response.body won't be there.
+ parserHandlesEnd = buffer;
+
+ parser(res, (error, object, files) => {
+ if (this.timedout) {
+ // Timeout has already handled all callbacks
+ return;
+ }
+
+ // Intentional (non-timeout) abort is supposed to preserve partial response,
+ // even if it doesn't parse.
+ if (error && !this._aborted) {
+ return this.callback(error);
+ }
+
+ if (parserHandlesEnd) {
+ this.emit('end');
+ this.callback(null, this._emitResponse(object, files));
+ }
+ });
+ } catch (err) {
+ this.callback(err);
+ return;
+ }
+ }
+
+ this.res = res;
+
+ // unbuffered
+ if (!buffer) {
+ debug('unbuffered %s %s', this.method, this.url);
+ this.callback(null, this._emitResponse());
+ if (multipart) return; // allow multipart to handle end event
+ res.once('end', () => {
+ debug('end %s %s', this.method, this.url);
+ this.emit('end');
+ });
+ return;
+ }
+
+ // terminating events
+ res.once('error', (error) => {
+ parserHandlesEnd = false;
+ this.callback(error, null);
+ });
+ if (!parserHandlesEnd)
+ res.once('end', () => {
+ debug('end %s %s', this.method, this.url);
+ // TODO: unless buffering emit earlier to stream
+ this.emit('end');
+ this.callback(null, this._emitResponse());
+ });
+ });
+
+ this.emit('request', this);
+
+ const getProgressMonitor = () => {
+ const lengthComputable = true;
+ const total = req.getHeader('Content-Length');
+ let loaded = 0;
+
+ const progress = new Stream.Transform();
+ progress._transform = (chunk, encoding, callback) => {
+ loaded += chunk.length;
+ this.emit('progress', {
+ direction: 'upload',
+ lengthComputable,
+ loaded,
+ total
+ });
+ callback(null, chunk);
+ };
+
+ return progress;
+ };
+
+ const bufferToChunks = (buffer) => {
+ const chunkSize = 16 * 1024; // default highWaterMark value
+ const chunking = new Stream.Readable();
+ const totalLength = buffer.length;
+ const remainder = totalLength % chunkSize;
+ const cutoff = totalLength - remainder;
+
+ for (let i = 0; i < cutoff; i += chunkSize) {
+ const chunk = buffer.slice(i, i + chunkSize);
+ chunking.push(chunk);
+ }
+
+ if (remainder > 0) {
+ const remainderBuffer = buffer.slice(-remainder);
+ chunking.push(remainderBuffer);
+ }
+
+ chunking.push(null); // no more data
+
+ return chunking;
+ };
+
+ // if a FormData instance got created, then we send that as the request body
+ const formData = this._formData;
+ if (formData) {
+ // set headers
+ const headers = formData.getHeaders();
+ for (const i in headers) {
+ if (hasOwn(headers, i)) {
+ debug('setting FormData header: "%s: %s"', i, headers[i]);
+ req.setHeader(i, headers[i]);
+ }
+ }
+
+ // attempt to get "Content-Length" header
+ formData.getLength((error, length) => {
+ // TODO: Add chunked encoding when no length (if err)
+ if (error) debug('formData.getLength had error', error, length);
+
+ debug('got FormData Content-Length: %s', length);
+ if (typeof length === 'number') {
+ req.setHeader('Content-Length', length);
+ }
+
+ formData.pipe(getProgressMonitor()).pipe(req);
+ });
+ } else if (Buffer.isBuffer(data)) {
+ bufferToChunks(data).pipe(getProgressMonitor()).pipe(req);
+ } else {
+ req.end(data);
+ }
+};
+
+// Check whether response has a non-0-sized gzip-encoded body
+Request.prototype._shouldDecompress = (res) => {
+ return hasNonEmptyResponseContent(res) && (isGzipOrDeflateEncoding(res) || isBrotliEncoding(res));
+};
+
+
+/**
+ * Overrides DNS for selected hostnames. Takes object mapping hostnames to IP addresses.
+ *
+ * When making a request to a URL with a hostname exactly matching a key in the object,
+ * use the given IP address to connect, instead of using DNS to resolve the hostname.
+ *
+ * A special host `*` matches every hostname (keep redirects in mind!)
+ *
+ * request.connect({
+ * 'test.example.com': '127.0.0.1',
+ * 'ipv6.example.com': '::1',
+ * })
+ */
+Request.prototype.connect = function (connectOverride) {
+ if (typeof connectOverride === 'string') {
+ this._connectOverride = { '*': connectOverride };
+ } else if (typeof connectOverride === 'object') {
+ this._connectOverride = connectOverride;
+ } else {
+ this._connectOverride = undefined;
+ }
+
+ return this;
+};
+
+Request.prototype.trustLocalhost = function (toggle) {
+ this._trustLocalhost = toggle === undefined ? true : toggle;
+ return this;
+};
+
+// generate HTTP verb methods
+if (!methods.includes('del')) {
+ // create a copy so we don't cause conflicts with
+ // other packages using the methods package and
+ // npm 3.x
+ methods = [...methods];
+ methods.push('del');
+}
+
+for (let method of methods) {
+ const name = method;
+ method = method === 'del' ? 'delete' : method;
+
+ method = method.toUpperCase();
+ request[name] = (url, data, fn) => {
+ const request_ = request(method, url);
+ if (typeof data === 'function') {
+ fn = data;
+ data = null;
+ }
+
+ if (data) {
+ if (method === 'GET' || method === 'HEAD') {
+ request_.query(data);
+ } else {
+ request_.send(data);
+ }
+ }
+
+ if (fn) request_.end(fn);
+ return request_;
+ };
+}
+
+/**
+ * Check if `mime` is text and should be buffered.
+ *
+ * @param {String} mime
+ * @return {Boolean}
+ * @api public
+ */
+
+function isText(mime) {
+ const parts = mime.split('/');
+ let type = parts[0];
+ if (type) type = type.toLowerCase().trim();
+ let subtype = parts[1];
+ if (subtype) subtype = subtype.toLowerCase().trim();
+
+ return type === 'text' || subtype === 'x-www-form-urlencoded';
+}
+
+// This is not a catchall, but a start. It might be useful
+// in the long run to have file that includes all binary
+// content types from https://www.iana.org/assignments/media-types/media-types.xhtml
+function isBinary(mime) {
+ let [registry, name] = mime.split('/');
+ if (registry) registry = registry.toLowerCase().trim();
+ if (name) name = name.toLowerCase().trim();
+ return (
+ ['audio', 'font', 'image', 'video'].includes(registry) ||
+ ['gz', 'gzip'].includes(name)
+ );
+}
+
+/**
+ * Check if `mime` is json or has +json structured syntax suffix.
+ *
+ * @param {String} mime
+ * @return {Boolean}
+ * @api private
+ */
+
+function isJSON(mime) {
+ // should match /json or +json
+ // but not /json-seq
+ return /[/+]json($|[^-\w])/i.test(mime);
+}
+
+/**
+ * Check if we should follow the redirect `code`.
+ *
+ * @param {Number} code
+ * @return {Boolean}
+ * @api private
+ */
+
+function isRedirect(code) {
+ return [301, 302, 303, 305, 307, 308].includes(code);
+}
+
+function hasNonEmptyResponseContent(res) {
+ if (res.statusCode === 204 || res.statusCode === 304) {
+ // These aren't supposed to have any body
+ return false;
+ }
+
+ // header content is a string, and distinction between 0 and no information is crucial
+ if (res.headers['content-length'] === '0') {
+ // We know that the body is empty (unfortunately, this check does not cover chunked encoding)
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/node/parsers/image.js b/src/node/parsers/image.js
new file mode 100644
index 000000000..11b6962a5
--- /dev/null
+++ b/src/node/parsers/image.js
@@ -0,0 +1,10 @@
+module.exports = (res, fn) => {
+ const data = []; // Binary data needs binary storage
+
+ res.on('data', (chunk) => {
+ data.push(chunk);
+ });
+ res.on('end', () => {
+ fn(null, Buffer.concat(data));
+ });
+};
diff --git a/src/node/parsers/index.js b/src/node/parsers/index.js
new file mode 100644
index 000000000..57ad3e9c8
--- /dev/null
+++ b/src/node/parsers/index.js
@@ -0,0 +1,11 @@
+exports['application/x-www-form-urlencoded'] = require('./urlencoded');
+exports['application/json'] = require('./json');
+exports.text = require('./text');
+
+exports['application/json-seq'] = exports.text;
+
+const binary = require('./image');
+
+exports['application/octet-stream'] = binary;
+exports['application/pdf'] = binary;
+exports.image = binary;
diff --git a/src/node/parsers/json.js b/src/node/parsers/json.js
new file mode 100644
index 000000000..8938b6a2b
--- /dev/null
+++ b/src/node/parsers/json.js
@@ -0,0 +1,22 @@
+module.exports = function (res, fn) {
+ res.text = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ res.text += chunk;
+ });
+ res.on('end', () => {
+ let body;
+ let error;
+ try {
+ body = res.text && JSON.parse(res.text);
+ } catch (err) {
+ error = err;
+ // issue #675: return the raw response if the response parsing fails
+ error.rawResponse = res.text || null;
+ // issue #876: return the http status code if the response parsing fails
+ error.statusCode = res.statusCode;
+ } finally {
+ fn(error, body);
+ }
+ });
+};
diff --git a/src/node/parsers/text.js b/src/node/parsers/text.js
new file mode 100644
index 000000000..f17f22b27
--- /dev/null
+++ b/src/node/parsers/text.js
@@ -0,0 +1,8 @@
+module.exports = (res, fn) => {
+ res.text = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ res.text += chunk;
+ });
+ res.on('end', fn);
+};
diff --git a/lib/node/parsers/urlencoded.js b/src/node/parsers/urlencoded.js
similarity index 53%
rename from lib/node/parsers/urlencoded.js
rename to src/node/parsers/urlencoded.js
index 245c665f4..b0725dba4 100644
--- a/lib/node/parsers/urlencoded.js
+++ b/src/node/parsers/urlencoded.js
@@ -1,19 +1,20 @@
-
/**
* Module dependencies.
*/
-var qs = require('qs');
+const qs = require('qs');
-module.exports = function(res, fn){
+module.exports = (res, fn) => {
res.text = '';
res.setEncoding('ascii');
- res.on('data', function(chunk){ res.text += chunk; });
- res.on('end', function(){
+ res.on('data', (chunk) => {
+ res.text += chunk;
+ });
+ res.on('end', () => {
try {
fn(null, qs.parse(res.text));
} catch (err) {
fn(err);
}
});
-};
\ No newline at end of file
+};
diff --git a/src/node/response.js b/src/node/response.js
new file mode 100644
index 000000000..af1abc0bd
--- /dev/null
+++ b/src/node/response.js
@@ -0,0 +1,138 @@
+/**
+ * Module dependencies.
+ */
+
+const util = require('util');
+const Stream = require('stream');
+const ResponseBase = require('../response-base');
+const { mixin } = require('../utils');
+
+/**
+ * Expose `Response`.
+ */
+
+module.exports = Response;
+
+/**
+ * Initialize a new `Response` with the given `xhr`.
+ *
+ * - set flags (.ok, .error, etc)
+ * - parse header
+ *
+ * @param {Request} req
+ * @param {Object} options
+ * @constructor
+ * @extends {Stream}
+ * @implements {ReadableStream}
+ * @api private
+ */
+
+function Response(request) {
+ Stream.call(this);
+ this.res = request.res;
+ const { res } = this;
+ this.request = request;
+ this.req = request.req;
+ this.text = res.text;
+ this.files = res.files || {};
+ this.buffered = request._resBuffered;
+ this.headers = res.headers;
+ this.header = this.headers;
+ this._setStatusProperties(res.statusCode);
+ this._setHeaderProperties(this.header);
+ this.setEncoding = res.setEncoding.bind(res);
+ res.on('data', this.emit.bind(this, 'data'));
+ res.on('end', this.emit.bind(this, 'end'));
+ res.on('close', this.emit.bind(this, 'close'));
+ res.on('error', this.emit.bind(this, 'error'));
+}
+
+// Lazy access res.body.
+// https://github.com/nodejs/node/pull/39520#issuecomment-889697136
+Object.defineProperty(Response.prototype, 'body', {
+ get() {
+ return this._body !== undefined
+ ? this._body
+ : this.res.body !== undefined
+ ? this.res.body
+ : {};
+ },
+ set(value) {
+ this._body = value;
+ }
+});
+
+/**
+ * Inherit from `Stream`.
+ */
+
+util.inherits(Response, Stream);
+
+mixin(Response.prototype, ResponseBase.prototype);
+
+/**
+ * Implements methods of a `ReadableStream`
+ */
+
+Response.prototype.destroy = function (error) {
+ this.res.destroy(error);
+};
+
+/**
+ * Pause.
+ */
+
+Response.prototype.pause = function () {
+ this.res.pause();
+};
+
+/**
+ * Resume.
+ */
+
+Response.prototype.resume = function () {
+ this.res.resume();
+};
+
+/**
+ * Return an `Error` representative of this response.
+ *
+ * @return {Error}
+ * @api public
+ */
+
+Response.prototype.toError = function () {
+ const { req } = this;
+ const { method } = req;
+ const { path } = req;
+
+ const message = `cannot ${method} ${path} (${this.status})`;
+ const error = new Error(message);
+ error.status = this.status;
+ error.text = this.text;
+ error.method = method;
+ error.path = path;
+
+ return error;
+};
+
+Response.prototype.setStatusProperties = function (status) {
+ console.warn('In superagent 2.x setStatusProperties is a private method');
+ return this._setStatusProperties(status);
+};
+
+/**
+ * To json.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+Response.prototype.toJSON = function () {
+ return {
+ req: this.request.toJSON(),
+ header: this.header,
+ status: this.status,
+ text: this.text
+ };
+};
diff --git a/src/node/unzip.js b/src/node/unzip.js
new file mode 100644
index 000000000..1eeff9802
--- /dev/null
+++ b/src/node/unzip.js
@@ -0,0 +1,72 @@
+/**
+ * Module dependencies.
+ */
+
+const { StringDecoder } = require('string_decoder');
+const Stream = require('stream');
+const { chooseDecompresser } = require('./decompress');
+
+/**
+ * Buffers response data events and re-emits when they're decompressed.
+ *
+ * @param {Request} req
+ * @param {Response} res
+ * @api private
+ */
+
+exports.decompress = (request, res) => {
+ let decompresser = chooseDecompresser(res);
+
+ const stream = new Stream();
+ let decoder;
+
+ // make node responseOnEnd() happy
+ stream.req = request;
+
+ decompresser.on('error', (error) => {
+ if (error && error.code === 'Z_BUF_ERROR') {
+ // unexpected end of file is ignored by browsers and curl
+ stream.emit('end');
+ return;
+ }
+
+ stream.emit('error', error);
+ });
+
+ // pipe to unzip
+ res.pipe(decompresser);
+
+ // override `setEncoding` to capture encoding
+ res.setEncoding = (type) => {
+ decoder = new StringDecoder(type);
+ };
+
+ // decode upon decompressing with captured encoding
+ decompresser.on('data', (buf) => {
+ if (decoder) {
+ const string_ = decoder.write(buf);
+ if (string_.length > 0) stream.emit('data', string_);
+ } else {
+ stream.emit('data', buf);
+ }
+ });
+
+ decompresser.on('end', () => {
+ stream.emit('end');
+ });
+
+ // override `on` to capture data listeners
+ const _on = res.on;
+ res.on = function (type, fn) {
+ if (type === 'data' || type === 'end') {
+ stream.on(type, fn.bind(res));
+ } else if (type === 'error') {
+ stream.on(type, fn.bind(res));
+ _on.call(res, type, fn);
+ } else {
+ _on.call(res, type, fn);
+ }
+
+ return this;
+ };
+};
diff --git a/src/request-base.js b/src/request-base.js
new file mode 100644
index 000000000..c69262e31
--- /dev/null
+++ b/src/request-base.js
@@ -0,0 +1,778 @@
+/**
+ * Module of mixed-in functions shared between node and client code
+ */
+const { isObject, hasOwn } = require('./utils');
+
+/**
+ * Expose `RequestBase`.
+ */
+
+module.exports = RequestBase;
+
+/**
+ * Initialize a new `RequestBase`.
+ *
+ * @api public
+ */
+
+function RequestBase() {}
+
+/**
+ * Clear previous timeout.
+ *
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.clearTimeout = function () {
+ clearTimeout(this._timer);
+ clearTimeout(this._responseTimeoutTimer);
+ clearTimeout(this._uploadTimeoutTimer);
+ delete this._timer;
+ delete this._responseTimeoutTimer;
+ delete this._uploadTimeoutTimer;
+ return this;
+};
+
+/**
+ * Override default response body parser
+ *
+ * This function will be called to convert incoming data into request.body
+ *
+ * @param {Function}
+ * @api public
+ */
+
+RequestBase.prototype.parse = function (fn) {
+ this._parser = fn;
+ return this;
+};
+
+/**
+ * Set format of binary response body.
+ * In browser valid formats are 'blob' and 'arraybuffer',
+ * which return Blob and ArrayBuffer, respectively.
+ *
+ * In Node all values result in Buffer.
+ *
+ * Examples:
+ *
+ * req.get('/')
+ * .responseType('blob')
+ * .end(callback);
+ *
+ * @param {String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.responseType = function (value) {
+ this._responseType = value;
+ return this;
+};
+
+/**
+ * Override default request body serializer
+ *
+ * This function will be called to convert data set via .send or .attach into payload to send
+ *
+ * @param {Function}
+ * @api public
+ */
+
+RequestBase.prototype.serialize = function (fn) {
+ this._serializer = fn;
+ return this;
+};
+
+/**
+ * Set timeouts.
+ *
+ * - response timeout is time between sending request and receiving the first byte of the response. Includes DNS and connection time.
+ * - deadline is the time from start of the request to receiving response body in full. If the deadline is too short large files may not load at all on slow connections.
+ * - upload is the time since last bit of data was sent or received. This timeout works only if deadline timeout is off
+ *
+ * Value of 0 or false means no timeout.
+ *
+ * @param {Number|Object} ms or {response, deadline}
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.timeout = function (options) {
+ if (!options || typeof options !== 'object') {
+ this._timeout = options;
+ this._responseTimeout = 0;
+ this._uploadTimeout = 0;
+ return this;
+ }
+
+ for (const option in options) {
+ if (hasOwn(options, option)) {
+ switch (option) {
+ case 'deadline':
+ this._timeout = options.deadline;
+ break;
+ case 'response':
+ this._responseTimeout = options.response;
+ break;
+ case 'upload':
+ this._uploadTimeout = options.upload;
+ break;
+ default:
+ console.warn('Unknown timeout option', option);
+ }
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Set number of retry attempts on error.
+ *
+ * Failed requests will be retried 'count' times if timeout or err.code >= 500.
+ *
+ * @param {Number} count
+ * @param {Function} [fn]
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.retry = function (count, fn) {
+ // Default to 1 if no count passed or true
+ if (arguments.length === 0 || count === true) count = 1;
+ if (count <= 0) count = 0;
+ this._maxRetries = count;
+ this._retries = 0;
+ this._retryCallback = fn;
+ return this;
+};
+
+//
+// NOTE: we do not include ESOCKETTIMEDOUT because that is from `request` package
+//
+//
+// NOTE: we do not include EADDRINFO because it was removed from libuv in 2014
+//
+//
+//
+//
+// TODO: expose these as configurable defaults
+//
+const ERROR_CODES = new Set([
+ 'ETIMEDOUT',
+ 'ECONNRESET',
+ 'EADDRINUSE',
+ 'ECONNREFUSED',
+ 'EPIPE',
+ 'ENOTFOUND',
+ 'ENETUNREACH',
+ 'EAI_AGAIN'
+]);
+
+const STATUS_CODES = new Set([
+ 408, 413, 429, 500, 502, 503, 504, 521, 522, 524
+]);
+
+// TODO: we would need to make this easily configurable before adding it in (e.g. some might want to add POST)
+// const METHODS = new Set(['GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE']);
+
+/**
+ * Determine if a request should be retried.
+ * (Inspired by https://github.com/sindresorhus/got#retry)
+ *
+ * @param {Error} err an error
+ * @param {Response} [res] response
+ * @returns {Boolean} if segment should be retried
+ */
+RequestBase.prototype._shouldRetry = function (error, res) {
+ if (!this._maxRetries || this._retries++ >= this._maxRetries) {
+ return false;
+ }
+
+ if (this._retryCallback) {
+ try {
+ const override = this._retryCallback(error, res);
+ if (override === true) return true;
+ if (override === false) return false;
+ // undefined falls back to defaults
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ // TODO: we would need to make this easily configurable before adding it in (e.g. some might want to add POST)
+ /*
+ if (
+ this.req &&
+ this.req.method &&
+ !METHODS.has(this.req.method.toUpperCase())
+ )
+ return false;
+ */
+ if (res && res.status && STATUS_CODES.has(res.status)) return true;
+ if (error) {
+ if (error.code && ERROR_CODES.has(error.code)) return true;
+ // Superagent timeout
+ if (error.timeout && error.code === 'ECONNABORTED') return true;
+ if (error.crossDomain) return true;
+ }
+
+ return false;
+};
+
+/**
+ * Retry request
+ *
+ * @return {Request} for chaining
+ * @api private
+ */
+
+RequestBase.prototype._retry = function () {
+ this.clearTimeout();
+
+ // node
+ if (this.req) {
+ this.req = null;
+ this.req = this.request();
+ }
+
+ this._aborted = false;
+ this.timedout = false;
+ this.timedoutError = null;
+
+ return this._end();
+};
+
+/**
+ * Promise support
+ *
+ * @param {Function} resolve
+ * @param {Function} [reject]
+ * @return {Request}
+ */
+
+RequestBase.prototype.then = function (resolve, reject) {
+ if (!this._fullfilledPromise) {
+ const self = this;
+ if (this._endCalled) {
+ console.warn(
+ 'Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises'
+ );
+ }
+
+ this._fullfilledPromise = new Promise((resolve, reject) => {
+ self.on('abort', () => {
+ if (this._maxRetries && this._maxRetries > this._retries) {
+ return;
+ }
+
+ if (this.timedout && this.timedoutError) {
+ reject(this.timedoutError);
+ return;
+ }
+
+ const error = new Error('Aborted');
+ error.code = 'ABORTED';
+ error.status = this.status;
+ error.method = this.method;
+ error.url = this.url;
+ reject(error);
+ });
+ self.end((error, res) => {
+ if (error) reject(error);
+ else resolve(res);
+ });
+ });
+ }
+
+ return this._fullfilledPromise.then(resolve, reject);
+};
+
+RequestBase.prototype.catch = function (callback) {
+ return this.then(undefined, callback);
+};
+
+/**
+ * Allow for extension
+ */
+
+RequestBase.prototype.use = function (fn) {
+ fn(this);
+ return this;
+};
+
+RequestBase.prototype.ok = function (callback) {
+ if (typeof callback !== 'function') throw new Error('Callback required');
+ this._okCallback = callback;
+ return this;
+};
+
+RequestBase.prototype._isResponseOK = function (res) {
+ if (!res) {
+ return false;
+ }
+
+ if (this._okCallback) {
+ return this._okCallback(res);
+ }
+
+ return res.status >= 200 && res.status < 300;
+};
+
+/**
+ * Get request header `field`.
+ * Case-insensitive.
+ *
+ * @param {String} field
+ * @return {String}
+ * @api public
+ */
+
+RequestBase.prototype.get = function (field) {
+ return this._header[field.toLowerCase()];
+};
+
+/**
+ * Get case-insensitive header `field` value.
+ * This is a deprecated internal API. Use `.get(field)` instead.
+ *
+ * (getHeader is no longer used internally by the superagent code base)
+ *
+ * @param {String} field
+ * @return {String}
+ * @api private
+ * @deprecated
+ */
+
+RequestBase.prototype.getHeader = RequestBase.prototype.get;
+
+/**
+ * Set header `field` to `val`, or multiple fields with one object.
+ * Case-insensitive.
+ *
+ * Examples:
+ *
+ * req.get('/')
+ * .set('Accept', 'application/json')
+ * .set('X-API-Key', 'foobar')
+ * .end(callback);
+ *
+ * req.get('/')
+ * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' })
+ * .end(callback);
+ *
+ * @param {String|Object} field
+ * @param {String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.set = function (field, value) {
+ if (isObject(field)) {
+ for (const key in field) {
+ if (hasOwn(field, key)) this.set(key, field[key]);
+ }
+
+ return this;
+ }
+
+ this._header[field.toLowerCase()] = value;
+ this.header[field] = value;
+ return this;
+};
+
+/**
+ * Remove header `field`.
+ * Case-insensitive.
+ *
+ * Example:
+ *
+ * req.get('/')
+ * .unset('User-Agent')
+ * .end(callback);
+ *
+ * @param {String} field field name
+ */
+RequestBase.prototype.unset = function (field) {
+ delete this._header[field.toLowerCase()];
+ delete this.header[field];
+ return this;
+};
+
+/**
+ * Write the field `name` and `val`, or multiple fields with one object
+ * for "multipart/form-data" request bodies.
+ *
+ * ``` js
+ * request.post('/upload')
+ * .field('foo', 'bar')
+ * .end(callback);
+ *
+ * request.post('/upload')
+ * .field({ foo: 'bar', baz: 'qux' })
+ * .end(callback);
+ * ```
+ *
+ * @param {String|Object} name name of field
+ * @param {String|Blob|File|Buffer|fs.ReadStream} val value of field
+ * @param {String} options extra options, e.g. 'blob'
+ * @return {Request} for chaining
+ * @api public
+ */
+RequestBase.prototype.field = function (name, value, options) {
+ // name should be either a string or an object.
+ if (name === null || undefined === name) {
+ throw new Error('.field(name, val) name can not be empty');
+ }
+
+ if (this._data) {
+ throw new Error(
+ ".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"
+ );
+ }
+
+ if (isObject(name)) {
+ for (const key in name) {
+ if (hasOwn(name, key)) this.field(key, name[key]);
+ }
+
+ return this;
+ }
+
+ if (Array.isArray(value)) {
+ for (const i in value) {
+ if (hasOwn(value, i)) this.field(name, value[i]);
+ }
+
+ return this;
+ }
+
+ // val should be defined now
+ if (value === null || undefined === value) {
+ throw new Error('.field(name, val) val can not be empty');
+ }
+
+ if (typeof value === 'boolean') {
+ value = String(value);
+ }
+
+ // fix https://github.com/ladjs/superagent/issues/1680
+ if (options) this._getFormData().append(name, value, options);
+ else this._getFormData().append(name, value);
+
+ return this;
+};
+
+/**
+ * Abort the request, and clear potential timeout.
+ *
+ * @return {Request} request
+ * @api public
+ */
+RequestBase.prototype.abort = function () {
+ if (this._aborted) {
+ return this;
+ }
+
+ this._aborted = true;
+ if (this.xhr) this.xhr.abort(); // browser
+ if (this.req) {
+ this.req.abort(); // node
+ }
+
+ this.clearTimeout();
+ this.emit('abort');
+ return this;
+};
+
+RequestBase.prototype._auth = function (user, pass, options, base64Encoder) {
+ switch (options.type) {
+ case 'basic':
+ this.set('Authorization', `Basic ${base64Encoder(`${user}:${pass}`)}`);
+ break;
+
+ case 'auto':
+ this.username = user;
+ this.password = pass;
+ break;
+
+ case 'bearer': // usage would be .auth(accessToken, { type: 'bearer' })
+ this.set('Authorization', `Bearer ${user}`);
+ break;
+ default:
+ break;
+ }
+
+ return this;
+};
+
+/**
+ * Enable transmission of cookies with x-domain requests.
+ *
+ * Note that for this to work the origin must not be
+ * using "Access-Control-Allow-Origin" with a wildcard,
+ * and also must set "Access-Control-Allow-Credentials"
+ * to "true".
+ * @param {Boolean} [on=true] - Set 'withCredentials' state
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.withCredentials = function (on) {
+ // This is browser-only functionality. Node side is no-op.
+ if (on === undefined) on = true;
+ this._withCredentials = on;
+ return this;
+};
+
+/**
+ * Set the max redirects to `n`. Does nothing in browser XHR implementation.
+ *
+ * @param {Number} n
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.redirects = function (n) {
+ this._maxRedirects = n;
+ return this;
+};
+
+/**
+ * Maximum size of buffered response body, in bytes. Counts uncompressed size.
+ * Default 200MB.
+ *
+ * @param {Number} n number of bytes
+ * @return {Request} for chaining
+ */
+RequestBase.prototype.maxResponseSize = function (n) {
+ if (typeof n !== 'number') {
+ throw new TypeError('Invalid argument');
+ }
+
+ this._maxResponseSize = n;
+ return this;
+};
+
+/**
+ * Convert to a plain javascript object (not JSON string) of scalar properties.
+ * Note as this method is designed to return a useful non-this value,
+ * it cannot be chained.
+ *
+ * @return {Object} describing method, url, and data of this request
+ * @api public
+ */
+
+RequestBase.prototype.toJSON = function () {
+ return {
+ method: this.method,
+ url: this.url,
+ data: this._data,
+ headers: this._header
+ };
+};
+
+/**
+ * Send `data` as the request body, defaulting the `.type()` to "json" when
+ * an object is given.
+ *
+ * Examples:
+ *
+ * // manual json
+ * request.post('/user')
+ * .type('json')
+ * .send('{"name":"tj"}')
+ * .end(callback)
+ *
+ * // auto json
+ * request.post('/user')
+ * .send({ name: 'tj' })
+ * .end(callback)
+ *
+ * // manual x-www-form-urlencoded
+ * request.post('/user')
+ * .type('form')
+ * .send('name=tj')
+ * .end(callback)
+ *
+ * // auto x-www-form-urlencoded
+ * request.post('/user')
+ * .type('form')
+ * .send({ name: 'tj' })
+ * .end(callback)
+ *
+ * // defaults to x-www-form-urlencoded
+ * request.post('/user')
+ * .send('name=tobi')
+ * .send('species=ferret')
+ * .end(callback)
+ *
+ * @param {String|Object} data
+ * @return {Request} for chaining
+ * @api public
+ */
+
+// eslint-disable-next-line complexity
+RequestBase.prototype.send = function (data) {
+ const isObject_ = isObject(data);
+ let type = this._header['content-type'];
+
+ if (this._formData) {
+ throw new Error(
+ ".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"
+ );
+ }
+
+ if (isObject_ && !this._data) {
+ if (Array.isArray(data)) {
+ this._data = [];
+ } else if (!this._isHost(data)) {
+ this._data = {};
+ }
+ } else if (data && this._data && this._isHost(this._data)) {
+ throw new Error("Can't merge these send calls");
+ }
+
+ // merge
+ if (isObject_ && isObject(this._data)) {
+ for (const key in data) {
+ if (typeof data[key] == 'bigint' && !data[key].toJSON)
+ throw new Error('Cannot serialize BigInt value to json');
+ if (hasOwn(data, key)) this._data[key] = data[key];
+ }
+ }
+ else if (typeof data === 'bigint') throw new Error("Cannot send value of type BigInt");
+ else if (typeof data === 'string') {
+ // default to x-www-form-urlencoded
+ if (!type) this.type('form');
+ type = this._header['content-type'];
+ if (type) type = type.toLowerCase().trim();
+ if (type === 'application/x-www-form-urlencoded') {
+ this._data = this._data ? `${this._data}&${data}` : data;
+ } else {
+ this._data = (this._data || '') + data;
+ }
+ } else {
+ this._data = data;
+ }
+
+ if (!isObject_ || this._isHost(data)) {
+ return this;
+ }
+
+ // default to json
+ if (!type) this.type('json');
+ return this;
+};
+
+/**
+ * Sort `querystring` by the sort function
+ *
+ *
+ * Examples:
+ *
+ * // default order
+ * request.get('/user')
+ * .query('name=Nick')
+ * .query('search=Manny')
+ * .sortQuery()
+ * .end(callback)
+ *
+ * // customized sort function
+ * request.get('/user')
+ * .query('name=Nick')
+ * .query('search=Manny')
+ * .sortQuery(function(a, b){
+ * return a.length - b.length;
+ * })
+ * .end(callback)
+ *
+ *
+ * @param {Function} sort
+ * @return {Request} for chaining
+ * @api public
+ */
+
+RequestBase.prototype.sortQuery = function (sort) {
+ // _sort default to true but otherwise can be a function or boolean
+ this._sort = typeof sort === 'undefined' ? true : sort;
+ return this;
+};
+
+/**
+ * Compose querystring to append to req.url
+ *
+ * @api private
+ */
+RequestBase.prototype._finalizeQueryString = function () {
+ const query = this._query.join('&');
+ if (query) {
+ this.url += (this.url.includes('?') ? '&' : '?') + query;
+ }
+
+ this._query.length = 0; // Makes the call idempotent
+
+ if (this._sort) {
+ const index = this.url.indexOf('?');
+ if (index >= 0) {
+ const queryArray = this.url.slice(index + 1).split('&');
+ if (typeof this._sort === 'function') {
+ queryArray.sort(this._sort);
+ } else {
+ queryArray.sort();
+ }
+
+ this.url = this.url.slice(0, index) + '?' + queryArray.join('&');
+ }
+ }
+};
+
+// For backwards compat only
+RequestBase.prototype._appendQueryString = () => {
+ console.warn('Unsupported');
+};
+
+/**
+ * Invoke callback with timeout error.
+ *
+ * @api private
+ */
+
+RequestBase.prototype._timeoutError = function (reason, timeout, errno) {
+ if (this._aborted) {
+ return;
+ }
+
+ const error = new Error(`${reason + timeout}ms exceeded`);
+ error.timeout = timeout;
+ error.code = 'ECONNABORTED';
+ error.errno = errno;
+ this.timedout = true;
+ this.timedoutError = error;
+ this.abort();
+ this.callback(error);
+};
+
+RequestBase.prototype._setTimeouts = function () {
+ const self = this;
+
+ // deadline
+ if (this._timeout && !this._timer) {
+ this._timer = setTimeout(() => {
+ self._timeoutError('Timeout of ', self._timeout, 'ETIME');
+ }, this._timeout);
+ }
+
+ // response timeout
+ if (this._responseTimeout && !this._responseTimeoutTimer) {
+ this._responseTimeoutTimer = setTimeout(() => {
+ self._timeoutError(
+ 'Response timeout of ',
+ self._responseTimeout,
+ 'ETIMEDOUT'
+ );
+ }, this._responseTimeout);
+ }
+};
diff --git a/src/response-base.js b/src/response-base.js
new file mode 100644
index 000000000..64ae231a2
--- /dev/null
+++ b/src/response-base.js
@@ -0,0 +1,119 @@
+/**
+ * Module dependencies.
+ */
+
+const utils = require('./utils');
+
+/**
+ * Expose `ResponseBase`.
+ */
+
+module.exports = ResponseBase;
+
+/**
+ * Initialize a new `ResponseBase`.
+ *
+ * @api public
+ */
+
+function ResponseBase() {}
+
+/**
+ * Get case-insensitive `field` value.
+ *
+ * @param {String} field
+ * @return {String}
+ * @api public
+ */
+
+ResponseBase.prototype.get = function (field) {
+ return this.header[field.toLowerCase()];
+};
+
+/**
+ * Set header related properties:
+ *
+ * - `.type` the content type without params
+ *
+ * A response of "Content-Type: text/plain; charset=utf-8"
+ * will provide you with a `.type` of "text/plain".
+ *
+ * @param {Object} header
+ * @api private
+ */
+
+ResponseBase.prototype._setHeaderProperties = function (header) {
+ // TODO: moar!
+ // TODO: make this a util
+
+ // content-type
+ const ct = header['content-type'] || '';
+ this.type = utils.type(ct);
+
+ // params
+ const parameters = utils.params(ct);
+ for (const key in parameters) {
+ if (Object.prototype.hasOwnProperty.call(parameters, key))
+ this[key] = parameters[key];
+ }
+
+ this.links = {};
+
+ // links
+ try {
+ if (header.link) {
+ this.links = utils.parseLinks(header.link);
+ }
+ } catch (err) {
+ // ignore
+ }
+};
+
+/**
+ * Set flags such as `.ok` based on `status`.
+ *
+ * For example a 2xx response will give you a `.ok` of __true__
+ * whereas 5xx will be __false__ and `.error` will be __true__. The
+ * `.clientError` and `.serverError` are also available to be more
+ * specific, and `.statusType` is the class of error ranging from 1..5
+ * sometimes useful for mapping respond colors etc.
+ *
+ * "sugar" properties are also defined for common cases. Currently providing:
+ *
+ * - .noContent
+ * - .badRequest
+ * - .unauthorized
+ * - .notAcceptable
+ * - .notFound
+ *
+ * @param {Number} status
+ * @api private
+ */
+
+ResponseBase.prototype._setStatusProperties = function (status) {
+ const type = Math.trunc(status / 100);
+
+ // status / class
+ this.statusCode = status;
+ this.status = this.statusCode;
+ this.statusType = type;
+
+ // basics
+ this.info = type === 1;
+ this.ok = type === 2;
+ this.redirect = type === 3;
+ this.clientError = type === 4;
+ this.serverError = type === 5;
+ this.error = type === 4 || type === 5 ? this.toError() : false;
+
+ // sugar
+ this.created = status === 201;
+ this.accepted = status === 202;
+ this.noContent = status === 204;
+ this.badRequest = status === 400;
+ this.unauthorized = status === 401;
+ this.notAcceptable = status === 406;
+ this.forbidden = status === 403;
+ this.notFound = status === 404;
+ this.unprocessableEntity = status === 422;
+};
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 000000000..8f16ab0dc
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,133 @@
+
+/**
+ * Return the mime type for the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+exports.type = (string_) => string_.split(/ *; */).shift();
+
+/**
+ * Return header field parameters.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+exports.params = (value) => {
+ const object = {};
+ for (const string_ of value.split(/ *; */)) {
+ const parts = string_.split(/ *= */);
+ const key = parts.shift();
+ const value = parts.shift();
+
+ if (key && value) object[key] = value;
+ }
+
+ return object;
+};
+
+/**
+ * Parse Link header fields.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+exports.parseLinks = (value) => {
+ const object = {};
+ for (const string_ of value.split(/ *, */)) {
+ const parts = string_.split(/ *; */);
+ const url = parts[0].slice(1, -1);
+ const rel = parts[1].split(/ *= */)[1].slice(1, -1);
+ object[rel] = url;
+ }
+
+ return object;
+};
+
+/**
+ * Strip content related fields from `header`.
+ *
+ * @param {Object} header
+ * @return {Object} header
+ * @api private
+ */
+
+exports.cleanHeader = (header, changesOrigin) => {
+ delete header['content-type'];
+ delete header['content-length'];
+ delete header['transfer-encoding'];
+ delete header.host;
+ // secuirty
+ if (changesOrigin) {
+ delete header.authorization;
+ delete header.cookie;
+ }
+
+ return header;
+};
+
+exports.normalizeHostname = (hostname) => {
+ const [,normalized] = hostname.match(/^\[([^\]]+)\]$/) || [];
+ return normalized || hostname;
+};
+
+/**
+ * Check if `obj` is an object.
+ *
+ * @param {Object} object
+ * @return {Boolean}
+ * @api private
+ */
+exports.isObject = (object) => {
+ return object !== null && typeof object === 'object';
+};
+
+/**
+ * Object.hasOwn fallback/polyfill.
+ *
+ * @type {(object: object, property: string) => boolean} object
+ * @api private
+ */
+exports.hasOwn =
+ Object.hasOwn ||
+ function (object, property) {
+ if (object == null) {
+ throw new TypeError('Cannot convert undefined or null to object');
+ }
+
+ return Object.prototype.hasOwnProperty.call(new Object(object), property);
+ };
+
+exports.mixin = (target, source) => {
+ for (const key in source) {
+ if (exports.hasOwn(source, key)) {
+ target[key] = source[key];
+ }
+ }
+};
+
+/**
+ * Check if the response is compressed using Gzip or Deflate.
+ * @param {Object} res
+ * @return {Boolean}
+ */
+
+exports.isGzipOrDeflateEncoding = (res) => {
+ return new RegExp(/^\s*(?:deflate|gzip)\s*$/).test(res.headers['content-encoding']);
+};
+
+/**
+ * Check if the response is compressed using Brotli.
+ * @param {Object} res
+ * @return {Boolean}
+ */
+
+exports.isBrotliEncoding = (res) => {
+ return new RegExp(/^\s*(?:br)\s*$/).test(res.headers['content-encoding']);
+};
diff --git a/test/agent-base.js b/test/agent-base.js
new file mode 100644
index 000000000..592f996e8
--- /dev/null
+++ b/test/agent-base.js
@@ -0,0 +1,52 @@
+const assert = require('assert');
+const getSetup = require('./support/setup');
+
+const request = require('./support/client');
+
+describe('Agent', () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ it('should remember defaults', () => {
+ if (typeof Promise === 'undefined') {
+ return;
+ }
+
+ let called = 0;
+ let event_called = 0;
+ const agent = request
+ .agent()
+ .accept('json')
+ .use(() => {
+ called++;
+ })
+ .once('request', () => {
+ event_called++;
+ })
+ .query({ hello: 'world' })
+ .set('X-test', 'testing');
+ assert.equal(0, called);
+ assert.equal(0, event_called);
+
+ return agent
+ .get(`${base}/echo`)
+ .then((res) => {
+ assert.equal(1, called);
+ assert.equal(1, event_called);
+ assert.equal('application/json', res.headers.accept);
+ assert.equal('testing', res.headers['x-test']);
+
+ return agent.get(`${base}/querystring`);
+ })
+ .then((res) => {
+ assert.equal(2, called);
+ assert.equal(2, event_called);
+ assert.deepEqual({ hello: 'world' }, res.body);
+ });
+ });
+});
diff --git a/test/basic.js b/test/basic.js
index dc0a71e68..ab6b5703a 100644
--- a/test/basic.js
+++ b/test/basic.js
@@ -1,301 +1,775 @@
-var should = require('should');
-var assert = require('assert');
-var url = require('url');
-
-var request = require('../');
-
-var NODE = true;
-var uri = 'http://localhost:5000';
-if (typeof window !== 'undefined') {
- NODE = false;
- uri = '//' + window.location.host;
-}
-else {
- process.env.ZUUL_PORT = 5000;
- require('./support/server');
- uri = 'http://localhost:5000';
-}
-
-describe('request', function(){
-
- describe('with a callback', function(){
- it('should invoke .end()', function(done){
- request
- .get(uri + '/login', function(err, res){
- assert(res.status == 200);
+const assert = require('assert');
+const getSetup = require('./support/setup');
+
+const request = require('./support/client');
+
+describe('request', function () {
+ let setup;
+ let NODE;
+ let uri;
+
+ before(async () => {
+ setup = await getSetup();
+ NODE = setup.NODE;
+ uri = setup.uri;
+ });
+
+ this.timeout(20_000);
+
+ describe('res.statusCode', () => {
+ it('should set statusCode', (done) => {
+ request.get(`${uri}/login`, (error, res) => {
+ try {
+ assert.strictEqual(res.statusCode, 200);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('should allow the send shorthand', () => {
+ it('with callback in the method call', (done) => {
+ request.get(`${uri}/login`, (error, res) => {
+ assert.equal(res.status, 200);
done();
- })
- })
- })
+ });
+ });
- describe('.end()', function(){
- it('should issue a request', function(done){
- request
- .get(uri + '/login')
- .end(function(err, res){
- assert(res.status == 200);
+ it('with data in the method call', (done) => {
+ request.post(`${uri}/echo`, { foo: 'bar' }).end((error, res) => {
+ assert.equal('{"foo":"bar"}', res.text);
done();
});
- })
- })
+ });
- describe('res.error', function(){
- it('should should be an Error object', function(done){
- request
- .get(uri + '/error')
- .end(function(err, res){
- if (NODE) {
- res.error.message.should.equal('cannot GET /error (500)');
+ it('with callback and data in the method call', (done) => {
+ request.post(`${uri}/echo`, { foo: 'bar' }, (error, res) => {
+ assert.equal('{"foo":"bar"}', res.text);
+ done();
+ });
+ });
+ });
+
+ describe('with a callback', () => {
+ it('should invoke .end()', (done) => {
+ request.get(`${uri}/login`, (error, res) => {
+ try {
+ assert.equal(res.status, 200);
+ done();
+ } catch (err) {
+ done(err);
}
- else {
- res.error.message.should.equal('cannot GET ' + uri + '/error (500)');
+ });
+ });
+ });
+
+ describe('.end()', () => {
+ it('should issue a request', (done) => {
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert.equal(res.status, 200);
+ done();
+ } catch (err) {
+ done(err);
}
- assert(res.error.status === 500);
- assert(err, 'should have an error for 500');
- assert.equal(err.message, 'Internal Server Error');
- done();
});
- })
- })
+ });
- describe('res.header', function(){
- it('should be an object', function(done){
- request
- .get(uri + '/login')
- .end(function(err, res){
- assert('Express' == res.header['x-powered-by']);
- done();
+ it('is optional with a promise', () => {
+ if (typeof Promise === 'undefined') {
+ return;
+ }
+
+ return request
+ .get(`${uri}/login`)
+ .then((res) => res.status)
+ .then()
+ .then((status) => {
+ assert.equal(200, status, 'Real promises pass results through');
+ });
+ });
+
+ it('called only once with a promise', () => {
+ if (typeof Promise === 'undefined') {
+ return;
+ }
+
+ const request_ = request.get(`${uri}/unique`);
+
+ return Promise.all([request_, request_, request_]).then((results) => {
+ for (const item of results) {
+ assert.deepEqual(
+ item.body,
+ results[0].body,
+ 'It should keep returning the same result after being called once'
+ );
+ }
});
- })
- })
+ });
+ });
- describe('res.charset', function(){
- it('should be set when present', function(done){
+ describe('res.error', () => {
+ it('ok', (done) => {
+ let calledErrorEvent = false;
+ let calledOKHandler = false;
request
- .get(uri + '/login')
- .end(function(err, res){
- res.charset.should.equal('utf-8');
- done();
- });
- })
- })
+ .get(`${uri}/error`)
+ .ok((res) => {
+ assert.strictEqual(500, res.status);
+ calledOKHandler = true;
+ return true;
+ })
+ .on('error', (error) => {
+ calledErrorEvent = true;
+ })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.strictEqual(res.status, 500);
+ assert(!calledErrorEvent);
+ assert(calledOKHandler);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- describe('res.statusType', function(){
- it('should provide the first digit', function(done){
+ it('should be an Error object', (done) => {
+ let calledErrorEvent = false;
request
- .get(uri + '/login')
- .end(function(err, res){
- assert(!err, 'should not have an error for success responses');
- assert(200 == res.status);
- assert(2 == res.statusType);
- done();
+ .get(`${uri}/error`)
+ .on('error', (error) => {
+ assert.strictEqual(error.status, 500);
+ calledErrorEvent = true;
+ })
+ .end((error, res) => {
+ try {
+ if (NODE) {
+ res.error.message.should.equal('cannot GET /error (500)');
+ } else {
+ res.error.message.should.equal(`cannot GET ${uri}/error (500)`);
+ }
+
+ assert.strictEqual(res.error.status, 500);
+ assert(error, 'should have an error for 500');
+ assert.equal(error.message, 'Internal Server Error');
+ assert(calledErrorEvent);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('with .then() promise', () => {
+ if (typeof Promise === 'undefined') {
+ return;
+ }
+
+ return request.get(`${uri}/error`).then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(err.message, 'Internal Server Error');
+ }
+ );
+ });
+
+ it('with .ok() returning false', () => {
+ if (typeof Promise === 'undefined') {
+ return;
+ }
+
+ return request
+ .get(`${uri}/echo`)
+ .ok(() => false)
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'OK');
+ }
+ );
+ });
+
+ it('with .ok() throwing an Error', () => {
+ if (typeof Promise === 'undefined') {
+ return;
+ }
+
+ return request
+ .get(`${uri}/echo`)
+ .ok(() => {
+ throw new Error('boom');
+ })
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(200, err.status);
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'boom');
+ }
+ );
+ });
+
+ it('with .ok() throwing an Error with status', () => {
+ if (typeof Promise === 'undefined') {
+ return;
+ }
+
+ return request
+ .get(`${uri}/echo`)
+ .ok(() => {
+ const err = new Error('boom');
+ err.status = 404;
+ throw err;
+ })
+ .then(
+ () => {
+ assert.fail();
+ },
+ (err) => {
+ assert.equal(404, err.status);
+ assert.equal(200, err.response.status);
+ assert.equal(err.message, 'boom');
+ }
+ );
+ });
+ });
+
+ describe('res.header', () => {
+ it('should be an object', (done) => {
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert.equal('Express', res.header['x-powered-by']);
+ done();
+ } catch (err) {
+ done(err);
+ }
});
- })
- })
+ });
+ });
- describe('res.type', function(){
- it('should provide the mime-type void of params', function(done){
- request
- .get(uri + '/login')
- .end(function(err, res){
- res.type.should.equal('text/html');
- res.charset.should.equal('utf-8');
- done();
+ describe('set headers', () => {
+ before(() => {
+ Object.prototype.invalid = 'invalid';
+ });
+
+ after(() => {
+ delete Object.prototype.invalid;
+ });
+
+ it('should only set headers for ownProperties of header', (done) => {
+ try {
+ request
+ .get(`${uri}/echo-headers`)
+ .set('valid', 'ok')
+ .end((error, res) => {
+ if (
+ !error &&
+ res.body &&
+ res.body.valid &&
+ !res.body.hasOwnProperty('invalid')
+ ) {
+ return done();
+ }
+
+ done(error || new Error('fail'));
+ });
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ describe('res.charset', () => {
+ it('should be set when present', (done) => {
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ res.charset.should.equal('utf-8');
+ done();
+ } catch (err) {
+ done(err);
+ }
});
- })
- })
+ });
+ });
- describe('req.set(field, val)', function(){
- it('should set the header field', function(done){
+ describe('res.statusType', () => {
+ it('should provide the first digit', (done) => {
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ assert(!error, 'should not have an error for success responses');
+ assert.equal(200, res.status);
+ assert.equal(2, res.statusType);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('res.type', () => {
+ it('should provide the mime-type void of params', (done) => {
+ request.get(`${uri}/login`).end((error, res) => {
+ try {
+ res.type.should.equal('text/html');
+ res.charset.should.equal('utf-8');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('req.set(field, val)', () => {
+ it('should set the header field', (done) => {
request
- .post(uri + '/echo')
- .set('X-Foo', 'bar')
- .set('X-Bar', 'baz')
- .end(function(err, res){
- assert('bar' == res.header['x-foo']);
- assert('baz' == res.header['x-bar']);
- done();
- })
- })
- })
+ .post(`${uri}/echo`)
+ .set('X-Foo', 'bar')
+ .set('X-Bar', 'baz')
+ .end((error, res) => {
+ try {
+ assert.equal('bar', res.header['x-foo']);
+ assert.equal('baz', res.header['x-bar']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
- describe('req.set(obj)', function(){
- it('should set the header fields', function(done){
+ describe('req.set(obj)', () => {
+ it('should set the header fields', (done) => {
request
- .post(uri + '/echo')
- .set({ 'X-Foo': 'bar', 'X-Bar': 'baz' })
- .end(function(err, res){
- assert('bar' == res.header['x-foo']);
- assert('baz' == res.header['x-bar']);
- done();
- })
- })
- })
+ .post(`${uri}/echo`)
+ .set({ 'X-Foo': 'bar', 'X-Bar': 'baz' })
+ .end((error, res) => {
+ try {
+ assert.equal('bar', res.header['x-foo']);
+ assert.equal('baz', res.header['x-bar']);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
- describe('req.type(str)', function(){
- it('should set the Content-Type', function(done){
+ describe('req.type(str)', () => {
+ it('should set the Content-Type', (done) => {
request
- .post(uri + '/echo')
- .type('text/x-foo')
- .end(function(err, res){
- res.header['content-type'].should.equal('text/x-foo');
- done();
- });
- })
+ .post(`${uri}/echo`)
+ .type('text/x-foo')
+ .end((error, res) => {
+ try {
+ res.header['content-type'].should.equal('text/x-foo');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- it('should map "json"', function(done){
+ it('should map "json"', (done) => {
request
- .post(uri + '/echo')
- .type('json')
- .send('{"a": 1}')
- .end(function(err, res){
- res.should.be.json;
- done();
- });
- })
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('{"a": 1}')
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- it('should map "html"', function(done){
+ it('should map "html"', (done) => {
request
- .post(uri + '/echo')
- .type('html')
- .end(function(err, res){
- res.header['content-type'].should.equal('text/html');
- done();
- });
- })
- })
+ .post(`${uri}/echo`)
+ .type('html')
+ .end((error, res) => {
+ try {
+ res.header['content-type'].should.equal('text/html');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
- describe('req.accept(str)', function(){
- it('should set Accept', function(done){
+ describe('req.accept(str)', () => {
+ it('should set Accept', (done) => {
request
- .get(uri + '/echo')
- .accept('text/x-foo')
- .end(function(err, res){
- res.header['accept'].should.equal('text/x-foo');
- done();
- });
- })
+ .get(`${uri}/echo`)
+ .accept('text/x-foo')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('text/x-foo');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- it('should map "json"', function(done){
+ it('should map "json"', (done) => {
request
- .get(uri + '/echo')
- .accept('json')
- .end(function(err, res){
- res.header['accept'].should.equal('application/json');
- done();
- });
- })
+ .get(`${uri}/echo`)
+ .accept('json')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('application/json');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- it('should map "xml"', function(done){
+ it('should map "xml"', (done) => {
request
- .get(uri + '/echo')
- .accept('xml')
- .end(function(err, res){
- res.header['accept'].should.equal('application/xml');
- done();
- });
- })
+ .get(`${uri}/echo`)
+ .accept('xml')
+ .end((error, res) => {
+ try {
+ // Mime module keeps changing this :(
+ assert(
+ res.header.accept == 'application/xml' ||
+ res.header.accept == 'text/xml'
+ );
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- it('should map "html"', function(done){
+ it('should map "html"', (done) => {
request
- .get(uri + '/echo')
- .accept('html')
- .end(function(err, res){
- res.header['accept'].should.equal('text/html');
- done();
- });
- })
- })
+ .get(`${uri}/echo`)
+ .accept('html')
+ .end((error, res) => {
+ try {
+ res.header.accept.should.equal('text/html');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
- describe('req.send(str)', function(){
- it('should write the string', function(done){
+ describe('req.send(str)', () => {
+ it('should write the string', (done) => {
request
- .post(uri + '/echo')
- .type('json')
- .send('{"name":"tobi"}')
- .end(function(err, res){
- res.text.should.equal('{"name":"tobi"}');
- done();
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('{"name":"tobi"}')
+ .end((error, res) => {
+ try {
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('req.send(Object)', () => {
+ it('should default to json', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ describe('when called several times', () => {
+ it('should merge the objects', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .send({ age: 1 })
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ if (NODE) {
+ res.buffered.should.be.true();
+ }
+
+ res.text.should.equal('{"name":"tobi","age":1}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
});
- })
- })
+ });
+ });
- describe('req.send(Object)', function(){
- it('should default to json', function(done){
+ describe('.end(fn)', () => {
+ it('should check arity', (done) => {
request
- .post(uri + '/echo')
- .send({ name: 'tobi' })
- .end(function(err, res){
- res.should.be.json
- res.text.should.equal('{"name":"tobi"}');
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should emit request', (done) => {
+ const request_ = request.post(`${uri}/echo`);
+ request_.on('request', (request) => {
+ assert.equal(request_, request);
done();
});
- })
+ request_.end();
+ });
- describe('when called several times', function(){
- it('should merge the objects', function(done){
- request
- .post(uri + '/echo')
+ it('should emit response', (done) => {
+ request
+ .post(`${uri}/echo`)
.send({ name: 'tobi' })
- .send({ age: 1 })
- .end(function(err, res){
- res.should.be.json
- if (NODE) {
- res.buffered.should.be.true;
- }
- res.text.should.equal('{"name":"tobi","age":1}');
+ .on('response', (res) => {
+ res.text.should.equal('{"name":"tobi"}');
done();
- });
- })
- })
- })
+ })
+ .end();
+ });
+ });
+
+ describe('.then(fulfill, reject)', () => {
+ it('should support successful fulfills with .then(fulfill)', (done) => {
+ if (typeof Promise === 'undefined') {
+ return done();
+ }
- describe('.end(fn)', function(){
- it('should check arity', function(done){
request
- .post(uri + '/echo')
- .send({ name: 'tobi' })
- .end(function(err, res){
- assert(null == err);
- res.text.should.equal('{"name":"tobi"}');
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .then((res) => {
+ res.type.should.equal('application/json');
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ });
+ });
+
+ it('should reject an error with .then(null, reject)', (done) => {
+ if (typeof Promise === 'undefined') {
+ return done();
+ }
+
+ request.get(`${uri}/error`).then(null, (err) => {
+ assert.equal(err.status, 500);
+ assert.equal(err.response.text, 'boom');
done();
});
- })
+ });
+ });
- it('should emit request', function(done){
- var req = request.post(uri + '/echo');
- req.on('request', function(request){
- assert(req == request);
+ describe('.catch(reject)', () => {
+ it('should reject an error with .catch(reject)', (done) => {
+ if (typeof Promise === 'undefined') {
+ return done();
+ }
+
+ request.get(`${uri}/error`).catch((err) => {
+ assert.equal(err.status, 500);
+ assert.equal(err.response.text, 'boom');
done();
});
- req.end();
- })
+ });
+ });
- it('should emit response', function(done){
- request
- .post(uri + '/echo')
- .send({ name: 'tobi' })
- .on('response', function(res){
- res.text.should.equal('{"name":"tobi"}');
- done();
- })
- .end();
- })
- })
-
- describe('.abort()', function(){
- it('should abort the request', function(done){
- var req = request
- .get(uri + '/delay/3000')
- .end(function(err, res){
- assert(false, 'should not complete the request');
+ describe('.abort()', () => {
+ it('should abort the request', (done) => {
+ const request_ = request.get(`${uri}/delay/3000`);
+ request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
});
- req.on('abort', done);
+ request_.on('error', (error) => {
+ done(error);
+ });
+ request_.on('abort', done);
- setTimeout(function() {
- req.abort();
+ setTimeout(() => {
+ request_.abort();
+ }, 500);
+ });
+ it('should abort the promise', () => {
+ const request_ = request.get(`${uri}/delay/3000`);
+ setTimeout(() => {
+ request_.abort();
+ }, 10);
+ return request_.then(
+ () => {
+ assert.fail('should not complete the request');
+ },
+ (err) => {
+ assert.equal('ABORTED', err.code);
+ }
+ );
+ });
+
+ it('should allow chaining .abort() several times', (done) => {
+ const request_ = request.get(`${uri}/delay/3000`);
+ request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ // This also verifies only a single 'done' event is emitted
+ request_.on('abort', done);
+
+ setTimeout(() => {
+ request_.abort().abort().abort();
}, 1000);
- })
- })
-})
+ });
+
+ it('should not allow abort then end', (done) => {
+ request
+ .get(`${uri}/delay/3000`)
+ .abort()
+ .end((error, res) => {
+ done(error ? undefined : new Error('Expected abort error'));
+ });
+ });
+ });
+
+ describe('req.toJSON()', () => {
+ it('should describe the request', (done) => {
+ const request_ = request.post(`${uri}/echo`).send({ foo: 'baz' });
+ request_.end((error, res) => {
+ try {
+ const json = request_.toJSON();
+ assert.equal('POST', json.method);
+ assert(/\/echo$/.test(json.url));
+ assert.equal('baz', json.data.foo);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('req.options()', () => {
+ it('should allow request body', (done) => {
+ request
+ .options(`${uri}/options/echo/body`)
+ .send({ foo: 'baz' })
+ .end((error, res) => {
+ try {
+ assert.equal(error, null);
+ assert.strictEqual(res.body.foo, 'baz');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('req.sortQuery()', () => {
+ it('nop with no querystring', (done) => {
+ request
+ .get(`${uri}/url`)
+ .sortQuery()
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should sort the request querystring', (done) => {
+ request
+ .get(`${uri}/url`)
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery()
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?order=desc&search=Manny');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should allow disabling sorting', (done) => {
+ request
+ .get(`${uri}/url`)
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery() // take default of true
+ .sortQuery(false) // override it in later call
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?search=Manny&order=desc');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should sort the request querystring using customized function', (done) => {
+ request
+ .get(`${uri}/url`)
+ .query('name=Nick')
+ .query('search=Manny')
+ .query('order=desc')
+ .sortQuery((a, b) => a.length - b.length)
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, '/url?name=Nick&order=desc&search=Manny');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+});
diff --git a/test/client/request.js b/test/client/request.js
index 1cecacc9e..3263bddb7 100644
--- a/test/client/request.js
+++ b/test/client/request.js
@@ -1,658 +1,305 @@
-var assert = require('assert');
-var request = require('../../');
-
-it('Request inheritance', function(){
- assert(request.get('/') instanceof request.Request);
-});
-
-it('request() simple GET without callback', function(next){
- request('GET', 'test/test.request.js').end();
- next();
-});
-
-it('request() simple GET', function(next){
- request('GET', '/ok').end(function(err, res){
- assert(res instanceof request.Response, 'respond with Response');
- assert(res.ok, 'response should be ok');
- assert(res.text, 'res.text');
- next();
- });
-});
-
-it('request() simple HEAD', function(next){
- request.head('/ok').end(function(err, res){
- assert(res instanceof request.Response, 'respond with Response');
- assert(res.ok, 'response should be ok');
- assert(!res.text, 'res.text');
- next();
- });
-});
-
-it('request() error object', function(next) {
- request('GET', '/error').end(function(err, res) {
- assert(err);
- assert(res.error, 'response should be an error');
- assert(res.error.message == 'cannot GET /error (500)');
- assert(res.error.status == 500);
- assert(res.error.method == 'GET');
- assert(res.error.url == '/error');
- next();
- });
-});
-
-it('request() GET 5xx', function(next){
- request('GET', '/error').end(function(err, res){
- assert(err);
- assert(err.message == 'Internal Server Error');
- assert(!res.ok, 'response should not be ok');
- assert(res.error, 'response should be an error');
- assert(!res.clientError, 'response should not be a client error');
- assert(res.serverError, 'response should be a server error');
- next();
- });
-});
-
-it('request() GET 4xx', function(next){
- request('GET', '/notfound').end(function(err, res){
- assert(err);
- assert.equal(err.message, 'Not Found');
- assert(!res.ok, 'response should not be ok');
- assert(res.error, 'response should be an error');
- assert(res.clientError, 'response should be a client error');
- assert(!res.serverError, 'response should not be a server error');
- next();
- });
-});
-
-it('request() GET 404 Not Found', function(next){
- request('GET', '/notfound').end(function(err, res){
- assert(err);
- assert(res.notFound, 'response should be .notFound');
- next();
- });
-});
-
-it('request() GET 400 Bad Request', function(next){
- request('GET', '/bad-request').end(function(err, res){
- assert(err);
- assert(res.badRequest, 'response should be .badRequest');
- next();
- });
-});
-
-it('request() GET 401 Bad Request', function(next){
- request('GET', '/unauthorized').end(function(err, res){
- assert(err);
- assert(res.unauthorized, 'response should be .unauthorized');
- next();
- });
-});
-
-it('request() GET 406 Not Acceptable', function(next){
- request('GET', '/not-acceptable').end(function(err, res){
- assert(err);
- assert(res.notAcceptable, 'response should be .notAcceptable');
- next();
- });
-});
-
-it('request() GET 204 No Content', function(next){
- request('GET', '/no-content').end(function(err, res){
- assert.ifError(err);
- assert(res.noContent, 'response should be .noContent');
- next();
- });
-});
-
-it('request() DELETE 204 No Content', function(next){
- request('DELETE', '/no-content').end(function(err, res){
- assert.ifError(err);
- assert(res.noContent, 'response should be .noContent');
- next();
- });
-});
-
-it('request() header parsing', function(next){
- request('GET', '/notfound').end(function(err, res){
- assert(err);
- assert('text/html; charset=utf-8' == res.header['content-type']);
- assert('Express' == res.header['x-powered-by']);
- next();
- });
-});
-
-it('request() .status', function(next){
- request('GET', '/notfound').end(function(err, res){
- assert(err);
- assert(404 == res.status, 'response .status');
- assert(4 == res.statusType, 'response .statusType');
- next();
- });
-});
-
-it('get()', function(next){
- request.get('/notfound').end(function(err, res){
- assert(err);
- assert(404 == res.status, 'response .status');
- assert(4 == res.statusType, 'response .statusType');
- next();
- });
-});
-
-// This test results in a weird Jetty error on IE9 and IE11 saying PATCH is not a supported method. Looks like something's up with SauceLabs
-var isIE11 = !!navigator.userAgent.match(/Trident.*rv[ :]*11\./);
-var isIE9OrOlder = !window.atob;
-if (!isIE9OrOlder && !isIE11) { // Don't run on IE9 or older, or IE11
- it('patch()', function(next){
- request.patch('/user/12').end(function(err, res){
- assert('updated' == res.text);
+const assert = require('assert');
+const request = require('../support/client');
+
+describe('request', function () {
+ this.timeout(20_000);
+
+ it('request() error object', (next) => {
+ request('GET', '/error').end((error, res) => {
+ assert(error);
+ assert(res.error, 'response should be an error');
+ assert.equal(res.error.message, 'cannot GET /error (500)');
+ assert.equal(res.error.status, 500);
+ assert.equal(res.error.method, 'GET');
+ assert.equal(res.error.url, '/error');
next();
});
});
-}
-
-it('put()', function(next){
- request.put('/user/12').end(function(err, res){
- assert('updated' == res.text, 'response text');
- next();
- });
-});
-
-it('post()', function(next){
- request.post('/user').end(function(err, res){
- assert('created' == res.text, 'response text');
- next();
- });
-});
-
-it('del()', function(next){
- request.del('/user/12').end(function(err, res){
- assert('deleted' == res.text, 'response text');
- next();
- });
-});
-
-it('post() data', function(next){
- request.post('/todo/item')
- .type('application/octet-stream')
- .send('tobi')
- .end(function(err, res){
- assert('added "tobi"' == res.text, 'response text');
- next();
- });
-});
-
-it('request .type()', function(next){
- request
- .post('/user/12/pet')
- .type('urlencoded')
- .send('pet=tobi')
- .end(function(err, res){
- assert('added pet "tobi"' == res.text, 'response text');
- next();
- });
-});
-
-it('request .type() with alias', function(next){
- request
- .post('/user/12/pet')
- .type('application/x-www-form-urlencoded')
- .send('pet=tobi')
- .end(function(err, res){
- assert('added pet "tobi"' == res.text, 'response text');
- next();
- });
-});
-
-it('request .get() with no data or callback', function(next){
- request.get('/echo-header/content-type');
- next();
-});
-
-it('request .send() with no data only', function(next){
- request.post('/user/5/pet').type('urlencoded').send('pet=tobi');
- next();
-});
-
-it('request .send() with callback only', function(next){
- request
- .get('/echo-header/accept')
- .set('Accept', 'foo/bar')
- .end(function(err, res){
- assert('foo/bar' == res.text);
- next();
- });
-});
-
-it('request .accept() with json', function(next){
- request
- .get('/echo-header/accept')
- .accept('json')
- .end(function(err, res){
- assert('application/json' == res.text);
- next();
- });
-});
-
-it('request .accept() with application/json', function(next){
- request
- .get('/echo-header/accept')
- .accept('application/json')
- .end(function(err, res){
- assert('application/json' == res.text);
- next();
- });
-});
-
-it('request .accept() with xml', function(next){
- request
- .get('/echo-header/accept')
- .accept('xml')
- .end(function(err, res){
- assert('application/xml' == res.text, res.text);
- next();
- });
-});
-
-it('request .accept() with application/xml', function(next){
- request
- .get('/echo-header/accept')
- .accept('application/xml')
- .end(function(err, res){
- assert('application/xml' == res.text);
- next();
- });
-});
-// FIXME: ie6 will POST rather than GET here due to data(),
-// but I'm not 100% sure why. Newer IEs are OK.
-it('request .end()', function(next){
- request
- .get('/echo-header/content-type')
- .set('Content-Type', 'text/plain')
- .send('wahoo')
- .end(function(err, res){
- assert('text/plain' == res.text);
- next();
- });
-});
-
-it('request .send()', function(next){
- request
- .get('/echo-header/content-type')
- .set('Content-Type', 'text/plain')
- .send('wahoo')
- .end(function(err, res){
- assert('text/plain' == res.text);
- next();
- });
-});
-
-it('request .set()', function(next){
- request
- .get('/echo-header/content-type')
- .set('Content-Type', 'text/plain')
- .send('wahoo')
- .end(function(err, res){
- assert('text/plain' == res.text);
- next();
- });
-});
-
-it('request .set(object)', function(next){
- request
- .get('/echo-header/content-type')
- .set({ 'Content-Type': 'text/plain' })
- .send('wahoo')
- .end(function(err, res){
- assert('text/plain' == res.text);
- next();
- });
-});
+ // This test results in a weird Jetty error on IE9 and IE11 saying PATCH is not a supported method. Looks like something's up with SauceLabs
+ const isIE11 = Boolean(/Trident.*rv[ :]*11\./.test(navigator.userAgent));
+ const isIE9OrOlder = !window.atob;
+ if (!isIE9OrOlder && !isIE11) {
+ // Don't run on IE9 or older, or IE11
+ it('patch()', (next) => {
+ request.patch('/user/12').end((error, res) => {
+ assert.equal('updated', res.text);
+ next();
+ });
+ });
+ }
-it('POST urlencoded', function(next){
- request
- .post('/pet')
- .type('urlencoded')
- .send({ name: 'Manny', species: 'cat' })
- .end(function(err, res){
- assert('added Manny the cat' == res.text);
- next();
- });
-});
+ it('POST native FormData', (next) => {
+ if (!window.FormData) {
+ // Skip test if FormData is not supported by browser
+ return next();
+ }
-it('POST json', function(next){
- request
- .post('/pet')
- .type('json')
- .send({ name: 'Manny', species: 'cat' })
- .end(function(err, res){
- assert('added Manny the cat' == res.text);
- next();
- });
-});
+ const data = new FormData();
+ data.append('foo', 'bar');
-it('POST json array', function(next){
- request
- .post('/echo')
- .send([1,2,3])
- .end(function(err, res){
- assert('application/json' == res.header['content-type'].split(';')[0]);
- assert('[1,2,3]' == res.text);
- next();
- });
-});
+ request
+ .post('/echo')
+ .send(data)
+ .end((error, res) => {
+ assert.equal('multipart/form-data', res.type);
+ next();
+ });
+ });
+
+ it('defaults attached files to original file names', (next) => {
+ if (!window.FormData) {
+ // Skip test if FormData is are not supported by browser
+ return next();
+ }
+
+ try {
+ var file = new File([''], 'image.jpg', { type: 'image/jpeg' });
+ } catch (err) {
+ // Skip if file constructor not supported.
+ return next();
+ }
-it('POST json default', function(next){
- request
- .post('/pet')
- .send({ name: 'Manny', species: 'cat' })
- .end(function(err, res){
- assert('added Manny the cat' == res.text);
- next();
- });
-});
+ request
+ .post('/echo')
+ .attach('image', file)
+ .end((error, res) => {
+ const regx = new RegExp(`filename="${file.name}"`);
+ assert.notEqual(res.text.match(regx), null);
+ next();
+ });
+ });
+
+ it('attach() cannot be mixed with send()', () => {
+ if (!window.FormData || !window.File) {
+ // Skip test if FormData is are not supported by browser
+ return;
+ }
+
+ assert.throws(() => {
+ const file = new File([''], 'image.jpg', { type: 'image/jpeg' });
+ request.post('/echo').attach('image', file).send('hi');
+ });
-it('POST multiple .send() calls', function(next){
- request
- .post('/pet')
- .send({ name: 'Manny' })
- .send({ species: 'cat' })
- .end(function(err, res){
- assert('added Manny the cat' == res.text);
- next();
+ assert.throws(() => {
+ const file = new File([''], 'image.jpg', { type: 'image/jpeg' });
+ request.post('/echo').send('hi').attach('image', file);
+ });
});
-});
-
-it('POST multiple .send() strings', function(next){
- request
- .post('/echo')
- .send('user[name]=tj')
- .send('user[email]=tj@vision-media.ca')
- .end(function(err, res){
- assert('application/x-www-form-urlencoded' == res.header['content-type'].split(';')[0]);
- assert(res.text == 'user[name]=tj&user[email]=tj@vision-media.ca')
- next();
- })
-});
-
-it('POST native FormData', function(next){
- if (!window.FormData) {
- // Skip test if FormData is not supported by browser
- return next();
- }
- var data = new FormData();
- data.append('foo', 'bar');
-
- request
- .post('/echo')
- .send(data)
- .end(function(err, res){
- assert('multipart/form-data' == res.type);
+ it('GET invalid json', (next) => {
+ request.get('/invalid-json').end((error, res) => {
+ assert(error.parse);
+ assert.deepEqual(
+ error.rawResponse,
+ ")]}', {'header':{'code':200,'text':'OK','version':'1.0'},'data':'some data'}"
+ );
next();
});
-});
-
-it('GET .type', function(next){
- request
- .get('/pets')
- .end(function(err, res){
- assert('application/json' == res.type);
- next();
- });
-});
-
-it('GET Content-Type params', function(next){
- request
- .get('/text')
- .end(function(err, res){
- assert('utf-8' == res.charset);
- next();
- });
-});
-
-it('GET json', function(next){
- request
- .get('/pets')
- .end(function(err, res){
- assert.deepEqual(res.body, ['tobi', 'loki', 'jane']);
- next();
- });
-});
-
-it('GET x-www-form-urlencoded', function(next){
- request
- .get('/foo')
- .end(function(err, res){
- assert.deepEqual(res.body, { foo: 'bar' });
- next();
});
-});
-it('GET shorthand', function(next){
- request.get('/foo', function(err, res){
- assert('foo=bar' == res.text);
- next();
+ it('GET querystring empty objects', (next) => {
+ const request_ = request.get('/querystring').query({});
+ request_.end((error, res) => {
+ assert.deepEqual(request_._query, []);
+ assert.deepEqual(res.body, {});
+ next();
+ });
});
-});
-it('POST shorthand', function(next){
- request.post('/user/0/pet', { pet: 'tobi' }, function(err, res){
- assert('added pet "tobi"' == res.text);
- next();
+ it('GET querystring object .get(uri, obj)', (next) => {
+ request.get('/querystring', { search: 'Manny' }).end((error, res) => {
+ assert.deepEqual(res.body, { search: 'Manny' });
+ next();
+ });
});
-});
-it('POST shorthand without callback', function(next){
- request.post('/user/0/pet', { pet: 'tobi' }).end(function(err, res){
- assert('added pet "tobi"' == res.text);
- next();
+ it('GET querystring object .get(uri, obj, fn)', (next) => {
+ request.get('/querystring', { search: 'Manny' }, (error, res) => {
+ assert.deepEqual(res.body, { search: 'Manny' });
+ next();
+ });
});
-});
-it('GET querystring object', function(next){
- request
- .get('/querystring')
- .query({ search: 'Manny' })
- .end(function(err, res){
- assert.deepEqual(res.body, { search: 'Manny' });
- next();
+ it('GET querystring object with null value', (next) => {
+ request.get('/url', { nil: null }).end((error, res) => {
+ assert.equal(res.text, '/url?nil');
+ next();
+ });
});
-});
-it('GET querystring append original', function(next){
- request
- .get('/querystring?search=Manny')
- .query({ range: '1..5' })
- .end(function(err, res){
- assert.deepEqual(res.body, { search: 'Manny', range: '1..5' });
- next();
- });
-});
+ it('GET blob object', (next) => {
+ if (typeof Blob === 'undefined') {
+ return next();
+ }
-it('GET querystring multiple objects', function(next){
- request
- .get('/querystring')
- .query({ search: 'Manny' })
- .query({ range: '1..5' })
- .query({ order: 'desc' })
- .end(function(err, res){
- assert.deepEqual(res.body, { search: 'Manny', range: '1..5', order: 'desc' });
- next();
+ request
+ .get('/blob', { foo: 'bar' })
+ .responseType('blob')
+ .end((error, res) => {
+ assert(res.xhr.response instanceof Blob);
+ assert(res.body instanceof Blob);
+ next();
+ });
+ });
+
+ it('Reject node-only function', () => {
+ assert.throws(() => {
+ request.get().write();
+ });
+ assert.throws(() => {
+ request.get().pipe();
+ });
});
-});
-it('GET querystring empty objects', function(next){
- var req = request
- .get('/querystring')
- .query({})
- .end(function(err, res){
- assert.deepEqual(req._query, []);
- assert.deepEqual(res.body, {});
- next();
- });
-});
+ window.btoa = window.btoa || null;
+ it('basic auth', (next) => {
+ window.btoa = window.btoa || require('Base64').btoa;
-it('GET querystring with strings', function(next){
- request
- .get('/querystring')
- .query('search=Manny')
- .query('range=1..5')
- .query('order=desc')
- .end(function(err, res){
- assert.deepEqual(res.body, { search: 'Manny', range: '1..5', order: 'desc' });
- next();
+ request
+ .post('/auth')
+ .auth('foo', 'bar')
+ .end((error, res) => {
+ assert.equal('foo', res.body.user);
+ assert.equal('bar', res.body.pass);
+ next();
+ });
});
-});
-it('GET querystring with strings and objects', function(next){
- request
- .get('/querystring')
- .query('search=Manny')
- .query({ order: 'desc', range: '1..5' })
- .end(function(err, res){
- assert.deepEqual(res.body, { search: 'Manny', range: '1..5', order: 'desc' });
- next();
- });
-});
+ it('auth type "basic"', (next) => {
+ window.btoa = window.btoa || require('Base64').btoa;
-it('GET querystring object .get(uri, obj)', function(next){
- request
- .get('/querystring', { search: 'Manny' })
- .end(function(err, res){
- assert.deepEqual(res.body, { search: 'Manny' });
- next();
+ request
+ .post('/auth')
+ .auth('foo', 'bar', { type: 'basic' })
+ .end((error, res) => {
+ assert.equal('foo', res.body.user);
+ assert.equal('bar', res.body.pass);
+ next();
+ });
});
-});
-it('GET querystring object .get(uri, obj, fn)', function(next){
- request
- .get('/querystring', { search: 'Manny'}, function(err, res){
- assert.deepEqual(res.body, { search: 'Manny' });
- next();
- });
-});
+ it('auth type "auto"', (next) => {
+ window.btoa = window.btoa || require('Base64').btoa;
-it('request(method, url)', function(next){
- request('GET', '/foo').end(function(err, res){
- assert('bar' == res.body.foo);
- next();
+ request
+ .post('/auth')
+ .auth('foo', 'bar', { type: 'auto' })
+ .end((error, res) => {
+ assert.equal('foo', res.body.user);
+ assert.equal('bar', res.body.pass);
+ next();
+ });
});
-});
-it('request(url)', function(next){
- request('/foo').end(function(err, res){
- assert('bar' == res.body.foo);
- next();
- });
-});
+ it('progress event listener on xhr object registered when some on the request', () => {
+ const request_ = request.get('/foo').on('progress', (data) => {});
+ request_.end();
-it('request(url, fn)', function(next){
- request('/foo', function(err, res){
- assert('bar' == res.body.foo);
- next();
+ if (request_.xhr.upload) {
+ // Only run assertion on capable browsers
+ assert.notEqual(null, request_.xhr.upload.onprogress);
+ }
});
-});
-it('req.timeout(ms)', function(next){
- request
- .get('/delay/3000')
- .timeout(1000)
- .end(function(err, res){
- assert(err, 'error missing');
- assert(1000 == err.timeout, 'err.timeout missing');
- assert('timeout of 1000ms exceeded' == err.message, 'err.message incorrect');
- assert(null == res);
- next();
- })
-})
-
-it('req.timeout(ms) with redirect', function(next) {
- request
- .get('/delay/const')
- .timeout(1000)
- .end(function(err, res) {
- assert(err, 'error missing');
- assert(1000 == err.timeout, 'err.timeout missing');
- assert('timeout of 1000ms exceeded' == err.message, 'err.message incorrect');
- assert(null == res);
- next();
- });
-});
+ it('no progress event listener on xhr object when none registered on request', () => {
+ const request_ = request.get('/foo');
+ request_.end();
-window.btoa = window.btoa || null;
-it('basic auth', function(next){
- window.btoa = window.btoa || require('Base64').btoa;
-
- request
- .post('/auth')
- .auth('foo', 'bar')
- .end(function(err, res){
- assert('foo' == res.body.user);
- assert('bar' == res.body.pass);
- next();
+ if (request_.xhr.upload) {
+ // Only run assertion on capable browsers
+ assert.strictEqual(null, request_.xhr.upload.onprogress);
+ }
});
-});
-it('request event', function(next){
- request
- .get('/foo')
- .on('request', function(req){
- assert('/foo' == req.url);
- next();
- })
- .end();
-});
-
-it('response event', function(next){
- request
- .get('/foo')
- .on('response', function(res){
- assert('bar' == res.body.foo);
- next();
- })
- .end();
-});
+ it('Request#parse overrides body parser no matter Content-Type', (done) => {
+ let runParser = false;
-it('progress event listener on xhr object registered when some on the request', function(){
- var req = request
- .get('/foo')
- .on('progress', function(data) {
- })
- .end();
+ function testParser(data) {
+ runParser = true;
+ return JSON.stringify(data);
+ }
- if (req.xhr.upload) { // Only run assertion on capable browsers
- assert(null !== req.xhr.upload.onprogress);
- }
-});
+ request
+ .post('/user')
+ .serialize(testParser)
+ .type('json')
+ .send({ foo: 123 })
+ .end((error) => {
+ if (error) return done(error);
+ assert(runParser);
+ done();
+ });
+ });
+
+ // Don't run on browsers without xhr2 support
+ if ('FormData' in window) {
+ it('xhr2 download file old hack', (next) => {
+ request.parse['application/vnd.superagent'] = (object) => object;
+
+ request
+ .get('/arraybuffer')
+ .on('request', function () {
+ this.xhr.responseType = 'arraybuffer';
+ })
+ .on('response', (res) => {
+ assert(res.body instanceof ArrayBuffer);
+ next();
+ })
+ .end();
+ });
-it('no progress event listener on xhr object when none registered on request', function(){
- var req = request
- .get('/foo')
- .end();
+ it('xhr2 download file responseType', (next) => {
+ request.parse['application/vnd.superagent'] = (object) => object;
+
+ request
+ .get('/arraybuffer')
+ .responseType('arraybuffer')
+ .on('response', (res) => {
+ assert(res.body instanceof ArrayBuffer);
+ next();
+ })
+ .end();
+ });
- if (req.xhr.upload) { // Only run assertion on capable browsers
- assert(null === req.xhr.upload.onprogress);
+ it('get error status code and rawResponse on file download', (next) => {
+ request
+ .get('/arraybuffer-unauthorized')
+ .responseType('arraybuffer')
+ .end((error, res) => {
+ assert.equal(error.status, 401);
+ assert(res.body instanceof ArrayBuffer);
+ assert(error.response.body instanceof ArrayBuffer);
+ const decodedString = String.fromCharCode.apply(
+ null,
+ new Uint8Array(res.body)
+ );
+ assert(
+ decodedString,
+ '{"message":"Authorization has been denied for this request."}'
+ );
+ next();
+ });
+ });
}
-});
-// Don't run on browsers without xhr2 support
-if ('FormData' in window) {
- it('xhr2 download file', function(next) {
- request.parse['application/vnd.superagent'] = function (obj) {
- return obj;
- };
+ it('parse should take precedence over default parse', (done) => {
+ request
+ .get('/foo')
+ .parse((res, text) => `customText: ${res.status}`)
+ .end((error, res) => {
+ assert(res.ok);
+ assert(res.body === 'customText: 200');
+ done();
+ });
+ });
+ it('handles `xhr.open()` errors', (done) => {
request
- .get('/arraybuffer')
- .on('request', function () {
- this.xhr.responseType = 'arraybuffer';
- })
- .on('response', function(res) {
- assert(res.body instanceof ArrayBuffer);
- next();
- })
- .end();
+ .get('http://foo\0.com') // throws "Failed to execute 'open' on 'XMLHttpRequest': Invalid URL"
+ .end((error, res) => {
+ assert(error);
+ done();
+ });
});
-}
+});
diff --git a/test/client/serialize.js b/test/client/serialize.js
index c89fc1828..f61773715 100644
--- a/test/client/serialize.js
+++ b/test/client/serialize.js
@@ -1,44 +1,90 @@
-var assert = require('assert');
+const assert = require('assert');
-var request = require('../../');
+const request = require('../support/client');
-function serialize(obj, res) {
- var val = request.serializeObject(obj);
- assert(val == res
- , JSON.stringify(obj) + ' to "' + res + '" serialization failed. got: '
- + '"' + val + '"');
+function serialize(object, res) {
+ const value = request.serializeObject(object);
+ assert.equal(
+ value,
+ res,
+ `${JSON.stringify(
+ object
+ )} to "${res}" serialization failed. got: "${value}"`
+ );
}
-function parse(str, obj) {
- var val = request.parseString(str);
- assert.deepEqual(val
- , obj
- , '"' + str + '" to '
- + JSON.stringify(obj) + ' parse failed. got: '
- + JSON.stringify(val));
+function parse(string_, object) {
+ const value = request.parseString(string_);
+ assert.deepEqual(
+ value,
+ object,
+ `"${string_}" to ${JSON.stringify(
+ object
+ )} parse failed. got: ${JSON.stringify(value)}`
+ );
}
-describe('request.serializeObject()', function(){
- it('should serialize', function() {
+describe('request.serializeObject()', () => {
+ it('should serialize', () => {
serialize('test', 'test');
serialize('foo=bar', 'foo=bar');
serialize({ foo: 'bar' }, 'foo=bar');
- serialize({ foo: null }, '');
+ serialize({ foo: null }, 'foo');
serialize({ foo: 'null' }, 'foo=null');
serialize({ foo: undefined }, '');
serialize({ foo: 'undefined' }, 'foo=undefined');
serialize({ name: 'tj', age: 24 }, 'name=tj&age=24');
- serialize({ name: '&tj&' }, 'name=%26tj%26');
- serialize({ '&name&': 'tj' }, '%26name%26=tj');
+ serialize({ name: '&tj&' }, 'name=%26tj%26');
+ serialize({ '&name&': 'tj' }, '%26name%26=tj');
+ serialize({ hello: '`test`' }, 'hello=%60test%60');
+ serialize({ $hello: 'test' }, '$hello=test');
+ // eslint-disable-next-line no-dupe-keys
+ serialize({ foo: 'foo', foo: 'bar' }, 'foo=foo&foo=bar');
});
});
-describe('request.parseString()', function(){
- it('should parse', function() {
+describe('request.parseString()', () => {
+ it('should parse', () => {
parse('name=tj', { name: 'tj' });
parse('name=Manny&species=cat', { name: 'Manny', species: 'cat' });
- parse('redirect=/&ok', { redirect: '/', ok: 'undefined' });
+ parse('redirect=/&ok', { redirect: '/', ok: '' });
parse('%26name=tj', { '&name': 'tj' });
parse('name=tj%26', { name: 'tj&' });
+ parse('%60', { '`': '' });
+ });
+});
+
+describe('Merging objects', () => {
+ it("Don't mix FormData and JSON", () => {
+ if (!window.FormData) {
+ // Skip test if FormData is not supported by browser
+ return;
+ }
+
+ const data = new FormData();
+ data.append('foo', 'bar');
+
+ assert.throws(() => {
+ request.post('/echo').send(data).send({ allowed: false });
+ });
+ });
+
+ it("Don't mix Blob and JSON", () => {
+ if (!window.Blob) {
+ return;
+ }
+
+ request
+ .post('/echo')
+ .send(new Blob(['will be cleared']))
+ .send(false)
+ .send({ allowed: true });
+
+ assert.throws(() => {
+ request
+ .post('/echo')
+ .send(new Blob(['hello']))
+ .send({ allowed: false });
+ });
});
});
diff --git a/test/client/xdomain.js b/test/client/xdomain.js
index 0ea4708ab..477338aa9 100644
--- a/test/client/xdomain.js
+++ b/test/client/xdomain.js
@@ -1,33 +1,49 @@
-var assert = require('assert');
-var request = require('../../');
+const assert = require('assert');
+const request = require('../support/client');
-describe('xdomain', function(){
+describe('xdomain', function () {
+ this.timeout(20_000);
// TODO (defunctzombie) I am not certain this actually forces xdomain request
// use localtunnel.me and tunnel127.com alias instead
- it('should support req.withCredentials()', function(next){
+ it('should support req.withCredentials()', (next) => {
request
- .get('//' + window.location.host + '/xdomain')
- .withCredentials()
- .end(function(err, res){
- assert(200 == res.status);
- assert('tobi' == res.text);
- next();
- });
+ .get(`//${window.location.host}/xdomain`)
+ .withCredentials()
+ .end((error, res) => {
+ assert.equal(200, res.status);
+ assert.equal('tobi', res.text);
+ next();
+ });
});
// xdomain not supported in old IE and IE11 gives weird Jetty errors (looks like a SauceLabs issue)
- var isIE11 = !!navigator.userAgent.match(/Trident.*rv[ :]*11\./);
- var isIE9OrOlder = !window.atob;
- if (!isIE9OrOlder && !isIE11) { // Don't run on IE9 or older, or IE11
- it('should handle x-domain failure', function(next){
- request
- .get('//tunne127.com')
- .end(function(err, res){
- assert(err, 'error missing');
- assert(err.crossDomain, 'not .crossDomain');
+ const isIE11 = Boolean(/Trident.*rv[ :]*11\./.test(navigator.userAgent));
+ const isIE9OrOlder = !window.atob;
+ if (!isIE9OrOlder && !isIE11) {
+ // Don't run on IE9 or older, or IE11
+ it('should handle x-domain failure', (next) => {
+ request.get('//tunne127.com').end((error, res) => {
+ assert(error, 'error missing');
+ assert(error.crossDomain, 'not .crossDomain');
next();
});
});
+
+ it('should handle x-domain failure after repeat attempts', (next) => {
+ request
+ .get('//tunne127.com')
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'error missing');
+ assert(error.crossDomain, 'not .crossDomain');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
}
});
diff --git a/test/content-type.js b/test/content-type.js
new file mode 100644
index 000000000..097b93850
--- /dev/null
+++ b/test/content-type.js
@@ -0,0 +1,38 @@
+const assert = require('assert');
+const getSetup = require('./support/setup');
+
+const request = require('./support/client');
+
+describe('req.set("Content-Type", contentType)', function () {
+ let setup;
+ let uri;
+
+ before(async () => {
+ setup = await getSetup();
+ uri = setup.uri;
+ });
+
+ this.timeout(20_000);
+
+ it('should work with just the contentType component', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json')
+ .send({ name: 'tobi' })
+ .end((error) => {
+ assert(!error);
+ done();
+ });
+ });
+
+ it('should work with the charset component', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json; charset=utf-8')
+ .send({ name: 'tobi' })
+ .end((error) => {
+ assert(!error);
+ done();
+ });
+ });
+});
diff --git a/test/form.js b/test/form.js
new file mode 100644
index 000000000..6c9fc5f2b
--- /dev/null
+++ b/test/form.js
@@ -0,0 +1,158 @@
+const assert = require('assert');
+const should = require('should');
+
+const getSetup = require('./support/setup');
+const request = require('./support/client');
+
+if (!assert.deepStrictEqual) assert.deepStrictEqual = assert.deepEqual;
+
+describe('req.send(Object) as "form"', () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ describe('with req.type() set to form', () => {
+ it('should send x-www-form-urlencoded data', (done) => {
+ request
+ .post(`${base}/echo`)
+ .type('form')
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.text.should.equal('name=tobi');
+ done();
+ });
+ });
+ });
+
+ describe('when called several times', () => {
+ it('should merge the objects', (done) => {
+ request
+ .post(`${base}/echo`)
+ .type('form')
+ .send({ name: { first: 'tobi', last: 'holowaychuk' } })
+ .send({ age: '1' })
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.text.should.equal(
+ 'name%5Bfirst%5D=tobi&name%5Blast%5D=holowaychuk&age=1'
+ );
+ done();
+ });
+ });
+ });
+});
+
+describe('req.attach', () => {
+ it('ignores null file', (done) => {
+ request
+ .post('/echo')
+ .attach('image', null)
+ .end((error, res) => {
+ done();
+ });
+ });
+});
+
+describe('req.field', function () {
+ let setup;
+ let base;
+ let formDataSupported;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+
+ formDataSupported = setup.NODE || FormData !== 'undefined';
+ });
+
+ this.timeout(5000);
+ it('allow bools', (done) => {
+ if (!formDataSupported) {
+ return done();
+ }
+
+ request
+ .post(`${base}/formecho`)
+ .field('bools', true)
+ .field('strings', 'true')
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { bools: 'true', strings: 'true' });
+ done();
+ });
+ });
+
+ it('allow objects', (done) => {
+ if (!formDataSupported) {
+ return done();
+ }
+
+ request
+ .post(`${base}/formecho`)
+ .field({ bools: true, strings: 'true' })
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { bools: 'true', strings: 'true' });
+ done();
+ });
+ });
+
+ it('works with arrays in objects', (done) => {
+ if (!formDataSupported) {
+ return done();
+ }
+
+ request
+ .post(`${base}/formecho`)
+ .field({ numbers: [1, 2, 3] })
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { numbers: ['1', '2', '3'] });
+ done();
+ });
+ });
+
+ it('works with arrays', (done) => {
+ if (!formDataSupported) {
+ return done();
+ }
+
+ request
+ .post(`${base}/formecho`)
+ .field('letters', ['a', 'b', 'c'])
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.deepStrictEqual(res.body, { letters: ['a', 'b', 'c'] });
+ done();
+ });
+ });
+
+ it('throw when empty', () => {
+ should.throws(() => {
+ request.post(`${base}/echo`).field();
+ }, /name/);
+
+ should.throws(() => {
+ request.post(`${base}/echo`).field('name');
+ }, /val/);
+ });
+
+ it('cannot be mixed with send()', () => {
+ assert.throws(() => {
+ request.post('/echo').field('form', 'data').send('hi');
+ });
+
+ assert.throws(() => {
+ request.post('/echo').send('hi').field('form', 'data');
+ });
+ });
+});
diff --git a/test/json.js b/test/json.js
new file mode 100644
index 000000000..967c39040
--- /dev/null
+++ b/test/json.js
@@ -0,0 +1,286 @@
+const getSetup = require('./support/setup');
+
+const doesntWorkInHttp2 = !process.env.HTTP2_TEST;
+
+const assert = require('assert');
+const request = require('./support/client');
+
+describe('req.send(Object) as "json"', function () {
+ let setup;
+ let uri;
+ let doesntWorkInBrowserYet;
+
+ before(async () => {
+ setup = await getSetup();
+ uri = setup.uri;
+ doesntWorkInBrowserYet = setup.NODE;
+ });
+
+ this.timeout(20_000);
+
+ it('should default to json', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi"}');
+ done();
+ });
+ });
+
+ it('should work with arrays', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .send([1, 2, 3])
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('[1,2,3]');
+ done();
+ });
+ });
+
+ it('should work with value null', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('null')
+ .end((error, res) => {
+ res.should.be.json();
+ assert.strictEqual(res.body, null);
+ done();
+ });
+ });
+
+ it('should work with value false', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('false')
+ .end((error, res) => {
+ res.should.be.json();
+ res.body.should.equal(false);
+ done();
+ });
+ });
+
+ if (doesntWorkInBrowserYet)
+ it('should work with value 0', (done) => {
+ // fails in IE9
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('0')
+ .end((error, res) => {
+ res.should.be.json();
+ res.body.should.equal(0);
+ done();
+ });
+ });
+
+ it('should work with empty string value', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send('""')
+ .end((error, res) => {
+ res.should.be.json();
+ res.body.should.equal('');
+ done();
+ });
+ });
+
+ if (doesntWorkInBrowserYet && doesntWorkInHttp2)
+ it('should work with GET', (done) => {
+ request
+ .get(`${uri}/echo`)
+ .send({ tobi: 'ferret' })
+ .end((error, res) => {
+ try {
+ res.should.be.json();
+ res.text.should.equal('{"tobi":"ferret"}');
+ ({ tobi: 'ferret' }.should.eql(res.body));
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should work with vendor MIME type', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/vnd.example+json')
+ .send({ name: 'vendor' })
+ .end((error, res) => {
+ res.text.should.equal('{"name":"vendor"}');
+ ({ name: 'vendor' }.should.eql(res.body));
+ done();
+ });
+ });
+
+ it('should error for BigInt object', (done) => {
+ try {
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send({number: 1n})
+ throw new Error('Should have thrown error for object with BigInt')
+ } catch (error) {
+ assert.strictEqual(error.message, 'Cannot serialize BigInt value to json');
+ }
+ done();
+ });
+
+ describe('when BigInts have a .toJSON property', function () {
+ before(function () {
+ // eslint-disable-next-line node/no-unsupported-features/es-builtins
+ BigInt.prototype.toJSON = function () {
+ return this.toString();
+ };
+ });
+
+ it('should accept BigInt properties', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .send({ number: 1n })
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('{"number":"1"}');
+ done();
+ });
+ });
+
+ after(function () {
+ // eslint-disable-next-line node/no-unsupported-features/es-builtins
+ delete BigInt.prototype.toJSON;
+ });
+ });
+
+
+ it('should error for BigInt primitive', (done) => {
+ try {
+ request
+ .post(`${uri}/echo`)
+ .type('json')
+ .send(1n)
+ throw new Error('Should have thrown error for BigInt primitive')
+ } catch (error) {
+ assert.strictEqual(error.message, 'Cannot send value of type BigInt');
+ }
+ done();
+ });
+
+ describe('when called several times', () => {
+ it('should merge the objects', (done) => {
+ request
+ .post(`${uri}/echo`)
+ .send({ name: 'tobi' })
+ .send({ age: 1 })
+ .end((error, res) => {
+ res.should.be.json();
+ res.text.should.equal('{"name":"tobi","age":1}');
+ ({ name: 'tobi', age: 1 }.should.eql(res.body));
+ done();
+ });
+ });
+ });
+});
+
+describe('res.body', function () {
+ let setup;
+ let uri;
+ let doesntWorkInBrowserYet;
+
+ before(async () => {
+ setup = await getSetup();
+ uri = setup.uri;
+ doesntWorkInBrowserYet = setup.NODE;
+ });
+
+ this.timeout(20_000);
+
+ describe('application/json', () => {
+ it('should parse the body', (done) => {
+ request.get(`${uri}/json`).end((error, res) => {
+ res.text.should.equal('{"name":"manny"}');
+ res.body.should.eql({ name: 'manny' });
+ done();
+ });
+ });
+ });
+
+ if (doesntWorkInBrowserYet)
+ describe('HEAD requests', () => {
+ it('should not throw a parse error', (done) => {
+ request.head(`${uri}/json`).end((error, res) => {
+ try {
+ assert.strictEqual(error, null);
+ assert.strictEqual(res.text, undefined);
+ assert.strictEqual(Object.keys(res.body).length, 0);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('Invalid JSON response', () => {
+ it('should return the raw response', (done) => {
+ request.get(`${uri}/invalid-json`).end((error, res) => {
+ assert.deepEqual(
+ error.rawResponse,
+ ")]}', {'header':{'code':200,'text':'OK','version':'1.0'},'data':'some data'}"
+ );
+ done();
+ });
+ });
+
+ it('should return the http status code', (done) => {
+ request.get(`${uri}/invalid-json-forbidden`).end((error, res) => {
+ assert.equal(error.statusCode, 403);
+ done();
+ });
+ });
+ });
+
+ if (doesntWorkInBrowserYet)
+ describe('No content', () => {
+ it('should not throw a parse error', (done) => {
+ request.get(`${uri}/no-content`).end((error, res) => {
+ try {
+ assert.strictEqual(error, null);
+ assert.strictEqual(res.text, '');
+ assert.strictEqual(Object.keys(res.body).length, 0);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ if (doesntWorkInBrowserYet)
+ describe('application/json+hal', () => {
+ it('should parse the body', (done) => {
+ request.get(`${uri}/json-hal`).end((error, res) => {
+ if (error) return done(error);
+ res.text.should.equal('{"name":"hal 5000"}');
+ res.body.should.eql({ name: 'hal 5000' });
+ done();
+ });
+ });
+ });
+
+ if (doesntWorkInBrowserYet)
+ describe('vnd.collection+json', () => {
+ it('should parse the body', (done) => {
+ request.get(`${uri}/collection-json`).end((error, res) => {
+ res.text.should.equal('{"name":"chewbacca"}');
+ res.body.should.eql({ name: 'chewbacca' });
+ done();
+ });
+ });
+ });
+});
diff --git a/test/node/agency.js b/test/node/agency.js
index b2c0deaa6..b67c3eb0b 100644
--- a/test/node/agency.js
+++ b/test/node/agency.js
@@ -1,202 +1,207 @@
-var express = require('express')
- , app = express()
- , request = require('../../')
- , assert = require('assert')
- , should = require('should')
- , cookieParser = require('cookie-parser')
- , session = require('express-session');
+'use strict';
+
+const express = require('../support/express');
+
+const app = express();
+const request = require('../support/client');
+const assert = require('assert');
+const cookieParser = require('cookie-parser');
+const cookiejar = require('cookiejar');
+const session = require('express-session');
+let http = require('http');
+
+if (process.env.HTTP2_TEST) {
+ http = require('http2');
+ http.Http2ServerResponse.prototype._implicitHeader = function () {
+ this.writeHead(this.statusCode);
+ };
+}
app.use(cookieParser());
-app.use(session({
- secret: 'secret',
- resave: true,
- saveUninitialized: true
-}));
-
-app.post('/signin', function(req, res) {
- req.session.user = 'hunter@hunterloftis.com';
+app.use(
+ session({
+ secret: 'secret',
+ resave: true,
+ saveUninitialized: true
+ })
+);
+
+app.post('/signin', (request_, res) => {
+ request_.session.user = 'hunter@hunterloftis.com';
res.redirect('/dashboard');
});
-app.post('/setcookie', function(req, res) {
+app.post('/setcookie', (request_, res) => {
res.cookie('cookie', 'jar');
res.sendStatus(200);
});
-app.get('/getcookie', function(req, res) {
- res.status(200).send(req.cookies.cookie);
+app.get('/getcookie', (request_, res) => {
+ res.status(200).send(request_.cookies.cookie);
});
-app.get('/dashboard', function(req, res) {
- if (req.session.user) return res.status(200).send('dashboard');
+app.get('/cookieheader', (request_, res) => {
+ res.status(200).send(request_.headers.cookie);
+});
+
+app.get('/dashboard', (request_, res) => {
+ if (request_.session.user) return res.status(200).send('dashboard');
res.status(401).send('dashboard');
});
-app.all('/signout', function(req, res) {
- req.session.regenerate(function() {
+app.all('/signout', (request_, res) => {
+ request_.session.regenerate(() => {
res.status(200).send('signout');
});
});
-app.get('/', function(req, res) {
- if (req.session.user) return res.redirect('/dashboard');
+app.get('/', (request_, res) => {
+ if (request_.session.user) return res.redirect('/dashboard');
res.status(200).send('home');
});
-app.post('/redirect', function(req, res) {
+app.post('/redirect', (request_, res) => {
res.redirect('/simple');
});
-app.get('/simple', function(req, res) {
+app.get('/simple', (request_, res) => {
res.status(200).send('simple');
});
-app.listen(4000);
-
-describe('request', function() {
- describe('persistent agent', function() {
- var agent1 = request.agent();
- var agent2 = request.agent();
- var agent3 = request.agent();
- var agent4 = request.agent();
-
- it('should gain a session on POST', function(done) {
- agent3
- .post('http://localhost:4000/signin')
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- should.not.exist(res.headers['set-cookie']);
- res.text.should.include('dashboard');
- done();
- });
- });
+let base = 'http://localhost';
+let server;
+before(function listen(done) {
+ server = http.createServer(app);
+ server = server.listen(0, function listening() {
+ base += `:${server.address().port}`;
+ done();
+ });
+});
- it('should start with empty session (set cookies)', function(done) {
- agent1
- .get('http://localhost:4000/dashboard')
- .end(function(err, res) {
- should.exist(err);
- res.should.have.status(401);
- should.exist(res.headers['set-cookie']);
- done();
- });
+describe('request', () => {
+ describe('persistent agent', () => {
+ const agent1 = request.agent();
+ const agent2 = request.agent();
+ const agent3 = request.agent();
+ const agent4 = request.agent();
+
+ it('should gain a session on POST', () =>
+ agent3.post(`${base}/signin`).then((res) => {
+ assert.equal(res.status, 200);
+ assert.ok('set-cookie' in res.headers === false);
+ assert.equal(res.text, 'dashboard');
+ }));
+
+ it('should start with empty session (set cookies)', (done) => {
+ agent1.get(`${base}/dashboard`).end((error, res) => {
+ assert.ok(error instanceof Error);
+ assert.equal(res.status, 401);
+ assert.ok('set-cookie' in res.headers);
+ done();
+ });
});
- it('should gain a session (cookies already set)', function(done) {
- agent1
- .post('http://localhost:4000/signin')
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- should.not.exist(res.headers['set-cookie']);
- res.text.should.include('dashboard');
- done();
- });
- });
+ it('should gain a session (cookies already set)', () =>
+ agent1.post(`${base}/signin`).then((res) => {
+ assert.equal(res.status, 200);
+ assert.ok('set-cookie' in res.headers === false);
+ assert.equal('dashboard', res.text);
+ }));
- it('should persist cookies across requests', function(done) {
- agent1
- .get('http://localhost:4000/dashboard')
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- done();
- });
- });
+ it('should persist cookies across requests', () =>
+ agent1.get(`${base}/dashboard`).then((res) => {
+ assert.equal(res.status, 200);
+ }));
- it('should have the cookie set in the end callback', function(done) {
+ it('should have the cookie set in the end callback', () =>
+ agent4
+ .post(`${base}/setcookie`)
+ .then(() => agent4.get(`${base}/getcookie`))
+ .then((res) => {
+ assert.equal(res.status, 200);
+ assert.strictEqual(res.text, 'jar');
+ }));
+
+ it('should produce a valid cookie header', (done) => {
agent4
- .post('http://localhost:4000/setcookie')
- .end(function(err, res) {
- agent4
- .get('http://localhost:4000/getcookie')
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- assert(res.text === 'jar');
- done();
- });
+ .set('Cookie', 'first_cookie=dummy; cookie=jam')
+ .get(`${base}/cookieheader`)
+ .then((res) => {
+ const cookiePairs = res.text.split('; '); // https://httpwg.org/specs/rfc6265.html#rfc.section.4.2.1
+ assert.deepStrictEqual(cookiePairs, [
+ 'first_cookie=dummy',
+ 'cookie=jar',
+ `connect.sid=${agent4.jar.getCookie('connect.sid', cookiejar.CookieAccessInfo.All).value}`,
+ ]);
+ done();
});
});
- it('should not share cookies', function(done) {
- agent2
- .get('http://localhost:4000/dashboard')
- .end(function(err, res) {
- should.exist(err);
- res.should.have.status(401);
- done();
- });
+ it('should not share cookies between domains', () => {
+ assert.equal(agent4.get('https://google.com').cookies, "");
});
- it('should not lose cookies between agents', function(done) {
- agent1
- .get('http://localhost:4000/dashboard')
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- done();
- });
+ it('should send cookies to allowed domain with a different path', () => {
+ const postRequest = agent4.post(`${base}/x/y/z`)
+ const cookiesNames = postRequest.cookies.split(';').map(cookie => cookie.split('=')[0])
+ assert.deepStrictEqual(cookiesNames, ['cookie', ' connect.sid']);
});
- it('should be able to follow redirects', function(done) {
- agent1
- .get('http://localhost:4000/')
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- res.text.should.include('dashboard');
- done();
- });
+ it('should not share cookies', (done) => {
+ agent2.get(`${base}/dashboard`).end((error, res) => {
+ assert.ok(error instanceof Error);
+ assert.equal(res.status, 401);
+ done();
+ });
});
- it('should be able to post redirects', function(done) {
+ it('should not lose cookies between agents', () =>
+ agent1.get(`${base}/dashboard`).then((res) => {
+ assert.equal(res.status, 200);
+ }));
+
+ it('should be able to follow redirects', () =>
+ agent1.get(base).then((res) => {
+ assert.equal(res.status, 200);
+ assert.equal(res.text, 'dashboard');
+ }));
+
+ it('should be able to post redirects', () =>
agent1
- .post('http://localhost:4000/redirect')
+ .post(`${base}/redirect`)
.send({ foo: 'bar', baz: 'blaaah' })
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- res.text.should.include('simple');
- res.redirects.should.eql(['http://localhost:4000/simple']);
- done();
- });
- });
+ .then((res) => {
+ assert.equal(res.status, 200);
+ assert.equal(res.text, 'simple');
+ assert.deepStrictEqual(res.redirects, [`${base}/simple`]);
+ }));
- it('should be able to limit redirects', function(done) {
+ it('should be able to limit redirects', (done) => {
agent1
- .get('http://localhost:4000/')
+ .get(base)
.redirects(0)
- .end(function(err, res) {
- should.exist(err);
- res.should.have.status(302);
- res.redirects.should.eql([]);
- res.header.location.should.equal('/dashboard');
+ .end((error, res) => {
+ assert.ok(error instanceof Error);
+ assert.equal(res.status, 302);
+ assert.deepEqual(res.redirects, []);
+ assert.equal(res.header.location, '/dashboard');
done();
});
});
- it('should be able to create a new session (clear cookie)', function(done) {
- agent1
- .post('http://localhost:4000/signout')
- .end(function(err, res) {
- should.not.exist(err);
- res.should.have.status(200);
- should.exist(res.headers['set-cookie']);
- done();
- });
- });
-
- it('should regenerate with an empty session', function(done) {
- agent1
- .get('http://localhost:4000/dashboard')
- .end(function(err, res) {
- should.exist(err);
- res.should.have.status(401);
- should.not.exist(res.headers['set-cookie']);
- done();
- });
+ it('should be able to create a new session (clear cookie)', () =>
+ agent1.post(`${base}/signout`).then((res) => {
+ assert.equal(res.status, 200);
+ assert.ok('set-cookie' in res.headers);
+ }));
+
+ it('should regenerate with an empty session', (done) => {
+ agent1.get(`${base}/dashboard`).end((error, res) => {
+ assert.ok(error instanceof Error);
+ assert.equal(res.status, 401);
+ assert.ok('set-cookie' in res.headers === false);
+ done();
+ });
});
});
});
diff --git a/test/node/basic-auth.js b/test/node/basic-auth.js
index 7850e43c3..757b20f65 100644
--- a/test/node/basic-auth.js
+++ b/test/node/basic-auth.js
@@ -1,54 +1,52 @@
+const assert = require('assert');
+const { format } = require('url');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
-var EventEmitter = require('events').EventEmitter
- , request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express()
- , basicAuth = require('basic-auth-connect');
+describe('Basic auth', () => {
+ let setup;
+ let base;
-app.get('/', basicAuth('tobi', 'learnboost'), function(req, res){
- res.end('you win!');
-});
-
-app.get('/again', basicAuth('tobi', ''), function(req, res){
- res.end('you win again!');
-});
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
-app.listen(3010);
+ describe('when credentials are present in url', () => {
+ it('should set Authorization', (done) => {
+ const new_url = new URL(base);
+ new_url.username = 'tobi';
+ new_url.password = 'learnboost';
+ new_url.pathname = '/basic-auth';
-describe('Basic auth', function(){
- describe('when credentials are present in url', function(){
- it('should set Authorization', function(done){
- request
- .get('http://tobi:learnboost@localhost:3010')
- .end(function(err, res){
- res.status.should.equal(200);
+ request.get(format(new_url)).end((error, res) => {
+ assert.equal(res.status, 200);
done();
});
- })
- })
+ });
+ });
- describe('req.auth(user, pass)', function(){
- it('should set Authorization', function(done){
+ describe('req.auth(user, pass)', () => {
+ it('should set Authorization', (done) => {
request
- .get('http://localhost:3010')
- .auth('tobi', 'learnboost')
- .end(function(err, res){
- res.status.should.equal(200);
- done();
- });
- })
- })
+ .get(`${base}/basic-auth`)
+ .auth('tobi', 'learnboost')
+ .end((error, res) => {
+ assert.equal(res.status, 200);
+ done();
+ });
+ });
+ });
- describe('req.auth(user + ":" + pass)', function(){
- it('should set authorization', function(done){
+ describe('req.auth(user + ":" + pass)', () => {
+ it('should set authorization', (done) => {
request
- .get('http://localhost:3010/again')
- .auth('tobi')
- .end(function(err, res){
- res.status.should.eql(200);
- done();
- });
- })
- })
-})
+ .get(`${base}/basic-auth/again`)
+ .auth('tobi')
+ .end((error, res) => {
+ assert.equal(res.status, 200);
+ done();
+ });
+ });
+ });
+});
diff --git a/test/node/basic.js b/test/node/basic.js
index af866d908..d25103629 100644
--- a/test/node/basic.js
+++ b/test/node/basic.js
@@ -1,280 +1,342 @@
-var EventEmitter = require('events').EventEmitter;
-var assert = require('better-assert');
-var fs = require('fs');
-var StringDecoder = require('string_decoder').StringDecoder;
-var url = require('url');
+'use strict';
-var request = require('../../');
+const assert = require('assert');
+const fs = require('fs');
+const { EventEmitter } = require('events');
+const { StringDecoder } = require('string_decoder');
+const getSetup = require('../support/setup');
+const request = require('../support/client');
-describe('[node] request', function(){
+const doesntWorkInHttp2 = !process.env.HTTP2_TEST;
- describe('with an object', function(){
- it('should format the url', function(done){
- request
- .get(url.parse('http://localhost:5000/login'))
- .end(function(err, res){
- assert(res.ok);
- done();
- })
- })
- })
+describe('[node] request', () => {
+ let setup;
+ let base;
- describe('without a schema', function(){
- it('should default to http', function(done){
- request
- .get('localhost:5000/login')
- .end(function(err, res){
- assert(res.status == 200);
- done();
- })
- })
- })
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
- describe('req.toJSON()', function(){
- it('should describe the request', function(done){
- request
- .post(':5000/echo')
- .send({ foo: 'baz' })
- .end(function(err, res){
- var obj = res.request.toJSON();
- assert('POST' == obj.method);
- assert(':5000/echo' == obj.url);
- assert('baz' == obj.data.foo);
+ describe('with an url', () => {
+ it('should preserve the encoding of the url', (done) => {
+ request.get(`${base}/url?a=(b%29`).end((error, res) => {
+ assert.equal('/url?a=(b%29', res.text);
done();
});
- })
- })
+ });
+ });
- describe('should allow the send shorthand', function() {
- it('with callback in the method call', function(done) {
- request
- .get('http://localhost:5000/login', function(err, res) {
- assert(res.status == 200);
- done();
- });
- })
+ describe('with an object', () => {
+ it('should format the url', () =>
+ request.get(new URL(`${base}/login`)).then((res) => {
+ assert.ok(res.ok);
+ }));
+ });
- it('with data in the method call', function(done) {
- request
- .post('http://localhost:5000/echo', { foo: 'bar' })
- .end(function(err, res) {
- assert('{"foo":"bar"}' == res.text);
- done();
- });
- })
+ describe('without a schema', () => {
+ it('should default to http', () =>
+ request.get(`${base}/login`).then((res) => {
+ assert.equal(res.status, 200);
+ }));
+ });
- it('with callback and data in the method call', function(done) {
+ describe('res.toJSON()', () => {
+ it('should describe the response', () =>
request
- .post('http://localhost:5000/echo', { foo: 'bar' }, function(err, res) {
- assert('{"foo":"bar"}' == res.text);
- done();
- });
- })
- })
+ .post(`${base}/echo`)
+ .send({ foo: 'baz' })
+ .then((res) => {
+ const object = res.toJSON();
+ assert.equal('object', typeof object.header);
+ assert.equal('object', typeof object.req);
+ assert.equal(200, object.status);
+ assert.equal('{"foo":"baz"}', object.text);
+ }));
+ });
- describe('res.toJSON()', function(){
- it('should describe the response', function(done){
- request
- .post('http://localhost:5000/echo')
- .send({ foo: 'baz' })
- .end(function(err, res){
- var obj = res.toJSON();
- assert('object' == typeof obj.header);
- assert('object' == typeof obj.req);
- assert(200 == obj.status);
- assert('{"foo":"baz"}' == obj.text);
+ describe('res.links', () => {
+ it('should default to an empty object', () =>
+ request.get(`${base}/login`).then((res) => {
+ assert.deepEqual(res.links, {});
+ }));
+
+ it('should parse the Link header field', (done) => {
+ request.get(`${base}/links`).end((error, res) => {
+ assert.equal(
+ res.links.next,
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=2'
+ );
done();
});
});
- })
+ });
- describe('res.links', function(){
- it('should default to an empty object', function(done){
+ describe('req.unset(field)', () => {
+ it('should remove the header field', (done) => {
request
- .get('http://localhost:5000/login')
- .end(function(err, res){
- res.links.should.eql({});
- done();
- })
- })
+ .post(`${base}/echo`)
+ .unset('User-Agent')
+ .end((error, res) => {
+ assert.equal(res.header['user-agent'], undefined);
+ done();
+ });
+ });
+ });
- it('should parse the Link header field', function(done){
- request
- .get('http://localhost:5000/links')
- .end(function(err, res){
- res.links.next.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=2');
- done();
- })
- })
- })
+ describe('case-insensitive', () => {
+ it('should set/get header fields case-insensitively', () => {
+ const req = request.post(`${base}/echo`);
+ req.set('MiXeD', 'helloes');
+ assert.strictEqual(req.get('mixed'), 'helloes');
+ });
- describe('req.unset(field)', function(){
- it('should remove the header field', function(done){
- request
- .post('http://localhost:5000/echo')
- .unset('User-Agent')
- .end(function(err, res){
- assert(void 0 == res.header['user-agent']);
- done();
- })
- })
- })
-
- describe('req.write(str)', function(){
- it('should write the given data', function(done){
- var req = request.post('http://localhost:5000/echo');
- req.set('Content-Type', 'application/json');
- req.write('{"name"').should.be.a.boolean;
- req.write(':"tobi"}').should.be.a.boolean;
- req.end(function(err, res){
- res.text.should.equal('{"name":"tobi"}');
+ it('should unset header fields case-insensitively', () => {
+ const req = request.post(`${base}/echo`);
+ req.set('MiXeD', 'helloes');
+ req.unset('MIXED');
+ assert.strictEqual(req.get('mixed'), undefined);
+ });
+ });
+
+ describe('req.write(str)', () => {
+ it('should write the given data', (done) => {
+ const request_ = request.post(`${base}/echo`);
+ request_.set('Content-Type', 'application/json');
+ assert.equal('boolean', typeof request_.write('{"name"'));
+ assert.equal('boolean', typeof request_.write(':"tobi"}'));
+ request_.end((error, res) => {
+ assert.equal(res.text, '{"name":"tobi"}');
done();
});
- })
- })
+ });
+ });
- describe('req.pipe(stream)', function(){
- it('should pipe the response to the given stream', function(done){
- var stream = new EventEmitter;
+ describe('req.pipe(stream)', () => {
+ it('should pipe the response to the given stream', (done) => {
+ const stream = new EventEmitter();
- stream.buf = '';
+ let buf = '';
stream.writable = true;
- stream.write = function(chunk){
- this.buf += chunk;
+ stream.write = function (chunk) {
+ buf += chunk;
};
- stream.end = function(){
- this.buf.should.equal('{"name":"tobi"}');
+ stream.end = function () {
+ assert.equal(buf, '{"name":"tobi"}');
done();
};
- request
- .post('http://localhost:5000/echo')
- .send('{"name":"tobi"}')
- .pipe(stream);
- })
- })
-
- describe('.buffer()', function(){
- it('should enable buffering', function(done){
- request
- .get('http://localhost:5000/custom')
- .buffer()
- .end(function(err, res){
- assert(null == err);
- assert('custom stuff' == res.text);
- assert(res.buffered);
+ request.post(`${base}/echo`)
+ .send('{"name":"tobi"}')
+ .pipe(stream);
+ });
+ });
+
+ describe('ipv6 address', () => {
+ it('should successfully query an ipv6 address', (done) => {
+ request.get(`http://[::]:${process.env.ZUUL_PORT}/url?a=(b%29`).end((error, res) => {
+ assert.equal('/url?a=(b%29', res.text);
done();
});
- })
- })
+ });
- describe('.buffer(false)', function(){
- it('should disable buffering', function(done){
+ it('should successfully query an ipv6 address', (done) => {
+ request.get(`http://[::1]:${process.env.ZUUL_PORT}/url?a=(b%29`).end((error, res) => {
+ assert.equal('/url?a=(b%29', res.text);
+ done();
+ });
+ });
+ });
+
+ describe('.buffer()', () => {
+ it('should enable buffering', (done) => {
request
- .post('http://localhost:5000/echo')
- .type('application/x-dog')
- .send('hello this is dog')
- .buffer(false)
- .end(function(err, res){
- assert(null == err);
- assert(null == res.text);
- res.body.should.eql({});
- var buf = '';
- res.setEncoding('utf8');
- res.on('data', function(chunk){ buf += chunk });
- res.on('end', function(){
- buf.should.equal('hello this is dog');
+ .get(`${base}/custom`)
+ .buffer()
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal('custom stuff', res.text);
+ assert.ok(res.buffered);
done();
});
- });
- })
- })
+ });
+ it("should take precedence over request.buffer['someMimeType'] = false", (done) => {
+ const type = 'application/barbaz';
+ const send = 'some text';
+ request.buffer[type] = false;
+ request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .buffer()
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert.ok(res.buffered);
+ done();
+ });
+ });
+ });
+
+ describe('.buffer(false)', () => {
+ it('should disable buffering', (done) => {
+ request
+ .post(`${base}/echo`)
+ .type('application/x-dog')
+ .send('hello this is dog')
+ .buffer(false)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.deepEqual(res.body, {});
+ let str = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ str += chunk;
+ });
+ res.on('end', () => {
+ assert.equal(str, 'hello this is dog');
+ done();
+ });
+ });
+ });
- describe('.agent()', function(){
- it('should return the defaut agent', function(done){
- var req = request.post('http://localhost:5000/echo');
- req.agent().should.equal(false);
+ it("should take precedence over request.buffer['someMimeType'] = true", (done) => {
+ const type = 'application/foobar';
+ const send = 'hello this is a dog';
+ request.buffer[type] = true;
+ request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .buffer(false)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert.equal(res.buffered, false);
+ assert.deepEqual(res.body, {});
+ let str = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ str += chunk;
+ });
+ res.on('end', () => {
+ assert.equal(str, send);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('.withCredentials()', () => {
+ it('should not throw an error when using the client-side "withCredentials" method', (done) => {
+ request
+ .get(`${base}/custom`)
+ .withCredentials()
+ .end((error, res) => {
+ assert.ifError(error);
+ done();
+ });
+ });
+ });
+
+ describe('.agent()', () => {
+ it('should return the defaut agent', (done) => {
+ const agent = request.post(`${base}/echo`).agent();
+ assert.equal(agent, false);
done();
- })
- })
-
- describe('.agent(undefined)', function(){
- it('should set an agent to undefined and ensure it is chainable', function(done){
- var req = request.get();
- var ret = req.agent(undefined);
- ret.should.equal(req);
- assert(req.agent() === undefined);
+ });
+ });
+
+ describe('.agent(undefined)', () => {
+ it('should set an agent to undefined and ensure it is chainable', (done) => {
+ const request_ = request.get(`${base}/echo`);
+ const returnValue = request_.agent(undefined);
+ assert.equal(returnValue, request_);
+ assert.strictEqual(request_.agent(), undefined);
done();
- })
- })
-
- describe('.agent(new http.Agent())', function(){
- it('should set passed agent', function(done){
- var http = require('http');
- var req = request.get();
- var agent = new http.Agent();
- var ret = req.agent(agent);
- ret.should.equal(req);
- req.agent().should.equal(agent)
+ });
+ });
+
+ describe('.agent(new http.Agent())', () => {
+ it('should set passed agent', (done) => {
+ const http = require('http');
+ const request_ = request.get(`${base}/echo`);
+ const agent = new http.Agent();
+ const returnValue = request_.agent(agent);
+ assert.equal(returnValue, request_);
+ assert.equal(request_.agent(), agent);
done();
- })
- })
+ });
+ });
+
+ describe('with a content type other than application/json or text/*', () => {
+ it('should still use buffering', () => {
+ return request
+ .post(`${base}/echo`)
+ .type('application/x-dog')
+ .send('hello this is dog')
+ .then((res) => {
+ assert.equal(res.text, null);
+ assert.equal(res.body.toString(), 'hello this is dog');
+ assert.equal(res.buffered, true);
+ });
+ });
+ });
- describe('with a content type other than application/json or text/*', function(){
- it('should disable buffering', function(done){
+ describe('content-length', () => {
+ it('should be set to the byte length of a non-buffer object', (done) => {
+ const decoder = new StringDecoder('utf8');
+ let img = fs.readFileSync(`${__dirname}/fixtures/test.png`);
+ img = decoder.write(img);
request
- .post('http://localhost:5000/echo')
- .type('application/x-dog')
- .send('hello this is dog')
- .end(function(err, res){
- assert(null == err);
- assert(null == res.text);
- res.body.should.eql({});
- var buf = '';
- res.setEncoding('utf8');
- res.buffered.should.be.false;
- res.on('data', function(chunk){ buf += chunk });
- res.on('end', function(){
- buf.should.equal('hello this is dog');
+ .post(`${base}/echo`)
+ .type('application/x-image')
+ .send(img)
+ .buffer(false)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal(res.buffered, false);
+ assert.equal(res.header['content-length'], Buffer.byteLength(img));
done();
});
- });
- })
- })
+ });
- describe('content-length', function() {
- it('should be set to the byte length of a non-buffer object', function (done) {
- var decoder = new StringDecoder('utf8');
- var img = fs.readFileSync(__dirname + '/fixtures/test.png');
- img = decoder.write(img);
+ it('should be set to the length of a buffer object', (done) => {
+ const img = fs.readFileSync(`${__dirname}/fixtures/test.png`);
request
- .post('http://localhost:5000/echo')
- .type('application/x-image')
- .send(img)
- .buffer(false)
- .end(function(err, res){
- assert(null == err);
- assert(!res.buffered);
- assert(res.header['content-length'] == Buffer.byteLength(img));
- done();
- });
- })
+ .post(`${base}/echo`)
+ .type('application/x-image')
+ .send(img)
+ .buffer(true)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.buffered);
+ assert.equal(res.header['content-length'], img.length);
+ done();
+ });
+ });
+ });
- it('should be set to the length of a buffer object', function(done){
- var img = fs.readFileSync(__dirname + '/fixtures/test.png');
+ if (doesntWorkInHttp2)
+ it('should send body with .get().send()', (next) => {
request
- .post('http://localhost:5000/echo')
- .type('application/x-image')
- .send(img)
- .buffer(true)
- .end(function(err, res){
- assert(null == err);
- assert(res.buffered);
- assert(res.header['content-length'] == img.length);
- done();
- });
- })
- })
+ .get(`${base}/echo`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal(res.text, 'wahoo');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
});
diff --git a/test/node/buffers.js b/test/node/buffers.js
new file mode 100644
index 000000000..607d7b2fc
--- /dev/null
+++ b/test/node/buffers.js
@@ -0,0 +1,120 @@
+'use strict';
+const assert = require('assert');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
+
+describe("req.buffer['someMimeType']", () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ it('should respect that agent.buffer(true) takes precedent', (done) => {
+ const agent = request.agent();
+ agent.buffer(true);
+ const type = 'application/somerandomtype';
+ const send = 'somerandomtext';
+ request.buffer[type] = false;
+ agent
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+ });
+
+ it('should respect that agent.buffer(false) takes precedent', (done) => {
+ const agent = request.agent();
+ agent.buffer(false);
+ const type = 'application/barrr';
+ const send = 'some random text2';
+ request.buffer[type] = true;
+ agent
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+ });
+
+ it('should disable buffering for that mimetype when false', (done) => {
+ const type = 'application/bar';
+ const send = 'some random text';
+ request.buffer[type] = false;
+ request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(null, res.text);
+ assert.equal(res.type, type);
+ assert(!res.buffered);
+ res.body.should.eql({});
+ let buf = '';
+ res.setEncoding('utf8');
+ res.on('data', (chunk) => {
+ buf += chunk;
+ });
+ res.on('end', () => {
+ buf.should.equal(send);
+ done();
+ });
+ });
+ });
+ it('should enable buffering for that mimetype when true', (done) => {
+ const type = 'application/baz';
+ const send = 'woooo';
+ request.buffer[type] = true;
+ request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .end((error, res) => {
+ delete request.buffer[type];
+ assert.ifError(error);
+ assert.equal(res.type, type);
+ assert.equal(send, res.text);
+ assert(res.buffered);
+ done();
+ });
+ });
+ it('should fallback to default handling for that mimetype when undefined', () => {
+ const type = 'application/bazzz';
+ const send = 'woooooo';
+ return request
+ .post(`${base}/echo`)
+ .type(type)
+ .send(send)
+ .then((res) => {
+ assert.equal(res.type, type);
+ assert.equal(send, res.body.toString());
+ assert(res.buffered);
+ });
+ });
+});
diff --git a/test/node/content-type.js b/test/node/content-type.js
deleted file mode 100644
index 1ff4ebfdb..000000000
--- a/test/node/content-type.js
+++ /dev/null
@@ -1,35 +0,0 @@
-var request = require('../../')
- , express = require('express')
- , assert = require('better-assert')
- , app = express();
-
-app.all('/echo', function(req, res){
- res.writeHead(200, req.headers);
- req.pipe(res);
-});
-
-describe('req.set("Content-Type", contentType)', function(){
-
- it('should work with just the contentType component', function(done){
- request
- .post('http://localhost:3005/echo')
- .set('Content-Type', 'application/json')
- .send({ name: 'tobi' })
- .end(function(err, res){
- assert(!err);
- done();
- });
- });
-
- it('should work with the charset component', function(done){
- request
- .post('http://localhost:3005/echo')
- .set('Content-Type', 'application/json; charset=utf-8')
- .send({ name: 'tobi' })
- .end(function(err, res){
- assert(!err);
- done();
- });
- });
-
-});
diff --git a/test/node/exports.js b/test/node/exports.js
index d25e45e12..62c5230ae 100644
--- a/test/node/exports.js
+++ b/test/node/exports.js
@@ -1,23 +1,31 @@
+'use strict';
+const request = require('../support/client');
-var request = require('../../');
+describe('exports', () => {
+ it('should expose .protocols', () => {
+ Object.keys(request.protocols).should.eql(['http:', 'https:', 'http2:']);
+ });
-describe('exports', function(){
- it('should expose Part', function(){
- request.Part.should.be.a.function;
- })
+ it('should expose .serialize', () => {
+ Object.keys(request.serialize).should.eql([
+ 'application/x-www-form-urlencoded',
+ 'application/json'
+ ]);
+ });
- it('should expose .protocols', function(){
- Object.keys(request.protocols)
- .should.eql(['http:', 'https:']);
- })
+ it('should expose .parse', () => {
+ Object.keys(request.parse).should.eql([
+ 'application/x-www-form-urlencoded',
+ 'application/json',
+ 'text',
+ 'application/json-seq',
+ 'application/octet-stream',
+ 'application/pdf',
+ 'image'
+ ]);
+ });
- it('should expose .serialize', function(){
- Object.keys(request.serialize)
- .should.eql(['application/x-www-form-urlencoded', 'application/json']);
- })
-
- it('should expose .parse', function(){
- Object.keys(request.parse)
- .should.eql(['application/x-www-form-urlencoded', 'application/json', 'text', 'image']);
- })
-})
+ it('should export .buffer', () => {
+ Object.keys(request.buffer).should.eql([]);
+ });
+});
diff --git a/test/node/fixtures/ca.cert.pem b/test/node/fixtures/ca.cert.pem
new file mode 100644
index 000000000..af0298e0e
--- /dev/null
+++ b/test/node/fixtures/ca.cert.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICljCCAX4CCQDnGz3+qH/zGzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJD
+QTAeFw0xODEyMDIxNjIyMThaFw0zMjA4MTAxNjIyMThaMA0xCzAJBgNVBAMMAkNB
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtgUDcB7R94A22I0D8Iba
+c000ZBINtvnyvoPP5U2hRsxI9tcW/LLm0GhzM5XJJNQ0jSv0ixIFKomtFBSjKMq/
+NH156SqKqyDGU1fPnGjzcJulxceODIokAqNCbZ7Bys6nqUdilNfLQ4bBBWYsEUWT
+vgbUeDRHvQGycou/pLpYGLCJ4tpc4n6ybox1uPi0qlvFI7aWvQFjOxxR0VeRixXf
+qXjVCDIr9OJIWiXLrJYDlYqG2gRF/yTDZ4qmQxbZzJ6AXMpaRUiHUO0FHu7baEux
+ylIc0KVcAmYMGdhFlmDrMNRsmnADKPX9DIMh92XWyE10oNK50I1rIhpvN4XfQx6E
+UwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCIXj1vfTVRB4ea/udjazPHaLAeocbg
+d8babbDItOm1ApAoUhNuxPVxyISSFrowCVlaWxB+1ztfeUAB/Axfj0mbXk1hgo2D
+4+rft8hOtdg91bU+gHPd/7QGkpPIs5PC+TsnVj0mNqZ5o8qZsLhgoXp3Dl5yMhEs
+sRegLkmBQHzEsKFU2cSxVD7BXXGLDJxcoR4friGOXdIZeYwqHTZsuR3O7JOVbLew
+dURqD70jPuf9v1tBnkJPbUECMlL7BCw6ZQtglSvjPP/waWir9TMsDk+xwPK8NPbv
+DGi+w++cImBbxcnIMBTk4XtlFcOnCCAYUfkaxZMw2jNhYbjiEZOGUG8m
+-----END CERTIFICATE-----
diff --git a/test/node/fixtures/ca.key.pem b/test/node/fixtures/ca.key.pem
new file mode 100644
index 000000000..8e3ff13da
--- /dev/null
+++ b/test/node/fixtures/ca.key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAtgUDcB7R94A22I0D8Ibac000ZBINtvnyvoPP5U2hRsxI9tcW
+/LLm0GhzM5XJJNQ0jSv0ixIFKomtFBSjKMq/NH156SqKqyDGU1fPnGjzcJulxceO
+DIokAqNCbZ7Bys6nqUdilNfLQ4bBBWYsEUWTvgbUeDRHvQGycou/pLpYGLCJ4tpc
+4n6ybox1uPi0qlvFI7aWvQFjOxxR0VeRixXfqXjVCDIr9OJIWiXLrJYDlYqG2gRF
+/yTDZ4qmQxbZzJ6AXMpaRUiHUO0FHu7baEuxylIc0KVcAmYMGdhFlmDrMNRsmnAD
+KPX9DIMh92XWyE10oNK50I1rIhpvN4XfQx6EUwIDAQABAoIBAAomAWkQ7tgD2Ar+
+4cdZXXisR02FDCq1iOlCZCb+yw0teqv4lhmEyAW6rSGlKk/ZXQy59KqEWLFkd7f0
+5pvxofOFQ3TSoGSmMSiYbsTjvR78LqP2Wl8snJFhFOUTwu5R01tG1aJC3dxn+P8a
+ET7tSC2CJ/qDv7Q0EgT88bR3h03pAc2PJAGWJ2QwutAwV+6Ilyt3hcz29f7JGQac
+Da0jY/7RIpvZL20gsecb/6Q0a50VEbkuJ+OF5QyIQNT9qtfvsqavtsjHgBWs3iFG
+yKaDCey4X7D+NsyJBKNCuDqWXzq/4+mQ3atBIvYq2mcIpIbaku7TEWHvpxfzsT/F
+SzLncMECgYEA4qR1GU38UxZ/8x6Cc4Z3DKUuCLBqvv69IHPcfSkhrtTeApdsv14U
+KE66/Z3g4FXeV018GYUzGODfjBt4VnDCutz9tmiHFH+T2zDpgC7hvODC8rJ+D21b
+G3Q5eyA+XRao1XHjc2UD/Ev6iJPjgCT23Lur2bhAqCKkV4g2EAWCgyMCgYEAzZjY
+5M7Jw+0g5b6ILnFX66JbESrw87OtiIpiQh5XUQssKsmfONzbXUp6ch/MRTFVHVDZ
+XmG35Xyg6vy1FYg9HTALl1tnkh9UIhKrH7jx+6euhc0HyWCCqSp/3CpJqVjdoxtS
+vtZigSaxwTDaOYc8r54cWhHIIrpTrcqY4mxdZRECgYBpGWhv9pkXEqz82d4Wonlc
+dNDXGLA3p7uea/wIUmWbRH07aGr2hzMDyhaue2MHxOoZRAZTc1BRrh4cQ7TXKO00
+aDyDNQ/G8q5nC9SMK7FkvDnK//izQLvqDEiHj1k8I8DhUjHulh52BenFIgdyqjGM
+BL9ZdDcPgRkCuliPr25pTwKBgG8rf9QxEJ51oT05Sk+6j+zk7FMbIhDUjjfvg/P2
+jgZPgUFdpk/L9H28YPtGwGCFrV1dszu6oQJg4m5N2OjcsxcOPKZKEXXMpOSLraZI
+jegiolbNJ7G3Es/AIET/RLdiSu4APzzblYX6U1GARe+ndaQMXY5CYTKOB+NIUmTU
+bafRAoGAV8PptnjWf9ehJvt96Rz0FdfpN7leqdweK2JrGZwJQ9BRcKwUO758lmyN
+nC62Sd0mwHivxRqI6e2u7OSvGj9e7iLnHMl4qYie7VvsWAWmztH2ntEI0iWA3V3N
+M3T9+sz+nslfhWKgIA80EqOWB1AF0EmbmmqK0igWEbYynAr6v0Q=
+-----END RSA PRIVATE KEY-----
diff --git a/test/node/fixtures/ca.srl b/test/node/fixtures/ca.srl
new file mode 100644
index 000000000..25c55bcde
--- /dev/null
+++ b/test/node/fixtures/ca.srl
@@ -0,0 +1 @@
+B23299143E26EFC5
diff --git a/test/node/fixtures/cert.csr b/test/node/fixtures/cert.csr
new file mode 100644
index 000000000..46b2de775
--- /dev/null
+++ b/test/node/fixtures/cert.csr
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAsjNYMUk1HzBLM4fL/8c6Z86X/b13V1bMXrrcV28w
+jmvQv4lADlghZGt/zbinyvZrASp8v1btR9gLaNTdrxxnz5FWu1bY947ljgadudh0
+STLNs4jWI2wcjgguA8B2rDtvap0bn9JkpjQGcFuqEdJRlr26OwsC5SNCtFxQmTrb
+sneFMWK3JsL1riCrV9Cw0L3joki/YKYTTwRZJ2TXrrHvOLQCIUacwCU4oJg19D0F
+KU/rDtdUFFojnTio0RFU4/OpjxKzwqjSJE80nPSRHAqXtSgmphrRk4ZVeTxKEGKC
+oAt/RBwqvxPFjNlbr1EWY9S7SpFu06V35jXJYD9CWD/WcQIDAQABoAAwDQYJKoZI
+hvcNAQELBQADggEBACia2hDAe1YqzVn/P2KSX+/9yDrjOhU20hKF1CTZr3Zfjep9
+R86ldz1aFc2N2yqDjUYpj0+DODEXlEfNaYcD2nn93qgy/gAmfQUvCejBh6vsGiIW
+qc41lzEGHa9/ErxQ7yvETeuY8OozjDAoUs5NUbFL6OmwJFqX9VzLbQ8uQxNU3z3l
+AKVkWBWIubqpSa8tDn/+wf8vW/xtOCZ/Zd5YBQdTCwb5BdS3vpBkwpJbPBvR97Mc
+2d+2a5qyNqZdb1CuATS2MJC35zp62FErhcnnVqtaAcaE/C/in1OO3wLJvXa5/LKp
+1riBUJLHscGbIhe+KjlLoosXuOcUNvBkNbt8MFQ=
+-----END CERTIFICATE REQUEST-----
diff --git a/test/node/fixtures/cert.pem b/test/node/fixtures/cert.pem
index b0afc3252..4ba100e1a 100644
--- a/test/node/fixtures/cert.pem
+++ b/test/node/fixtures/cert.pem
@@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE-----
-MIICpDCCAYwCCQCP3UEJzq8t6DANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDEwls
-b2NhbGhvc3QwHhcNMTMwOTExMTQzNDM1WhcNMTMxMDExMTQzNDM1WjAUMRIwEAYD
-VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5
-PH7dzxu9F9MOPJHvy0osayMgMdUUOj7A1RSjM4dEGUb+Ex99uSC3i2m+ftQ2ahPY
-jhyowt3sOCFSnMNe1bgd6C1DLLShrir/ZarzB5bKItCiG0oPOOxXHOuDHoA6bpqi
-ZGJvWeFQBPNzUL4xmF6wCIOt69d0yMMYFqEk8bLZ/Nba6b3uxLr9ZaDU1ZeAsu1g
-Z1s2ST71Uvn03YEAc19yKaCnTH1BfWMkV2thdWQZ4/d3Dvovb4yUENL4VaQzloyy
-ptVP4frYZTEo2VD1dSpH6gb13s4wdiaznDJNvqbdZM4A/jSgP9gvmMztuhZdn4SR
-qwnPoGVIVxtIWijYlGF5AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHRAO+/f6Yvm
-qMWO/Y0atI5gZ9/FGuw8OPMiNdbLa/0kLOaqlAcxn6GD1jMIjyalIQz22dddDKJ3
-7OqVF/uldLypuEIFyoHAqy8IRQYUXsqgIeW2V+6T/fGS0QWoEWUim8D9Cwxs5hnA
-kLf4KrIihSG5RzjuJ7RbGMF+3UMOGuXQy8m99eI8O/MNjyhk6EnPgU5ghL3wk4pb
-X+sgh3bUN5NHE1Nmh94l9k1XMyVZniDbnM2DhVf3kkkDHQPZwtNVZL5/jsLZfFLC
-FqHdPk1xgGFxoQsRgHN78LYp113yxyPfWdKRVmizaZsxm4/FRdt/O+bUVfgg4X6x
-dgH8W+6R8rI=
+MIICnTCCAYUCCQCyMpkUPibvxTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJD
+QTAeFw0xODEyMDIxNjIzNTBaFw0zMjA4MTAxNjIzNTBaMBQxEjAQBgNVBAMMCWxv
+Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALIzWDFJNR8w
+SzOHy//HOmfOl/29d1dWzF663FdvMI5r0L+JQA5YIWRrf824p8r2awEqfL9W7UfY
+C2jU3a8cZ8+RVrtW2PeO5Y4GnbnYdEkyzbOI1iNsHI4ILgPAdqw7b2qdG5/SZKY0
+BnBbqhHSUZa9ujsLAuUjQrRcUJk627J3hTFitybC9a4gq1fQsNC946JIv2CmE08E
+WSdk166x7zi0AiFGnMAlOKCYNfQ9BSlP6w7XVBRaI504qNERVOPzqY8Ss8Ko0iRP
+NJz0kRwKl7UoJqYa0ZOGVXk8ShBigqALf0QcKr8TxYzZW69RFmPUu0qRbtOld+Y1
+yWA/Qlg/1nECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAWYlRC8e5flZED/JDP0VG
+aNENVwv0bFbbzp1urgQeq1TVBDHGKuMJM9klVMBQiMT9q+nPjEgfynSiLVuY7FWs
+yD/hFjQMLV2cz7tdBY6l+IV1sAtBdX3ZRmmzttaAs4xmFXCLXdTm4KJ5bPcRnRLA
+klMKOsvNY90mRteXvhQy04J2TJVpIld05+yjnYVWoODyX9A/Xda+33qjZECLTnj4
+c5L3mamPZEo4nwY+329d7dNvEu+ETp4UzwkzwPFHSGLD01YEJIu83TTi1WDBZygz
+dxPUkLKYzRkpYVp3hbZsvkCQVtruY/e6DADZTKAlhL41V6VdH35Es0uw7JrbPf7z
+jQ==
-----END CERTIFICATE-----
diff --git a/test/node/fixtures/cert.pfx b/test/node/fixtures/cert.pfx
new file mode 100644
index 000000000..8cb83b7cb
Binary files /dev/null and b/test/node/fixtures/cert.pfx differ
diff --git a/test/node/fixtures/key.pem b/test/node/fixtures/key.pem
index 7cc18a100..b7687d616 100644
--- a/test/node/fixtures/key.pem
+++ b/test/node/fixtures/key.pem
@@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAuTx+3c8bvRfTDjyR78tKLGsjIDHVFDo+wNUUozOHRBlG/hMf
-fbkgt4tpvn7UNmoT2I4cqMLd7DghUpzDXtW4HegtQyy0oa4q/2Wq8weWyiLQohtK
-DzjsVxzrgx6AOm6aomRib1nhUATzc1C+MZhesAiDrevXdMjDGBahJPGy2fzW2um9
-7sS6/WWg1NWXgLLtYGdbNkk+9VL59N2BAHNfcimgp0x9QX1jJFdrYXVkGeP3dw76
-L2+MlBDS+FWkM5aMsqbVT+H62GUxKNlQ9XUqR+oG9d7OMHYms5wyTb6m3WTOAP40
-oD/YL5jM7boWXZ+EkasJz6BlSFcbSFoo2JRheQIDAQABAoIBAHyPp6g0ayy+5pf+
-NwyPIXO5H8e4eta9TBGTt+r+7YjnjouwBE8gvFVwlE0bMEzfDDVlavQ5Bc6g+Bd7
-fw04njTOOhGf8F+ApT1U+p2ujsGio7U+sJCH8LWrpttnGUcxtR5abq7+O7r5eVQk
-CaGEGrg5IYNEwn+vuTFrljUnquNWM17NNJ83Qj8B5JGqvuVaP5iclw2AmfCHe6aG
-sXEo739T47jY6YBwSvPbtELWMMgciqJ7ZRw9Cr72/MM51jw5AhCC4ZqZswE4fs5T
-T0zEZpoyDE44ylx9Ics2goAfDdCGhS/C5sgNN/AkkecVQuY53lBt/js6zhSCvgkz
-wAQxagECgYEA4/5NFP2hMWruO8yt2B5UytCzvfBRXmJcooY927BBHdtYd9lv0sid
-7yZR81KfsUCXKhHGrUFPPdFPEkgNwi6QOExT9Apf+VSGVm6XObEEEyqT2enyVsiT
-xgPVrgDKj6IDc3fXHA4wcsVxaVJ4qIt4rWtG3x6oEdZOC1bsvjkCAqECgYEAz/2d
-cRGxXCgCzNEjD/QkaW/hEMEnmFnLReOaIKBVkhxzD2FEFhSrem19uZddj3KIPC2E
-NGU1ZmcyqCaAOJt7DYiOX9zqhc12Cai26S3D8OlavSc4J04AqfJs697Ok7VXxUSA
-jO4BCVi/UNtzAKmK8tVpsWqv/ETTUJXVUdq1x9kCgYEAuigeuh/paNc9lBgobgk+
-BKfpyxGY7q7zokRn56P/VyiNELa6hmoGAonQahOxjmIFy3TeOwLTd88ad/vbOA0a
-9szj06RQ/tzUH2iHE7UEdb3TIR/THqcBebIR29SLkEGh/bsBKcgwKNYsJuoO2Neg
-fkDUikOWyZGpAbtE7IDRsmECgYBxgIFOls0m8V61zttHdX/5WeiEcCPfbAEV3qLZ
-cyW/Wm8f0YCKXDVH1kBp60RPZ70Yue4PebuualqmkHwgaBi6xe6MOc5xvjHQC5Xl
-oefvrCisWJ64NEUAeR8fiLNKwAdpy3wrbCZ8p0WgJmGX1u3Qns3S19m53QVEUL/c
-r3HL4QKBgFpWeWZKwdaJ+4iPxiMdgUVrPNw435QpBVricANLVCl0Z93tIc5vB2CN
-YwzdQOM+o94+oaswEefWksl4YX0x44lKQMFxhAuYRhj/vezrrCy0bDPinu065azG
-FZ31YMFD84NUYGgjTh9cpAoMydqtdjzn6Puvh3lGr0YFzgIhoR7t
+MIIEowIBAAKCAQEAsjNYMUk1HzBLM4fL/8c6Z86X/b13V1bMXrrcV28wjmvQv4lA
+DlghZGt/zbinyvZrASp8v1btR9gLaNTdrxxnz5FWu1bY947ljgadudh0STLNs4jW
+I2wcjgguA8B2rDtvap0bn9JkpjQGcFuqEdJRlr26OwsC5SNCtFxQmTrbsneFMWK3
+JsL1riCrV9Cw0L3joki/YKYTTwRZJ2TXrrHvOLQCIUacwCU4oJg19D0FKU/rDtdU
+FFojnTio0RFU4/OpjxKzwqjSJE80nPSRHAqXtSgmphrRk4ZVeTxKEGKCoAt/RBwq
+vxPFjNlbr1EWY9S7SpFu06V35jXJYD9CWD/WcQIDAQABAoIBAGvpieO2yHONpEyd
+VJ0dAbJxOjuPa+C5EGPhVqOgEtB8W9pRfDfziK0uKCPlSb8wAFahaw/XzTMvkqE2
+HtT3J6pcAiDKk/M+VqbuPL+ZY7ocCNNK7xpeUuBz9aGSAIuGJo9yepMLLqYzZR7P
+c6r9KSlW1ZsBrQwjkTZ1nN1d9kMY+SBpW4PY3lfA69dfPDQ6hsbg+7e37MkP2ygP
+LL3H+oljAZJiRSWu7+F2FwTUkNom9va6aKr2FuUgr7pd9PnBoDIEyEWu1sF6nJ85
+vVbGRCCkvub+D55U6Q7PsscOY8lc6Z3JaQA7sv8alSHR+q9AOEvkc4uc2V4J9LOa
+ZlZzOD0CgYEA1nYxoHe1hNIKHRjY38wSbyfPAKZ9tzrPLq44JBe+9MoMN+pOLA9N
+SVorLz9OHl8A4gCMeLgXrX06ECHor1dbC90k+jiU0taOdcADs0BwAbWrgMyiOsjJ
+RbwTRUTsAuLLldTBf4sFysCyG0SYCqFL7hJJh2HJKOg428V8b0sfaV8CgYEA1Lcw
+eEc11R80Huv5yV8WeYu+uqBo38P1qoC+zlpa7PRw9GX4Ovi20hwkqWNlOyho0TJa
+x9HLl87WW/p7pjCM6kcBdHOn0eqHgoUYknYbsVyK0o2P5ajMldOkRu03lqjNuB9/
+Q4BYp8Sqwyv/aTGMNDa/TlX8S+3Lk8WIkhBPQi8CgYEAm6xoEadTp/ofRUfIBYvI
+xc8Lv9ka4GpcAfKM5gYmouIXRG9cFzd0To6ZUk6NkhY8OdFUJjzbUx/XieZTVRQA
+DviT4t43iWQdPPQIu5FGvLb2qyPfjvQ4xdnj0yBYgS/HwBcT7lUn+yktIAYGp5C1
+4TZ9ETy2HG+U9lLAJLlPL9kCgYA+N6rMs39ya+MR3FG+bbqkKJTL/5lNQgL8MRYe
+Q11vC3xyb9TwYskOob6zcOguKn6mGcVlxt5287/NPXGnRXIiIEyzpBSFGMU0DvwF
+8tfcw8WzGkbplLrqY/Ib8Myem5c4cLYHp2XHBIYx+g+F1EE/EHhaUFowV0iBW3i7
+yFt2bwKBgG1EycLZ5EMXtitMNdPigRR7UJnNAPSreYrSrFSKYrR6tAHhsAf/uiK/
+Wf3hXJvIwRrJOJc29juCi3r8i5xhcoRN7kdd1dOXzEX4azn6PCi27b7wDN20w/Wv
+QeeV1QQ0x1ok7AHWLBEZLuYFaded0UnGrDwtwKQ9wg8oH98n8jkR
-----END RSA PRIVATE KEY-----
diff --git a/test/node/fixtures/passcert.pfx b/test/node/fixtures/passcert.pfx
new file mode 100644
index 000000000..d48d66b3b
Binary files /dev/null and b/test/node/fixtures/passcert.pfx differ
diff --git a/test/node/fixtures/test.aac b/test/node/fixtures/test.aac
new file mode 100644
index 000000000..8b91d23e7
Binary files /dev/null and b/test/node/fixtures/test.aac differ
diff --git a/test/node/flags.js b/test/node/flags.js
index 20d095ac9..67d0bbda9 100644
--- a/test/node/flags.js
+++ b/test/node/flags.js
@@ -1,59 +1,35 @@
+'use strict';
-var EventEmitter = require('events').EventEmitter
- , request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express();
+const assert = require('assert');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
-app.get('/error', function(req, res){
- throw new Error('oh noes');
-});
-
-app.get('/login', function(req, res){
- res.status(200).send('');
-});
-
-app.get('/bad-request', function(req, res){
- res.status(400).end();
-});
-
-app.get('/unauthorized', function(req, res){
- res.status(401).end();
-});
-
-app.get('/not-acceptable', function(req, res){
- res.status(406).end();
-});
+describe('flags', () => {
+ let setup;
+ let base;
-app.get('/no-content', function(req, res){
- res.status(204).end();
-});
-
-app.listen(3004);
-
-describe('flags', function(){
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
- describe('with 4xx response', function(){
- it('should set res.error and res.clientError', function(done){
- request
- .get('http://localhost:3004/notfound')
- .end(function(err, res){
- assert(err);
+ describe('with 4xx response', () => {
+ it('should set res.error and res.clientError', (done) => {
+ request.get(`${base}/notfound`).end((error, res) => {
+ assert(error);
assert(!res.ok, 'response should not be ok');
assert(res.error, 'response should be an error');
assert(res.clientError, 'response should be a client error');
assert(!res.serverError, 'response should not be a server error');
done();
});
- })
- })
+ });
+ });
- describe('with 5xx response', function(){
- it('should set res.error and res.serverError', function(done){
- request
- .get('http://localhost:3004/error')
- .end(function(err, res){
- assert(err);
+ describe('with 5xx response', () => {
+ it('should set res.error and res.serverError', (done) => {
+ request.get(`${base}/error`).end((error, res) => {
+ assert(error);
assert(!res.ok, 'response should not be ok');
assert(!res.notFound, 'response should not be notFound');
assert(res.error, 'response should be an error');
@@ -61,66 +37,80 @@ describe('flags', function(){
assert(res.serverError, 'response should be a server error');
done();
});
- })
- })
+ });
+ });
- describe('with 404 Not Found', function(){
- it('should res.notFound', function(done){
- request
- .get('http://localhost:3004/notfound')
- .end(function(err, res){
- assert(err);
+ describe('with 404 Not Found', () => {
+ it('should res.notFound', (done) => {
+ request.get(`${base}/notfound`).end((error, res) => {
+ assert(error);
assert(res.notFound, 'response should be .notFound');
done();
});
- })
- })
+ });
+ });
- describe('with 400 Bad Request', function(){
- it('should set req.badRequest', function(done){
- request
- .get('http://localhost:3004/bad-request')
- .end(function(err, res){
- assert(err);
+ describe('with 400 Bad Request', () => {
+ it('should set req.badRequest', (done) => {
+ request.get(`${base}/bad-request`).end((error, res) => {
+ assert(error);
assert(res.badRequest, 'response should be .badRequest');
done();
});
- })
- })
+ });
+ });
- describe('with 401 Bad Request', function(){
- it('should set res.unauthorized', function(done){
- request
- .get('http://localhost:3004/unauthorized')
- .end(function(err, res){
- assert(err);
+ describe('with 401 Bad Request', () => {
+ it('should set res.unauthorized', (done) => {
+ request.get(`${base}/unauthorized`).end((error, res) => {
+ assert(error);
assert(res.unauthorized, 'response should be .unauthorized');
done();
});
- })
- })
+ });
+ });
- describe('with 406 Not Acceptable', function(){
- it('should set res.notAcceptable', function(done){
- request
- .get('http://localhost:3004/not-acceptable')
- .end(function(err, res){
- assert(err);
+ describe('with 406 Not Acceptable', () => {
+ it('should set res.notAcceptable', (done) => {
+ request.get(`${base}/not-acceptable`).end((error, res) => {
+ assert(error);
assert(res.notAcceptable, 'response should be .notAcceptable');
done();
});
- })
- })
+ });
+ });
- describe('with 204 No Content', function(){
- it('should set res.noContent', function(done){
- request
- .get('http://localhost:3004/no-content')
- .end(function(err, res){
- assert(!err);
+ describe('with 204 No Content', () => {
+ it('should set res.noContent', (done) => {
+ request.get(`${base}/no-content`).end((error, res) => {
+ assert(!error);
assert(res.noContent, 'response should be .noContent');
done();
});
- })
- })
-})
+ });
+ });
+
+ describe('with 201 Created', () => {
+ it('should set res.created', (done) => {
+ request.post(`${base}/created`).end((error, res) => {
+ assert(!error);
+ assert(res.created, 'response should be .created');
+ done();
+ });
+ });
+ });
+
+ describe('with 422 Unprocessable Entity', () => {
+ it('should set res.unprocessableEntity', (done) => {
+ request.post(`${base}/unprocessable-entity`).end((error, res) => {
+ assert(error);
+ assert(
+ res.unprocessableEntity,
+ 'response should be .unprocessableEntity'
+ );
+
+ done();
+ });
+ });
+ });
+});
diff --git a/test/node/form.js b/test/node/form.js
index db7765e57..f24634133 100644
--- a/test/node/form.js
+++ b/test/node/form.js
@@ -1,76 +1,70 @@
+'use strict';
-var request = require('../../')
- , express = require('express')
- , assert = require('better-assert')
- , app = express();
+const assert = require('assert');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
-app.post('/echo', function(req, res){
- res.writeHead(200, req.headers);
- req.pipe(res);
-});
+describe('Merging objects', () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
-app.get('/form-data', function(req, res){
- res.header('Content-Type', 'application/x-www-form-urlencoded');
- res.send('pet[name]=manny');
+ it("Don't mix Buffer and JSON", () => {
+ assert.throws(() => {
+ request
+ .post('/echo')
+ .send(Buffer.from('some buffer'))
+ .send({ allowed: false });
+ });
+ });
});
-app.listen(3002);
+describe('req.send(String)', () => {
+ let setup;
+ let base;
-describe('req.send(Object) as "form"', function(){
- describe('with req.type() set to form', function(){
- it('should send x-www-form-urlencoded data', function(done){
- request
- .post('http://localhost:3002/echo')
- .type('form')
- .send({ name: 'tobi' })
- .end(function(err, res){
- res.header['content-type'].should.equal('application/x-www-form-urlencoded');
- res.text.should.equal('name=tobi');
- done();
- });
- })
- })
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
- describe('when called several times', function(){
- it('should merge the objects', function(done){
- request
- .post('http://localhost:3002/echo')
- .type('form')
- .send({ name: { first: 'tobi', last: 'holowaychuk' } })
- .send({ age: '1' })
- .end(function(err, res){
- res.header['content-type'].should.equal('application/x-www-form-urlencoded');
- res.text.should.equal('name%5Bfirst%5D=tobi&name%5Blast%5D=holowaychuk&age=1');
+ it('should default to "form"', (done) => {
+ request
+ .post(`${base}/echo`)
+ .send('user[name]=tj')
+ .send('user[email]=tj@vision-media.ca')
+ .end((error, res) => {
+ res.header['content-type'].should.equal(
+ 'application/x-www-form-urlencoded'
+ );
+ res.body.should.eql({
+ user: { name: 'tj', email: 'tj@vision-media.ca' }
+ });
done();
});
- })
- })
-})
+ });
+});
-describe('req.send(String)', function(){
- it('should default to "form"', function(done){
- request
- .post('http://localhost:3002/echo')
- .send('user[name]=tj')
- .send('user[email]=tj@vision-media.ca')
- .end(function(err, res){
- res.header['content-type'].should.equal('application/x-www-form-urlencoded');
- res.body.should.eql({ user: { name: 'tj', email: 'tj@vision-media.ca' } });
- done();
- })
- })
-})
+describe('res.body', () => {
+ let setup;
+ let base;
-describe('res.body', function(){
- describe('application/x-www-form-urlencoded', function(){
- it('should parse the body', function(done){
- request
- .get('http://localhost:3002/form-data')
- .end(function(err, res){
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ describe('application/x-www-form-urlencoded', () => {
+ it('should parse the body', (done) => {
+ request.get(`${base}/form-data`).end((error, res) => {
res.text.should.equal('pet[name]=manny');
- res.body.should.eql({ pet: { name: 'manny' }});
+ res.body.should.eql({ pet: { name: 'manny' } });
done();
});
- })
- })
-})
+ });
+ });
+});
diff --git a/test/node/http2.js b/test/node/http2.js
new file mode 100644
index 000000000..c587ae27a
--- /dev/null
+++ b/test/node/http2.js
@@ -0,0 +1,44 @@
+'use strict';
+if (!process.env.HTTP2_TEST) {
+ return;
+}
+
+const assert = require('assert');
+const request = require('../..');
+const getSetup = require('../support/setup');
+
+describe('request.get().http2()', () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ it('should preserve the encoding of the url', (done) => {
+ request
+ .get(`${base}/url?a=(b%29`)
+ .http2()
+ .end((error, res) => {
+ assert.equal('/url?a=(b%29', res.text);
+ done();
+ });
+ });
+
+ it('should format the url', () =>
+ request
+ .get(new URL(`${base}/login`))
+ .http2()
+ .then((res) => {
+ assert(res.ok);
+ }));
+
+ it.skip('should default to http', () =>
+ request
+ .get('localhost:5000/login')
+ .http2()
+ .then((res) => {
+ assert.equal(res.status, 200);
+ }));
+});
diff --git a/test/node/https.js b/test/node/https.js
index c2daadc7d..70b80b078 100644
--- a/test/node/https.js
+++ b/test/node/https.js
@@ -1,59 +1,247 @@
+'use strict';
-var EventEmitter = require('events').EventEmitter
- , request = require('../..')
- , express = require('express')
- , assert = require('better-assert')
- , app = express()
- , url = require('url')
- , https = require('https')
- , fs = require('fs')
- , key = fs.readFileSync(__dirname + '/fixtures/key.pem')
- , cert = fs.readFileSync(__dirname + '/fixtures/cert.pem')
- , server;
-
-app.get('/', function(req, res){
+const assert = require('assert');
+
+const https = require('https');
+const fs = require('fs');
+const express = require('../support/express');
+const request = require('../support/client');
+
+const app = express();
+
+const ca = fs.readFileSync(`${__dirname}/fixtures/ca.cert.pem`);
+const key = fs.readFileSync(`${__dirname}/fixtures/key.pem`);
+const pfx = fs.readFileSync(`${__dirname}/fixtures/cert.pfx`);
+const cert = fs.readFileSync(`${__dirname}/fixtures/cert.pem`);
+const passpfx = fs.readFileSync(`${__dirname}/fixtures/passcert.pfx`);
+
+/*
+
+openssl genrsa -out ca.key.pem 2048
+openssl req -x509 -new -nodes -key ca.key.pem -sha256 -days 5000 -out ca.cert.pem # specify CN = CA
+
+openssl genrsa -out key.pem 2048
+openssl req -new -key key.pem -out cert.csr # specify CN = localhost
+
+openssl x509 -req -in cert.csr -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -out cert.pem -days 5000 -sha256
+openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.pfx # empty password
+
+openssl pkcs12 -export -in cert.pem -inkey key.pem -out passcert.pfx # password test
+
+ */
+let http2;
+if (process.env.HTTP2_TEST) {
+ http2 = require('http2');
+}
+
+let server;
+
+app.get('/', (request_, res) => {
res.send('Safe and secure!');
});
-server = https.createServer({
- key: key,
- cert: cert
-}, app);
-
-server.listen(8443);
+// WARNING: this .listen() boilerplate is slightly different from most tests
+// due to HTTPS. Do not copy/paste without examination.
+const base = 'https://localhost';
+let testEndpoint;
-describe('https', function(){
+describe('https', () => {
+ describe('certificate authority', () => {
+ before(function listen(done) {
+ server = process.env.HTTP2_TEST
+ ? http2.createSecureServer(
+ {
+ key,
+ cert
+ },
+ app
+ )
+ : https.createServer(
+ {
+ key,
+ cert
+ },
+ app
+ );
- describe('request', function(){
- it('should give a good response', function(done){
- request
- .get('https://localhost:8443/')
- .ca(cert)
- .end(function(err, res){
- assert(res.ok);
- assert('Safe and secure!' === res.text);
+ server.listen(0, function listening() {
+ testEndpoint = `${base}:${server.address().port}`;
done();
});
});
- });
- describe('.agent', function () {
- it('should be able to make multiple requests without redefining the certificate', function(done){
- var agent = request.agent({ca: cert});
- agent
- .get('https://localhost:8443/')
- .end(function(err, res){
- assert(res.ok);
- assert('Safe and secure!' === res.text);
- agent
- .get(url.parse('https://localhost:8443/'))
- .end(function(err, res){
+ after(() => {
+ if (server) server.close();
+ });
+
+ describe('request', () => {
+ it('should give a good response', (done) => {
+ request
+ .get(testEndpoint)
+ .ca(ca)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ });
+
+ it('should reject unauthorized response', () => {
+ return request
+ .get(testEndpoint)
+ .trustLocalhost(false)
+ .then(
+ () => {
+ throw new Error('Allows MITM');
+ },
+ () => {}
+ );
+ });
+
+ it('should not reject unauthorized response', () => {
+ return request
+ .get(testEndpoint)
+ .disableTLSCerts()
+ .then(({ status }) => {
+ assert.strictEqual(status, 200);
+ });
+ });
+
+ it('should trust localhost unauthorized response', () => {
+ return request.get(testEndpoint).trustLocalhost(true);
+ });
+
+ it('should trust overriden localhost unauthorized response', () => {
+ return request
+ .get(`https://example.com:${server.address().port}`)
+ .connect('127.0.0.1')
+ .trustLocalhost();
+ });
+ });
+
+ describe('.agent', () => {
+ it('should be able to make multiple requests without redefining the certificate', (done) => {
+ const agent = request.agent({ ca });
+ agent.get(testEndpoint).end((error, res) => {
+ assert.ifError(error);
assert(res.ok);
- assert('Safe and secure!' === res.text);
- done();
+ assert.strictEqual('Safe and secure!', res.text);
+ agent.get(new URL(testEndpoint)).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
});
});
});
});
-})
+ describe.skip('client certificates', () => {
+ before(function listen(done) {
+ server = process.env.HTTP2_TEST
+ ? http2.createSecureServer(
+ {
+ ca,
+ key,
+ cert,
+ requestCert: true,
+ rejectUnauthorized: true
+ },
+ app
+ )
+ : https.createServer(
+ {
+ ca,
+ key,
+ cert,
+ requestCert: true,
+ rejectUnauthorized: true
+ },
+ app
+ );
+
+ server.listen(0, function listening() {
+ testEndpoint = `${base}:${server.address().port}`;
+ done();
+ });
+ });
+
+ after(() => {
+ if (server) server.close();
+ });
+
+ describe('request', () => {
+ it('should give a good response with client certificates and CA', (done) => {
+ request
+ .get(testEndpoint)
+ .ca(ca)
+ .key(key)
+ .cert(cert)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ });
+ it('should give a good response with client pfx', (done) => {
+ request
+ .get(testEndpoint)
+ .pfx(pfx)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ });
+ it('should give a good response with client pfx with passphrase', (done) => {
+ request
+ .get(testEndpoint)
+ .pfx({
+ pfx: passpfx,
+ passphrase: 'test'
+ })
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ });
+ });
+
+ describe('.agent', () => {
+ it('should be able to make multiple requests without redefining the certificates', (done) => {
+ const agent = request.agent({ ca, key, cert });
+ agent.get(testEndpoint).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ agent.get(new URL(testEndpoint)).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ });
+ });
+ it('should be able to make multiple requests without redefining pfx', (done) => {
+ const agent = request.agent({ pfx });
+ agent.get(testEndpoint).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ agent.get(new URL(testEndpoint)).end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('Safe and secure!', res.text);
+ done();
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/test/node/image.js b/test/node/image.js
index 54ced7a58..2b66b499f 100644
--- a/test/node/image.js
+++ b/test/node/image.js
@@ -1,34 +1,54 @@
-/* jshint indent: 2 */
-/* jshint laxcomma: true */
+'use strict';
-var EventEmitter = require('events').EventEmitter
- , fs = require('fs')
- , request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express();
+const fs = require('fs');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
+const img = fs.readFileSync(`${__dirname}/fixtures/test.png`);
-describe('res.body', function(){
- 'use strict';
+describe('res.body', () => {
+ let setup;
+ let base;
- var img = fs.readFileSync(__dirname + '/fixtures/test.png');
-
- app.get('/image', function(req, res){
- res.writeHead(200, {'Content-Type': 'image/png' });
- res.end(img, 'binary');
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
});
- app.listen(3011);
-
- describe('image/png', function(){
- it('should parse the body', function(done){
- request
- .get('http://localhost:3011/image')
- .end(function(err, res){
+ describe('image/png', () => {
+ it('should parse the body', (done) => {
+ request.get(`${base}/image`).end((error, res) => {
+ res.type.should.equal('image/png');
+ Buffer.isBuffer(res.body).should.be.true();
(res.body.length - img.length).should.equal(0);
done();
});
});
});
+ describe('application/octet-stream', () => {
+ it('should parse the body', (done) => {
+ request
+ .get(`${base}/image-as-octets`)
+ .buffer(true) // that's tech debt :(
+ .end((error, res) => {
+ res.type.should.equal('application/octet-stream');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+ });
+ });
+ });
+ describe('application/octet-stream', () => {
+ it('should parse the body (using responseType)', (done) => {
+ request
+ .get(`${base}/image-as-octets`)
+ .responseType('blob')
+ .end((error, res) => {
+ res.type.should.equal('application/octet-stream');
+ Buffer.isBuffer(res.body).should.be.true();
+ (res.body.length - img.length).should.equal(0);
+ done();
+ });
+ });
+ });
});
diff --git a/test/node/incoming-multipart.js b/test/node/incoming-multipart.js
index 84ba031b8..ad18fdf3a 100644
--- a/test/node/incoming-multipart.js
+++ b/test/node/incoming-multipart.js
@@ -1,5 +1,4 @@
-
-// var request = require('../../')
+// var request = require('../support/client')
// , express = require('express')
// , assert = require('assert')
// , app = express()
@@ -24,18 +23,25 @@
// }, 1000);
// });
-// app.listen(3007);
+// var base = 'http://localhost'
+// var server;
+// before(function listen(done) {
+// server = app.listen(0, function listening() {
+// base += ':' + server.address().port;
+// done();
+// });
+// });
// describe('request multipart/form-data', function(){
// describe('req.body', function(){
// it('should be populated with fields', function(done){
-// request.get('http://localhost:3007/', function(err, res){
+// request.get(base, function(err, res){
// if (err) return done(err);
// res.status.should.equal(200);
// res.body.should.eql({ name: 'tobi' });
// res.files.image.name.should.equal('something.png');
// res.files.image.type.should.equal('image/png');
-// assert(null == res.text, 'res.text should be empty for multipart');
+// assert.equal(null, res.text, 'res.text should be empty for multipart');
// done();
// });
// })
diff --git a/test/node/inflate.js b/test/node/inflate.js
index 354d3c21a..7b48a1e01 100644
--- a/test/node/inflate.js
+++ b/test/node/inflate.js
@@ -1,64 +1,247 @@
+'use strict';
+require('should');
+require('should-http');
-var request = require('../../')
- , express = require('express')
- , zlib
+const assert = require('assert');
+const zlib = require('zlib');
+let http = require('http');
+const getPort = require('get-port');
+const express = require('../support/express');
+const request = require('../support/client');
-/**
- * Only require zlib for Node 0.6+.
- */
+if (process.env.HTTP2_TEST) {
+ http = require('http2');
+}
-try {
- zlib = require('zlib');
-} catch (e) { }
+const app = express();
+const subject = 'some long long long long string';
-if (zlib) {
- var app = express()
- , subject = 'some long long long long string';
+let base = 'http://localhost';
+let server;
- app.listen(3080);
+before(function listen(done) {
+ server = http.createServer(app);
- app.get('/binary', function(req, res){
- zlib.deflate(subject, function (err, buf){
- res.set('Content-Encoding', 'gzip');
- res.send(buf);
+ getPort().then((port) => {
+ server = server.listen(port, function listening() {
+ base += `:${server.address().port}`;
+ done();
});
});
+});
+
+app.get('/binary', (request_, res) => {
+ zlib.deflate(subject, (error, buf) => {
+ res.set('Content-Encoding', 'gzip');
+ res.send(buf);
+ });
+});
+
+app.get('/binary-brotli', (request_, res) => {
+ zlib.brotliCompress(subject, (error, buf) => {
+ res.set('Content-Encoding', 'br');
+ res.send(buf);
+ });
+});
+
+app.get('/corrupt', (request_, res) => {
+ res.set('Content-Encoding', 'gzip');
+ res.send('blah');
+});
+
+app.get('/corrupt-brotli', (request_, res) => {
+ res.set('Content-Encoding', 'br');
+ res.send('blah');
+});
+
+app.get('/nocontent', (request_, res, next) => {
+ res.statusCode = 204;
+ res.set('Content-Type', 'text/plain');
+ res.set('Content-Encoding', 'gzip');
+ res.send('');
+});
+
+app.get('/nocontent-brotli', (request_, res, next) => {
+ res.statusCode = 204;
+ res.set('Content-Type', 'text/plain');
+ res.set('Content-Encoding', 'br');
+ res.send('');
+});
+
+app.get('/', (request_, res, next) => {
+ zlib.deflate(subject, (error, buf) => {
+ res.set('Content-Type', 'text/plain');
+ res.set('Content-Encoding', 'gzip');
+ res.send(buf);
+ });
+});
- app.get('/', function (req, res, next){
- zlib.deflate(subject, function (err, buf){
- res.set('Content-Type', 'text/plain');
- res.set('Content-Encoding', 'gzip');
- res.send(buf);
+app.get('/junk', (request_, res) => {
+ zlib.deflate(subject, (error, buf) => {
+ res.set('Content-Type', 'text/plain');
+ res.set('Content-Encoding', 'gzip');
+ res.write(buf);
+ res.end(' 0 junk');
+ });
+});
+
+app.get('/junk-brotli', (request_, res) => {
+ zlib.brotliCompress(subject, (error, buf) => {
+ res.set('Content-Type', 'text/plain');
+ res.set('Content-Encoding', 'br');
+ res.write(buf);
+ res.end(' 0 junk');
+ });
+});
+
+app.get('/chopped', (request_, res) => {
+ zlib.deflate(`${subject}123456`, (error, buf) => {
+ res.set('Content-Type', 'text/plain');
+ res.set('Content-Encoding', 'gzip');
+ res.send(buf.slice(0, -1));
+ });
+});
+
+app.get('/chopped-brotli', (request_, res) => {
+ zlib.brotliCompress(`${subject}123456`, (error, buf) => {
+ res.set('Content-Type', 'text/plain');
+ res.set('Content-Encoding', 'br');
+ res.send(buf.slice(0, -1));
+ });
+});
+
+describe('zlib', () => {
+ it('should deflate the content', (done) => {
+ request.get(base).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.equal(subject);
+ res.headers['content-length'].should.be.below(subject.length);
+ done();
});
});
- describe('zlib', function(){
- it('should deflate the content', function(done){
- request
- .get('http://localhost:3080')
- .end(function(err, res){
- res.should.have.status(200);
- res.text.should.equal(subject);
- res.headers['content-length'].should.be.below(subject.length);
+ it('should protect from zip bombs', (done) => {
+ request
+ .get(base)
+ .buffer(true)
+ .maxResponseSize(1)
+ .end((error, res) => {
+ try {
+ assert.equal('Maximum response size reached', error && error.message);
done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should ignore trailing junk', (done) => {
+ request.get(`${base}/junk`).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.equal(subject);
+ done();
+ });
+ });
+
+ it('should ignore trailing junk-brotli', (done) => {
+ request.get(`${base}/junk-brotli`).end((error, res) => {
+ res.should.have.status(200);
+ res.text.should.equal(subject);
+ done();
+ });
+ });
+
+ it('should ignore missing data', (done) => {
+ request.get(`${base}/chopped`).end((error, res) => {
+ assert.equal(undefined, error);
+ res.should.have.status(200);
+ res.text.should.startWith(subject);
+ done();
+ });
+ });
+
+ it('should ignore missing brotli data', (done) => {
+ request.get(`${base}/chopped-brotli`).end((error, res) => {
+ assert.equal(undefined, error);
+ res.should.have.status(200);
+ res.text.should.startWith(subject);
+ done();
+ });
+ });
+
+ it('should handle corrupted responses', (done) => {
+ request.get(`${base}/corrupt`).end((error, res) => {
+ assert(error, 'missing error');
+ assert(!res, 'response should not be defined');
+ done();
+ });
+ });
+
+ it('should handle brotli corrupted responses', (done) => {
+ request.get(`${base}/corrupt-brotli`).end((error, res) => {
+ res.text.should.equal('');
+ done();
+ });
+ });
+
+ it('should handle no content with gzip header', (done) => {
+ request.get(`${base}/nocontent`).end((error, res) => {
+ assert.ifError(error);
+ assert(res);
+ res.should.have.status(204);
+ res.text.should.equal('');
+ res.headers.should.not.have.property('content-length');
+ done();
+ });
+ });
+
+ it('should handle no content with gzip header', (done) => {
+ request.get(`${base}/nocontent-brotli`).end((error, res) => {
+ assert.ifError(error);
+ assert(res);
+ res.should.have.status(204);
+ res.text.should.equal('');
+ res.headers.should.not.have.property('content-length');
+ done();
+ });
+ });
+
+ describe('without encoding set', () => {
+ it('should buffer if asked', () => {
+ return request
+ .get(`${base}/binary`)
+ .buffer(true)
+ .then((res) => {
+ res.should.have.status(200);
+ assert(res.headers['content-length']);
+ assert(res.body.byteLength);
+ assert.equal(subject, res.body.toString());
});
});
- describe('without encoding set', function(){
- it('should emit buffers', function(done){
- request
- .get('http://localhost:3080/binary')
- .end(function(err, res){
- res.should.have.status(200);
- res.headers['content-length'].should.be.below(subject.length);
+ it('should buffer Brotli if asked', () => {
+ return request
+ .get(`${base}/binary-brotli`)
+ .buffer(true)
+ .then((res) => {
+ res.should.have.status(200);
+ assert(res.headers['content-length']);
+ assert(res.body.byteLength);
+ assert.equal(subject, res.body.toString());
+ });
+ });
+
+ it('should emit buffers', (done) => {
+ request.get(`${base}/binary`).end((error, res) => {
+ res.should.have.status(200);
+ res.headers['content-length'].should.be.below(subject.length);
- res.on('data', function(chunk){
- chunk.should.have.length(subject.length);
- });
+ res.on('data', (chunk) => {
+ chunk.should.have.length(subject.length);
+ });
- res.on('end', done);
- });
- })
- })
+ res.on('end', done);
+ });
+ });
});
-}
+});
diff --git a/test/node/json.js b/test/node/json.js
deleted file mode 100644
index eb80431cd..000000000
--- a/test/node/json.js
+++ /dev/null
@@ -1,194 +0,0 @@
-
-var EventEmitter = require('events').EventEmitter
- , request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express();
-
-app.all('/echo', function(req, res){
- res.writeHead(200, req.headers);
- req.pipe(res);
-});
-
-app.get('/json', function(req, res){
- res.send({ name: 'manny' });
-});
-
-app.get('/no-content', function(req, res){
- res.status(204);
- res.end();
-});
-
-app.get('/json-hal', function(req, res){
- res.set('content-type', 'application/hal+json');
- res.send({ name: 'hal 5000' });
-});
-
-app.get('/collection-json', function(req, res){
- res.set('content-type', 'application/vnd.collection+json');
- res.send({ name: 'chewbacca' });
-});
-
-app.listen(3005);
-
-describe('req.send(Object) as "json"', function(){
- it('should default to json', function(done){
- request
- .post('http://localhost:3005/echo')
- .send({ name: 'tobi' })
- .end(function(err, res){
- res.should.be.json
- res.text.should.equal('{"name":"tobi"}');
- done();
- });
- })
-
- it('should work with arrays', function(done){
- request
- .post('http://localhost:3005/echo')
- .send([1,2,3])
- .end(function(err, res){
- res.should.be.json
- res.text.should.equal('[1,2,3]');
- done();
- });
- });
-
- it('should work with value null', function(done){
- request
- .post('http://localhost:3005/echo')
- .type('json')
- .send('null')
- .end(function(err, res){
- res.should.be.json
- assert(res.body === null);
- done();
- });
- });
-
- it('should work with value false', function(done){
- request
- .post('http://localhost:3005/echo')
- .type('json')
- .send('false')
- .end(function(err, res){
- res.should.be.json
- res.body.should.equal(false);
- done();
- });
- });
-
- it('should work with value 0', function(done){
- request
- .post('http://localhost:3005/echo')
- .type('json')
- .send('0')
- .end(function(err, res){
- res.should.be.json
- res.body.should.equal(0);
- done();
- });
- });
-
- it('should work with empty string value', function(done){
- request
- .post('http://localhost:3005/echo')
- .type('json')
- .send('""')
- .end(function(err, res){
- res.should.be.json
- res.body.should.equal("");
- done();
- });
- });
-
- it('should work with GET', function(done){
- request
- .get('http://localhost:3005/echo')
- .send({ tobi: 'ferret' })
- .end(function(err, res){
- res.should.be.json
- res.text.should.equal('{"tobi":"ferret"}');
- done();
- });
- });
-
- describe('when called several times', function(){
- it('should merge the objects', function(done){
- request
- .post('http://localhost:3005/echo')
- .send({ name: 'tobi' })
- .send({ age: 1 })
- .end(function(err, res){
- res.should.be.json
- res.text.should.equal('{"name":"tobi","age":1}');
- done();
- });
- })
- })
-})
-
-describe('res.body', function(){
- describe('application/json', function(){
- it('should parse the body', function(done){
- request
- .get('http://localhost:3005/json')
- .end(function(err, res){
- res.text.should.equal('{"name":"manny"}');
- res.body.should.eql({ name: 'manny' });
- done();
- });
- })
- })
-
- describe('HEAD requests', function(){
- it('should not throw a parse error', function(done){
- request
- .head('http://localhost:3005/json')
- .end(function(err, res){
- assert(err === null);
- assert(res.text === undefined)
- assert(Object.keys(res.body).length === 0)
- done();
- });
- });
- });
-
- describe('No content', function(){
- it('should not throw a parse error', function(done){
- request
- .get('http://localhost:3005/no-content')
- .end(function(err, res){
- assert(err === null);
- assert(res.text === '');
- assert(Object.keys(res.body).length === 0);
- done();
- });
- });
- });
-
- describe('application/json+hal', function(){
- it('should parse the body', function(done){
- request
- .get('http://localhost:3005/json-hal')
- .end(function(err, res){
- if (err) return done(err);
- res.text.should.equal('{"name":"hal 5000"}');
- res.body.should.eql({ name: 'hal 5000' });
- done();
- });
- })
- })
-
- describe('vnd.collection+json', function(){
- it('should parse the body', function(done){
- request
- .get('http://localhost:3005/collection-json')
- .end(function(err, res){
- res.text.should.equal('{"name":"chewbacca"}');
- res.body.should.eql({ name: 'chewbacca' });
- done();
- });
- })
- })
-})
diff --git a/test/node/lookup.js b/test/node/lookup.js
new file mode 100644
index 000000000..0eafef210
--- /dev/null
+++ b/test/node/lookup.js
@@ -0,0 +1,26 @@
+'use strict';
+const assert = require('assert');
+const dns = require('dns');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
+
+let base = null;
+
+function myLookup(hostname, options, callback) {
+ dns.lookup(hostname, options, callback);
+}
+
+describe('req.lookup()', () => {
+ before(async () => {
+ const setup = await getSetup();
+ base = setup.uri;
+ });
+ it('should set a custom lookup', (done) => {
+ const r = request.get(`${base}/ok`).lookup(myLookup);
+ assert(r.lookup() === myLookup);
+ r.then((res) => {
+ res.text.should.equal('ok');
+ done();
+ });
+ });
+});
diff --git a/test/node/multipart.js b/test/node/multipart.js
index 95631f3f5..bf992b382 100644
--- a/test/node/multipart.js
+++ b/test/node/multipart.js
@@ -1,265 +1,257 @@
+'use strict';
-var request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express()
- , fs = require('fs');
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const should = require('should');
+const getPort = require('get-port');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
+const IS_WINDOWS = require('os').platform() === 'win32';
function read(file) {
return fs.readFileSync(file, 'utf8');
}
-// app.post('/echo', function(req, res){
-// res.writeHead(200, req.headers);
-// req.pipe(res);
-// });
+function getFullPath(filename) {
+ if (!IS_WINDOWS) {
+ return filename;
+ }
-// app.listen(3005);
-
-// function boundary(ct) {
-// return ct.match(/boundary="(.*)"/)[1];
-// }
-
-describe('Request', function(){
-// describe('#part()', function(){
-// it('should return a new Part', function(){
-// var req = request.post('http://localhost:3005');
-// req.part().constructor.name.should.equal('Part');
-// req.part().constructor.name.should.equal('Part');
-// req.part().should.not.equal(req.part());
-// })
-// })
-
-// it('should default res.files to {}', function(done){
-// var req = request.post('http://localhost:3005/echo');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// res.files.should.eql({});
-// res.body.should.eql({});
-// done();
-// });
-// })
-
-// describe('#field(name, value)', function(){
-// it('should set a multipart field value', function(done){
-// var req = request.post('http://localhost:3005/echo');
-
-// req.field('user[name]', 'tobi');
-// req.field('user[age]', '2');
-// req.field('user[species]', 'ferret');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// res.body['user[name]'].should.equal('tobi');
-// res.body['user[age]'].should.equal('2');
-// res.body['user[species]'].should.equal('ferret');
-// done();
-// });
-// })
-
-// it('should work with file attachments', function(done){
-// var req = request.post('http://localhost:3005/echo');
-
-// req.field('name', 'Tobi');
-// req.attach('document', 'test/node/fixtures/user.html');
-// req.field('species', 'ferret');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// res.body.name.should.equal('Tobi');
-// res.body.species.should.equal('ferret');
-
-// var html = res.files.document;
-// html.name.should.equal('user.html');
-// html.type.should.equal('text/html');
-// read(html.path).should.equal('name ');
-// done();
-// })
-// })
-// })
-
-// describe('#attach(name, path)', function(){
-// it('should attach a file', function(done){
-// var req = request.post('http://localhost:3005/echo');
-
-// req.attach('one', 'test/node/fixtures/user.html');
-// req.attach('two', 'test/node/fixtures/user.json');
-// req.attach('three', 'test/node/fixtures/user.txt');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// var html = res.files.one;
-// var json = res.files.two
-// var text = res.files.three;
-
-// html.name.should.equal('user.html');
-// html.type.should.equal('text/html');
-// read(html.path).should.equal('name ');
-
-// json.name.should.equal('user.json');
-// json.type.should.equal('application/json');
-// read(json.path).should.equal('{"name":"tobi"}');
-
-// text.name.should.equal('user.txt');
-// text.type.should.equal('text/plain');
-// read(text.path).should.equal('Tobi');
-
-// done();
-// })
-// })
-
-// describe('when a file does not exist', function(){
-// it('should emit an error', function(done){
-// var req = request.post('http://localhost:3005/echo');
-
-// req.attach('name', 'foo');
-// req.attach('name2', 'bar');
-// req.attach('name3', 'baz');
-
-// req.on('error', function(err){
-// err.message.should.include('ENOENT');
-// err.path.should.equal('foo');
-// done();
-// });
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// assert(0, 'end() was called');
-// });
-// })
-// })
-// })
+ const fullPath = path.join(__dirname, '../../', filename);
+ return fullPath;
+}
- describe('#attach(name, path, filename)', function(){
- it('should use the custom filename', function(done){
+describe('Multipart', () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ describe('#field(name, value)', () => {
+ it('should set a multipart field value', () => {
+ const request_ = request.post(`${base}/echo`);
+
+ request_.field('user[name]', 'tobi');
+ request_.field('user[age]', '2');
+ request_.field('user[species]', 'ferret');
+
+ return request_.then((res) => {
+ res.body['user[name]'].should.equal('tobi');
+ res.body['user[age]'].should.equal('2');
+ res.body['user[species]'].should.equal('ferret');
+ });
+ });
+
+ it('should work with file attachments', () => {
+ const request_ = request.post(`${base}/echo`);
+
+ request_.field('name', 'Tobi');
+ request_.attach('document', 'test/node/fixtures/user.html');
+ request_.field('species', 'ferret');
+
+ return request_.then((res) => {
+ res.body.name.should.equal('Tobi');
+ res.body.species.should.equal('ferret');
+
+ const html = res.files.document;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('name ');
+ });
+ });
+ });
+
+ describe('#attach(name, path)', () => {
+ it('should attach a file', () => {
+ const request_ = request.post(`${base}/echo`);
+
+ request_.attach('one', 'test/node/fixtures/user.html');
+ request_.attach('two', 'test/node/fixtures/user.json');
+ request_.attach('three', 'test/node/fixtures/user.txt');
+
+ return request_.then((res) => {
+ const html = res.files.one;
+ const json = res.files.two;
+ const text = res.files.three;
+
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('name ');
+
+ json.originalFilename.should.equal('user.json');
+ json.mimetype.should.equal('application/json');
+ read(json.filepath).should.equal('{"name":"tobi"}');
+
+ text.originalFilename.should.equal('user.txt');
+ text.mimetype.should.equal('text/plain');
+ read(text.filepath).should.equal('Tobi');
+ });
+ });
+
+ describe('when a file does not exist', () => {
+ it('should fail the request with an error', (done) => {
+ const request_ = request.post(`${base}/echo`);
+
+ request_.attach('name', 'foo');
+ // request_.attach('name2', 'bar');
+ // request_.attach('name3', 'baz');
+
+ request_.end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed.');
+ error.code.should.equal('ENOENT');
+ error.message.should.containEql('ENOENT');
+ if (IS_WINDOWS) {
+ error.path
+ .toLowerCase()
+ .should.equal(getFullPath('foo').toLowerCase());
+ } else {
+ error.path.should.equal(getFullPath('foo'));
+ }
+
+ done();
+ });
+ });
+
+ it('promise should fail', () => {
+ return request
+ .post(`${base}/echo`)
+ .field({ a: 1, b: 2 })
+ .attach('c', 'does-not-exist.txt')
+ .then(
+ (res) => assert.fail('It should not allow this'),
+ (err) => {
+ err.code.should.equal('ENOENT');
+ if (IS_WINDOWS) {
+ err.path
+ .toLowerCase()
+ .should.equal(
+ getFullPath('does-not-exist.txt').toLowerCase()
+ );
+ } else {
+ err.path.should.equal(getFullPath('does-not-exist.txt'));
+ }
+ }
+ );
+ });
+
+ it('should report ENOENT via the callback', (done) => {
+ request
+ .post(`${base}/echo`)
+ .attach('name', 'file-does-not-exist')
+ .end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed');
+ error.code.should.equal('ENOENT');
+ done();
+ });
+ });
+
+ it('should report ENOENT via Promise', () => {
+ return request
+ .post(`${base}/echo`)
+ .attach('name', 'file-does-not-exist')
+ .then(
+ (res) => assert.fail('Request should have failed'),
+ (err) => err.code.should.equal('ENOENT')
+ );
+ });
+ });
+ });
+
+ describe('#attach(name, path, filename)', () => {
+ it('should use the custom filename', () =>
request
- .post(':3005/echo')
- .attach('document', 'test/node/fixtures/user.html', 'doc.html')
- .end(function(err, res){
- if (err) return done(err);
- var html = res.files.document;
- html.name.should.equal('doc.html');
- html.type.should.equal('text/html');
- read(html.path).should.equal('name ');
- done();
- })
- })
- })
-})
-
-// describe('Part', function(){
-// describe('with a single part', function(){
-// it('should construct a multipart request', function(done){
-// var req = request.post('http://localhost:3005/echo');
-
-// req
-// .part()
-// .set('Content-Disposition', 'attachment; name="image"; filename="image.png"')
-// .set('Content-Type', 'image/png')
-// .write('some image data');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// var ct = res.header['content-type'];
-// ct.should.include('multipart/form-data; boundary=');
-// res.body.should.eql({});
-// res.files.image.name.should.equal('image.png');
-// res.files.image.type.should.equal('image/png');
-// done();
-// });
-// })
-// })
-
-// describe('with several parts', function(){
-// it('should construct a multipart request', function(done){
-
-// var req = request.post('http://localhost:3005/echo');
-
-// req.part()
-// .set('Content-Type', 'image/png')
-// .set('Content-Disposition', 'attachment; filename="myimage.png"')
-// .write('some image data');
-
-// var part = req.part()
-// .set('Content-Type', 'image/png')
-// .set('Content-Disposition', 'attachment; filename="another.png"')
-
-// part.write('random');
-// part.write('thing');
-// part.write('here');
-
-// req.part()
-// .set('Content-Disposition', 'form-data; name="name"')
-// .set('Content-Type', 'text/plain')
-// .write('tobi');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// res.body.name.should.equal('tobi');
-// Object.keys(res.files).should.eql(['myimage.png', 'another.png']);
-// done();
-// });
-// })
-// })
-
-// describe('with a Content-Type specified', function(){
-// it('should append the boundary', function(done){
-// var req = request
-// .post('http://localhost:3005/echo')
-// .type('multipart/form-data');
-
-// req
-// .part()
-// .set('Content-Type', 'text/plain')
-// .set('Content-Disposition', 'form-data; name="name"')
-// .write('Tobi');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// res.header['content-type'].should.include('boundary=');
-// res.body.name.should.equal('Tobi');
-// done();
-// });
-// })
-// })
-
-// describe('#name(str)', function(){
-// it('should set Content-Disposition to form-data and name param', function(done){
-// var req = request
-// .post('http://localhost:3005/echo');
-
-// req
-// .part()
-// .name('user[name]')
-// .write('Tobi');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// res.body['user[name]'].should.equal('Tobi');
-// done();
-// });
-// })
-// })
-
-// describe('#attachment(name, path)', function(){
-// it('should set Content-Disposition and Content-Type', function(done){
-// var req = request
-// .post('http://localhost:3005/echo')
-// .type('multipart/form-data');
-
-// req
-// .part()
-// .attachment('file', 'path/to/my.txt')
-// .write('Tobi');
-
-// req.end(function(err, res){
-// if (err) return done(err);
-// var file = res.files.file;
-// file.name.should.equal('my.txt');
-// file.type.should.equal('text/plain');
-// done();
-// });
-// })
-// })
-// })
+ .post(`${base}/echo`)
+ .attach('document', 'test/node/fixtures/user.html', 'doc.html')
+ .then((res) => {
+ const html = res.files.document;
+ html.originalFilename.should.equal('doc.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('name ');
+ }));
+ it('should fire progress event', (done) => {
+ let loaded = 0;
+ let total = 0;
+ let uploadEventWasFired = false;
+ request
+ .post(`${base}/echo`)
+ .attach('document', 'test/node/fixtures/user.html')
+ .on('progress', (event) => {
+ total = event.total;
+ loaded = event.loaded;
+ if (event.direction === 'upload') {
+ uploadEventWasFired = true;
+ }
+ })
+ .end((error, res) => {
+ if (error) return done(error);
+ const html = res.files.document;
+ html.originalFilename.should.equal('user.html');
+ html.mimetype.should.equal('text/html');
+ read(html.filepath).should.equal('name ');
+ total.should.equal(223);
+ loaded.should.equal(223);
+ uploadEventWasFired.should.equal(true);
+ done();
+ });
+ });
+ it('filesystem errors should be caught', (done) => {
+ request
+ .post(`${base}/echo`)
+ .attach('filedata', 'test/node/fixtures/non-existent-file.ext')
+ .end((error, res) => {
+ assert.ok(Boolean(error), 'Request should have failed.');
+ error.code.should.equal('ENOENT');
+ if (IS_WINDOWS) {
+ error.path
+ .toLowerCase()
+ .should.equal(
+ getFullPath(
+ 'test/node/fixtures/non-existent-file.ext'
+ ).toLowerCase()
+ );
+ } else {
+ error.path.should.equal(
+ getFullPath('test/node/fixtures/non-existent-file.ext')
+ );
+ }
+
+ done();
+ });
+ });
+ });
+
+ describe('#field(name, val)', () => {
+ it('should set a multipart field value', (done) => {
+ request
+ .post(`${base}/echo`)
+ .field('first-name', 'foo')
+ .field('last-name', 'bar')
+ .end((error, res) => {
+ if (error) done(error);
+ res.should.be.ok();
+ res.body['first-name'].should.equal('foo');
+ res.body['last-name'].should.equal('bar');
+ done();
+ });
+ });
+ });
+
+ describe('#field(object)', () => {
+ it('should set multiple multipart fields', (done) => {
+ request
+ .post(`${base}/echo`)
+ .field({ 'first-name': 'foo', 'last-name': 'bar' })
+ .end((error, res) => {
+ if (error) done(error);
+ res.should.be.ok();
+ res.body['first-name'].should.equal('foo');
+ res.body['last-name'].should.equal('bar');
+ done();
+ });
+ });
+ });
+});
diff --git a/test/node/network-error.js b/test/node/network-error.js
index be11a2e29..3828590ea 100644
--- a/test/node/network-error.js
+++ b/test/node/network-error.js
@@ -1,35 +1,32 @@
-
-var request = require('../..')
- , express = require('express')
- , assert = require('assert')
- , net = require('net');
+'use strict';
+const assert = require('assert');
+const net = require('net');
+const request = require('../support/client');
+const express = require('../support/express');
function getFreePort(fn) {
- var server = net.createServer();
- server.listen(0, function(){
- var port = server.address().port;
- server.close(function(){
+ const server = net.createServer();
+ server.listen(0, () => {
+ const { port } = server.address();
+ server.close(() => {
fn(port);
});
});
-};
+}
-describe('with network error', function(){
- before(function(done){
- var self = this;
+describe('with network error', () => {
+ before(function (done) {
// connecting to a free port
// will trigger a connection refused
- getFreePort(function(port){
- self.port = port;
+ getFreePort((port) => {
+ this.port = port;
done();
});
});
- it('should error', function(done) {
- request
- .get('http://localhost:' + this.port + '/')
- .end(function(err, res){
- assert(err, 'expected an error');
+ it('should error', function (done) {
+ request.get(`http://localhost:${this.port}/`).end((error, res) => {
+ assert(error, 'expected an error');
done();
});
});
diff --git a/test/node/not-modified.js b/test/node/not-modified.js
index 3029ac0d2..67e8ab64b 100644
--- a/test/node/not-modified.js
+++ b/test/node/not-modified.js
@@ -1,42 +1,36 @@
+'use strict';
+const request = require('../support/client');
+const getSetup = require('../support/setup');
-var request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express();
+describe('request', () => {
+ let setup;
+ let base;
-app.get('/', function(req, res){
- if (req.header('if-modified-since')) {
- res.status(304).end();
- } else {
- res.send('' + Date.now());
- }
-});
-
-app.listen(3008);
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
-describe('request', function(){
- describe('not modified', function(){
- var ts;
- it('should start with 200', function(done){
- request
- .get('http://localhost:3008/')
- .end(function(err, res){
- res.should.have.status(200)
+ describe('not modified', () => {
+ let ts;
+ it('should start with 200', (done) => {
+ request.get(`${base}/if-mod`).end((error, res) => {
+ res.should.have.status(200);
res.text.should.match(/^\d+$/);
- ts = +res.text;
+ ts = Number(res.text);
done();
});
- })
+ });
- it('should then be 304', function(done){
+ it('should then be 304', (done) => {
request
- .get('http://localhost:3008/')
- .set('If-Modified-Since', new Date(ts).toUTCString())
- .end(function(err, res){
- res.should.have.status(304)
- // res.text.should.be.empty
- done();
- });
- })
- })
-})
+ .get(`${base}/if-mod`)
+ .set('If-Modified-Since', new Date(ts).toUTCString())
+ .end((error, res) => {
+ res.should.have.status(304);
+ // res.text.should.be.empty
+ done();
+ });
+ });
+ });
+});
diff --git a/test/node/parsers.js b/test/node/parsers.js
index 2788be394..b09d21edf 100644
--- a/test/node/parsers.js
+++ b/test/node/parsers.js
@@ -1,74 +1,88 @@
+'use strict';
+const assert = require('assert');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
-var request = require('../..')
- , express = require('express')
- , assert = require('better-assert')
- , fs = require('fs')
- , app = express();
-
-app.get('/manny', function(req, res){
- res.status(200).json({name:"manny"});
-});
+const doesntWorkInHttp2 = !process.env.HTTP2_TEST;
+describe('req.parse(fn)', () => {
+ let setup;
+ let base;
-var img = fs.readFileSync(__dirname + '/fixtures/test.png');
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
-app.get('/image', function(req, res){
- res.writeHead(200, {'Content-Type': 'image/png' });
- res.end(img, 'binary');
-});
+ it('should take precedence over default parsers', (done) => {
+ request
+ .get(`${base}/manny`)
+ .parse(request.parse['application/json'])
+ .end((error, res) => {
+ assert(res.ok);
+ assert.equal('{"name":"manny"}', res.text);
+ assert.equal('manny', res.body.name);
+ done();
+ });
+ });
-app.listen(3033);
+ it('should be the only parser', () =>
+ request
+ .get(`${base}/image`)
+ .buffer(false)
+ .parse((res, fn) => {
+ res.on('data', () => {});
+ })
+ .then((res) => {
+ assert(res.ok);
+ assert.strictEqual(res.text, undefined);
+ res.body.should.eql({});
+ }));
-describe('req.parse(fn)', function(){
- it('should take precedence over default parsers', function(done){
+ it('should emit error if parser throws', (done) => {
request
- .get('http://localhost:3033/manny')
- .parse(request.parse['application/json'])
- .end(function(err, res){
- assert(res.ok);
- assert('{"name":"manny"}' == res.text);
- assert('manny' == res.body.name);
- done();
- });
- })
+ .get(`${base}/manny`)
+ .parse(() => {
+ throw new Error('I am broken');
+ })
+ .on('error', (error) => {
+ error.message.should.equal('I am broken');
+ done();
+ })
+ .end();
+ });
- it('should be the only parser', function(done){
+ it('should emit error if parser returns an error', (done) => {
request
- .get('http://localhost:3033/image')
- .parse(function(res, fn) {
- res.on('data', function() {});
- })
- .end(function(err, res){
- assert(res.ok);
- assert(res.text === undefined);
- res.body.should.eql({});
- done();
+ .get(`${base}/manny`)
+ .parse((res, fn) => {
+ fn(new Error('I am broken'));
+ })
+ .on('error', (error) => {
+ error.message.should.equal('I am broken');
+ done();
+ })
+ .end();
+ });
+
+ if (doesntWorkInHttp2)
+ it('should not emit error on chunked json', (done) => {
+ request.get(`${base}/chunked-json`).end((error) => {
+ assert.ifError(error);
+ done();
+ });
});
- })
- it('should emit error if parser throws', function(done){
- request
- .get('http://localhost:3033/manny')
- .parse(function() {
- throw new Error('I am broken');
- })
- .on('error', function(err) {
- err.message.should.equal('I am broken');
- done();
- })
- .end();
- })
+ if (doesntWorkInHttp2)
+ it('should not emit error on aborted chunked json', (done) => {
+ const request_ = request.get(`${base}/chunked-json`);
+ request_.end((error) => {
+ assert.ifError(error);
+ done();
+ });
- it('should emit error if parser returns an error', function(done){
- request
- .get('http://localhost:3033/manny')
- .parse(function(res, fn) {
- fn(new Error('I am broken'));
- })
- .on('error', function(err) {
- err.message.should.equal('I am broken');
- done();
- })
- .end()
- })
-})
+ setTimeout(() => {
+ request_.abort();
+ }, 150);
+ });
+});
diff --git a/test/node/pipe-callback.js b/test/node/pipe-callback.js
new file mode 100644
index 000000000..bcb7d44e4
--- /dev/null
+++ b/test/node/pipe-callback.js
@@ -0,0 +1,44 @@
+const assert = require('node:assert');
+const { Readable } = require('node:stream');
+const getSetup = require('../support/setup');
+const request = require('../support/client');
+
+describe('[node] pipe callback handling', () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ it('should work with pipe without callback', (done) => {
+ const body = Readable.from(JSON.stringify({ name: 'john' }));
+ const request_ = request
+ .post(`${base}/echo`)
+ .set('Content-Type', 'application/json')
+ .on('response', (res) => {
+ assert(res);
+ assert.equal(res.status, 200);
+ assert.equal(res.text, '{"name":"john"}');
+ done();
+ });
+
+ body.pipe(request_);
+ });
+
+ it('should work with pipe and callback', (done) => {
+ const body = Readable.from(JSON.stringify({ name: 'jane' }));
+ const request_ = request
+ .post(`${base}/echo`)
+ .set('Content-Type', 'application/json')
+ .on('response', (res) => {
+ assert(res);
+ assert.equal(res.status, 200);
+ assert.equal(res.text, '{"name":"jane"}');
+ done();
+ });
+
+ body.pipe(request_);
+ });
+});
diff --git a/test/node/pipe-redirect.js b/test/node/pipe-redirect.js
index 11901c844..3b7525f74 100644
--- a/test/node/pipe-redirect.js
+++ b/test/node/pipe-redirect.js
@@ -1,53 +1,39 @@
-var request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express()
- , fs = require('fs')
- , should = require('should');
+'use strict';
+const fs = require('fs');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
-app.get('/', function (req, res) {
- res.redirect('/movies');
-});
-
-app.get('/movies', function (req, res) {
- res.redirect('/movies/all');
-});
-
-app.get('/movies/all', function (req, res) {
- res.redirect('/movies/all/0');
-});
+describe('pipe on redirect', () => {
+ let setup;
+ let base;
+ const destinationPath = 'test/node/fixtures/pipe.txt';
-app.get('/movies/all/0', function (req, res) {
- res.send('first movie page');
-});
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
-app.listen(3012);
+ after((done) => {
+ // Remove tmp file
+ fs.unlink(destinationPath, done);
+ });
-describe('pipe on redirect', function () {
- afterEach(removeTmpfile);
- it('should follow Location', function (done) {
- var stream = fs.createWriteStream('test/node/fixtures/pipe.txt');
- var redirects = [];
- var req = request
- .get('http://localhost:3012/')
- .on('redirect', function (res) {
+ it('should follow Location', (done) => {
+ const stream = fs.createWriteStream(destinationPath);
+ const redirects = [];
+ const request_ = request
+ .get(base)
+ .on('redirect', (res) => {
redirects.push(res.headers.location);
})
- .on('end', function () {
- var arr = [];
- arr.push('/movies');
- arr.push('/movies/all');
- arr.push('/movies/all/0');
- redirects.should.eql(arr);
- fs.readFileSync('test/node/fixtures/pipe.txt', 'utf8').should.eql('first movie page');
- done();
+ .connect({
+ inapplicable: 'should be ignored'
});
- req.pipe(stream);
+ stream.on('finish', () => {
+ redirects.should.eql(['/movies', '/movies/all', '/movies/all/0']);
+ fs.readFileSync(destinationPath, 'utf8').should.eql('first movie page');
+ done();
+ });
+ request_.pipe(stream);
});
});
-
-function removeTmpfile(done) {
- fs.unlink('test/node/fixtures/pipe.txt', function (err) {
- done();
- });
-}
diff --git a/test/node/pipe.js b/test/node/pipe.js
index 551c969df..c6f11222c 100644
--- a/test/node/pipe.js
+++ b/test/node/pipe.js
@@ -1,51 +1,204 @@
+'use strict';
+const request = require('../support/client');
+const express = require('../support/express');
-var request = require('../../')
- , express = require('express')
- , app = express()
- , fs = require('fs')
- , bodyParser = require('body-parser');
+const app = express();
+const fs = require('fs');
+const bodyParser = require('body-parser');
+let http = require('http');
+const zlib = require('zlib');
+const { pipeline } = require('stream');
+
+if (process.env.HTTP2_TEST) {
+ http = require('http2');
+}
app.use(bodyParser.json());
-app.post('/', function(req, res){
- res.send(req.body);
+app.get('/', (request_, res) => {
+ fs.createReadStream('test/node/fixtures/user.json').pipe(res);
+});
+
+app.get('/gzip', (request_, res) => {
+ res.writeHead(200, {
+ 'Content-Encoding': 'gzip'
+ });
+ fs.createReadStream('test/node/fixtures/user.json').pipe(new zlib.createGzip()).pipe(res);
+});
+
+app.get('/redirect', (request_, res) => {
+ res.set('Location', '/').sendStatus(302);
+});
+
+app.get('/badRedirectNoLocation', (request_, res) => {
+ res.set('Location', '').sendStatus(302);
+});
+
+app.post('/', (request_, res) => {
+ if (process.env.HTTP2_TEST) {
+ // body-parser does not support http2 yet.
+ // This section can be remove after body-parser supporting http2.
+ res.set('content-type', 'application/json');
+ request_.pipe(res);
+ } else {
+ res.send(request_.body);
+ }
+});
+
+let base = 'http://localhost';
+let server;
+before(function listen(done) {
+ server = http.createServer(app);
+ server.listen(0, function listening() {
+ base += `:${server.address().port}`;
+ done();
+ });
});
-app.listen(3020);
+describe('request pipe', () => {
+ const destinationPath = 'test/node/fixtures/tmp.json';
-describe('request pipe', function(){
- afterEach(removeTmpfile);
+ after(function removeTmpfile(done) {
+ fs.unlink(destinationPath, done);
+ });
- it('should act as a writable stream', function(done){
- var req = request.post('http://localhost:3020');
- var stream = fs.createReadStream('test/node/fixtures/user.json');
+ it('should act as a writable stream', (done) => {
+ const request_ = request.post(base);
+ const stream = fs.createReadStream('test/node/fixtures/user.json');
- req.type('json');
+ request_.type('json');
- req.on('response', function(res){
+ request_.on('response', (res) => {
res.body.should.eql({ name: 'tobi' });
done();
});
- stream.pipe(req);
- })
+ stream.pipe(request_);
+ });
+
+ it('end() stops piping', (done) => {
+ const stream = fs.createWriteStream(destinationPath);
+ request.get(base).end((error, res) => {
+ try {
+ res.pipe(stream);
+ return done(new Error('Did not prevent nonsense pipe'));
+ } catch {
+ /* expected error */
+ }
+
+ done();
+ });
+ });
- it('should act as a readable stream', function(done){
- var stream = fs.createWriteStream('test/node/fixtures/tmp.json');
+ it('should act as a readable stream', (done) => {
+ const stream = fs.createWriteStream(destinationPath);
- var req = request.get('http://localhost:3025');
- req.type('json');
+ let responseCalled = false;
+ const request_ = request.get(base);
+ request_.type('json');
- req.on('end', function(){
- JSON.parse(fs.readFileSync('test/node/fixtures/tmp.json', 'utf8')).should.eql({ name: 'tobi' });
+ request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+ });
+ stream.on('finish', () => {
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ responseCalled.should.be.true();
done();
});
- req.pipe(stream);
- })
-});
+ request_.pipe(stream);
+ });
-function removeTmpfile(done){
- fs.unlink('test/node/fixtures/tmp.json', function(err){
- done();
+ it('should act as a readable stream with unzip', (done) => {
+ const stream = fs.createWriteStream(destinationPath);
+
+ let responseCalled = false;
+ const request_ = request.get(base + '/gzip');
+ request_.type('json');
+
+ request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+ });
+ stream.on('finish', () => {
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ responseCalled.should.be.true();
+ done();
+ });
+ request_.pipe(stream);
});
-}
+
+ it('should act as a readable stream with unzip and node.stream.pipeline', (done) => {
+ const stream = fs.createWriteStream(destinationPath);
+
+ let responseCalled = false;
+ const request_ = request.get(base + '/gzip');
+ request_.type('json');
+
+ request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+ });
+ // pipeline automatically ends streams by default. Since unzipping introduces a transform stream that is
+ // not monitored by pipeline, we need to make sure request_ does not emit 'end' until the unzip step
+ // has finished writing data. Otherwise, we'll either end up with truncated data or a 'write after end' error.
+ pipeline(request_, stream, function (err) {
+ (!!err).should.be.false();
+ responseCalled.should.be.true();
+
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ done();
+ });
+ });
+
+ it('should follow redirects', (done) => {
+ const stream = fs.createWriteStream(destinationPath);
+
+ let responseCalled = false;
+ const request_ = request.get(base + '/redirect');
+ request_.type('json');
+
+ request_.on('response', (res) => {
+ res.status.should.eql(200);
+ responseCalled = true;
+ });
+ stream.on('finish', () => {
+ JSON.parse(fs.readFileSync(destinationPath)).should.eql({
+ name: 'tobi'
+ });
+ responseCalled.should.be.true();
+ done();
+ });
+ request_.pipe(stream);
+ });
+
+ it('should not throw on bad redirects', (done) => {
+ const stream = fs.createWriteStream(destinationPath);
+
+ let responseCalled = false;
+ let errorCalled = false;
+ const request_ = request.get(base + '/badRedirectNoLocation');
+ request_.type('json');
+
+ request_.on('response', (res) => {
+ responseCalled = true;
+ });
+ request_.on('error', (error) => {
+ error.message.should.eql('No location header for redirect');
+ errorCalled = true;
+ stream.end();
+ });
+ stream.on('finish', () => {
+ responseCalled.should.be.false();
+ errorCalled.should.be.true();
+ done();
+ });
+ request_.pipe(stream);
+ });
+});
diff --git a/test/node/query.js b/test/node/query.js
index e752ff9dd..dfa0efd75 100644
--- a/test/node/query.js
+++ b/test/node/query.js
@@ -1,143 +1,227 @@
+'use strict';
+let http = require('http');
+const assert = require('assert');
+const fs = require('fs');
+const request = require('../support/client');
+const express = require('../support/express');
-var request = require('../..')
- , express = require('express')
- , assert = require('better-assert')
- , app = express();
+const app = express();
-app.get('/', function(req, res){
- res.status(200).send(req.query);
+if (process.env.HTTP2_TEST) {
+ http = require('http2');
+}
+
+app.get('/raw-query', (request_, res) => {
+ res.status(200).send(request_.url.slice(request_.url.indexOf('?') + 1));
});
-app.delete('/', function(req, res){
- res.status(200).send(req.query);
+app.get('/', (request_, res) => {
+ res.status(200).send(request_.query);
});
-before(function(done) {
- app.listen(3006, done);
+app.delete('/url', (request_, res) => {
+ res.status(200).send(request_.url);
});
-describe('req.query(String)', function(){
- it('should supply uri malformed error to the callback', function(done){
- request
- .get('http://localhost:3006')
- .query('name=toby')
- .query('a=\uD800')
- .query({ b: '\uD800' })
- .end(function(err, res){
- assert(err instanceof Error);
- assert('URIError' == err.name);
- done();
- });
- })
+app.delete('/', (request_, res) => {
+ res.status(200).send(request_.query);
+});
+
+app.put('/', (request_, res) => {
+ res.status(200).send(request_.query);
+});
+
+let base = 'http://localhost';
+let server;
+before(function listen(done) {
+ server = http.createServer(app);
+ server = server.listen(0, function listening() {
+ base += `:${server.address().port}`;
+ done();
+ });
+});
+
+describe('req.query(String)', () => {
+ // This is no longer true as of qs v3.0.0 (https://github.com/ljharb/qs/commit/0c6f2a6318c94f6226d3cf7fe36094e9685042b6)
+ // it('should supply uri malformed error to the callback')
- it('should support passing in a string', function(done){
+ it('should support passing in a string', (done) => {
request
- .del('http://localhost:3006')
- .query('name=t%F6bi')
- .end(function(err, res){
- res.body.should.eql({ name: 't%F6bi' });
- done();
- });
- })
+ .del(base)
+ .query('name=t%F6bi')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi' });
+ done();
+ });
+ });
- it('should work with url query-string and string for query', function(done){
+ it('should work with url query-string and string for query', (done) => {
request
- .del('http://localhost:3006/?name=tobi')
- .query('age=2%20')
- .end(function(err, res){
- res.body.should.eql({ name: 'tobi', age: '2 ' });
- done();
- });
- })
+ .del(`${base}/?name=tobi`)
+ .query('age=2%20')
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', age: '2 ' });
+ done();
+ });
+ });
- it('should support compound elements in a string', function(done){
+ it('should support compound elements in a string', (done) => {
request
- .del('http://localhost:3006/')
+ .del(base)
.query('name=t%F6bi&age=2')
- .end(function(err, res){
+ .end((error, res) => {
res.body.should.eql({ name: 't%F6bi', age: '2' });
done();
});
- })
+ });
- it('should work when called multiple times with a string', function(done){
+ it('should work when called multiple times with a string', (done) => {
request
- .del('http://localhost:3006/')
- .query('name=t%F6bi')
- .query('age=2%F6')
- .end(function(err, res){
- res.body.should.eql({ name: 't%F6bi', age: '2%F6' });
- done();
- });
- })
+ .del(base)
+ .query('name=t%F6bi')
+ .query('age=2%F6')
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2%F6' });
+ done();
+ });
+ });
- it('should work with normal `query` object and query string', function(done){
+ it('should work with normal `query` object and query string', (done) => {
request
- .del('http://localhost:3006/')
- .query('name=t%F6bi')
- .query({ age: '2' })
- .end(function(err, res){
- res.body.should.eql({ name: 't%F6bi', age: '2' });
- done();
- });
- })
-})
+ .del(base)
+ .query('name=t%F6bi')
+ .query({ age: '2' })
+ .end((error, res) => {
+ res.body.should.eql({ name: 't%F6bi', age: '2' });
+ done();
+ });
+ });
+
+ it('should not encode raw backticks, but leave encoded ones as is', () => {
+ return Promise.all([
+ request
+ .get(`${base}/raw-query`)
+ .query('name=`t%60bi`&age`=2')
+ .then((res) => {
+ res.text.should.eql('name=`t%60bi`&age`=2');
+ }),
+
+ request.get(base + '/raw-query?`age%60`=2%60`').then((res) => {
+ res.text.should.eql('`age%60`=2%60`');
+ }),
+
+ request
+ .get(`${base}/raw-query`)
+ .query('name=`t%60bi`')
+ .query('age`=2')
+ .then((res) => {
+ res.text.should.eql('name=`t%60bi`&age`=2');
+ })
+ ]);
+ });
+});
-describe('req.query(Object)', function(){
- it('should construct the query-string', function(done){
+describe('req.query(Object)', () => {
+ it('should construct the query-string', (done) => {
request
- .del('http://localhost:3006/')
- .query({ name: 'tobi' })
- .query({ order: 'asc' })
- .query({ limit: ['1', '2'] })
- .end(function(err, res){
- res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
- done();
- });
- })
+ .del(base)
+ .query({ name: 'tobi' })
+ .query({ order: 'asc' })
+ .query({ limit: ['1', '2'] })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
+ done();
+ });
+ });
+
+ // See commit message for the reasoning here.
+ it('should encode raw backticks', (done) => {
+ request
+ .get(`${base}/raw-query`)
+ .query({ name: '`tobi`' })
+ .query({ 'orde%60r': null })
+ .query({ '`limit`': ['%602`'] })
+ .end((error, res) => {
+ res.text.should.eql('name=%60tobi%60&orde%2560r&%60limit%60=%25602%60');
+ done();
+ });
+ });
- it('should not error on dates', function(done){
- var date = new Date(0);
+ it('should not error on dates', (done) => {
+ const date = new Date(0);
request
- .del('http://localhost:3006/')
- .query({ at: date })
- .end(function(err, res){
- assert(date.toISOString() == res.body.at);
- done();
- });
- })
+ .del(base)
+ .query({ at: date })
+ .end((error, res) => {
+ assert.equal(date.toISOString(), res.body.at);
+ done();
+ });
+ });
- it('should work after setting header fields', function(done){
+ it('should work after setting header fields', (done) => {
request
- .del('http://localhost:3006/')
- .set('Foo', 'bar')
- .set('Bar', 'baz')
- .query({ name: 'tobi' })
- .query({ order: 'asc' })
- .query({ limit: ['1', '2'] })
- .end(function(err, res){
- res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
- done();
- });
- })
+ .del(base)
+ .set('Foo', 'bar')
+ .set('Bar', 'baz')
+ .query({ name: 'tobi' })
+ .query({ order: 'asc' })
+ .query({ limit: ['1', '2'] })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
+ done();
+ });
+ });
- it('should append to the original query-string', function(done){
+ it('should append to the original query-string', (done) => {
request
- .del('http://localhost:3006/?name=tobi')
- .query({ order: 'asc' })
- .end(function(err, res) {
- res.body.should.eql({ name: 'tobi', order: 'asc' });
+ .del(`${base}/?name=tobi`)
+ .query({ order: 'asc' })
+ .end((error, res) => {
+ res.body.should.eql({ name: 'tobi', order: 'asc' });
+ done();
+ });
+ });
+
+ it('should retain the original query-string', (done) => {
+ request.del(`${base}/?name=tobi`).end((error, res) => {
+ res.body.should.eql({ name: 'tobi' });
done();
});
});
- it('should retain the original query-string', function(done){
+ it('should keep only keys with null querystring values', (done) => {
request
- .del('http://localhost:3006/?name=tobi')
- .end(function(err, res) {
+ .del(`${base}/url`)
+ .query({ nil: null })
+ .end((error, res) => {
+ res.text.should.equal('/url?nil');
+ done();
+ });
+ });
+
+ it('query-string should be sent on pipe', (done) => {
+ const request_ = request.put(`${base}/?name=tobi`);
+ const stream = fs.createReadStream('test/node/fixtures/user.json');
+
+ request_.on('response', (res) => {
res.body.should.eql({ name: 'tobi' });
done();
});
+ request_.on('error', (err) => {
+ done(err);
+ });
+
+ stream.on('error', (err) => {
+ done(err);
+ });
+
+ // wait until stream is valid before piping
+ stream.on('open', () => {
+ // wait until request_ is ready before piping
+ setTimeout(() => {
+ stream.pipe(request_);
+ }, 10);
+ });
});
-})
+});
diff --git a/test/node/redirects-other-host.js b/test/node/redirects-other-host.js
index d42571ed5..067866e17 100644
--- a/test/node/redirects-other-host.js
+++ b/test/node/redirects-other-host.js
@@ -1,168 +1,198 @@
+'use strict';
+const assert = require('assert');
+const request = require('../support/client');
+const express = require('../support/express');
-var request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express()
- , app2 = express()
- , should = require('should');
+const app = express();
+const app2 = express();
+const should = require('should');
+let http = require('http');
-app.all('/test-301', function(req, res){
- res.redirect(301, 'http://localhost:3211/');
+if (process.env.HTTP2_TEST) {
+ http = require('http2');
+}
+
+let base = 'http://localhost';
+let server;
+before(function listen(done) {
+ server = http.createServer(app);
+ server = server.listen(0, function listening() {
+ base += `:${server.address().port}`;
+ done();
+ });
});
-app.all('/test-302', function(req, res){
- res.redirect(302, 'http://localhost:3211/');
+
+let base2 = 'http://localhost';
+let server2;
+before(function listen(done) {
+ server2 = http.createServer(app2);
+ server2 = server2.listen(0, function listening() {
+ base2 += `:${server2.address().port}`;
+ done();
+ });
});
-app.all('/test-303', function(req, res){
- res.redirect(303, 'http://localhost:3211/');
+
+app.all('/test-301', (request_, res) => {
+ res.redirect(301, `${base2}/`);
});
-app.all('/test-307', function(req, res){
- res.redirect(307, 'http://localhost:3211/');
+app.all('/test-302', (request_, res) => {
+ res.redirect(302, `${base2}/`);
});
-app.all('/test-308', function(req, res){
- res.redirect(308, 'http://localhost:3211/');
+app.all('/test-303', (request_, res) => {
+ res.redirect(303, `${base2}/`);
});
-
-app.listen(3210);
-
-
-app2.all('/', function(req, res) {
- res.send(req.method);
+app.all('/test-307', (request_, res) => {
+ res.redirect(307, `${base2}/`);
+});
+app.all('/test-308', (request_, res) => {
+ res.redirect(308, `${base2}/`);
});
-app2.listen(3211);
+app2.all('/', (request_, res) => {
+ res.send(request_.method);
+});
-describe('request.get', function(){
- describe('on 301 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .get('http://localhost:3210/test-301')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+describe('request.get', () => {
+ describe('on 301 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.get(`${base}/test-301`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
- describe('on 302 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .get('http://localhost:3210/test-302')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+ describe('on 302 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.get(`${base}/test-302`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
- describe('on 303 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .get('http://localhost:3210/test-303')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+ describe('on 303 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.get(`${base}/test-303`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
- describe('on 307 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .get('http://localhost:3210/test-307')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+ describe('on 307 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.get(`${base}/test-307`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
- describe('on 308 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .get('http://localhost:3210/test-308')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+ describe('on 308 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.get(`${base}/test-308`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
});
-describe('request.post', function(){
- describe('on 301 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .post('http://localhost:3210/test-301')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+describe('request.post', () => {
+ describe('on 301 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.post(`${base}/test-301`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
- describe('on 302 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .post('http://localhost:3210/test-302')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+ describe('on 302 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.post(`${base}/test-302`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
- describe('on 303 redirect', function(){
- it('should follow Location with a GET request', function(done){
- var req = request
- .post('http://localhost:3210/test-303')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('GET');
- done();
- });
- })
+ describe('on 303 redirect', () => {
+ it('should follow Location with a GET request', (done) => {
+ const request_ = request.post(`${base}/test-303`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('GET');
+ done();
+ });
+ });
});
- describe('on 307 redirect', function(){
- it('should follow Location with a POST request', function(done){
- var req = request
- .post('http://localhost:3210/test-307')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('POST');
- done();
- });
- })
+ describe('on 307 redirect', () => {
+ it('should follow Location with a POST request', (done) => {
+ const request_ = request.post(`${base}/test-307`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('POST');
+ done();
+ });
+ });
});
- describe('on 308 redirect', function(){
- it('should follow Location with a POST request', function(done){
- var req = request
- .post('http://localhost:3210/test-308')
- .redirects(1)
- .end(function(err, res){
- req.req._headers.host.should.eql('localhost:3211');
- res.status.should.eql(200);
- res.text.should.eql('POST');
- done();
- });
- })
+ describe('on 308 redirect', () => {
+ it('should follow Location with a POST request', (done) => {
+ const request_ = request.post(`${base}/test-308`).redirects(1);
+ request_.end((error, res) => {
+ const headers = request_.req.getHeaders
+ ? request_.req.getHeaders()
+ : request_.req._headers;
+ headers.host.should.eql(`localhost:${server2.address().port}`);
+ res.status.should.eql(200);
+ res.text.should.eql('POST');
+ done();
+ });
+ });
});
-});
\ No newline at end of file
+});
diff --git a/test/node/redirects.js b/test/node/redirects.js
index 0652db061..7757817fc 100644
--- a/test/node/redirects.js
+++ b/test/node/redirects.js
@@ -1,306 +1,359 @@
+'use strict';
-var EventEmitter = require('events').EventEmitter
- , request = require('../../')
- , express = require('express')
- , assert = require('assert')
- , app = express()
- , should = require('should');
+const assert = require('assert');
+const getSetup = require('../support/setup');
+const request = require('../support/client');
-app.get('/', function(req, res){
- res.set('QUERY', JSON.stringify(req.query));
- res.redirect('/movies');
-});
-
-app.get('/movies', function(req, res){
- res.set('QUERY', JSON.stringify(req.query));
- res.redirect('/movies/all');
-});
-
-app.get('/movies/all', function(req, res){
- res.set('QUERY', JSON.stringify(req.query));
- res.redirect('/movies/all/0');
-});
-
-app.get('/movies/all/0', function(req, res){
- res.set('QUERY', JSON.stringify(req.query));
- res.send('first movie page');
-});
-
-app.get('/movies/random', function(req, res){
- res.redirect('/movie/4');
-});
-
-app.get('/movie/4', function(req, res){
- setTimeout(function(){
- res.send('not-so-random movie');
- }, 1000);
-});
-
-app.post('/movie', function(req, res){
- res.redirect('/movies/all/0');
-});
-
-app.put('/redirect-303', function(req, res){
- res.redirect(303, '/reply-method');
-});
-
-app.put('/redirect-307', function(req, res){
- res.redirect(307, '/reply-method');
-});
-
-app.put('/redirect-308', function(req, res){
- res.redirect(308, '/reply-method');
-});
-
-app.all('/reply-method', function(req, res){
- res.send('method=' + req.method.toLowerCase());
-});
-
-app.get('/tobi', function(req, res){
- res.send('tobi');
-});
-
-app.get('/relative', function(req, res){
- res.redirect('tobi');
-});
-
-app.get('/relative/sub', function(req, res){
- res.redirect('../tobi');
-});
-
-app.get('/header', function(req, res){
- res.redirect('/header/2');
-});
-
-app.post('/header', function(req, res){
- res.redirect('/header/2');
-});
-
-app.get('/header/2', function(req, res){
- res.send(req.headers);
-});
+describe('request', () => {
+ let setup;
+ let base;
-app.get('/bad-redirect', function(req, res){
- res.status(307).end();
-});
-
-app.listen(3003);
-
-describe('request', function(){
- describe('on redirect', function(){
- it('should follow Location', function(done){
- var redirects = [];
-
- request
- .get('http://localhost:3003/')
- .on('redirect', function(res){
- redirects.push(res.headers.location);
- })
- .end(function(err, res){
- var arr = [];
- arr.push('/movies');
- arr.push('/movies/all');
- arr.push('/movies/all/0');
- redirects.should.eql(arr);
- res.text.should.equal('first movie page');
- done();
- });
- })
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
- it('should retain header fields', function(done){
+ describe('on redirect', () => {
+ it('should merge cookies if agent is used', (done) => {
request
- .get('http://localhost:3003/header')
- .set('X-Foo', 'bar')
- .end(function(err, res){
- res.body.should.have.property('x-foo', 'bar');
- done();
- });
- })
+ .agent()
+ .get(`${base}/cookie-redirect`)
+ .set('Cookie', 'orig=1; replaced=not')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), 'orig=1/.test');
+ assert(/replaced=yes/.test(res.text), 'replaced=yes/.test');
+ assert(/from-redir=1/.test(res.text), 'from-redir=1');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- it('should remove Content-* fields', function(done){
+ it('should not merge cookies if agent is not used', (done) => {
request
- .post('http://localhost:3003/header')
- .type('txt')
- .set('X-Foo', 'bar')
- .set('X-Bar', 'baz')
- .send('hey')
- .end(function(err, res){
- res.body.should.have.property('x-foo', 'bar');
- res.body.should.have.property('x-bar', 'baz');
- res.body.should.not.have.property('content-type');
- res.body.should.not.have.property('content-length');
- res.body.should.not.have.property('transfer-encoding');
- done();
+ .get(`${base}/cookie-redirect`)
+ .set('Cookie', 'orig=1; replaced=not')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), '/orig=1');
+ assert(/replaced=not/.test(res.text), '/replaced=not');
+ assert(!/replaced=yes/.test(res.text), '!/replaced=yes');
+ assert(!/from-redir/.test(res.text), '!/from-redir');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should have previously set cookie for subsquent requests when agent is used', (done) => {
+ const agent = request.agent();
+ agent.get(`${base}/set-cookie`).end((error) => {
+ assert.ifError(error);
+ agent
+ .get(`${base}/show-cookies`)
+ .set({ Cookie: 'orig=1' })
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/orig=1/.test(res.text), 'orig=1/.test');
+ assert(/persist=123/.test(res.text), 'persist=123');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
});
- })
-
- it('should preserve timeout across redirects', function(done){
- request
- .get('http://localhost:3003/movies/random')
- .timeout(250)
- .end(function(err, res){
- assert(err instanceof Error, 'expected an error');
- err.should.have.property('timeout', 250);
- done();
+ });
+
+ it('should overwrite previously set cookie during a redirect when agent is used', (done) => {
+ const agent = request.agent();
+ agent.get(`${base}/set-cookie`).end((error) => {
+ assert.ifError(error);
+ agent
+ .get(`${base}/cookie-redirect`)
+ .redirects(1)
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(/replaced=yes/.test(res.text), 'replaced=yes');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
});
})
- it('should not resend query parameters', function(done) {
- var redirects = [];
- var query = [];
+ it('should follow Location', (done) => {
+ const redirects = [];
request
- .get('http://localhost:3003/?foo=bar')
- .on('redirect', function(res){
- query.push(res.headers.query);
- redirects.push(res.headers.location);
- })
- .end(function(err, res){
- var arr = [];
- arr.push('/movies');
- arr.push('/movies/all');
- arr.push('/movies/all/0');
- redirects.should.eql(arr);
- res.text.should.equal('first movie page');
+ .get(base)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should follow Location with IP override', () => {
+ const redirects = [];
+ const url = new URL(base);
+ return request
+ .get(`http://redir.example.com:${url.port || '80'}${url.pathname}`)
+ .connect({
+ '*': url.hostname
+ })
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ });
+ });
+
+ it('should follow Location with IP:port override', () => {
+ const redirects = [];
+ const url = new URL(base);
+ return request
+ .get(`http://redir.example.com:9999${url.pathname}`)
+ .connect({
+ '*': { host: url.hostname, port: url.port || 80 }
+ })
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ const array = ['/movies', '/movies/all', '/movies/all/0'];
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+ });
+ });
- query.should.eql(['{"foo":"bar"}', '{}', '{}']);
- res.headers.query.should.eql('{}');
- done();
- });
- })
+ it('should not follow on HEAD by default', () => {
+ const redirects = [];
- it('should handle no location header', function(done){
- request
- .get('http://localhost:3003/bad-redirect')
- .end(function(err, res){
- err.message.should.equal('No location header for redirect');
- done();
- });
- })
+ return request
+ .head(base)
+ .ok(() => true)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql([]);
+ res.status.should.equal(302);
+ });
+ });
- describe('when relative', function(){
- it('should redirect to a sibling path', function(done){
- var redirects = [];
+ it('should follow on HEAD when redirects are set', (done) => {
+ const redirects = [];
- request
- .get('http://localhost:3003/relative')
- .on('redirect', function(res){
+ request
+ .head(base)
+ .redirects(10)
+ .on('redirect', (res) => {
redirects.push(res.headers.location);
})
- .end(function(err, res){
- var arr = [];
- redirects.should.eql(['tobi']);
- res.text.should.equal('tobi');
- done();
+ .end((error, res) => {
+ try {
+ const array = [];
+ array.push('/movies', '/movies/all', '/movies/all/0');
+ redirects.should.eql(array);
+ assert(!res.text);
+ done();
+ } catch (err) {
+ done(err);
+ }
});
- })
+ });
- it('should redirect to a parent path', function(done){
- var redirects = [];
+ it('should remove Content-* fields', (done) => {
+ request
+ .post(`${base}/header`)
+ .type('txt')
+ .set('X-Foo', 'bar')
+ .set('X-Bar', 'baz')
+ .send('hey')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ res.body.should.have.property('x-bar', 'baz');
+ res.body.should.not.have.property('content-type');
+ res.body.should.not.have.property('content-length');
+ res.body.should.not.have.property('transfer-encoding');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- request
- .get('http://localhost:3003/relative/sub')
- .on('redirect', function(res){
+ it('should retain cookies', (done) => {
+ request
+ .get(`${base}/header`)
+ .set('Cookie', 'foo=bar;')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('cookie', 'foo=bar;');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should not resend query parameters', (done) => {
+ const redirects = [];
+ const query = [];
+
+ request
+ .get(`${base}/?foo=bar`)
+ .on('redirect', (res) => {
+ query.push(res.headers.query);
redirects.push(res.headers.location);
})
- .end(function(err, res){
- var arr = [];
- redirects.should.eql(['../tobi']);
- res.text.should.equal('tobi');
- done();
+ .end((error, res) => {
+ try {
+ const array = [];
+ array.push('/movies', '/movies/all', '/movies/all/0');
+ redirects.should.eql(array);
+ res.text.should.equal('first movie page');
+
+ query.should.eql(['{"foo":"bar"}', '{}', '{}']);
+ res.headers.query.should.eql('{}');
+ done();
+ } catch (err) {
+ done(err);
+ }
});
- })
- })
- })
+ });
- describe('req.redirects(n)', function(){
- it('should alter the default number of redirects to follow', function(done){
- var redirects = [];
+ it('should handle no location header', (done) => {
+ request.get(`${base}/bad-redirect`).end((error, res) => {
+ try {
+ error.message.should.equal('No location header for redirect');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
- request
- .get('http://localhost:3003/')
- .redirects(2)
- .on('redirect', function(res){
- redirects.push(res.headers.location);
- })
- .end(function(err, res){
- var arr = [];
- assert(res.redirect, 'res.redirect');
- arr.push('/movies');
- arr.push('/movies/all');
- redirects.should.eql(arr);
- res.text.should.match(/Moved Temporarily/);
- done();
+ describe('when relative', () => {
+ it('should redirect to a sibling path', (done) => {
+ const redirects = [];
+
+ request
+ .get(`${base}/relative`)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ redirects.should.eql(['tobi']);
+ res.text.should.equal('tobi');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
});
- })
- })
- describe('on POST', function(){
- it('should redirect as GET', function(done){
- var redirects = [];
+ it('should redirect to a parent path', (done) => {
+ const redirects = [];
- request
- .post('http://localhost:3003/movie')
- .send({ name: 'Tobi' })
- .redirects(2)
- .on('redirect', function(res){
- redirects.push(res.headers.location);
- })
- .end(function(err, res){
- var arr = [];
- arr.push('/movies/all/0');
- redirects.should.eql(arr);
- res.text.should.equal('first movie page');
- done();
+ request
+ .get(`${base}/relative/sub`)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ redirects.should.eql(['../tobi']);
+ res.text.should.equal('tobi');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
});
- })
- })
+ });
+ });
- describe('on 303', function(){
- it('should redirect with same method', function(done){
- request
- .put('http://localhost:3003/redirect-303')
- .send({msg: "hello"})
- .redirects(1)
- .on('redirect', function(res) {
- res.headers.location.should.equal('/reply-method')
- })
- .end(function(err, res){
- res.text.should.equal('method=get');
- done();
- })
- })
- })
+ describe('req.redirects(n)', () => {
+ it('should alter the default number of redirects to follow', (done) => {
+ const redirects = [];
- describe('on 307', function(){
- it('should redirect with same method', function(done){
request
- .put('http://localhost:3003/redirect-307')
- .send({msg: "hello"})
- .redirects(1)
- .on('redirect', function(res) {
- res.headers.location.should.equal('/reply-method')
- })
- .end(function(err, res){
- res.text.should.equal('method=put');
- done();
- })
- })
- })
+ .get(base)
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .end((error, res) => {
+ try {
+ const array = [];
+ assert(res.redirect, 'res.redirect');
+ array.push('/movies', '/movies/all');
+ redirects.should.eql(array);
+ res.text.should.match(/Moved Temporarily|Found/);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+ });
+
+ describe('on POST', () => {
+ it('should redirect as GET', () => {
+ const redirects = [];
+
+ return request
+ .post(`${base}/movie`)
+ .send({ name: 'Tobi' })
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql(['/movies/all/0']);
+ res.text.should.equal('first movie page');
+ });
+ });
+
+ it('using multipart/form-data should redirect as GET', () => {
+ const redirects = [];
- describe('on 308', function(){
- it('should redirect with same method', function(done){
request
- .put('http://localhost:3003/redirect-308')
- .send({msg: "hello"})
- .redirects(1)
- .on('redirect', function(res) {
- res.headers.location.should.equal('/reply-method')
- })
- .end(function(err, res){
- res.text.should.equal('method=put');
- done();
- })
- })
- })
-})
+ .post(`${base}/movie`)
+ .type('form')
+ .field('name', 'Tobi')
+ .redirects(2)
+ .on('redirect', (res) => {
+ redirects.push(res.headers.location);
+ })
+ .then((res) => {
+ redirects.should.eql(['/movies/all/0']);
+ res.text.should.equal('first movie page');
+ });
+ });
+ });
+});
diff --git a/test/node/response-readable-stream.js b/test/node/response-readable-stream.js
index 002aaf53a..fdd076781 100644
--- a/test/node/response-readable-stream.js
+++ b/test/node/response-readable-stream.js
@@ -1,41 +1,63 @@
+'use strict';
+const request = require('../support/client');
+const express = require('../support/express');
-var request = require('../../')
- , express = require('express')
- , app = express()
- , fs = require('fs');
+const app = express();
+const fs = require('fs');
+let http = require('http');
-app.get('/', function(req, res){
+if (process.env.HTTP2_TEST) {
+ http = require('http2');
+}
+
+app.get('/', (request_, res) => {
fs.createReadStream('test/node/fixtures/user.json').pipe(res);
});
-app.listen(3025);
-
-describe('response', function(){
- it('should act as a readable stream', function(done){
- var req = request
- .get('http://localhost:3025')
- .buffer(false);
+let base = 'http://localhost';
+let server;
+before(function listen(done) {
+ server = http.createServer(app);
+ server = server.listen(0, function listening() {
+ base += `:${server.address().port}`;
+ done();
+ });
+});
- req.end(function(err,res){
- if (err) return done(err);
- var trackEndEvent = 0;
- var trackCloseEvent = 0;
+describe('response', () => {
+ it('should act as a readable stream', (done) => {
+ const request_ = request.get(base).buffer(false);
- res.on('end',function(){
- trackEndEvent++;
- trackEndEvent.should.equal(1);
- trackCloseEvent.should.equal(0); // close should not have been called
- done();
- });
+ request_.end((error, res) => {
+ if (error) return done(error);
+ let trackEndEvent = 0;
+ let trackCloseEvent = 0;
- res.on('close',function(){
- trackCloseEvent++;
- });
+ res.on('end', () => {
+ trackEndEvent++;
+ trackEndEvent.should.equal(1);
+ if (!process.env.HTTP2_TEST) {
+ trackCloseEvent.should.equal(0); // close should not have been called
+ }
+ done();
+ });
- (function(){ res.pause() }).should.not.throw();
- (function(){ res.resume() }).should.not.throw();
- (function(){ res.destroy() }).should.not.throw();
+ res.on('close', () => {
+ trackCloseEvent++;
});
+
+ setTimeout(() => {
+ (() => {
+ res.pause();
+ }).should.not.throw();
+ (() => {
+ res.resume();
+ }).should.not.throw();
+ (() => {
+ res.destroy();
+ }).should.not.throw();
+ }, 50);
});
+ });
});
diff --git a/test/node/serialize.js b/test/node/serialize.js
new file mode 100644
index 000000000..0d01c6911
--- /dev/null
+++ b/test/node/serialize.js
@@ -0,0 +1,28 @@
+'use strict';
+
+const assert = require('assert');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
+
+describe('req.serialize(fn)', () => {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ it('should take precedence over default parsers', (done) => {
+ request
+ .post(`${base}/echo`)
+ .send({ foo: 123 })
+ .serialize(() => '{"bar":456}')
+ .end((error, res) => {
+ assert.ifError(error);
+ assert.equal('{"bar":456}', res.text);
+ assert.equal(456, res.body.bar);
+ done();
+ });
+ });
+});
diff --git a/test/node/set-host.js b/test/node/set-host.js
new file mode 100644
index 000000000..6bd58f1b3
--- /dev/null
+++ b/test/node/set-host.js
@@ -0,0 +1,46 @@
+'use strict';
+const request = require('../support/client');
+const express = require('../support/express');
+
+const app = express();
+const http = require('http');
+const assert = require('assert');
+
+describe('request.get().set()', () => {
+ if (process.env.HTTP2_TEST) {
+ return; // request object doesn't look the same
+ }
+
+ let server;
+
+ after(function exitServer() {
+ if (typeof server.close === 'function') {
+ server.close();
+ } else {
+ server.destroy();
+ }
+ });
+
+ it('should set host header after get()', (done) => {
+ app.get('/', (request_, res) => {
+ assert.equal(request_.hostname, 'example.com');
+ res.end();
+ });
+
+ server = http.createServer(app);
+ server.listen(0, function listening() {
+ request
+ .get(`http://localhost:${server.address().port}`)
+ .set('host', 'example.com')
+ .then(() => {
+ return request
+ .get(`http://example.com:${server.address().port}`)
+ .connect({
+ 'example.com': 'localhost',
+ '*': 'fail'
+ });
+ })
+ .then(() => done(), done);
+ });
+ });
+});
diff --git a/test/node/timeout.js b/test/node/timeout.js
deleted file mode 100644
index 3240af6cb..000000000
--- a/test/node/timeout.js
+++ /dev/null
@@ -1,30 +0,0 @@
-
-var request = require('../..')
- , express = require('express')
- , assert = require('assert')
- , app = express();
-
-app.get('/:ms', function(req, res){
- var ms = parseInt(req.params.ms, 10);
- setTimeout(function(){
- res.send('hello');
- }, ms);
-});
-
-app.listen(3009);
-
-describe('.timeout(ms)', function(){
- describe('when timeout is exceeded', function(done){
- it('should error', function(done){
- request
- .get('http://localhost:3009/500')
- .timeout(150)
- .end(function(err, res){
- assert(err, 'expected an error');
- assert('number' == typeof err.timeout, 'expected an error with .timeout');
- assert('ECONNABORTED' == err.code, 'expected abort error code')
- done();
- });
- })
- })
-})
\ No newline at end of file
diff --git a/test/node/toError.js b/test/node/toError.js
index 4db766406..300100fdf 100644
--- a/test/node/toError.js
+++ b/test/node/toError.js
@@ -1,28 +1,39 @@
+'use strict';
+const assert = require('assert');
+const request = require('../support/client');
+const express = require('../support/express');
-var request = require('../..')
- , express = require('express')
- , assert = require('better-assert')
- , app = express()
- , url = require('url');
+const app = express();
+let http = require('http');
-app.get('/', function(req, res){
+if (process.env.HTTP2_TEST) {
+ http = require('http2');
+}
+
+app.get('/', (request_, res) => {
res.status(400).send('invalid json');
});
-app.listen(8888);
+let base = 'http://localhost';
+let server;
+before(function listen(done) {
+ server = http.createServer(app);
+ server = server.listen(0, function listening() {
+ base += `:${server.address().port}`;
+ done();
+ });
+});
-describe('res.toError()', function(){
- it('should return an Error', function(done){
- request
- .get('http://localhost:8888/')
- .end(function(err, res){
- var err = res.toError();
- assert(err.status == 400);
- assert(err.method == 'GET');
- assert(err.path == '/');
- assert(err.message == 'cannot GET / (400)');
- assert(err.text == 'invalid json');
+describe('res.toError()', () => {
+ it('should return an Error', (done) => {
+ request.get(base).end((err, res) => {
+ const error = res.toError();
+ assert.equal(error.status, 400);
+ assert.equal(error.method, 'GET');
+ assert.equal(error.path, '/');
+ assert.equal(error.message, 'cannot GET / (400)');
+ assert.equal(error.text, 'invalid json');
done();
});
- })
-})
+ });
+});
diff --git a/test/node/unix-sockets.js b/test/node/unix-sockets.js
new file mode 100644
index 000000000..a443079af
--- /dev/null
+++ b/test/node/unix-sockets.js
@@ -0,0 +1,124 @@
+'use strict';
+const assert = require('assert');
+
+let http = require('http');
+let https = require('https');
+const os = require('os');
+const fs = require('fs');
+const express = require('../support/express');
+const request = require('../support/client');
+
+const app = express();
+
+const key = fs.readFileSync(`${__dirname}/fixtures/key.pem`);
+const cert = fs.readFileSync(`${__dirname}/fixtures/cert.pem`);
+const cacert = fs.readFileSync(`${__dirname}/fixtures/ca.cert.pem`);
+const httpSockPath = [os.tmpdir(), 'superagent-http.sock'].join('/');
+const httpsSockPath = [os.tmpdir(), 'superagent-https.sock'].join('/');
+let httpServer;
+let httpsServer;
+
+if (process.env.HTTP2_TEST) {
+ http = https = require('http2');
+}
+
+app.get('/', (request_, res) => {
+ res.send('root ok!');
+});
+
+app.get('/request/path', (request_, res) => {
+ res.send('request path ok!');
+});
+
+describe('[unix-sockets] http', () => {
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ before((done) => {
+ if (fs.existsSync(httpSockPath) === true) {
+ // try unlink if sock file exists
+ fs.unlinkSync(httpSockPath);
+ }
+
+ httpServer = http.createServer(app);
+ httpServer.listen(httpSockPath, done);
+ });
+
+ const base = `http+unix://${httpSockPath.replace(/\//g, '%2F')}`;
+
+ describe('request', () => {
+ it('path: / (root)', (done) => {
+ request.get(`${base}/`).end((error, res) => {
+ assert(res.ok);
+ assert.strictEqual('root ok!', res.text);
+ done();
+ });
+ });
+
+ it('path: /request/path', (done) => {
+ request.get(`${base}/request/path`).end((error, res) => {
+ assert(res.ok);
+ assert.strictEqual('request path ok!', res.text);
+ done();
+ });
+ });
+ });
+
+ after(() => {
+ if (typeof httpServer.close === 'function') {
+ httpServer.close();
+ } else httpServer.destroy();
+ });
+});
+
+describe('[unix-sockets] https', () => {
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ before((done) => {
+ if (fs.existsSync(httpsSockPath) === true) {
+ // try unlink if sock file exists
+ fs.unlinkSync(httpsSockPath);
+ }
+
+ httpsServer = process.env.HTTP2_TEST
+ ? https.createSecureServer({ key, cert }, app)
+ : https.createServer({ key, cert }, app);
+
+ httpsServer.listen(httpsSockPath, done);
+ });
+
+ const base = `https+unix://${httpsSockPath.replace(/\//g, '%2F')}`;
+
+ describe('request', () => {
+ it('path: / (root)', (done) => {
+ request
+ .get(`${base}/`)
+ .ca(cacert)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('root ok!', res.text);
+ done();
+ });
+ });
+
+ it('path: /request/path', (done) => {
+ request
+ .get(`${base}/request/path`)
+ .ca(cacert)
+ .end((error, res) => {
+ assert.ifError(error);
+ assert(res.ok);
+ assert.strictEqual('request path ok!', res.text);
+ done();
+ });
+ });
+ });
+
+ after((done) => {
+ httpsServer.close(done);
+ });
+});
diff --git a/test/node/user-agent.js b/test/node/user-agent.js
index 78b04a4c4..3e0f008ac 100644
--- a/test/node/user-agent.js
+++ b/test/node/user-agent.js
@@ -1,48 +1,20 @@
+'use strict';
+const assert = require('assert');
+const request = require('../support/client');
+const getSetup = require('../support/setup');
-var request = require('../..')
- , express = require('express')
- , assert = require('better-assert')
- , app = express()
- , url = require('url');
+describe('req.get()', () => {
+ let setup;
+ let base;
-app.all('/ua', function(req, res){
- res.writeHead(200, req.headers);
- req.pipe(res);
-});
-
-app.listen(3345);
-
-describe('req.get()', function(){
- it('should set a default user-agent', function(done){
- request
- .get('http://localhost:3345/ua')
- .end(function(err, res){
- assert(res.headers);
- assert(res.headers['user-agent']);
- assert(/^node-superagent\/\d+\.\d+\.\d+$/.test(res.headers['user-agent']));
- done();
- });
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
});
- it('should be able to override user-agent', function(done){
- request
- .get('http://localhost:3345/ua')
- .set('User-Agent', 'foo/bar')
- .end(function(err, res){
+ it('should not set a default user-agent', () =>
+ request.get(`${base}/ua`).then((res) => {
assert(res.headers);
- assert(res.headers['user-agent'] == 'foo/bar');
- done();
- });
- });
-
- it('should be able to wipe user-agent', function(done){
- request
- .get('http://localhost:3345/ua')
- .unset('User-Agent')
- .end(function(err, res){
- assert(res.headers);
- assert(res.headers['user-agent'] == void 0);
- done();
- });
- });
+ assert(!res.headers['user-agent']);
+ }));
});
diff --git a/test/node/utils.js b/test/node/utils.js
index c43d68924..50980bc18 100644
--- a/test/node/utils.js
+++ b/test/node/utils.js
@@ -1,33 +1,149 @@
-var assert = require('assert');
-var utils = require('../../lib/node/utils');
+'use strict';
+const assert = require('assert');
+const utils =
+ process.env.OLD_NODE_TEST === '1'
+ ? // eslint-disable-next-line node/no-missing-require
+ require('../../../utils')
+ : require('../../lib/utils');
-describe('utils.type(str)', function(){
- it('should return the mime type', function(){
- utils.type('application/json; charset=utf-8')
+describe('utils.type(str)', () => {
+ it('should return the mime type', () => {
+ utils
+ .type('application/json; charset=utf-8')
.should.equal('application/json');
- utils.type('application/json')
- .should.equal('application/json');
- })
-})
-
-describe('utils.params(str)', function(){
- it('should return the field parameters', function(){
- var str = 'application/json; charset=utf-8; foo = bar';
- var obj = utils.params(str);
- obj.charset.should.equal('utf-8');
- obj.foo.should.equal('bar');
-
- var str = 'application/json';
- utils.params(str).should.eql({});
- })
-})
-
-describe('utils.parseLinks(str)', function(){
- it('should parse links', function(){
- var str = '; rel="next", ; rel="last"';
- var ret = utils.parseLinks(str);
- ret.next.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=2');
- ret.last.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=5');
- })
-})
+ utils.type('application/json').should.equal('application/json');
+ });
+});
+
+describe('utils.params(str)', () => {
+ it('should return the field parameters', () => {
+ const object = utils.params('application/json; charset=utf-8; foo = bar');
+ object.charset.should.equal('utf-8');
+ object.foo.should.equal('bar');
+
+ utils.params('application/json').should.eql({});
+ });
+});
+
+describe('utils.parseLinks(str)', () => {
+ it('should parse links', () => {
+ const string_ =
+ '; rel="next", ; rel="last"';
+ const returnValue = utils.parseLinks(string_);
+ returnValue.next.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=2'
+ );
+ returnValue.last.should.equal(
+ 'https://api.github.com/repos/visionmedia/mocha/issues?page=5'
+ );
+ });
+});
+
+describe('utils.isGzipOrDeflateEncoding(res)', () => {
+ it('should return true when content encoding is gzip', () => {
+ utils.isGzipOrDeflateEncoding({
+ headers: {
+ 'content-encoding': 'gzip',
+ },
+ }).should.equal(true);
+ });
+ it('should return true when content encoding is deflate', () => {
+ utils.isGzipOrDeflateEncoding({
+ headers: {
+ 'content-encoding': 'deflate',
+ },
+ }).should.equal(true);
+ });
+ it('should return false when content encoding is bla', () => {
+ utils.isGzipOrDeflateEncoding({
+ headers: {
+ 'content-encoding': 'bla',
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding has a lot of spaces followed with gzip', () => {
+ utils.isGzipOrDeflateEncoding({
+ headers: {
+ 'content-encoding': " " * 10**6 + " gzip",
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding repeates it self', () => {
+ utils.isGzipOrDeflateEncoding({
+ headers: {
+ 'content-encoding': "gzip deflate gzip deflate gzip deflate gzip deflate gzip deflate gzip deflate gzip deflate gzip deflate",
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding repeates it self wuth a lot of spaces', () => {
+ utils.isGzipOrDeflateEncoding({
+ headers: {
+ 'content-encoding': " gzip deflate gzip deflate gzip deflate gzip deflate gzip deflate gzip deflate",
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding - nested patterns', () => {
+ utils.isGzipOrDeflateEncoding({
+ headers: {
+ 'content-encoding': " " * 10**5 + ("gzip deflate " * 1000)
+ },
+ }).should.equal(false);
+ });
+
+
+});
+
+describe('utils.isBrotliEncoding(res)', () => {
+ it('should return true when content encoding is br', () => {
+ utils.isBrotliEncoding({
+ headers: {
+ 'content-encoding': 'br',
+ },
+ }).should.equal(true);
+ });
+ it('should return false when content encoding is bla', () => {
+ utils.isBrotliEncoding({
+ headers: {
+ 'content-encoding': 'bla',
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding has a lot of spaces followed with br', () => {
+ utils.isBrotliEncoding({
+ headers: {
+ 'content-encoding': " " * 10**6 + " br",
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding repeates it self', () => {
+ utils.isBrotliEncoding({
+ headers: {
+ 'content-encoding': " br br br br br br br br br br br br br br br br br br br br br br br br br br br br br br",
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding repeates it self wuth a lot of spaces', () => {
+ utils.isBrotliEncoding({
+ headers: {
+ 'content-encoding': "br br br br br br br br br br br br br br",
+ },
+ }).should.equal(false);
+ });
+
+ it('should return true when content encoding - nested patterns', () => {
+ utils.isBrotliEncoding({
+ headers: {
+ 'content-encoding': " " * 10**5 + ("br " * 1000)
+ },
+ }).should.equal(false);
+ });
+
+});
diff --git a/test/redirects.js b/test/redirects.js
new file mode 100644
index 000000000..7cba5f46a
--- /dev/null
+++ b/test/redirects.js
@@ -0,0 +1,141 @@
+const assert = require('assert');
+
+const getSetup = require('./support/setup');
+const request = require('./support/client');
+
+describe('request', function () {
+ let setup;
+ let base;
+ let isMSIE;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ isMSIE = !setup.NODE && /Trident\//.test(navigator.userAgent);
+ });
+
+ this.timeout(20_000);
+ describe('on redirect', () => {
+ it('should retain header fields', (done) => {
+ request
+ .get(`${base}/header`)
+ .set('X-Foo', 'bar')
+ .end((error, res) => {
+ try {
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should preserve timeout across redirects', (done) => {
+ request
+ .get(`${base}/movies/random`)
+ .timeout(250)
+ .end((error, res) => {
+ try {
+ assert(error instanceof Error, 'expected an error');
+ error.should.have.property('timeout', 250);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should successfully redirect after retry on error', (done) => {
+ const id = Math.random() * 1_000_000 * Date.now();
+ request
+ .get(`${base}/error/redirect/${id}`)
+ .retry(2)
+ .end((error, res) => {
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'first movie page');
+ done();
+ });
+ });
+
+ it('should preserve retries across redirects', (done) => {
+ const id = Math.random() * 1_000_000 * Date.now();
+ request
+ .get(`${base}/error/redirect-error${id}`)
+ .retry(2)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ });
+ });
+ });
+
+ describe('on 303', () => {
+ it('should redirect with same method', (done) => {
+ request
+ .put(`${base}/redirect-303`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+
+ res.text.should.equal('method=get');
+ done();
+ });
+ });
+ });
+
+ describe('on 307', () => {
+ it('should redirect with same method', (done) => {
+ if (isMSIE) return done(); // IE9 broken
+
+ request
+ .put(`${base}/redirect-307`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+
+ res.text.should.equal('method=put');
+ done();
+ });
+ });
+ });
+
+ describe('on 308', () => {
+ it('should redirect with same method', (done) => {
+ if (isMSIE) return done(); // IE9 broken
+
+ request
+ .put(`${base}/redirect-308`)
+ .send({ msg: 'hello' })
+ .redirects(1)
+ .on('redirect', (res) => {
+ res.headers.location.should.equal('/reply-method');
+ })
+ .end((error, res) => {
+ if (error) {
+ done(error);
+ return;
+ }
+
+ res.text.should.equal('method=put');
+ done();
+ });
+ });
+ });
+});
diff --git a/test/request.js b/test/request.js
new file mode 100644
index 000000000..e4ec3b739
--- /dev/null
+++ b/test/request.js
@@ -0,0 +1,991 @@
+const fs = require('fs');
+
+const assert = require('assert');
+const getSetup = require('./support/setup');
+
+const request = require('./support/client');
+
+const binData = fs.readFileSync(`${__dirname}/node/fixtures/test.aac`);
+
+describe('request', function () {
+ let setup;
+ let uri;
+
+ before(async () => {
+ setup = await getSetup();
+ uri = setup.uri;
+ });
+
+ this.timeout(20_000);
+
+ it('Request inheritance', () => {
+ assert(request.get(`${uri}/`) instanceof request.Request);
+ });
+
+ it('request() simple GET without callback', (next) => {
+ request('GET', 'test/test.request.js').end();
+ next();
+ });
+
+ it('request() simple GET', (next) => {
+ request('GET', `${uri}/ok`).end((error, res) => {
+ try {
+ assert(res instanceof request.Response, 'respond with Response');
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'res.text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() simple HEAD', (next) => {
+ request.head(`${uri}/ok`).end((error, res) => {
+ try {
+ assert(res instanceof request.Response, 'respond with Response');
+ assert(res.ok, 'response should be ok');
+ assert(!res.text, 'res.text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() GET 5xx', (next) => {
+ request('GET', `${uri}/error`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(error.message, 'Internal Server Error');
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(!res.clientError, 'response should not be a client error');
+ assert(res.serverError, 'response should be a server error');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() GET 4xx', (next) => {
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(error.message, 'Not Found');
+ assert(!res.ok, 'response should not be ok');
+ assert(res.error, 'response should be an error');
+ assert(res.clientError, 'response should be a client error');
+ assert(!res.serverError, 'response should not be a server error');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() GET 404 Not Found', (next) => {
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.notFound, 'response should be .notFound');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() GET 400 Bad Request', (next) => {
+ request('GET', `${uri}/bad-request`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.badRequest, 'response should be .badRequest');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() GET 401 Bad Request', (next) => {
+ request('GET', `${uri}/unauthorized`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.unauthorized, 'response should be .unauthorized');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() GET 406 Not Acceptable', (next) => {
+ request('GET', `${uri}/not-acceptable`).end((error, res) => {
+ try {
+ assert(error);
+ assert(res.notAcceptable, 'response should be .notAcceptable');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() GET 204 No Content', (next) => {
+ request('GET', `${uri}/no-content`).end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() DELETE 204 No Content', (next) => {
+ request('DELETE', `${uri}/no-content`).end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() header parsing', (next) => {
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal('text/html; charset=utf-8', res.header['content-type']);
+ assert.equal('Express', res.header['x-powered-by']);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request() .status', (next) => {
+ request('GET', `${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(404, res.status, 'response .status');
+ assert.equal(4, res.statusType, 'response .statusType');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('get()', (next) => {
+ request.get(`${uri}/notfound`).end((error, res) => {
+ try {
+ assert(error);
+ assert.equal(404, res.status, 'response .status');
+ assert.equal(4, res.statusType, 'response .statusType');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('put()', (next) => {
+ request.put(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('updated', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('put().send()', (next) => {
+ request
+ .put(`${uri}/user/13/body`)
+ .send({ user: 'new' })
+ .end((error, res) => {
+ try {
+ assert.equal('received new', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('post()', (next) => {
+ request.post(`${uri}/user`).end((error, res) => {
+ try {
+ assert.equal('created', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('del()', (next) => {
+ request.del(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('deleted', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('delete()', (next) => {
+ request.delete(`${uri}/user/12`).end((error, res) => {
+ try {
+ assert.equal('deleted', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('post() data', (next) => {
+ request
+ .post(`${uri}/todo/item`)
+ .type('application/octet-stream')
+ .send('tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .type()', (next) => {
+ request
+ .post(`${uri}/user/12/pet`)
+ .type('urlencoded')
+ .send('pet=tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .type() with alias', (next) => {
+ request
+ .post(`${uri}/user/12/pet`)
+ .type('application/x-www-form-urlencoded')
+ .send('pet=tobi')
+ .end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text, 'response text');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .get() with no data or callback', (next) => {
+ request.get(`${uri}/echo-header/content-type`);
+ next();
+ });
+
+ it('request .send() with no data only', (next) => {
+ request.post(`${uri}/user/5/pet`).type('urlencoded').send('pet=tobi');
+ next();
+ });
+
+ it('request .send() with callback only', (next) => {
+ request
+ .get(`${uri}/echo-header/accept`)
+ .set('Accept', 'foo/bar')
+ .end((error, res) => {
+ try {
+ assert.equal('foo/bar', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .accept() with json', (next) => {
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('json')
+ .end((error, res) => {
+ try {
+ assert.equal('application/json', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .accept() with application/json', (next) => {
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('application/json')
+ .end((error, res) => {
+ try {
+ assert.equal('application/json', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .accept() with xml', (next) => {
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('xml')
+ .end((error, res) => {
+ try {
+ // We can't depend on mime module to be consistent with this
+ assert(res.text == 'application/xml' || res.text == 'text/xml');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .accept() with application/xml', (next) => {
+ request
+ .get(`${uri}/echo-header/accept`)
+ .accept('application/xml')
+ .end((error, res) => {
+ try {
+ assert.equal('application/xml', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ // FIXME: ie6 will POST rather than GET here due to data(),
+ // but I'm not 100% sure why. Newer IEs are OK.
+ it('request .end()', (next) => {
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .send()', (next) => {
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .set()', (next) => {
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set('Content-Type', 'text/plain')
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request .set(object)', (next) => {
+ request
+ .put(`${uri}/echo-header/content-type`)
+ .set({ 'Content-Type': 'text/plain' })
+ .send('wahoo')
+ .end((error, res) => {
+ try {
+ assert.equal('text/plain', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST urlencoded', (next) => {
+ request
+ .post(`${uri}/pet`)
+ .type('urlencoded')
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST json', (next) => {
+ request
+ .post(`${uri}/pet`)
+ .type('json')
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST json array', (next) => {
+ request
+ .post(`${uri}/echo`)
+ .send([1, 2, 3])
+ .end((error, res) => {
+ try {
+ assert.equal(
+ 'application/json',
+ res.header['content-type'].split(';')[0]
+ );
+ assert.equal('[1,2,3]', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST json default', (next) => {
+ request
+ .post(`${uri}/pet`)
+ .send({ name: 'Manny', species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST json contentType charset', (next) => {
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/json; charset=UTF-8')
+ .send({ data: ['data1', 'data2'] })
+ .end((error, res) => {
+ try {
+ assert.equal('{"data":["data1","data2"]}', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST json contentType vendor', (next) => {
+ request
+ .post(`${uri}/echo`)
+ .set('Content-Type', 'application/vnd.example+json')
+ .send({ data: ['data1', 'data2'] })
+ .end((error, res) => {
+ try {
+ assert.equal('{"data":["data1","data2"]}', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST multiple .send() calls', (next) => {
+ request
+ .post(`${uri}/pet`)
+ .send({ name: 'Manny' })
+ .send({ species: 'cat' })
+ .end((error, res) => {
+ try {
+ assert.equal('added Manny the cat', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST multiple .send() strings', (next) => {
+ request
+ .post(`${uri}/echo`)
+ .send('user[name]=tj')
+ .send('user[email]=tj@vision-media.ca')
+ .end((error, res) => {
+ try {
+ assert.equal(
+ 'application/x-www-form-urlencoded',
+ res.header['content-type'].split(';')[0]
+ );
+ assert.equal(
+ res.text,
+ 'user[name]=tj&user[email]=tj@vision-media.ca'
+ );
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST with no data', (next) => {
+ request
+ .post(`${uri}/empty-body`)
+ .send()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.noContent, 'response should be .noContent');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET .type', (next) => {
+ request.get(`${uri}/pets`).end((error, res) => {
+ try {
+ assert.equal('application/json', res.type);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET Content-Type params', (next) => {
+ request.get(`${uri}/text`).end((error, res) => {
+ try {
+ assert.equal('utf-8', res.charset);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET json', (next) => {
+ request.get(`${uri}/pets`).end((error, res) => {
+ try {
+ assert.deepEqual(res.body, ['tobi', 'loki', 'jane']);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET json-seq', (next) => {
+ request
+ .get(`${uri}/json-seq`)
+ .buffer()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.deepEqual(res.text, '\u001E{"id":1}\n\u001E{"id":2}\n');
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET binary data', (next) => {
+ request
+ .get(`${uri}/binary-data`)
+ .buffer()
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert.deepEqual(res.body, binData);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET x-www-form-urlencoded', (next) => {
+ request.get(`${uri}/foo`).end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { foo: 'bar' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET shorthand', (next) => {
+ request.get(`${uri}/foo`, (error, res) => {
+ try {
+ assert.equal('foo=bar', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST shorthand', (next) => {
+ request.post(`${uri}/user/0/pet`, { pet: 'tobi' }, (error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('POST shorthand without callback', (next) => {
+ request.post(`${uri}/user/0/pet`, { pet: 'tobi' }).end((error, res) => {
+ try {
+ assert.equal('added pet "tobi"', res.text);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring object with array', (next) => {
+ request
+ .get(`${uri}/querystring`)
+ .query({ val: ['a', 'b', 'c'] })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { val: ['a', 'b', 'c'] });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring object with array and primitives', (next) => {
+ request
+ .get(`${uri}/querystring`)
+ .query({ array: ['a', 'b', 'c'], string: 'foo', number: 10 })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ array: ['a', 'b', 'c'],
+ string: 'foo',
+ number: 10
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring object with two arrays', (next) => {
+ request
+ .get(`${uri}/querystring`)
+ .query({ array1: ['a', 'b', 'c'], array2: [1, 2, 3] })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ array1: ['a', 'b', 'c'],
+ array2: [1, 2, 3]
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring object', (next) => {
+ request
+ .get(`${uri}/querystring`)
+ .query({ search: 'Manny' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { search: 'Manny' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring append original', (next) => {
+ request
+ .get(`${uri}/querystring?search=Manny`)
+ .query({ range: '1..5' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, { search: 'Manny', range: '1..5' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring multiple objects', (next) => {
+ request
+ .get(`${uri}/querystring`)
+ .query({ search: 'Manny' })
+ .query({ range: '1..5' })
+ .query({ order: 'desc' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring with strings', (next) => {
+ request
+ .get(`${uri}/querystring`)
+ .query('search=Manny')
+ .query('range=1..5')
+ .query('order=desc')
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET querystring with strings and objects', (next) => {
+ request
+ .get(`${uri}/querystring`)
+ .query('search=Manny')
+ .query({ order: 'desc', range: '1..5' })
+ .end((error, res) => {
+ try {
+ assert.deepEqual(res.body, {
+ search: 'Manny',
+ range: '1..5',
+ order: 'desc'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('GET shorthand payload goes to querystring', (next) => {
+ request.get(
+ `${uri}/querystring`,
+ { foo: 'FOO', bar: 'BAR' },
+ (error, res) => {
+ try {
+ assert.deepEqual(res.body, { foo: 'FOO', bar: 'BAR' });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ }
+ );
+ });
+
+ it('HEAD shorthand payload goes to querystring', (next) => {
+ request.head(
+ `${uri}/querystring-in-header`,
+ { foo: 'FOO', bar: 'BAR' },
+ (error, res) => {
+ try {
+ assert.deepEqual(JSON.parse(res.headers.query), {
+ foo: 'FOO',
+ bar: 'BAR'
+ });
+ next();
+ } catch (err) {
+ next(err);
+ }
+ }
+ );
+ });
+
+ it('request(method, url)', (next) => {
+ request('GET', `${uri}/foo`).end((error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request(url)', (next) => {
+ request(`${uri}/foo`).end((error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request(url, fn)', (next) => {
+ request(`${uri}/foo`, (error, res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('req.timeout(ms)', (next) => {
+ const request_ = request.get(`${uri}/delay/3000`).timeout(1000);
+ request_.end((error, res) => {
+ try {
+ assert(error, 'error missing');
+ assert.equal(1000, error.timeout, 'err.timeout missing');
+ assert.equal(
+ 'Timeout of 1000ms exceeded',
+ error.message,
+ 'err.message incorrect'
+ );
+ assert.equal(null, res);
+ assert(request_.timedout, true);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('req.timeout(ms) with redirect', (next) => {
+ const request_ = request.get(`${uri}/delay/const`).timeout(1000);
+ request_.end((error, res) => {
+ try {
+ assert(error, 'error missing');
+ assert.equal(1000, error.timeout, 'err.timeout missing');
+ assert.equal(
+ 'Timeout of 1000ms exceeded',
+ error.message,
+ 'err.message incorrect'
+ );
+ assert.equal(null, res);
+ assert(request_.timedout, true);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('request event', (next) => {
+ request
+ .get(`${uri}/foo`)
+ .on('request', (request_) => {
+ try {
+ assert.equal(`${uri}/foo`, request_.url);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ })
+ .end();
+ });
+
+ it('response event', (next) => {
+ request
+ .get(`${uri}/foo`)
+ .on('response', (res) => {
+ try {
+ assert.equal('bar', res.body.foo);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ })
+ .end();
+ });
+
+ it('response should set statusCode', (next) => {
+ request.get(`${uri}/ok`, (error, res) => {
+ try {
+ assert.strictEqual(res.statusCode, 200);
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+
+ it('req.toJSON()', (next) => {
+ request.get(`${uri}/ok`).end((error, res) => {
+ try {
+ const index = (res.request || res.req).toJSON();
+ for (const property of ['url', 'method', 'data', 'headers']) {
+ assert(index.hasOwnProperty(property));
+ }
+
+ next();
+ } catch (err) {
+ next(err);
+ }
+ });
+ });
+});
diff --git a/test/retry.js b/test/retry.js
new file mode 100644
index 000000000..52c023418
--- /dev/null
+++ b/test/retry.js
@@ -0,0 +1,295 @@
+const assert = require('assert');
+const getSetup = require('./support/setup');
+
+const request = require('./support/client');
+
+function uniqid() {
+ return Math.random() * 10_000_000;
+}
+
+describe('.retry(count)', function () {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ this.timeout(15_000);
+
+ it('should not retry if passed "0"', (done) => {
+ request
+ .get(`${base}/error`)
+ .retry(0)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should not retry if passed an invalid number', (done) => {
+ request
+ .get(`${base}/error`)
+ .retry(-2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should not retry if passed undefined', (done) => {
+ request
+ .get(`${base}/error`)
+ .retry(undefined)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(
+ undefined,
+ error.retries,
+ 'expected an error without .retries'
+ );
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should handle server error after repeat attempt', (done) => {
+ request
+ .get(`${base}/error`)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should retry if passed nothing', (done) => {
+ request
+ .get(`${base}/error`)
+ .retry()
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(1, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should retry if passed "true"', (done) => {
+ request
+ .get(`${base}/error`)
+ .retry(true)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(1, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should handle successful request after repeat attempt from server error', (done) => {
+ request
+ .get(`${base}/error/ok/${uniqid()}`)
+ .query({ qs: 'present' })
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert(res.text, 'res.text');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should handle server timeout error after repeat attempt', (done) => {
+ request
+ .get(`${base}/delay/400`)
+ .timeout(200)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should handle successful request after repeat attempt from server timeout', (done) => {
+ const url = `/delay/1200/ok/${uniqid()}?built=in`;
+ request
+ .get(base + url)
+ .query('string=ified')
+ .query({ json: 'ed' })
+ .timeout(600)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert.equal(res.text, `ok = ${url}&string=ified&json=ed`);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should handle successful request after repeat attempt from server timeout when using .then(fulfill, reject)', (done) => {
+ const url = `/delay/1200/ok/${uniqid()}?built=in`;
+ request
+ .get(base + url)
+ .query('string=ified')
+ .query({ json: 'ed' })
+ .timeout(600)
+ .retry(1)
+ .then((res, error) => {
+ try {
+ assert.ifError(error);
+ assert(res.ok, 'response should be ok');
+ assert.equal(res.text, `ok = ${url}&string=ified&json=ed`);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should correctly abort a retry attempt', (done) => {
+ let aborted = false;
+ const request_ = request.get(`${base}/delay/400`).timeout(200).retry(2);
+ request_.end((error, res) => {
+ try {
+ assert(false, 'should not complete the request');
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ request_.on('abort', () => {
+ aborted = true;
+ });
+
+ setTimeout(() => {
+ request_.abort();
+ setTimeout(() => {
+ try {
+ assert(aborted, 'should be aborted');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ }, 150);
+ }, 150);
+ });
+
+ it('should correctly retain header fields', (done) => {
+ request
+ .get(`${base}/error/ok/${uniqid()}`)
+ .query({ qs: 'present' })
+ .retry(2)
+ .set('X-Foo', 'bar')
+ .end((error, res) => {
+ try {
+ assert.ifError(error);
+ assert(res.body);
+ res.body.should.have.property('x-foo', 'bar');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should not retry on 4xx responses', (done) => {
+ request
+ .get(`${base}/bad-request`)
+ .retry(2)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(0, error.retries, 'expected an error with 0 .retries');
+ assert.equal(400, error.status, 'expected an error status of 400');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+
+ it('should execute callback on retry if passed', (done) => {
+ let callbackCallCount = 0;
+ function retryCallback(request) {
+ callbackCallCount++;
+ }
+
+ request
+ .get(`${base}/error`)
+ .retry(2, retryCallback)
+ .end((error, res) => {
+ try {
+ assert(error, 'expected an error');
+ assert.equal(2, error.retries, 'expected an error with .retries');
+ assert.equal(500, error.status, 'expected an error status of 500');
+ assert.equal(
+ 2,
+ callbackCallCount,
+ 'expected the callback to be called on each retry'
+ );
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ });
+});
diff --git a/test/support/client.js b/test/support/client.js
new file mode 100644
index 000000000..af843103e
--- /dev/null
+++ b/test/support/client.js
@@ -0,0 +1,7 @@
+const request = require('../..');
+
+if (process.env.HTTP2_TEST) {
+ request.http2 = true;
+}
+
+exports = module.exports = request;
diff --git a/test/support/express/index.js b/test/support/express/index.js
new file mode 100644
index 000000000..fed7e8632
--- /dev/null
+++ b/test/support/express/index.js
@@ -0,0 +1,30 @@
+const process = require('process');
+const express = require('express');
+
+let http2Request;
+let http2Res;
+if (process.env.HTTP2_TEST) {
+ const http2 = require('http2');
+ const requestDecorator = require('./requestDecorator');
+ const resDecorator = require('./responseDecorator');
+ http2Request = requestDecorator(
+ Object.create(http2.Http2ServerRequest.prototype)
+ );
+ http2Res = resDecorator(Object.create(http2.Http2ServerResponse.prototype));
+}
+
+function createApp() {
+ const app = express();
+ if (process.env.HTTP2_TEST) {
+ app.request = Object.create(http2Request, {
+ app: { configurable: true, enumerable: true, writable: true, value: app }
+ });
+ app.response = Object.create(http2Res, {
+ app: { configurable: true, enumerable: true, writable: true, value: app }
+ });
+ }
+
+ return app;
+}
+
+module.exports = createApp;
diff --git a/test/support/express/requestDecorator.js b/test/support/express/requestDecorator.js
new file mode 100644
index 000000000..196406b9c
--- /dev/null
+++ b/test/support/express/requestDecorator.js
@@ -0,0 +1,502 @@
+/*!
+ * express
+ * Copyright(c) 2009-2013 TJ Holowaychuk
+ * Copyright(c) 2013 Roman Shtylman
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict';
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+const { isIP } = require('net');
+const accepts = require('accepts');
+const typeis = require('type-is');
+const fresh = require('fresh');
+const parseRange = require('range-parser');
+const parse = require('parseurl');
+const proxyaddr = require('proxy-addr');
+
+/**
+ * Request prototype.
+ * @public
+ */
+
+/**
+ * Module exports.
+ * @public
+ */
+
+module.exports = setMethods;
+
+function setMethods(request) {
+ /**
+ * Return request header.
+ *
+ * The `Referrer` header field is special-cased,
+ * both `Referrer` and `Referer` are interchangeable.
+ *
+ * Examples:
+ *
+ * req.get('Content-Type');
+ * // => "text/plain"
+ *
+ * req.get('content-type');
+ * // => "text/plain"
+ *
+ * req.get('Something');
+ * // => undefined
+ *
+ * Aliased as `req.header()`.
+ *
+ * @param {String} name
+ * @return {String}
+ * @public
+ */
+
+ request.get = request.header = function header(name) {
+ if (!name) {
+ throw new TypeError('name argument is required to req.get');
+ }
+
+ if (typeof name !== 'string') {
+ throw new TypeError('name must be a string to req.get');
+ }
+
+ const lc = name.toLowerCase();
+
+ switch (lc) {
+ case 'referer':
+ case 'referrer':
+ return this.headers.referrer || this.headers.referer;
+ default:
+ return this.headers[lc];
+ }
+ };
+
+ /**
+ * To do: update docs.
+ *
+ * Check if the given `type(s)` is acceptable, returning
+ * the best match when true, otherwise `undefined`, in which
+ * case you should respond with 406 "Not Acceptable".
+ *
+ * The `type` value may be a single MIME type string
+ * such as "application/json", an extension name
+ * such as "json", a comma-delimited list such as "json, html, text/plain",
+ * an argument list such as `"json", "html", "text/plain"`,
+ * or an array `["json", "html", "text/plain"]`. When a list
+ * or array is given, the _best_ match, if any is returned.
+ *
+ * Examples:
+ *
+ * // Accept: text/html
+ * req.accepts('html');
+ * // => "html"
+ *
+ * // Accept: text/*, application/json
+ * req.accepts('html');
+ * // => "html"
+ * req.accepts('text/html');
+ * // => "text/html"
+ * req.accepts('json, text');
+ * // => "json"
+ * req.accepts('application/json');
+ * // => "application/json"
+ *
+ * // Accept: text/*, application/json
+ * req.accepts('image/png');
+ * req.accepts('png');
+ * // => undefined
+ *
+ * // Accept: text/*;q=.5, application/json
+ * req.accepts(['html', 'json']);
+ * req.accepts('html', 'json');
+ * req.accepts('html, json');
+ * // => "json"
+ *
+ * @param {String|Array} type(s)
+ * @return {String|Array|Boolean}
+ * @public
+ */
+
+ request.accepts = function () {
+ const accept = accepts(this);
+ return accept.types.apply(accept, arguments);
+ };
+
+ /**
+ * Check if the given `encoding`s are accepted.
+ *
+ * @param {String} ...encoding
+ * @return {String|Array}
+ * @public
+ */
+
+ request.acceptsEncodings = function () {
+ const accept = accepts(this);
+ return accept.encodings.apply(accept, arguments);
+ };
+
+ /**
+ * Check if the given `charset`s are acceptable,
+ * otherwise you should respond with 406 "Not Acceptable".
+ *
+ * @param {String} ...charset
+ * @return {String|Array}
+ * @public
+ */
+
+ request.acceptsCharsets = function () {
+ const accept = accepts(this);
+ return accept.charsets.apply(accept, arguments);
+ };
+
+ /**
+ * Check if the given `lang`s are acceptable,
+ * otherwise you should respond with 406 "Not Acceptable".
+ *
+ * @param {String} ...lang
+ * @return {String|Array}
+ * @public
+ */
+
+ request.acceptsLanguages = function () {
+ const accept = accepts(this);
+ return accept.languages.apply(accept, arguments);
+ };
+
+ /**
+ * Parse Range header field, capping to the given `size`.
+ *
+ * Unspecified ranges such as "0-" require knowledge of your resource length. In
+ * the case of a byte range this is of course the total number of bytes. If the
+ * Range header field is not given `undefined` is returned, `-1` when unsatisfiable,
+ * and `-2` when syntactically invalid.
+ *
+ * When ranges are returned, the array has a "type" property which is the type of
+ * range that is required (most commonly, "bytes"). Each array element is an object
+ * with a "start" and "end" property for the portion of the range.
+ *
+ * The "combine" option can be set to `true` and overlapping & adjacent ranges
+ * will be combined into a single range.
+ *
+ * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"
+ * should respond with 4 users when available, not 3.
+ *
+ * @param {number} size
+ * @param {object} [options]
+ * @param {boolean} [options.combine=false]
+ * @return {number|array}
+ * @public
+ */
+
+ request.range = function range(size, options) {
+ const range = this.get('Range');
+ if (!range) return;
+ return parseRange(size, range, options);
+ };
+
+ /**
+ * Parse the query string of `req.url`.
+ *
+ * This uses the "query parser" setting to parse the raw
+ * string into an object.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ defineGetter(request, 'query', function query() {
+ const queryparse = this.app.get('query parser fn');
+
+ if (!queryparse) {
+ // parsing is disabled
+ return Object.create(null);
+ }
+
+ const querystring = parse(this).query;
+
+ return queryparse(querystring);
+ });
+
+ /**
+ * Check if the incoming request contains the "Content-Type"
+ * header field, and it contains the give mime `type`.
+ *
+ * Examples:
+ *
+ * // With Content-Type: text/html; charset=utf-8
+ * req.is('html');
+ * req.is('text/html');
+ * req.is('text/*');
+ * // => true
+ *
+ * // When Content-Type is application/json
+ * req.is('json');
+ * req.is('application/json');
+ * req.is('application/*');
+ * // => true
+ *
+ * req.is('html');
+ * // => false
+ *
+ * @param {String|Array} types...
+ * @return {String|false|null}
+ * @public
+ */
+
+ request.is = function is(types) {
+ let array = types;
+
+ // support flattened arguments
+ if (!Array.isArray(types)) {
+ array = Array.from({ length: arguments.length });
+ for (let i = 0; i < array.length; i++) {
+ array[i] = arguments[i];
+ }
+ }
+
+ return typeis(this, array);
+ };
+
+ /**
+ * Return the protocol string "http" or "https"
+ * when requested with TLS. When the "trust proxy"
+ * setting trusts the socket address, the
+ * "X-Forwarded-Proto" header field will be trusted
+ * and used if present.
+ *
+ * If you're running behind a reverse proxy that
+ * supplies https for you this may be enabled.
+ *
+ * @return {String}
+ * @public
+ */
+
+ defineGetter(request, 'protocol', function protocol() {
+ const proto = this.connection.encrypted ? 'https' : 'http';
+ const trust = this.app.get('trust proxy fn');
+
+ if (!trust(this.connection.remoteAddress, 0)) {
+ return proto;
+ }
+
+ // Note: X-Forwarded-Proto is normally only ever a
+ // single value, but this is to be safe.
+ const header = this.get('X-Forwarded-Proto') || proto;
+ const index = header.indexOf(',');
+
+ return index !== -1 ? header.slice(0, index).trim() : header.trim();
+ });
+
+ /**
+ * Short-hand for:
+ *
+ * req.protocol === 'https'
+ *
+ * @return {Boolean}
+ * @public
+ */
+
+ defineGetter(request, 'secure', function secure() {
+ return this.protocol === 'https';
+ });
+
+ /**
+ * Return the remote address from the trusted proxy.
+ *
+ * The is the remote address on the socket unless
+ * "trust proxy" is set.
+ *
+ * @return {String}
+ * @public
+ */
+
+ defineGetter(request, 'ip', function ip() {
+ const trust = this.app.get('trust proxy fn');
+ return proxyaddr(this, trust);
+ });
+
+ /**
+ * When "trust proxy" is set, trusted proxy addresses + client.
+ *
+ * For example if the value were "client, proxy1, proxy2"
+ * you would receive the array `["client", "proxy1", "proxy2"]`
+ * where "proxy2" is the furthest down-stream and "proxy1" and
+ * "proxy2" were trusted.
+ *
+ * @return {Array}
+ * @public
+ */
+
+ defineGetter(request, 'ips', function ips() {
+ const trust = this.app.get('trust proxy fn');
+ const addrs = proxyaddr.all(this, trust);
+
+ // reverse the order (to farthest -> closest)
+ // and remove socket address
+ addrs.reverse().pop();
+
+ return addrs;
+ });
+
+ /**
+ * Return subdomains as an array.
+ *
+ * Subdomains are the dot-separated parts of the host before the main domain of
+ * the app. By default, the domain of the app is assumed to be the last two
+ * parts of the host. This can be changed by setting "subdomain offset".
+ *
+ * For example, if the domain is "tobi.ferrets.example.com":
+ * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
+ * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
+ *
+ * @return {Array}
+ * @public
+ */
+
+ defineGetter(request, 'subdomains', function subdomains() {
+ const { hostname } = this;
+
+ if (!hostname) return [];
+
+ const offset = this.app.get('subdomain offset');
+ const subdomains = !isIP(hostname)
+ ? hostname.split('.').reverse()
+ : [hostname];
+
+ return subdomains.slice(offset);
+ });
+
+ /**
+ * Short-hand for `url.parse(req.url).pathname`.
+ *
+ * @return {String}
+ * @public
+ */
+
+ defineGetter(request, 'path', function path() {
+ return parse(this).pathname;
+ });
+
+ /**
+ * Parse the "Host" header field to a host.
+ *
+ * When the "trust proxy" setting trusts the socket
+ * address, the "X-Forwarded-Host" header field will
+ * be trusted.
+ *
+ * @return {String}
+ * @public
+ */
+
+ defineGetter(request, 'host', function host() {
+ const trust = this.app.get('trust proxy fn');
+ let value = this.get('X-Forwarded-Host');
+
+ if (!value || !trust(this.connection.remoteAddress, 0)) {
+ value = this.get('Host');
+ }
+
+ return value || undefined;
+ });
+
+ /**
+ * Parse the "Host" header field to a hostname.
+ *
+ * When the "trust proxy" setting trusts the socket
+ * address, the "X-Forwarded-Host" header field will
+ * be trusted.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ defineGetter(request, 'hostname', function hostname() {
+ const { host } = this;
+
+ if (!host) return;
+
+ // IPv6 literal support
+ const offset = host[0] === '[' ? host.indexOf(']') + 1 : 0;
+ const index = host.indexOf(':', offset);
+
+ return index !== -1 ? host.slice(0, index) : host;
+ });
+
+ /**
+ * Check if the request is fresh, aka
+ * Last-Modified and/or the ETag
+ * still match.
+ *
+ * @return {Boolean}
+ * @public
+ */
+
+ defineGetter(request, 'fresh', function () {
+ const { method } = this;
+ const { res } = this;
+ const status = res.statusCode;
+
+ // GET or HEAD for weak freshness validation only
+ if (method !== 'GET' && method !== 'HEAD') return false;
+
+ // 2xx or 304 as per rfc2616 14.26
+ if ((status >= 200 && status < 300) || status === 304) {
+ return fresh(this.headers, {
+ etag: res.get('ETag'),
+ 'last-modified': res.get('Last-Modified')
+ });
+ }
+
+ return false;
+ });
+
+ /**
+ * Check if the request is stale, aka
+ * "Last-Modified" and / or the "ETag" for the
+ * resource has changed.
+ *
+ * @return {Boolean}
+ * @public
+ */
+
+ defineGetter(request, 'stale', function stale() {
+ return !this.fresh;
+ });
+
+ /**
+ * Check if the request was an _XMLHttpRequest_.
+ *
+ * @return {Boolean}
+ * @public
+ */
+
+ defineGetter(request, 'xhr', function xhr() {
+ const value = this.get('X-Requested-With') || '';
+ return value.toLowerCase() === 'xmlhttprequest';
+ });
+
+ return request;
+}
+
+/**
+ * Helper function for creating a getter on an object.
+ *
+ * @param {Object} obj
+ * @param {String} name
+ * @param {Function} getter
+ * @private
+ */
+function defineGetter(object, name, getter) {
+ Object.defineProperty(object, name, {
+ configurable: true,
+ enumerable: true,
+ get: getter
+ });
+}
diff --git a/test/support/express/responseDecorator.js b/test/support/express/responseDecorator.js
new file mode 100644
index 000000000..26535e879
--- /dev/null
+++ b/test/support/express/responseDecorator.js
@@ -0,0 +1,1017 @@
+/*!
+ * express
+ * Copyright(c) 2009-2013 TJ Holowaychuk
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict';
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+const path = require('path');
+const { Buffer } = require('safe-buffer');
+const contentDisposition = require('content-disposition');
+const encodeUrl = require('encodeurl');
+const escapeHtml = require('escape-html');
+const onFinished = require('on-finished');
+const pathIsAbsolute = require('path-is-absolute');
+const statuses = require('statuses');
+const merge = require('utils-merge');
+const { sign } = require('cookie-signature');
+const cookie = require('cookie');
+const send = require('send');
+const { normalizeType } = require('./utils');
+const { normalizeTypes } = require('./utils');
+const { setCharset } = require('./utils');
+
+const { extname } = path;
+const { mime } = send;
+const { resolve } = path;
+const vary = require('vary');
+/**
+ * Module exports.
+ * @public
+ */
+
+module.exports = setMethods;
+
+function setMethods(res) {
+ /**
+ * Module variables.
+ * @private
+ */
+
+ const charsetRegExp = /;\s*charset\s*=/;
+
+ /**
+ * Set status `code`.
+ *
+ * @param {Number} code
+ * @return {ServerResponse}
+ * @public
+ */
+
+ res.status = function status(code) {
+ this.statusCode = code;
+ return this;
+ };
+
+ /**
+ * Set Link header field with the given `links`.
+ *
+ * Examples:
+ *
+ * res.links({
+ * next: 'http://api.example.com/users?page=2',
+ * last: 'http://api.example.com/users?page=5'
+ * });
+ *
+ * @param {Object} links
+ * @return {ServerResponse}
+ * @public
+ */
+
+ res.links = function (links) {
+ let link = this.get('Link') || '';
+ if (link) link += ', ';
+ return this.set(
+ 'Link',
+ link +
+ Object.keys(links)
+ .map((rel) => {
+ return '<' + links[rel] + '>; rel="' + rel + '"';
+ })
+ .join(', ')
+ );
+ };
+
+ /**
+ * Send a response.
+ *
+ * Examples:
+ *
+ * res.send(Buffer.from('wahoo'));
+ * res.send({ some: 'json' });
+ * res.send('some html
');
+ *
+ * @param {string|number|boolean|object|Buffer} body
+ * @public
+ */
+
+ res.send = function send(body) {
+ let chunk = body;
+ let encoding;
+ const { req } = this;
+ let type;
+
+ // settings
+ const { app } = this;
+
+ switch (typeof chunk) {
+ // string defaulting to html
+ case 'string':
+ if (!this.get('Content-Type')) {
+ this.type('html');
+ }
+
+ break;
+ case 'boolean':
+ case 'number':
+ case 'object':
+ if (chunk === null) {
+ chunk = '';
+ } else if (Buffer.isBuffer(chunk)) {
+ if (!this.get('Content-Type')) {
+ this.type('bin');
+ }
+ } else {
+ return this.json(chunk);
+ }
+
+ break;
+ }
+
+ // write strings in utf-8
+ if (typeof chunk === 'string') {
+ encoding = 'utf8';
+ type = this.get('Content-Type');
+
+ // reflect this in content-type
+ if (typeof type === 'string') {
+ this.set('Content-Type', setCharset(type, 'utf-8'));
+ }
+ }
+
+ // determine if ETag should be generated
+ const etagFn = app.get('etag fn');
+ const generateETag = !this.get('ETag') && typeof etagFn === 'function';
+
+ // populate Content-Length
+ let length_;
+ if (chunk !== undefined) {
+ if (Buffer.isBuffer(chunk)) {
+ // get length of Buffer
+ length_ = chunk.length;
+ } else if (!generateETag && chunk.length < 1000) {
+ // just calculate length when no ETag + small chunk
+ length_ = Buffer.byteLength(chunk, encoding);
+ } else {
+ // convert chunk to Buffer and calculate
+ chunk = Buffer.from(chunk, encoding);
+ encoding = undefined;
+ length_ = chunk.length;
+ }
+
+ this.set('Content-Length', length_);
+ }
+
+ // populate ETag
+ let etag;
+ if (
+ generateETag &&
+ length_ !== undefined &&
+ (etag = etagFn(chunk, encoding))
+ ) {
+ this.set('ETag', etag);
+ }
+
+ // freshness
+ if (req.fresh) this.statusCode = 304;
+
+ // strip irrelevant headers
+ if (this.statusCode === 204 || this.statusCode === 304) {
+ this.removeHeader('Content-Type');
+ this.removeHeader('Content-Length');
+ this.removeHeader('Transfer-Encoding');
+ chunk = '';
+ }
+
+ if (req.method === 'HEAD') {
+ // skip body for HEAD
+ this.end();
+ } else {
+ // respond
+ this.end(chunk, encoding);
+ }
+
+ return this;
+ };
+
+ /**
+ * Send JSON response.
+ *
+ * Examples:
+ *
+ * res.json(null);
+ * res.json({ user: 'tj' });
+ *
+ * @param {string|number|boolean|object} obj
+ * @public
+ */
+
+ res.json = function json(object) {
+ // settings
+ const { app } = this;
+ const escape = app.get('json escape');
+ const replacer = app.get('json replacer');
+ const spaces = app.get('json spaces');
+ const body = stringify(object, replacer, spaces, escape);
+
+ // content-type
+ if (!this.get('Content-Type')) {
+ this.set('Content-Type', 'application/json');
+ }
+
+ return this.send(body);
+ };
+
+ /**
+ * Send JSON response with JSONP callback support.
+ *
+ * Examples:
+ *
+ * res.jsonp(null);
+ * res.jsonp({ user: 'tj' });
+ *
+ * @param {string|number|boolean|object} obj
+ * @public
+ */
+
+ res.jsonp = function jsonp(object) {
+ // settings
+ const { app } = this;
+ const escape = app.get('json escape');
+ const replacer = app.get('json replacer');
+ const spaces = app.get('json spaces');
+ let body = stringify(object, replacer, spaces, escape);
+ let callback = this.req.query[app.get('jsonp callback name')];
+
+ // content-type
+ if (!this.get('Content-Type')) {
+ this.set('X-Content-Type-Options', 'nosniff');
+ this.set('Content-Type', 'application/json');
+ }
+
+ // fixup callback
+ if (Array.isArray(callback)) {
+ callback = callback[0];
+ }
+
+ // jsonp
+ if (typeof callback === 'string' && callback.length > 0) {
+ this.set('X-Content-Type-Options', 'nosniff');
+ this.set('Content-Type', 'text/javascript');
+
+ // restrict callback charset
+ callback = callback.replace(/[^[\]\w$.]/g, '');
+
+ // replace chars not allowed in JavaScript that are in JSON
+ body = body.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
+
+ // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
+ // the typeof check is just to reduce client error noise
+ body =
+ '/**/ typeof ' +
+ callback +
+ " === 'function' && " +
+ callback +
+ '(' +
+ body +
+ ');';
+ }
+
+ return this.send(body);
+ };
+
+ /**
+ * Send given HTTP status code.
+ *
+ * Sets the response status to `statusCode` and the body of the
+ * response to the standard description from node's http.STATUS_CODES
+ * or the statusCode number if no description.
+ *
+ * Examples:
+ *
+ * res.sendStatus(200);
+ *
+ * @param {number} statusCode
+ * @public
+ */
+
+ res.sendStatus = function sendStatus(statusCode) {
+ const body = statuses(statusCode) || String(statusCode);
+
+ this.statusCode = statusCode;
+ this.type('txt');
+
+ return this.send(body);
+ };
+
+ /**
+ * Transfer the file at the given `path`.
+ *
+ * Automatically sets the _Content-Type_ response header field.
+ * The callback `callback(err)` is invoked when the transfer is complete
+ * or when an error occurs. Be sure to check `res.sentHeader`
+ * if you wish to attempt responding, as the header and some data
+ * may have already been transferred.
+ *
+ * Options:
+ *
+ * - `maxAge` defaulting to 0 (can be string converted by `ms`)
+ * - `root` root directory for relative filenames
+ * - `headers` object of headers to serve with file
+ * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
+ *
+ * Other options are passed along to `send`.
+ *
+ * Examples:
+ *
+ * The following example illustrates how `res.sendFile()` may
+ * be used as an alternative for the `static()` middleware for
+ * dynamic situations. The code backing `res.sendFile()` is actually
+ * the same code, so HTTP cache support etc is identical.
+ *
+ * app.get('/user/:uid/photos/:file', function(req, res){
+ * var uid = req.params.uid
+ * , file = req.params.file;
+ *
+ * req.user.mayViewFilesFrom(uid, function(yes){
+ * if (yes) {
+ * res.sendFile('/uploads/' + uid + '/' + file);
+ * } else {
+ * res.send(403, 'Sorry! you cant see that.');
+ * }
+ * });
+ * });
+ *
+ * @public
+ */
+
+ res.sendFile = function sendFile(path, options, callback) {
+ let done = callback;
+ const { req } = this;
+ const res = this;
+ const { next } = req;
+ let options_ = options || {};
+
+ if (!path) {
+ throw new TypeError('path argument is required to res.sendFile');
+ }
+
+ // support function as second arg
+ if (typeof options === 'function') {
+ done = options;
+ options_ = {};
+ }
+
+ if (!options_.root && !pathIsAbsolute(path)) {
+ throw new TypeError(
+ 'path must be absolute or specify root to res.sendFile'
+ );
+ }
+
+ // create file stream
+ const pathname = encodeURI(path);
+ const file = send(req, pathname, options_);
+
+ // transfer
+ sendfile(res, file, options_, (error) => {
+ if (done) return done(error);
+ if (error && error.code === 'EISDIR') return next();
+
+ // next() all but write errors
+ if (error && error.code !== 'ECONNABORTED' && error.syscall !== 'write') {
+ next(error);
+ }
+ });
+ };
+
+ /**
+ * Transfer the file at the given `path` as an attachment.
+ *
+ * Optionally providing an alternate attachment `filename`,
+ * and optional callback `callback(err)`. The callback is invoked
+ * when the data transfer is complete, or when an error has
+ * ocurred. Be sure to check `res.headersSent` if you plan to respond.
+ *
+ * Optionally providing an `options` object to use with `res.sendFile()`.
+ * This function will set the `Content-Disposition` header, overriding
+ * any `Content-Disposition` header passed as header options in order
+ * to set the attachment and filename.
+ *
+ * This method uses `res.sendFile()`.
+ *
+ * @public
+ */
+
+ res.download = function download(path, filename, options, callback) {
+ let done = callback;
+ let name = filename;
+ let options_ = options || null;
+
+ // support function as second or third arg
+ if (typeof filename === 'function') {
+ done = filename;
+ name = null;
+ options_ = null;
+ } else if (typeof options === 'function') {
+ done = options;
+ options_ = null;
+ }
+
+ // set Content-Disposition when file is sent
+ const headers = {
+ 'Content-Disposition': contentDisposition(name || path)
+ };
+
+ // merge user-provided headers
+ if (options_ && options_.headers) {
+ const keys = Object.keys(options_.headers);
+ for (const key of keys) {
+ if (key.toLowerCase() !== 'content-disposition') {
+ headers[key] = options_.headers[key];
+ }
+ }
+ }
+
+ // merge user-provided options
+ options_ = Object.create(options_);
+ options_.headers = headers;
+
+ // Resolve the full path for sendFile
+ const fullPath = resolve(path);
+
+ // send file
+ return this.sendFile(fullPath, options_, done);
+ };
+
+ /**
+ * Set _Content-Type_ response header with `type` through `mime.lookup()`
+ * when it does not contain "/", or set the Content-Type to `type` otherwise.
+ *
+ * Examples:
+ *
+ * res.type('.html');
+ * res.type('html');
+ * res.type('json');
+ * res.type('application/json');
+ * res.type('png');
+ *
+ * @param {String} type
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.contentType = res.type = function contentType(type) {
+ const ct = !type.includes('/') ? mime.lookup(type) : type;
+
+ return this.set('Content-Type', ct);
+ };
+
+ /**
+ * Respond to the Acceptable formats using an `obj`
+ * of mime-type callbacks.
+ *
+ * This method uses `req.accepted`, an array of
+ * acceptable types ordered by their quality values.
+ * When "Accept" is not present the _first_ callback
+ * is invoked, otherwise the first match is used. When
+ * no match is performed the server responds with
+ * 406 "Not Acceptable".
+ *
+ * Content-Type is set for you, however if you choose
+ * you may alter this within the callback using `res.type()`
+ * or `res.set('Content-Type', ...)`.
+ *
+ * res.format({
+ * 'text/plain': function(){
+ * res.send('hey');
+ * },
+ *
+ * 'text/html': function(){
+ * res.send('hey
');
+ * },
+ *
+ * 'appliation/json': function(){
+ * res.send({ message: 'hey' });
+ * }
+ * });
+ *
+ * In addition to canonicalized MIME types you may
+ * also use extnames mapped to these types:
+ *
+ * res.format({
+ * text: function(){
+ * res.send('hey');
+ * },
+ *
+ * html: function(){
+ * res.send('hey
');
+ * },
+ *
+ * json: function(){
+ * res.send({ message: 'hey' });
+ * }
+ * });
+ *
+ * By default Express passes an `Error`
+ * with a `.status` of 406 to `next(err)`
+ * if a match is not made. If you provide
+ * a `.default` callback it will be invoked
+ * instead.
+ *
+ * @param {Object} obj
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.format = function (object) {
+ const { req } = this;
+ const { next } = req;
+
+ const fn = object.default;
+ if (fn) delete object.default;
+ const keys = Object.keys(object);
+
+ const key = keys.length > 0 ? req.accepts(keys) : false;
+
+ this.vary('Accept');
+
+ if (key) {
+ this.set('Content-Type', normalizeType(key).value);
+ object[key](req, this, next);
+ } else if (fn) {
+ fn();
+ } else {
+ const error = new Error('Not Acceptable');
+ error.status = error.statusCode = 406;
+ error.types = normalizeTypes(keys).map((o) => {
+ return o.value;
+ });
+ next(error);
+ }
+
+ return this;
+ };
+
+ /**
+ * Set _Content-Disposition_ header to _attachment_ with optional `filename`.
+ *
+ * @param {String} filename
+ * @return {ServerResponse}
+ * @public
+ */
+
+ res.attachment = function attachment(filename) {
+ if (filename) {
+ this.type(extname(filename));
+ }
+
+ this.set('Content-Disposition', contentDisposition(filename));
+
+ return this;
+ };
+
+ /**
+ * Append additional header `field` with value `val`.
+ *
+ * Example:
+ *
+ * res.append('Link', [' ', ' ']);
+ * res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
+ * res.append('Warning', '199 Miscellaneous warning');
+ *
+ * @param {String} field
+ * @param {String|Array} val
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.append = function append(field, value_) {
+ const previous = this.get(field);
+ let value = value_;
+
+ if (previous) {
+ // concat the new and prev vals
+ value = Array.isArray(previous)
+ ? previous.concat(value_)
+ : Array.isArray(value_)
+ ? [previous].concat(value_)
+ : [previous, value_];
+ }
+
+ return this.set(field, value);
+ };
+
+ /**
+ * Set header `field` to `val`, or pass
+ * an object of header fields.
+ *
+ * Examples:
+ *
+ * res.set('Foo', ['bar', 'baz']);
+ * res.set('Accept', 'application/json');
+ * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
+ *
+ * Aliased as `res.header()`.
+ *
+ * @param {String|Object} field
+ * @param {String|Array} val
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.set = res.header = function header(field, value_) {
+ if (arguments.length === 2) {
+ let value = Array.isArray(value_) ? value_.map(String) : String(value_);
+
+ // add charset to content-type
+ if (field.toLowerCase() === 'content-type') {
+ if (Array.isArray(value)) {
+ throw new TypeError('Content-Type cannot be set to an Array');
+ }
+
+ if (!charsetRegExp.test(value)) {
+ const charset = mime.charsets.lookup(value.split(';')[0]);
+ if (charset) value += '; charset=' + charset.toLowerCase();
+ }
+ }
+
+ this.setHeader(field, value);
+ } else {
+ for (const key in field) {
+ this.set(key, field[key]);
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Get value for header `field`.
+ *
+ * @param {String} field
+ * @return {String}
+ * @public
+ */
+
+ res.get = function (field) {
+ return this.getHeader(field);
+ };
+
+ /**
+ * Clear cookie `name`.
+ *
+ * @param {String} name
+ * @param {Object} [options]
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.clearCookie = function clearCookie(name, options) {
+ const options_ = merge({ expires: new Date(1), path: '/' }, options);
+
+ return this.cookie(name, '', options_);
+ };
+
+ /**
+ * Set cookie `name` to `value`, with the given `options`.
+ *
+ * Options:
+ *
+ * - `maxAge` max-age in milliseconds, converted to `expires`
+ * - `signed` sign the cookie
+ * - `path` defaults to "/"
+ *
+ * Examples:
+ *
+ * // "Remember Me" for 15 minutes
+ * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
+ *
+ * // save as above
+ * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
+ *
+ * @param {String} name
+ * @param {String|Object} value
+ * @param {Object} [options]
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.cookie = function (name, value, options) {
+ const options_ = merge({}, options);
+ const { secret } = this.req;
+ const { signed } = options_;
+
+ if (signed && !secret) {
+ throw new Error('cookieParser("secret") required for signed cookies');
+ }
+
+ let value_ =
+ typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value);
+
+ if (signed) {
+ value_ = 's:' + sign(value_, secret);
+ }
+
+ if ('maxAge' in options_) {
+ options_.expires = new Date(Date.now() + options_.maxAge);
+ options_.maxAge /= 1000;
+ }
+
+ if (options_.path == null) {
+ options_.path = '/';
+ }
+
+ this.append('Set-Cookie', cookie.serialize(name, String(value_), options_));
+
+ return this;
+ };
+
+ /**
+ * Set the location header to `url`.
+ *
+ * The given `url` can also be "back", which redirects
+ * to the _Referrer_ or _Referer_ headers or "/".
+ *
+ * Examples:
+ *
+ * res.location('/foo/bar').;
+ * res.location('http://example.com');
+ * res.location('../login');
+ *
+ * @param {String} url
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.location = function location(url) {
+ let loc = url;
+
+ // "back" is an alias for the referrer
+ if (url === 'back') {
+ loc = this.req.get('Referrer') || '/';
+ }
+
+ // set location
+ return this.set('Location', encodeUrl(loc));
+ };
+
+ /**
+ * Redirect to the given `url` with optional response `status`
+ * defaulting to 302.
+ *
+ * The resulting `url` is determined by `res.location()`, so
+ * it will play nicely with mounted apps, relative paths,
+ * `"back"` etc.
+ *
+ * Examples:
+ *
+ * res.redirect('/foo/bar');
+ * res.redirect('http://example.com');
+ * res.redirect(301, 'http://example.com');
+ * res.redirect('../login'); // /blog/post/1 -> /blog/login
+ *
+ * @public
+ */
+
+ res.redirect = function redirect(url) {
+ let address = url;
+ let body;
+ let status = 302;
+
+ // allow status / url
+ if (arguments.length === 2) {
+ status = arguments[0];
+ address = arguments[1];
+ }
+
+ // Set location header
+ address = this.location(address).get('Location');
+
+ // Support text/{plain,html} by default
+ this.format({
+ text() {
+ body = statuses(status) + '. Redirecting to ' + address;
+ },
+
+ html() {
+ const u = escapeHtml(address);
+ body =
+ '' +
+ statuses(status) +
+ '. Redirecting to ' +
+ u +
+ '
';
+ },
+
+ default() {
+ body = '';
+ }
+ });
+
+ // Respond
+ this.statusCode = status;
+ this.set('Content-Length', Buffer.byteLength(body));
+
+ if (this.req.method === 'HEAD') {
+ this.end();
+ } else {
+ this.end(body);
+ }
+ };
+
+ /**
+ * Add `field` to Vary. If already present in the Vary set, then
+ * this call is simply ignored.
+ *
+ * @param {Array|String} field
+ * @return {ServerResponse} for chaining
+ * @public
+ */
+
+ res.vary = function (field) {
+ vary(this, field);
+
+ return this;
+ };
+
+ /**
+ * Render `view` with the given `options` and optional callback `fn`.
+ * When a callback function is given a response will _not_ be made
+ * automatically, otherwise a response of _200_ and _text/html_ is given.
+ *
+ * Options:
+ *
+ * - `cache` boolean hinting to the engine it should cache
+ * - `filename` filename of the view being rendered
+ *
+ * @public
+ */
+
+ res.render = function render(view, options, callback) {
+ const { app } = this.req;
+ let done = callback;
+ let options_ = options || {};
+ const { req } = this;
+ const self = this;
+
+ // support callback function as second arg
+ if (typeof options === 'function') {
+ done = options;
+ options_ = {};
+ }
+
+ // merge res.locals
+ options_._locals = self.locals;
+
+ // default callback to respond
+ done =
+ done ||
+ function (error, string_) {
+ if (error) return req.next(error);
+ self.send(string_);
+ };
+
+ // render
+ app.render(view, options_, done);
+ };
+
+ // pipe the send file stream
+ function sendfile(res, file, options, callback) {
+ let done = false;
+ let streaming;
+
+ // request aborted
+ function onaborted() {
+ if (done) return;
+ done = true;
+
+ const error = new Error('Request aborted');
+ error.code = 'ECONNABORTED';
+ callback(error);
+ }
+
+ // directory
+ function ondirectory() {
+ if (done) return;
+ done = true;
+
+ const error = new Error('EISDIR, read');
+ error.code = 'EISDIR';
+ callback(error);
+ }
+
+ // errors
+ function onerror(error) {
+ if (done) return;
+ done = true;
+ callback(error);
+ }
+
+ // ended
+ function onend() {
+ if (done) return;
+ done = true;
+ callback();
+ }
+
+ // file
+ function onfile() {
+ streaming = false;
+ }
+
+ // finished
+ function onfinish(error) {
+ if (error && error.code === 'ECONNRESET') return onaborted();
+ if (error) return onerror(error);
+ if (done) return;
+
+ setImmediate(() => {
+ if (streaming !== false && !done) {
+ onaborted();
+ return;
+ }
+
+ if (done) return;
+ done = true;
+ callback();
+ });
+ }
+
+ // streaming
+ function onstream() {
+ streaming = true;
+ }
+
+ file.on('directory', ondirectory);
+ file.on('end', onend);
+ file.on('error', onerror);
+ file.on('file', onfile);
+ file.on('stream', onstream);
+
+ if (options.headers) {
+ // set headers on successful transfer
+ file.on('headers', function headers(res) {
+ const object = options.headers;
+ const keys = Object.keys(object);
+
+ for (const k of keys) {
+ res.setHeader(k, object[k]);
+ }
+ });
+ }
+
+ // pipe
+ file.pipe(res);
+
+ onFinished(res, onfinish);
+ }
+
+ return res;
+}
+
+/**
+ * Stringify JSON, like JSON.stringify, but v8 optimized, with the
+ * ability to escape characters that can trigger HTML sniffing.
+ *
+ * @param {*} value
+ * @param {function} replaces
+ * @param {number} spaces
+ * @param {boolean} escape
+ * @returns {string}
+ * @private
+ */
+
+function stringify(value, replacer, spaces, escape) {
+ // v8 checks arguments.length for optimizing simple call
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4730
+ let json =
+ replacer || spaces
+ ? JSON.stringify(value, replacer, spaces)
+ : JSON.stringify(value);
+
+ if (escape) {
+ json = json.replace(/[<>&]/g, (c) => {
+ switch (c.charCodeAt(0)) {
+ case 0x3c:
+ return '\\u003c';
+ case 0x3e:
+ return '\\u003e';
+ case 0x26:
+ return '\\u0026';
+ default:
+ return c;
+ }
+ });
+ }
+
+ return json;
+}
diff --git a/test/support/express/utils.js b/test/support/express/utils.js
new file mode 100644
index 000000000..b3dbc7a65
--- /dev/null
+++ b/test/support/express/utils.js
@@ -0,0 +1,276 @@
+/*!
+ * express
+ * Copyright(c) 2009-2013 TJ Holowaychuk
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict';
+
+/**
+ * Module dependencies.
+ * @api private
+ */
+
+const querystring = require('querystring');
+const { Buffer } = require('safe-buffer');
+const contentType = require('content-type');
+const { mime } = require('send');
+const etag = require('etag');
+const proxyaddr = require('proxy-addr');
+const qs = require('qs');
+
+let isHttp2Supported = true;
+
+/**
+ * Test for http2 support
+ * @api private
+ */
+try {
+ require('http2');
+} catch {
+ isHttp2Supported = false;
+}
+/**
+ * Return strong ETag for `body`.
+ *
+ * @param {String|Buffer} body
+ * @param {String} [encoding]
+ * @return {String}
+ * @api private
+ */
+
+exports.etag = createETagGenerator({ weak: false });
+
+/**
+ * Return weak ETag for `body`.
+ *
+ * @param {String|Buffer} body
+ * @param {String} [encoding]
+ * @return {String}
+ * @api private
+ */
+
+exports.wetag = createETagGenerator({ weak: true });
+
+/**
+ * Normalize the given `type`, for example "html" becomes "text/html".
+ *
+ * @param {String} type
+ * @return {Object}
+ * @api private
+ */
+
+exports.normalizeType = function (type) {
+ return ~type.indexOf('/')
+ ? acceptParameters(type)
+ : { value: mime.lookup(type), params: {} };
+};
+
+/**
+ * Normalize `types`, for example "html" becomes "text/html".
+ *
+ * @param {Array} types
+ * @return {Array}
+ * @api private
+ */
+
+exports.normalizeTypes = function (types) {
+ const returnValue = [];
+
+ for (const element of types) {
+ returnValue.push(exports.normalizeType(element));
+ }
+
+ return returnValue;
+};
+
+/**
+ * Parse accept params `str` returning an
+ * object with `.value`, `.quality` and `.params`.
+ * also includes `.originalIndex` for stable sorting
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+function acceptParameters(string_, index) {
+ const parts = string_.split(/ *; */);
+ const returnValue = {
+ value: parts[0],
+ quality: 1,
+ params: {},
+ originalIndex: index
+ };
+
+ for (let i = 1; i < parts.length; ++i) {
+ const pms = parts[i].split(/ *= */);
+ if (pms[0] === 'q') {
+ returnValue.quality = Number.parseFloat(pms[1]);
+ } else {
+ returnValue.params[pms[0]] = pms[1];
+ }
+ }
+
+ return returnValue;
+}
+
+/**
+ * Compile "etag" value to function.
+ *
+ * @param {Boolean|String|Function} val
+ * @return {Function}
+ * @api private
+ */
+
+exports.compileETag = function (value) {
+ let fn;
+
+ if (typeof value === 'function') {
+ return value;
+ }
+
+ switch (value) {
+ case true:
+ fn = exports.wetag;
+ break;
+ case false:
+ break;
+ case 'strong':
+ fn = exports.etag;
+ break;
+ case 'weak':
+ fn = exports.wetag;
+ break;
+ default:
+ throw new TypeError('unknown value for etag function: ' + value);
+ }
+
+ return fn;
+};
+
+/**
+ * Compile "query parser" value to function.
+ *
+ * @param {String|Function} val
+ * @return {Function}
+ * @api private
+ */
+
+exports.compileQueryParser = function compileQueryParser(value) {
+ let fn;
+
+ if (typeof value === 'function') {
+ return value;
+ }
+
+ switch (value) {
+ case true:
+ fn = querystring.parse;
+ break;
+ case false:
+ break;
+ case 'extended':
+ fn = parseExtendedQueryString;
+ break;
+ case 'simple':
+ fn = querystring.parse;
+ break;
+ default:
+ throw new TypeError('unknown value for query parser function: ' + value);
+ }
+
+ return fn;
+};
+
+/**
+ * Compile "proxy trust" value to function.
+ *
+ * @param {Boolean|String|Number|Array|Function} val
+ * @return {Function}
+ * @api private
+ */
+
+exports.compileTrust = function (value) {
+ if (typeof value === 'function') return value;
+
+ if (value === true) {
+ // Support plain true/false
+ return function () {
+ return true;
+ };
+ }
+
+ if (typeof value === 'number') {
+ // Support trusting hop count
+ return function (a, i) {
+ return i < value;
+ };
+ }
+
+ if (typeof value === 'string') {
+ // Support comma-separated values
+ value = value.split(/ *, */);
+ }
+
+ return proxyaddr.compile(value || []);
+};
+
+/**
+ * Flag for http2 support
+ */
+exports.isHttp2Supported = isHttp2Supported;
+/**
+ * Set the charset in a given Content-Type string.
+ *
+ * @param {String} type
+ * @param {String} charset
+ * @return {String}
+ * @api private
+ */
+
+exports.setCharset = function setCharset(type, charset) {
+ if (!type || !charset) {
+ return type;
+ }
+
+ // parse type
+ const parsed = contentType.parse(type);
+
+ // set charset
+ parsed.parameters.charset = charset;
+
+ // format type
+ return contentType.format(parsed);
+};
+
+/**
+ * Create an ETag generator function, generating ETags with
+ * the given options.
+ *
+ * @param {object} options
+ * @return {function}
+ * @private
+ */
+
+function createETagGenerator(options) {
+ return function generateETag(body, encoding) {
+ const buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body;
+
+ return etag(buf, options);
+ };
+}
+
+/**
+ * Parse an extended query string with qs.
+ *
+ * @return {Object}
+ * @private
+ */
+
+function parseExtendedQueryString(string_) {
+ return qs.parse(string_, {
+ allowPrototypes: true
+ });
+}
diff --git a/test/support/server.js b/test/support/server.js
index 337a3791e..840b4c2c4 100644
--- a/test/support/server.js
+++ b/test/support/server.js
@@ -1,184 +1,601 @@
-var express = require('express');
-var bodyParser = require('body-parser');
-var cookieParser = require('cookie-parser');
-
-var app = express();
-
-app.use(function(req, res, next) {
+const fs = require('node:fs');
+const path = require('node:path');
+const process = require('node:process');
+const { Buffer } = require('node:buffer');
+let http = require('node:http');
+const multer = require('multer');
+const bodyParser = require('body-parser');
+const cookieParser = require('cookie-parser');
+const basicAuth = require('basic-auth-connect');
+const express = require('./express');
+
+let isPseudoHeader;
+
+if (process.env.HTTP2_TEST) {
+ http = require('node:http2');
+ const {
+ HTTP2_HEADER_AUTHORITY,
+ HTTP2_HEADER_METHOD,
+ HTTP2_HEADER_PATH,
+ HTTP2_HEADER_SCHEME,
+ HTTP2_HEADER_STATUS
+ } = http.constants;
+ isPseudoHeader = function (name) {
+ switch (name) {
+ case HTTP2_HEADER_STATUS: // :status
+ case HTTP2_HEADER_METHOD: // :method
+ case HTTP2_HEADER_PATH: // :path
+ case HTTP2_HEADER_AUTHORITY: // :authority
+ case HTTP2_HEADER_SCHEME: {
+ // :scheme
+ return true;
+ }
+
+ default: {
+ return false;
+ }
+ }
+ };
+}
+
+const app = express();
+
+app.use((request, res, next) => {
res.set('Cache-Control', 'no-cache, no-store');
next();
});
-app.all('/echo', function(req, res){
- res.writeHead(200, req.headers);
- req.pipe(res);
+app.all('/url', (request, res) => {
+ res.send(request.url);
+});
+
+app.all('/echo', (request, res) => {
+ const { headers } = request;
+ if (process.env.HTTP2_TEST) {
+ for (const name of Object.keys(headers)) {
+ if (isPseudoHeader(name)) {
+ delete headers[name];
+ }
+ }
+ }
+
+ res.writeHead(200, headers);
+ request.pipe(res);
+});
+
+let uniq = 0;
+app.all('/unique', (request, res) => {
+ res.send(`never the same ${uniq++}`);
});
app.use(bodyParser.urlencoded({ extended: true }));
+app.use(multer().none());
+
+app.all('/formecho', (request, res) => {
+ if (
+ !/application\/x-www-form-urlencoded|multipart\/form-data/.test(
+ request.headers['content-type']
+ )
+ ) {
+ return res.status(400).end('wrong type');
+ }
+
+ res.json(request.body);
+});
+
app.use(bodyParser.json());
app.use(cookieParser());
-app.use('/xdomain', function(req, res, next){
- if (!req.get('Origin')) return next();
- res.set('Access-Control-Allow-Origin', req.get('Origin'));
+app.use('/xdomain', (request, res, next) => {
+ if (!request.get('Origin')) return next();
+ res.set('Access-Control-Allow-Origin', request.get('Origin'));
res.set('Access-Control-Allow-Credentials', 'true');
res.set('Access-Control-Allow-Methods', 'POST');
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
- if ('OPTIONS' == req.method) return res.send(200);
+ if (request.method === 'OPTIONS') return res.send(200);
next();
});
-app.get('/xdomain', function(req, res){
+app.get('/xdomain', (request, res) => {
res.send('tobi');
});
-app.get('/login', function(req, res){
+app.get('/login', (request, res) => {
res.status(200).send('');
});
-app.get('/json', function(req, res){
+app.get('/json', (request, res) => {
res.status(200).json({ name: 'manny' });
});
-app.get('/ok', function(req, res){
+app.get('/json-hal', (request, res) => {
+ res.set('content-type', 'application/hal+json');
+ res.send({ name: 'hal 5000' });
+});
+
+app.get('/ok', (request, res) => {
res.send('ok');
});
-app.get('/foo', function(req, res){
+app.get('/foo', (request, res) => {
res
.header('Content-Type', 'application/x-www-form-urlencoded')
.send('foo=bar');
});
-app.get('/', function(req, res){
+app.get('/form-data', (request, res) => {
+ res.header('Content-Type', 'application/x-www-form-urlencoded');
+ res.send('pet[name]=manny');
+});
+
+app.post('/movie', (request, res) => {
+ res.redirect('/movies/all/0');
+});
+
+app.get('/', (request, res) => {
+ res.set('QUERY', JSON.stringify(request.query));
res.redirect('/movies');
});
-app.get('/movies', function(req, res){
+app.get('/movies', (request, res) => {
+ res.set('QUERY', JSON.stringify(request.query));
res.redirect('/movies/all');
});
-app.get('/movies/all', function(req, res){
+app.get('/movies/all', (request, res) => {
+ res.set('QUERY', JSON.stringify(request.query));
res.redirect('/movies/all/0');
});
-app.get('/movies/all/0', function(req, res){
+app.get('/movies/all/0', (request, res) => {
+ res.set('QUERY', JSON.stringify(request.query));
res.status(200).send('first movie page');
});
-app.get('/links', function(req, res){
- res.header('Link', '; rel="next"');
+app.get('/movies/random', (request, res) => {
+ res.redirect('/movie/4');
+});
+
+app.get('/movie/4', (request, res) => {
+ setTimeout(() => {
+ res.send('not-so-random movie');
+ }, 1000);
+});
+
+app.get('/links', (request, res) => {
+ res.header(
+ 'Link',
+ '; rel="next"'
+ );
res.end();
});
-app.get('/xml', function(req, res){
+app.get('/xml', (request, res) => {
res.type('xml');
res.status(200).send(' ');
});
-app.get('/custom', function(req, res){
+app.get('/custom', (request, res) => {
res.type('application/x-custom');
res.status(200).send('custom stuff');
});
-app.put('/user/:id', function(req, res){
+app.put('/user/:id', (request, res) => {
res.send('updated');
});
-app.patch('/user/:id', function(req, res){
+app.put('/user/:id/body', (request, res) => {
+ res.send(`received ${request.body.user}`);
+});
+
+app.patch('/user/:id', (request, res) => {
res.send('updated');
});
-app.post('/user/:id/pet', function(req, res){
- res.send('added pet "' + req.body.pet + '"');
+app.post('/user/:id/pet', (request, res) => {
+ res.send(`added pet "${request.body.pet}"`);
});
-app.post('/user', function(req, res){
+app.post('/user', (request, res) => {
res.send('created');
});
-app.delete('/user/:id', function(req, res){
+app.delete('/user/:id', (request, res) => {
res.send('deleted');
});
-app.post('/todo/item', function(req, res){
- var buf = '';
- req.on('data', function(chunk){ buf += chunk; });
- req.on('end', function(){
- res.send('added "' + buf + '"');
+app.post('/todo/item', (request, res) => {
+ let buf = '';
+ request.on('data', (chunk) => {
+ buf += chunk;
+ });
+ request.on('end', () => {
+ res.send(`added "${buf}"`);
});
});
-app.get('/delay/const', function (req, res) {
+app.get('/delay/const', (request, res) => {
res.redirect('/delay/3000');
});
-app.get('/delay/:ms', function(req, res){
- var ms = ~~req.params.ms;
- setTimeout(function(){
+app.get('/delay/zip', (request, res) => {
+ res.writeHead(200, {
+ 'Content-Type': 'text/plain',
+ 'Content-Encoding': 'gzip'
+ });
+ setTimeout(() => {
+ res.end();
+ }, 10_000);
+});
+
+app.get('/delay/json', (request, res) => {
+ res.writeHead(200, { 'Content-Type': 'application/json' });
+ setTimeout(() => {
+ res.end();
+ }, 10_000);
+});
+
+let slowBodyCallback;
+app.get('/delay/slowbody', (request, res) => {
+ res.writeHead(200, { 'Content-Type': 'application/octet-stream' });
+
+ // Send lots of garbage data to overflow all buffers along the way,
+ // so that the browser gets some data before the request is done
+ const initialDataSent = new Promise((resolve) => {
+ res.write(Buffer.alloc(4000), () => {
+ res.write(Buffer.alloc(16_000));
+ resolve();
+ });
+ });
+
+ // Make sure sending of request body takes over 1s,
+ // so that the test can't pass by accident.
+ const minimumTime = new Promise((resolve) => {
+ setTimeout(resolve, 1001);
+ });
+
+ new Promise((resolve) => {
+ // Waiting full 10 seconds for the test would be too annoying,
+ // so the remote callback is a hack to push the test forward
+ slowBodyCallback = resolve;
+ setTimeout(resolve, 10_000);
+ })
+ .then(() => Promise.all([initialDataSent, minimumTime]))
+ .then(() => {
+ res.end('bye');
+ });
+});
+
+app.get('/delay/slowbody/finish', (request, res) => {
+ if (slowBodyCallback) slowBodyCallback();
+ res.sendStatus(204);
+});
+
+app.get('/delay/:ms', (request, res) => {
+ const ms = Math.trunc(request.params.ms);
+ setTimeout(() => {
res.sendStatus(200);
}, ms);
});
-app.get('/querystring', function(req, res){
- res.send(req.query);
+app.get('/querystring', (request, res) => {
+ res.send(request.query);
+});
+
+app.get('/querystring-in-header', (request, res) => {
+ res.set('query', JSON.stringify(request.query));
+ res.send();
+});
+
+app.all('/echo-header/:field', (request, res) => {
+ res.send(request.headers[request.params.field]);
});
-app.get('/echo-header/:field', function(req, res){
- res.send(req.headers[req.params.field]);
+app.get('/echo-headers', (request, res) => {
+ res.json(request.headers);
});
-app.post('/pet', function(req, res){
- res.send('added ' + req.body.name + ' the ' + req.body.species);
+app.post('/pet', (request, res) => {
+ res.send(`added ${request.body.name} the ${request.body.species}`);
});
-app.get('/pets', function(req, res){
+app.get('/pets', (request, res) => {
res.send(['tobi', 'loki', 'jane']);
});
-app.get('/text', function(req, res){
- res.send("just some text");
+app.get('/json-seq', (request, res) => {
+ res
+ .set('content-type', 'application/json-seq')
+ .send('\u001E{"id":1}\n\u001E{"id":2}\n');
+});
+
+app.get('/invalid-json', (request, res) => {
+ res.set('content-type', 'application/json');
+ // sample invalid json taken from https://github.com/swagger-api/swagger-ui/issues/1354
+ res.send(
+ ")]}', {'header':{'code':200,'text':'OK','version':'1.0'},'data':'some data'}"
+ );
+});
+
+app.get('/invalid-json-forbidden', (request, res) => {
+ res.set('content-type', 'application/json');
+ res.status(403).send('Forbidden');
});
-app.post('/auth', function(req, res) {
- var auth = req.headers.authorization,
- parts = auth.split(' '),
- credentials = new Buffer(parts[1], 'base64').toString().split(':'),
- user = credentials[0],
- pass = credentials[1];
+app.get('/text', (request, res) => {
+ res.send('just some text');
+});
+
+app.get('/basic-auth', basicAuth('tobi', 'learnboost'), (request, res) => {
+ res.end('you win!');
+});
- res.send({ user : user, pass : pass });
+app.get('/basic-auth/again', basicAuth('tobi', ''), (request, res) => {
+ res.end('you win again!');
});
-app.get('/error', function(req, res){
+app.post('/auth', basicAuth('foo', 'bar'), (request, res) => {
+ const auth = request.headers.authorization;
+ const parts = auth.split(' ');
+ const credentials = Buffer.from(parts[1], 'base64').toString().split(':');
+ const user = credentials[0];
+ const pass = credentials[1];
+
+ res.send({ user, pass });
+});
+
+app.get('/error', (request, res) => {
res.status(500).send('boom');
});
-app.get('/unauthorized', function(req, res){
+app.get('/unauthorized', (request, res) => {
res.sendStatus(401);
});
-app.get('/bad-request', function(req, res){
+app.get('/bad-request', (request, res) => {
res.sendStatus(400);
});
-app.get('/not-acceptable', function(req, res){
+app.get('/not-acceptable', (request, res) => {
res.sendStatus(406);
});
-app.get('/no-content', function(req, res){
+app.get('/no-content', (request, res) => {
res.sendStatus(204);
});
-app.delete('/no-content', function(req, res){
+app.delete('/no-content', (request, res) => {
res.set('content-type', 'application/json');
res.sendStatus(204);
});
-app.get('/arraybuffer', function(req, res) {
- var content = new ArrayBuffer(1000);
+app.post('/created', (request, res) => {
+ res.status(201).send('created');
+});
+
+app.post('/unprocessable-entity', (request, res) => {
+ res.status(422).send('unprocessable entity');
+});
+
+app.get('/arraybuffer', (request, res) => {
+ const content = new ArrayBuffer(1000);
res.set('Content-Type', 'application/vnd.superagent');
res.send(content);
});
-app.listen(process.env.ZUUL_PORT);
+app.get('/arraybuffer-unauthorized', (request, res) => {
+ res.set('Content-Type', 'application/json');
+ res
+ .status(401)
+ .send('{"message":"Authorization has been denied for this request."}');
+});
+
+app.post('/empty-body', bodyParser.text(), (request, res) => {
+ if (
+ typeof request.body === 'object' &&
+ Object.keys(request.body).length === 0
+ ) {
+ res.sendStatus(204);
+ } else {
+ res.sendStatus(400);
+ }
+});
+
+app.get('/collection-json', (request, res) => {
+ res.set('content-type', 'application/vnd.collection+json');
+ res.send({ name: 'chewbacca' });
+});
+
+app.get('/invalid-json', (request, res) => {
+ res.set('content-type', 'application/json');
+ // sample invalid json taken from https://github.com/swagger-api/swagger-ui/issues/1354
+ res.send(
+ ")]}', {'header':{'code':200,'text':'OK','version':'1.0'},'data':'some data'}"
+ );
+});
+
+app.options('/options/echo/body', bodyParser.json(), (request, res) => {
+ res.send(request.body);
+});
+
+app.get('/cookie-redirect', (request, res) => {
+ res.set('Set-Cookie', 'replaced=yes');
+ res.append('Set-Cookie', 'from-redir=1', true);
+ res.redirect(303, '/show-cookies');
+});
+
+app.get('/set-cookie', (request, res) => {
+ res.cookie('replaced', 'no');
+ res.cookie('persist', '123');
+ res.send('ok');
+});
+
+app.get('/show-cookies', (request, res) => {
+ res.set('content-type', 'text/plain');
+ res.send(request.headers.cookie);
+});
+
+app.put('/redirect-303', (request, res) => {
+ res.redirect(303, '/reply-method');
+});
+
+app.put('/redirect-307', (request, res) => {
+ res.redirect(307, '/reply-method');
+});
+
+app.put('/redirect-308', (request, res) => {
+ res.redirect(308, '/reply-method');
+});
+
+app.all('/reply-method', (request, res) => {
+ res.send(`method=${request.method.toLowerCase()}`);
+});
+
+app.get('/tobi', (request, res) => {
+ res.send('tobi');
+});
+
+app.get('/relative', (request, res) => {
+ res.redirect('tobi');
+});
+
+app.get('/relative/sub', (request, res) => {
+ res.redirect('../tobi');
+});
+
+app.get('/header', (request, res) => {
+ res.redirect('/header/2');
+});
+
+app.post('/header', (request, res) => {
+ res.redirect('/header/2');
+});
+
+app.get('/header/2', (request, res) => {
+ res.send(request.headers);
+});
+
+app.get('/bad-redirect', (request, res) => {
+ res.status(307).end();
+});
+
+app.all('/ua', (request, res) => {
+ const { headers } = request;
+ if (process.env.HTTP2_TEST) {
+ for (const name of Object.keys(headers)) {
+ if (isPseudoHeader(name)) {
+ delete headers[name];
+ }
+ }
+ }
+
+ res.writeHead(200, headers);
+ request.pipe(res);
+});
+
+app.get('/manny', (request, res) => {
+ res.status(200).json({ name: 'manny' });
+});
+
+function serveImageWithType(res, type) {
+ const img = fs.readFileSync(
+ path.join(__dirname, '../node/fixtures/test.png')
+ );
+ res.writeHead(200, { 'Content-Type': type });
+ res.end(img, 'binary');
+}
+
+app.get('/image', (request, res) => {
+ serveImageWithType(res, 'image/png');
+});
+app.get('/image-as-octets', (request, res) => {
+ serveImageWithType(res, 'application/octet-stream');
+});
+
+app.get('/binary-data', (request, res) => {
+ const binData = fs.readFileSync(
+ path.join(__dirname, '../node/fixtures/test.aac')
+ );
+ res.writeHead(200, { 'Content-type': 'audio/aac' });
+ res.end(binData, 'binary');
+});
+
+app.get('/chunked-json', (request, res) => {
+ res.set('content-type', 'application/json');
+ res.set('Transfer-Encoding', 'chunked');
+
+ let chunk = 0;
+ const interval = setInterval(() => {
+ chunk++;
+ if (chunk === 1) res.write(`{ "name_${chunk}": "`);
+ if (chunk > 1) res.write(`value_${chunk}", "name_${chunk}": "`);
+ if (chunk === 10) {
+ clearInterval(interval);
+ res.write(`value_${chunk}"}`);
+ res.end();
+ }
+ }, 10);
+});
+
+app.get('/if-mod', (request, res) => {
+ if (request.header('if-modified-since')) {
+ res.status(304).end();
+ } else {
+ res.send(`${Date.now()}`);
+ }
+});
+
+const called = {};
+app.get('/error/ok/:id', (request, res) => {
+ if (request.query.qs !== 'present') {
+ return res.status(400).end('query string lost');
+ }
+
+ const { id } = request.params;
+ if (!called[id]) {
+ called[id] = true;
+ res.status(500).send('boom');
+ } else {
+ res.send(request.headers);
+ delete called[id];
+ }
+});
+
+app.get('/delay/:ms/ok/:id', (request, res) => {
+ const { id } = request.params;
+ if (!called[id]) {
+ called[id] = true;
+ const ms = Math.trunc(request.params.ms);
+ setTimeout(() => {
+ res.sendStatus(200);
+ }, ms);
+ } else {
+ res.send(`ok = ${request.url}`);
+ delete called[id];
+ }
+});
+
+app.get('/error/redirect/:id', (request, res) => {
+ const { id } = request.params;
+ if (!called[id]) {
+ called[id] = true;
+ res.status(500).send('boom');
+ } else {
+ res.redirect('/movies');
+ delete called[id];
+ }
+});
+
+app.get('/error/redirect-error:id', (request, res) => {
+ const { id } = request.params;
+ if (!called[id]) {
+ called[id] = true;
+ res.status(500).send('boom');
+ } else {
+ res.redirect('/error');
+ delete called[id];
+ }
+});
+
+const server = http.createServer(app);
+server.listen(process.env.ZUUL_PORT);
diff --git a/test/support/setup.js b/test/support/setup.js
new file mode 100644
index 000000000..ee251837f
--- /dev/null
+++ b/test/support/setup.js
@@ -0,0 +1,36 @@
+require('should');
+require('should-http');
+
+const getPort = require('get-port');
+
+let NODE;
+let uri;
+
+async function getSetup() {
+ if (NODE && uri) {
+ return { NODE, uri };
+ }
+
+ NODE = true;
+ if (typeof window !== 'undefined') {
+ NODE = false;
+ uri = `//${window.location.host}`;
+ } else {
+ try {
+ const port = await getPort();
+
+ // check that another call to the function hasn't set the uri already
+ if (!uri) {
+ process.env.ZUUL_PORT = port;
+ uri = `http://localhost:${process.env.ZUUL_PORT}`;
+ require('./server');
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ return { NODE, uri };
+}
+
+module.exports = getSetup;
diff --git a/test/timeout.js b/test/timeout.js
new file mode 100644
index 000000000..7acf39b5c
--- /dev/null
+++ b/test/timeout.js
@@ -0,0 +1,140 @@
+const assert = require('assert');
+const getSetup = require('./support/setup');
+
+const request = require('./support/client');
+
+describe('.timeout(ms)', function () {
+ let setup;
+ let base;
+
+ before(async () => {
+ setup = await getSetup();
+ base = setup.uri;
+ });
+
+ this.timeout(15_000);
+
+ describe('when timeout is exceeded', () => {
+ it('should error', (done) => {
+ request
+ .get(`${base}/delay/500`)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ });
+
+ it('should error in promise interface ', (done) => {
+ request
+ .get(`${base}/delay/500`)
+ .timeout(150)
+ .catch((err) => {
+ assert(err, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof err.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', err.code, 'expected abort error code');
+ done();
+ });
+ });
+
+ it('should handle gzip timeout', (done) => {
+ request
+ .get(`${base}/delay/zip`)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ });
+
+ it('should handle buffer timeout', (done) => {
+ request
+ .get(`${base}/delay/json`)
+ .buffer(true)
+ .timeout(150)
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ });
+
+ it('should error on deadline', (done) => {
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ deadline: 150 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ done();
+ });
+ });
+
+ it('should support setting individual options', (done) => {
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ deadline: 10 })
+ .timeout({ response: 99_999 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ assert.equal('ETIME', error.errno);
+ done();
+ });
+ });
+
+ it('should error on response', (done) => {
+ request
+ .get(`${base}/delay/500`)
+ .timeout({ response: 150 })
+ .end((error, res) => {
+ assert(error, 'expected an error');
+ assert.equal(
+ 'number',
+ typeof error.timeout,
+ 'expected an error with .timeout'
+ );
+ assert.equal('ECONNABORTED', error.code, 'expected abort error code');
+ assert.equal('ETIMEDOUT', error.errno);
+ done();
+ });
+ });
+
+ it('should accept slow body with fast response', (done) => {
+ request
+ .get(`${base}/delay/slowbody`)
+ .timeout({ response: 1000 })
+ .on('progress', () => {
+ // This only makes the test faster without relying on arbitrary timeouts
+ request.get(`${base}/delay/slowbody/finish`).end();
+ })
+ .end(done);
+ });
+ });
+});
diff --git a/test/use.js b/test/use.js
new file mode 100644
index 000000000..541cffc88
--- /dev/null
+++ b/test/use.js
@@ -0,0 +1,95 @@
+const assert = require('assert');
+const getSetup = require('./support/setup');
+
+const request = require('./support/client');
+
+describe('request', function () {
+ let setup;
+ let uri;
+
+ before(async function () {
+ setup = await getSetup();
+ uri = setup.uri;
+ });
+
+ this.timeout(20_000);
+ describe('use', () => {
+ it('should use plugin success', (done) => {
+ const now = `${Date.now()}`;
+ function uuid(request_) {
+ request_.set('X-UUID', now);
+ return request_;
+ }
+
+ function prefix(request_) {
+ request_.url = uri + request_.url;
+ return request_;
+ }
+
+ request
+ .get('/echo')
+ .use(uuid)
+ .use(prefix)
+ .end((error, res) => {
+ assert.strictEqual(res.statusCode, 200);
+ assert.equal(res.get('X-UUID'), now);
+ done();
+ });
+ });
+ });
+});
+
+describe('subclass', () => {
+ let OriginalRequest;
+ beforeEach(() => {
+ OriginalRequest = request.Request;
+ });
+ afterEach(() => {
+ request.Request = OriginalRequest;
+ });
+
+ it('should be an instance of Request', () => {
+ const request_ = request.get('/');
+ assert(request_ instanceof request.Request);
+ });
+
+ it('should use patched subclass', () => {
+ assert(OriginalRequest);
+
+ let constructorCalled;
+ let sendCalled;
+ function NewRequest(...args) {
+ constructorCalled = true;
+ OriginalRequest.apply(this, args);
+ }
+
+ NewRequest.prototype = Object.create(OriginalRequest.prototype);
+ NewRequest.prototype.send = function () {
+ sendCalled = true;
+ return this;
+ };
+
+ request.Request = NewRequest;
+
+ const request_ = request.get('/').send();
+ assert(constructorCalled);
+ assert(sendCalled);
+ assert(request_ instanceof NewRequest);
+ assert(request_ instanceof OriginalRequest);
+ });
+
+ it('should use patched subclass in agent too', () => {
+ if (!request.agent) return; // Node-only
+
+ function NewRequest(...args) {
+ OriginalRequest.apply(this, args);
+ }
+
+ NewRequest.prototype = Object.create(OriginalRequest.prototype);
+ request.Request = NewRequest;
+
+ const request_ = request.agent().del('http://test.com/');
+ assert(request_ instanceof NewRequest);
+ assert(request_ instanceof OriginalRequest);
+ });
+});