Update node_modules

This commit is contained in:
crazy-max 2020-01-17 09:12:33 +00:00
parent cd24e4fd44
commit a67e5f4eeb
45 changed files with 7571 additions and 3066 deletions

33
node_modules/qs/.editorconfig generated vendored Normal file
View File

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

1
node_modules/qs/.eslintignore generated vendored Normal file
View File

@ -0,0 +1 @@
dist

21
node_modules/qs/.eslintrc generated vendored Normal file
View File

@ -0,0 +1,21 @@
{
"root": true,
"extends": "@ljharb",
"rules": {
"complexity": 0,
"consistent-return": 1,
"func-name-matching": 0,
"id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
"indent": [2, 4],
"max-lines-per-function": [2, { "max": 150 }],
"max-params": [2, 14],
"max-statements": [2, 52],
"multiline-comment-style": 0,
"no-continue": 1,
"no-magic-numbers": 0,
"no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
"operator-linebreak": [2, "before"],
}
}

12
node_modules/qs/.github/FUNDING.yml generated vendored Normal file
View File

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

285
node_modules/qs/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,285 @@
## **6.9.1**
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
- [Fix] `parse`: with comma true, do not split non-string values (#334)
- [meta] add `funding` field
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`
- [Tests] use shared travis-ci config
## **6.9.0**
- [New] `parse`/`stringify`: Pass extra key/value argument to `decoder` (#333)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `evalmd`
- [Tests] `parse`: add passing `arrayFormat` tests
- [Tests] add `posttest` using `npx aud` to run `npm audit` without a lockfile
- [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
## **6.8.0**
- [New] add `depth=false` to preserve the original key; [Fix] `depth=0` should preserve the original key (#326)
- [New] [Fix] stringify symbols and bigints
- [Fix] ensure node 0.12 can stringify Symbols
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
- [Refactor] `formats`: tiny bit of cleanup.
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `safe-publish-latest`, `iconv-lite`, `tape`
- [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended (#326)
- [Tests] use `eclint` instead of `editorconfig-tools`
- [docs] readme: add security note
- [meta] add github sponsorship
- [meta] add FUNDING.yml
- [meta] Clean up license text so its properly detected as BSD-3-Clause
## **6.7.0**
- [New] `stringify`/`parse`: add `comma` as an `arrayFormat` option (#276, #219)
- [Fix] correctly parse nested arrays (#212)
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source, also with an array source
- [Robustness] `stringify`: cache `Object.prototype.hasOwnProperty`
- [Refactor] `utils`: `isBuffer`: small tweak; add tests
- [Refactor] use cached `Array.isArray`
- [Refactor] `parse`/`stringify`: make a function to normalize the options
- [Refactor] `utils`: reduce observable [[Get]]s
- [Refactor] `stringify`/`utils`: cache `Array.isArray`
- [Tests] always use `String(x)` over `x.toString()`
- [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
- [Tests] temporarily allow coverage to fail
## **6.6.0**
- [New] Add support for iso-8859-1, utf8 "sentinel" and numeric entities (#268)
- [New] move two-value combine to a `utils` function (#189)
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]` (#260)
- [Fix] `stringify`: do not crash in an obscure combo of `interpretNumericEntities`, a bad custom `decoder`, & `iso-8859-1`
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
- [refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
- [Refactor] `parse`: only need to reassign the var once
- [Refactor] `parse`/`stringify`: clean up `charset` options checking; fix defaults
- [Refactor] add missing defaults
- [Refactor] `parse`: one less `concat` call
- [Refactor] `utils`: `compactQueue`: make it explicitly side-effecting
- [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`, `iconv-lite`, `safe-publish-latest`, `tape`
- [Tests] up to `node` `v10.10`, `v9.11`, `v8.12`, `v6.14`, `v4.9`; pin included builds to LTS
## **6.5.2**
- [Fix] use `safer-buffer` instead of `Buffer` constructor
- [Refactor] utils: `module.exports` one thing, instead of mutating `exports` (#230)
- [Dev Deps] update `browserify`, `eslint`, `iconv-lite`, `safer-buffer`, `tape`, `browserify`
## **6.5.1**
- [Fix] Fix parsing & compacting very deep objects (#224)
- [Refactor] name utils functions
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`
- [Tests] up to `node` `v8.4`; use `nvm install-latest-npm` so newer npm doesnt break older node
- [Tests] Use precise dist for Node.js 0.6 runtime (#225)
- [Tests] make 0.6 required, now that its passing
- [Tests] on `node` `v8.2`; fix npm on node 0.6
## **6.5.0**
- [New] add `utils.assign`
- [New] pass default encoder/decoder to custom encoder/decoder functions (#206)
- [New] `parse`/`stringify`: add `ignoreQueryPrefix`/`addQueryPrefix` options, respectively (#213)
- [Fix] Handle stringifying empty objects with addQueryPrefix (#217)
- [Fix] do not mutate `options` argument (#207)
- [Refactor] `parse`: cache index to reuse in else statement (#182)
- [Docs] add various badges to readme (#208)
- [Dev Deps] update `eslint`, `browserify`, `iconv-lite`, `tape`
- [Tests] up to `node` `v8.1`, `v7.10`, `v6.11`; npm v4.6 breaks on node < v1; npm v5+ breaks on node < v4
- [Tests] add `editorconfig-tools`
## **6.4.0**
- [New] `qs.stringify`: add `encodeValuesOnly` option
- [Fix] follow `allowPrototypes` option during merge (#201, #201)
- [Fix] support keys starting with brackets (#202, #200)
- [Fix] chmod a-x
- [Dev Deps] update `eslint`
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
- [eslint] reduce warnings
## **6.3.2**
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Dev Deps] update `eslint`
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.3.1**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties (thanks, @snyk!)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `iconv-lite`, `qs-iconv`, `tape`
- [Tests] on all node minors; improve test matrix
- [Docs] document stringify option `allowDots` (#195)
- [Docs] add empty object and array values example (#195)
- [Docs] Fix minor inconsistency/typo (#192)
- [Docs] document stringify option `sort` (#191)
- [Refactor] `stringify`: throw faster with an invalid encoder
- [Refactor] remove unnecessary escapes (#184)
- Remove contributing.md, since `qs` is no longer part of `hapi` (#183)
## **6.3.0**
- [New] Add support for RFC 1738 (#174, #173)
- [New] `stringify`: Add `serializeDate` option to customize Date serialization (#159)
- [Fix] ensure `utils.merge` handles merging two arrays
- [Refactor] only constructors should be capitalized
- [Refactor] capitalized var names are for constructors only
- [Refactor] avoid using a sparse array
- [Robustness] `formats`: cache `String#replace`
- [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`; add `safe-publish-latest`
- [Tests] up to `node` `v6.8`, `v4.6`; improve test matrix
- [Tests] flesh out arrayLimit/arrayFormat tests (#107)
- [Tests] skip Object.create tests when null objects are not available
- [Tests] Turn on eslint for test files (#175)
## **6.2.3**
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.2.2**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
## **6.2.1**
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
- [Refactor] Be explicit and use `Object.prototype.hasOwnProperty.call`
- [Tests] remove `parallelshell` since it does not reliably report failures
- [Tests] up to `node` `v6.3`, `v5.12`
- [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`, `qs-iconv`
## [**6.2.0**](https://github.com/ljharb/qs/issues?milestone=36&state=closed)
- [New] pass Buffers to the encoder/decoder directly (#161)
- [New] add "encoder" and "decoder" options, for custom param encoding/decoding (#160)
- [Fix] fix compacting of nested sparse arrays (#150)
## **6.1.2
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.1.1**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
## [**6.1.0**](https://github.com/ljharb/qs/issues?milestone=35&state=closed)
- [New] allowDots option for `stringify` (#151)
- [Fix] "sort" option should work at a depth of 3 or more (#151)
- [Fix] Restore `dist` directory; will be removed in v7 (#148)
## **6.0.4**
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
- [Fix] chmod a-x
- [Fix] support keys starting with brackets (#202, #200)
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
## **6.0.3**
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
- [Fix] Restore `dist` directory; will be removed in v7 (#148)
## [**6.0.2**](https://github.com/ljharb/qs/issues?milestone=33&state=closed)
- Revert ES6 requirement and restore support for node down to v0.8.
## [**6.0.1**](https://github.com/ljharb/qs/issues?milestone=32&state=closed)
- [**#127**](https://github.com/ljharb/qs/pull/127) Fix engines definition in package.json
## [**6.0.0**](https://github.com/ljharb/qs/issues?milestone=31&state=closed)
- [**#124**](https://github.com/ljharb/qs/issues/124) Use ES6 and drop support for node < v4
## **5.2.1**
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
## [**5.2.0**](https://github.com/ljharb/qs/issues?milestone=30&state=closed)
- [**#64**](https://github.com/ljharb/qs/issues/64) Add option to sort object keys in the query string
## [**5.1.0**](https://github.com/ljharb/qs/issues?milestone=29&state=closed)
- [**#117**](https://github.com/ljharb/qs/issues/117) make URI encoding stringified results optional
- [**#106**](https://github.com/ljharb/qs/issues/106) Add flag `skipNulls` to optionally skip null values in stringify
## [**5.0.0**](https://github.com/ljharb/qs/issues?milestone=28&state=closed)
- [**#114**](https://github.com/ljharb/qs/issues/114) default allowDots to false
- [**#100**](https://github.com/ljharb/qs/issues/100) include dist to npm
## [**4.0.0**](https://github.com/ljharb/qs/issues?milestone=26&state=closed)
- [**#98**](https://github.com/ljharb/qs/issues/98) make returning plain objects and allowing prototype overwriting properties optional
## [**3.1.0**](https://github.com/ljharb/qs/issues?milestone=24&state=closed)
- [**#89**](https://github.com/ljharb/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
## [**3.0.0**](https://github.com/ljharb/qs/issues?milestone=23&state=closed)
- [**#80**](https://github.com/ljharb/qs/issues/80) qs.parse silently drops properties
- [**#77**](https://github.com/ljharb/qs/issues/77) Perf boost
- [**#60**](https://github.com/ljharb/qs/issues/60) Add explicit option to disable array parsing
- [**#74**](https://github.com/ljharb/qs/issues/74) Bad parse when turning array into object
- [**#81**](https://github.com/ljharb/qs/issues/81) Add a `filter` option
- [**#68**](https://github.com/ljharb/qs/issues/68) Fixed issue with recursion and passing strings into objects.
- [**#66**](https://github.com/ljharb/qs/issues/66) Add mixed array and object dot notation support Closes: #47
- [**#76**](https://github.com/ljharb/qs/issues/76) RFC 3986
- [**#85**](https://github.com/ljharb/qs/issues/85) No equal sign
- [**#84**](https://github.com/ljharb/qs/issues/84) update license attribute
## [**2.4.1**](https://github.com/ljharb/qs/issues?milestone=20&state=closed)
- [**#73**](https://github.com/ljharb/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
## [**2.4.0**](https://github.com/ljharb/qs/issues?milestone=19&state=closed)
- [**#70**](https://github.com/ljharb/qs/issues/70) Add arrayFormat option
## [**2.3.3**](https://github.com/ljharb/qs/issues?milestone=18&state=closed)
- [**#59**](https://github.com/ljharb/qs/issues/59) make sure array indexes are >= 0, closes #57
- [**#58**](https://github.com/ljharb/qs/issues/58) make qs usable for browser loader
## [**2.3.2**](https://github.com/ljharb/qs/issues?milestone=17&state=closed)
- [**#55**](https://github.com/ljharb/qs/issues/55) allow merging a string into an object
## [**2.3.1**](https://github.com/ljharb/qs/issues?milestone=16&state=closed)
- [**#52**](https://github.com/ljharb/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
## [**2.3.0**](https://github.com/ljharb/qs/issues?milestone=15&state=closed)
- [**#50**](https://github.com/ljharb/qs/issues/50) add option to omit array indices, closes #46
## [**2.2.5**](https://github.com/ljharb/qs/issues?milestone=14&state=closed)
- [**#39**](https://github.com/ljharb/qs/issues/39) Is there an alternative to Buffer.isBuffer?
- [**#49**](https://github.com/ljharb/qs/issues/49) refactor utils.merge, fixes #45
- [**#41**](https://github.com/ljharb/qs/issues/41) avoid browserifying Buffer, for #39
## [**2.2.4**](https://github.com/ljharb/qs/issues?milestone=13&state=closed)
- [**#38**](https://github.com/ljharb/qs/issues/38) how to handle object keys beginning with a number
## [**2.2.3**](https://github.com/ljharb/qs/issues?milestone=12&state=closed)
- [**#37**](https://github.com/ljharb/qs/issues/37) parser discards first empty value in array
- [**#36**](https://github.com/ljharb/qs/issues/36) Update to lab 4.x
## [**2.2.2**](https://github.com/ljharb/qs/issues?milestone=11&state=closed)
- [**#33**](https://github.com/ljharb/qs/issues/33) Error when plain object in a value
- [**#34**](https://github.com/ljharb/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
- [**#24**](https://github.com/ljharb/qs/issues/24) Changelog? Semver?
## [**2.2.1**](https://github.com/ljharb/qs/issues?milestone=10&state=closed)
- [**#32**](https://github.com/ljharb/qs/issues/32) account for circular references properly, closes #31
- [**#31**](https://github.com/ljharb/qs/issues/31) qs.parse stackoverflow on circular objects
## [**2.2.0**](https://github.com/ljharb/qs/issues?milestone=9&state=closed)
- [**#26**](https://github.com/ljharb/qs/issues/26) Don't use Buffer global if it's not present
- [**#30**](https://github.com/ljharb/qs/issues/30) Bug when merging non-object values into arrays
- [**#29**](https://github.com/ljharb/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
- [**#23**](https://github.com/ljharb/qs/issues/23) Ability to not limit parameters?
## [**2.1.0**](https://github.com/ljharb/qs/issues?milestone=8&state=closed)
- [**#22**](https://github.com/ljharb/qs/issues/22) Enable using a RegExp as delimiter
## [**2.0.0**](https://github.com/ljharb/qs/issues?milestone=7&state=closed)
- [**#18**](https://github.com/ljharb/qs/issues/18) Why is there arrayLimit?
- [**#20**](https://github.com/ljharb/qs/issues/20) Configurable parametersLimit
- [**#21**](https://github.com/ljharb/qs/issues/21) make all limits optional, for #18, for #20
## [**1.2.2**](https://github.com/ljharb/qs/issues?milestone=6&state=closed)
- [**#19**](https://github.com/ljharb/qs/issues/19) Don't overwrite null values
## [**1.2.1**](https://github.com/ljharb/qs/issues?milestone=5&state=closed)
- [**#16**](https://github.com/ljharb/qs/issues/16) ignore non-string delimiters
- [**#15**](https://github.com/ljharb/qs/issues/15) Close code block
## [**1.2.0**](https://github.com/ljharb/qs/issues?milestone=4&state=closed)
- [**#12**](https://github.com/ljharb/qs/issues/12) Add optional delim argument
- [**#13**](https://github.com/ljharb/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
## [**1.1.0**](https://github.com/ljharb/qs/issues?milestone=3&state=closed)
- [**#7**](https://github.com/ljharb/qs/issues/7) Empty values of a POST array disappear after being submitted
- [**#9**](https://github.com/ljharb/qs/issues/9) Should not omit equals signs (=) when value is null
- [**#6**](https://github.com/ljharb/qs/issues/6) Minor grammar fix in README
## [**1.0.2**](https://github.com/ljharb/qs/issues?milestone=2&state=closed)
- [**#5**](https://github.com/ljharb/qs/issues/5) array holes incorrectly copied into object on large index

29
node_modules/qs/LICENSE.md generated vendored Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/contributors)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

598
node_modules/qs/README.md generated vendored Normal file
View File

@ -0,0 +1,598 @@
# qs <sup>[![Version Badge][2]][1]</sup>
[![Build Status][3]][4]
[![dependency status][5]][6]
[![dev dependency status][7]][8]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]
[![npm badge][11]][1]
A querystring parsing and stringifying library with some added security.
Lead Maintainer: [Jordan Harband](https://github.com/ljharb)
The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).
## Usage
```javascript
var qs = require('qs');
var assert = require('assert');
var obj = qs.parse('a=c');
assert.deepEqual(obj, { a: 'c' });
var str = qs.stringify(obj);
assert.equal(str, 'a=c');
```
### Parsing Objects
[](#preventEval)
```javascript
qs.parse(string, [options]);
```
**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.
For example, the string `'foo[bar]=baz'` converts to:
```javascript
assert.deepEqual(qs.parse('foo[bar]=baz'), {
foo: {
bar: 'baz'
}
});
```
When using the `plainObjects` option the parsed value is returned as a null object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:
```javascript
var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });
```
By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties. *WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.
```javascript
var protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });
assert.deepEqual(protoObject, { a: { hasOwnProperty: 'b' } });
```
URI encoded strings work too:
```javascript
assert.deepEqual(qs.parse('a%5Bb%5D=c'), {
a: { b: 'c' }
});
```
You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
```javascript
assert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), {
foo: {
bar: {
baz: 'foobarbaz'
}
}
});
```
By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like
`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
```javascript
var expected = {
a: {
b: {
c: {
d: {
e: {
f: {
'[g][h][i]': 'j'
}
}
}
}
}
}
};
var string = 'a[b][c][d][e][f][g][h][i]=j';
assert.deepEqual(qs.parse(string), expected);
```
This depth can be overridden by passing a `depth` option to `qs.parse(string, [options])`:
```javascript
var deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
assert.deepEqual(deep, { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } });
```
The depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number.
For similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option:
```javascript
var limited = qs.parse('a=b&c=d', { parameterLimit: 1 });
assert.deepEqual(limited, { a: 'b' });
```
To bypass the leading question mark, use `ignoreQueryPrefix`:
```javascript
var prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });
assert.deepEqual(prefixed, { a: 'b', c: 'd' });
```
An optional delimiter can also be passed:
```javascript
var delimited = qs.parse('a=b;c=d', { delimiter: ';' });
assert.deepEqual(delimited, { a: 'b', c: 'd' });
```
Delimiters can be a regular expression too:
```javascript
var regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
assert.deepEqual(regexed, { a: 'b', c: 'd', e: 'f' });
```
Option `allowDots` can be used to enable dot notation:
```javascript
var withDots = qs.parse('a.b=c', { allowDots: true });
assert.deepEqual(withDots, { a: { b: 'c' } });
```
If you have to deal with legacy browsers or services, there's
also support for decoding percent-encoded octets as iso-8859-1:
```javascript
var oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
assert.deepEqual(oldCharset, { a: '§' });
```
Some services add an initial `utf8=✓` value to forms so that old
Internet Explorer versions are more likely to submit the form as
utf-8. Additionally, the server can check the value against wrong
encodings of the checkmark character and detect that a query string
or `application/x-www-form-urlencoded` body was *not* sent as
utf-8, eg. if the form had an `accept-charset` parameter or the
containing page had a different character set.
**qs** supports this mechanism via the `charsetSentinel` option.
If specified, the `utf8` parameter will be omitted from the
returned object. It will be used to switch to `iso-8859-1`/`utf-8`
mode depending on how the checkmark is encoded.
**Important**: When you specify both the `charset` option and the
`charsetSentinel` option, the `charset` will be overridden when
the request contains a `utf8` parameter from which the actual
charset can be deduced. In that sense the `charset` will behave
as the default charset rather than the authoritative charset.
```javascript
var detectedAsUtf8 = qs.parse('utf8=%E2%9C%93&a=%C3%B8', {
charset: 'iso-8859-1',
charsetSentinel: true
});
assert.deepEqual(detectedAsUtf8, { a: 'ø' });
// Browsers encode the checkmark as &#10003; when submitting as iso-8859-1:
var detectedAsIso8859_1 = qs.parse('utf8=%26%2310003%3B&a=%F8', {
charset: 'utf-8',
charsetSentinel: true
});
assert.deepEqual(detectedAsIso8859_1, { a: 'ø' });
```
If you want to decode the `&#...;` syntax to the actual character,
you can specify the `interpretNumericEntities` option as well:
```javascript
var detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
charset: 'iso-8859-1',
interpretNumericEntities: true
});
assert.deepEqual(detectedAsIso8859_1, { a: '☺' });
```
It also works when the charset has been detected in `charsetSentinel`
mode.
### Parsing Arrays
**qs** can also parse arrays using a similar `[]` notation:
```javascript
var withArray = qs.parse('a[]=b&a[]=c');
assert.deepEqual(withArray, { a: ['b', 'c'] });
```
You may specify an index as well:
```javascript
var withIndexes = qs.parse('a[1]=c&a[0]=b');
assert.deepEqual(withIndexes, { a: ['b', 'c'] });
```
Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number
to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving
their order:
```javascript
var noSparse = qs.parse('a[1]=b&a[15]=c');
assert.deepEqual(noSparse, { a: ['b', 'c'] });
```
Note that an empty string is also a value, and will be preserved:
```javascript
var withEmptyString = qs.parse('a[]=&a[]=b');
assert.deepEqual(withEmptyString, { a: ['', 'b'] });
var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');
assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });
```
**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will
instead be converted to an object with the index as the key. This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
```javascript
var withMaxIndex = qs.parse('a[100]=b');
assert.deepEqual(withMaxIndex, { a: { '100': 'b' } });
```
This limit can be overridden by passing an `arrayLimit` option:
```javascript
var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });
assert.deepEqual(withArrayLimit, { a: { '1': 'b' } });
```
To disable array parsing entirely, set `parseArrays` to `false`.
```javascript
var noParsingArrays = qs.parse('a[]=b', { parseArrays: false });
assert.deepEqual(noParsingArrays, { a: { '0': 'b' } });
```
If you mix notations, **qs** will merge the two items into an object:
```javascript
var mixedNotation = qs.parse('a[0]=b&a[b]=c');
assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });
```
You can also create arrays of objects:
```javascript
var arraysOfObjects = qs.parse('a[][b]=c');
assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
```
Some people use comma to join array, **qs** can parse it:
```javascript
var arraysOfObjects = qs.parse('a=b,c', { comma: true })
assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
```
(_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
### Stringifying
[](#preventEval)
```javascript
qs.stringify(object, [options]);
```
When stringifying, **qs** by default URI encodes output. Objects are stringified as you would expect:
```javascript
assert.equal(qs.stringify({ a: 'b' }), 'a=b');
assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
```
This encoding can be disabled by setting the `encode` option to `false`:
```javascript
var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });
assert.equal(unencoded, 'a[b]=c');
```
Encoding can be disabled for keys by setting the `encodeValuesOnly` option to `true`:
```javascript
var encodedValues = qs.stringify(
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
{ encodeValuesOnly: true }
);
assert.equal(encodedValues,'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h');
```
This encoding can also be replaced by a custom encoding method set as `encoder` option:
```javascript
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) {
// Passed in values `a`, `b`, `c`
return // Return encoded string
}})
```
_(Note: the `encoder` option does not apply if `encode` is `false`)_
Analogue to the `encoder` there is a `decoder` option for `parse` to override decoding of properties and values:
```javascript
var decoded = qs.parse('x=z', { decoder: function (str) {
// Passed in values `x`, `z`
return // Return decoded string
}})
```
You can encode keys and values using different logic by using the type argument provided to the encoder:
```javascript
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
if (type === 'key') {
return // Encoded key
} else if (type === 'value') {
return // Encoded value
}
}})
```
The type argument is also provided to the decoder:
```javascript
var decoded = qs.parse('x=z', { decoder: function (str, defaultEncoder, charset, type) {
if (type === 'key') {
return // Decoded key
} else if (type === 'value') {
return // Decoded value
}
}})
```
Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
When arrays are stringified, by default they are given explicit indices:
```javascript
qs.stringify({ a: ['b', 'c', 'd'] });
// 'a[0]=b&a[1]=c&a[2]=d'
```
You may override this by setting the `indices` option to `false`:
```javascript
qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
// 'a=b&a=c&a=d'
```
You may use the `arrayFormat` option to specify the format of the output array:
```javascript
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
// 'a[0]=b&a[1]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
// 'a[]=b&a[]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
// 'a=b&a=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
// 'a=b,c'
```
When objects are stringified, by default they use bracket notation:
```javascript
qs.stringify({ a: { b: { c: 'd', e: 'f' } } });
// 'a[b][c]=d&a[b][e]=f'
```
You may override this to use dot notation by setting the `allowDots` option to `true`:
```javascript
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });
// 'a.b.c=d&a.b.e=f'
```
Empty strings and null values will omit the value, but the equals sign (=) remains in place:
```javascript
assert.equal(qs.stringify({ a: '' }), 'a=');
```
Key with no values (such as an empty object or array) will return nothing:
```javascript
assert.equal(qs.stringify({ a: [] }), '');
assert.equal(qs.stringify({ a: {} }), '');
assert.equal(qs.stringify({ a: [{}] }), '');
assert.equal(qs.stringify({ a: { b: []} }), '');
assert.equal(qs.stringify({ a: { b: {}} }), '');
```
Properties that are set to `undefined` will be omitted entirely:
```javascript
assert.equal(qs.stringify({ a: null, b: undefined }), 'a=');
```
The query string may optionally be prepended with a question mark:
```javascript
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
```
The delimiter may be overridden with stringify as well:
```javascript
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
```
If you only want to override the serialization of `Date` objects, you can provide a `serializeDate` option:
```javascript
var date = new Date(7);
assert.equal(qs.stringify({ a: date }), 'a=1970-01-01T00:00:00.007Z'.replace(/:/g, '%3A'));
assert.equal(
qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } }),
'a=7'
);
```
You may use the `sort` option to affect the order of parameter keys:
```javascript
function alphabeticalSort(a, b) {
return a.localeCompare(b);
}
assert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }), 'a=c&b=f&z=y');
```
Finally, you can use the `filter` option to restrict which keys will be included in the stringified output.
If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you
pass an array, it will be used to select properties and array indices for stringification:
```javascript
function filterFunc(prefix, value) {
if (prefix == 'b') {
// Return an `undefined` value to omit a property.
return;
}
if (prefix == 'e[f]') {
return value.getTime();
}
if (prefix == 'e[g][0]') {
return value * 2;
}
return value;
}
qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc });
// 'a=b&c=d&e[f]=123&e[g][0]=4'
qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] });
// 'a=b&e=f'
qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] });
// 'a[0]=b&a[2]=d'
```
### Handling of `null` values
By default, `null` values are treated like empty strings:
```javascript
var withNull = qs.stringify({ a: null, b: '' });
assert.equal(withNull, 'a=&b=');
```
Parsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings.
```javascript
var equalsInsensitive = qs.parse('a&b=');
assert.deepEqual(equalsInsensitive, { a: '', b: '' });
```
To distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null`
values have no `=` sign:
```javascript
var strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
assert.equal(strictNull, 'a&b=');
```
To parse values without `=` back to `null` use the `strictNullHandling` flag:
```javascript
var parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true });
assert.deepEqual(parsedStrictNull, { a: null, b: '' });
```
To completely skip rendering keys with `null` values, use the `skipNulls` flag:
```javascript
var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
assert.equal(nullsSkipped, 'a=b');
```
If you're communicating with legacy systems, you can switch to `iso-8859-1`
using the `charset` option:
```javascript
var iso = qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' });
assert.equal(iso, '%E6=%E6');
```
Characters that don't exist in `iso-8859-1` will be converted to numeric
entities, similar to what browsers do:
```javascript
var numeric = qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' });
assert.equal(numeric, 'a=%26%239786%3B');
```
You can use the `charsetSentinel` option to announce the character by
including an `utf8=✓` parameter with the proper encoding if the checkmark,
similar to what Ruby on Rails and others do when submitting forms.
```javascript
var sentinel = qs.stringify({ a: '☺' }, { charsetSentinel: true });
assert.equal(sentinel, 'utf8=%E2%9C%93&a=%E2%98%BA');
var isoSentinel = qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' });
assert.equal(isoSentinel, 'utf8=%26%2310003%3B&a=%E6');
```
### Dealing with special character sets
By default the encoding and decoding of characters is done in `utf-8`,
and `iso-8859-1` support is also built in via the `charset` parameter.
If you wish to encode querystrings to a different character set (i.e.
[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the
[`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library:
```javascript
var encoder = require('qs-iconv/encoder')('shift_jis');
var shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder });
assert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I');
```
This also works for decoding of query strings:
```javascript
var decoder = require('qs-iconv/decoder')('shift_jis');
var obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder });
assert.deepEqual(obj, { a: 'こんにちは!' });
```
### RFC 3986 and RFC 1738 space encoding
RFC3986 used as default option and encodes ' ' to *%20* which is backward compatible.
In the same time, output can be stringified as per RFC1738 with ' ' equal to '+'.
```
assert.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
```
## Security
Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
[1]: https://npmjs.org/package/qs
[2]: http://versionbadg.es/ljharb/qs.svg
[3]: https://api.travis-ci.org/ljharb/qs.svg
[4]: https://travis-ci.org/ljharb/qs
[5]: https://david-dm.org/ljharb/qs.svg
[6]: https://david-dm.org/ljharb/qs
[7]: https://david-dm.org/ljharb/qs/dev-status.svg
[8]: https://david-dm.org/ljharb/qs?type=dev
[9]: https://ci.testling.com/ljharb/qs.png
[10]: https://ci.testling.com/ljharb/qs
[11]: https://nodei.co/npm/qs.png?downloads=true&stars=true
[license-image]: http://img.shields.io/npm/l/qs.svg
[license-url]: LICENSE
[downloads-image]: http://img.shields.io/npm/dm/qs.svg
[downloads-url]: http://npm-stat.com/charts.html?package=qs

812
node_modules/qs/dist/qs.js generated vendored Normal file
View File

@ -0,0 +1,812 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Qs = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
'use strict';
var replace = String.prototype.replace;
var percentTwenties = /%20/g;
var util = require('./utils');
var Format = {
RFC1738: 'RFC1738',
RFC3986: 'RFC3986'
};
module.exports = util.assign(
{
'default': Format.RFC3986,
formatters: {
RFC1738: function (value) {
return replace.call(value, percentTwenties, '+');
},
RFC3986: function (value) {
return String(value);
}
}
},
Format
);
},{"./utils":5}],2:[function(require,module,exports){
'use strict';
var stringify = require('./stringify');
var parse = require('./parse');
var formats = require('./formats');
module.exports = {
formats: formats,
parse: parse,
stringify: stringify
};
},{"./formats":1,"./parse":3,"./stringify":4}],3:[function(require,module,exports){
'use strict';
var utils = require('./utils');
var has = Object.prototype.hasOwnProperty;
var isArray = Array.isArray;
var defaults = {
allowDots: false,
allowPrototypes: false,
arrayLimit: 20,
charset: 'utf-8',
charsetSentinel: false,
comma: false,
decoder: utils.decode,
delimiter: '&',
depth: 5,
ignoreQueryPrefix: false,
interpretNumericEntities: false,
parameterLimit: 1000,
parseArrays: true,
plainObjects: false,
strictNullHandling: false
};
var interpretNumericEntities = function (str) {
return str.replace(/&#(\d+);/g, function ($0, numberStr) {
return String.fromCharCode(parseInt(numberStr, 10));
});
};
// This is what browsers will submit when the ✓ character occurs in an
// application/x-www-form-urlencoded body and the encoding of the page containing
// the form is iso-8859-1, or when the submitted form has an accept-charset
// attribute of iso-8859-1. Presumably also with other charsets that do not contain
// the ✓ character, such as us-ascii.
var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('&#10003;')
// These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded.
var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
var parseValues = function parseQueryStringValues(str, options) {
var obj = {};
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
var parts = cleanStr.split(options.delimiter, limit);
var skipIndex = -1; // Keep track of where the utf8 sentinel was found
var i;
var charset = options.charset;
if (options.charsetSentinel) {
for (i = 0; i < parts.length; ++i) {
if (parts[i].indexOf('utf8=') === 0) {
if (parts[i] === charsetSentinel) {
charset = 'utf-8';
} else if (parts[i] === isoSentinel) {
charset = 'iso-8859-1';
}
skipIndex = i;
i = parts.length; // The eslint settings do not allow break;
}
}
}
for (i = 0; i < parts.length; ++i) {
if (i === skipIndex) {
continue;
}
var part = parts[i];
var bracketEqualsPos = part.indexOf(']=');
var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1;
var key, val;
if (pos === -1) {
key = options.decoder(part, defaults.decoder, charset, 'key');
val = options.strictNullHandling ? null : '';
} else {
key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
val = options.decoder(part.slice(pos + 1), defaults.decoder, charset, 'value');
}
if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
val = interpretNumericEntities(val);
}
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
val = val.split(',');
}
if (part.indexOf('[]=') > -1) {
val = isArray(val) ? [val] : val;
}
if (has.call(obj, key)) {
obj[key] = utils.combine(obj[key], val);
} else {
obj[key] = val;
}
}
return obj;
};
var parseObject = function (chain, val, options) {
var leaf = val;
for (var i = chain.length - 1; i >= 0; --i) {
var obj;
var root = chain[i];
if (root === '[]' && options.parseArrays) {
obj = [].concat(leaf);
} else {
obj = options.plainObjects ? Object.create(null) : {};
var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
var index = parseInt(cleanRoot, 10);
if (!options.parseArrays && cleanRoot === '') {
obj = { 0: leaf };
} else if (
!isNaN(index)
&& root !== cleanRoot
&& String(index) === cleanRoot
&& index >= 0
&& (options.parseArrays && index <= options.arrayLimit)
) {
obj = [];
obj[index] = leaf;
} else {
obj[cleanRoot] = leaf;
}
}
leaf = obj;
}
return leaf;
};
var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
if (!givenKey) {
return;
}
// Transform dot notation to bracket notation
var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
// The regex chunks
var brackets = /(\[[^[\]]*])/;
var child = /(\[[^[\]]*])/g;
// Get the parent
var segment = options.depth > 0 && brackets.exec(key);
var parent = segment ? key.slice(0, segment.index) : key;
// Stash the parent if it exists
var keys = [];
if (parent) {
// If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties
if (!options.plainObjects && has.call(Object.prototype, parent)) {
if (!options.allowPrototypes) {
return;
}
}
keys.push(parent);
}
// Loop through children appending to the array until we hit depth
var i = 0;
while (options.depth > 0 && (segment = child.exec(key)) !== null && i < options.depth) {
i += 1;
if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) {
if (!options.allowPrototypes) {
return;
}
}
keys.push(segment[1]);
}
// If there's a remainder, just add whatever is left
if (segment) {
keys.push('[' + key.slice(segment.index) + ']');
}
return parseObject(keys, val, options);
};
var normalizeParseOptions = function normalizeParseOptions(opts) {
if (!opts) {
return defaults;
}
if (opts.decoder !== null && opts.decoder !== undefined && typeof opts.decoder !== 'function') {
throw new TypeError('Decoder has to be a function.');
}
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
throw new Error('The charset option must be either utf-8, iso-8859-1, or undefined');
}
var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset;
return {
allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
charset: charset,
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,
decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,
delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
// eslint-disable-next-line no-implicit-coercion, no-extra-parens
depth: (typeof opts.depth === 'number' || opts.depth === false) ? +opts.depth : defaults.depth,
ignoreQueryPrefix: opts.ignoreQueryPrefix === true,
interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities,
parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit,
parseArrays: opts.parseArrays !== false,
plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects,
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
};
};
module.exports = function (str, opts) {
var options = normalizeParseOptions(opts);
if (str === '' || str === null || typeof str === 'undefined') {
return options.plainObjects ? Object.create(null) : {};
}
var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
var obj = options.plainObjects ? Object.create(null) : {};
// Iterate over the keys and setup the new object
var keys = Object.keys(tempObj);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var newObj = parseKeys(key, tempObj[key], options);
obj = utils.merge(obj, newObj, options);
}
return utils.compact(obj);
};
},{"./utils":5}],4:[function(require,module,exports){
'use strict';
var utils = require('./utils');
var formats = require('./formats');
var has = Object.prototype.hasOwnProperty;
var arrayPrefixGenerators = {
brackets: function brackets(prefix) {
return prefix + '[]';
},
comma: 'comma',
indices: function indices(prefix, key) {
return prefix + '[' + key + ']';
},
repeat: function repeat(prefix) {
return prefix;
}
};
var isArray = Array.isArray;
var push = Array.prototype.push;
var pushToArray = function (arr, valueOrArray) {
push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
};
var toISO = Date.prototype.toISOString;
var defaultFormat = formats['default'];
var defaults = {
addQueryPrefix: false,
allowDots: false,
charset: 'utf-8',
charsetSentinel: false,
delimiter: '&',
encode: true,
encoder: utils.encode,
encodeValuesOnly: false,
format: defaultFormat,
formatter: formats.formatters[defaultFormat],
// deprecated
indices: false,
serializeDate: function serializeDate(date) {
return toISO.call(date);
},
skipNulls: false,
strictNullHandling: false
};
var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
return typeof v === 'string'
|| typeof v === 'number'
|| typeof v === 'boolean'
|| typeof v === 'symbol'
|| typeof v === 'bigint';
};
var stringify = function stringify(
object,
prefix,
generateArrayPrefix,
strictNullHandling,
skipNulls,
encoder,
filter,
sort,
allowDots,
serializeDate,
formatter,
encodeValuesOnly,
charset
) {
var obj = object;
if (typeof filter === 'function') {
obj = filter(prefix, obj);
} else if (obj instanceof Date) {
obj = serializeDate(obj);
} else if (generateArrayPrefix === 'comma' && isArray(obj)) {
obj = obj.join(',');
}
if (obj === null) {
if (strictNullHandling) {
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key') : prefix;
}
obj = '';
}
if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
if (encoder) {
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key');
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value'))];
}
return [formatter(prefix) + '=' + formatter(String(obj))];
}
var values = [];
if (typeof obj === 'undefined') {
return values;
}
var objKeys;
if (isArray(filter)) {
objKeys = filter;
} else {
var keys = Object.keys(obj);
objKeys = sort ? keys.sort(sort) : keys;
}
for (var i = 0; i < objKeys.length; ++i) {
var key = objKeys[i];
if (skipNulls && obj[key] === null) {
continue;
}
if (isArray(obj)) {
pushToArray(values, stringify(
obj[key],
typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
generateArrayPrefix,
strictNullHandling,
skipNulls,
encoder,
filter,
sort,
allowDots,
serializeDate,
formatter,
encodeValuesOnly,
charset
));
} else {
pushToArray(values, stringify(
obj[key],
prefix + (allowDots ? '.' + key : '[' + key + ']'),
generateArrayPrefix,
strictNullHandling,
skipNulls,
encoder,
filter,
sort,
allowDots,
serializeDate,
formatter,
encodeValuesOnly,
charset
));
}
}
return values;
};
var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
if (!opts) {
return defaults;
}
if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
throw new TypeError('Encoder has to be a function.');
}
var charset = opts.charset || defaults.charset;
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
}
var format = formats['default'];
if (typeof opts.format !== 'undefined') {
if (!has.call(formats.formatters, opts.format)) {
throw new TypeError('Unknown format option provided.');
}
format = opts.format;
}
var formatter = formats.formatters[format];
var filter = defaults.filter;
if (typeof opts.filter === 'function' || isArray(opts.filter)) {
filter = opts.filter;
}
return {
addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,
allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
charset: charset,
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,
encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,
encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
filter: filter,
formatter: formatter,
serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
sort: typeof opts.sort === 'function' ? opts.sort : null,
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
};
};
module.exports = function (object, opts) {
var obj = object;
var options = normalizeStringifyOptions(opts);
var objKeys;
var filter;
if (typeof options.filter === 'function') {
filter = options.filter;
obj = filter('', obj);
} else if (isArray(options.filter)) {
filter = options.filter;
objKeys = filter;
}
var keys = [];
if (typeof obj !== 'object' || obj === null) {
return '';
}
var arrayFormat;
if (opts && opts.arrayFormat in arrayPrefixGenerators) {
arrayFormat = opts.arrayFormat;
} else if (opts && 'indices' in opts) {
arrayFormat = opts.indices ? 'indices' : 'repeat';
} else {
arrayFormat = 'indices';
}
var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
if (!objKeys) {
objKeys = Object.keys(obj);
}
if (options.sort) {
objKeys.sort(options.sort);
}
for (var i = 0; i < objKeys.length; ++i) {
var key = objKeys[i];
if (options.skipNulls && obj[key] === null) {
continue;
}
pushToArray(keys, stringify(
obj[key],
key,
generateArrayPrefix,
options.strictNullHandling,
options.skipNulls,
options.encode ? options.encoder : null,
options.filter,
options.sort,
options.allowDots,
options.serializeDate,
options.formatter,
options.encodeValuesOnly,
options.charset
));
}
var joined = keys.join(options.delimiter);
var prefix = options.addQueryPrefix === true ? '?' : '';
if (options.charsetSentinel) {
if (options.charset === 'iso-8859-1') {
// encodeURIComponent('&#10003;'), the "numeric entity" representation of a checkmark
prefix += 'utf8=%26%2310003%3B&';
} else {
// encodeURIComponent('✓')
prefix += 'utf8=%E2%9C%93&';
}
}
return joined.length > 0 ? prefix + joined : '';
};
},{"./formats":1,"./utils":5}],5:[function(require,module,exports){
'use strict';
var has = Object.prototype.hasOwnProperty;
var isArray = Array.isArray;
var hexTable = (function () {
var array = [];
for (var i = 0; i < 256; ++i) {
array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase());
}
return array;
}());
var compactQueue = function compactQueue(queue) {
while (queue.length > 1) {
var item = queue.pop();
var obj = item.obj[item.prop];
if (isArray(obj)) {
var compacted = [];
for (var j = 0; j < obj.length; ++j) {
if (typeof obj[j] !== 'undefined') {
compacted.push(obj[j]);
}
}
item.obj[item.prop] = compacted;
}
}
};
var arrayToObject = function arrayToObject(source, options) {
var obj = options && options.plainObjects ? Object.create(null) : {};
for (var i = 0; i < source.length; ++i) {
if (typeof source[i] !== 'undefined') {
obj[i] = source[i];
}
}
return obj;
};
var merge = function merge(target, source, options) {
/* eslint no-param-reassign: 0 */
if (!source) {
return target;
}
if (typeof source !== 'object') {
if (isArray(target)) {
target.push(source);
} else if (target && typeof target === 'object') {
if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
target[source] = true;
}
} else {
return [target, source];
}
return target;
}
if (!target || typeof target !== 'object') {
return [target].concat(source);
}
var mergeTarget = target;
if (isArray(target) && !isArray(source)) {
mergeTarget = arrayToObject(target, options);
}
if (isArray(target) && isArray(source)) {
source.forEach(function (item, i) {
if (has.call(target, i)) {
var targetItem = target[i];
if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
target[i] = merge(targetItem, item, options);
} else {
target.push(item);
}
} else {
target[i] = item;
}
});
return target;
}
return Object.keys(source).reduce(function (acc, key) {
var value = source[key];
if (has.call(acc, key)) {
acc[key] = merge(acc[key], value, options);
} else {
acc[key] = value;
}
return acc;
}, mergeTarget);
};
var assign = function assignSingleSource(target, source) {
return Object.keys(source).reduce(function (acc, key) {
acc[key] = source[key];
return acc;
}, target);
};
var decode = function (str, decoder, charset) {
var strWithoutPlus = str.replace(/\+/g, ' ');
if (charset === 'iso-8859-1') {
// unescape never throws, no try...catch needed:
return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape);
}
// utf-8
try {
return decodeURIComponent(strWithoutPlus);
} catch (e) {
return strWithoutPlus;
}
};
var encode = function encode(str, defaultEncoder, charset) {
// This code was originally written by Brian White (mscdex) for the io.js core querystring library.
// It has been adapted here for stricter adherence to RFC 3986
if (str.length === 0) {
return str;
}
var string = str;
if (typeof str === 'symbol') {
string = Symbol.prototype.toString.call(str);
} else if (typeof str !== 'string') {
string = String(str);
}
if (charset === 'iso-8859-1') {
return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) {
return '%26%23' + parseInt($0.slice(2), 16) + '%3B';
});
}
var out = '';
for (var i = 0; i < string.length; ++i) {
var c = string.charCodeAt(i);
if (
c === 0x2D // -
|| c === 0x2E // .
|| c === 0x5F // _
|| c === 0x7E // ~
|| (c >= 0x30 && c <= 0x39) // 0-9
|| (c >= 0x41 && c <= 0x5A) // a-z
|| (c >= 0x61 && c <= 0x7A) // A-Z
) {
out += string.charAt(i);
continue;
}
if (c < 0x80) {
out = out + hexTable[c];
continue;
}
if (c < 0x800) {
out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]);
continue;
}
if (c < 0xD800 || c >= 0xE000) {
out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]);
continue;
}
i += 1;
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
out += hexTable[0xF0 | (c >> 18)]
+ hexTable[0x80 | ((c >> 12) & 0x3F)]
+ hexTable[0x80 | ((c >> 6) & 0x3F)]
+ hexTable[0x80 | (c & 0x3F)];
}
return out;
};
var compact = function compact(value) {
var queue = [{ obj: { o: value }, prop: 'o' }];
var refs = [];
for (var i = 0; i < queue.length; ++i) {
var item = queue[i];
var obj = item.obj[item.prop];
var keys = Object.keys(obj);
for (var j = 0; j < keys.length; ++j) {
var key = keys[j];
var val = obj[key];
if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
queue.push({ obj: obj, prop: key });
refs.push(val);
}
}
}
compactQueue(queue);
return value;
};
var isRegExp = function isRegExp(obj) {
return Object.prototype.toString.call(obj) === '[object RegExp]';
};
var isBuffer = function isBuffer(obj) {
if (!obj || typeof obj !== 'object') {
return false;
}
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));
};
var combine = function combine(a, b) {
return [].concat(a, b);
};
module.exports = {
arrayToObject: arrayToObject,
assign: assign,
combine: combine,
compact: compact,
decode: decode,
encode: encode,
isBuffer: isBuffer,
isRegExp: isRegExp,
merge: merge
};
},{}]},{},[2])(2)
});

26
node_modules/qs/lib/formats.js generated vendored Normal file
View File

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

11
node_modules/qs/lib/index.js generated vendored Normal file
View File

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

248
node_modules/qs/lib/parse.js generated vendored Normal file
View File

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

279
node_modules/qs/lib/stringify.js generated vendored Normal file
View File

@ -0,0 +1,279 @@
'use strict';
var utils = require('./utils');
var formats = require('./formats');
var has = Object.prototype.hasOwnProperty;
var arrayPrefixGenerators = {
brackets: function brackets(prefix) {
return prefix + '[]';
},
comma: 'comma',
indices: function indices(prefix, key) {
return prefix + '[' + key + ']';
},
repeat: function repeat(prefix) {
return prefix;
}
};
var isArray = Array.isArray;
var push = Array.prototype.push;
var pushToArray = function (arr, valueOrArray) {
push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
};
var toISO = Date.prototype.toISOString;
var defaultFormat = formats['default'];
var defaults = {
addQueryPrefix: false,
allowDots: false,
charset: 'utf-8',
charsetSentinel: false,
delimiter: '&',
encode: true,
encoder: utils.encode,
encodeValuesOnly: false,
format: defaultFormat,
formatter: formats.formatters[defaultFormat],
// deprecated
indices: false,
serializeDate: function serializeDate(date) {
return toISO.call(date);
},
skipNulls: false,
strictNullHandling: false
};
var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
return typeof v === 'string'
|| typeof v === 'number'
|| typeof v === 'boolean'
|| typeof v === 'symbol'
|| typeof v === 'bigint';
};
var stringify = function stringify(
object,
prefix,
generateArrayPrefix,
strictNullHandling,
skipNulls,
encoder,
filter,
sort,
allowDots,
serializeDate,
formatter,
encodeValuesOnly,
charset
) {
var obj = object;
if (typeof filter === 'function') {
obj = filter(prefix, obj);
} else if (obj instanceof Date) {
obj = serializeDate(obj);
} else if (generateArrayPrefix === 'comma' && isArray(obj)) {
obj = obj.join(',');
}
if (obj === null) {
if (strictNullHandling) {
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key') : prefix;
}
obj = '';
}
if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
if (encoder) {
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key');
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value'))];
}
return [formatter(prefix) + '=' + formatter(String(obj))];
}
var values = [];
if (typeof obj === 'undefined') {
return values;
}
var objKeys;
if (isArray(filter)) {
objKeys = filter;
} else {
var keys = Object.keys(obj);
objKeys = sort ? keys.sort(sort) : keys;
}
for (var i = 0; i < objKeys.length; ++i) {
var key = objKeys[i];
if (skipNulls && obj[key] === null) {
continue;
}
if (isArray(obj)) {
pushToArray(values, stringify(
obj[key],
typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
generateArrayPrefix,
strictNullHandling,
skipNulls,
encoder,
filter,
sort,
allowDots,
serializeDate,
formatter,
encodeValuesOnly,
charset
));
} else {
pushToArray(values, stringify(
obj[key],
prefix + (allowDots ? '.' + key : '[' + key + ']'),
generateArrayPrefix,
strictNullHandling,
skipNulls,
encoder,
filter,
sort,
allowDots,
serializeDate,
formatter,
encodeValuesOnly,
charset
));
}
}
return values;
};
var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
if (!opts) {
return defaults;
}
if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
throw new TypeError('Encoder has to be a function.');
}
var charset = opts.charset || defaults.charset;
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
}
var format = formats['default'];
if (typeof opts.format !== 'undefined') {
if (!has.call(formats.formatters, opts.format)) {
throw new TypeError('Unknown format option provided.');
}
format = opts.format;
}
var formatter = formats.formatters[format];
var filter = defaults.filter;
if (typeof opts.filter === 'function' || isArray(opts.filter)) {
filter = opts.filter;
}
return {
addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,
allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
charset: charset,
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,
encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,
encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
filter: filter,
formatter: formatter,
serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
sort: typeof opts.sort === 'function' ? opts.sort : null,
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
};
};
module.exports = function (object, opts) {
var obj = object;
var options = normalizeStringifyOptions(opts);
var objKeys;
var filter;
if (typeof options.filter === 'function') {
filter = options.filter;
obj = filter('', obj);
} else if (isArray(options.filter)) {
filter = options.filter;
objKeys = filter;
}
var keys = [];
if (typeof obj !== 'object' || obj === null) {
return '';
}
var arrayFormat;
if (opts && opts.arrayFormat in arrayPrefixGenerators) {
arrayFormat = opts.arrayFormat;
} else if (opts && 'indices' in opts) {
arrayFormat = opts.indices ? 'indices' : 'repeat';
} else {
arrayFormat = 'indices';
}
var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
if (!objKeys) {
objKeys = Object.keys(obj);
}
if (options.sort) {
objKeys.sort(options.sort);
}
for (var i = 0; i < objKeys.length; ++i) {
var key = objKeys[i];
if (options.skipNulls && obj[key] === null) {
continue;
}
pushToArray(keys, stringify(
obj[key],
key,
generateArrayPrefix,
options.strictNullHandling,
options.skipNulls,
options.encode ? options.encoder : null,
options.filter,
options.sort,
options.allowDots,
options.serializeDate,
options.formatter,
options.encodeValuesOnly,
options.charset
));
}
var joined = keys.join(options.delimiter);
var prefix = options.addQueryPrefix === true ? '?' : '';
if (options.charsetSentinel) {
if (options.charset === 'iso-8859-1') {
// encodeURIComponent('&#10003;'), the "numeric entity" representation of a checkmark
prefix += 'utf8=%26%2310003%3B&';
} else {
// encodeURIComponent('✓')
prefix += 'utf8=%E2%9C%93&';
}
}
return joined.length > 0 ? prefix + joined : '';
};

236
node_modules/qs/lib/utils.js generated vendored Normal file
View File

@ -0,0 +1,236 @@
'use strict';
var has = Object.prototype.hasOwnProperty;
var isArray = Array.isArray;
var hexTable = (function () {
var array = [];
for (var i = 0; i < 256; ++i) {
array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase());
}
return array;
}());
var compactQueue = function compactQueue(queue) {
while (queue.length > 1) {
var item = queue.pop();
var obj = item.obj[item.prop];
if (isArray(obj)) {
var compacted = [];
for (var j = 0; j < obj.length; ++j) {
if (typeof obj[j] !== 'undefined') {
compacted.push(obj[j]);
}
}
item.obj[item.prop] = compacted;
}
}
};
var arrayToObject = function arrayToObject(source, options) {
var obj = options && options.plainObjects ? Object.create(null) : {};
for (var i = 0; i < source.length; ++i) {
if (typeof source[i] !== 'undefined') {
obj[i] = source[i];
}
}
return obj;
};
var merge = function merge(target, source, options) {
/* eslint no-param-reassign: 0 */
if (!source) {
return target;
}
if (typeof source !== 'object') {
if (isArray(target)) {
target.push(source);
} else if (target && typeof target === 'object') {
if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
target[source] = true;
}
} else {
return [target, source];
}
return target;
}
if (!target || typeof target !== 'object') {
return [target].concat(source);
}
var mergeTarget = target;
if (isArray(target) && !isArray(source)) {
mergeTarget = arrayToObject(target, options);
}
if (isArray(target) && isArray(source)) {
source.forEach(function (item, i) {
if (has.call(target, i)) {
var targetItem = target[i];
if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
target[i] = merge(targetItem, item, options);
} else {
target.push(item);
}
} else {
target[i] = item;
}
});
return target;
}
return Object.keys(source).reduce(function (acc, key) {
var value = source[key];
if (has.call(acc, key)) {
acc[key] = merge(acc[key], value, options);
} else {
acc[key] = value;
}
return acc;
}, mergeTarget);
};
var assign = function assignSingleSource(target, source) {
return Object.keys(source).reduce(function (acc, key) {
acc[key] = source[key];
return acc;
}, target);
};
var decode = function (str, decoder, charset) {
var strWithoutPlus = str.replace(/\+/g, ' ');
if (charset === 'iso-8859-1') {
// unescape never throws, no try...catch needed:
return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape);
}
// utf-8
try {
return decodeURIComponent(strWithoutPlus);
} catch (e) {
return strWithoutPlus;
}
};
var encode = function encode(str, defaultEncoder, charset) {
// This code was originally written by Brian White (mscdex) for the io.js core querystring library.
// It has been adapted here for stricter adherence to RFC 3986
if (str.length === 0) {
return str;
}
var string = str;
if (typeof str === 'symbol') {
string = Symbol.prototype.toString.call(str);
} else if (typeof str !== 'string') {
string = String(str);
}
if (charset === 'iso-8859-1') {
return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) {
return '%26%23' + parseInt($0.slice(2), 16) + '%3B';
});
}
var out = '';
for (var i = 0; i < string.length; ++i) {
var c = string.charCodeAt(i);
if (
c === 0x2D // -
|| c === 0x2E // .
|| c === 0x5F // _
|| c === 0x7E // ~
|| (c >= 0x30 && c <= 0x39) // 0-9
|| (c >= 0x41 && c <= 0x5A) // a-z
|| (c >= 0x61 && c <= 0x7A) // A-Z
) {
out += string.charAt(i);
continue;
}
if (c < 0x80) {
out = out + hexTable[c];
continue;
}
if (c < 0x800) {
out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]);
continue;
}
if (c < 0xD800 || c >= 0xE000) {
out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]);
continue;
}
i += 1;
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
out += hexTable[0xF0 | (c >> 18)]
+ hexTable[0x80 | ((c >> 12) & 0x3F)]
+ hexTable[0x80 | ((c >> 6) & 0x3F)]
+ hexTable[0x80 | (c & 0x3F)];
}
return out;
};
var compact = function compact(value) {
var queue = [{ obj: { o: value }, prop: 'o' }];
var refs = [];
for (var i = 0; i < queue.length; ++i) {
var item = queue[i];
var obj = item.obj[item.prop];
var keys = Object.keys(obj);
for (var j = 0; j < keys.length; ++j) {
var key = keys[j];
var val = obj[key];
if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
queue.push({ obj: obj, prop: key });
refs.push(val);
}
}
}
compactQueue(queue);
return value;
};
var isRegExp = function isRegExp(obj) {
return Object.prototype.toString.call(obj) === '[object RegExp]';
};
var isBuffer = function isBuffer(obj) {
if (!obj || typeof obj !== 'object') {
return false;
}
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));
};
var combine = function combine(a, b) {
return [].concat(a, b);
};
module.exports = {
arrayToObject: arrayToObject,
assign: assign,
combine: combine,
compact: compact,
decode: decode,
encode: encode,
isBuffer: isBuffer,
isRegExp: isRegExp,
merge: merge
};

94
node_modules/qs/package.json generated vendored Normal file
View File

@ -0,0 +1,94 @@
{
"_args": [
[
"qs@6.9.1",
"/home/runner/work/ghaction-upx/ghaction-upx"
]
],
"_from": "qs@6.9.1",
"_id": "qs@6.9.1",
"_inBundle": false,
"_integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==",
"_location": "/qs",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "qs@6.9.1",
"name": "qs",
"escapedName": "qs",
"rawSpec": "6.9.1",
"saveSpec": null,
"fetchSpec": "6.9.1"
},
"_requiredBy": [
"/typed-rest-client"
],
"_resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz",
"_spec": "6.9.1",
"_where": "/home/runner/work/ghaction-upx/ghaction-upx",
"bugs": {
"url": "https://github.com/ljharb/qs/issues"
},
"contributors": [
{
"name": "Jordan Harband",
"email": "ljharb@gmail.com",
"url": "http://ljharb.codes"
}
],
"dependencies": {},
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
"devDependencies": {
"@ljharb/eslint-config": "^15.0.0",
"browserify": "^16.5.0",
"covert": "^1.1.1",
"eclint": "^2.8.1",
"eslint": "^6.6.0",
"evalmd": "^0.0.19",
"for-each": "^0.3.3",
"has-symbols": "^1.0.0",
"iconv-lite": "^0.4.24",
"mkdirp": "^0.5.1",
"object-inspect": "^1.6.0",
"qs-iconv": "^1.0.4",
"safe-publish-latest": "^1.1.3",
"safer-buffer": "^2.1.2",
"tape": "^4.11.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
},
"homepage": "https://github.com/ljharb/qs",
"keywords": [
"querystring",
"qs",
"query",
"url",
"parse",
"stringify"
],
"license": "BSD-3-Clause",
"main": "lib/index.js",
"name": "qs",
"repository": {
"type": "git",
"url": "git+https://github.com/ljharb/qs.git"
},
"scripts": {
"coverage": "covert test",
"dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js",
"lint": "eslint lib/*.js test/*.js",
"postlint": "eclint check * lib/* test/*",
"posttest": "npx aud",
"prepublish": "safe-publish-latest && npm run dist",
"pretest": "npm run --silent readme && npm run --silent lint",
"readme": "evalmd README.md",
"test": "npm run --silent coverage",
"tests-only": "node test"
},
"version": "6.9.1"
}

18
node_modules/qs/test/.eslintrc generated vendored Normal file
View File

@ -0,0 +1,18 @@
{
"rules": {
"array-bracket-newline": 0,
"array-element-newline": 0,
"consistent-return": 2,
"function-paren-newline": 0,
"max-lines": 0,
"max-lines-per-function": 0,
"max-nested-callbacks": [2, 3],
"max-statements": 0,
"no-buffer-constructor": 0,
"no-extend-native": 0,
"no-magic-numbers": 0,
"no-throw-literal": 0,
"object-curly-newline": 0,
"sort-keys": 0,
}
}

7
node_modules/qs/test/index.js generated vendored Normal file
View File

@ -0,0 +1,7 @@
'use strict';
require('./parse');
require('./stringify');
require('./utils');

758
node_modules/qs/test/parse.js generated vendored Normal file
View File

@ -0,0 +1,758 @@
'use strict';
var test = require('tape');
var qs = require('../');
var utils = require('../lib/utils');
var iconv = require('iconv-lite');
var SaferBuffer = require('safer-buffer').Buffer;
test('parse()', function (t) {
t.test('parses a simple string', function (st) {
st.deepEqual(qs.parse('0=foo'), { 0: 'foo' });
st.deepEqual(qs.parse('foo=c++'), { foo: 'c ' });
st.deepEqual(qs.parse('a[>=]=23'), { a: { '>=': '23' } });
st.deepEqual(qs.parse('a[<=>]==23'), { a: { '<=>': '=23' } });
st.deepEqual(qs.parse('a[==]=23'), { a: { '==': '23' } });
st.deepEqual(qs.parse('foo', { strictNullHandling: true }), { foo: null });
st.deepEqual(qs.parse('foo'), { foo: '' });
st.deepEqual(qs.parse('foo='), { foo: '' });
st.deepEqual(qs.parse('foo=bar'), { foo: 'bar' });
st.deepEqual(qs.parse(' foo = bar = baz '), { ' foo ': ' bar = baz ' });
st.deepEqual(qs.parse('foo=bar=baz'), { foo: 'bar=baz' });
st.deepEqual(qs.parse('foo=bar&bar=baz'), { foo: 'bar', bar: 'baz' });
st.deepEqual(qs.parse('foo2=bar2&baz2='), { foo2: 'bar2', baz2: '' });
st.deepEqual(qs.parse('foo=bar&baz', { strictNullHandling: true }), { foo: 'bar', baz: null });
st.deepEqual(qs.parse('foo=bar&baz'), { foo: 'bar', baz: '' });
st.deepEqual(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'), {
cht: 'p3',
chd: 't:60,40',
chs: '250x100',
chl: 'Hello|World'
});
st.end();
});
t.test('arrayFormat: brackets allows only explicit arrays', function (st) {
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'brackets' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'brackets' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'brackets' }), { a: 'b,c' });
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'brackets' }), { a: ['b', 'c'] });
st.end();
});
t.test('arrayFormat: indices allows only indexed arrays', function (st) {
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'indices' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'indices' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'indices' }), { a: 'b,c' });
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'indices' }), { a: ['b', 'c'] });
st.end();
});
t.test('arrayFormat: comma allows only comma-separated arrays', function (st) {
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'comma' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'comma' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'comma' }), { a: 'b,c' });
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'comma' }), { a: ['b', 'c'] });
st.end();
});
t.test('arrayFormat: repeat allows only repeated values', function (st) {
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'repeat' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'repeat' }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'repeat' }), { a: 'b,c' });
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'repeat' }), { a: ['b', 'c'] });
st.end();
});
t.test('allows enabling dot notation', function (st) {
st.deepEqual(qs.parse('a.b=c'), { 'a.b': 'c' });
st.deepEqual(qs.parse('a.b=c', { allowDots: true }), { a: { b: 'c' } });
st.end();
});
t.deepEqual(qs.parse('a[b]=c'), { a: { b: 'c' } }, 'parses a single nested string');
t.deepEqual(qs.parse('a[b][c]=d'), { a: { b: { c: 'd' } } }, 'parses a double nested string');
t.deepEqual(
qs.parse('a[b][c][d][e][f][g][h]=i'),
{ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } },
'defaults to a depth of 5'
);
t.test('only parses one level when depth = 1', function (st) {
st.deepEqual(qs.parse('a[b][c]=d', { depth: 1 }), { a: { b: { '[c]': 'd' } } });
st.deepEqual(qs.parse('a[b][c][d]=e', { depth: 1 }), { a: { b: { '[c][d]': 'e' } } });
st.end();
});
t.test('uses original key when depth = 0', function (st) {
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { depth: 0 }), { 'a[0]': 'b', 'a[1]': 'c' });
st.deepEqual(qs.parse('a[0][0]=b&a[0][1]=c&a[1]=d&e=2', { depth: 0 }), { 'a[0][0]': 'b', 'a[0][1]': 'c', 'a[1]': 'd', e: '2' });
st.end();
});
t.test('uses original key when depth = false', function (st) {
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { depth: false }), { 'a[0]': 'b', 'a[1]': 'c' });
st.deepEqual(qs.parse('a[0][0]=b&a[0][1]=c&a[1]=d&e=2', { depth: false }), { 'a[0][0]': 'b', 'a[0][1]': 'c', 'a[1]': 'd', e: '2' });
st.end();
});
t.deepEqual(qs.parse('a=b&a=c'), { a: ['b', 'c'] }, 'parses a simple array');
t.test('parses an explicit array', function (st) {
st.deepEqual(qs.parse('a[]=b'), { a: ['b'] });
st.deepEqual(qs.parse('a[]=b&a[]=c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[]=b&a[]=c&a[]=d'), { a: ['b', 'c', 'd'] });
st.end();
});
t.test('parses a mix of simple and explicit arrays', function (st) {
st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[0]=b&a=c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b&a[0]=c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[1]=b&a=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[]=b&a=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b&a[1]=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b&a[]=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] });
st.end();
});
t.test('parses a nested array', function (st) {
st.deepEqual(qs.parse('a[b][]=c&a[b][]=d'), { a: { b: ['c', 'd'] } });
st.deepEqual(qs.parse('a[>=]=25'), { a: { '>=': '25' } });
st.end();
});
t.test('allows to specify array indices', function (st) {
st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] });
st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 20 }), { a: ['c'] });
st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 0 }), { a: { 1: 'c' } });
st.deepEqual(qs.parse('a[1]=c'), { a: ['c'] });
st.end();
});
t.test('limits specific array indices to arrayLimit', function (st) {
st.deepEqual(qs.parse('a[20]=a', { arrayLimit: 20 }), { a: ['a'] });
st.deepEqual(qs.parse('a[21]=a', { arrayLimit: 20 }), { a: { 21: 'a' } });
st.end();
});
t.deepEqual(qs.parse('a[12b]=c'), { a: { '12b': 'c' } }, 'supports keys that begin with a number');
t.test('supports encoded = signs', function (st) {
st.deepEqual(qs.parse('he%3Dllo=th%3Dere'), { 'he=llo': 'th=ere' });
st.end();
});
t.test('is ok with url encoded strings', function (st) {
st.deepEqual(qs.parse('a[b%20c]=d'), { a: { 'b c': 'd' } });
st.deepEqual(qs.parse('a[b]=c%20d'), { a: { b: 'c d' } });
st.end();
});
t.test('allows brackets in the value', function (st) {
st.deepEqual(qs.parse('pets=["tobi"]'), { pets: '["tobi"]' });
st.deepEqual(qs.parse('operators=[">=", "<="]'), { operators: '[">=", "<="]' });
st.end();
});
t.test('allows empty values', function (st) {
st.deepEqual(qs.parse(''), {});
st.deepEqual(qs.parse(null), {});
st.deepEqual(qs.parse(undefined), {});
st.end();
});
t.test('transforms arrays to objects', function (st) {
st.deepEqual(qs.parse('foo[0]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } });
st.deepEqual(qs.parse('foo[bad]=baz&foo[0]=bar'), { foo: { bad: 'baz', 0: 'bar' } });
st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar'), { foo: { bad: 'baz', 0: 'bar' } });
st.deepEqual(qs.parse('foo[]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } });
st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } });
st.deepEqual(qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb'), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { 0: 'b', t: 'u' } });
st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: true }), { a: { 0: 'b', t: 'u', hasOwnProperty: 'c' } });
st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: false }), { a: { 0: 'b', x: 'y' } });
st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: true }), { a: { 0: 'b', hasOwnProperty: 'c', x: 'y' } });
st.end();
});
t.test('transforms arrays to objects (dot notation)', function (st) {
st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad.boo=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
st.deepEqual(qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15'], bar: '2' }] });
st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15', '16'], bar: '2' }] });
st.deepEqual(qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } });
st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } });
st.deepEqual(qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true }), { foo: { 0: 'bar', bad: 'baz' } });
st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } });
st.deepEqual(qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb', { allowDots: true }), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
st.end();
});
t.test('correctly prunes undefined values when converting an array to an object', function (st) {
st.deepEqual(qs.parse('a[2]=b&a[99999999]=c'), { a: { 2: 'b', 99999999: 'c' } });
st.end();
});
t.test('supports malformed uri characters', function (st) {
st.deepEqual(qs.parse('{%:%}', { strictNullHandling: true }), { '{%:%}': null });
st.deepEqual(qs.parse('{%:%}='), { '{%:%}': '' });
st.deepEqual(qs.parse('foo=%:%}'), { foo: '%:%}' });
st.end();
});
t.test('doesn\'t produce empty keys', function (st) {
st.deepEqual(qs.parse('_r=1&'), { _r: '1' });
st.end();
});
t.test('cannot access Object prototype', function (st) {
qs.parse('constructor[prototype][bad]=bad');
qs.parse('bad[constructor][prototype][bad]=bad');
st.equal(typeof Object.prototype.bad, 'undefined');
st.end();
});
t.test('parses arrays of objects', function (st) {
st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] });
st.deepEqual(qs.parse('a[0][b]=c'), { a: [{ b: 'c' }] });
st.end();
});
t.test('allows for empty strings in arrays', function (st) {
st.deepEqual(qs.parse('a[]=b&a[]=&a[]=c'), { a: ['b', '', 'c'] });
st.deepEqual(
qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 }),
{ a: ['b', null, 'c', ''] },
'with arrayLimit 20 + array indices: null then empty string works'
);
st.deepEqual(
qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 }),
{ a: ['b', null, 'c', ''] },
'with arrayLimit 0 + array brackets: null then empty string works'
);
st.deepEqual(
qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 }),
{ a: ['b', '', 'c', null] },
'with arrayLimit 20 + array indices: empty string then null works'
);
st.deepEqual(
qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 }),
{ a: ['b', '', 'c', null] },
'with arrayLimit 0 + array brackets: empty string then null works'
);
st.deepEqual(
qs.parse('a[]=&a[]=b&a[]=c'),
{ a: ['', 'b', 'c'] },
'array brackets: empty strings work'
);
st.end();
});
t.test('compacts sparse arrays', function (st) {
st.deepEqual(qs.parse('a[10]=1&a[2]=2', { arrayLimit: 20 }), { a: ['2', '1'] });
st.deepEqual(qs.parse('a[1][b][2][c]=1', { arrayLimit: 20 }), { a: [{ b: [{ c: '1' }] }] });
st.deepEqual(qs.parse('a[1][2][3][c]=1', { arrayLimit: 20 }), { a: [[[{ c: '1' }]]] });
st.deepEqual(qs.parse('a[1][2][3][c][1]=1', { arrayLimit: 20 }), { a: [[[{ c: ['1'] }]]] });
st.end();
});
t.test('parses semi-parsed strings', function (st) {
st.deepEqual(qs.parse({ 'a[b]': 'c' }), { a: { b: 'c' } });
st.deepEqual(qs.parse({ 'a[b]': 'c', 'a[d]': 'e' }), { a: { b: 'c', d: 'e' } });
st.end();
});
t.test('parses buffers correctly', function (st) {
var b = SaferBuffer.from('test');
st.deepEqual(qs.parse({ a: b }), { a: b });
st.end();
});
t.test('parses jquery-param strings', function (st) {
// readable = 'filter[0][]=int1&filter[0][]==&filter[0][]=77&filter[]=and&filter[2][]=int2&filter[2][]==&filter[2][]=8'
var encoded = 'filter%5B0%5D%5B%5D=int1&filter%5B0%5D%5B%5D=%3D&filter%5B0%5D%5B%5D=77&filter%5B%5D=and&filter%5B2%5D%5B%5D=int2&filter%5B2%5D%5B%5D=%3D&filter%5B2%5D%5B%5D=8';
var expected = { filter: [['int1', '=', '77'], 'and', ['int2', '=', '8']] };
st.deepEqual(qs.parse(encoded), expected);
st.end();
});
t.test('continues parsing when no parent is found', function (st) {
st.deepEqual(qs.parse('[]=&a=b'), { 0: '', a: 'b' });
st.deepEqual(qs.parse('[]&a=b', { strictNullHandling: true }), { 0: null, a: 'b' });
st.deepEqual(qs.parse('[foo]=bar'), { foo: 'bar' });
st.end();
});
t.test('does not error when parsing a very long array', function (st) {
var str = 'a[]=a';
while (Buffer.byteLength(str) < 128 * 1024) {
str = str + '&' + str;
}
st.doesNotThrow(function () {
qs.parse(str);
});
st.end();
});
t.test('should not throw when a native prototype has an enumerable property', function (st) {
Object.prototype.crash = '';
Array.prototype.crash = '';
st.doesNotThrow(qs.parse.bind(null, 'a=b'));
st.deepEqual(qs.parse('a=b'), { a: 'b' });
st.doesNotThrow(qs.parse.bind(null, 'a[][b]=c'));
st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] });
delete Object.prototype.crash;
delete Array.prototype.crash;
st.end();
});
t.test('parses a string with an alternative string delimiter', function (st) {
st.deepEqual(qs.parse('a=b;c=d', { delimiter: ';' }), { a: 'b', c: 'd' });
st.end();
});
t.test('parses a string with an alternative RegExp delimiter', function (st) {
st.deepEqual(qs.parse('a=b; c=d', { delimiter: /[;,] */ }), { a: 'b', c: 'd' });
st.end();
});
t.test('does not use non-splittable objects as delimiters', function (st) {
st.deepEqual(qs.parse('a=b&c=d', { delimiter: true }), { a: 'b', c: 'd' });
st.end();
});
t.test('allows overriding parameter limit', function (st) {
st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: 1 }), { a: 'b' });
st.end();
});
t.test('allows setting the parameter limit to Infinity', function (st) {
st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: Infinity }), { a: 'b', c: 'd' });
st.end();
});
t.test('allows overriding array limit', function (st) {
st.deepEqual(qs.parse('a[0]=b', { arrayLimit: -1 }), { a: { 0: 'b' } });
st.deepEqual(qs.parse('a[-1]=b', { arrayLimit: -1 }), { a: { '-1': 'b' } });
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 }), { a: { 0: 'b', 1: 'c' } });
st.end();
});
t.test('allows disabling array parsing', function (st) {
var indices = qs.parse('a[0]=b&a[1]=c', { parseArrays: false });
st.deepEqual(indices, { a: { 0: 'b', 1: 'c' } });
st.equal(Array.isArray(indices.a), false, 'parseArrays:false, indices case is not an array');
var emptyBrackets = qs.parse('a[]=b', { parseArrays: false });
st.deepEqual(emptyBrackets, { a: { 0: 'b' } });
st.equal(Array.isArray(emptyBrackets.a), false, 'parseArrays:false, empty brackets case is not an array');
st.end();
});
t.test('allows for query string prefix', function (st) {
st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
st.deepEqual(qs.parse('foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: false }), { '?foo': 'bar' });
st.end();
});
t.test('parses an object', function (st) {
var input = {
'user[name]': { 'pop[bob]': 3 },
'user[email]': null
};
var expected = {
user: {
name: { 'pop[bob]': 3 },
email: null
}
};
var result = qs.parse(input);
st.deepEqual(result, expected);
st.end();
});
t.test('parses string with comma as array divider', function (st) {
st.deepEqual(qs.parse('foo=bar,tee', { comma: true }), { foo: ['bar', 'tee'] });
st.deepEqual(qs.parse('foo[bar]=coffee,tee', { comma: true }), { foo: { bar: ['coffee', 'tee'] } });
st.deepEqual(qs.parse('foo=', { comma: true }), { foo: '' });
st.deepEqual(qs.parse('foo', { comma: true }), { foo: '' });
st.deepEqual(qs.parse('foo', { comma: true, strictNullHandling: true }), { foo: null });
st.end();
});
t.test('use number decoder, parses string that has one number with comma option enabled', function (st) {
var decoder = function (str, defaultDecoder, charset, type) {
if (!isNaN(Number(str))) {
return parseFloat(str);
}
return defaultDecoder(str, defaultDecoder, charset, type);
};
st.deepEqual(qs.parse('foo=1', { comma: true, decoder: decoder }), { foo: 1 });
st.deepEqual(qs.parse('foo=0', { comma: true, decoder: decoder }), { foo: 0 });
st.end();
});
t.test('parses brackets holds array of arrays when having two parts of strings with comma as array divider', function (st) {
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=4,5,6', { comma: true }), { foo: [['1', '2', '3'], ['4', '5', '6']] });
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=', { comma: true }), { foo: [['1', '2', '3'], ''] });
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=,', { comma: true }), { foo: [['1', '2', '3'], ['', '']] });
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=a', { comma: true }), { foo: [['1', '2', '3'], 'a'] });
st.end();
});
t.test('parses an object in dot notation', function (st) {
var input = {
'user.name': { 'pop[bob]': 3 },
'user.email.': null
};
var expected = {
user: {
name: { 'pop[bob]': 3 },
email: null
}
};
var result = qs.parse(input, { allowDots: true });
st.deepEqual(result, expected);
st.end();
});
t.test('parses an object and not child values', function (st) {
var input = {
'user[name]': { 'pop[bob]': { test: 3 } },
'user[email]': null
};
var expected = {
user: {
name: { 'pop[bob]': { test: 3 } },
email: null
}
};
var result = qs.parse(input);
st.deepEqual(result, expected);
st.end();
});
t.test('does not blow up when Buffer global is missing', function (st) {
var tempBuffer = global.Buffer;
delete global.Buffer;
var result = qs.parse('a=b&c=d');
global.Buffer = tempBuffer;
st.deepEqual(result, { a: 'b', c: 'd' });
st.end();
});
t.test('does not crash when parsing circular references', function (st) {
var a = {};
a.b = a;
var parsed;
st.doesNotThrow(function () {
parsed = qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a });
});
st.equal('foo' in parsed, true, 'parsed has "foo" property');
st.equal('bar' in parsed.foo, true);
st.equal('baz' in parsed.foo, true);
st.equal(parsed.foo.bar, 'baz');
st.deepEqual(parsed.foo.baz, a);
st.end();
});
t.test('does not crash when parsing deep objects', function (st) {
var parsed;
var str = 'foo';
for (var i = 0; i < 5000; i++) {
str += '[p]';
}
str += '=bar';
st.doesNotThrow(function () {
parsed = qs.parse(str, { depth: 5000 });
});
st.equal('foo' in parsed, true, 'parsed has "foo" property');
var depth = 0;
var ref = parsed.foo;
while ((ref = ref.p)) {
depth += 1;
}
st.equal(depth, 5000, 'parsed is 5000 properties deep');
st.end();
});
t.test('parses null objects correctly', { skip: !Object.create }, function (st) {
var a = Object.create(null);
a.b = 'c';
st.deepEqual(qs.parse(a), { b: 'c' });
var result = qs.parse({ a: a });
st.equal('a' in result, true, 'result has "a" property');
st.deepEqual(result.a, a);
st.end();
});
t.test('parses dates correctly', function (st) {
var now = new Date();
st.deepEqual(qs.parse({ a: now }), { a: now });
st.end();
});
t.test('parses regular expressions correctly', function (st) {
var re = /^test$/;
st.deepEqual(qs.parse({ a: re }), { a: re });
st.end();
});
t.test('does not allow overwriting prototype properties', function (st) {
st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: false }), {});
st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: false }), {});
st.deepEqual(
qs.parse('toString', { allowPrototypes: false }),
{},
'bare "toString" results in {}'
);
st.end();
});
t.test('can allow overwriting prototype properties', function (st) {
st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }), { a: { hasOwnProperty: 'b' } });
st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: true }), { hasOwnProperty: 'b' });
st.deepEqual(
qs.parse('toString', { allowPrototypes: true }),
{ toString: '' },
'bare "toString" results in { toString: "" }'
);
st.end();
});
t.test('params starting with a closing bracket', function (st) {
st.deepEqual(qs.parse(']=toString'), { ']': 'toString' });
st.deepEqual(qs.parse(']]=toString'), { ']]': 'toString' });
st.deepEqual(qs.parse(']hello]=toString'), { ']hello]': 'toString' });
st.end();
});
t.test('params starting with a starting bracket', function (st) {
st.deepEqual(qs.parse('[=toString'), { '[': 'toString' });
st.deepEqual(qs.parse('[[=toString'), { '[[': 'toString' });
st.deepEqual(qs.parse('[hello[=toString'), { '[hello[': 'toString' });
st.end();
});
t.test('add keys to objects', function (st) {
st.deepEqual(
qs.parse('a[b]=c&a=d'),
{ a: { b: 'c', d: true } },
'can add keys to objects'
);
st.deepEqual(
qs.parse('a[b]=c&a=toString'),
{ a: { b: 'c' } },
'can not overwrite prototype'
);
st.deepEqual(
qs.parse('a[b]=c&a=toString', { allowPrototypes: true }),
{ a: { b: 'c', toString: true } },
'can overwrite prototype with allowPrototypes true'
);
st.deepEqual(
qs.parse('a[b]=c&a=toString', { plainObjects: true }),
{ a: { b: 'c', toString: true } },
'can overwrite prototype with plainObjects true'
);
st.end();
});
t.test('can return null objects', { skip: !Object.create }, function (st) {
var expected = Object.create(null);
expected.a = Object.create(null);
expected.a.b = 'c';
expected.a.hasOwnProperty = 'd';
st.deepEqual(qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true }), expected);
st.deepEqual(qs.parse(null, { plainObjects: true }), Object.create(null));
var expectedArray = Object.create(null);
expectedArray.a = Object.create(null);
expectedArray.a[0] = 'b';
expectedArray.a.c = 'd';
st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray);
st.end();
});
t.test('can parse with custom encoding', function (st) {
st.deepEqual(qs.parse('%8c%a7=%91%e5%8d%e3%95%7b', {
decoder: function (str) {
var reg = /%([0-9A-F]{2})/ig;
var result = [];
var parts = reg.exec(str);
while (parts) {
result.push(parseInt(parts[1], 16));
parts = reg.exec(str);
}
return String(iconv.decode(SaferBuffer.from(result), 'shift_jis'));
}
}), { : '大阪府' });
st.end();
});
t.test('receives the default decoder as a second argument', function (st) {
st.plan(1);
qs.parse('a', {
decoder: function (str, defaultDecoder) {
st.equal(defaultDecoder, utils.decode);
}
});
st.end();
});
t.test('throws error with wrong decoder', function (st) {
st['throws'](function () {
qs.parse({}, { decoder: 'string' });
}, new TypeError('Decoder has to be a function.'));
st.end();
});
t.test('does not mutate the options argument', function (st) {
var options = {};
qs.parse('a[b]=true', options);
st.deepEqual(options, {});
st.end();
});
t.test('throws if an invalid charset is specified', function (st) {
st['throws'](function () {
qs.parse('a=b', { charset: 'foobar' });
}, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'));
st.end();
});
t.test('parses an iso-8859-1 string if asked to', function (st) {
st.deepEqual(qs.parse('%A2=%BD', { charset: 'iso-8859-1' }), { '¢': '½' });
st.end();
});
var urlEncodedCheckmarkInUtf8 = '%E2%9C%93';
var urlEncodedOSlashInUtf8 = '%C3%B8';
var urlEncodedNumCheckmark = '%26%2310003%3B';
var urlEncodedNumSmiley = '%26%239786%3B';
t.test('prefers an utf-8 charset specified by the utf8 sentinel to a default charset of iso-8859-1', function (st) {
st.deepEqual(qs.parse('utf8=' + urlEncodedCheckmarkInUtf8 + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'iso-8859-1' }), { ø: 'ø' });
st.end();
});
t.test('prefers an iso-8859-1 charset specified by the utf8 sentinel to a default charset of utf-8', function (st) {
st.deepEqual(qs.parse('utf8=' + urlEncodedNumCheckmark + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'utf-8' }), { 'ø': 'ø' });
st.end();
});
t.test('does not require the utf8 sentinel to be defined before the parameters whose decoding it affects', function (st) {
st.deepEqual(qs.parse('a=' + urlEncodedOSlashInUtf8 + '&utf8=' + urlEncodedNumCheckmark, { charsetSentinel: true, charset: 'utf-8' }), { a: 'ø' });
st.end();
});
t.test('should ignore an utf8 sentinel with an unknown value', function (st) {
st.deepEqual(qs.parse('utf8=foo&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'utf-8' }), { ø: 'ø' });
st.end();
});
t.test('uses the utf8 sentinel to switch to utf-8 when no default charset is given', function (st) {
st.deepEqual(qs.parse('utf8=' + urlEncodedCheckmarkInUtf8 + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true }), { ø: 'ø' });
st.end();
});
t.test('uses the utf8 sentinel to switch to iso-8859-1 when no default charset is given', function (st) {
st.deepEqual(qs.parse('utf8=' + urlEncodedNumCheckmark + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true }), { 'ø': 'ø' });
st.end();
});
t.test('interprets numeric entities in iso-8859-1 when `interpretNumericEntities`', function (st) {
st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'iso-8859-1', interpretNumericEntities: true }), { foo: '☺' });
st.end();
});
t.test('handles a custom decoder returning `null`, in the `iso-8859-1` charset, when `interpretNumericEntities`', function (st) {
st.deepEqual(qs.parse('foo=&bar=' + urlEncodedNumSmiley, {
charset: 'iso-8859-1',
decoder: function (str, defaultDecoder, charset) {
return str ? defaultDecoder(str, defaultDecoder, charset) : null;
},
interpretNumericEntities: true
}), { foo: null, bar: '☺' });
st.end();
});
t.test('does not interpret numeric entities in iso-8859-1 when `interpretNumericEntities` is absent', function (st) {
st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'iso-8859-1' }), { foo: '&#9786;' });
st.end();
});
t.test('does not interpret numeric entities when the charset is utf-8, even when `interpretNumericEntities`', function (st) {
st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'utf-8', interpretNumericEntities: true }), { foo: '&#9786;' });
st.end();
});
t.test('does not interpret %uXXXX syntax in iso-8859-1 mode', function (st) {
st.deepEqual(qs.parse('%u263A=%u263A', { charset: 'iso-8859-1' }), { '%u263A': '%u263A' });
st.end();
});
t.test('allows for decoding keys and values differently', function (st) {
var decoder = function (str, defaultDecoder, charset, type) {
if (type === 'key') {
return defaultDecoder(str, defaultDecoder, charset, type).toLowerCase();
}
if (type === 'value') {
return defaultDecoder(str, defaultDecoder, charset, type).toUpperCase();
}
throw 'this should never happen! type: ' + type;
};
st.deepEqual(qs.parse('KeY=vAlUe', { decoder: decoder }), { key: 'VALUE' });
st.end();
});
t.end();
});

