feat: ✨ Created a mini nodeJS server with NewMan for testing without PostMan GUI.
This will mimic a run in a CD/CI environment or docker container.
This commit is contained in:
136
node_modules/postman-url-encoder/CHANGELOG.yaml
generated
vendored
Normal file
136
node_modules/postman-url-encoder/CHANGELOG.yaml
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
3.0.5:
|
||||
date: 2021-08-16
|
||||
fixed bugs:
|
||||
- Fixed a bug where `encoder.encodeHost` throws in the browser
|
||||
chores:
|
||||
- Setup browser tests
|
||||
|
||||
3.0.4:
|
||||
date: 2021-08-09
|
||||
fixed bugs:
|
||||
- Fixed a bug where leading slashes are trimmed incorrectly
|
||||
chores:
|
||||
- Removed punycode fallback for Electron v3
|
||||
- Updated dependencies
|
||||
|
||||
3.0.3:
|
||||
date: 2021-07-25
|
||||
fixed bugs:
|
||||
- >-
|
||||
GH-133 Fixed a bug where slashes in the `file` protocol are handled
|
||||
incorrectly
|
||||
|
||||
3.0.2:
|
||||
date: 2021-07-14
|
||||
fixed bugs:
|
||||
- >-
|
||||
GH-130 Fixed a bug where extra slashes and backslashes in the protocol are
|
||||
not handled correctly
|
||||
chores:
|
||||
- Added secure codecov publish script
|
||||
- Updated dependencies
|
||||
|
||||
3.0.1:
|
||||
date: 2021-01-02
|
||||
chores:
|
||||
- Updated dependencies
|
||||
|
||||
3.0.0:
|
||||
date: 2020-10-04
|
||||
new features:
|
||||
- GH-48 Added URL parser module
|
||||
breaking changes:
|
||||
- GH-49 Dropped support for Node < v10
|
||||
chores:
|
||||
- GH-50 Convert EncodeSet to ES6 class
|
||||
- GH-52 Automated releases and publish process
|
||||
- GH-51 Automated gh-pages docs deployment
|
||||
- >-
|
||||
GH-49 Updated .npmignore to prevent the addition of tests and config files
|
||||
in the published package
|
||||
- GH-49 Added system test
|
||||
- GH-49 Moved postman-collection to dev-dependencies
|
||||
- GH-49 Updated nyc configuration
|
||||
- GH-49 Updated ESLint rules
|
||||
- GH-49 Updated dependencies
|
||||
|
||||
2.1.3:
|
||||
date: 2020-07-29
|
||||
fixed bugs:
|
||||
- >-
|
||||
GH-41 Fixed a bug where square brackets are not removed from IPv6 hostname
|
||||
on `toNodeURL()`
|
||||
chores:
|
||||
- Updated dependencies
|
||||
|
||||
2.1.2:
|
||||
date: 2020-07-13
|
||||
chores:
|
||||
- Updated dependencies
|
||||
|
||||
2.1.1:
|
||||
date: 2020-03-15
|
||||
fixed bugs:
|
||||
- GH-20 Removed `&` and `=` from QUERY_ENCODE_SET
|
||||
|
||||
2.1.0:
|
||||
date: 2020-03-02
|
||||
new features:
|
||||
- GH-11 Added an option to disable encoding on `toNodeUrl()`
|
||||
- >-
|
||||
GH-13 Added `resolveNodeUrl()` method to resolve target URL relative to a
|
||||
base URL
|
||||
fixed bugs:
|
||||
- >-
|
||||
GH-12 Fixed a bug where Node.js native `url.domainToASCII` was not working
|
||||
as expected in Electron v3
|
||||
- GH-10 Handle query parameters with empty key or value
|
||||
- >-
|
||||
GH-9 Fixed a bug where query params without value were changed to params
|
||||
with empty value while encoding single param
|
||||
|
||||
2.0.0:
|
||||
date: 2020-02-06
|
||||
new features:
|
||||
- Implementation of the WHATWG URL Standard
|
||||
- Introduces new `/encoder` module with various encoding helpers
|
||||
- Method `toNodeUrl()` now follows the WHATWG URL standard for encoding
|
||||
breaking changes:
|
||||
- Renamed legacy `toNodeUrl()` method to `toLegacyNodeUrl()`
|
||||
- |
|
||||
Changed `encode()` function to encode SPACE, ("), (#), (&), ('), (<),
|
||||
(=), and (>). In previous versions, it was encoding SPACE, (!), ("), ((),
|
||||
()), (<), (>), (%), ('), and (*).
|
||||
- Deprecated `toLegacyNodeUrl()` in favor of `toNodeUrl()`
|
||||
- Deprecated `encode()` in favor of `encodeQueryParam()`
|
||||
- |
|
||||
Dropped support for following methods:
|
||||
-> percentEncode()
|
||||
-> isPreEncoded()
|
||||
-> isPreEncodedCharacter()
|
||||
-> charactersToPercentEncode()
|
||||
chores:
|
||||
- Updated dependencies
|
||||
- Update license from MIT to Apache-2.0
|
||||
|
||||
1.0.3:
|
||||
date: 2019-10-16
|
||||
chores:
|
||||
- Updated dependencies
|
||||
|
||||
1.0.2:
|
||||
date: 2019-04-08
|
||||
new features:
|
||||
- >-
|
||||
Added `toNodeUrl()` function to convert a string URL into Node's URL type
|
||||
object
|
||||
|
||||
1.0.1:
|
||||
date: 2017-02-01
|
||||
chore:
|
||||
- Updated package.json
|
||||
|
||||
1.0.0:
|
||||
date: 2017-02-01
|
||||
initial release:
|
||||
- Initial Release
|
176
node_modules/postman-url-encoder/LICENSE.md
generated
vendored
Normal file
176
node_modules/postman-url-encoder/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
49
node_modules/postman-url-encoder/README.md
generated
vendored
Normal file
49
node_modules/postman-url-encoder/README.md
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# Postman URL Encoder [](https://travis-ci.com/postmanlabs/postman-url-encoder) [](https://codecov.io/gh/postmanlabs/postman-url-encoder)
|
||||
|
||||
Postman URL Encoder is a NodeJS module that provides various URL encoding related APIs. This module is created to
|
||||
implement the [WHATWG URL specification](https://url.spec.whatwg.org/) to remove dependency on Node's URL APIs across
|
||||
Postman systems. These APIs are useful to encode different parts (like hostname, path, query) of URL and convert
|
||||
[PostmanUrl](http://www.postmanlabs.com/postman-collection/Url.html) object into
|
||||
[Node's Url](https://nodejs.org/dist/latest-v10.x/docs/api/url.html#url_legacy_urlobject) like object.
|
||||
|
||||
## Installing the Postman URL Encoder
|
||||
|
||||
Postman URL Encoder can be installed using NPM or directly from the git repository within your NodeJS projects. If
|
||||
installing from NPM, the following command installs the module and saves in your `package.json`
|
||||
|
||||
```terminal
|
||||
> npm install postman-url-encoder --save
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
Following example snippet shows how to convert [PostmanUrl](http://www.postmanlabs.com/postman-collection/Url.html)
|
||||
object into [Node's Url](https://nodejs.org/dist/latest-v10.x/docs/api/url.html#url_legacy_urlobject) like object.
|
||||
|
||||
```javascript
|
||||
var PostmanUrl = require('postman-collection').Url,
|
||||
pmEncoder = require('postman-url-encoder'),
|
||||
myUrl;
|
||||
|
||||
// Create PostmanUrl object
|
||||
myUrl = new PostmanUrl('http://example.com/p/a/t/h?q1=v1');
|
||||
|
||||
// convert PostmanUrl object to Node's Url like object
|
||||
myUrl = pmEncoder.toNodeUrl(myUrl));
|
||||
// {
|
||||
// protocol: 'http:',
|
||||
// slashes: true,
|
||||
// auth: null,
|
||||
// host: 'example.com',
|
||||
// port: null,
|
||||
// hostname: 'example.com',
|
||||
// hash: null,
|
||||
// search: '?q1=v1',
|
||||
// query: 'q1=v1',
|
||||
// pathname: '/p/a/t/h',
|
||||
// path: '/p/a/t/h?q1=v1',
|
||||
// href: 'http://example.com/p/a/t/h?q1=v1'
|
||||
// }
|
||||
```
|
||||
|
||||
To know more about provided APIs, head over to [Postman URL Encoder Docs](http://www.postmanlabs.com/postman-url-encoder).
|
341
node_modules/postman-url-encoder/encoder/encode-set.js
generated
vendored
Normal file
341
node_modules/postman-url-encoder/encoder/encode-set.js
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* @fileoverview
|
||||
* An EncodeSet represents a set of characters that should be percent-encoded.
|
||||
*
|
||||
* Different characters need to be encoded in different parts of an URL.
|
||||
* For example, a literal ? question mark in an URL’s path would indicate the
|
||||
* start of the query string. A question mark meant to be part of the path
|
||||
* therefore needs to be percent-encoded.
|
||||
* In the query string however, a question mark does not have any special
|
||||
* meaning and does not need to be percent-encoded.
|
||||
*
|
||||
* A few sets are defined in this module.
|
||||
* Use the {@link EncodeSet} class to define different ones.
|
||||
*
|
||||
* @see {@link https://url.spec.whatwg.org/#simple-encode-set}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A character (String), or character code (Number).
|
||||
*
|
||||
* @typedef {String|Number} Char
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Set or Array of {@link Char}(s).
|
||||
*
|
||||
* @typedef {Set.<Char>|Array.<Char>} CharSet
|
||||
*/
|
||||
|
||||
const QUERY_ENCODE_CHARS = [' ', '"', '#', '\'', '<', '>'],
|
||||
FRAGMENT_EXTEND_CHARS = [' ', '"', '<', '>', '`'],
|
||||
PATH_EXTEND_CHARS = ['#', '?', '{', '}'],
|
||||
USERINFO_EXTEND_CHARS = ['/', ':', ';', '=', '@', '[', '\\', ']', '^', '|'];
|
||||
|
||||
/**
|
||||
* Returns a number representing the UTF-16 code unit value of the character.
|
||||
*
|
||||
* @private
|
||||
* @param {Char} char Character or character code
|
||||
* @returns {Number} Character code
|
||||
*/
|
||||
function charCode (char) {
|
||||
const code = (typeof char === 'string') ?
|
||||
// get char code from string
|
||||
char.charCodeAt(0) :
|
||||
// or, normalize char code using double Bitwise NOT
|
||||
// Refer: https://jsperf.com/truncating-decimals
|
||||
~~char;
|
||||
|
||||
// ensure UTF-16 range [0, 0xFFFF]
|
||||
return (code >= 0 && code <= 0xFFFF) ? code : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the EncodeSet with the given characters.
|
||||
*
|
||||
* @note Mutates the input EncodeSet.
|
||||
*
|
||||
* @private
|
||||
* @param {EncodeSet} encodeSet Instance of EncodeSet
|
||||
* @param {CharSet} chars Character set to extend
|
||||
* @returns {EncodeSet} Given EncodeSet
|
||||
*/
|
||||
function extendEncodeSet (encodeSet, chars) {
|
||||
// special handling for Uint8Array chars which signify an existing encode
|
||||
// set used to extend the given encodeSet.
|
||||
if (chars instanceof Uint8Array) {
|
||||
// iterate over fixed / known size set
|
||||
encodeSet._set.forEach((encoded, index) => {
|
||||
if (!encoded && chars[index]) {
|
||||
// encode charCodeAt(index)
|
||||
encodeSet._set[index] = 1;
|
||||
}
|
||||
});
|
||||
|
||||
return encodeSet;
|
||||
}
|
||||
|
||||
// check if the input characters are iterable or not
|
||||
if (!(chars && typeof chars.forEach === 'function')) {
|
||||
return encodeSet;
|
||||
}
|
||||
|
||||
chars.forEach((char) => {
|
||||
encodeSet.add(char);
|
||||
});
|
||||
|
||||
return encodeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a set of characters / bytes that should be percent-encoded.
|
||||
*/
|
||||
class EncodeSet {
|
||||
/**
|
||||
* @param {CharSet} chars Character set to encode
|
||||
*/
|
||||
constructor (chars) {
|
||||
/**
|
||||
* Indexes in Uint8Array represents char codes for characters to encode.
|
||||
*
|
||||
* Size: 128, ASCII range [0, 0x7F]
|
||||
*
|
||||
* where,
|
||||
* 1 -> encode
|
||||
* 0 -> don't encode
|
||||
*
|
||||
* @private
|
||||
* @type {Uint8Array}
|
||||
*/
|
||||
this._set = new Uint8Array(0x80);
|
||||
|
||||
// encode C0 control codes [00, 0x1F] AND 0x7F
|
||||
this._set.fill(1, 0, 0x20); // 0 to 31
|
||||
this._set[0x7F] = 1; // 127
|
||||
|
||||
/**
|
||||
* A Boolean indicating whether or not this EncodeSet is sealed.
|
||||
*
|
||||
* @private
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this._sealed = false;
|
||||
|
||||
// extend this set with input characters
|
||||
extendEncodeSet(this, chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new character to the EncodeSet.
|
||||
*
|
||||
* @example
|
||||
* var xyzEncodeSet = new EncodeSet(['x', 'y', 'z'])
|
||||
*
|
||||
* xyzEncodeSet
|
||||
* .add('X')
|
||||
* .add(89) // Y
|
||||
* .add(0x5a) // Z
|
||||
*
|
||||
* @param {Char} char Character or character code
|
||||
* @returns {EncodeSet} Current EncodeSet
|
||||
*/
|
||||
add (char) {
|
||||
// bail out if the EncodeSet is sealed
|
||||
if (this._sealed) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const code = charCode(char);
|
||||
|
||||
// ensure ASCII range
|
||||
if (code < 0x80) {
|
||||
this._set[code] = 1;
|
||||
}
|
||||
|
||||
// chaining
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean asserting whether the given char code will be encoded in
|
||||
* the EncodeSet or not.
|
||||
*
|
||||
* @note Always encode C0 control codes in the range U+0000 to U+001F and U+007F
|
||||
* Refer: https://infra.spec.whatwg.org/#c0-control
|
||||
*
|
||||
* @example
|
||||
* var tildeEncodeSet = new EncodeSet(['~'])
|
||||
*
|
||||
* // returns true
|
||||
* tildeEncodeSet.has('~'.charCodeAt(0))
|
||||
*
|
||||
* // returns false
|
||||
* tildeEncodeSet.has(65) // A
|
||||
*
|
||||
* // returns true
|
||||
* tildeEncodeSet.has(31) // \u001f (control character)
|
||||
*
|
||||
* @param {Number} code Character code
|
||||
* @returns {Boolean} Returns true if the character with the specified char code
|
||||
* exists in the EncodeSet; otherwise false
|
||||
*/
|
||||
has (code) {
|
||||
// encode if not in ASCII range (-∞, 0) OR (127, ∞)
|
||||
if (code < 0 || code > 0x7F) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// encode if present in the set
|
||||
return Boolean(this._set[code]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the current EncodeSet.
|
||||
*
|
||||
* @example
|
||||
* var set1 = new EncodeSet(['<', '>'])
|
||||
* var set1Copy = set1.clone().add('=')
|
||||
*
|
||||
* @returns {EncodeSet} New EncodeSet instance
|
||||
*/
|
||||
clone () {
|
||||
return new EncodeSet(this._set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seals the current EncodeSet to prevent new characters being added to it.
|
||||
*
|
||||
* @example
|
||||
* var set = new EncodeSet()
|
||||
*
|
||||
* set.add(95)
|
||||
* set.has(95) // returns true
|
||||
*
|
||||
* set.seal()
|
||||
* set.add(100)
|
||||
* set.has(100) // returns false
|
||||
*
|
||||
* @returns {EncodeSet} Current EncodeSet
|
||||
*/
|
||||
seal () {
|
||||
this._sealed = true;
|
||||
|
||||
try {
|
||||
// @note Cannot freeze array buffer views with elements.
|
||||
// So, rely upon the alternative `Object.seal` method and avoid mutations
|
||||
// via EncodeSet~add method.
|
||||
// Also, sealed Uint8Array enumerates faster in V8!
|
||||
Object.seal(this._set);
|
||||
}
|
||||
catch (_) {
|
||||
// silently swallow exceptions
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EncodeSet by extending the input EncodeSet with additional
|
||||
* characters.
|
||||
*
|
||||
* @example
|
||||
* var fooEncodeSet = new EncodeSet(['f', 'o'])
|
||||
* var foobarEncodeSet = EncodeSet.extend(fooEncodeSet, new Set(['b', 'a', 'r']))
|
||||
*
|
||||
* @param {EncodeSet} encodeSet Instance of EncodeSet
|
||||
* @param {CharSet} chars Character set to encode
|
||||
* @returns {EncodeSet} Copy of given `encodeSet` with extended `chars`
|
||||
* @throws {TypeError} Argument `encodeSet` must be of type {@link EncodeSet}
|
||||
*/
|
||||
static extend (encodeSet, chars) {
|
||||
if (!EncodeSet.isEncodeSet(encodeSet)) {
|
||||
throw new TypeError('Argument `encodeSet` must be EncodeSet');
|
||||
}
|
||||
|
||||
// extend the cloned encodeSet to avoid mutations
|
||||
return extendEncodeSet(encodeSet.clone(), chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the input value is an EncodeSet or not.
|
||||
*
|
||||
* @example
|
||||
* // returns true
|
||||
* EncodeSet.isEncodeSet(new EncodeSet([40, 41]))
|
||||
*
|
||||
* // returns false
|
||||
* EncodeSet.isEncodeSet(new Set([28, 05]))
|
||||
*
|
||||
* @param {*} value The value to be tested
|
||||
* @returns {Boolean} true if the given value is an EncodeSet; otherwise, false
|
||||
*/
|
||||
static isEncodeSet (value) {
|
||||
return Boolean(value) && (value instanceof EncodeSet);
|
||||
}
|
||||
}
|
||||
|
||||
const // eslint-disable-line one-var
|
||||
|
||||
/**
|
||||
* The C0 control percent-encode set are the C0 controls and all code points
|
||||
* greater than U+007E (~).
|
||||
*
|
||||
* @const
|
||||
* @type {EncodeSet}
|
||||
* @see {@link https://url.spec.whatwg.org/#c0-control-percent-encode-set}
|
||||
*/
|
||||
C0_CONTROL_ENCODE_SET = new EncodeSet().seal(),
|
||||
|
||||
/**
|
||||
* The fragment percent-encode set is the C0 control percent-encode set and
|
||||
* U+0020 SPACE, U+0022 ("), U+003C (<), U+003E (>), and U+0060 (`).
|
||||
*
|
||||
* @const
|
||||
* @type {EncodeSet}
|
||||
* @see {@link https://url.spec.whatwg.org/#fragment-percent-encode-set}
|
||||
*/
|
||||
FRAGMENT_ENCODE_SET = EncodeSet.extend(C0_CONTROL_ENCODE_SET, FRAGMENT_EXTEND_CHARS).seal(),
|
||||
|
||||
/**
|
||||
* The path percent-encode set is the fragment percent-encode set and
|
||||
* U+0023 (#), U+003F (?), U+007B ({), and U+007D (}).
|
||||
*
|
||||
* @const
|
||||
* @type {EncodeSet}
|
||||
* @see {@link https://url.spec.whatwg.org/#path-percent-encode-set}
|
||||
*/
|
||||
PATH_ENCODE_SET = EncodeSet.extend(FRAGMENT_ENCODE_SET, PATH_EXTEND_CHARS).seal(),
|
||||
|
||||
/**
|
||||
* The userinfo percent-encode set is the path percent-encode set and
|
||||
* U+002F (/), U+003A (:), U+003B (;), U+003D (=), U+0040 (@), U+005B ([),
|
||||
* U+005C (\), U+005D (]), U+005E (^), and U+007C (|).
|
||||
*
|
||||
* @const
|
||||
* @type {EncodeSet}
|
||||
* @see {@link https://url.spec.whatwg.org/#userinfo-percent-encode-set}
|
||||
*/
|
||||
USERINFO_ENCODE_SET = EncodeSet.extend(PATH_ENCODE_SET, USERINFO_EXTEND_CHARS).seal(),
|
||||
|
||||
/**
|
||||
* The query percent-encode set is the C0 control percent-encode set and
|
||||
* U+0020 SPACE, U+0022 ("), U+0023 (#), U+0027 ('), U+003C (<), and U+003E (>).
|
||||
*
|
||||
* @const
|
||||
* @type {EncodeSet}
|
||||
* @see {@link https://url.spec.whatwg.org/#query-state}
|
||||
*/
|
||||
QUERY_ENCODE_SET = new EncodeSet(QUERY_ENCODE_CHARS).seal();
|
||||
|
||||
module.exports = {
|
||||
// EncodeSet class
|
||||
EncodeSet,
|
||||
|
||||
// Constants
|
||||
PATH_ENCODE_SET,
|
||||
QUERY_ENCODE_SET,
|
||||
FRAGMENT_ENCODE_SET,
|
||||
USERINFO_ENCODE_SET,
|
||||
C0_CONTROL_ENCODE_SET
|
||||
};
|
366
node_modules/postman-url-encoder/encoder/index.js
generated
vendored
Normal file
366
node_modules/postman-url-encoder/encoder/index.js
generated
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* This module helps to encode different URL components and expose utility
|
||||
* methods to percent-encode a given string using an {@link EncodeSet}.
|
||||
*
|
||||
* @example
|
||||
* const encoder = require('postman-url-encoder/encoder')
|
||||
*
|
||||
* // returns 'xn--48jwgn17gdel797d.com'
|
||||
* encoder.encodeHost('郵便屋さん.com')
|
||||
*
|
||||
* @example <caption>Using EncodeSet</caption>
|
||||
* var EncodeSet = require('postman-url-encoder/encoder').EncodeSet
|
||||
*
|
||||
* var fragmentEncodeSet = new EncodeSet([' ', '"', '<', '>', '`'])
|
||||
*
|
||||
* // returns false
|
||||
* fragmentEncodeSet.has('['.charCodeAt(0))
|
||||
*
|
||||
* // returns true
|
||||
* fragmentEncodeSet.has('<'.charCodeAt(0))
|
||||
*
|
||||
* @module postman-url-encoder/encoder
|
||||
* @see {@link https://url.spec.whatwg.org/#url-representation}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview
|
||||
* This module determines which of the reserved characters in the different
|
||||
* URL components should be percent-encoded and which can be safely used.
|
||||
*
|
||||
* The generic URI syntax consists of a hierarchical sequence of components
|
||||
* referred to as the scheme, authority, path, query, and fragment.
|
||||
*
|
||||
* URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
|
||||
*
|
||||
* hier-part = "//" authority path-abempty
|
||||
* / path-absolute
|
||||
* / path-rootless
|
||||
* / path-empty
|
||||
*
|
||||
* authority = [ userinfo "@" ] host [ ":" port ]
|
||||
*
|
||||
* @see {@link https://tools.ietf.org/html/rfc3986#section-2}
|
||||
* @see {@link https://tools.ietf.org/html/rfc3986#section-3}
|
||||
*/
|
||||
|
||||
const encodeSet = require('./encode-set'),
|
||||
|
||||
_percentEncode = require('./percent-encode').encode,
|
||||
_percentEncodeCharCode = require('./percent-encode').encodeCharCode,
|
||||
|
||||
EncodeSet = encodeSet.EncodeSet,
|
||||
|
||||
PATH_ENCODE_SET = encodeSet.PATH_ENCODE_SET,
|
||||
QUERY_ENCODE_SET = encodeSet.QUERY_ENCODE_SET,
|
||||
USERINFO_ENCODE_SET = encodeSet.USERINFO_ENCODE_SET,
|
||||
FRAGMENT_ENCODE_SET = encodeSet.FRAGMENT_ENCODE_SET,
|
||||
C0_CONTROL_ENCODE_SET = encodeSet.C0_CONTROL_ENCODE_SET,
|
||||
|
||||
PARAM_VALUE_ENCODE_SET = EncodeSet.extend(QUERY_ENCODE_SET, ['&']).seal(),
|
||||
PARAM_KEY_ENCODE_SET = EncodeSet.extend(QUERY_ENCODE_SET, ['&', '=']).seal(),
|
||||
|
||||
E = '',
|
||||
EQUALS = '=',
|
||||
AMPERSAND = '&',
|
||||
STRING = 'string',
|
||||
OBJECT = 'object',
|
||||
|
||||
PATH_SEPARATOR = '/',
|
||||
DOMAIN_SEPARATOR = '.',
|
||||
|
||||
/**
|
||||
* Returns the Punycode ASCII serialization of the domain.
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
* @param {String} domain domain name
|
||||
* @returns {String} punycode encoded domain name
|
||||
*/
|
||||
domainToASCII = (function () {
|
||||
// @note `url.domainToASCII` returns empty string for invalid domain.
|
||||
const domainToASCII = require('url').domainToASCII;
|
||||
|
||||
// use faster native `url` method in Node.js
|
||||
/* istanbul ignore next */
|
||||
if (typeof domainToASCII === 'function') {
|
||||
return domainToASCII;
|
||||
}
|
||||
|
||||
// else, lazy load `punycode` dependency in browser
|
||||
/* istanbul ignore next */
|
||||
return require('punycode').toASCII;
|
||||
}());
|
||||
|
||||
/**
|
||||
* Returns the Punycode ASCII serialization of the domain.
|
||||
*
|
||||
* @note Returns input hostname on invalid domain.
|
||||
*
|
||||
* @example
|
||||
* // returns 'xn--fiq228c.com'
|
||||
* encodeHost('中文.com')
|
||||
*
|
||||
* // returns 'xn--48jwgn17gdel797d.com'
|
||||
* encodeHost(['郵便屋さん', 'com'])
|
||||
*
|
||||
* // returns '127.0.0.1'
|
||||
* encodeHost('127.1')
|
||||
*
|
||||
* // returns 'xn--iñvalid.com'
|
||||
* encodeHost('xn--iñvalid.com')
|
||||
*
|
||||
* @param {String|String[]} hostName host or domain name
|
||||
* @returns {String} Punycode-encoded hostname
|
||||
*/
|
||||
function encodeHost (hostName) {
|
||||
if (Array.isArray(hostName)) {
|
||||
hostName = hostName.join(DOMAIN_SEPARATOR);
|
||||
}
|
||||
|
||||
if (typeof hostName !== STRING) {
|
||||
return E;
|
||||
}
|
||||
|
||||
// return input host name if `domainToASCII` returned an empty string
|
||||
return domainToASCII(hostName) || hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes URL path or individual path segments.
|
||||
*
|
||||
* @example
|
||||
* // returns 'foo/bar&baz'
|
||||
* encodePath('foo/bar&baz')
|
||||
*
|
||||
* // returns 'foo/bar/%20%22%3C%3E%60%23%3F%7B%7D'
|
||||
* encodePath(['foo', 'bar', ' "<>\`#?{}'])
|
||||
*
|
||||
* @param {String|String[]} path Path or path segments
|
||||
* @returns {String} Percent-encoded path
|
||||
*/
|
||||
function encodePath (path) {
|
||||
if (Array.isArray(path) && path.length) {
|
||||
path = path.join(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
if (typeof path !== STRING) {
|
||||
return E;
|
||||
}
|
||||
|
||||
return _percentEncode(path, PATH_ENCODE_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes URL userinfo (username / password) fields.
|
||||
*
|
||||
* @example
|
||||
* // returns 'info~%20%22%3C%3E%60%23%3F%7B%7D%2F%3A%3B%3D%40%5B%5C%5D%5E%7C'
|
||||
* encodeAuth('info~ "<>`#?{}/:;=@[\\]^|')
|
||||
*
|
||||
* @param {String} param Parameter to encode
|
||||
* @returns {String} Percent-encoded parameter
|
||||
*/
|
||||
function encodeUserInfo (param) {
|
||||
if (typeof param !== STRING) {
|
||||
return E;
|
||||
}
|
||||
|
||||
return _percentEncode(param, USERINFO_ENCODE_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes URL fragment identifier or hash.
|
||||
*
|
||||
* @example
|
||||
* // returns 'fragment#%20%22%3C%3E%60'
|
||||
* encodeHash('fragment# "<>`')
|
||||
*
|
||||
* @param {String} fragment Hash or fragment identifier to encode
|
||||
* @returns {String} Percent-encoded fragment
|
||||
*/
|
||||
function encodeFragment (fragment) {
|
||||
if (typeof fragment !== STRING) {
|
||||
return E;
|
||||
}
|
||||
|
||||
return _percentEncode(fragment, FRAGMENT_ENCODE_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes single query parameter and returns as a string.
|
||||
*
|
||||
* @example
|
||||
* // returns 'param%20%22%23%27%3C%3E'
|
||||
* encodeQueryParam('param "#\'<>')
|
||||
*
|
||||
* // returns 'foo=bar'
|
||||
* encodeQueryParam({ key: 'foo', value: 'bar' })
|
||||
*
|
||||
* @param {Object|String} param Query param to encode
|
||||
* @returns {String} Percent-encoded query param
|
||||
*/
|
||||
function encodeQueryParam (param) {
|
||||
if (!param) {
|
||||
return E;
|
||||
}
|
||||
|
||||
if (typeof param === STRING) {
|
||||
return _percentEncode(param, QUERY_ENCODE_SET);
|
||||
}
|
||||
|
||||
let key = param.key,
|
||||
value = param.value,
|
||||
result;
|
||||
|
||||
if (typeof key === STRING) {
|
||||
result = _percentEncode(key, PARAM_KEY_ENCODE_SET);
|
||||
}
|
||||
else {
|
||||
result = E;
|
||||
}
|
||||
|
||||
if (typeof value === STRING) {
|
||||
result += EQUALS + _percentEncode(value, PARAM_VALUE_ENCODE_SET);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes list of query parameters and returns encoded query string.
|
||||
*
|
||||
* @example
|
||||
* // returns 'foo=bar&=foo%26bar'
|
||||
* encodeQueryParams([{ key: 'foo', value: 'bar' }, { value: 'foo&bar' }])
|
||||
*
|
||||
* // returns 'q1=foo&q2=bar&q2=baz'
|
||||
* encodeQueryParams({ q1: 'foo', q2: ['bar', 'baz'] })
|
||||
*
|
||||
* @param {Object|Object[]} params Query params to encode
|
||||
* @returns {String} Percent-encoded query string
|
||||
*/
|
||||
function encodeQueryParams (params) {
|
||||
let i,
|
||||
j,
|
||||
ii,
|
||||
jj,
|
||||
paramKey,
|
||||
paramKeys,
|
||||
paramValue,
|
||||
result = E,
|
||||
notFirstParam = false;
|
||||
|
||||
if (!(params && typeof params === OBJECT)) {
|
||||
return E;
|
||||
}
|
||||
|
||||
// handle array of query params
|
||||
if (Array.isArray(params)) {
|
||||
for (i = 0, ii = params.length; i < ii; i++) {
|
||||
// @todo Add helper in PropertyList to filter disabled QueryParam
|
||||
if (!params[i] || params[i].disabled === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't add '&' for the very first enabled param
|
||||
notFirstParam && (result += AMPERSAND);
|
||||
notFirstParam = true;
|
||||
|
||||
result += encodeQueryParam(params[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// handle object with query params
|
||||
paramKeys = Object.keys(params);
|
||||
|
||||
for (i = 0, ii = paramKeys.length; i < ii; i++) {
|
||||
paramKey = paramKeys[i];
|
||||
paramValue = params[paramKey];
|
||||
|
||||
// { key: ['value1', 'value2', 'value3'] }
|
||||
if (Array.isArray(paramValue)) {
|
||||
for (j = 0, jj = paramValue.length; j < jj; j++) {
|
||||
notFirstParam && (result += AMPERSAND);
|
||||
notFirstParam = true;
|
||||
|
||||
result += encodeQueryParam({ key: paramKey, value: paramValue[j] });
|
||||
}
|
||||
}
|
||||
// { key: 'value' }
|
||||
else {
|
||||
notFirstParam && (result += AMPERSAND);
|
||||
notFirstParam = true;
|
||||
|
||||
result += encodeQueryParam({ key: paramKey, value: paramValue });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent-encode the given string with the given {@link EncodeSet}.
|
||||
*
|
||||
* @example <caption>Defaults to C0_CONTROL_ENCODE_SET</caption>
|
||||
* // returns 'foo %00 bar'
|
||||
* percentEncode('foo \u0000 bar')
|
||||
*
|
||||
* @example <caption>Encode literal @ using custom EncodeSet</caption>
|
||||
* // returns 'foo%40bar'
|
||||
* percentEncode('foo@bar', new EncodeSet(['@']))
|
||||
*
|
||||
* @param {String} value String to percent-encode
|
||||
* @param {EncodeSet} [encodeSet=C0_CONTROL_ENCODE_SET] EncodeSet to use for encoding
|
||||
* @returns {String} Percent-encoded string
|
||||
*/
|
||||
function percentEncode (value, encodeSet) {
|
||||
if (!(value && typeof value === STRING)) {
|
||||
return E;
|
||||
}
|
||||
|
||||
// defaults to C0_CONTROL_ENCODE_SET
|
||||
if (!EncodeSet.isEncodeSet(encodeSet)) {
|
||||
encodeSet = C0_CONTROL_ENCODE_SET;
|
||||
}
|
||||
|
||||
return _percentEncode(value, encodeSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent encode a character with given code.
|
||||
*
|
||||
* @example
|
||||
* // returns '%20'
|
||||
* percentEncodeCharCode(32)
|
||||
*
|
||||
* @param {Number} code Character code
|
||||
* @returns {String} Percent-encoded character
|
||||
*/
|
||||
function percentEncodeCharCode (code) {
|
||||
// ensure [0x00, 0xFF] range
|
||||
if (!(Number.isInteger(code) && code >= 0 && code <= 0xFF)) {
|
||||
return E;
|
||||
}
|
||||
|
||||
return _percentEncodeCharCode(code);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
// URL components
|
||||
encodeHost,
|
||||
encodePath,
|
||||
encodeUserInfo,
|
||||
encodeFragment,
|
||||
encodeQueryParam,
|
||||
encodeQueryParams,
|
||||
|
||||
/** @type EncodeSet */
|
||||
EncodeSet,
|
||||
|
||||
// Utilities
|
||||
percentEncode,
|
||||
percentEncodeCharCode
|
||||
};
|
118
node_modules/postman-url-encoder/encoder/percent-encode.js
generated
vendored
Normal file
118
node_modules/postman-url-encoder/encoder/percent-encode.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* This modules provides simple percent (URI) encoding.
|
||||
*
|
||||
* @note Safety check for input types is not done intentionally as these
|
||||
* functions are invoked in the hot code path.
|
||||
*
|
||||
* @private
|
||||
* @module postman-url-encoder/encoder/percent-encode
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview
|
||||
* A percent-encoding mechanism is used to represent a data octet in a component
|
||||
* when that octet's corresponding character is outside the allowed set or is
|
||||
* being used as a delimiter of, or within, the component.
|
||||
* A percent-encoded octet is encoded as a character triplet, consisting of the
|
||||
* percent character "%" followed by the two hexadecimal digits representing
|
||||
* that octet's numeric value.
|
||||
*
|
||||
* For example, "%20" is the percent-encoding for the binary octet "00100000"
|
||||
* (ABNF: %x20), which in US-ASCII corresponds to the space character (SP).
|
||||
*
|
||||
* @see {@link https://en.wikipedia.org/wiki/Percent-encoding}
|
||||
* @see {@link https://tools.ietf.org/html/rfc3986#section-2.1}
|
||||
*/
|
||||
|
||||
const E = '',
|
||||
ZERO = '0',
|
||||
PERCENT = '%';
|
||||
|
||||
/**
|
||||
* Checks if character with given code is valid hexadecimal digit or not.
|
||||
*
|
||||
* @private
|
||||
* @param {Number} byte Byte
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isPreEncodedCharacter (byte) {
|
||||
return (byte >= 0x30 && byte <= 0x39) || // 0-9
|
||||
(byte >= 0x41 && byte <= 0x46) || // A-F
|
||||
(byte >= 0x61 && byte <= 0x66); // a-f
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if character at given index in the buffer is already percent encoded or not.
|
||||
*
|
||||
* @private
|
||||
* @param {Buffer} buffer Buffer to check the character from
|
||||
* @param {Number} i Index of the character to check
|
||||
* @returns {Boolean} true if the character is encoded, false otherwise
|
||||
*/
|
||||
function isPreEncoded (buffer, i) {
|
||||
// if it is % check next two bytes for percent encode characters
|
||||
// looking for pattern %00 - %FF
|
||||
return buffer[i] === 0x25 && // %
|
||||
isPreEncodedCharacter(buffer[i + 1]) &&
|
||||
isPreEncodedCharacter(buffer[i + 2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent encode a character with given code.
|
||||
*
|
||||
* @example
|
||||
* // returns '%20'
|
||||
* encodeCharCode(32)
|
||||
*
|
||||
* @param {Number} code Character code
|
||||
* @returns {String} Percent-encoded character
|
||||
*/
|
||||
function encodeCharCode (code) {
|
||||
let hex = code.toString(16).toUpperCase();
|
||||
|
||||
(hex.length === 1) && (hex = ZERO + hex);
|
||||
|
||||
return PERCENT + hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent-encode the given string with the given {@link EncodeSet}.
|
||||
*
|
||||
* @example
|
||||
* // returns 'foo%40bar'
|
||||
* encode('foo@bar', new EncodeSet(['@']))
|
||||
*
|
||||
* @param {String} value String to percent-encode
|
||||
* @param {EncodeSet} encodeSet EncodeSet to use for encoding
|
||||
* @returns {String} Percent-encoded string
|
||||
*/
|
||||
function encode (value, encodeSet) {
|
||||
let i,
|
||||
ii,
|
||||
charCode,
|
||||
encoded = E,
|
||||
buffer = Buffer.from(value);
|
||||
|
||||
for (i = 0, ii = buffer.length; i < ii; ++i) {
|
||||
// avoid double encoding
|
||||
if (i < ii - 2 && isPreEncoded(buffer, i)) {
|
||||
encoded += PERCENT + String.fromCharCode(buffer[++i], buffer[++i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
charCode = buffer[i];
|
||||
|
||||
encoded += encodeSet.has(charCode) ?
|
||||
// encode if char code present in encodeSet
|
||||
encodeCharCode(charCode) :
|
||||
// or, append string from char code
|
||||
String.fromCharCode(charCode);
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
encode,
|
||||
encodeCharCode
|
||||
};
|
447
node_modules/postman-url-encoder/index.js
generated
vendored
Normal file
447
node_modules/postman-url-encoder/index.js
generated
vendored
Normal file
@@ -0,0 +1,447 @@
|
||||
/**
|
||||
* Implementation of the WHATWG URL Standard.
|
||||
*
|
||||
* @example
|
||||
* const urlEncoder = require('postman-url-encoder')
|
||||
*
|
||||
* // Encoding URL string to Node.js compatible Url object
|
||||
* urlEncoder.toNodeUrl('郵便屋さん.com/foo&bar/{baz}?q=("foo")#`hash`')
|
||||
*
|
||||
* // Encoding URI component
|
||||
* urlEncoder.encode('qüêry štrìng')
|
||||
*
|
||||
* // Encoding query string object
|
||||
* urlEncoder.encodeQueryString({ q1: 'foo', q2: ['bãr', 'baž'] })
|
||||
*
|
||||
* @module postman-url-encoder
|
||||
* @see {@link https://url.spec.whatwg.org}
|
||||
*/
|
||||
|
||||
const querystring = require('querystring'),
|
||||
|
||||
legacy = require('./legacy'),
|
||||
parser = require('./parser'),
|
||||
encoder = require('./encoder'),
|
||||
QUERY_ENCODE_SET = require('./encoder/encode-set').QUERY_ENCODE_SET,
|
||||
|
||||
E = '',
|
||||
COLON = ':',
|
||||
BACK_SLASH = '\\',
|
||||
DOUBLE_SLASH = '//',
|
||||
DOUBLE_BACK_SLASH = '\\\\',
|
||||
STRING = 'string',
|
||||
OBJECT = 'object',
|
||||
FUNCTION = 'function',
|
||||
DEFAULT_PROTOCOL = 'http',
|
||||
LEFT_SQUARE_BRACKET = '[',
|
||||
RIGHT_SQUARE_BRACKET = ']',
|
||||
|
||||
PATH_SEPARATOR = '/',
|
||||
QUERY_SEPARATOR = '?',
|
||||
PARAMS_SEPARATOR = '&',
|
||||
SEARCH_SEPARATOR = '#',
|
||||
DOMAIN_SEPARATOR = '.',
|
||||
AUTH_CREDENTIALS_SEPARATOR = '@',
|
||||
|
||||
// @note this regular expression is referred from Node.js URL parser
|
||||
PROTOCOL_RE = /^[a-z0-9.+-]+:(?:\/\/|\\\\)./i,
|
||||
|
||||
/**
|
||||
* Protocols that always contain a // bit.
|
||||
*
|
||||
* @private
|
||||
* @see {@link https://github.com/nodejs/node/blob/v10.17.0/lib/url.js#L91}
|
||||
*/
|
||||
SLASHED_PROTOCOLS = {
|
||||
'file:': true,
|
||||
'ftp:': true,
|
||||
'gopher:': true,
|
||||
'http:': true,
|
||||
'https:': true,
|
||||
'ws:': true,
|
||||
'wss:': true
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns stringified URL from Url object but only includes parts till given
|
||||
* part name.
|
||||
*
|
||||
* @example
|
||||
* var url = 'http://postman.com/foo?q=v#hash';
|
||||
* getUrlTill(toNodeUrl(url), 'host')
|
||||
* // returns 'http://postman.com'
|
||||
*
|
||||
* @private
|
||||
* @param {Object} url base URL
|
||||
* @param {String} [urlPart='query'] one of ['host', 'pathname', 'query']
|
||||
*/
|
||||
function getUrlTill (url, urlPart) {
|
||||
let result = '';
|
||||
|
||||
if (url.protocol) {
|
||||
result += url.protocol + DOUBLE_SLASH;
|
||||
}
|
||||
|
||||
if (url.auth) {
|
||||
result += url.auth + AUTH_CREDENTIALS_SEPARATOR;
|
||||
}
|
||||
|
||||
result += url.host || E;
|
||||
|
||||
if (urlPart === 'host') { return result; }
|
||||
|
||||
result += url.pathname;
|
||||
|
||||
if (urlPart === 'pathname') { return result; }
|
||||
|
||||
// urlPart must be query at this point
|
||||
return result + (url.search || E);
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent-encode the given string using QUERY_ENCODE_SET.
|
||||
*
|
||||
* @deprecated since version 2.0, use {@link encodeQueryParam} instead.
|
||||
*
|
||||
* @example
|
||||
* // returns 'foo%20%22%23%26%27%3C%3D%3E%20bar'
|
||||
* encode('foo "#&\'<=> bar')
|
||||
*
|
||||
* // returns ''
|
||||
* encode(['foobar'])
|
||||
*
|
||||
* @param {String} value String to percent-encode
|
||||
* @returns {String} Percent-encoded string
|
||||
*/
|
||||
function encode (value) {
|
||||
return encoder.percentEncode(value, QUERY_ENCODE_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent-encode the URL query string or x-www-form-urlencoded body object
|
||||
* according to RFC3986.
|
||||
*
|
||||
* @example
|
||||
* // returns 'q1=foo&q2=bar&q2=baz'
|
||||
* encodeQueryString({ q1: 'foo', q2: ['bar', 'baz'] })
|
||||
*
|
||||
* @param {Object} query Object representing query or urlencoded body
|
||||
* @returns {String} Percent-encoded string
|
||||
*/
|
||||
function encodeQueryString (query) {
|
||||
if (!(query && typeof query === OBJECT)) {
|
||||
return E;
|
||||
}
|
||||
|
||||
// rely upon faster querystring module
|
||||
query = querystring.stringify(query);
|
||||
|
||||
// encode characters not encoded by querystring.stringify() according to RFC3986.
|
||||
return query.replace(/[!'()*]/g, function (c) {
|
||||
return encoder.percentEncodeCharCode(c.charCodeAt(0));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts PostmanUrl / URL string into Node.js compatible Url object.
|
||||
*
|
||||
* @example <caption>Using URL string</caption>
|
||||
* toNodeUrl('郵便屋さん.com/foo&bar/{baz}?q=("foo")#`hash`')
|
||||
* // returns
|
||||
* // {
|
||||
* // protocol: 'http:',
|
||||
* // slashes: true,
|
||||
* // auth: null,
|
||||
* // host: 'xn--48jwgn17gdel797d.com',
|
||||
* // port: null,
|
||||
* // hostname: 'xn--48jwgn17gdel797d.com',
|
||||
* // hash: '#%60hash%60',
|
||||
* // search: '?q=(%22foo%22)',
|
||||
* // query: 'q=(%22foo%22)',
|
||||
* // pathname: '/foo&bar/%7Bbaz%7D',
|
||||
* // path: '/foo&bar/%7Bbaz%7D?q=(%22foo%22)',
|
||||
* // href: 'http://xn--48jwgn17gdel797d.com/foo&bar/%7Bbaz%7D?q=(%22foo%22)#%60hash%60'
|
||||
* // }
|
||||
*
|
||||
* @example <caption>Using PostmanUrl instance</caption>
|
||||
* toNodeUrl(new sdk.Url({
|
||||
* host: 'example.com',
|
||||
* query: [{ key: 'foo', value: 'bar & baz' }]
|
||||
* }))
|
||||
*
|
||||
* @param {PostmanUrl|String} url URL string or PostmanUrl object
|
||||
* @param {Boolean} disableEncoding Turn encoding off
|
||||
* @returns {Url} Node.js like parsed and encoded object
|
||||
*/
|
||||
function toNodeUrl (url, disableEncoding) {
|
||||
let nodeUrl = {
|
||||
protocol: null,
|
||||
slashes: null,
|
||||
auth: null,
|
||||
host: null,
|
||||
port: null,
|
||||
hostname: null,
|
||||
hash: null,
|
||||
search: null,
|
||||
query: null,
|
||||
pathname: null,
|
||||
path: null,
|
||||
href: E
|
||||
},
|
||||
port,
|
||||
hostname,
|
||||
pathname,
|
||||
authUser,
|
||||
queryParams,
|
||||
authPassword;
|
||||
|
||||
// Check if PostmanUrl instance and prepare segments
|
||||
if (url && url.constructor && url.constructor._postman_propertyName === 'Url') {
|
||||
// @note getPath() always adds a leading '/', similar to Node.js API
|
||||
pathname = url.getPath();
|
||||
hostname = url.getHost().toLowerCase();
|
||||
|
||||
if (url.query && url.query.count()) {
|
||||
queryParams = url.getQueryString({ ignoreDisabled: true });
|
||||
queryParams = disableEncoding ? queryParams : encoder.encodeQueryParam(queryParams);
|
||||
|
||||
// either all the params are disabled or a single param is like { key: '' } (http://localhost?)
|
||||
// in that case, query separator ? must be included in the raw URL.
|
||||
// @todo Add helper in SDK to handle this
|
||||
if (queryParams === E) {
|
||||
// check if there's any enabled param, if so, set queryString to empty string
|
||||
// otherwise (all disabled), it will be set as undefined
|
||||
queryParams = url.query.find(function (param) { return !(param && param.disabled); }) && E;
|
||||
}
|
||||
}
|
||||
|
||||
if (url.auth) {
|
||||
authUser = url.auth.user;
|
||||
authPassword = url.auth.password;
|
||||
}
|
||||
}
|
||||
// Parser URL string and prepare segments
|
||||
else if (typeof url === STRING) {
|
||||
url = parser.parse(url);
|
||||
|
||||
pathname = PATH_SEPARATOR + (url.path || []).join(PATH_SEPARATOR);
|
||||
hostname = (url.host || []).join(DOMAIN_SEPARATOR).toLowerCase();
|
||||
queryParams = url.query && (queryParams = url.query.join(PARAMS_SEPARATOR)) &&
|
||||
(disableEncoding ? queryParams : encoder.encodeQueryParam(queryParams));
|
||||
authUser = (url.auth || [])[0];
|
||||
authPassword = (url.auth || [])[1];
|
||||
}
|
||||
// bail out with empty URL object for invalid input
|
||||
else {
|
||||
return nodeUrl;
|
||||
}
|
||||
|
||||
// @todo Add helper in SDK to normalize port
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
if (!(url.port == null) && typeof url.port.toString === FUNCTION) {
|
||||
port = url.port.toString();
|
||||
}
|
||||
|
||||
// #protocol
|
||||
nodeUrl.protocol = (typeof url.protocol === STRING) ? url.protocol.toLowerCase() : DEFAULT_PROTOCOL;
|
||||
nodeUrl.protocol += COLON;
|
||||
|
||||
// #slashes
|
||||
nodeUrl.slashes = SLASHED_PROTOCOLS[nodeUrl.protocol] || false;
|
||||
|
||||
// #href = protocol://
|
||||
nodeUrl.href = nodeUrl.protocol + DOUBLE_SLASH;
|
||||
|
||||
// #auth
|
||||
if (url.auth) {
|
||||
if (typeof authUser === STRING) {
|
||||
nodeUrl.auth = disableEncoding ? authUser : encoder.encodeUserInfo(authUser);
|
||||
}
|
||||
|
||||
if (typeof authPassword === STRING) {
|
||||
!nodeUrl.auth && (nodeUrl.auth = E);
|
||||
nodeUrl.auth += COLON + (disableEncoding ? authPassword : encoder.encodeUserInfo(authPassword));
|
||||
}
|
||||
|
||||
if (typeof nodeUrl.auth === STRING) {
|
||||
// #href = protocol://user:password@
|
||||
nodeUrl.href += nodeUrl.auth + AUTH_CREDENTIALS_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
// #host, #hostname
|
||||
nodeUrl.host = nodeUrl.hostname = hostname = encoder.encodeHost(hostname); // @note always encode hostname
|
||||
|
||||
// #href = protocol://user:password@host.name
|
||||
nodeUrl.href += nodeUrl.hostname;
|
||||
|
||||
// #port
|
||||
if (typeof port === STRING) {
|
||||
nodeUrl.port = port;
|
||||
|
||||
// #host = (#hostname):(#port)
|
||||
nodeUrl.host = nodeUrl.hostname + COLON + port;
|
||||
|
||||
// #href = protocol://user:password@host.name:port
|
||||
nodeUrl.href += COLON + port;
|
||||
}
|
||||
|
||||
// #path, #pathname
|
||||
nodeUrl.path = nodeUrl.pathname = disableEncoding ? pathname : encoder.encodePath(pathname);
|
||||
|
||||
// #href = protocol://user:password@host.name:port/p/a/t/h
|
||||
nodeUrl.href += nodeUrl.pathname;
|
||||
|
||||
if (typeof queryParams === STRING) {
|
||||
// #query
|
||||
nodeUrl.query = queryParams;
|
||||
|
||||
// #search
|
||||
nodeUrl.search = QUERY_SEPARATOR + nodeUrl.query;
|
||||
|
||||
// #path = (#pathname)?(#search)
|
||||
nodeUrl.path = nodeUrl.pathname + nodeUrl.search;
|
||||
|
||||
// #href = protocol://user:password@host.name:port/p/a/t/h?q=query
|
||||
nodeUrl.href += nodeUrl.search;
|
||||
}
|
||||
|
||||
if (typeof url.hash === STRING) {
|
||||
// #hash
|
||||
nodeUrl.hash = SEARCH_SEPARATOR + (disableEncoding ? url.hash : encoder.encodeFragment(url.hash));
|
||||
|
||||
// #href = protocol://user:password@host.name:port/p/a/t/h?q=query#hash
|
||||
nodeUrl.href += nodeUrl.hash;
|
||||
}
|
||||
|
||||
// Finally apply Node.js shenanigans
|
||||
// # Remove square brackets from IPv6 #hostname
|
||||
// Refer: https://github.com/nodejs/node/blob/v12.18.3/lib/url.js#L399
|
||||
// Refer: https://github.com/nodejs/node/blob/v12.18.3/lib/internal/url.js#L1273
|
||||
if (hostname[0] === LEFT_SQUARE_BRACKET && hostname[hostname.length - 1] === RIGHT_SQUARE_BRACKET) {
|
||||
nodeUrl.hostname = hostname.slice(1, -1);
|
||||
}
|
||||
|
||||
return nodeUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a relative URL with respect to given base URL.
|
||||
* This is a replacement method for Node's url.resolve() which is compatible
|
||||
* with URL object generated by toNodeUrl().
|
||||
*
|
||||
* @example
|
||||
* // returns 'http://postman.com/baz'
|
||||
* resolveNodeUrl('http://postman.com/foo/bar', '/baz')
|
||||
*
|
||||
* @param {Object|String} base URL string or toNodeUrl() object
|
||||
* @param {String} relative Relative URL to resolve
|
||||
* @returns {String} Resolved URL
|
||||
*/
|
||||
function resolveNodeUrl (base, relative) {
|
||||
// normalize arguments
|
||||
typeof base === STRING && (base = toNodeUrl(base));
|
||||
typeof relative !== STRING && (relative = E);
|
||||
|
||||
// bail out if base is not an object
|
||||
if (!(base && typeof base === OBJECT)) {
|
||||
return relative;
|
||||
}
|
||||
|
||||
let i,
|
||||
ii,
|
||||
index,
|
||||
baseHref,
|
||||
relative_0,
|
||||
relative_01,
|
||||
basePathname,
|
||||
requiredProps = ['protocol', 'auth', 'host', 'pathname', 'search', 'href'];
|
||||
|
||||
// bail out if base is not like Node url object
|
||||
for (i = 0, ii = requiredProps.length; i < ii; i++) {
|
||||
if (!Object.hasOwnProperty.call(base, requiredProps[i])) {
|
||||
return relative;
|
||||
}
|
||||
}
|
||||
|
||||
// cache base.href and base.pathname
|
||||
baseHref = base.href;
|
||||
basePathname = base.pathname;
|
||||
|
||||
// cache relative's first two chars
|
||||
relative_0 = relative.slice(0, 1);
|
||||
relative_01 = relative.slice(0, 2);
|
||||
|
||||
// @note relative can be one of
|
||||
// #1 empty string
|
||||
// #2 protocol relative, starts with // or \\
|
||||
// #3 path relative, starts with / or \
|
||||
// #4 just query or hash, starts with ? or #
|
||||
// #5 absolute URL, starts with :// or :\\
|
||||
// #6 free from path, with or without query and hash
|
||||
|
||||
// #1 empty string
|
||||
if (!relative) {
|
||||
// return base as it is if there is no hash
|
||||
if ((index = baseHref.indexOf(SEARCH_SEPARATOR)) === -1) {
|
||||
return baseHref;
|
||||
}
|
||||
|
||||
// else, return base without the hash
|
||||
return baseHref.slice(0, index);
|
||||
}
|
||||
|
||||
// #2 protocol relative, starts with // or \\
|
||||
// @note \\ is not converted to //
|
||||
if (relative_01 === DOUBLE_SLASH || relative_01 === DOUBLE_BACK_SLASH) {
|
||||
return base.protocol + relative;
|
||||
}
|
||||
|
||||
// #3 path relative, starts with / or \
|
||||
// @note \(s) are not converted to /
|
||||
if (relative_0 === PATH_SEPARATOR || relative_0 === BACK_SLASH) {
|
||||
return getUrlTill(base, 'host') + relative;
|
||||
}
|
||||
|
||||
// #4 just hash, starts with #
|
||||
if (relative_0 === SEARCH_SEPARATOR) {
|
||||
return getUrlTill(base, 'query') + relative;
|
||||
}
|
||||
|
||||
// #4 just query, starts with ?
|
||||
if (relative_0 === QUERY_SEPARATOR) {
|
||||
return getUrlTill(base, 'pathname') + relative;
|
||||
}
|
||||
|
||||
// #5 absolute URL, starts with :// or :\\
|
||||
// @note :\\ is not converted to ://
|
||||
if (PROTOCOL_RE.test(relative)) {
|
||||
return relative;
|
||||
}
|
||||
|
||||
// #6 free from path, with or without query and hash
|
||||
// remove last path segment form base path
|
||||
basePathname = basePathname.slice(0, basePathname.lastIndexOf(PATH_SEPARATOR) + 1);
|
||||
|
||||
return getUrlTill(base, 'host') + basePathname + relative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts URL string into Node.js compatible Url object using the v1 encoder.
|
||||
*
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
* @param {String} url URL string
|
||||
* @returns {Url} Node.js compatible Url object
|
||||
*/
|
||||
function toLegacyNodeUrl (url) {
|
||||
return legacy.toNodeUrl(url);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
encode,
|
||||
toNodeUrl,
|
||||
resolveNodeUrl,
|
||||
toLegacyNodeUrl,
|
||||
encodeQueryString
|
||||
};
|
242
node_modules/postman-url-encoder/legacy.js
generated
vendored
Normal file
242
node_modules/postman-url-encoder/legacy.js
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
var url = require('url'),
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
E = '',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
QUERY_SEPARATOR = '?',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
AMPERSAND = '&',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
EQUALS = '=',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
PERCENT = '%',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ZERO = '0',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
STRING = 'string',
|
||||
|
||||
encoder;
|
||||
|
||||
encoder = {
|
||||
/**
|
||||
* Percent encode a character with given code.
|
||||
*
|
||||
* @param {Number} c - character code of the character to encode
|
||||
* @returns {String} - percent encoding of given character
|
||||
*/
|
||||
percentEncode (c) {
|
||||
var hex = c.toString(16).toUpperCase();
|
||||
|
||||
(hex.length === 1) && (hex = ZERO + hex);
|
||||
|
||||
return PERCENT + hex;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if character at given index in the buffer is already percent encoded or not.
|
||||
*
|
||||
* @param {Buffer} buffer -
|
||||
* @param {Number} i -
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isPreEncoded (buffer, i) {
|
||||
// If it is % check next two bytes for percent encode characters
|
||||
// looking for pattern %00 - %FF
|
||||
return (buffer[i] === 0x25 &&
|
||||
(encoder.isPreEncodedCharacter(buffer[i + 1]) &&
|
||||
encoder.isPreEncodedCharacter(buffer[i + 2]))
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if character with given code is valid hexadecimal digit or not.
|
||||
*
|
||||
* @param {Number} byte -
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isPreEncodedCharacter (byte) {
|
||||
return (byte >= 0x30 && byte <= 0x39) || // 0-9
|
||||
(byte >= 0x41 && byte <= 0x46) || // A-F
|
||||
(byte >= 0x61 && byte <= 0x66); // a-f
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether given character should be percent encoded or not for fixture.
|
||||
*
|
||||
* @param {Number} byte -
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
charactersToPercentEncode (byte) {
|
||||
return (byte < 0x23 || byte > 0x7E || // Below # and after ~
|
||||
byte === 0x3C || byte === 0x3E || // > and <
|
||||
byte === 0x28 || byte === 0x29 || // ( and )
|
||||
byte === 0x25 || // %
|
||||
byte === 0x27 || // '
|
||||
byte === 0x2A // *
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Percent encode a query string according to RFC 3986.
|
||||
* Note: This function is supposed to be used on top of node's inbuilt url encoding
|
||||
* to solve issue https://github.com/nodejs/node/issues/8321
|
||||
*
|
||||
* @param {String} value -
|
||||
* @returns {String}
|
||||
*/
|
||||
encode (value) {
|
||||
if (!value) { return E; }
|
||||
|
||||
var buffer = Buffer.from(value),
|
||||
ret = E,
|
||||
i,
|
||||
ii;
|
||||
|
||||
for (i = 0, ii = buffer.length; i < ii; ++i) {
|
||||
if (encoder.charactersToPercentEncode(buffer[i]) && !encoder.isPreEncoded(buffer, i)) {
|
||||
ret += encoder.percentEncode(buffer[i]);
|
||||
}
|
||||
else {
|
||||
ret += String.fromCodePoint(buffer[i]); // Only works in ES6 (available in Node v4+)
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a query string into an array, preserving parameter values
|
||||
*
|
||||
* @private
|
||||
* @param {String} string -
|
||||
* @returns {*}
|
||||
*/
|
||||
function parseQueryString (string) {
|
||||
var parts;
|
||||
|
||||
if (typeof string === STRING) {
|
||||
parts = string.split(AMPERSAND);
|
||||
|
||||
return parts.map(function (param, idx) {
|
||||
if (param === E && idx !== (parts.length - 1)) {
|
||||
return { key: null, value: null };
|
||||
}
|
||||
|
||||
var index = (typeof param === STRING) ? param.indexOf(EQUALS) : -1,
|
||||
paramObj = {};
|
||||
|
||||
// this means that there was no value for this key (not even blank,
|
||||
// so we store this info) and the value is set to null
|
||||
if (index < 0) {
|
||||
paramObj.key = param.substr(0, param.length);
|
||||
paramObj.value = null;
|
||||
}
|
||||
else {
|
||||
paramObj.key = param.substr(0, index);
|
||||
paramObj.value = param.substr(index + 1);
|
||||
}
|
||||
|
||||
return paramObj;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringifies a query string, from an array of parameters
|
||||
*
|
||||
* @private
|
||||
* @param {Object[]} parameters -
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringifyQueryParams (parameters) {
|
||||
return parameters ? parameters.map(function (param) {
|
||||
var key = param.key,
|
||||
value = param.value;
|
||||
|
||||
if (value === undefined) {
|
||||
return E;
|
||||
}
|
||||
|
||||
if (key === null) {
|
||||
key = E;
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return encoder.encode(key);
|
||||
}
|
||||
|
||||
return encoder.encode(key) + EQUALS + encoder.encode(value);
|
||||
}).join(AMPERSAND) : E;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts URL string into Node's Url object with encoded values
|
||||
*
|
||||
* @private
|
||||
* @param {String} urlString -
|
||||
* @returns {Url}
|
||||
*/
|
||||
function toNodeUrl (urlString) {
|
||||
var parsed = url.parse(urlString),
|
||||
rawQs = parsed.query,
|
||||
qs,
|
||||
search,
|
||||
path,
|
||||
str;
|
||||
|
||||
if (!(rawQs && rawQs.length)) { return parsed; }
|
||||
|
||||
qs = stringifyQueryParams(parseQueryString(rawQs));
|
||||
search = QUERY_SEPARATOR + qs;
|
||||
path = parsed.pathname + search;
|
||||
|
||||
parsed.query = qs;
|
||||
parsed.search = search;
|
||||
parsed.path = path;
|
||||
|
||||
str = url.format(parsed);
|
||||
|
||||
// Parse again, because Node does not guarantee consistency of properties
|
||||
return url.parse(str);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
toNodeUrl
|
||||
};
|
70
node_modules/postman-url-encoder/package.json
generated
vendored
Normal file
70
node_modules/postman-url-encoder/package.json
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "postman-url-encoder",
|
||||
"version": "3.0.5",
|
||||
"description": "Implementation of the WHATWG URL Standard",
|
||||
"author": "Postman Inc.",
|
||||
"license": "Apache-2.0",
|
||||
"main": "index.js",
|
||||
"homepage": "https://github.com/postmanlabs/postman-url-encoder#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/postmanlabs/postman-url-encoder/issues",
|
||||
"email": "help@postman.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/postmanlabs/postman-url-encoder.git"
|
||||
},
|
||||
"keywords": [
|
||||
"postman",
|
||||
"url-encoder",
|
||||
"url-parser",
|
||||
"whatwg-url"
|
||||
],
|
||||
"scripts": {
|
||||
"build-docs": "node npm/build-docs.js",
|
||||
"codecov": "node npm/publish-coverage.js",
|
||||
"release": "node npm/create-release.js",
|
||||
"test": "npm run test-lint && npm run test-unit && npm run test-browser",
|
||||
"test-benchmark": "node npm/test-benchmark.js",
|
||||
"test-browser": "node npm/test-browser.js",
|
||||
"test-lint": "node npm/test-lint.js",
|
||||
"test-system": "node npm/test-system.js",
|
||||
"test-unit": "nyc --nycrc-path=.nycrc.js node npm/test-unit.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@postman/csv-parse": "^4.0.2",
|
||||
"@postman/shipit": "^0.3.0",
|
||||
"async": "^3.2.1",
|
||||
"bipbip": "^0.4.2",
|
||||
"browserify": "^17.0.0",
|
||||
"chai": "^4.3.4",
|
||||
"chalk": "^4.1.2",
|
||||
"colors": "^1.4.0",
|
||||
"editorconfig": "^0.15.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-jsdoc": "^36.0.7",
|
||||
"eslint-plugin-lodash": "^7.3.0",
|
||||
"eslint-plugin-mocha": "^9.0.0",
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"jsdoc": "^3.6.7",
|
||||
"karma": "^6.3.4",
|
||||
"karma-browserify": "^8.1.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"mocha": "^9.0.3",
|
||||
"nyc": "^15.1.0",
|
||||
"parse-gitignore": "^1.0.1",
|
||||
"postman-collection": "^4.0.2",
|
||||
"postman-jsdoc-theme": "^0.0.3",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"shelljs": "^0.8.3",
|
||||
"watchify": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
}
|
293
node_modules/postman-url-encoder/parser/index.js
generated
vendored
Normal file
293
node_modules/postman-url-encoder/parser/index.js
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
/**
|
||||
* This module helps to break URL strings up into components
|
||||
* (protocol, auth, host, port, path, query, and hash) keeping the variables
|
||||
* intact.
|
||||
*
|
||||
* @example
|
||||
* const parser = require('postman-url-encoder/parser')
|
||||
*
|
||||
* // returns
|
||||
* // {
|
||||
* // raw: 'protocol://{{user}}:{{p@ssw?rd}}@{{host.name}}.com:{{#port}}/p/a/t/h?q=query#hash',
|
||||
* // protocol: 'protocol',
|
||||
* // auth: [ '{{user}}', '{{p@ssw?rd}}' ],
|
||||
* // host: [ '{{host.name}}', 'com' ],
|
||||
* // port: '{{#port}}',
|
||||
* // path: [ 'p', 'a', 't', 'h' ],
|
||||
* // query: [ 'q=query' ],
|
||||
* // hash: 'hash'
|
||||
* // }
|
||||
* parser.parse('protocol://{{user}}:{{p@ssw?rd}}@{{host.name}}.com:{{#port}}/p/a/t/h?q=query#hash')
|
||||
*
|
||||
* @module postman-url-encoder/parser
|
||||
*/
|
||||
|
||||
const ReplacementTracker = require('./replacement-tracker'),
|
||||
|
||||
REGEX_ALL_BACKSLASHES = /\\/g,
|
||||
REGEX_LEADING_SLASHES = /^\/+/,
|
||||
REGEX_ALL_VARIABLES = /{{[^{}]*[.:/?#@&\]][^{}]*}}/g,
|
||||
|
||||
HASH_SEPARATOR = '#',
|
||||
PATH_SEPARATOR = '/',
|
||||
PORT_SEPARATOR = ':',
|
||||
AUTH_SEPARATOR = '@',
|
||||
QUERY_SEPARATOR = '?',
|
||||
DOMAIN_SEPARATOR = '.',
|
||||
PROTOCOL_SEPARATOR = '://',
|
||||
AUTH_SEGMENTS_SEPARATOR = ':',
|
||||
QUERY_SEGMENTS_SEPARATOR = '&',
|
||||
|
||||
E = '',
|
||||
STRING = 'string',
|
||||
FILE_PROTOCOL = 'file',
|
||||
SAFE_REPLACE_CHAR = '_',
|
||||
CLOSING_SQUARE_BRACKET = ']',
|
||||
URL_PROPERTIES_ORDER = ['protocol', 'auth', 'host', 'port', 'path', 'query', 'hash'];
|
||||
|
||||
/**
|
||||
* Normalize the given string by replacing the variables which includes
|
||||
* reserved characters in its name.
|
||||
* The replaced characters are added to the given replacement tracker instance.
|
||||
*
|
||||
* @private
|
||||
* @param {String} str String to normalize
|
||||
* @param {ReplacementTracker} replacements ReplacementTracker instance
|
||||
* @returns {String} Normalized string
|
||||
*/
|
||||
function normalizeVariables (str, replacements) {
|
||||
let normalizedString = E,
|
||||
pointer = 0, // pointer till witch the string is normalized
|
||||
variable,
|
||||
match,
|
||||
index;
|
||||
|
||||
// find all the instances of {{<variable>}} which includes reserved chars
|
||||
// "Hello {{user#name}}!!!"
|
||||
// ↑ (pointer = 0)
|
||||
while ((match = REGEX_ALL_VARIABLES.exec(str)) !== null) {
|
||||
// {{user#name}}
|
||||
variable = match[0];
|
||||
|
||||
// starting index of the {{variable}} in the string
|
||||
// "Hello {{user#name}}!!!"
|
||||
// ↑ (index = 6)
|
||||
index = match.index;
|
||||
|
||||
// [pointer, index) string is normalized + the safe replacement character
|
||||
// "Hello " + "_"
|
||||
normalizedString += str.slice(pointer, index) + SAFE_REPLACE_CHAR;
|
||||
|
||||
// track the replacement done for the {{variable}}
|
||||
replacements.add(variable, index);
|
||||
|
||||
// update the pointer
|
||||
// "Hello {{user#name}}!!!"
|
||||
// ↑ (pointer = 19)
|
||||
pointer = index + variable.length;
|
||||
}
|
||||
|
||||
// avoid slicing the string in case of no matches
|
||||
if (pointer === 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// whatever left in the string is normalized as well
|
||||
/* istanbul ignore else */
|
||||
if (pointer < str.length) {
|
||||
// "Hello _" + "!!!"
|
||||
normalizedString += str.slice(pointer);
|
||||
}
|
||||
|
||||
return normalizedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update replaced characters in the URL object with its original value.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} url URL tracker object
|
||||
* @param {ReplacementTracker} replacements ReplacementTracker instance
|
||||
*/
|
||||
function applyReplacements (url, replacements) {
|
||||
let i,
|
||||
ii,
|
||||
prop;
|
||||
|
||||
// traverse each URL property in the given order
|
||||
for (i = 0, ii = URL_PROPERTIES_ORDER.length; i < ii; ++i) {
|
||||
prop = url[URL_PROPERTIES_ORDER[i]];
|
||||
|
||||
// bail out if the given property is not set (undefined or '')
|
||||
if (!(prop && prop.value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
prop.value = replacements.apply(prop.value, prop.beginIndex, prop.endIndex);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input string by decomposing the URL into constituent parts,
|
||||
* such as path, host, port, etc.
|
||||
*
|
||||
* @param {String} urlString The URL string to parse
|
||||
* @returns {Object} Parsed URL object
|
||||
*/
|
||||
function parse (urlString) {
|
||||
let url = {
|
||||
protocol: { value: undefined, beginIndex: 0, endIndex: 0 },
|
||||
auth: { value: undefined, beginIndex: 0, endIndex: 0 },
|
||||
host: { value: undefined, beginIndex: 0, endIndex: 0 },
|
||||
port: { value: undefined, beginIndex: 0, endIndex: 0 },
|
||||
path: { value: undefined, beginIndex: 0, endIndex: 0 },
|
||||
query: { value: undefined, beginIndex: 0, endIndex: 0 },
|
||||
hash: { value: undefined, beginIndex: 0, endIndex: 0 }
|
||||
},
|
||||
parsedUrl = {
|
||||
raw: urlString,
|
||||
protocol: undefined,
|
||||
auth: undefined,
|
||||
host: undefined,
|
||||
port: undefined,
|
||||
path: undefined,
|
||||
query: undefined,
|
||||
hash: undefined
|
||||
},
|
||||
replacements = new ReplacementTracker(),
|
||||
pointer = 0,
|
||||
_length,
|
||||
length,
|
||||
index,
|
||||
port;
|
||||
|
||||
// bail out if input string is empty
|
||||
if (!(urlString && typeof urlString === STRING)) {
|
||||
return parsedUrl;
|
||||
}
|
||||
|
||||
// trim leading whitespace characters
|
||||
parsedUrl.raw = urlString = urlString.trimLeft();
|
||||
|
||||
// normalize the given string
|
||||
urlString = normalizeVariables(urlString, replacements);
|
||||
length = urlString.length;
|
||||
|
||||
// 1. url.hash
|
||||
if ((index = urlString.indexOf(HASH_SEPARATOR)) !== -1) {
|
||||
// extract from the back
|
||||
url.hash.value = urlString.slice(index + 1);
|
||||
url.hash.beginIndex = pointer + index + 1;
|
||||
url.hash.endIndex = pointer + length;
|
||||
|
||||
urlString = urlString.slice(0, (length = index));
|
||||
}
|
||||
|
||||
// 2. url.query
|
||||
if ((index = urlString.indexOf(QUERY_SEPARATOR)) !== -1) {
|
||||
// extract from the back
|
||||
url.query.value = urlString.slice(index + 1).split(QUERY_SEGMENTS_SEPARATOR);
|
||||
url.query.beginIndex = pointer + index + 1;
|
||||
url.query.endIndex = pointer + length;
|
||||
|
||||
urlString = urlString.slice(0, (length = index));
|
||||
}
|
||||
|
||||
// 3. url.protocol
|
||||
urlString = urlString.replace(REGEX_ALL_BACKSLASHES, PATH_SEPARATOR); // sanitize slashes
|
||||
|
||||
// @todo support `protocol:host/path` and `protocol:/host/path`
|
||||
if ((index = urlString.indexOf(PROTOCOL_SEPARATOR)) !== -1) {
|
||||
// extract from the front
|
||||
url.protocol.value = urlString.slice(0, index);
|
||||
url.protocol.beginIndex = pointer;
|
||||
url.protocol.endIndex = pointer + index;
|
||||
|
||||
urlString = urlString.slice(index + 3);
|
||||
length -= index + 3;
|
||||
pointer += index + 3;
|
||||
|
||||
// special handling for extra slashes in protocol e.g, http:///example.com
|
||||
_length = length; // length with leading slashes
|
||||
|
||||
urlString = urlString.replace(REGEX_LEADING_SLASHES,
|
||||
(url.protocol.value.toLowerCase() === FILE_PROTOCOL) ?
|
||||
// file:////path -> file:///path
|
||||
PATH_SEPARATOR :
|
||||
// protocol:////host/path -> protocol://host/path
|
||||
E);
|
||||
|
||||
length = urlString.length; // length without slashes
|
||||
pointer += _length - length; // update pointer
|
||||
}
|
||||
|
||||
// 4. url.path
|
||||
if ((index = urlString.indexOf(PATH_SEPARATOR)) !== -1) {
|
||||
// extract from the back
|
||||
url.path.value = urlString.slice(index + 1).split(PATH_SEPARATOR);
|
||||
url.path.beginIndex = pointer + index + 1;
|
||||
url.path.endIndex = pointer + length;
|
||||
|
||||
urlString = urlString.slice(0, (length = index));
|
||||
}
|
||||
|
||||
// 5. url.auth
|
||||
if ((index = urlString.lastIndexOf(AUTH_SEPARATOR)) !== -1) {
|
||||
// extract from the front
|
||||
url.auth.value = urlString.slice(0, index);
|
||||
url.auth.beginIndex = pointer;
|
||||
url.auth.endIndex = pointer + index;
|
||||
|
||||
urlString = urlString.slice(index + 1);
|
||||
length -= index + 1;
|
||||
pointer += index + 1;
|
||||
|
||||
// separate username:password
|
||||
if ((index = url.auth.value.indexOf(AUTH_SEGMENTS_SEPARATOR)) !== -1) {
|
||||
url.auth.value = [url.auth.value.slice(0, index), url.auth.value.slice(index + 1)];
|
||||
}
|
||||
else {
|
||||
url.auth.value = [url.auth.value];
|
||||
}
|
||||
}
|
||||
|
||||
// 6. url.port
|
||||
if ((index = urlString.lastIndexOf(PORT_SEPARATOR)) !== -1 &&
|
||||
// eslint-disable-next-line lodash/prefer-includes
|
||||
(port = urlString.slice(index + 1)).indexOf(CLOSING_SQUARE_BRACKET) === -1
|
||||
) {
|
||||
// extract from the back
|
||||
url.port.value = port;
|
||||
url.port.beginIndex = pointer + index + 1;
|
||||
url.port.endIndex = pointer + length;
|
||||
|
||||
urlString = urlString.slice(0, (length = index));
|
||||
}
|
||||
|
||||
// 7. url.host
|
||||
if (urlString) {
|
||||
url.host.value = urlString.split(DOMAIN_SEPARATOR);
|
||||
url.host.beginIndex = pointer;
|
||||
url.host.endIndex = pointer + length;
|
||||
}
|
||||
|
||||
// apply replacements back, if any
|
||||
replacements.count() && applyReplacements(url, replacements);
|
||||
|
||||
// finally, prepare parsed url
|
||||
parsedUrl.protocol = url.protocol.value;
|
||||
parsedUrl.auth = url.auth.value;
|
||||
parsedUrl.host = url.host.value;
|
||||
parsedUrl.port = url.port.value;
|
||||
parsedUrl.path = url.path.value;
|
||||
parsedUrl.query = url.query.value;
|
||||
parsedUrl.hash = url.hash.value;
|
||||
|
||||
return parsedUrl;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parse
|
||||
};
|
151
node_modules/postman-url-encoder/parser/replacement-tracker.js
generated
vendored
Normal file
151
node_modules/postman-url-encoder/parser/replacement-tracker.js
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
|
||||
/**
|
||||
* Tracks replacements done on a string and expose utility to patch replacements.
|
||||
*
|
||||
* @note due to performance reasons, it doesn't store the original string or
|
||||
* perform ops on the string.
|
||||
*
|
||||
* @private
|
||||
* @constructor
|
||||
*/
|
||||
class ReplacementTracker {
|
||||
constructor () {
|
||||
this.replacements = [];
|
||||
this._offset = 0;
|
||||
this._length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new replacement to track.
|
||||
*
|
||||
* @param {String} value -
|
||||
* @param {Number} index -
|
||||
*/
|
||||
add (value, index) {
|
||||
this.replacements.push({
|
||||
value: value,
|
||||
index: index - this._offset
|
||||
});
|
||||
|
||||
this._offset += value.length - 1; // - 1 replaced character
|
||||
this._length++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of replacements.
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
count () {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the lower index of replacement position for a given value using inexact
|
||||
* binary search.
|
||||
*
|
||||
* @private
|
||||
* @param {Number} index -
|
||||
* @returns {Number}
|
||||
*/
|
||||
_findLowerIndex (index) {
|
||||
let length = this.count(),
|
||||
start = 0,
|
||||
end = length - 1,
|
||||
mid;
|
||||
|
||||
while (start <= end) {
|
||||
mid = (start + end) >> 1; // divide by 2
|
||||
|
||||
if (this.replacements[mid].index >= index) {
|
||||
end = mid - 1;
|
||||
}
|
||||
else {
|
||||
start = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return start >= length ? -1 : start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches a given string by apply all the applicable replacements done in the
|
||||
* given range.
|
||||
*
|
||||
* @private
|
||||
* @param {String} input -
|
||||
* @param {Number} beginIndex -
|
||||
* @param {Number} endIndex -
|
||||
* @returns {String}
|
||||
*/
|
||||
_applyInString (input, beginIndex, endIndex) {
|
||||
let index,
|
||||
replacement,
|
||||
replacementIndex,
|
||||
replacementValue,
|
||||
offset = 0,
|
||||
length = this.count();
|
||||
|
||||
// bail out if no replacements are done in the given range OR empty string
|
||||
if (!input || (index = this._findLowerIndex(beginIndex)) === -1) {
|
||||
return input;
|
||||
}
|
||||
|
||||
do {
|
||||
replacement = this.replacements[index];
|
||||
replacementIndex = replacement.index;
|
||||
replacementValue = replacement.value;
|
||||
|
||||
// bail out if all the replacements are done in the given range
|
||||
if (replacementIndex >= endIndex) {
|
||||
break;
|
||||
}
|
||||
|
||||
replacementIndex = offset + replacementIndex - beginIndex;
|
||||
input = input.slice(0, replacementIndex) + replacementValue + input.slice(replacementIndex + 1);
|
||||
offset += replacementValue.length - 1;
|
||||
} while (++index < length);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches a given string or array of strings by apply all the applicable
|
||||
* replacements done in the given range.
|
||||
*
|
||||
* @param {String|String[]} input -
|
||||
* @param {Number} beginIndex -
|
||||
* @param {Number} endIndex -
|
||||
* @returns {String|String[]}
|
||||
*/
|
||||
apply (input, beginIndex, endIndex) {
|
||||
let i,
|
||||
ii,
|
||||
length,
|
||||
_endIndex,
|
||||
_beginIndex,
|
||||
value = input;
|
||||
|
||||
// apply replacements in string
|
||||
if (typeof input === 'string') {
|
||||
return this._applyInString(input, beginIndex, endIndex);
|
||||
}
|
||||
|
||||
// apply replacements in the splitted string (Array)
|
||||
_beginIndex = beginIndex;
|
||||
|
||||
// traverse all the segments until all the replacements are patched
|
||||
for (i = 0, ii = input.length; i < ii; ++i) {
|
||||
value = input[i];
|
||||
_endIndex = _beginIndex + (length = value.length);
|
||||
|
||||
// apply replacements applicable for individual segment
|
||||
input[i] = this._applyInString(value, _beginIndex, _endIndex);
|
||||
_beginIndex += length + 1; // + 1 separator
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReplacementTracker;
|
Reference in New Issue
Block a user