738
node_modules/qs/test/stringify.js generated vendored Normal file
View File

@ -0,0 +1,738 @@
'use strict';
var test = require('tape');
var qs = require('../');
var utils = require('../lib/utils');
var iconv = require('iconv-lite');
var SaferBuffer = require('safer-buffer').Buffer;
var hasSymbols = require('has-symbols');
var hasBigInt = typeof BigInt === 'function';
test('stringify()', function (t) {
t.test('stringifies a querystring object', function (st) {
st.equal(qs.stringify({ a: 'b' }), 'a=b');
st.equal(qs.stringify({ a: 1 }), 'a=1');
st.equal(qs.stringify({ a: 1, b: 2 }), 'a=1&b=2');
st.equal(qs.stringify({ a: 'A_Z' }), 'a=A_Z');
st.equal(qs.stringify({ a: '€' }), 'a=%E2%82%AC');
st.equal(qs.stringify({ a: '' }), 'a=%EE%80%80');
st.equal(qs.stringify({ a: 'א' }), 'a=%D7%90');
st.equal(qs.stringify({ a: '𐐷' }), 'a=%F0%90%90%B7');
st.end();
});
t.test('stringifies falsy values', function (st) {
st.equal(qs.stringify(undefined), '');
st.equal(qs.stringify(null), '');
st.equal(qs.stringify(null, { strictNullHandling: true }), '');
st.equal(qs.stringify(false), '');
st.equal(qs.stringify(0), '');
st.end();
});
t.test('stringifies symbols', { skip: !hasSymbols() }, function (st) {
st.equal(qs.stringify(Symbol.iterator), '');
st.equal(qs.stringify([Symbol.iterator]), '0=Symbol%28Symbol.iterator%29');
st.equal(qs.stringify({ a: Symbol.iterator }), 'a=Symbol%28Symbol.iterator%29');
st.equal(
qs.stringify({ a: [Symbol.iterator] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
'a[]=Symbol%28Symbol.iterator%29'
);
st.end();
});
t.test('stringifies bigints', { skip: !hasBigInt }, function (st) {
var three = BigInt(3);
var encodeWithN = function (value, defaultEncoder, charset) {
var result = defaultEncoder(value, defaultEncoder, charset);
return typeof value === 'bigint' ? result + 'n' : result;
};
st.equal(qs.stringify(three), '');
st.equal(qs.stringify([three]), '0=3');
st.equal(qs.stringify([three], { encoder: encodeWithN }), '0=3n');
st.equal(qs.stringify({ a: three }), 'a=3');
st.equal(qs.stringify({ a: three }, { encoder: encodeWithN }), 'a=3n');
st.equal(
qs.stringify({ a: [three] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
'a[]=3'
);
st.equal(
qs.stringify({ a: [three] }, { encodeValuesOnly: true, encoder: encodeWithN, arrayFormat: 'brackets' }),
'a[]=3n'
);
st.end();
});
t.test('adds query prefix', function (st) {
st.equal(qs.stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b');
st.end();
});
t.test('with query prefix, outputs blank string given an empty object', function (st) {
st.equal(qs.stringify({}, { addQueryPrefix: true }), '');
st.end();
});
t.test('stringifies nested falsy values', function (st) {
st.equal(qs.stringify({ a: { b: { c: null } } }), 'a%5Bb%5D%5Bc%5D=');
st.equal(qs.stringify({ a: { b: { c: null } } }, { strictNullHandling: true }), 'a%5Bb%5D%5Bc%5D');
st.equal(qs.stringify({ a: { b: { c: false } } }), 'a%5Bb%5D%5Bc%5D=false');
st.end();
});
t.test('stringifies a nested object', function (st) {
st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }), 'a%5Bb%5D%5Bc%5D%5Bd%5D=e');
st.end();
});
t.test('stringifies a nested object with dots notation', function (st) {
st.equal(qs.stringify({ a: { b: 'c' } }, { allowDots: true }), 'a.b=c');
st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true }), 'a.b.c.d=e');
st.end();
});
t.test('stringifies an array value', function (st) {
st.equal(
qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' }),
'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
'indices => indices'
);
st.equal(
qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' }),
'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d',
'brackets => brackets'
);
st.equal(
qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma' }),
'a=b%2Cc%2Cd',
'comma => comma'
);
st.equal(
qs.stringify({ a: ['b', 'c', 'd'] }),
'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
'default => indices'
);
st.end();
});
t.test('omits nulls when asked', function (st) {
st.equal(qs.stringify({ a: 'b', c: null }, { skipNulls: true }), 'a=b');
st.end();
});
t.test('omits nested nulls when asked', function (st) {
st.equal(qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true }), 'a%5Bb%5D=c');
st.end();
});
t.test('omits array indices when asked', function (st) {
st.equal(qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }), 'a=b&a=c&a=d');
st.end();
});
t.test('stringifies a nested array value', function (st) {
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d');
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'comma' }), 'a%5Bb%5D=c%2Cd'); // a[b]=c,d
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
st.end();
});
t.test('stringifies a nested array value with dots notation', function (st) {
st.equal(
qs.stringify(
{ a: { b: ['c', 'd'] } },
{ allowDots: true, encode: false, arrayFormat: 'indices' }
),
'a.b[0]=c&a.b[1]=d',
'indices: stringifies with dots + indices'
);
st.equal(
qs.stringify(
{ a: { b: ['c', 'd'] } },
{ allowDots: true, encode: false, arrayFormat: 'brackets' }
),
'a.b[]=c&a.b[]=d',
'brackets: stringifies with dots + brackets'
);
st.equal(
qs.stringify(
{ a: { b: ['c', 'd'] } },
{ allowDots: true, encode: false, arrayFormat: 'comma' }
),
'a.b=c,d',
'comma: stringifies with dots + comma'
);
st.equal(
qs.stringify(
{ a: { b: ['c', 'd'] } },
{ allowDots: true, encode: false }
),
'a.b[0]=c&a.b[1]=d',
'default: stringifies with dots + indices'
);
st.end();
});
t.test('stringifies an object inside an array', function (st) {
st.equal(
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices' }),
'a%5B0%5D%5Bb%5D=c', // a[0][b]=c
'indices => brackets'
);
st.equal(
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets' }),
'a%5B%5D%5Bb%5D=c', // a[][b]=c
'brackets => brackets'
);
st.equal(
qs.stringify({ a: [{ b: 'c' }] }),
'a%5B0%5D%5Bb%5D=c',
'default => indices'
);
st.equal(
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices' }),
'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1',
'indices => indices'
);
st.equal(
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets' }),
'a%5B%5D%5Bb%5D%5Bc%5D%5B%5D=1',
'brackets => brackets'
);
st.equal(
qs.stringify({ a: [{ b: { c: [1] } }] }),
'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1',
'default => indices'
);
st.end();
});
t.test('stringifies an array with mixed objects and primitives', function (st) {
st.equal(
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'indices' }),
'a[0][b]=1&a[1]=2&a[2]=3',
'indices => indices'
);
st.equal(
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'brackets' }),
'a[][b]=1&a[]=2&a[]=3',
'brackets => brackets'
);
st.equal(
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }),
'a[0][b]=1&a[1]=2&a[2]=3',
'default => indices'
);
st.end();
});
t.test('stringifies an object inside an array with dots notation', function (st) {
st.equal(
qs.stringify(
{ a: [{ b: 'c' }] },
{ allowDots: true, encode: false, arrayFormat: 'indices' }
),
'a[0].b=c',
'indices => indices'
);
st.equal(
qs.stringify(
{ a: [{ b: 'c' }] },
{ allowDots: true, encode: false, arrayFormat: 'brackets' }
),
'a[].b=c',
'brackets => brackets'
);
st.equal(
qs.stringify(
{ a: [{ b: 'c' }] },
{ allowDots: true, encode: false }
),
'a[0].b=c',
'default => indices'
);
st.equal(
qs.stringify(
{ a: [{ b: { c: [1] } }] },
{ allowDots: true, encode: false, arrayFormat: 'indices' }
),
'a[0].b.c[0]=1',
'indices => indices'
);
st.equal(
qs.stringify(
{ a: [{ b: { c: [1] } }] },
{ allowDots: true, encode: false, arrayFormat: 'brackets' }
),
'a[].b.c[]=1',
'brackets => brackets'
);
st.equal(
qs.stringify(
{ a: [{ b: { c: [1] } }] },
{ allowDots: true, encode: false }
),
'a[0].b.c[0]=1',
'default => indices'
);
st.end();
});
t.test('does not omit object keys when indices = false', function (st) {
st.equal(qs.stringify({ a: [{ b: 'c' }] }, { indices: false }), 'a%5Bb%5D=c');
st.end();
});
t.test('uses indices notation for arrays when indices=true', function (st) {
st.equal(qs.stringify({ a: ['b', 'c'] }, { indices: true }), 'a%5B0%5D=b&a%5B1%5D=c');
st.end();
});
t.test('uses indices notation for arrays when no arrayFormat is specified', function (st) {
st.equal(qs.stringify({ a: ['b', 'c'] }), 'a%5B0%5D=b&a%5B1%5D=c');
st.end();
});
t.test('uses indices notation for arrays when no arrayFormat=indices', function (st) {
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }), 'a%5B0%5D=b&a%5B1%5D=c');
st.end();
});
t.test('uses repeat notation for arrays when no arrayFormat=repeat', function (st) {
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }), 'a=b&a=c');
st.end();
});
t.test('uses brackets notation for arrays when no arrayFormat=brackets', function (st) {
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }), 'a%5B%5D=b&a%5B%5D=c');
st.end();
});
t.test('stringifies a complicated object', function (st) {
st.equal(qs.stringify({ a: { b: 'c', d: 'e' } }), 'a%5Bb%5D=c&a%5Bd%5D=e');
st.end();
});
t.test('stringifies an empty value', function (st) {
st.equal(qs.stringify({ a: '' }), 'a=');
st.equal(qs.stringify({ a: null }, { strictNullHandling: true }), 'a');
st.equal(qs.stringify({ a: '', b: '' }), 'a=&b=');
st.equal(qs.stringify({ a: null, b: '' }, { strictNullHandling: true }), 'a&b=');
st.equal(qs.stringify({ a: { b: '' } }), 'a%5Bb%5D=');
st.equal(qs.stringify({ a: { b: null } }, { strictNullHandling: true }), 'a%5Bb%5D');
st.equal(qs.stringify({ a: { b: null } }, { strictNullHandling: false }), 'a%5Bb%5D=');
st.end();
});
t.test('stringifies a null object', { skip: !Object.create }, function (st) {
var obj = Object.create(null);
obj.a = 'b';
st.equal(qs.stringify(obj), 'a=b');
st.end();
});
t.test('returns an empty string for invalid input', function (st) {
st.equal(qs.stringify(undefined), '');
st.equal(qs.stringify(false), '');
st.equal(qs.stringify(null), '');
st.equal(qs.stringify(''), '');
st.end();
});
t.test('stringifies an object with a null object as a child', { skip: !Object.create }, function (st) {
var obj = { a: Object.create(null) };
obj.a.b = 'c';
st.equal(qs.stringify(obj), 'a%5Bb%5D=c');
st.end();
});
t.test('drops keys with a value of undefined', function (st) {
st.equal(qs.stringify({ a: undefined }), '');
st.equal(qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true }), 'a%5Bc%5D');
st.equal(qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false }), 'a%5Bc%5D=');
st.equal(qs.stringify({ a: { b: undefined, c: '' } }), 'a%5Bc%5D=');
st.end();
});
t.test('url encodes values', function (st) {
st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
st.end();
});
t.test('stringifies a date', function (st) {
var now = new Date();
var str = 'a=' + encodeURIComponent(now.toISOString());
st.equal(qs.stringify({ a: now }), str);
st.end();
});
t.test('stringifies the weird object from qs', function (st) {
st.equal(qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' }), 'my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F');
st.end();
});
t.test('skips properties that are part of the object prototype', function (st) {
Object.prototype.crash = 'test';
st.equal(qs.stringify({ a: 'b' }), 'a=b');
st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
delete Object.prototype.crash;
st.end();
});
t.test('stringifies boolean values', function (st) {
st.equal(qs.stringify({ a: true }), 'a=true');
st.equal(qs.stringify({ a: { b: true } }), 'a%5Bb%5D=true');
st.equal(qs.stringify({ b: false }), 'b=false');
st.equal(qs.stringify({ b: { c: false } }), 'b%5Bc%5D=false');
st.end();
});
t.test('stringifies buffer values', function (st) {
st.equal(qs.stringify({ a: SaferBuffer.from('test') }), 'a=test');
st.equal(qs.stringify({ a: { b: SaferBuffer.from('test') } }), 'a%5Bb%5D=test');
st.end();
});
t.test('stringifies an object using an alternative delimiter', function (st) {
st.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
st.end();
});
t.test('doesn\'t blow up when Buffer global is missing', function (st) {
var tempBuffer = global.Buffer;
delete global.Buffer;
var result = qs.stringify({ a: 'b', c: 'd' });
global.Buffer = tempBuffer;
st.equal(result, 'a=b&c=d');
st.end();
});
t.test('selects properties when filter=array', function (st) {
st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b');
st.equal(qs.stringify({ a: 1 }, { filter: [] }), '');
st.equal(
qs.stringify(
{ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
{ filter: ['a', 'b', 0, 2], arrayFormat: 'indices' }
),
'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3',
'indices => indices'
);
st.equal(
qs.stringify(
{ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
{ filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' }
),
'a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3',
'brackets => brackets'
);
st.equal(
qs.stringify(
{ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
{ filter: ['a', 'b', 0, 2] }
),
'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3',
'default => indices'
);
st.end();
});
t.test('supports custom representations when filter=function', function (st) {
var calls = 0;
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
var filterFunc = function (prefix, value) {
calls += 1;
if (calls === 1) {
st.equal(prefix, '', 'prefix is empty');
st.equal(value, obj);
} else if (prefix === 'c') {
return void 0;
} else if (value instanceof Date) {
st.equal(prefix, 'e[f]');
return value.getTime();
}
return value;
};
st.equal(qs.stringify(obj, { filter: filterFunc }), 'a=b&e%5Bf%5D=1257894000000');
st.equal(calls, 5);
st.end();
});
t.test('can disable uri encoding', function (st) {
st.equal(qs.stringify({ a: 'b' }, { encode: false }), 'a=b');
st.equal(qs.stringify({ a: { b: 'c' } }, { encode: false }), 'a[b]=c');
st.equal(qs.stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false }), 'a=b&c');
st.end();
});
t.test('can sort the keys', function (st) {
var sort = function (a, b) {
return a.localeCompare(b);
};
st.equal(qs.stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort }), 'a=c&b=f&z=y');
st.equal(qs.stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort }), 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a');
st.end();
});
t.test('can sort the keys at depth 3 or more too', function (st) {
var sort = function (a, b) {
return a.localeCompare(b);
};
st.equal(
qs.stringify(
{ a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' },
{ sort: sort, encode: false }
),
'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb'
);
st.equal(
qs.stringify(
{ a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' },
{ sort: null, encode: false }
),
'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b'
);
st.end();
});
t.test('can stringify with custom encoding', function (st) {
st.equal(qs.stringify({ : '大阪府', '': '' }, {
encoder: function (str) {
if (str.length === 0) {
return '';
}
var buf = iconv.encode(str, 'shiftjis');
var result = [];
for (var i = 0; i < buf.length; ++i) {
result.push(buf.readUInt8(i).toString(16));
}
return '%' + result.join('%');
}
}), '%8c%a7=%91%e5%8d%e3%95%7b&=');
st.end();
});
t.test('receives the default encoder as a second argument', function (st) {
st.plan(2);
qs.stringify({ a: 1 }, {
encoder: function (str, defaultEncoder) {
st.equal(defaultEncoder, utils.encode);
}
});
st.end();
});
t.test('throws error with wrong encoder', function (st) {
st['throws'](function () {
qs.stringify({}, { encoder: 'string' });
}, new TypeError('Encoder has to be a function.'));
st.end();
});
t.test('can use custom encoder for a buffer object', { skip: typeof Buffer === 'undefined' }, function (st) {
st.equal(qs.stringify({ a: SaferBuffer.from([1]) }, {
encoder: function (buffer) {
if (typeof buffer === 'string') {
return buffer;
}
return String.fromCharCode(buffer.readUInt8(0) + 97);
}
}), 'a=b');
st.equal(qs.stringify({ a: SaferBuffer.from('a b') }, {
encoder: function (buffer) {
return buffer;
}
}), 'a=a b');
st.end();
});
t.test('serializeDate option', function (st) {
var date = new Date();
st.equal(
qs.stringify({ a: date }),
'a=' + date.toISOString().replace(/:/g, '%3A'),
'default is toISOString'
);
var mutatedDate = new Date();
mutatedDate.toISOString = function () {
throw new SyntaxError();
};
st['throws'](function () {
mutatedDate.toISOString();
}, SyntaxError);
st.equal(
qs.stringify({ a: mutatedDate }),
'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'),
'toISOString works even when method is not locally present'
);
var specificDate = new Date(6);
st.equal(
qs.stringify(
{ a: specificDate },
{ serializeDate: function (d) { return d.getTime() * 7; } }
),
'a=42',
'custom serializeDate function called'
);
st.end();
});
t.test('RFC 1738 spaces serialization', function (st) {
st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d');
st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC1738 }), 'a+b=a+b');
st.end();
});
t.test('RFC 3986 spaces serialization', function (st) {
st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d');
st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC3986 }), 'a%20b=a%20b');
st.end();
});
t.test('Backward compatibility to RFC 3986', function (st) {
st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }), 'a%20b=a%20b');
st.end();
});
t.test('Edge cases and unknown formats', function (st) {
['UFO1234', false, 1234, null, {}, []].forEach(
function (format) {
st['throws'](
function () {
qs.stringify({ a: 'b c' }, { format: format });
},
new TypeError('Unknown format option provided.')
);
}
);
st.end();
});
t.test('encodeValuesOnly', function (st) {
st.equal(
qs.stringify(
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
{ encodeValuesOnly: true }
),
'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h'
);
st.equal(
qs.stringify(
{ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }
),
'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h'
);
st.end();
});
t.test('encodeValuesOnly - strictNullHandling', function (st) {
st.equal(
qs.stringify(
{ a: { b: null } },
{ encodeValuesOnly: true, strictNullHandling: true }
),
'a[b]'
);
st.end();
});
t.test('throws if an invalid charset is specified', function (st) {
st['throws'](function () {
qs.stringify({ a: 'b' }, { charset: 'foobar' });
}, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'));
st.end();
});
t.test('respects a charset of iso-8859-1', function (st) {
st.equal(qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' }), '%E6=%E6');
st.end();
});
t.test('encodes unrepresentable chars as numeric entities in iso-8859-1 mode', function (st) {
st.equal(qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' }), 'a=%26%239786%3B');
st.end();
});
t.test('respects an explicit charset of utf-8 (the default)', function (st) {
st.equal(qs.stringify({ a: 'æ' }, { charset: 'utf-8' }), 'a=%C3%A6');
st.end();
});
t.test('adds the right sentinel when instructed to and the charset is utf-8', function (st) {
st.equal(qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' }), 'utf8=%E2%9C%93&a=%C3%A6');
st.end();
});
t.test('adds the right sentinel when instructed to and the charset is iso-8859-1', function (st) {
st.equal(qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' }), 'utf8=%26%2310003%3B&a=%E6');
st.end();
});
t.test('does not mutate the options argument', function (st) {
var options = {};
qs.stringify({}, options);
st.deepEqual(options, {});
st.end();
});
t.test('strictNullHandling works with custom filter', function (st) {
var filter = function (prefix, value) {
return value;
};
var options = { strictNullHandling: true, filter: filter };
st.equal(qs.stringify({ key: null }, options), 'key');
st.end();
});
t.test('strictNullHandling works with null serializeDate', function (st) {
var serializeDate = function () {
return null;
};
var options = { strictNullHandling: true, serializeDate: serializeDate };
var date = new Date();
st.equal(qs.stringify({ key: date }, options), 'key');
st.end();
});
t.test('allows for encoding keys and values differently', function (st) {
var encoder = function (str, defaultEncoder, charset, type) {
if (type === 'key') {
return defaultEncoder(str, defaultEncoder, charset, type).toLowerCase();
}
if (type === 'value') {
return defaultEncoder(str, defaultEncoder, charset, type).toUpperCase();
}
throw 'this should never happen! type: ' + type;
};
st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
st.end();
});
t.end();
});

136
node_modules/qs/test/utils.js generated vendored Normal file
View File

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

View File

@ -1,4 +1,4 @@
export { BasicCredentialHandler } from "./handlers/basiccreds"; export { BasicCredentialHandler } from "./handlers/basiccreds";
export { BearerCredentialHandler } from "./handlers/bearertoken"; export { BearerCredentialHandler } from "./handlers/bearertoken";
export { NtlmCredentialHandler } from "./handlers/ntlm"; export { NtlmCredentialHandler } from "./handlers/ntlm";
export { PersonalAccessTokenCredentialHandler } from "./handlers/personalaccesstoken"; export { PersonalAccessTokenCredentialHandler } from "./handlers/personalaccesstoken";

View File

@ -1,10 +1,10 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
var basiccreds_1 = require("./handlers/basiccreds"); var basiccreds_1 = require("./handlers/basiccreds");
exports.BasicCredentialHandler = basiccreds_1.BasicCredentialHandler; exports.BasicCredentialHandler = basiccreds_1.BasicCredentialHandler;
var bearertoken_1 = require("./handlers/bearertoken"); var bearertoken_1 = require("./handlers/bearertoken");
exports.BearerCredentialHandler = bearertoken_1.BearerCredentialHandler; exports.BearerCredentialHandler = bearertoken_1.BearerCredentialHandler;
var ntlm_1 = require("./handlers/ntlm"); var ntlm_1 = require("./handlers/ntlm");
exports.NtlmCredentialHandler = ntlm_1.NtlmCredentialHandler; exports.NtlmCredentialHandler = ntlm_1.NtlmCredentialHandler;
var personalaccesstoken_1 = require("./handlers/personalaccesstoken"); var personalaccesstoken_1 = require("./handlers/personalaccesstoken");
exports.PersonalAccessTokenCredentialHandler = personalaccesstoken_1.PersonalAccessTokenCredentialHandler; exports.PersonalAccessTokenCredentialHandler = personalaccesstoken_1.PersonalAccessTokenCredentialHandler;

View File

@ -1,103 +1,105 @@
/// <reference types="node" /> /// <reference types="node" />
import url = require("url"); import url = require("url");
import http = require("http"); import http = require("http");
import ifm = require('./Interfaces'); import ifm = require('./Interfaces');
export declare enum HttpCodes { export declare enum HttpCodes {
OK = 200, OK = 200,
MultipleChoices = 300, MultipleChoices = 300,
MovedPermanently = 301, MovedPermanently = 301,
ResourceMoved = 302, ResourceMoved = 302,
SeeOther = 303, SeeOther = 303,
NotModified = 304, NotModified = 304,
UseProxy = 305, UseProxy = 305,
SwitchProxy = 306, SwitchProxy = 306,
TemporaryRedirect = 307, TemporaryRedirect = 307,
PermanentRedirect = 308, PermanentRedirect = 308,
BadRequest = 400, BadRequest = 400,
Unauthorized = 401, Unauthorized = 401,
PaymentRequired = 402, PaymentRequired = 402,
Forbidden = 403, Forbidden = 403,
NotFound = 404, NotFound = 404,
MethodNotAllowed = 405, MethodNotAllowed = 405,
NotAcceptable = 406, NotAcceptable = 406,
ProxyAuthenticationRequired = 407, ProxyAuthenticationRequired = 407,
RequestTimeout = 408, RequestTimeout = 408,
Conflict = 409, Conflict = 409,
Gone = 410, Gone = 410,
InternalServerError = 500, TooManyRequests = 429,
NotImplemented = 501, InternalServerError = 500,
BadGateway = 502, NotImplemented = 501,
ServiceUnavailable = 503, BadGateway = 502,
GatewayTimeout = 504, ServiceUnavailable = 503,
} GatewayTimeout = 504
export declare class HttpClientResponse implements ifm.IHttpClientResponse { }
constructor(message: http.IncomingMessage); export declare class HttpClientResponse implements ifm.IHttpClientResponse {
message: http.IncomingMessage; constructor(message: http.IncomingMessage);
readBody(): Promise<string>; message: http.IncomingMessage;
} readBody(): Promise<string>;
export interface RequestInfo { }
options: http.RequestOptions; export interface RequestInfo {
parsedUrl: url.Url; options: http.RequestOptions;
httpModule: any; parsedUrl: url.Url;
} httpModule: any;
export declare function isHttps(requestUrl: string): boolean; }
export declare class HttpClient implements ifm.IHttpClient { export declare function isHttps(requestUrl: string): boolean;
userAgent: string; export declare class HttpClient implements ifm.IHttpClient {
handlers: ifm.IRequestHandler[]; userAgent: string | null | undefined;
requestOptions: ifm.IRequestOptions; handlers: ifm.IRequestHandler[];
private _ignoreSslError; requestOptions: ifm.IRequestOptions;
private _socketTimeout; private _ignoreSslError;
private _httpProxy; private _socketTimeout;
private _httpProxyBypassHosts; private _httpProxy;
private _allowRedirects; private _httpProxyBypassHosts;
private _maxRedirects; private _allowRedirects;
private _allowRetries; private _allowRedirectDowngrade;
private _maxRetries; private _maxRedirects;
private _agent; private _allowRetries;
private _proxyAgent; private _maxRetries;
private _keepAlive; private _agent;
private _disposed; private _proxyAgent;
private _certConfig; private _keepAlive;
private _ca; private _disposed;
private _cert; private _certConfig;
private _key; private _ca;
constructor(userAgent: string, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions); private _cert;
options(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; private _key;
get(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; constructor(userAgent: string | null | undefined, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions);
del(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; options(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
post(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; get(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
patch(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; del(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
put(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; post(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
head(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; patch(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; put(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
/** head(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
* Makes a raw http request. sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
* All other methods such as get, post, patch, and request ultimately call this. /**
* Prefer get, del, post and patch * Makes a raw http request.
*/ * All other methods such as get, post, patch, and request ultimately call this.
request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; * Prefer get, del, post and patch
/** */
* Needs to be called if keepAlive is set to true in request options. request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
*/ /**
dispose(): void; * Needs to be called if keepAlive is set to true in request options.
/** */
* Raw request. dispose(): void;
* @param info /**
* @param data * Raw request.
*/ * @param info
requestRaw(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream): Promise<ifm.IHttpClientResponse>; * @param data
/** */
* Raw request with callback. requestRaw(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream): Promise<ifm.IHttpClientResponse>;
* @param info /**
* @param data * Raw request with callback.
* @param onResult * @param info
*/ * @param data
requestRawWithCallback(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: ifm.IHttpClientResponse) => void): void; * @param onResult
private _prepareRequest(method, requestUrl, headers); */
private _isPresigned(requestUrl); requestRawWithCallback(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: ifm.IHttpClientResponse) => void): void;
private _mergeHeaders(headers); private _prepareRequest;
private _getAgent(requestUrl); private _isPresigned;
private _getProxy(requestUrl); private _mergeHeaders;
private _isBypassProxy(requestUrl); private _getAgent;
private _performExponentialBackoff(retryNumber); private _getProxy;
} private _isMatchInBypassProxyList;
private _performExponentialBackoff;
}

View File

@ -1,455 +1,487 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) { return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const url = require("url"); const url = require("url");
const http = require("http"); const http = require("http");
const https = require("https"); const https = require("https");
let fs; const util = require("./Util");
let tunnel; let fs;
var HttpCodes; let tunnel;
(function (HttpCodes) { var HttpCodes;
HttpCodes[HttpCodes["OK"] = 200] = "OK"; (function (HttpCodes) {
HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; HttpCodes[HttpCodes["OK"] = 200] = "OK";
HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices";
HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently";
HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved";
HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther";
HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified";
HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy";
HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy";
HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect";
HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect";
HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest";
HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized";
HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired";
HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden";
HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound";
HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed";
HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable";
HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout";
HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict";
HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; HttpCodes[HttpCodes["Gone"] = 410] = "Gone";
HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests";
HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError";
HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented";
HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway";
})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {})); HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable";
const HttpRedirectCodes = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect]; HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout";
const HttpResponseRetryCodes = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout]; })(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {}));
const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; const HttpRedirectCodes = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect];
const ExponentialBackoffCeiling = 10; const HttpResponseRetryCodes = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout];
const ExponentialBackoffTimeSlice = 5; const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
class HttpClientResponse { const ExponentialBackoffCeiling = 10;
constructor(message) { const ExponentialBackoffTimeSlice = 5;
this.message = message; class HttpClientResponse {
} constructor(message) {
readBody() { this.message = message;
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { }
let output = ''; readBody() {
this.message.on('data', (chunk) => { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
output += chunk; let buffer = Buffer.alloc(0);
}); const encodingCharset = util.obtainContentCharset(this);
this.message.on('end', () => { // Extract Encoding from header: 'content-encoding'
resolve(output); // Match `gzip`, `gzip, deflate` variations of GZIP encoding
}); const contentEncoding = this.message.headers['content-encoding'] || '';
})); const isGzippedEncoded = new RegExp('(gzip$)|(gzip, *deflate)').test(contentEncoding);
} this.message.on('data', function (data) {
} const chunk = (typeof data === 'string') ? Buffer.from(data, encodingCharset) : data;
exports.HttpClientResponse = HttpClientResponse; buffer = Buffer.concat([buffer, chunk]);
function isHttps(requestUrl) { }).on('end', function () {
let parsedUrl = url.parse(requestUrl); return __awaiter(this, void 0, void 0, function* () {
return parsedUrl.protocol === 'https:'; if (isGzippedEncoded) { // Process GZipped Response Body HERE
} const gunzippedBody = yield util.decompressGzippedContent(buffer, encodingCharset);
exports.isHttps = isHttps; resolve(gunzippedBody);
var EnvironmentVariables; }
(function (EnvironmentVariables) { resolve(buffer.toString(encodingCharset));
EnvironmentVariables["HTTP_PROXY"] = "HTTP_PROXY"; });
EnvironmentVariables["HTTPS_PROXY"] = "HTTPS_PROXY"; }).on('error', function (err) {
})(EnvironmentVariables || (EnvironmentVariables = {})); reject(err);
class HttpClient { });
constructor(userAgent, handlers, requestOptions) { }));
this._ignoreSslError = false; }
this._allowRedirects = true; }
this._maxRedirects = 50; exports.HttpClientResponse = HttpClientResponse;
this._allowRetries = false; function isHttps(requestUrl) {
this._maxRetries = 1; let parsedUrl = url.parse(requestUrl);
this._keepAlive = false; return parsedUrl.protocol === 'https:';
this._disposed = false; }
this.userAgent = userAgent; exports.isHttps = isHttps;
this.handlers = handlers || []; var EnvironmentVariables;
this.requestOptions = requestOptions; (function (EnvironmentVariables) {
if (requestOptions) { EnvironmentVariables["HTTP_PROXY"] = "HTTP_PROXY";
if (requestOptions.ignoreSslError != null) { EnvironmentVariables["HTTPS_PROXY"] = "HTTPS_PROXY";
this._ignoreSslError = requestOptions.ignoreSslError; EnvironmentVariables["NO_PROXY"] = "NO_PROXY";
} })(EnvironmentVariables || (EnvironmentVariables = {}));
this._socketTimeout = requestOptions.socketTimeout; class HttpClient {
this._httpProxy = requestOptions.proxy; constructor(userAgent, handlers, requestOptions) {
if (requestOptions.proxy && requestOptions.proxy.proxyBypassHosts) { this._ignoreSslError = false;
this._httpProxyBypassHosts = []; this._allowRedirects = true;
requestOptions.proxy.proxyBypassHosts.forEach(bypass => { this._allowRedirectDowngrade = false;
this._httpProxyBypassHosts.push(new RegExp(bypass, 'i')); this._maxRedirects = 50;
}); this._allowRetries = false;
} this._maxRetries = 1;
this._certConfig = requestOptions.cert; this._keepAlive = false;
if (this._certConfig) { this._disposed = false;
// If using cert, need fs this.userAgent = userAgent;
fs = require('fs'); this.handlers = handlers || [];
// cache the cert content into memory, so we don't have to read it from disk every time let no_proxy = process.env[EnvironmentVariables.NO_PROXY];
if (this._certConfig.caFile && fs.existsSync(this._certConfig.caFile)) { if (no_proxy) {
this._ca = fs.readFileSync(this._certConfig.caFile, 'utf8'); this._httpProxyBypassHosts = [];
} no_proxy.split(',').forEach(bypass => {
if (this._certConfig.certFile && fs.existsSync(this._certConfig.certFile)) { this._httpProxyBypassHosts.push(new RegExp(bypass, 'i'));
this._cert = fs.readFileSync(this._certConfig.certFile, 'utf8'); });
} }
if (this._certConfig.keyFile && fs.existsSync(this._certConfig.keyFile)) { this.requestOptions = requestOptions;
this._key = fs.readFileSync(this._certConfig.keyFile, 'utf8'); if (requestOptions) {
} if (requestOptions.ignoreSslError != null) {
} this._ignoreSslError = requestOptions.ignoreSslError;
if (requestOptions.allowRedirects != null) { }
this._allowRedirects = requestOptions.allowRedirects; this._socketTimeout = requestOptions.socketTimeout;
} this._httpProxy = requestOptions.proxy;
if (requestOptions.maxRedirects != null) { if (requestOptions.proxy && requestOptions.proxy.proxyBypassHosts) {
this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); this._httpProxyBypassHosts = [];
} requestOptions.proxy.proxyBypassHosts.forEach(bypass => {
if (requestOptions.keepAlive != null) { this._httpProxyBypassHosts.push(new RegExp(bypass, 'i'));
this._keepAlive = requestOptions.keepAlive; });
} }
if (requestOptions.allowRetries != null) { this._certConfig = requestOptions.cert;
this._allowRetries = requestOptions.allowRetries; if (this._certConfig) {
} // If using cert, need fs
if (requestOptions.maxRetries != null) { fs = require('fs');
this._maxRetries = requestOptions.maxRetries; // cache the cert content into memory, so we don't have to read it from disk every time
} if (this._certConfig.caFile && fs.existsSync(this._certConfig.caFile)) {
} this._ca = fs.readFileSync(this._certConfig.caFile, 'utf8');
} }
options(requestUrl, additionalHeaders) { if (this._certConfig.certFile && fs.existsSync(this._certConfig.certFile)) {
return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); this._cert = fs.readFileSync(this._certConfig.certFile, 'utf8');
} }
get(requestUrl, additionalHeaders) { if (this._certConfig.keyFile && fs.existsSync(this._certConfig.keyFile)) {
return this.request('GET', requestUrl, null, additionalHeaders || {}); this._key = fs.readFileSync(this._certConfig.keyFile, 'utf8');
} }
del(requestUrl, additionalHeaders) { }
return this.request('DELETE', requestUrl, null, additionalHeaders || {}); if (requestOptions.allowRedirects != null) {
} this._allowRedirects = requestOptions.allowRedirects;
post(requestUrl, data, additionalHeaders) { }
return this.request('POST', requestUrl, data, additionalHeaders || {}); if (requestOptions.allowRedirectDowngrade != null) {
} this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade;
patch(requestUrl, data, additionalHeaders) { }
return this.request('PATCH', requestUrl, data, additionalHeaders || {}); if (requestOptions.maxRedirects != null) {
} this._maxRedirects = Math.max(requestOptions.maxRedirects, 0);
put(requestUrl, data, additionalHeaders) { }
return this.request('PUT', requestUrl, data, additionalHeaders || {}); if (requestOptions.keepAlive != null) {
} this._keepAlive = requestOptions.keepAlive;
head(requestUrl, additionalHeaders) { }
return this.request('HEAD', requestUrl, null, additionalHeaders || {}); if (requestOptions.allowRetries != null) {
} this._allowRetries = requestOptions.allowRetries;
sendStream(verb, requestUrl, stream, additionalHeaders) { }
return this.request(verb, requestUrl, stream, additionalHeaders); if (requestOptions.maxRetries != null) {
} this._maxRetries = requestOptions.maxRetries;
/** }
* Makes a raw http request. }
* All other methods such as get, post, patch, and request ultimately call this. }
* Prefer get, del, post and patch options(requestUrl, additionalHeaders) {
*/ return this.request('OPTIONS', requestUrl, null, additionalHeaders || {});
request(verb, requestUrl, data, headers) { }
return __awaiter(this, void 0, void 0, function* () { get(requestUrl, additionalHeaders) {
if (this._disposed) { return this.request('GET', requestUrl, null, additionalHeaders || {});
throw new Error("Client has already been disposed."); }
} del(requestUrl, additionalHeaders) {
let info = this._prepareRequest(verb, requestUrl, headers); return this.request('DELETE', requestUrl, null, additionalHeaders || {});
// Only perform retries on reads since writes may not be idempotent. }
let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1; post(requestUrl, data, additionalHeaders) {
let numTries = 0; return this.request('POST', requestUrl, data, additionalHeaders || {});
let response; }
while (numTries < maxTries) { patch(requestUrl, data, additionalHeaders) {
response = yield this.requestRaw(info, data); return this.request('PATCH', requestUrl, data, additionalHeaders || {});
// Check if it's an authentication challenge }
if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { put(requestUrl, data, additionalHeaders) {
let authenticationHandler; return this.request('PUT', requestUrl, data, additionalHeaders || {});
for (let i = 0; i < this.handlers.length; i++) { }
if (this.handlers[i].canHandleAuthentication(response)) { head(requestUrl, additionalHeaders) {
authenticationHandler = this.handlers[i]; return this.request('HEAD', requestUrl, null, additionalHeaders || {});
break; }
} sendStream(verb, requestUrl, stream, additionalHeaders) {
} return this.request(verb, requestUrl, stream, additionalHeaders);
if (authenticationHandler) { }
return authenticationHandler.handleAuthentication(this, info, data); /**
} * Makes a raw http request.
else { * All other methods such as get, post, patch, and request ultimately call this.
// We have received an unauthorized response but have no handlers to handle it. * Prefer get, del, post and patch
// Let the response return to the caller. */
return response; request(verb, requestUrl, data, headers) {
} return __awaiter(this, void 0, void 0, function* () {
} if (this._disposed) {
let redirectsRemaining = this._maxRedirects; throw new Error("Client has already been disposed.");
while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 }
&& this._allowRedirects let parsedUrl = url.parse(requestUrl);
&& redirectsRemaining > 0) { let info = this._prepareRequest(verb, parsedUrl, headers);
const redirectUrl = response.message.headers["location"]; // Only perform retries on reads since writes may not be idempotent.
if (!redirectUrl) { let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1;
// if there's no location to redirect to, we won't let numTries = 0;
break; let response;
} while (numTries < maxTries) {
// we need to finish reading the response before reassigning response response = yield this.requestRaw(info, data);
// which will leak the open socket. // Check if it's an authentication challenge
yield response.readBody(); if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) {
// let's make the request with the new redirectUrl let authenticationHandler;
info = this._prepareRequest(verb, redirectUrl, headers); for (let i = 0; i < this.handlers.length; i++) {
response = yield this.requestRaw(info, data); if (this.handlers[i].canHandleAuthentication(response)) {
redirectsRemaining--; authenticationHandler = this.handlers[i];
} break;
if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) { }
// If not a retry code, return immediately instead of retrying }
return response; if (authenticationHandler) {
} return authenticationHandler.handleAuthentication(this, info, data);
numTries += 1; }
if (numTries < maxTries) { else {
yield response.readBody(); // We have received an unauthorized response but have no handlers to handle it.
yield this._performExponentialBackoff(numTries); // Let the response return to the caller.
} return response;
} }
return response; }
}); let redirectsRemaining = this._maxRedirects;
} while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1
/** && this._allowRedirects
* Needs to be called if keepAlive is set to true in request options. && redirectsRemaining > 0) {
*/ const redirectUrl = response.message.headers["location"];
dispose() { if (!redirectUrl) {
if (this._agent) { // if there's no location to redirect to, we won't
this._agent.destroy(); break;
} }
this._disposed = true; let parsedRedirectUrl = url.parse(redirectUrl);
} if (parsedUrl.protocol == 'https:' && parsedUrl.protocol != parsedRedirectUrl.protocol && !this._allowRedirectDowngrade) {
/** throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.");
* Raw request. }
* @param info // we need to finish reading the response before reassigning response
* @param data // which will leak the open socket.
*/ yield response.readBody();
requestRaw(info, data) { // let's make the request with the new redirectUrl
return new Promise((resolve, reject) => { info = this._prepareRequest(verb, parsedRedirectUrl, headers);
let callbackForResult = function (err, res) { response = yield this.requestRaw(info, data);
if (err) { redirectsRemaining--;
reject(err); }
} if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
resolve(res); // If not a retry code, return immediately instead of retrying
}; return response;
this.requestRawWithCallback(info, data, callbackForResult); }
}); numTries += 1;
} if (numTries < maxTries) {
/** yield response.readBody();
* Raw request with callback. yield this._performExponentialBackoff(numTries);
* @param info }
* @param data }
* @param onResult return response;
*/ });
requestRawWithCallback(info, data, onResult) { }
let socket; /**
let isDataString = typeof (data) === 'string'; * Needs to be called if keepAlive is set to true in request options.
if (typeof (data) === 'string') { */
info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8'); dispose() {
} if (this._agent) {
let callbackCalled = false; this._agent.destroy();
let handleResult = (err, res) => { }
if (!callbackCalled) { this._disposed = true;
callbackCalled = true; }
onResult(err, res); /**
} * Raw request.
}; * @param info
let req = info.httpModule.request(info.options, (msg) => { * @param data
let res = new HttpClientResponse(msg); */
handleResult(null, res); requestRaw(info, data) {
}); return new Promise((resolve, reject) => {
req.on('socket', (sock) => { let callbackForResult = function (err, res) {
socket = sock; if (err) {
}); reject(err);
// If we ever get disconnected, we want the socket to timeout eventually }
req.setTimeout(this._socketTimeout || 3 * 60000, () => { resolve(res);
if (socket) { };
socket.end(); this.requestRawWithCallback(info, data, callbackForResult);
} });
handleResult(new Error('Request timeout: ' + info.options.path), null); }
}); /**
req.on('error', function (err) { * Raw request with callback.
// err has statusCode property * @param info
// res should have headers * @param data
handleResult(err, null); * @param onResult
}); */
if (data && typeof (data) === 'string') { requestRawWithCallback(info, data, onResult) {
req.write(data, 'utf8'); let socket;
} let isDataString = typeof (data) === 'string';
if (data && typeof (data) !== 'string') { if (typeof (data) === 'string') {
data.on('close', function () { info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8');
req.end(); }
}); let callbackCalled = false;
data.pipe(req); let handleResult = (err, res) => {
} if (!callbackCalled) {
else { callbackCalled = true;
req.end(); onResult(err, res);
} }
} };
_prepareRequest(method, requestUrl, headers) { let req = info.httpModule.request(info.options, (msg) => {
const info = {}; let res = new HttpClientResponse(msg);
info.parsedUrl = url.parse(requestUrl); handleResult(null, res);
const usingSsl = info.parsedUrl.protocol === 'https:'; });
info.httpModule = usingSsl ? https : http; req.on('socket', (sock) => {
const defaultPort = usingSsl ? 443 : 80; socket = sock;
info.options = {}; });
info.options.host = info.parsedUrl.hostname; // If we ever get disconnected, we want the socket to timeout eventually
info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort; req.setTimeout(this._socketTimeout || 3 * 60000, () => {
info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); if (socket) {
info.options.method = method; socket.end();
info.options.headers = this._mergeHeaders(headers); }
info.options.headers["user-agent"] = this.userAgent; handleResult(new Error('Request timeout: ' + info.options.path), null);
info.options.agent = this._getAgent(requestUrl); });
// gives handlers an opportunity to participate req.on('error', function (err) {
if (this.handlers && !this._isPresigned(requestUrl)) { // err has statusCode property
this.handlers.forEach((handler) => { // res should have headers
handler.prepareRequest(info.options); handleResult(err, null);
}); });
} if (data && typeof (data) === 'string') {
return info; req.write(data, 'utf8');
} }
_isPresigned(requestUrl) { if (data && typeof (data) !== 'string') {
if (this.requestOptions && this.requestOptions.presignedUrlPatterns) { data.on('close', function () {
const patterns = this.requestOptions.presignedUrlPatterns; req.end();
for (let i = 0; i < patterns.length; i++) { });
if (requestUrl.match(patterns[i])) { data.pipe(req);
return true; }
} else {
} req.end();
} }
return false; }
} _prepareRequest(method, requestUrl, headers) {
_mergeHeaders(headers) { const info = {};
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {}); info.parsedUrl = requestUrl;
if (this.requestOptions && this.requestOptions.headers) { const usingSsl = info.parsedUrl.protocol === 'https:';
return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers)); info.httpModule = usingSsl ? https : http;
} const defaultPort = usingSsl ? 443 : 80;
return lowercaseKeys(headers || {}); info.options = {};
} info.options.host = info.parsedUrl.hostname;
_getAgent(requestUrl) { info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort;
let agent; info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
let proxy = this._getProxy(requestUrl); info.options.method = method;
let useProxy = proxy.proxyUrl && proxy.proxyUrl.hostname && !this._isBypassProxy(requestUrl); info.options.headers = this._mergeHeaders(headers);
if (this._keepAlive && useProxy) { if (this.userAgent != null) {
agent = this._proxyAgent; info.options.headers["user-agent"] = this.userAgent;
} }
if (this._keepAlive && !useProxy) { info.options.agent = this._getAgent(info.parsedUrl);
agent = this._agent; // gives handlers an opportunity to participate
} if (this.handlers && !this._isPresigned(url.format(requestUrl))) {
// if agent is already assigned use that agent. this.handlers.forEach((handler) => {
if (!!agent) { handler.prepareRequest(info.options);
return agent; });
} }
let parsedUrl = url.parse(requestUrl); return info;
const usingSsl = parsedUrl.protocol === 'https:'; }
let maxSockets = 100; _isPresigned(requestUrl) {
if (!!this.requestOptions) { if (this.requestOptions && this.requestOptions.presignedUrlPatterns) {
maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; const patterns = this.requestOptions.presignedUrlPatterns;
} for (let i = 0; i < patterns.length; i++) {
if (useProxy) { if (requestUrl.match(patterns[i])) {
// If using proxy, need tunnel return true;
if (!tunnel) { }
tunnel = require('tunnel'); }
} }
const agentOptions = { return false;
maxSockets: maxSockets, }
keepAlive: this._keepAlive, _mergeHeaders(headers) {
proxy: { const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {});
proxyAuth: proxy.proxyAuth, if (this.requestOptions && this.requestOptions.headers) {
host: proxy.proxyUrl.hostname, return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
port: proxy.proxyUrl.port }
}, return lowercaseKeys(headers || {});
}; }
let tunnelAgent; _getAgent(parsedUrl) {
const overHttps = proxy.proxyUrl.protocol === 'https:'; let agent;
if (usingSsl) { let proxy = this._getProxy(parsedUrl);
tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; let useProxy = proxy.proxyUrl && proxy.proxyUrl.hostname && !this._isMatchInBypassProxyList(parsedUrl);
} if (this._keepAlive && useProxy) {
else { agent = this._proxyAgent;
tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; }
} if (this._keepAlive && !useProxy) {
agent = tunnelAgent(agentOptions); agent = this._agent;
this._proxyAgent = agent; }
} // if agent is already assigned use that agent.
// if reusing agent across request and tunneling agent isn't assigned create a new agent if (!!agent) {
if (this._keepAlive && !agent) { return agent;
const options = { keepAlive: this._keepAlive, maxSockets: maxSockets }; }
agent = usingSsl ? new https.Agent(options) : new http.Agent(options); const usingSsl = parsedUrl.protocol === 'https:';
this._agent = agent; let maxSockets = 100;
} if (!!this.requestOptions) {
// if not using private agent and tunnel agent isn't setup then use global agent maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets;
if (!agent) { }
agent = usingSsl ? https.globalAgent : http.globalAgent; if (useProxy) {
} // If using proxy, need tunnel
if (usingSsl && this._ignoreSslError) { if (!tunnel) {
// we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process tunnel = require('tunnel');
// http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options }
// we have to cast it to any and change it directly const agentOptions = {
agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false }); maxSockets: maxSockets,
} keepAlive: this._keepAlive,
if (usingSsl && this._certConfig) { proxy: {
agent.options = Object.assign(agent.options || {}, { ca: this._ca, cert: this._cert, key: this._key, passphrase: this._certConfig.passphrase }); proxyAuth: proxy.proxyAuth,
} host: proxy.proxyUrl.hostname,
return agent; port: proxy.proxyUrl.port
} },
_getProxy(requestUrl) { };
const parsedUrl = url.parse(requestUrl); let tunnelAgent;
let usingSsl = parsedUrl.protocol === 'https:'; const overHttps = proxy.proxyUrl.protocol === 'https:';
let proxyConfig = this._httpProxy; if (usingSsl) {
// fallback to http_proxy and https_proxy env tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
let https_proxy = process.env[EnvironmentVariables.HTTPS_PROXY]; }
let http_proxy = process.env[EnvironmentVariables.HTTP_PROXY]; else {
if (!proxyConfig) { tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
if (https_proxy && usingSsl) { }
proxyConfig = { agent = tunnelAgent(agentOptions);
proxyUrl: https_proxy this._proxyAgent = agent;
}; }
} // if reusing agent across request and tunneling agent isn't assigned create a new agent
else if (http_proxy) { if (this._keepAlive && !agent) {
proxyConfig = { const options = { keepAlive: this._keepAlive, maxSockets: maxSockets };
proxyUrl: http_proxy agent = usingSsl ? new https.Agent(options) : new http.Agent(options);
}; this._agent = agent;
} }
} // if not using private agent and tunnel agent isn't setup then use global agent
let proxyUrl; if (!agent) {
let proxyAuth; agent = usingSsl ? https.globalAgent : http.globalAgent;
if (proxyConfig) { }
if (proxyConfig.proxyUrl.length > 0) { if (usingSsl && this._ignoreSslError) {
proxyUrl = url.parse(proxyConfig.proxyUrl); // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
} // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
if (proxyConfig.proxyUsername || proxyConfig.proxyPassword) { // we have to cast it to any and change it directly
proxyAuth = proxyConfig.proxyUsername + ":" + proxyConfig.proxyPassword; agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false });
} }
} if (usingSsl && this._certConfig) {
return { proxyUrl: proxyUrl, proxyAuth: proxyAuth }; agent.options = Object.assign(agent.options || {}, { ca: this._ca, cert: this._cert, key: this._key, passphrase: this._certConfig.passphrase });
} }
_isBypassProxy(requestUrl) { return agent;
if (!this._httpProxyBypassHosts) { }
return false; _getProxy(parsedUrl) {
} let usingSsl = parsedUrl.protocol === 'https:';
let bypass = false; let proxyConfig = this._httpProxy;
this._httpProxyBypassHosts.forEach(bypassHost => { // fallback to http_proxy and https_proxy env
if (bypassHost.test(requestUrl)) { let https_proxy = process.env[EnvironmentVariables.HTTPS_PROXY];
bypass = true; let http_proxy = process.env[EnvironmentVariables.HTTP_PROXY];
} if (!proxyConfig) {
}); if (https_proxy && usingSsl) {
return bypass; proxyConfig = {
} proxyUrl: https_proxy
_performExponentialBackoff(retryNumber) { };
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); }
const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); else if (http_proxy) {
return new Promise(resolve => setTimeout(() => resolve(), ms)); proxyConfig = {
} proxyUrl: http_proxy
} };
exports.HttpClient = HttpClient; }
}
let proxyUrl;
let proxyAuth;
if (proxyConfig) {
if (proxyConfig.proxyUrl.length > 0) {
proxyUrl = url.parse(proxyConfig.proxyUrl);
}
if (proxyConfig.proxyUsername || proxyConfig.proxyPassword) {
proxyAuth = proxyConfig.proxyUsername + ":" + proxyConfig.proxyPassword;
}
}
return { proxyUrl: proxyUrl, proxyAuth: proxyAuth };
}
_isMatchInBypassProxyList(parsedUrl) {
if (!this._httpProxyBypassHosts) {
return false;
}
let bypass = false;
this._httpProxyBypassHosts.forEach(bypassHost => {
if (bypassHost.test(parsedUrl.href)) {
bypass = true;
}
});
return bypass;
}
_performExponentialBackoff(retryNumber) {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
return new Promise(resolve => setTimeout(() => resolve(), ms));
}
}
exports.HttpClient = HttpClient;

View File

@ -0,0 +1 @@
export {};

View File

@ -1,2 +1,2 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -1,62 +1,74 @@
/// <reference types="node" /> /// <reference types="node" />
import http = require("http"); import http = require("http");
import url = require("url"); import url = require("url");
export interface IHeaders { export interface IHeaders {
[key: string]: any; [key: string]: any;
} }
export interface IBasicCredentials { export interface IBasicCredentials {
username: string; username: string;
password: string; password: string;
} }
export interface IHttpClient { export interface IHttpClient {
options(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>; options(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
get(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>; get(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
del(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>; del(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
post(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>; post(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
patch(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>; patch(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
put(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>; put(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>; sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: IHeaders): Promise<IHttpClientResponse>; request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: IHeaders): Promise<IHttpClientResponse>;
requestRaw(info: IRequestInfo, data: string | NodeJS.ReadableStream): Promise<IHttpClientResponse>; requestRaw(info: IRequestInfo, data: string | NodeJS.ReadableStream): Promise<IHttpClientResponse>;
requestRawWithCallback(info: IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: IHttpClientResponse) => void): void; requestRawWithCallback(info: IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: IHttpClientResponse) => void): void;
} }
export interface IRequestHandler { export interface IRequestHandler {
prepareRequest(options: http.RequestOptions): void; prepareRequest(options: http.RequestOptions): void;
canHandleAuthentication(response: IHttpClientResponse): boolean; canHandleAuthentication(response: IHttpClientResponse): boolean;
handleAuthentication(httpClient: IHttpClient, requestInfo: IRequestInfo, objs: any): Promise<IHttpClientResponse>; handleAuthentication(httpClient: IHttpClient, requestInfo: IRequestInfo, objs: any): Promise<IHttpClientResponse>;
} }
export interface IHttpClientResponse { export interface IHttpClientResponse {
message: http.IncomingMessage; message: http.IncomingMessage;
readBody(): Promise<string>; readBody(): Promise<string>;
} }
export interface IRequestInfo { export interface IRequestInfo {
options: http.RequestOptions; options: http.RequestOptions;
parsedUrl: url.Url; parsedUrl: url.Url;
httpModule: any; httpModule: any;
} }
export interface IRequestOptions { export interface IRequestOptions {
headers?: IHeaders; headers?: IHeaders;
socketTimeout?: number; socketTimeout?: number;
ignoreSslError?: boolean; ignoreSslError?: boolean;
proxy?: IProxyConfiguration; proxy?: IProxyConfiguration;
cert?: ICertConfiguration; cert?: ICertConfiguration;
allowRedirects?: boolean; allowRedirects?: boolean;
maxRedirects?: number; allowRedirectDowngrade?: boolean;
maxSockets?: number; maxRedirects?: number;
keepAlive?: boolean; maxSockets?: number;
presignedUrlPatterns?: RegExp[]; keepAlive?: boolean;
allowRetries?: boolean; presignedUrlPatterns?: RegExp[];
maxRetries?: number; allowRetries?: boolean;
} maxRetries?: number;
export interface IProxyConfiguration { }
proxyUrl: string; export interface IProxyConfiguration {
proxyUsername?: string; proxyUrl: string;
proxyPassword?: string; proxyUsername?: string;
proxyBypassHosts?: string[]; proxyPassword?: string;
} proxyBypassHosts?: string[];
export interface ICertConfiguration { }
caFile?: string; export interface ICertConfiguration {
certFile?: string; caFile?: string;
keyFile?: string; certFile?: string;
passphrase?: string; keyFile?: string;
} passphrase?: string;
}
export interface IRequestQueryParams {
options?: {
separator?: string;
arrayFormat?: string;
shouldAllowDots?: boolean;
shouldOnlyEncodeValues?: boolean;
};
params: {
[name: string]: string | number | (string | number)[];
};
}

View File

@ -1,5 +1,5 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
; ;

View File

@ -1,21 +1,21 @@
Typed Rest Client for Node.js Typed Rest Client for Node.js
Copyright (c) Microsoft Corporation Copyright (c) Microsoft Corporation
All rights reserved. All rights reserved.
MIT License MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,100 +1,110 @@
[![Build Status](https://dev.azure.com/ms/typed-rest-client/_apis/build/status/Microsoft.typed-rest-client?branchName=master)](https://dev.azure.com/ms/typed-rest-client/_build/latest?definitionId=42&branchName=master)
<a href="https://github.com/microsoft/typed-rest-client"><img alt="GitHub Actions status" src="https://github.com/microsoft/typed-rest-client/workflows/all-tests/badge.svg"></a>
# Typed REST and HTTP Client with TypeScript Typings
[![Build Status](https://dev.azure.com/ms/typed-rest-client/_apis/build/status/Microsoft.typed-rest-client?branchName=master)](https://dev.azure.com/ms/typed-rest-client/_build/latest?definitionId=42&branchName=master)
A lightweight REST and HTTP client optimized for use with TypeScript with generics and async await.
## Features # Typed REST and HTTP Client with TypeScript Typings
- REST and HTTP client with TypeScript generics and async/await/Promises A lightweight REST and HTTP client optimized for use with TypeScript with generics and async await.
- Typings included so no need to acquire separately (great for intellisense and no versioning drift)
- Basic, Bearer and NTLM Support out of the box. Extensible handlers for others. ## Features
- Proxy support
- Certificate support (Self-signed server and client cert) - REST and HTTP client with TypeScript generics and async/await/Promises
- Redirects supported - Typings included so no need to acquire separately (great for intellisense and no versioning drift)
- Basic, Bearer and NTLM Support out of the box. Extensible handlers for others.
Intellisense and compile support: - Proxy support
- Certificate support (Self-signed server and client cert)
![intellisense](./docs/intellisense.png) - Redirects supported
## Install Intellisense and compile support:
``` ![intellisense](./docs/intellisense.png)
npm install typed-rest-client --save
``` ## Install
Or to install the latest preview: ```
``` npm install typed-rest-client --save
npm install typed-rest-client@preview --save ```
```
Or to install the latest preview:
## Samples ```
npm install typed-rest-client@preview --save
See the [samples](./samples) for complete coding examples. Also see the [REST](./test/tests/resttests.ts) and [HTTP](./test/tests/httptests.ts) tests for detailed examples. ```
## Errors ## Samples
### HTTP See the [samples](./samples) for complete coding examples. Also see the [REST](./test/tests/resttests.ts) and [HTTP](./test/tests/httptests.ts) tests for detailed examples.
The HTTP client does not throw unless truly exceptional. ## Errors
* A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body. ### HTTP
* Redirects (3xx) will be followed by default.
The HTTP client does not throw unless truly exceptional.
See [HTTP tests](./test/tests/httptests.ts) for detailed examples. * A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body.
* Redirects (3xx) will be followed by default.
### REST
The REST client is a high-level client which uses the HTTP client. Its responsibility is to turn a body into a typed resource object. See [HTTP tests](./test/tests/httptests.ts) for detailed examples.
* A 200 will be success. ### REST
* Redirects (3xx) will be followed.
* A 404 will not throw but the result object will be null and the result statusCode will be set. The REST client is a high-level client which uses the HTTP client. Its responsibility is to turn a body into a typed resource object.
* Other 4xx and 5xx errors will throw. The status code will be attached to the error object. If a RESTful error object is returned (`{ message: xxx}`), then the error message will be that. Otherwise, it will be a generic, `Failed Request: (xxx)`.
* A 200 will be success.
See [REST tests](./test/tests/resttests.ts) for detailed examples. * Redirects (3xx) will be followed.
* A 404 will not throw but the result object will be null and the result statusCode will be set.
## Debugging * Other 4xx and 5xx errors will throw. The status code will be attached to the error object. If a RESTful error object is returned (`{ message: xxx}`), then the error message will be that. Otherwise, it will be a generic, `Failed Request: (xxx)`.
To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible: See [REST tests](./test/tests/resttests.ts) for detailed examples.
``` ## Debugging
export NODE_DEBUG=http
``` To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible:
or ```
export NODE_DEBUG=http
``` ```
set NODE_DEBUG=http
``` or
```
set NODE_DEBUG=http
## Node support ```
The typed-rest-client is built using the latest LTS version of Node 8. We also support the latest LTS for Node 4 and Node 6.
## Contributing ## Node support
To contribute to this repository, see the [contribution guide](./CONTRIBUTING.md) The typed-rest-client is built using the latest LTS version of Node 8. We also support the latest LTS for Node 4 and Node 6.
To build: ## Contributing
```bash To contribute to this repository, see the [contribution guide](./CONTRIBUTING.md)
$ npm run build
``` To build:
To run all tests: ```bash
```bash $ npm run build
$ npm test ```
```
To run all tests:
To just run unit tests: ```bash
```bash $ npm test
$ npm run units ```
```
To just run unit tests:
## Code of Conduct ```bash
$ npm run units
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ```
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Security Issues
Do you think there might be a security issue?
Have you been phished or identified a security vulnerability?
Please don't report it here - let us know by sending an email to secure@microsoft.com.

View File

@ -1,77 +1,78 @@
/// <reference types="node" /> /// <reference types="node" />
import httpm = require('./HttpClient'); import httpm = require('./HttpClient');
import ifm = require("./Interfaces"); import ifm = require("./Interfaces");
export interface IRestResponse<T> { export interface IRestResponse<T> {
statusCode: number; statusCode: number;
result: T | null; result: T | null;
headers: Object; headers: Object;
} }
export interface IRequestOptions { export interface IRequestOptions {
acceptHeader?: string; acceptHeader?: string;
additionalHeaders?: ifm.IHeaders; additionalHeaders?: ifm.IHeaders;
responseProcessor?: Function; responseProcessor?: Function;
deserializeDates?: boolean; deserializeDates?: boolean;
} queryParameters?: ifm.IRequestQueryParams;
export declare class RestClient { }
client: httpm.HttpClient; export declare class RestClient {
versionParam: string; client: httpm.HttpClient;
/** versionParam: string;
* Creates an instance of the RestClient /**
* @constructor * Creates an instance of the RestClient
* @param {string} userAgent - userAgent for requests * @constructor
* @param {string} baseUrl - (Optional) If not specified, use full urls per request. If supplied and a function passes a relative url, it will be appended to this * @param {string} userAgent - userAgent for requests
* @param {ifm.IRequestHandler[]} handlers - handlers are typically auth handlers (basic, bearer, ntlm supplied) * @param {string} baseUrl - (Optional) If not specified, use full urls per request. If supplied and a function passes a relative url, it will be appended to this
* @param {ifm.IRequestOptions} requestOptions - options for each http requests (http proxy setting, socket timeout) * @param {ifm.IRequestHandler[]} handlers - handlers are typically auth handlers (basic, bearer, ntlm supplied)
*/ * @param {ifm.IRequestOptions} requestOptions - options for each http requests (http proxy setting, socket timeout)
constructor(userAgent: string, baseUrl?: string, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions); */
private _baseUrl; constructor(userAgent: string, baseUrl?: string, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions);
/** private _baseUrl;
* Gets a resource from an endpoint /**
* Be aware that not found returns a null. Other error conditions reject the promise * Gets a resource from an endpoint
* @param {string} requestUrl - fully qualified or relative url * Be aware that not found returns a null. Other error conditions reject the promise
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {string} requestUrl - fully qualified or relative url
*/ * @param {IRequestOptions} requestOptions - (optional) requestOptions object
options<T>(requestUrl: string, options?: IRequestOptions): Promise<IRestResponse<T>>; */
/** options<T>(requestUrl: string, options?: IRequestOptions): Promise<IRestResponse<T>>;
* Gets a resource from an endpoint /**
* Be aware that not found returns a null. Other error conditions reject the promise * Gets a resource from an endpoint
* @param {string} resource - fully qualified url or relative path * Be aware that not found returns a null. Other error conditions reject the promise
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {string} resource - fully qualified url or relative path
*/ * @param {IRequestOptions} requestOptions - (optional) requestOptions object
get<T>(resource: string, options?: IRequestOptions): Promise<IRestResponse<T>>; */
/** get<T>(resource: string, options?: IRequestOptions): Promise<IRestResponse<T>>;
* Deletes a resource from an endpoint /**
* Be aware that not found returns a null. Other error conditions reject the promise * Deletes a resource from an endpoint
* @param {string} resource - fully qualified or relative url * Be aware that not found returns a null. Other error conditions reject the promise
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {string} resource - fully qualified or relative url
*/ * @param {IRequestOptions} requestOptions - (optional) requestOptions object
del<T>(resource: string, options?: IRequestOptions): Promise<IRestResponse<T>>; */
/** del<T>(resource: string, options?: IRequestOptions): Promise<IRestResponse<T>>;
* Creates resource(s) from an endpoint /**
* T type of object returned. * Creates resource(s) from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise * T type of object returned.
* @param {string} resource - fully qualified or relative url * Be aware that not found returns a null. Other error conditions reject the promise
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {string} resource - fully qualified or relative url
*/ * @param {IRequestOptions} requestOptions - (optional) requestOptions object
create<T>(resource: string, resources: any, options?: IRequestOptions): Promise<IRestResponse<T>>; */
/** create<T>(resource: string, resources: any, options?: IRequestOptions): Promise<IRestResponse<T>>;
* Updates resource(s) from an endpoint /**
* T type of object returned. * Updates resource(s) from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise * T type of object returned.
* @param {string} resource - fully qualified or relative url * Be aware that not found returns a null. Other error conditions reject the promise
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {string} resource - fully qualified or relative url
*/ * @param {IRequestOptions} requestOptions - (optional) requestOptions object
update<T>(resource: string, resources: any, options?: IRequestOptions): Promise<IRestResponse<T>>; */
/** update<T>(resource: string, resources: any, options?: IRequestOptions): Promise<IRestResponse<T>>;
* Replaces resource(s) from an endpoint /**
* T type of object returned. * Replaces resource(s) from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise * T type of object returned.
* @param {string} resource - fully qualified or relative url * Be aware that not found returns a null. Other error conditions reject the promise
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {string} resource - fully qualified or relative url
*/ * @param {IRequestOptions} requestOptions - (optional) requestOptions object
replace<T>(resource: string, resources: any, options?: IRequestOptions): Promise<IRestResponse<T>>; */
uploadStream<T>(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, options?: IRequestOptions): Promise<IRestResponse<T>>; replace<T>(resource: string, resources: any, options?: IRequestOptions): Promise<IRestResponse<T>>;
private _headersFromOptions(options, contentType?); uploadStream<T>(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, options?: IRequestOptions): Promise<IRestResponse<T>>;
private static dateTimeDeserializer(key, value); private _headersFromOptions;
private _processResponse<T>(res, options); private static dateTimeDeserializer;
} private _processResponse;
}

View File

@ -1,217 +1,217 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) { return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const httpm = require("./HttpClient"); const httpm = require("./HttpClient");
const util = require("./Util"); const util = require("./Util");
class RestClient { class RestClient {
/** /**
* Creates an instance of the RestClient * Creates an instance of the RestClient
* @constructor * @constructor
* @param {string} userAgent - userAgent for requests * @param {string} userAgent - userAgent for requests
* @param {string} baseUrl - (Optional) If not specified, use full urls per request. If supplied and a function passes a relative url, it will be appended to this * @param {string} baseUrl - (Optional) If not specified, use full urls per request. If supplied and a function passes a relative url, it will be appended to this
* @param {ifm.IRequestHandler[]} handlers - handlers are typically auth handlers (basic, bearer, ntlm supplied) * @param {ifm.IRequestHandler[]} handlers - handlers are typically auth handlers (basic, bearer, ntlm supplied)
* @param {ifm.IRequestOptions} requestOptions - options for each http requests (http proxy setting, socket timeout) * @param {ifm.IRequestOptions} requestOptions - options for each http requests (http proxy setting, socket timeout)
*/ */
constructor(userAgent, baseUrl, handlers, requestOptions) { constructor(userAgent, baseUrl, handlers, requestOptions) {
this.client = new httpm.HttpClient(userAgent, handlers, requestOptions); this.client = new httpm.HttpClient(userAgent, handlers, requestOptions);
if (baseUrl) { if (baseUrl) {
this._baseUrl = baseUrl; this._baseUrl = baseUrl;
} }
} }
/** /**
* Gets a resource from an endpoint * Gets a resource from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise * Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} requestUrl - fully qualified or relative url * @param {string} requestUrl - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/ */
options(requestUrl, options) { options(requestUrl, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let url = util.getUrl(requestUrl, this._baseUrl); let url = util.getUrl(requestUrl, this._baseUrl);
let res = yield this.client.options(url, this._headersFromOptions(options)); let res = yield this.client.options(url, this._headersFromOptions(options));
return this._processResponse(res, options); return this._processResponse(res, options);
}); });
} }
/** /**
* Gets a resource from an endpoint * Gets a resource from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise * Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} resource - fully qualified url or relative path * @param {string} resource - fully qualified url or relative path
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/ */
get(resource, options) { get(resource, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let url = util.getUrl(resource, this._baseUrl); let url = util.getUrl(resource, this._baseUrl, (options || {}).queryParameters);
let res = yield this.client.get(url, this._headersFromOptions(options)); let res = yield this.client.get(url, this._headersFromOptions(options));
return this._processResponse(res, options); return this._processResponse(res, options);
}); });
} }
/** /**
* Deletes a resource from an endpoint * Deletes a resource from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise * Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} resource - fully qualified or relative url * @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/ */
del(resource, options) { del(resource, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let url = util.getUrl(resource, this._baseUrl); let url = util.getUrl(resource, this._baseUrl);
let res = yield this.client.del(url, this._headersFromOptions(options)); let res = yield this.client.del(url, this._headersFromOptions(options));
return this._processResponse(res, options); return this._processResponse(res, options);
}); });
} }
/** /**
* Creates resource(s) from an endpoint * Creates resource(s) from an endpoint
* T type of object returned. * T type of object returned.
* Be aware that not found returns a null. Other error conditions reject the promise * Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} resource - fully qualified or relative url * @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/ */
create(resource, resources, options) { create(resource, resources, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let url = util.getUrl(resource, this._baseUrl); let url = util.getUrl(resource, this._baseUrl);
let headers = this._headersFromOptions(options, true); let headers = this._headersFromOptions(options, true);
let data = JSON.stringify(resources, null, 2); let data = JSON.stringify(resources, null, 2);
let res = yield this.client.post(url, data, headers); let res = yield this.client.post(url, data, headers);
return this._processResponse(res, options); return this._processResponse(res, options);
}); });
} }
/** /**
* Updates resource(s) from an endpoint * Updates resource(s) from an endpoint
* T type of object returned. * T type of object returned.
* Be aware that not found returns a null. Other error conditions reject the promise * Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} resource - fully qualified or relative url * @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/ */
update(resource, resources, options) { update(resource, resources, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let url = util.getUrl(resource, this._baseUrl); let url = util.getUrl(resource, this._baseUrl);
let headers = this._headersFromOptions(options, true); let headers = this._headersFromOptions(options, true);
let data = JSON.stringify(resources, null, 2); let data = JSON.stringify(resources, null, 2);
let res = yield this.client.patch(url, data, headers); let res = yield this.client.patch(url, data, headers);
return this._processResponse(res, options); return this._processResponse(res, options);
}); });
} }
/** /**
* Replaces resource(s) from an endpoint * Replaces resource(s) from an endpoint
* T type of object returned. * T type of object returned.
* Be aware that not found returns a null. Other error conditions reject the promise * Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} resource - fully qualified or relative url * @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object * @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/ */
replace(resource, resources, options) { replace(resource, resources, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let url = util.getUrl(resource, this._baseUrl); let url = util.getUrl(resource, this._baseUrl);
let headers = this._headersFromOptions(options, true); let headers = this._headersFromOptions(options, true);
let data = JSON.stringify(resources, null, 2); let data = JSON.stringify(resources, null, 2);
let res = yield this.client.put(url, data, headers); let res = yield this.client.put(url, data, headers);
return this._processResponse(res, options); return this._processResponse(res, options);
}); });
} }
uploadStream(verb, requestUrl, stream, options) { uploadStream(verb, requestUrl, stream, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let url = util.getUrl(requestUrl, this._baseUrl); let url = util.getUrl(requestUrl, this._baseUrl);
let headers = this._headersFromOptions(options, true); let headers = this._headersFromOptions(options, true);
let res = yield this.client.sendStream(verb, url, stream, headers); let res = yield this.client.sendStream(verb, url, stream, headers);
return this._processResponse(res, options); return this._processResponse(res, options);
}); });
} }
_headersFromOptions(options, contentType) { _headersFromOptions(options, contentType) {
options = options || {}; options = options || {};
let headers = options.additionalHeaders || {}; let headers = options.additionalHeaders || {};
headers["Accept"] = options.acceptHeader || "application/json"; headers["Accept"] = options.acceptHeader || "application/json";
if (contentType) { if (contentType) {
let found = false; let found = false;
for (let header in headers) { for (let header in headers) {
if (header.toLowerCase() == "content-type") { if (header.toLowerCase() == "content-type") {
found = true; found = true;
} }
} }
if (!found) { if (!found) {
headers["Content-Type"] = 'application/json; charset=utf-8'; headers["Content-Type"] = 'application/json; charset=utf-8';
} }
} }
return headers; return headers;
} }
static dateTimeDeserializer(key, value) { static dateTimeDeserializer(key, value) {
if (typeof value === 'string') { if (typeof value === 'string') {
let a = new Date(value); let a = new Date(value);
if (!isNaN(a.valueOf())) { if (!isNaN(a.valueOf())) {
return a; return a;
} }
} }
return value; return value;
} }
_processResponse(res, options) { _processResponse(res, options) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
const statusCode = res.message.statusCode; const statusCode = res.message.statusCode;
const response = { const response = {
statusCode: statusCode, statusCode: statusCode,
result: null, result: null,
headers: {} headers: {}
}; };
// not found leads to null obj returned // not found leads to null obj returned
if (statusCode == httpm.HttpCodes.NotFound) { if (statusCode == httpm.HttpCodes.NotFound) {
resolve(response); resolve(response);
} }
let obj; let obj;
let contents; let contents;
// get the result from the body // get the result from the body
try { try {
contents = yield res.readBody(); contents = yield res.readBody();
if (contents && contents.length > 0) { if (contents && contents.length > 0) {
if (options && options.deserializeDates) { if (options && options.deserializeDates) {
obj = JSON.parse(contents, RestClient.dateTimeDeserializer); obj = JSON.parse(contents, RestClient.dateTimeDeserializer);
} }
else { else {
obj = JSON.parse(contents); obj = JSON.parse(contents);
} }
if (options && options.responseProcessor) { if (options && options.responseProcessor) {
response.result = options.responseProcessor(obj); response.result = options.responseProcessor(obj);
} }
else { else {
response.result = obj; response.result = obj;
} }
} }
response.headers = res.message.headers; response.headers = res.message.headers;
} }
catch (err) { catch (err) {
// Invalid resource (contents not json); leaving result obj null // Invalid resource (contents not json); leaving result obj null
} }
// note that 3xx redirects are handled by the http layer. // note that 3xx redirects are handled by the http layer.
if (statusCode > 299) { if (statusCode > 299) {
let msg; let msg;
// if exception/error in body, attempt to get better error // if exception/error in body, attempt to get better error
if (obj && obj.message) { if (obj && obj.message) {
msg = obj.message; msg = obj.message;
} }
else if (contents && contents.length > 0) { else if (contents && contents.length > 0) {
// it may be the case that the exception is in the body message as string // it may be the case that the exception is in the body message as string
msg = contents; msg = contents;
} }
else { else {
msg = "Failed request: (" + statusCode + ")"; msg = "Failed request: (" + statusCode + ")";
} }
let err = new Error(msg); let err = new Error(msg);
// attach statusCode and body obj (if available) to the error object // attach statusCode and body obj (if available) to the error object
err['statusCode'] = statusCode; err['statusCode'] = statusCode;
if (response.result) { if (response.result) {
err['result'] = response.result; err['result'] = response.result;
} }
reject(err); reject(err);
} }
else { else {
resolve(response); resolve(response);
} }
})); }));
}); });
} }
} }
exports.RestClient = RestClient; exports.RestClient = RestClient;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,28 @@
/** /// <reference types="node" />
* creates an url from a request url and optional base url (http://server:8080) import { IRequestQueryParams, IHttpClientResponse } from './Interfaces';
* @param {string} resource - a fully qualified url or relative path /**
* @param {string} baseUrl - an optional baseUrl (http://server:8080) * creates an url from a request url and optional base url (http://server:8080)
* @return {string} - resultant url * @param {string} resource - a fully qualified url or relative path
*/ * @param {string} baseUrl - an optional baseUrl (http://server:8080)
export declare function getUrl(resource: string, baseUrl?: string): string; * @param {IRequestOptions} options - an optional options object, could include QueryParameters e.g.
* @return {string} - resultant url
*/
export declare function getUrl(resource: string, baseUrl?: string, queryParams?: IRequestQueryParams): string;
/**
* Decompress/Decode gzip encoded JSON
* Using Node.js built-in zlib module
*
* @param {Buffer} buffer
* @param {string} charset? - optional; defaults to 'utf-8'
* @return {Promise<string>}
*/
export declare function decompressGzippedContent(buffer: Buffer, charset?: string): Promise<string>;
/**
* Obtain Response's Content Charset.
* Through inspecting `content-type` response header.
* It Returns 'utf-8' if NO charset specified/matched.
*
* @param {IHttpClientResponse} response
* @return {string} - Content Encoding Charset; Default=utf-8
*/
export declare function obtainContentCharset(response: IHttpClientResponse): string;

View File

@ -1,35 +1,118 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
Object.defineProperty(exports, "__esModule", { value: true }); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
const url = require("url"); return new (P || (P = Promise))(function (resolve, reject) {
const path = require("path"); function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
/** function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
* creates an url from a request url and optional base url (http://server:8080) function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
* @param {string} resource - a fully qualified url or relative path step((generator = generator.apply(thisArg, _arguments || [])).next());
* @param {string} baseUrl - an optional baseUrl (http://server:8080) });
* @return {string} - resultant url };
*/ Object.defineProperty(exports, "__esModule", { value: true });
function getUrl(resource, baseUrl) { const qs = require("qs");
const pathApi = path.posix || path; const url = require("url");
if (!baseUrl) { const path = require("path");
return resource; const zlib = require("zlib");
} /**
else if (!resource) { * creates an url from a request url and optional base url (http://server:8080)
return baseUrl; * @param {string} resource - a fully qualified url or relative path
} * @param {string} baseUrl - an optional baseUrl (http://server:8080)
else { * @param {IRequestOptions} options - an optional options object, could include QueryParameters e.g.
const base = url.parse(baseUrl); * @return {string} - resultant url
const resultantUrl = url.parse(resource); */
// resource (specific per request) elements take priority function getUrl(resource, baseUrl, queryParams) {
resultantUrl.protocol = resultantUrl.protocol || base.protocol; const pathApi = path.posix || path;
resultantUrl.auth = resultantUrl.auth || base.auth; let requestUrl = '';
resultantUrl.host = resultantUrl.host || base.host; if (!baseUrl) {
resultantUrl.pathname = pathApi.resolve(base.pathname, resultantUrl.pathname); requestUrl = resource;
if (!resultantUrl.pathname.endsWith('/') && resource.endsWith('/')) { }
resultantUrl.pathname += '/'; else if (!resource) {
} requestUrl = baseUrl;
return url.format(resultantUrl); }
} else {
} const base = url.parse(baseUrl);
exports.getUrl = getUrl; const resultantUrl = url.parse(resource);
// resource (specific per request) elements take priority
resultantUrl.protocol = resultantUrl.protocol || base.protocol;
resultantUrl.auth = resultantUrl.auth || base.auth;
resultantUrl.host = resultantUrl.host || base.host;
resultantUrl.pathname = pathApi.resolve(base.pathname, resultantUrl.pathname);
if (!resultantUrl.pathname.endsWith('/') && resource.endsWith('/')) {
resultantUrl.pathname += '/';
}
requestUrl = url.format(resultantUrl);
}
return queryParams ?
getUrlWithParsedQueryParams(requestUrl, queryParams) :
requestUrl;
}
exports.getUrl = getUrl;
/**
*
* @param {string} requestUrl
* @param {IRequestQueryParams} queryParams
* @return {string} - Request's URL with Query Parameters appended/parsed.
*/
function getUrlWithParsedQueryParams(requestUrl, queryParams) {
const url = requestUrl.replace(/\?$/g, ''); // Clean any extra end-of-string "?" character
const parsedQueryParams = qs.stringify(queryParams.params, buildParamsStringifyOptions(queryParams));
return `${url}${parsedQueryParams}`;
}
/**
* Build options for QueryParams Stringifying.
*
* @param {IRequestQueryParams} queryParams
* @return {object}
*/
function buildParamsStringifyOptions(queryParams) {
let options = {
addQueryPrefix: true,
delimiter: (queryParams.options || {}).separator || '&',
allowDots: (queryParams.options || {}).shouldAllowDots || false,
arrayFormat: (queryParams.options || {}).arrayFormat || 'repeat',
encodeValuesOnly: (queryParams.options || {}).shouldOnlyEncodeValues || true
};
return options;
}
/**
* Decompress/Decode gzip encoded JSON
* Using Node.js built-in zlib module
*
* @param {Buffer} buffer
* @param {string} charset? - optional; defaults to 'utf-8'
* @return {Promise<string>}
*/
function decompressGzippedContent(buffer, charset) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
zlib.gunzip(buffer, function (error, buffer) {
if (error) {
reject(error);
}
resolve(buffer.toString(charset || 'utf-8'));
});
}));
});
}
exports.decompressGzippedContent = decompressGzippedContent;
/**
* Obtain Response's Content Charset.
* Through inspecting `content-type` response header.
* It Returns 'utf-8' if NO charset specified/matched.
*
* @param {IHttpClientResponse} response
* @return {string} - Content Encoding Charset; Default=utf-8
*/
function obtainContentCharset(response) {
// Find the charset, if specified.
// Search for the `charset=CHARSET` string, not including `;,\r\n`
// Example: content-type: 'application/json;charset=utf-8'
// |__ matches would be ['charset=utf-8', 'utf-8', index: 18, input: 'application/json; charset=utf-8']
// |_____ matches[1] would have the charset :tada: , in our example it's utf-8
// However, if the matches Array was empty or no charset found, 'utf-8' would be returned by default.
const contentType = response.message.headers['content-type'] || '';
const matches = contentType.match(/charset=([^;,\r\n]+)/i);
return (matches && matches[1]) ? matches[1] : 'utf-8';
}
exports.obtainContentCharset = obtainContentCharset;

View File

@ -1,9 +1,9 @@
import ifm = require('../Interfaces'); import ifm = require('../Interfaces');
export declare class BasicCredentialHandler implements ifm.IRequestHandler { export declare class BasicCredentialHandler implements ifm.IRequestHandler {
username: string; username: string;
password: string; password: string;
constructor(username: string, password: string); constructor(username: string, password: string);
prepareRequest(options: any): void; prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean; canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>; handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
} }

View File

@ -1,24 +1,24 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
class BasicCredentialHandler { class BasicCredentialHandler {
constructor(username, password) { constructor(username, password) {
this.username = username; this.username = username;
this.password = password; this.password = password;
} }
// currently implements pre-authorization // currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401 // TODO: support preAuth = false where it hooks on 401
prepareRequest(options) { prepareRequest(options) {
options.headers['Authorization'] = 'Basic ' + new Buffer(this.username + ':' + this.password).toString('base64'); options.headers['Authorization'] = 'Basic ' + new Buffer(this.username + ':' + this.password).toString('base64');
options.headers['X-TFS-FedAuthRedirect'] = 'Suppress'; options.headers['X-TFS-FedAuthRedirect'] = 'Suppress';
} }
// This handler cannot handle 401 // This handler cannot handle 401
canHandleAuthentication(response) { canHandleAuthentication(response) {
return false; return false;
} }
handleAuthentication(httpClient, requestInfo, objs) { handleAuthentication(httpClient, requestInfo, objs) {
return null; return null;
} }
} }
exports.BasicCredentialHandler = BasicCredentialHandler; exports.BasicCredentialHandler = BasicCredentialHandler;

View File

@ -1,8 +1,8 @@
import ifm = require('../Interfaces'); import ifm = require('../Interfaces');
export declare class BearerCredentialHandler implements ifm.IRequestHandler { export declare class BearerCredentialHandler implements ifm.IRequestHandler {
token: string; token: string;
constructor(token: string); constructor(token: string);
prepareRequest(options: any): void; prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean; canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>; handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
} }

View File

@ -1,23 +1,23 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
class BearerCredentialHandler { class BearerCredentialHandler {
constructor(token) { constructor(token) {
this.token = token; this.token = token;
} }
// currently implements pre-authorization // currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401 // TODO: support preAuth = false where it hooks on 401
prepareRequest(options) { prepareRequest(options) {
options.headers['Authorization'] = 'Bearer ' + this.token; options.headers['Authorization'] = 'Bearer ' + this.token;
options.headers['X-TFS-FedAuthRedirect'] = 'Suppress'; options.headers['X-TFS-FedAuthRedirect'] = 'Suppress';
} }
// This handler cannot handle 401 // This handler cannot handle 401
canHandleAuthentication(response) { canHandleAuthentication(response) {
return false; return false;
} }
handleAuthentication(httpClient, requestInfo, objs) { handleAuthentication(httpClient, requestInfo, objs) {
return null; return null;
} }
} }
exports.BearerCredentialHandler = BearerCredentialHandler; exports.BearerCredentialHandler = BearerCredentialHandler;

View File

@ -1,13 +1,13 @@
/// <reference types="node" /> /// <reference types="node" />
import ifm = require('../Interfaces'); import ifm = require('../Interfaces');
import http = require("http"); import http = require("http");
export declare class NtlmCredentialHandler implements ifm.IRequestHandler { export declare class NtlmCredentialHandler implements ifm.IRequestHandler {
private _ntlmOptions; private _ntlmOptions;
constructor(username: string, password: string, workstation?: string, domain?: string); constructor(username: string, password: string, workstation?: string, domain?: string);
prepareRequest(options: http.RequestOptions): void; prepareRequest(options: http.RequestOptions): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean; canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>; handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
private handleAuthenticationPrivate(httpClient, requestInfo, objs, finalCallback); private handleAuthenticationPrivate;
private sendType1Message(httpClient, requestInfo, objs, finalCallback); private sendType1Message;
private sendType3Message(httpClient, requestInfo, objs, res, callback); private sendType3Message;
} }

View File

@ -1,137 +1,137 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const http = require("http"); const http = require("http");
const https = require("https"); const https = require("https");
const _ = require("underscore"); const _ = require("underscore");
const ntlm = require("../opensource/node-http-ntlm/ntlm"); const ntlm = require("../opensource/node-http-ntlm/ntlm");
class NtlmCredentialHandler { class NtlmCredentialHandler {
constructor(username, password, workstation, domain) { constructor(username, password, workstation, domain) {
this._ntlmOptions = {}; this._ntlmOptions = {};
this._ntlmOptions.username = username; this._ntlmOptions.username = username;
this._ntlmOptions.password = password; this._ntlmOptions.password = password;
if (domain !== undefined) { if (domain !== undefined) {
this._ntlmOptions.domain = domain; this._ntlmOptions.domain = domain;
} }
else { else {
this._ntlmOptions.domain = ''; this._ntlmOptions.domain = '';
} }
if (workstation !== undefined) { if (workstation !== undefined) {
this._ntlmOptions.workstation = workstation; this._ntlmOptions.workstation = workstation;
} }
else { else {
this._ntlmOptions.workstation = ''; this._ntlmOptions.workstation = '';
} }
} }
prepareRequest(options) { prepareRequest(options) {
// No headers or options need to be set. We keep the credentials on the handler itself. // No headers or options need to be set. We keep the credentials on the handler itself.
// If a (proxy) agent is set, remove it as we don't support proxy for NTLM at this time // If a (proxy) agent is set, remove it as we don't support proxy for NTLM at this time
if (options.agent) { if (options.agent) {
delete options.agent; delete options.agent;
} }
} }
canHandleAuthentication(response) { canHandleAuthentication(response) {
if (response && response.message && response.message.statusCode === 401) { if (response && response.message && response.message.statusCode === 401) {
// Ensure that we're talking NTLM here // Ensure that we're talking NTLM here
// Once we have the www-authenticate header, split it so we can ensure we can talk NTLM // Once we have the www-authenticate header, split it so we can ensure we can talk NTLM
const wwwAuthenticate = response.message.headers['www-authenticate']; const wwwAuthenticate = response.message.headers['www-authenticate'];
if (wwwAuthenticate) { if (wwwAuthenticate) {
const mechanisms = wwwAuthenticate.split(', '); const mechanisms = wwwAuthenticate.split(', ');
const index = mechanisms.indexOf("NTLM"); const index = mechanisms.indexOf("NTLM");
if (index >= 0) { if (index >= 0) {
return true; return true;
} }
} }
} }
return false; return false;
} }
handleAuthentication(httpClient, requestInfo, objs) { handleAuthentication(httpClient, requestInfo, objs) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const callbackForResult = function (err, res) { const callbackForResult = function (err, res) {
if (err) { if (err) {
reject(err); reject(err);
} }
// We have to readbody on the response before continuing otherwise there is a hang. // We have to readbody on the response before continuing otherwise there is a hang.
res.readBody().then(() => { res.readBody().then(() => {
resolve(res); resolve(res);
}); });
}; };
this.handleAuthenticationPrivate(httpClient, requestInfo, objs, callbackForResult); this.handleAuthenticationPrivate(httpClient, requestInfo, objs, callbackForResult);
}); });
} }
handleAuthenticationPrivate(httpClient, requestInfo, objs, finalCallback) { handleAuthenticationPrivate(httpClient, requestInfo, objs, finalCallback) {
// Set up the headers for NTLM authentication // Set up the headers for NTLM authentication
requestInfo.options = _.extend(requestInfo.options, { requestInfo.options = _.extend(requestInfo.options, {
username: this._ntlmOptions.username, username: this._ntlmOptions.username,
password: this._ntlmOptions.password, password: this._ntlmOptions.password,
domain: this._ntlmOptions.domain, domain: this._ntlmOptions.domain,
workstation: this._ntlmOptions.workstation workstation: this._ntlmOptions.workstation
}); });
if (httpClient.isSsl === true) { if (httpClient.isSsl === true) {
requestInfo.options.agent = new https.Agent({ keepAlive: true }); requestInfo.options.agent = new https.Agent({ keepAlive: true });
} }
else { else {
requestInfo.options.agent = new http.Agent({ keepAlive: true }); requestInfo.options.agent = new http.Agent({ keepAlive: true });
} }
let self = this; let self = this;
// The following pattern of sending the type1 message following immediately (in a setImmediate) is // The following pattern of sending the type1 message following immediately (in a setImmediate) is
// critical for the NTLM exchange to happen. If we removed setImmediate (or call in a different manner) // critical for the NTLM exchange to happen. If we removed setImmediate (or call in a different manner)
// the NTLM exchange will always fail with a 401. // the NTLM exchange will always fail with a 401.
this.sendType1Message(httpClient, requestInfo, objs, function (err, res) { this.sendType1Message(httpClient, requestInfo, objs, function (err, res) {
if (err) { if (err) {
return finalCallback(err, null, null); return finalCallback(err, null, null);
} }
/// We have to readbody on the response before continuing otherwise there is a hang. /// We have to readbody on the response before continuing otherwise there is a hang.
res.readBody().then(() => { res.readBody().then(() => {
// It is critical that we have setImmediate here due to how connection requests are queued. // It is critical that we have setImmediate here due to how connection requests are queued.
// If setImmediate is removed then the NTLM handshake will not work. // If setImmediate is removed then the NTLM handshake will not work.
// setImmediate allows us to queue a second request on the same connection. If this second // setImmediate allows us to queue a second request on the same connection. If this second
// request is not queued on the connection when the first request finishes then node closes // request is not queued on the connection when the first request finishes then node closes
// the connection. NTLM requires both requests to be on the same connection so we need this. // the connection. NTLM requires both requests to be on the same connection so we need this.
setImmediate(function () { setImmediate(function () {
self.sendType3Message(httpClient, requestInfo, objs, res, finalCallback); self.sendType3Message(httpClient, requestInfo, objs, res, finalCallback);
}); });
}); });
}); });
} }
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js // The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
sendType1Message(httpClient, requestInfo, objs, finalCallback) { sendType1Message(httpClient, requestInfo, objs, finalCallback) {
const type1msg = ntlm.createType1Message(this._ntlmOptions); const type1msg = ntlm.createType1Message(this._ntlmOptions);
const type1options = { const type1options = {
headers: { headers: {
'Connection': 'keep-alive', 'Connection': 'keep-alive',
'Authorization': type1msg 'Authorization': type1msg
}, },
timeout: requestInfo.options.timeout || 0, timeout: requestInfo.options.timeout || 0,
agent: requestInfo.httpModule, agent: requestInfo.httpModule,
}; };
const type1info = {}; const type1info = {};
type1info.httpModule = requestInfo.httpModule; type1info.httpModule = requestInfo.httpModule;
type1info.parsedUrl = requestInfo.parsedUrl; type1info.parsedUrl = requestInfo.parsedUrl;
type1info.options = _.extend(type1options, _.omit(requestInfo.options, 'headers')); type1info.options = _.extend(type1options, _.omit(requestInfo.options, 'headers'));
return httpClient.requestRawWithCallback(type1info, objs, finalCallback); return httpClient.requestRawWithCallback(type1info, objs, finalCallback);
} }
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js // The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
sendType3Message(httpClient, requestInfo, objs, res, callback) { sendType3Message(httpClient, requestInfo, objs, res, callback) {
if (!res.message.headers && !res.message.headers['www-authenticate']) { if (!res.message.headers && !res.message.headers['www-authenticate']) {
throw new Error('www-authenticate not found on response of second request'); throw new Error('www-authenticate not found on response of second request');
} }
const type2msg = ntlm.parseType2Message(res.message.headers['www-authenticate']); const type2msg = ntlm.parseType2Message(res.message.headers['www-authenticate']);
const type3msg = ntlm.createType3Message(type2msg, this._ntlmOptions); const type3msg = ntlm.createType3Message(type2msg, this._ntlmOptions);
const type3options = { const type3options = {
headers: { headers: {
'Authorization': type3msg, 'Authorization': type3msg,
'Connection': 'Close' 'Connection': 'Close'
}, },
agent: requestInfo.httpModule, agent: requestInfo.httpModule,
}; };
const type3info = {}; const type3info = {};
type3info.httpModule = requestInfo.httpModule; type3info.httpModule = requestInfo.httpModule;
type3info.parsedUrl = requestInfo.parsedUrl; type3info.parsedUrl = requestInfo.parsedUrl;
type3options.headers = _.extend(type3options.headers, requestInfo.options.headers); type3options.headers = _.extend(type3options.headers, requestInfo.options.headers);
type3info.options = _.extend(type3options, _.omit(requestInfo.options, 'headers')); type3info.options = _.extend(type3options, _.omit(requestInfo.options, 'headers'));
return httpClient.requestRawWithCallback(type3info, objs, callback); return httpClient.requestRawWithCallback(type3info, objs, callback);
} }
} }
exports.NtlmCredentialHandler = NtlmCredentialHandler; exports.NtlmCredentialHandler = NtlmCredentialHandler;

View File

@ -1,8 +1,8 @@
import ifm = require('../Interfaces'); import ifm = require('../Interfaces');
export declare class PersonalAccessTokenCredentialHandler implements ifm.IRequestHandler { export declare class PersonalAccessTokenCredentialHandler implements ifm.IRequestHandler {
token: string; token: string;
constructor(token: string); constructor(token: string);
prepareRequest(options: any): void; prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean; canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>; handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
} }

View File

@ -1,23 +1,23 @@
"use strict"; "use strict";
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
class PersonalAccessTokenCredentialHandler { class PersonalAccessTokenCredentialHandler {
constructor(token) { constructor(token) {
this.token = token; this.token = token;
} }
// currently implements pre-authorization // currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401 // TODO: support preAuth = false where it hooks on 401
prepareRequest(options) { prepareRequest(options) {
options.headers['Authorization'] = 'Basic ' + new Buffer('PAT:' + this.token).toString('base64'); options.headers['Authorization'] = 'Basic ' + new Buffer('PAT:' + this.token).toString('base64');
options.headers['X-TFS-FedAuthRedirect'] = 'Suppress'; options.headers['X-TFS-FedAuthRedirect'] = 'Suppress';
} }
// This handler cannot handle 401 // This handler cannot handle 401
canHandleAuthentication(response) { canHandleAuthentication(response) {
return false; return false;
} }
handleAuthentication(httpClient, requestInfo, objs) { handleAuthentication(httpClient, requestInfo, objs) {
return null; return null;
} }
} }
exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler; exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler;

View File

@ -1,389 +1,389 @@
var crypto = require('crypto'); var crypto = require('crypto');
var flags = { var flags = {
NTLM_NegotiateUnicode : 0x00000001, NTLM_NegotiateUnicode : 0x00000001,
NTLM_NegotiateOEM : 0x00000002, NTLM_NegotiateOEM : 0x00000002,
NTLM_RequestTarget : 0x00000004, NTLM_RequestTarget : 0x00000004,
NTLM_Unknown9 : 0x00000008, NTLM_Unknown9 : 0x00000008,
NTLM_NegotiateSign : 0x00000010, NTLM_NegotiateSign : 0x00000010,
NTLM_NegotiateSeal : 0x00000020, NTLM_NegotiateSeal : 0x00000020,
NTLM_NegotiateDatagram : 0x00000040, NTLM_NegotiateDatagram : 0x00000040,
NTLM_NegotiateLanManagerKey : 0x00000080, NTLM_NegotiateLanManagerKey : 0x00000080,
NTLM_Unknown8 : 0x00000100, NTLM_Unknown8 : 0x00000100,
NTLM_NegotiateNTLM : 0x00000200, NTLM_NegotiateNTLM : 0x00000200,
NTLM_NegotiateNTOnly : 0x00000400, NTLM_NegotiateNTOnly : 0x00000400,
NTLM_Anonymous : 0x00000800, NTLM_Anonymous : 0x00000800,
NTLM_NegotiateOemDomainSupplied : 0x00001000, NTLM_NegotiateOemDomainSupplied : 0x00001000,
NTLM_NegotiateOemWorkstationSupplied : 0x00002000, NTLM_NegotiateOemWorkstationSupplied : 0x00002000,
NTLM_Unknown6 : 0x00004000, NTLM_Unknown6 : 0x00004000,
NTLM_NegotiateAlwaysSign : 0x00008000, NTLM_NegotiateAlwaysSign : 0x00008000,
NTLM_TargetTypeDomain : 0x00010000, NTLM_TargetTypeDomain : 0x00010000,
NTLM_TargetTypeServer : 0x00020000, NTLM_TargetTypeServer : 0x00020000,
NTLM_TargetTypeShare : 0x00040000, NTLM_TargetTypeShare : 0x00040000,
NTLM_NegotiateExtendedSecurity : 0x00080000, NTLM_NegotiateExtendedSecurity : 0x00080000,
NTLM_NegotiateIdentify : 0x00100000, NTLM_NegotiateIdentify : 0x00100000,
NTLM_Unknown5 : 0x00200000, NTLM_Unknown5 : 0x00200000,
NTLM_RequestNonNTSessionKey : 0x00400000, NTLM_RequestNonNTSessionKey : 0x00400000,
NTLM_NegotiateTargetInfo : 0x00800000, NTLM_NegotiateTargetInfo : 0x00800000,
NTLM_Unknown4 : 0x01000000, NTLM_Unknown4 : 0x01000000,
NTLM_NegotiateVersion : 0x02000000, NTLM_NegotiateVersion : 0x02000000,
NTLM_Unknown3 : 0x04000000, NTLM_Unknown3 : 0x04000000,
NTLM_Unknown2 : 0x08000000, NTLM_Unknown2 : 0x08000000,
NTLM_Unknown1 : 0x10000000, NTLM_Unknown1 : 0x10000000,
NTLM_Negotiate128 : 0x20000000, NTLM_Negotiate128 : 0x20000000,
NTLM_NegotiateKeyExchange : 0x40000000, NTLM_NegotiateKeyExchange : 0x40000000,
NTLM_Negotiate56 : 0x80000000 NTLM_Negotiate56 : 0x80000000
}; };
var typeflags = { var typeflags = {
NTLM_TYPE1_FLAGS : flags.NTLM_NegotiateUnicode NTLM_TYPE1_FLAGS : flags.NTLM_NegotiateUnicode
+ flags.NTLM_NegotiateOEM + flags.NTLM_NegotiateOEM
+ flags.NTLM_RequestTarget + flags.NTLM_RequestTarget
+ flags.NTLM_NegotiateNTLM + flags.NTLM_NegotiateNTLM
+ flags.NTLM_NegotiateOemDomainSupplied + flags.NTLM_NegotiateOemDomainSupplied
+ flags.NTLM_NegotiateOemWorkstationSupplied + flags.NTLM_NegotiateOemWorkstationSupplied
+ flags.NTLM_NegotiateAlwaysSign + flags.NTLM_NegotiateAlwaysSign
+ flags.NTLM_NegotiateExtendedSecurity + flags.NTLM_NegotiateExtendedSecurity
+ flags.NTLM_NegotiateVersion + flags.NTLM_NegotiateVersion
+ flags.NTLM_Negotiate128 + flags.NTLM_Negotiate128
+ flags.NTLM_Negotiate56, + flags.NTLM_Negotiate56,
NTLM_TYPE2_FLAGS : flags.NTLM_NegotiateUnicode NTLM_TYPE2_FLAGS : flags.NTLM_NegotiateUnicode
+ flags.NTLM_RequestTarget + flags.NTLM_RequestTarget
+ flags.NTLM_NegotiateNTLM + flags.NTLM_NegotiateNTLM
+ flags.NTLM_NegotiateAlwaysSign + flags.NTLM_NegotiateAlwaysSign
+ flags.NTLM_NegotiateExtendedSecurity + flags.NTLM_NegotiateExtendedSecurity
+ flags.NTLM_NegotiateTargetInfo + flags.NTLM_NegotiateTargetInfo
+ flags.NTLM_NegotiateVersion + flags.NTLM_NegotiateVersion
+ flags.NTLM_Negotiate128 + flags.NTLM_Negotiate128
+ flags.NTLM_Negotiate56 + flags.NTLM_Negotiate56
}; };
function createType1Message(options){ function createType1Message(options){
var domain = escape(options.domain.toUpperCase()); var domain = escape(options.domain.toUpperCase());
var workstation = escape(options.workstation.toUpperCase()); var workstation = escape(options.workstation.toUpperCase());
var protocol = 'NTLMSSP\0'; var protocol = 'NTLMSSP\0';
var BODY_LENGTH = 40; var BODY_LENGTH = 40;
var type1flags = typeflags.NTLM_TYPE1_FLAGS; var type1flags = typeflags.NTLM_TYPE1_FLAGS;
if(!domain || domain === '') if(!domain || domain === '')
type1flags = type1flags - flags.NTLM_NegotiateOemDomainSupplied; type1flags = type1flags - flags.NTLM_NegotiateOemDomainSupplied;
var pos = 0; var pos = 0;
var buf = new Buffer(BODY_LENGTH + domain.length + workstation.length); var buf = new Buffer(BODY_LENGTH + domain.length + workstation.length);
buf.write(protocol, pos, protocol.length); pos += protocol.length; // protocol buf.write(protocol, pos, protocol.length); pos += protocol.length; // protocol
buf.writeUInt32LE(1, pos); pos += 4; // type 1 buf.writeUInt32LE(1, pos); pos += 4; // type 1
buf.writeUInt32LE(type1flags, pos); pos += 4; // TYPE1 flag buf.writeUInt32LE(type1flags, pos); pos += 4; // TYPE1 flag
buf.writeUInt16LE(domain.length, pos); pos += 2; // domain length buf.writeUInt16LE(domain.length, pos); pos += 2; // domain length
buf.writeUInt16LE(domain.length, pos); pos += 2; // domain max length buf.writeUInt16LE(domain.length, pos); pos += 2; // domain max length
buf.writeUInt32LE(BODY_LENGTH + workstation.length, pos); pos += 4; // domain buffer offset buf.writeUInt32LE(BODY_LENGTH + workstation.length, pos); pos += 4; // domain buffer offset
buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation length buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation length
buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation max length buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation max length
buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // workstation buffer offset buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // workstation buffer offset
buf.writeUInt8(5, pos); pos += 1; //ProductMajorVersion buf.writeUInt8(5, pos); pos += 1; //ProductMajorVersion
buf.writeUInt8(1, pos); pos += 1; //ProductMinorVersion buf.writeUInt8(1, pos); pos += 1; //ProductMinorVersion
buf.writeUInt16LE(2600, pos); pos += 2; //ProductBuild buf.writeUInt16LE(2600, pos); pos += 2; //ProductBuild
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved1 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved1
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved2 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved2
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved3 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved3
buf.writeUInt8(15, pos); pos += 1; //NTLMRevisionCurrent buf.writeUInt8(15, pos); pos += 1; //NTLMRevisionCurrent
buf.write(workstation, pos, workstation.length, 'ascii'); pos += workstation.length; // workstation string buf.write(workstation, pos, workstation.length, 'ascii'); pos += workstation.length; // workstation string
buf.write(domain , pos, domain.length , 'ascii'); pos += domain.length; buf.write(domain , pos, domain.length , 'ascii'); pos += domain.length;
return 'NTLM ' + buf.toString('base64'); return 'NTLM ' + buf.toString('base64');
} }
function parseType2Message(rawmsg, callback){ function parseType2Message(rawmsg, callback){
var match = rawmsg.match(/NTLM (.+)?/); var match = rawmsg.match(/NTLM (.+)?/);
if(!match || !match[1]) if(!match || !match[1])
return callback(new Error("Couldn't find NTLM in the message type2 comming from the server")); return callback(new Error("Couldn't find NTLM in the message type2 comming from the server"));
var buf = new Buffer(match[1], 'base64'); var buf = new Buffer(match[1], 'base64');
var msg = {}; var msg = {};
msg.signature = buf.slice(0, 8); msg.signature = buf.slice(0, 8);
msg.type = buf.readInt16LE(8); msg.type = buf.readInt16LE(8);
if(msg.type != 2) if(msg.type != 2)
return callback(new Error("Server didn't return a type 2 message")); return callback(new Error("Server didn't return a type 2 message"));
msg.targetNameLen = buf.readInt16LE(12); msg.targetNameLen = buf.readInt16LE(12);
msg.targetNameMaxLen = buf.readInt16LE(14); msg.targetNameMaxLen = buf.readInt16LE(14);
msg.targetNameOffset = buf.readInt32LE(16); msg.targetNameOffset = buf.readInt32LE(16);
msg.targetName = buf.slice(msg.targetNameOffset, msg.targetNameOffset + msg.targetNameMaxLen); msg.targetName = buf.slice(msg.targetNameOffset, msg.targetNameOffset + msg.targetNameMaxLen);
msg.negotiateFlags = buf.readInt32LE(20); msg.negotiateFlags = buf.readInt32LE(20);
msg.serverChallenge = buf.slice(24, 32); msg.serverChallenge = buf.slice(24, 32);
msg.reserved = buf.slice(32, 40); msg.reserved = buf.slice(32, 40);
if(msg.negotiateFlags & flags.NTLM_NegotiateTargetInfo){ if(msg.negotiateFlags & flags.NTLM_NegotiateTargetInfo){
msg.targetInfoLen = buf.readInt16LE(40); msg.targetInfoLen = buf.readInt16LE(40);
msg.targetInfoMaxLen = buf.readInt16LE(42); msg.targetInfoMaxLen = buf.readInt16LE(42);
msg.targetInfoOffset = buf.readInt32LE(44); msg.targetInfoOffset = buf.readInt32LE(44);
msg.targetInfo = buf.slice(msg.targetInfoOffset, msg.targetInfoOffset + msg.targetInfoLen); msg.targetInfo = buf.slice(msg.targetInfoOffset, msg.targetInfoOffset + msg.targetInfoLen);
} }
return msg; return msg;
} }
function createType3Message(msg2, options){ function createType3Message(msg2, options){
var nonce = msg2.serverChallenge; var nonce = msg2.serverChallenge;
var username = options.username; var username = options.username;
var password = options.password; var password = options.password;
var negotiateFlags = msg2.negotiateFlags; var negotiateFlags = msg2.negotiateFlags;
var isUnicode = negotiateFlags & flags.NTLM_NegotiateUnicode; var isUnicode = negotiateFlags & flags.NTLM_NegotiateUnicode;
var isNegotiateExtendedSecurity = negotiateFlags & flags.NTLM_NegotiateExtendedSecurity; var isNegotiateExtendedSecurity = negotiateFlags & flags.NTLM_NegotiateExtendedSecurity;
var BODY_LENGTH = 72; var BODY_LENGTH = 72;
var domainName = escape(options.domain.toUpperCase()); var domainName = escape(options.domain.toUpperCase());
var workstation = escape(options.workstation.toUpperCase()); var workstation = escape(options.workstation.toUpperCase());
var workstationBytes, domainNameBytes, usernameBytes, encryptedRandomSessionKeyBytes; var workstationBytes, domainNameBytes, usernameBytes, encryptedRandomSessionKeyBytes;
var encryptedRandomSessionKey = ""; var encryptedRandomSessionKey = "";
if(isUnicode){ if(isUnicode){
workstationBytes = new Buffer(workstation, 'utf16le'); workstationBytes = new Buffer(workstation, 'utf16le');
domainNameBytes = new Buffer(domainName, 'utf16le'); domainNameBytes = new Buffer(domainName, 'utf16le');
usernameBytes = new Buffer(username, 'utf16le'); usernameBytes = new Buffer(username, 'utf16le');
encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'utf16le'); encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'utf16le');
}else{ }else{
workstationBytes = new Buffer(workstation, 'ascii'); workstationBytes = new Buffer(workstation, 'ascii');
domainNameBytes = new Buffer(domainName, 'ascii'); domainNameBytes = new Buffer(domainName, 'ascii');
usernameBytes = new Buffer(username, 'ascii'); usernameBytes = new Buffer(username, 'ascii');
encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'ascii'); encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'ascii');
} }
var lmChallengeResponse = calc_resp(create_LM_hashed_password_v1(password), nonce); var lmChallengeResponse = calc_resp(create_LM_hashed_password_v1(password), nonce);
var ntChallengeResponse = calc_resp(create_NT_hashed_password_v1(password), nonce); var ntChallengeResponse = calc_resp(create_NT_hashed_password_v1(password), nonce);
if(isNegotiateExtendedSecurity){ if(isNegotiateExtendedSecurity){
var pwhash = create_NT_hashed_password_v1(password); var pwhash = create_NT_hashed_password_v1(password);
var clientChallenge = ""; var clientChallenge = "";
for(var i=0; i < 8; i++){ for(var i=0; i < 8; i++){
clientChallenge += String.fromCharCode( Math.floor(Math.random()*256) ); clientChallenge += String.fromCharCode( Math.floor(Math.random()*256) );
} }
var clientChallengeBytes = new Buffer(clientChallenge, 'ascii'); var clientChallengeBytes = new Buffer(clientChallenge, 'ascii');
var challenges = ntlm2sr_calc_resp(pwhash, nonce, clientChallengeBytes); var challenges = ntlm2sr_calc_resp(pwhash, nonce, clientChallengeBytes);
lmChallengeResponse = challenges.lmChallengeResponse; lmChallengeResponse = challenges.lmChallengeResponse;
ntChallengeResponse = challenges.ntChallengeResponse; ntChallengeResponse = challenges.ntChallengeResponse;
} }
var signature = 'NTLMSSP\0'; var signature = 'NTLMSSP\0';
var pos = 0; var pos = 0;
var buf = new Buffer(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length + encryptedRandomSessionKeyBytes.length); var buf = new Buffer(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length + encryptedRandomSessionKeyBytes.length);
buf.write(signature, pos, signature.length); pos += signature.length; buf.write(signature, pos, signature.length); pos += signature.length;
buf.writeUInt32LE(3, pos); pos += 4; // type 1 buf.writeUInt32LE(3, pos); pos += 4; // type 1
buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseLen buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseLen
buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseMaxLen buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length, pos); pos += 4; // LmChallengeResponseOffset buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length, pos); pos += 4; // LmChallengeResponseOffset
buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseLen buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseLen
buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseMaxLen buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length, pos); pos += 4; // NtChallengeResponseOffset buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length, pos); pos += 4; // NtChallengeResponseOffset
buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameLen buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameLen
buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameMaxLen buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameMaxLen
buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // DomainNameOffset buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // DomainNameOffset
buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameLen buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameLen
buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameMaxLen buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length, pos); pos += 4; // UserNameOffset buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length, pos); pos += 4; // UserNameOffset
buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationLen buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationLen
buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationMaxLen buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length, pos); pos += 4; // WorkstationOffset buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length, pos); pos += 4; // WorkstationOffset
buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyLen buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyLen
buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyMaxLen buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length, pos); pos += 4; // EncryptedRandomSessionKeyOffset buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length, pos); pos += 4; // EncryptedRandomSessionKeyOffset
buf.writeUInt32LE(typeflags.NTLM_TYPE2_FLAGS, pos); pos += 4; // NegotiateFlags buf.writeUInt32LE(typeflags.NTLM_TYPE2_FLAGS, pos); pos += 4; // NegotiateFlags
buf.writeUInt8(5, pos); pos++; // ProductMajorVersion buf.writeUInt8(5, pos); pos++; // ProductMajorVersion
buf.writeUInt8(1, pos); pos++; // ProductMinorVersion buf.writeUInt8(1, pos); pos++; // ProductMinorVersion
buf.writeUInt16LE(2600, pos); pos += 2; // ProductBuild buf.writeUInt16LE(2600, pos); pos += 2; // ProductBuild
buf.writeUInt8(0, pos); pos++; // VersionReserved1 buf.writeUInt8(0, pos); pos++; // VersionReserved1
buf.writeUInt8(0, pos); pos++; // VersionReserved2 buf.writeUInt8(0, pos); pos++; // VersionReserved2
buf.writeUInt8(0, pos); pos++; // VersionReserved3 buf.writeUInt8(0, pos); pos++; // VersionReserved3
buf.writeUInt8(15, pos); pos++; // NTLMRevisionCurrent buf.writeUInt8(15, pos); pos++; // NTLMRevisionCurrent
domainNameBytes.copy(buf, pos); pos += domainNameBytes.length; domainNameBytes.copy(buf, pos); pos += domainNameBytes.length;
usernameBytes.copy(buf, pos); pos += usernameBytes.length; usernameBytes.copy(buf, pos); pos += usernameBytes.length;
workstationBytes.copy(buf, pos); pos += workstationBytes.length; workstationBytes.copy(buf, pos); pos += workstationBytes.length;
lmChallengeResponse.copy(buf, pos); pos += lmChallengeResponse.length; lmChallengeResponse.copy(buf, pos); pos += lmChallengeResponse.length;
ntChallengeResponse.copy(buf, pos); pos += ntChallengeResponse.length; ntChallengeResponse.copy(buf, pos); pos += ntChallengeResponse.length;
encryptedRandomSessionKeyBytes.copy(buf, pos); pos += encryptedRandomSessionKeyBytes.length; encryptedRandomSessionKeyBytes.copy(buf, pos); pos += encryptedRandomSessionKeyBytes.length;
return 'NTLM ' + buf.toString('base64'); return 'NTLM ' + buf.toString('base64');
} }
function create_LM_hashed_password_v1(password){ function create_LM_hashed_password_v1(password){
// fix the password length to 14 bytes // fix the password length to 14 bytes
password = password.toUpperCase(); password = password.toUpperCase();
var passwordBytes = new Buffer(password, 'ascii'); var passwordBytes = new Buffer(password, 'ascii');
var passwordBytesPadded = new Buffer(14); var passwordBytesPadded = new Buffer(14);
passwordBytesPadded.fill("\0"); passwordBytesPadded.fill("\0");
var sourceEnd = 14; var sourceEnd = 14;
if(passwordBytes.length < 14) sourceEnd = passwordBytes.length; if(passwordBytes.length < 14) sourceEnd = passwordBytes.length;
passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd); passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd);
// split into 2 parts of 7 bytes: // split into 2 parts of 7 bytes:
var firstPart = passwordBytesPadded.slice(0,7); var firstPart = passwordBytesPadded.slice(0,7);
var secondPart = passwordBytesPadded.slice(7); var secondPart = passwordBytesPadded.slice(7);
function encrypt(buf){ function encrypt(buf){
var key = insertZerosEvery7Bits(buf); var key = insertZerosEvery7Bits(buf);
var des = crypto.createCipheriv('DES-ECB', key, ''); var des = crypto.createCipheriv('DES-ECB', key, '');
return des.update("KGS!@#$%"); // page 57 in [MS-NLMP]); return des.update("KGS!@#$%"); // page 57 in [MS-NLMP]);
} }
var firstPartEncrypted = encrypt(firstPart); var firstPartEncrypted = encrypt(firstPart);
var secondPartEncrypted = encrypt(secondPart); var secondPartEncrypted = encrypt(secondPart);
return Buffer.concat([firstPartEncrypted, secondPartEncrypted]); return Buffer.concat([firstPartEncrypted, secondPartEncrypted]);
} }
function insertZerosEvery7Bits(buf){ function insertZerosEvery7Bits(buf){
var binaryArray = bytes2binaryArray(buf); var binaryArray = bytes2binaryArray(buf);
var newBinaryArray = []; var newBinaryArray = [];
for(var i=0; i<binaryArray.length; i++){ for(var i=0; i<binaryArray.length; i++){
newBinaryArray.push(binaryArray[i]); newBinaryArray.push(binaryArray[i]);
if((i+1)%7 === 0){ if((i+1)%7 === 0){
newBinaryArray.push(0); newBinaryArray.push(0);
} }
} }
return binaryArray2bytes(newBinaryArray); return binaryArray2bytes(newBinaryArray);
} }
function bytes2binaryArray(buf){ function bytes2binaryArray(buf){
var hex2binary = { var hex2binary = {
0: [0,0,0,0], 0: [0,0,0,0],
1: [0,0,0,1], 1: [0,0,0,1],
2: [0,0,1,0], 2: [0,0,1,0],
3: [0,0,1,1], 3: [0,0,1,1],
4: [0,1,0,0], 4: [0,1,0,0],
5: [0,1,0,1], 5: [0,1,0,1],
6: [0,1,1,0], 6: [0,1,1,0],
7: [0,1,1,1], 7: [0,1,1,1],
8: [1,0,0,0], 8: [1,0,0,0],
9: [1,0,0,1], 9: [1,0,0,1],
A: [1,0,1,0], A: [1,0,1,0],
B: [1,0,1,1], B: [1,0,1,1],
C: [1,1,0,0], C: [1,1,0,0],
D: [1,1,0,1], D: [1,1,0,1],
E: [1,1,1,0], E: [1,1,1,0],
F: [1,1,1,1] F: [1,1,1,1]
}; };
var hexString = buf.toString('hex').toUpperCase(); var hexString = buf.toString('hex').toUpperCase();
var array = []; var array = [];
for(var i=0; i<hexString.length; i++){ for(var i=0; i<hexString.length; i++){
var hexchar = hexString.charAt(i); var hexchar = hexString.charAt(i);
array = array.concat(hex2binary[hexchar]); array = array.concat(hex2binary[hexchar]);
} }
return array; return array;
} }
function binaryArray2bytes(array){ function binaryArray2bytes(array){
var binary2hex = { var binary2hex = {
'0000': 0, '0000': 0,
'0001': 1, '0001': 1,
'0010': 2, '0010': 2,
'0011': 3, '0011': 3,
'0100': 4, '0100': 4,
'0101': 5, '0101': 5,
'0110': 6, '0110': 6,
'0111': 7, '0111': 7,
'1000': 8, '1000': 8,
'1001': 9, '1001': 9,
'1010': 'A', '1010': 'A',
'1011': 'B', '1011': 'B',
'1100': 'C', '1100': 'C',
'1101': 'D', '1101': 'D',
'1110': 'E', '1110': 'E',
'1111': 'F' '1111': 'F'
}; };
var bufArray = []; var bufArray = [];
for(var i=0; i<array.length; i +=8 ){ for(var i=0; i<array.length; i +=8 ){
if((i+7) > array.length) if((i+7) > array.length)
break; break;
var binString1 = '' + array[i] + '' + array[i+1] + '' + array[i+2] + '' + array[i+3]; var binString1 = '' + array[i] + '' + array[i+1] + '' + array[i+2] + '' + array[i+3];
var binString2 = '' + array[i+4] + '' + array[i+5] + '' + array[i+6] + '' + array[i+7]; var binString2 = '' + array[i+4] + '' + array[i+5] + '' + array[i+6] + '' + array[i+7];
var hexchar1 = binary2hex[binString1]; var hexchar1 = binary2hex[binString1];
var hexchar2 = binary2hex[binString2]; var hexchar2 = binary2hex[binString2];
var buf = new Buffer(hexchar1 + '' + hexchar2, 'hex'); var buf = new Buffer(hexchar1 + '' + hexchar2, 'hex');
bufArray.push(buf); bufArray.push(buf);
} }
return Buffer.concat(bufArray); return Buffer.concat(bufArray);
} }
function create_NT_hashed_password_v1(password){ function create_NT_hashed_password_v1(password){
var buf = new Buffer(password, 'utf16le'); var buf = new Buffer(password, 'utf16le');
var md4 = crypto.createHash('md4'); var md4 = crypto.createHash('md4');
md4.update(buf); md4.update(buf);
return new Buffer(md4.digest()); return new Buffer(md4.digest());
} }
function calc_resp(password_hash, server_challenge){ function calc_resp(password_hash, server_challenge){
// padding with zeros to make the hash 21 bytes long // padding with zeros to make the hash 21 bytes long
var passHashPadded = new Buffer(21); var passHashPadded = new Buffer(21);
passHashPadded.fill("\0"); passHashPadded.fill("\0");
password_hash.copy(passHashPadded, 0, 0, password_hash.length); password_hash.copy(passHashPadded, 0, 0, password_hash.length);
var resArray = []; var resArray = [];
var des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(0,7)), ''); var des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(0,7)), '');
resArray.push( des.update(server_challenge.slice(0,8)) ); resArray.push( des.update(server_challenge.slice(0,8)) );
des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(7,14)), ''); des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(7,14)), '');
resArray.push( des.update(server_challenge.slice(0,8)) ); resArray.push( des.update(server_challenge.slice(0,8)) );
des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(14,21)), ''); des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(14,21)), '');
resArray.push( des.update(server_challenge.slice(0,8)) ); resArray.push( des.update(server_challenge.slice(0,8)) );
return Buffer.concat(resArray); return Buffer.concat(resArray);
} }
function ntlm2sr_calc_resp(responseKeyNT, serverChallenge, clientChallenge){ function ntlm2sr_calc_resp(responseKeyNT, serverChallenge, clientChallenge){
// padding with zeros to make the hash 16 bytes longer // padding with zeros to make the hash 16 bytes longer
var lmChallengeResponse = new Buffer(clientChallenge.length + 16); var lmChallengeResponse = new Buffer(clientChallenge.length + 16);
lmChallengeResponse.fill("\0"); lmChallengeResponse.fill("\0");
clientChallenge.copy(lmChallengeResponse, 0, 0, clientChallenge.length); clientChallenge.copy(lmChallengeResponse, 0, 0, clientChallenge.length);
var buf = Buffer.concat([serverChallenge, clientChallenge]); var buf = Buffer.concat([serverChallenge, clientChallenge]);
var md5 = crypto.createHash('md5'); var md5 = crypto.createHash('md5');
md5.update(buf); md5.update(buf);
var sess = md5.digest(); var sess = md5.digest();
var ntChallengeResponse = calc_resp(responseKeyNT, sess.slice(0,8)); var ntChallengeResponse = calc_resp(responseKeyNT, sess.slice(0,8));
return { return {
lmChallengeResponse: lmChallengeResponse, lmChallengeResponse: lmChallengeResponse,
ntChallengeResponse: ntChallengeResponse ntChallengeResponse: ntChallengeResponse
}; };
} }
exports.createType1Message = createType1Message; exports.createType1Message = createType1Message;
exports.parseType2Message = parseType2Message; exports.parseType2Message = parseType2Message;
exports.createType3Message = createType3Message; exports.createType3Message = createType3Message;

View File

@ -1,6 +1,6 @@
// This software (ntlm.js) was copied from a file of the same name at https://github.com/SamDecrock/node-http-ntlm/blob/master/ntlm.js. // This software (ntlm.js) was copied from a file of the same name at https://github.com/SamDecrock/node-http-ntlm/blob/master/ntlm.js.
// //
// As of this writing, it is a part of the node-http-ntlm module produced by SamDecrock. // As of this writing, it is a part of the node-http-ntlm module produced by SamDecrock.
// //
// It is used as a part of the NTLM support provided by the vso-node-api library. // It is used as a part of the NTLM support provided by the vso-node-api library.
// //

View File

@ -1,31 +1,31 @@
{ {
"_args": [ "_args": [
[ [
"typed-rest-client@1.5.0", "typed-rest-client@1.7.1",
"/home/runner/work/ghaction-upx/ghaction-upx" "/home/runner/work/ghaction-upx/ghaction-upx"
] ]
], ],
"_from": "typed-rest-client@1.5.0", "_from": "typed-rest-client@1.7.1",
"_id": "typed-rest-client@1.5.0", "_id": "typed-rest-client@1.7.1",
"_inBundle": false, "_inBundle": false,
"_integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==", "_integrity": "sha512-fZRDWFtUp3J2E0jOiCJYZ9LDrYZHpjY95su//ekqXERS7C1qojP6movh7M4JGURJnBuTVsO0g2N4vEoW5o3Djw==",
"_location": "/typed-rest-client", "_location": "/typed-rest-client",
"_phantomChildren": {}, "_phantomChildren": {},
"_requested": { "_requested": {
"type": "version", "type": "version",
"registry": true, "registry": true,
"raw": "typed-rest-client@1.5.0", "raw": "typed-rest-client@1.7.1",
"name": "typed-rest-client", "name": "typed-rest-client",
"escapedName": "typed-rest-client", "escapedName": "typed-rest-client",
"rawSpec": "1.5.0", "rawSpec": "1.7.1",
"saveSpec": null, "saveSpec": null,
"fetchSpec": "1.5.0" "fetchSpec": "1.7.1"
}, },
"_requiredBy": [ "_requiredBy": [
"/" "/"
], ],
"_resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz", "_resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.7.1.tgz",
"_spec": "1.5.0", "_spec": "1.7.1",
"_where": "/home/runner/work/ghaction-upx/ghaction-upx", "_where": "/home/runner/work/ghaction-upx/ghaction-upx",
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"
@ -34,6 +34,7 @@
"url": "https://github.com/Microsoft/typed-rest-client/issues" "url": "https://github.com/Microsoft/typed-rest-client/issues"
}, },
"dependencies": { "dependencies": {
"qs": "^6.9.1",
"tunnel": "0.0.4", "tunnel": "0.0.4",
"underscore": "1.8.3" "underscore": "1.8.3"
}, },
@ -72,5 +73,5 @@
"units": "node make.js units", "units": "node make.js units",
"validate": "node make.js validate" "validate": "node make.js validate"
}, },
"version": "1.5.0" "version": "1.7.1"
} }