project files added
This commit is contained in:
+200
@@ -0,0 +1,200 @@
|
||||
# Changelog
|
||||
|
||||
## 2.5.2
|
||||
|
||||
### Publishing changes
|
||||
|
||||
* [#262](https://github.com/lelylan/simple-oauth2/pull/262) Use files package option instead of .npmignore
|
||||
|
||||
### Documentation improvements
|
||||
|
||||
* [#267](https://github.com/lelylan/simple-oauth2/pull/267) Better document encoding of values for the token exchange process
|
||||
|
||||
## 2.5.1
|
||||
### New examples
|
||||
|
||||
* [#249](https://github.com/lelylan/simple-oauth2/pull/249) Add dropbox usage example
|
||||
|
||||
### Development dependencies upgradess
|
||||
|
||||
* [#254](https://github.com/lelylan/simple-oauth2/pull/254) Upgrade codebase to eslint v6 and airbnb-base-eslint configuration v14
|
||||
* [#253](https://github.com/lelylan/simple-oauth2/pull/253) Upgrade nock to v11
|
||||
* [#252](https://github.com/lelylan/simple-oauth2/pull/252) Use ava instead of mocha as test runner
|
||||
* [#252](https://github.com/lelylan/simple-oauth2/pull/252) Use ava built-in assertions library instead of chai
|
||||
|
||||
## 2.5.0
|
||||
### Dependencies upgrades
|
||||
|
||||
* [#250](https://github.com/lelylan/simple-oauth2/pull/250) Upgrade date-fns library to v2
|
||||
|
||||
## 2.4.0
|
||||
### Dependencies upgrades
|
||||
|
||||
* [#235](https://github.com/lelylan/simple-oauth2/pull/235) Upgrade Joi version to v15 (@hapi/joi)
|
||||
|
||||
### Development dependencies updates
|
||||
|
||||
* [#244](https://github.com/lelylan/simple-oauth2/pull/244) Upgrade mocha test runner to v6
|
||||
* [#244](https://github.com/lelylan/simple-oauth2/pull/244) Upgrade nyc to v14
|
||||
* [#244](https://github.com/lelylan/simple-oauth2/pull/244) Upgrade development dependencies to latest version available
|
||||
|
||||
## 2.3.0
|
||||
### Dependencies upgrades
|
||||
|
||||
* [#242](https://github.com/lelylan/simple-oauth2/pull/242) Upgrade debug dependency to v4
|
||||
|
||||
## 2.2.1
|
||||
|
||||
* Fix Joi schema missusage
|
||||
|
||||
## 2.2.0
|
||||
|
||||
* Fix access token expiration properties omission
|
||||
|
||||
## 2.1.0 (Not published)
|
||||
|
||||
* Ignore access token date properties when not available
|
||||
|
||||
## v2.0.1
|
||||
|
||||
* Add support to revoke accessToken and refreshToken in a single call with `revokeAll`
|
||||
|
||||
## v2.0.0
|
||||
|
||||
* Replace internal request library to wreck
|
||||
* Replace bluebird with native promise implementation
|
||||
* Replace callback interface with async/await
|
||||
* Improve integration tests coverage
|
||||
|
||||
## v1.5.1
|
||||
|
||||
* Add support to specify scopes as array in `getToken` method
|
||||
* Add support to empty strings and visual ASCII characters on `clientId`/`clientSecret` options
|
||||
|
||||
## v1.5.0
|
||||
|
||||
* Update debug dependency
|
||||
* Add support to encode the authorization headers
|
||||
|
||||
## v1.4.0
|
||||
|
||||
* Update dependencies
|
||||
* Add Node 8 to test matrix
|
||||
|
||||
## v1.3.0
|
||||
|
||||
* Add support for custom idParamName in authCode.authorizeURL() method
|
||||
|
||||
## v1.2.0
|
||||
|
||||
* Upgrade dependencies, to avoid using outdated/vulnerable versions
|
||||
|
||||
## v1.1.0
|
||||
|
||||
* Add support to body encoding format in library requests
|
||||
|
||||
## v1.0.3
|
||||
|
||||
* Add missing documentation for module options
|
||||
|
||||
## v1.0.2
|
||||
|
||||
* Parse token payload response `expires_in` property as integer
|
||||
|
||||
## v1.0.1
|
||||
|
||||
* Fixed documentation for **client** option.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
* Refactored test to use fixtures.
|
||||
* Update code to comply with more linter rules.
|
||||
* Fixed examples in README to return on error.
|
||||
* Added a working example example.
|
||||
* Clone params and configuration passed
|
||||
* Changed public api to, to make it consistent. Changed shortcut names to full names.
|
||||
* Changed public api to allow different sites for /authorize and /tokens
|
||||
* Added strict schema validation to module options.
|
||||
* Does not override expires_at property if passed into accessToken.create.
|
||||
|
||||
## v0.8.0 (1 August 2016)
|
||||
|
||||
* Upgraded code to strict mode.
|
||||
* Upgraded all the code base to es6.
|
||||
* Updated linter settings to meet the new es6 code requirements.
|
||||
* Fixed shared value for access token, causing tokens to be overriding.
|
||||
|
||||
## v0.7.0 (22 April 2016)
|
||||
|
||||
* Replaced internal logger by the debug module logger.
|
||||
* Fixed some project metadata.
|
||||
|
||||
## v0.6.0 (04 April 2016)
|
||||
|
||||
* Added optional sending support to the body auth params.
|
||||
* Updated license information.
|
||||
* Updated main dependencies version.
|
||||
* Fixed leaked token when a refresh token was used.
|
||||
|
||||
## v0.5.1 (25 January 2016)
|
||||
|
||||
* Fixed error class prototype inheritance. Now inherits correctly from Error.
|
||||
|
||||
## v0.5.0 (22 January 2016)
|
||||
|
||||
* Now all error states returned from the server, are rejected as HTTPError instances. (This allow to know what httpStatusCode was returned)
|
||||
|
||||
## v0.4.0 (18 January 2016)
|
||||
|
||||
* Updated project dependencies.
|
||||
* Added support for passing arguments to the refresh token action.
|
||||
* Added project badges.
|
||||
* Code general cleanup and applied code styleguide.
|
||||
* Created CONTRIBUTING guides! (Separated from README)
|
||||
* Fixed bug, which resolved promises even if the token wasn´t retrieved. #64
|
||||
|
||||
## v0.3.0 (29 November 2015)
|
||||
|
||||
* Better documentation!
|
||||
* Added support for promise based API
|
||||
|
||||
## v0.2.1 (17 October 2014)
|
||||
|
||||
* Adds revocation token method
|
||||
* Not using headers if access_token is defined as a part of the URL.
|
||||
* Changes from Pascal Case to Camel Case in the API.
|
||||
* Adds Bearer Header for requests.
|
||||
|
||||
## v0.1.7 (16 May 2013)
|
||||
|
||||
* Now compatible with Github
|
||||
|
||||
## v0.1.6 (24 Jan 2013)
|
||||
|
||||
* Updated name convention on using simple oauth2 configuration block.
|
||||
|
||||
## v0.1.5 (24 Jan 2013)
|
||||
|
||||
* Token expiration is now dinamically defined through the expires_in
|
||||
field returned with the access token from the OAuth2 server
|
||||
|
||||
## v0.1.4 (22 Jan 2013)
|
||||
|
||||
* Fixed missing Basic Auth that somehow is not created from the request library
|
||||
|
||||
## v0.1.3 (22 Jan 2013)
|
||||
|
||||
* Fixed bug on AccessToken#expired() as it had the inverse logic
|
||||
* AccessToken#refresh() now returns an AccessToken object
|
||||
|
||||
## v0.1.2 (22 Jan 2013)
|
||||
|
||||
* Updated documentation
|
||||
|
||||
## v0.1.1 (21 Jan 2013)
|
||||
|
||||
* Added Password credentials flow
|
||||
|
||||
## v0.1.0 (21 Jan 2013)
|
||||
|
||||
* First version Node client for OAuth2
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
# Simple OAuth2
|
||||
|
||||
[](https://www.npmjs.com/package/simple-oauth2)
|
||||
[](https://travis-ci.org/lelylan/simple-oauth2)
|
||||
[](https://david-dm.org/lelylan/simple-oauth2)
|
||||
|
||||
Node.js client library for [OAuth2](http://oauth.net/2/).
|
||||
|
||||
OAuth2 lets users grant the access to the desired resources to third party applications,
|
||||
giving them the possibility to enable and disable those accesses whenever they want.
|
||||
|
||||
Simple OAuth2 supports the following flows.
|
||||
|
||||
* [Authorization Code Flow](http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.1) (for apps with servers that can store persistent information).
|
||||
* [Password Credentials](http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.3) (when previous flow can't be used or during development).
|
||||
* [Client Credentials Flow](http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.4) (the client can request an access token using only its client credentials)
|
||||
|
||||
#### Thanks to Open Source
|
||||
|
||||
Simple OAuth 2.0 come to life thanks to the work I've made in Lelylan, an open source microservices architecture for the Internet of Things. If this project helped you in any way, think about giving us a <a href="https://github.com/lelylan/lelylan">star on Github</a>.
|
||||
|
||||
<a href="https://github.com/lelylan/lelylan">
|
||||
<img src="https://raw.githubusercontent.com/lelylan/lelylan/master/public/logo-lelylan.png" data-canonical-src="https://raw.githubusercontent.com/lelylan/lelylan/master/public/logo-lelylan.png" width="300"/></a>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
|
||||
- [Simple OAuth2](#simple-oauth2)
|
||||
- [Thanks to Open Source](#thanks-to-open-source)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Requirements](#requirements)
|
||||
- [Getting started](#getting-started)
|
||||
- [Installation](#installation)
|
||||
- [Options](#options)
|
||||
- [Example of Usage](#example-of-usage)
|
||||
- [OAuth2 Supported flows](#oauth2-supported-flows)
|
||||
- [Authorization Code flow](#authorization-code-flow)
|
||||
- [Password Credentials Flow](#password-credentials-flow)
|
||||
- [Client Credentials Flow](#client-credentials-flow)
|
||||
- [Helpers](#helpers)
|
||||
- [Access Token object](#access-token-object)
|
||||
- [Errors](#errors)
|
||||
- [Contributing](#contributing)
|
||||
- [Authors](#authors)
|
||||
- [Contributors](#contributors)
|
||||
- [Changelog](#changelog)
|
||||
- [License](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## Requirements
|
||||
|
||||
The node client library is tested against the latest Node 8 LTS and newer versions.
|
||||
|
||||
To use in node 4, 5 or 6, please use [simple-oauth2@1.x](https://github.com/lelylan/simple-oauth2/tree/1.5.0). Older node versions are unsupported.
|
||||
|
||||
## Getting started
|
||||
|
||||
### Installation
|
||||
|
||||
Install the client library using [npm](http://npmjs.org/):
|
||||
|
||||
```bash
|
||||
npm install --save simple-oauth2
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
Simple OAuth2 accepts an object with the following valid params.
|
||||
|
||||
* `client` - required object with the following properties:
|
||||
* `id` - Service registered client id. When required by the [spec](https://tools.ietf.org/html/rfc6749#appendix-B) this value will be automatically encoded. Required.
|
||||
* `secret` - Service registered client secret. When required by the [spec](https://tools.ietf.org/html/rfc6749#appendix-B) this value will be automatically encoded. Required.
|
||||
* `secretParamName` - Parameter name used to send the client secret. Default to **client_secret**.
|
||||
* `idParamName` - Parameter name used to send the client id. Default to **client_id**.
|
||||
|
||||
* `auth` - required object with the following properties.
|
||||
* `tokenHost` - String used to set the host to request the tokens to. Required.
|
||||
* `tokenPath` - String path to request an access token. Default to **/oauth/token**.
|
||||
* `revokePath` - String path to revoke an access token. Default to **/oauth/revoke**.
|
||||
* `authorizeHost` - String used to set the host to request an "authorization code". Default to the value set on `auth.tokenHost`.
|
||||
* `authorizePath` - String path to request an authorization code. Default to **/oauth/authorize**.
|
||||
|
||||
* `http` optional object used to set global options to the internal http library ([wreck](https://github.com/hapijs/wreck)).
|
||||
* All options except **baseUrl** are allowed. `headers.authorization` will always be overriden by the library to properly send the required credentials on each scenario. Default to `headers.Accept = application/json`.
|
||||
|
||||
* `options` optional object to setup the module.
|
||||
* `bodyFormat` - Format of data sent in the request body. Valid options are `form` or `json`. Defaults to **form**.
|
||||
* `authorizationMethod` - Indicates the method used to send the client.id/client.secret authorization params at the token request. Valid options are `header` or `body`. If set to **body**, the **bodyFormat** option will be used to format the credentials. Defaults to **header**.
|
||||
|
||||
```javascript
|
||||
// Set the configuration settings
|
||||
const credentials = {
|
||||
client: {
|
||||
id: '<client-id>',
|
||||
secret: '<client-secret>'
|
||||
},
|
||||
auth: {
|
||||
tokenHost: 'https://api.oauth.com'
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize the OAuth2 Library
|
||||
const oauth2 = require('simple-oauth2').create(credentials);
|
||||
```
|
||||
|
||||
### Example of Usage
|
||||
|
||||
See the [example folder](./example).
|
||||
|
||||
## OAuth2 Supported flows
|
||||
|
||||
### Authorization Code flow
|
||||
|
||||
The Authorization Code flow is made up from two parts. At first your application asks to
|
||||
the user the permission to access their data. If the user approves the OAuth2 server sends
|
||||
to the client an authorization code. In the second part, the client POST the authorization code
|
||||
along with its client secret to the oauth server in order to get the access token.
|
||||
|
||||
```javascript
|
||||
const oauth2 = require('simple-oauth2').create(credentials);
|
||||
|
||||
// Authorization oauth2 URI
|
||||
const authorizationUri = oauth2.authorizationCode.authorizeURL({
|
||||
redirect_uri: 'http://localhost:3000/callback',
|
||||
scope: '<scope>', // also can be an array of multiple scopes, ex. ['<scope1>, '<scope2>', '...']
|
||||
state: '<state>'
|
||||
});
|
||||
|
||||
// Redirect example using Express (see http://expressjs.com/api.html#res.redirect)
|
||||
res.redirect(authorizationUri);
|
||||
|
||||
// Get the access token object (the authorization code is given from the previous step).
|
||||
const tokenConfig = {
|
||||
code: '<code>',
|
||||
redirect_uri: 'http://localhost:3000/callback',
|
||||
scope: '<scope>', // also can be an array of multiple scopes, ex. ['<scope1>, '<scope2>', '...']
|
||||
};
|
||||
|
||||
// Save the access token
|
||||
try {
|
||||
const result = await oauth2.authorizationCode.getToken(tokenConfig)
|
||||
const accessToken = oauth2.accessToken.create(result);
|
||||
} catch (error) {
|
||||
console.log('Access Token Error', error.message);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Password Credentials Flow
|
||||
|
||||
This flow is suitable when the resource owner has a trust relationship with the
|
||||
client, such as its computer operating system or a highly privileged application.
|
||||
Use this flow only when other flows are not viable or when you need a fast way to
|
||||
test your application.
|
||||
|
||||
```javascript
|
||||
const oauth2 = require('simple-oauth2').create(credentials);
|
||||
|
||||
// Get the access token object.
|
||||
const tokenConfig = {
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
scope: '<scope>', // also can be an array of multiple scopes, ex. ['<scope1>, '<scope2>', '...']
|
||||
};
|
||||
|
||||
// Save the access token
|
||||
try {
|
||||
const result = await oauth2.ownerPassword.getToken(tokenConfig);
|
||||
const accessToken = oauth2.accessToken.create(result);
|
||||
} catch (error) {
|
||||
console.log('Access Token Error', error.message);
|
||||
}
|
||||
```
|
||||
|
||||
### Client Credentials Flow
|
||||
|
||||
This flow is suitable when client is requesting access to the protected resources under its control.
|
||||
|
||||
```javascript
|
||||
const oauth2 = require('simple-oauth2').create(credentials);
|
||||
const tokenConfig = {
|
||||
scope: '<scope>', // also can be an array of multiple scopes, ex. ['<scope1>, '<scope2>', '...']
|
||||
};
|
||||
|
||||
// Get the access token object for the client
|
||||
try {
|
||||
const result = await oauth2.clientCredentials.getToken(tokenConfig);
|
||||
const accessToken = oauth2.accessToken.create(result);
|
||||
} catch (error) {
|
||||
console.log('Access Token error', error.message);
|
||||
}
|
||||
```
|
||||
|
||||
## Helpers
|
||||
|
||||
### Access Token object
|
||||
|
||||
When a token expires we need to refresh it. Simple OAuth2 offers the
|
||||
AccessToken class that add a couple of useful methods to refresh the
|
||||
access token when it is expired.
|
||||
|
||||
```javascript
|
||||
// Sample of a JSON access token (you got it through previous steps)
|
||||
const tokenObject = {
|
||||
'access_token': '<access-token>',
|
||||
'refresh_token': '<refresh-token>',
|
||||
'expires_in': '7200'
|
||||
};
|
||||
|
||||
// Create the access token wrapper
|
||||
let accessToken = oauth2.accessToken.create(tokenObject);
|
||||
|
||||
// Check if the token is expired. If expired it is refreshed.
|
||||
if (accessToken.expired()) {
|
||||
try {
|
||||
accessToken = await accessToken.refresh();
|
||||
} catch (error) {
|
||||
console.log('Error refreshing access token: ', error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `expired` helper is useful for knowing when a token has definitively
|
||||
expired. However, there is a common race condition when tokens are near
|
||||
expiring. If an OAuth 2.0 token is issued with a `expires_in` property (as
|
||||
opposed to an `expires_at` property), there can be discrepancies between the
|
||||
time the OAuth 2.0 server issues the access token and when it is received.
|
||||
These come down to factors such as network and processing latency. This can be
|
||||
worked around by preemptively refreshing the access token:
|
||||
|
||||
```javascript
|
||||
// Provide a window of time before the actual expiration to refresh the token
|
||||
const EXPIRATION_WINDOW_IN_SECONDS = 300;
|
||||
|
||||
const { token } = accessToken;
|
||||
const expirationTimeInSeconds = token.expires_at.getTime() / 1000;
|
||||
const expirationWindowStart = expirationTimeInSeconds - EXPIRATION_WINDOW_IN_SECONDS;
|
||||
|
||||
// If the start of the window has passed, refresh the token
|
||||
const nowInSeconds = (new Date()).getTime() / 1000;
|
||||
const shouldRefresh = nowInSeconds >= expirationWindowStart;
|
||||
if (shouldRefresh) {
|
||||
try {
|
||||
accessToken = await accessToken.refresh();
|
||||
} catch (error) {
|
||||
console.log('Error refreshing access token: ', error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When you've done with the token or you want to log out, you can
|
||||
revoke the access token and refresh token.
|
||||
|
||||
```javascript
|
||||
// Revoke both access and refresh tokens
|
||||
try {
|
||||
// Revoke only the access token
|
||||
await accessToken.revoke('access_token');
|
||||
|
||||
// Session ended. But the refresh_token is still valid.
|
||||
// Revoke the refresh token
|
||||
await accessToken.revoke('refresh_token');
|
||||
} catch (error) {
|
||||
console.log('Error revoking token: ', error.message);
|
||||
}
|
||||
```
|
||||
|
||||
As a convenience method, you can also revoke both tokens in a single call:
|
||||
|
||||
```javascript
|
||||
// Revoke both access and refresh tokens
|
||||
try {
|
||||
// Revokes both tokens, refresh token is only revoked if the access_token is properly revoked
|
||||
await accessToken.revokeAll();
|
||||
} catch (error) {
|
||||
console.log('Error revoking token: ', error.message);
|
||||
}
|
||||
```
|
||||
|
||||
### Errors
|
||||
|
||||
Errors are returned when a 4xx or 5xx status code is received.
|
||||
|
||||
BoomError
|
||||
|
||||
As a standard [boom](https://github.com/hapijs/boom) error you can access any of the boom error properties. The total amount of information varies according to the generated status code.
|
||||
|
||||
```javascript
|
||||
|
||||
try {
|
||||
await oauth2.authorizationCode.getToken();
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
// => {
|
||||
// "statusCode": 401,
|
||||
// "error": "Unauthorized",
|
||||
// "message": "invalid password"
|
||||
// }
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING](https://github.com/lelylan/simple-oauth2/blob/master/CONTRIBUTING.md)
|
||||
|
||||
## Authors
|
||||
|
||||
[Andrea Reginato](http://twitter.com/lelylan)
|
||||
|
||||
### Contributors
|
||||
|
||||
Special thanks to the following people for submitting patches.
|
||||
|
||||
* [Jonathan Samines](http://twitter.com/jonathansamines)
|
||||
|
||||
## Changelog
|
||||
|
||||
See [CHANGELOG](https://github.com/lelylan/simple-oauth2/blob/master/CHANGELOG.md)
|
||||
|
||||
## License
|
||||
|
||||
Simple OAuth 2.0 is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const authCodeModule = require('./lib/client/auth-code');
|
||||
const passwordModule = require('./lib/client/password');
|
||||
const accessTokenModule = require('./lib/access-token');
|
||||
const clientCredentialsModule = require('./lib/client/client');
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-oauth-v2-31#appendix-A.1
|
||||
const vsCharRegEx = /^[\x20-\x7E]*$/;
|
||||
|
||||
const optionsSchema = Joi
|
||||
.object()
|
||||
.keys({
|
||||
client: Joi.object().keys({
|
||||
id: Joi.string().regex(vsCharRegEx).allow(''),
|
||||
secret: Joi.string().regex(vsCharRegEx).allow(''),
|
||||
secretParamName: Joi.string().default('client_secret'),
|
||||
idParamName: Joi.string().default('client_id'),
|
||||
}).required(),
|
||||
auth: Joi.object().keys({
|
||||
tokenHost: Joi.string().required().uri({ scheme: ['http', 'https'] }),
|
||||
tokenPath: Joi.string().default('/oauth/token'),
|
||||
revokePath: Joi.string().default('/oauth/revoke'),
|
||||
authorizeHost: Joi.string().default(Joi.ref('tokenHost')),
|
||||
authorizePath: Joi.string().default('/oauth/authorize'),
|
||||
}).required(),
|
||||
http: Joi.object().unknown(true),
|
||||
options: Joi.object().keys({
|
||||
bodyFormat: Joi.any().only('form', 'json').default('form'),
|
||||
authorizationMethod: Joi.any().only('header', 'body').default('header'),
|
||||
}).default(),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Creates a new simple-oauth2 client with the provided configuration
|
||||
* @param {Object} opts Module options as defined in schema
|
||||
* @returns {Object} The simple-oauth2 client
|
||||
*/
|
||||
create(opts = {}) {
|
||||
const options = Joi.attempt(opts, optionsSchema, 'Invalid options provided to simple-oauth2');
|
||||
|
||||
return {
|
||||
accessToken: accessTokenModule(options),
|
||||
ownerPassword: passwordModule(options),
|
||||
authorizationCode: authCodeModule(options),
|
||||
clientCredentials: clientCredentialsModule(options),
|
||||
};
|
||||
},
|
||||
};
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
|
||||
const isAfter = require('date-fns/isAfter');
|
||||
const parseToken = require('./parse-token');
|
||||
const coreModule = require('./../core');
|
||||
|
||||
/**
|
||||
* Wrapper for the Access Token Object
|
||||
*/
|
||||
module.exports = (config) => {
|
||||
const core = coreModule(config);
|
||||
|
||||
class AccessToken {
|
||||
constructor(token) {
|
||||
this.token = parseToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the access token is expired or not
|
||||
*/
|
||||
expired() {
|
||||
return isAfter(new Date(), this.token.expires_at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the access token
|
||||
* @param {Object} params An optional argument for additional API request params.
|
||||
*/
|
||||
async refresh(params) {
|
||||
const options = Object.assign({}, params, {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: this.token.refresh_token,
|
||||
});
|
||||
|
||||
const response = await core.request(config.auth.tokenPath, options);
|
||||
|
||||
return new AccessToken(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke access or refresh token
|
||||
* @param {String} tokenType A string containing the type of token to revoke.
|
||||
* Should be either "access_token" or "refresh_token"
|
||||
*/
|
||||
async revoke(tokenType) {
|
||||
const token = tokenType === 'access_token' ? this.token.access_token : this.token.refresh_token;
|
||||
const options = {
|
||||
token,
|
||||
token_type_hint: tokenType,
|
||||
};
|
||||
|
||||
return core.request(config.auth.revokePath, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke both the existing access and refresh tokens
|
||||
*/
|
||||
async revokeAll() {
|
||||
await this.revoke('access_token');
|
||||
await this.revoke('refresh_token');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an OAuth2.AccessToken instance
|
||||
* @param {Object} token An object containing the token object returned from the OAuth2 server.
|
||||
* @returns {AccessToken}
|
||||
*/
|
||||
function createAccessToken(token) {
|
||||
return new AccessToken(token);
|
||||
}
|
||||
|
||||
return {
|
||||
create: createAccessToken,
|
||||
};
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const debug = require('debug')('access-token');
|
||||
const isDate = require('date-fns/isDate');
|
||||
const parseISO = require('date-fns/parseISO');
|
||||
const addSeconds = require('date-fns/addSeconds');
|
||||
|
||||
const parseTokenDateProperties = (token) => {
|
||||
const parsedTokenProps = {};
|
||||
|
||||
if ('expires_at' in token) {
|
||||
if (!isDate(token.expires_at)) {
|
||||
parsedTokenProps.expires_at = parseISO(token.expires_at);
|
||||
}
|
||||
} else if ('expires_in' in token) {
|
||||
parsedTokenProps.expires_at = addSeconds(
|
||||
new Date(),
|
||||
Number.parseInt(token.expires_in, 10)
|
||||
);
|
||||
} else {
|
||||
debug('No token expiration property was found. Ignoring date parsing');
|
||||
}
|
||||
|
||||
return Object.assign({}, token, parsedTokenProps);
|
||||
};
|
||||
|
||||
module.exports = parseTokenDateProperties;
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
const url = require('url');
|
||||
const qs = require('querystring');
|
||||
const coreModule = require('./../core');
|
||||
|
||||
/**
|
||||
* Authorization Code flow implementation
|
||||
*/
|
||||
module.exports = (config) => {
|
||||
const core = coreModule(config);
|
||||
const authorizeUrl = url.resolve(config.auth.authorizeHost, config.auth.authorizePath);
|
||||
|
||||
/**
|
||||
* Redirect the user to the autorization page
|
||||
* @param {String} params.redirectURI A string that represents the registered application URI
|
||||
* where the user is redirected after authentication
|
||||
* @param {String|Array<String>} params.scope A String or array of strings
|
||||
* that represents the application privileges
|
||||
* @param {String} params.state A String that represents an option opaque value used by the client
|
||||
* to main the state between the request and the callback
|
||||
* @return {String} the absolute authorization url
|
||||
*/
|
||||
function authorizeURL(params = {}) {
|
||||
const baseParams = {
|
||||
response_type: 'code',
|
||||
[config.client.idParamName]: config.client.id,
|
||||
};
|
||||
|
||||
if (Array.isArray(params.scope)) {
|
||||
const scope = params.scope.join(',');
|
||||
|
||||
Object.assign(params, { scope });
|
||||
}
|
||||
|
||||
const options = Object.assign({}, baseParams, params);
|
||||
|
||||
return `${authorizeUrl}?${qs.stringify(options)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Access Token Object
|
||||
* @param {String} params.code Authorization code (from previous step)
|
||||
* @param {String} params.redirecURI A string that represents the callback uri
|
||||
* @return {Promise}
|
||||
*/
|
||||
async function getToken(params) {
|
||||
const options = Object.assign({}, params, {
|
||||
grant_type: 'authorization_code',
|
||||
});
|
||||
|
||||
return core.request(config.auth.tokenPath, options);
|
||||
}
|
||||
|
||||
return {
|
||||
authorizeURL,
|
||||
getToken,
|
||||
};
|
||||
};
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
const coreModule = require('./../core');
|
||||
|
||||
/**
|
||||
* Clients credentials flow implementation
|
||||
*/
|
||||
module.exports = (config) => {
|
||||
const core = coreModule(config);
|
||||
|
||||
/**
|
||||
* Returns the Access Token Object
|
||||
* @param {Object} params
|
||||
* @param {String} params.scope A string that represents the application privileges
|
||||
* @return {Promise}
|
||||
*/
|
||||
async function getToken(params) {
|
||||
const options = Object.assign({}, params, {
|
||||
grant_type: 'client_credentials',
|
||||
});
|
||||
|
||||
return core.request(config.auth.tokenPath, options);
|
||||
}
|
||||
|
||||
return {
|
||||
getToken,
|
||||
};
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const coreModule = require('./../core');
|
||||
|
||||
/**
|
||||
* User Password flow implementation
|
||||
*/
|
||||
module.exports = (config) => {
|
||||
const core = coreModule(config);
|
||||
|
||||
/**
|
||||
* Returns the Access Token Object
|
||||
* @param {Object} params
|
||||
* @param {String} params.username A string that represents the registered username
|
||||
* @param {String} params.password A string that represents the registered password
|
||||
* @param {String} params.scope A string that represents the application privileges
|
||||
* @return {Promise}
|
||||
*/
|
||||
async function getToken(params) {
|
||||
const options = Object.assign({}, params, {
|
||||
grant_type: 'password',
|
||||
});
|
||||
|
||||
return core.request(config.auth.tokenPath, options);
|
||||
}
|
||||
|
||||
return {
|
||||
getToken,
|
||||
};
|
||||
};
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
const Wreck = require('wreck');
|
||||
const querystring = require('querystring');
|
||||
const debug = require('debug')('simple-oauth2:index');
|
||||
const encoding = require('./encoding');
|
||||
|
||||
const defaultHeaders = {
|
||||
Accept: 'application/json',
|
||||
};
|
||||
|
||||
module.exports = (config) => {
|
||||
const httpOptions = Object.assign({}, config.http, {
|
||||
baseUrl: config.auth.tokenHost,
|
||||
headers: Object.assign({}, defaultHeaders, (config.http && config.http.headers)),
|
||||
});
|
||||
|
||||
const wreck = Wreck.defaults(httpOptions);
|
||||
|
||||
async function request(url, params) {
|
||||
let payload = params;
|
||||
const options = {
|
||||
json: true,
|
||||
headers: {},
|
||||
};
|
||||
|
||||
if (config.options.authorizationMethod === 'header') {
|
||||
const basicHeader = encoding.getAuthorizationHeaderToken(
|
||||
config.client.id,
|
||||
config.client.secret
|
||||
);
|
||||
|
||||
debug('Using header authentication. Authorization header set to %s', basicHeader);
|
||||
|
||||
options.headers.Authorization = `Basic ${basicHeader}`;
|
||||
} else {
|
||||
debug('Using body authentication');
|
||||
|
||||
payload = Object.assign({}, payload, {
|
||||
[config.client.idParamName]: config.client.id,
|
||||
[config.client.secretParamName]: config.client.secret,
|
||||
});
|
||||
}
|
||||
|
||||
if (config.options.bodyFormat === 'form') {
|
||||
debug('Using form request format');
|
||||
|
||||
// An example using `form` authorization params in the body is the
|
||||
// GitHub API.
|
||||
options.payload = querystring.stringify(payload);
|
||||
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
} else {
|
||||
debug('Using json request format');
|
||||
|
||||
// An example using `json` authorization params in the body is the
|
||||
// Amazon Developer Publishing API.
|
||||
options.payload = payload;
|
||||
options.headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
debug('Creating request to: (POST) %s', url);
|
||||
debug('Using options: %j', options);
|
||||
|
||||
const result = await wreck.post(url, options);
|
||||
|
||||
return result.payload;
|
||||
}
|
||||
|
||||
return {
|
||||
request,
|
||||
};
|
||||
};
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const HEADER_ENCODING_FORMAT = 'base64';
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Encode a single {value} using the application/x-www-form-urlencoded media type
|
||||
* while also applying some additional rules specified by the spec
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6749#appendix-B
|
||||
*
|
||||
* @param {String} value
|
||||
*/
|
||||
useFormURLEncode(value) {
|
||||
return encodeURIComponent(value).replace(/%20/g, '+');
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the authorization header used to request a valid token
|
||||
* @param {String} clientID
|
||||
* @param {String} clientSecret
|
||||
* @return {String} Authorization header string token
|
||||
*/
|
||||
getAuthorizationHeaderToken(clientID, clientSecret) {
|
||||
const encodedCredentials = `${this.useFormURLEncode(clientID)}:${this.useFormURLEncode(clientSecret)}`;
|
||||
|
||||
return Buffer.from(encodedCredentials).toString(HEADER_ENCODING_FORMAT);
|
||||
},
|
||||
};
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "8"
|
||||
- "10"
|
||||
- "12"
|
||||
- "node"
|
||||
|
||||
sudo: false
|
||||
|
||||
install:
|
||||
- "npm install"
|
||||
|
||||
os:
|
||||
- "linux"
|
||||
- "osx"
|
||||
- "windows"
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
## Methods
|
||||
|
||||
### `domain.analyze(domain, [options])`
|
||||
|
||||
Analyzes a string to verify it is a valid domain name where:
|
||||
- `domain` - the domain name string being verified.
|
||||
- `options` - optional settings:
|
||||
- `allowUnicode` - if `false`, Unicode characters are not allowd in domain names. Defaults to `true`.
|
||||
- `minDomainSegments` - the minimum number of domain segments (e.g. `x.y.z` has 3 segments) required. Defaults to `2`.
|
||||
- `tlds` - options to validate the top-level-domain segment (e.g. `com` in `example.com`). Can be set to one of:
|
||||
- `false` - disable TLD validation.
|
||||
- `true` - validate the TLD using the official list of [registered names](http://data.iana.org/TLD/tlds-alpha-by-domain.txt). This is the default setting.
|
||||
- an object with one (and only one) of:
|
||||
- `deny` - a `Set` with strings matching forbidden TLD values (all non-matching values are allowed).
|
||||
- `allow` - a `Set` with strings matching the only allowed TLD values. Can also be set to `true` which defaults to the official list of [registered names](http://data.iana.org/TLD/tlds-alpha-by-domain.txt).
|
||||
|
||||
If the `domain` is valid, no return value. If the `domain` is invalid, an object is returned with:
|
||||
- `error` - a string containing the reason the domain is invalid.
|
||||
|
||||
### `domain.isValid(domain, [options])`
|
||||
|
||||
Validates a string to verify it is a valid domain name where:
|
||||
- `domain` - the domain name string being verified.
|
||||
- `options` - same options as [`domain.analyze()`](#domainanalyzedomain-options).
|
||||
|
||||
### `email.analyze(email, [options])`
|
||||
|
||||
Analyzes a string to verify it is a valid email address where:
|
||||
- `email` - the email address string being verified.
|
||||
- `options` - optional settings:
|
||||
- `allowUnicode` - if `false`, Unicode characters are not allowd in the email address local and domain parts. Defaults to `true`.
|
||||
- `ignoreLength` - if `true`, the standards email maximum length limit is ignored. Defaults to `true`.
|
||||
- `minDomainSegments` - the minimum number of domain segments (e.g. `x.y.z` has 3 segments) required in the domain part. Defaults to `2`.
|
||||
- `tlds` - options to validate the top-level-domain segment (e.g. `com` in `example.com`) of the domain part. Can be set to one of:
|
||||
- `false` - disable TLD validation.
|
||||
- `true` - validate the TLD using the official list of [registered names](http://data.iana.org/TLD/tlds-alpha-by-domain.txt). This is the default setting.
|
||||
- an object with one (and only one) of:
|
||||
- `deny` - a `Set` with strings matching forbidden TLD values (all non-matching values are allowed).
|
||||
- `allow` - a `Set` with strings matching the only allowed TLD values. Can also be set to `true` which defaults to the official list of [registered names](http://data.iana.org/TLD/tlds-alpha-by-domain.txt).
|
||||
|
||||
If the `email` is valid, no return value. If the `email` is invalid, an object is returned with:
|
||||
- `error` - a string containing the reason the email is invalid.
|
||||
|
||||
|
||||
### `email.isValid(email, [options])`
|
||||
|
||||
Validates a string to verify it is a valid email address where:
|
||||
- `email` - the email address string being verified.
|
||||
- `options` - same options as [`email.analyze()`](#emailanalyzeemail-options).
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Breaking changes are documented using GitHub issues, see [issues labeled "release notes"](https://github.com/hapijs/address/issues?q=is%3Aissue+label%3A%22release+notes%22).
|
||||
|
||||
If you want changes of a specific minor or patch release, you can browse the [GitHub milestones](https://github.com/hapijs/address/milestones?state=closed&direction=asc&sort=due_date).
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
Copyright (c) 2019, Project contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<a href="https://hapi.dev"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# @hapi/address
|
||||
|
||||
#### Validate email address and domain.
|
||||
|
||||
**address** is part of the **hapi** ecosystem and was designed to work seamlessly with the [hapi web framework](https://hapi.dev) and its other components (but works great on its own or with other frameworks). If you are using a different web framework and find this module useful, check out [hapi](https://hapi.dev) – they work even better together.
|
||||
|
||||
### Visit the [hapi.dev](https://hapi.dev) Developer Portal for tutorials, documentation, and support
|
||||
|
||||
## Useful resources
|
||||
|
||||
- [Documentation and API](https://hapi.dev/family/address/)
|
||||
- [Versions status](https://hapi.dev/resources/status/#address)
|
||||
- [Project policies](https://hapi.dev/policies/)
|
||||
- [Free and commercial support options](https://hapi.dev/support/)
|
||||
Generated
Vendored
+238
@@ -0,0 +1,238 @@
|
||||
'use strict';
|
||||
|
||||
const Bench = require('bench');
|
||||
const Address = require('../');
|
||||
const Isemail = require('isemail');
|
||||
|
||||
|
||||
const tests = [
|
||||
['', false],
|
||||
['\r', false],
|
||||
['test', false],
|
||||
['@', false],
|
||||
['test@', false],
|
||||
['test@io', false],
|
||||
['test@io', true, { minDomainSegments: 1 }],
|
||||
['@io', false],
|
||||
['@iana.org', false],
|
||||
['test@iana.org', true],
|
||||
['test@nominet.org.uk', true],
|
||||
['test@about.museum', true],
|
||||
['a@iana.org', true],
|
||||
['êjness@iana.org', true],
|
||||
['ñoñó1234@iana.org', true],
|
||||
['ñoñó1234@something.com', true],
|
||||
['伊昭傑@郵件.商務', true],
|
||||
['\ud801\udc37\ud852\udf62@iana.org', true],
|
||||
['test.test@iana.org', true],
|
||||
['.test@iana.org', false],
|
||||
['test.@iana.org', false],
|
||||
['test..iana.org', false],
|
||||
['test_exa-mple.com', false],
|
||||
['!#$%&`*+/=?^`{|}~@iana.org', true],
|
||||
['test\\@test@iana.org', false],
|
||||
['123@iana.org', true],
|
||||
['test@123.com', true],
|
||||
['test@iana.123', false],
|
||||
['test@255.255.255.255', false],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org', true],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklmn@iana.org', false],
|
||||
['\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06@iana.org', false],
|
||||
['test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm', false],
|
||||
['test@\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06.org', true],
|
||||
['test@abcdefghijklmnopqrstuvwxyzabcdefghijklmno\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06.org', false],
|
||||
['test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm.com', false],
|
||||
['test@mason-dixon.com', true],
|
||||
['test@-iana.org', false],
|
||||
['test@iana-.com', false],
|
||||
['test@.iana.org', false],
|
||||
['test@iana.org.', false],
|
||||
['test@iana..com', false],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmno', false],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06', false],
|
||||
['abcdef@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef.hijklmnopqrstuv', false],
|
||||
['abcdef@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghi.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd\ud83d\ude06', false],
|
||||
['abcdef@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghi.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ud83d\ude06', false],
|
||||
['a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijkl.hijk', false],
|
||||
['a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijkl.\ud83d\ude06', false],
|
||||
['\"\r', false],
|
||||
['\"test\"@iana.org', false],
|
||||
['\"\"@iana.org', false],
|
||||
['\"\"\"@iana.org', false],
|
||||
['\"\\a\"@iana.org', false],
|
||||
['\"\\\"\"@iana.org', false],
|
||||
['\"\\\"@iana.org', false],
|
||||
['\"\\\\\"@iana.org', false],
|
||||
['test\"@iana.org', false],
|
||||
['\"test@iana.org', false],
|
||||
['\"test\"test@iana.org', false],
|
||||
['test\"text\"@iana.org', false],
|
||||
['\"test\"\"test\"@iana.org', false],
|
||||
['\"test\".\"test\"@iana.org', false],
|
||||
['\"test\\ test\"@iana.org', false],
|
||||
['\"test\".test@iana.org', false],
|
||||
['\"test\u0000\"@iana.org', false],
|
||||
['\"test\\\u0000\"@iana.org', false],
|
||||
['\"test\r\n test\"@iana.org', false],
|
||||
['\"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghj\"@iana.org', false],
|
||||
['\"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefg\\h\"@iana.org', false],
|
||||
['test@[255.255.255.255]', false],
|
||||
['test@a[255.255.255.255]', false],
|
||||
['test@[255.255.255]', false],
|
||||
['test@[255.255.255.255.255]', false],
|
||||
['test@[255.255.255.256]', false],
|
||||
['test@[1111:2222:3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:888G]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666::8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555::8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666::7777:8888]', false],
|
||||
['test@[IPv6::3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:::3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:1111::4444:5555::8888]', false],
|
||||
['test@[IPv6:::]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444::255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666::255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:::255.255.255.255]', false],
|
||||
['test@[IPv6::255.255.255.255]', false],
|
||||
['test@[255.255.255.255].local', false],
|
||||
['test@local.[255.255.255.255]', false],
|
||||
['test@local.[255.255.255.255].local', false],
|
||||
['test@local.(comment)[255.255.255.255].local', false],
|
||||
['test@local. [255.255.255.255].local', false],
|
||||
['test@local.[255.255.255.255](comment).local', false],
|
||||
['test@local.[255.255.255.255] .local', false],
|
||||
[' test @iana.org', false],
|
||||
['test@ iana .com', false],
|
||||
['test . test@iana.org', false],
|
||||
['\r\n test@iana.org', false],
|
||||
['\r\n \r\n test@iana.org', false],
|
||||
['(\r', false],
|
||||
['(comment)test@iana.org', false],
|
||||
['((comment)test@iana.org', false],
|
||||
['(comment(comment))test@iana.org', false],
|
||||
['test@(comment)iana.org', false],
|
||||
['test(comment)@iana.org', false],
|
||||
['test(comment)test@iana.org', false],
|
||||
['test@(comment)[255.255.255.255]', false],
|
||||
['(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org', false],
|
||||
['test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.com', false],
|
||||
['(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrst', false],
|
||||
['test@iana.org\n', false],
|
||||
['xn--test@iana.org', true],
|
||||
['test@iana.org-', false],
|
||||
['\"test@iana.org', false],
|
||||
['(test@iana.org', false],
|
||||
['test@(iana.org', false],
|
||||
['test@[1.2.3.4', false],
|
||||
['\"test\\\"@iana.org', false],
|
||||
['(comment\\)test@iana.org', false],
|
||||
['test@iana.org(comment\\)', false],
|
||||
['test@iana.org(comment\\', false],
|
||||
['test@[RFC-5322-domain-literal]', false],
|
||||
['test@[RFC-5322-郵件ñó-domain-literal]', false],
|
||||
['test@[RFC-5322]-domain-literal]', false],
|
||||
['test@[RFC-5322].domain-literal]', false],
|
||||
['test@[RFC-5322-[domain-literal]', false],
|
||||
['test@[', false],
|
||||
['test@[\u0007]', false],
|
||||
['test@[RFC-5322-\\\u0007-domain-literal]', false],
|
||||
['test@[RFC-5322-\\\t-domain-literal]', false],
|
||||
['test@[RFC-5322-\\]-domain-literal]', false],
|
||||
['test@[RFC-5322-\\郵-no-domain-literal]', false],
|
||||
['test@[RFC-5322--domain-literal]', false],
|
||||
['test@[RFC-5322-domain-literal\\]', false],
|
||||
['test@[RFC-5322-domain-literal\\', false],
|
||||
['test@[RFC 5322 domain literal]', false],
|
||||
['test@[RFC-5322-domain-literal] (comment)', false],
|
||||
['@iana.org', false],
|
||||
['test@.org', false],
|
||||
['\"\"@iana.org', false],
|
||||
['\"\"@iana.org', false],
|
||||
['\"\\\"@iana.org', false],
|
||||
['()test@iana.org', false],
|
||||
['()test@iana.org', false],
|
||||
['test@iana.org\r', false],
|
||||
['\rtest@iana.org', false],
|
||||
['\"\rtest\"@iana.org', false],
|
||||
['(\r)test@iana.org', false],
|
||||
['test@iana.org(\r)', false],
|
||||
['test@<iana>.org', false],
|
||||
['\ntest@iana.org', false],
|
||||
['\"\n\"@iana.org', false],
|
||||
['\"\\\n\"@iana.org', false],
|
||||
['(\n)test@iana.org', false],
|
||||
['\u0007@iana.org', false],
|
||||
['test@\u0007.org', false],
|
||||
['\"\u0007\"@iana.org', false],
|
||||
['\"\\\u0007\"@iana.org', false],
|
||||
['(\u0007)test@iana.org', false],
|
||||
['\r\ntest@iana.org', false],
|
||||
['\r\n \r\ntest@iana.org', false],
|
||||
[' \r\ntest@iana.org', false],
|
||||
[' \r\n test@iana.org', false],
|
||||
[' \r\n \r\ntest@iana.org', false],
|
||||
[' \r\n\r\ntest@iana.org', false],
|
||||
[' \r\n\r\n test@iana.org', false],
|
||||
['test@iana.org\r\n ', false],
|
||||
['test@iana.org\r\n \r\n ', false],
|
||||
['test@iana.org\r\n', false],
|
||||
['test@iana.org \r', false],
|
||||
['test@iana.org\r\n \r\n', false],
|
||||
['test@iana.org \r\n', false],
|
||||
['test@iana.org \r\n ', false],
|
||||
['test@iana.org \r\n \r\n', false],
|
||||
['test@iana.org \r\n\r\n', false],
|
||||
['test@iana.org \r\n\r\n ', false],
|
||||
['test@iana. org', false],
|
||||
['test@[\r', false],
|
||||
['test@[\r\n', false],
|
||||
[' test@iana.org', false],
|
||||
['test@iana.org ', false],
|
||||
['test@[IPv6:1::2:]', false],
|
||||
['\"test\\\u0094\"@iana.org', false],
|
||||
['test@iana/icann.org', false],
|
||||
['test@iana!icann.org', false],
|
||||
['test@iana?icann.org', false],
|
||||
['test@iana^icann.org', false],
|
||||
['test@iana{icann}.org', false],
|
||||
['test.(comment)test@iana.org', false],
|
||||
['test@iana.(comment)org', false],
|
||||
['test@iana(comment)iana.org', false],
|
||||
['(comment\r\n comment)test@iana.org', false],
|
||||
['test@org', true, { minDomainSegments: 1 }],
|
||||
['test\ud800@invalid', false],
|
||||
['\"\ud800\"@invalid', false],
|
||||
['\"\\\ud800\"@invalid', false],
|
||||
['(\ud800)thing@invalid', false],
|
||||
['\"\\\ud800\"@invalid', false],
|
||||
['test@\ud800\udfffñoñó郵件ñoñó郵件.郵件ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.noñó郵件.商務', true],
|
||||
['test@\ud800\udfffñoñó郵件ñoñó郵件.郵件ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.noñó郵件ñoñó郵.商務', false],
|
||||
['test@\ud800\udfffñoñó郵件ñoñó郵件.郵件ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.oñó郵件ñoñó郵件ñoñó郵件.商務', false],
|
||||
['test@ñoñoñó郵件\ud83d\ude06ñoñ.oñó郵件\uc138ñoñ.oñó郵件\u0644\u4eec\u010dñoñoñó郵件\u05dcño.ñoñó郵件\u092f\u672cñoñoñó郵件\uc138añoñ.oñó郵件\ud83d\ude06bc\uc138郵\ud83d\ude06ño.ñoñó郵件ñoñoñó郵件\ud83d\ude06ñoñoñó郵件\uc138ñoñ.oñó郵件\u0644\u4eecñoñoñó.郵件\ud83d\ude06ñoñoñó郵件郵\uc138ñoñoñó郵件\u0644\u4eecñoñoñó郵件.\ud83d\ude06ñoñoñó郵件郵\uc138\u0644\u4eec.郵件\ud83d\ude06ñoñoñó郵.件郵\uc138\u4eec\ud83d\ude06ñoñoñó件郵\uc138ñoñoñó郵件', false],
|
||||
['test@ñoñó郵件ñoñó郵件ñoñó郵件ñoñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件.商務', false],
|
||||
['\ud83d\ude06ñoñó郵件ñoñó郵件ñoñó\ud83d\ude06郵件ñoñoñó郵@\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06郵件ñoñó郵件ñoñó.郵件ñoñó郵件ñoñó\ud83d\ude06.郵件ñoñó郵件ñoñó.郵件ñoñó郵件.ñoñó郵件ñoñó.郵件ñoñó郵件.\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06商務.郵件ñoñó郵件ñoñó郵件.\ud83d\ude06商務.\ud83d\ude06商務.\ud83d\ude06商務', false]
|
||||
];
|
||||
|
||||
exports.compare = {
|
||||
address: function () {
|
||||
|
||||
for (const test of tests) {
|
||||
Address.email.isValid(test[0]);
|
||||
}
|
||||
},
|
||||
isemail: function () {
|
||||
|
||||
for (const test of tests) {
|
||||
Isemail.validate(test[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Bench.runMain();
|
||||
Generated
Vendored
+103
@@ -0,0 +1,103 @@
|
||||
'use strict';
|
||||
|
||||
const Url = require('url');
|
||||
|
||||
|
||||
const internals = {
|
||||
minDomainSegments: 2,
|
||||
nonAsciiRx: /[^\x00-\x7f]/,
|
||||
domainControlRx: /[\x00-\x20@\:\/]/, // Control + space + separators
|
||||
tldSegmentRx: /^[a-zA-Z](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/,
|
||||
domainSegmentRx: /^[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/,
|
||||
URL: Url.URL || URL // $lab:coverage:ignore$
|
||||
};
|
||||
|
||||
|
||||
exports.analyze = function (domain, options = {}) {
|
||||
|
||||
if (typeof domain !== 'string') {
|
||||
throw new Error('Invalid input: domain must be a string');
|
||||
}
|
||||
|
||||
if (!domain) {
|
||||
return { error: 'Domain must be a non-empty string' };
|
||||
}
|
||||
|
||||
if (domain.length > 256) {
|
||||
return { error: 'Domain too long' };
|
||||
}
|
||||
|
||||
const ascii = !internals.nonAsciiRx.test(domain);
|
||||
if (!ascii) {
|
||||
if (options.allowUnicode === false) { // Defaults to true
|
||||
return { error: 'Domain contains forbidden Unicode characters' };
|
||||
}
|
||||
|
||||
domain = domain.normalize('NFC');
|
||||
}
|
||||
|
||||
if (internals.domainControlRx.test(domain)) {
|
||||
return { error: 'Domain contains invalid character' };
|
||||
}
|
||||
|
||||
domain = internals.punycode(domain);
|
||||
|
||||
// https://tools.ietf.org/html/rfc1035 section 2.3.1
|
||||
|
||||
const minDomainSegments = options.minDomainSegments || internals.minDomainSegments;
|
||||
|
||||
const segments = domain.split('.');
|
||||
if (segments.length < minDomainSegments) {
|
||||
return { error: 'Domain lacks the minimum required number of segments' };
|
||||
}
|
||||
|
||||
const tlds = options.tlds;
|
||||
if (tlds) {
|
||||
const tld = segments[segments.length - 1].toLowerCase();
|
||||
if (tlds.deny && tlds.deny.has(tld) ||
|
||||
tlds.allow && !tlds.allow.has(tld)) {
|
||||
|
||||
return { error: 'Domain uses forbidden TLD' };
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < segments.length; ++i) {
|
||||
const segment = segments[i];
|
||||
|
||||
if (!segment.length) {
|
||||
return { error: 'Domain contains empty dot-separated segment' };
|
||||
}
|
||||
|
||||
if (segment.length > 63) {
|
||||
return { error: 'Domain contains dot-separated segment that is too long' };
|
||||
}
|
||||
|
||||
if (i < segments.length - 1) {
|
||||
if (!internals.domainSegmentRx.test(segment)) {
|
||||
return { error: 'Domain contains invalid character' };
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!internals.tldSegmentRx.test(segment)) {
|
||||
return { error: 'Domain contains invalid tld character' };
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.isValid = function (domain, options) {
|
||||
|
||||
return !exports.analyze(domain, options);
|
||||
};
|
||||
|
||||
|
||||
internals.punycode = function (domain) {
|
||||
|
||||
try {
|
||||
return new internals.URL(`http://${domain}`).host;
|
||||
}
|
||||
catch (err) {
|
||||
return domain;
|
||||
}
|
||||
};
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
'use strict';
|
||||
|
||||
const Util = require('util');
|
||||
|
||||
const Domain = require('./domain');
|
||||
|
||||
|
||||
const internals = {
|
||||
nonAsciiRx: /[^\x00-\x7f]/,
|
||||
encoder: new (Util.TextEncoder || TextEncoder)() // $lab:coverage:ignore$
|
||||
};
|
||||
|
||||
|
||||
exports.analyze = function (email, options) {
|
||||
|
||||
return internals.email(email, options);
|
||||
};
|
||||
|
||||
|
||||
exports.isValid = function (email, options) {
|
||||
|
||||
return !internals.email(email, options);
|
||||
};
|
||||
|
||||
|
||||
internals.email = function (email, options = {}) {
|
||||
|
||||
if (typeof email !== 'string') {
|
||||
throw new Error('Invalid input: email must be a string');
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
return { error: 'Address must be a non-empty string' };
|
||||
}
|
||||
|
||||
// Unicode
|
||||
|
||||
const ascii = !internals.nonAsciiRx.test(email);
|
||||
if (!ascii) {
|
||||
if (options.allowUnicode === false) { // Defaults to true
|
||||
return { error: 'Address contains forbidden Unicode characters' };
|
||||
}
|
||||
|
||||
email = email.normalize('NFC');
|
||||
}
|
||||
|
||||
// Basic structure
|
||||
|
||||
const parts = email.split('@');
|
||||
if (parts.length !== 2) {
|
||||
return { error: parts.length > 2 ? 'Address cannot contain more than one @ character' : 'Address must contain one @ character' };
|
||||
}
|
||||
|
||||
const [local, domain] = parts;
|
||||
|
||||
if (!local) {
|
||||
return { error: 'Address local part cannot be empty' };
|
||||
}
|
||||
|
||||
if (!options.ignoreLength) {
|
||||
if (email.length > 254) { // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
|
||||
return { error: 'Address too long' };
|
||||
}
|
||||
|
||||
if (internals.encoder.encode(local).length > 64) { // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
|
||||
return { error: 'Address local part too long' };
|
||||
}
|
||||
}
|
||||
|
||||
// Validate parts
|
||||
|
||||
return internals.local(local, ascii) || Domain.analyze(domain, options);
|
||||
};
|
||||
|
||||
|
||||
internals.local = function (local, ascii) {
|
||||
|
||||
const segments = local.split('.');
|
||||
for (const segment of segments) {
|
||||
if (!segment.length) {
|
||||
return { error: 'Address local part contains empty dot-separated segment' };
|
||||
}
|
||||
|
||||
if (ascii) {
|
||||
if (!internals.atextRx.test(segment)) {
|
||||
return { error: 'Address local part contains invalid character' };
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const char of segment) {
|
||||
if (internals.atextRx.test(char)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const binary = internals.binary(char);
|
||||
if (!internals.atomRx.test(binary)) {
|
||||
return { error: 'Address local part contains invalid character' };
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.binary = function (char) {
|
||||
|
||||
return Array.from(internals.encoder.encode(char)).map((v) => String.fromCharCode(v)).join('');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
From RFC 5321:
|
||||
|
||||
Mailbox = Local-part "@" ( Domain / address-literal )
|
||||
|
||||
Local-part = Dot-string / Quoted-string
|
||||
Dot-string = Atom *("." Atom)
|
||||
Atom = 1*atext
|
||||
atext = ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
|
||||
|
||||
Domain = sub-domain *("." sub-domain)
|
||||
sub-domain = Let-dig [Ldh-str]
|
||||
Let-dig = ALPHA / DIGIT
|
||||
Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
|
||||
|
||||
ALPHA = %x41-5A / %x61-7A ; a-z, A-Z
|
||||
DIGIT = %x30-39 ; 0-9
|
||||
|
||||
From RFC 6531:
|
||||
|
||||
sub-domain =/ U-label
|
||||
atext =/ UTF8-non-ascii
|
||||
|
||||
UTF8-non-ascii = UTF8-2 / UTF8-3 / UTF8-4
|
||||
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail /
|
||||
%xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail /
|
||||
%xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) /
|
||||
%xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
|
||||
UTF8-tail = %x80-BF
|
||||
|
||||
Note: The following are not supported:
|
||||
|
||||
RFC 5321: address-literal, Quoted-string
|
||||
RFC 5322: obs-*, CFWS
|
||||
*/
|
||||
|
||||
|
||||
internals.atextRx = /^[\w!#\$%&'\*\+\-/=\?\^`\{\|\}~]+$/; // _ included in \w
|
||||
|
||||
|
||||
internals.atomRx = new RegExp([
|
||||
|
||||
// %xC2-DF UTF8-tail
|
||||
'(?:[\\xc2-\\xdf][\\x80-\\xbf])',
|
||||
|
||||
// %xE0 %xA0-BF UTF8-tail %xE1-EC 2( UTF8-tail ) %xED %x80-9F UTF8-tail %xEE-EF 2( UTF8-tail )
|
||||
'(?:\\xe0[\\xa0-\\xbf][\\x80-\\xbf])|(?:[\\xe1-\\xec][\\x80-\\xbf]{2})|(?:\\xed[\\x80-\\x9f][\\x80-\\xbf])|(?:[\\xee-\\xef][\\x80-\\xbf]{2})',
|
||||
|
||||
// %xF0 %x90-BF 2( UTF8-tail ) %xF1-F3 3( UTF8-tail ) %xF4 %x80-8F 2( UTF8-tail )
|
||||
'(?:\\xf0[\\x90-\\xbf][\\x80-\\xbf]{2})|(?:[\\xf1-\\xf3][\\x80-\\xbf]{3})|(?:\\xf4[\\x80-\\x8f][\\x80-\\xbf]{2})'
|
||||
|
||||
].join('|'));
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
const Domain = require('./domain');
|
||||
const Email = require('./email');
|
||||
const Tlds = require('./tlds');
|
||||
|
||||
|
||||
const internals = {
|
||||
defaultTlds: { allow: Tlds, deny: null }
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
domain: {
|
||||
analyze(domain, options) {
|
||||
|
||||
options = internals.options(options);
|
||||
return Domain.analyze(domain, options);
|
||||
},
|
||||
|
||||
isValid(domain, options) {
|
||||
|
||||
options = internals.options(options);
|
||||
return Domain.isValid(domain, options);
|
||||
}
|
||||
},
|
||||
email: {
|
||||
analyze(email, options) {
|
||||
|
||||
options = internals.options(options);
|
||||
return Email.analyze(email, options);
|
||||
},
|
||||
|
||||
isValid(email, options) {
|
||||
|
||||
options = internals.options(options);
|
||||
return Email.isValid(email, options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.options = function (options) {
|
||||
|
||||
if (!options) {
|
||||
return { tlds: internals.defaultTlds };
|
||||
}
|
||||
|
||||
if (options.tlds === false) { // Defaults to true
|
||||
return options;
|
||||
}
|
||||
|
||||
if (!options.tlds ||
|
||||
options.tlds === true) {
|
||||
|
||||
return Object.assign({}, options, { tlds: internals.defaultTlds });
|
||||
}
|
||||
|
||||
if (typeof options.tlds !== 'object') {
|
||||
throw new Error('Invalid options: tlds must be a boolean or an object');
|
||||
}
|
||||
|
||||
if (options.tlds.deny) {
|
||||
if (options.tlds.deny instanceof Set === false) {
|
||||
throw new Error('Invalid options: tlds.deny must be a Set object');
|
||||
}
|
||||
|
||||
if (options.tlds.allow) {
|
||||
throw new Error('Invalid options: cannot specify both tlds.allow and tlds.deny lists');
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
if (options.tlds.allow === true) {
|
||||
return Object.assign({}, options, { tlds: internals.defaultTlds });
|
||||
}
|
||||
|
||||
if (options.tlds.allow instanceof Set === false) {
|
||||
throw new Error('Invalid options: tlds.allow must be a Set object or true');
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
+1542
File diff suppressed because it is too large
Load Diff
+56
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"_from": "@hapi/address@2.x.x",
|
||||
"_id": "@hapi/address@2.1.4",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==",
|
||||
"_location": "/simple-oauth2/@hapi/address",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "@hapi/address@2.x.x",
|
||||
"name": "@hapi/address",
|
||||
"escapedName": "@hapi%2faddress",
|
||||
"scope": "@hapi",
|
||||
"rawSpec": "2.x.x",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "2.x.x"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/simple-oauth2/@hapi/joi"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
||||
"_shasum": "5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5",
|
||||
"_spec": "@hapi/address@2.x.x",
|
||||
"_where": "C:\\Daten\\Git\\Tumortisch\\node_modules\\simple-oauth2\\node_modules\\@hapi\\joi",
|
||||
"bugs": {
|
||||
"url": "https://github.com/hapijs/address/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {},
|
||||
"deprecated": false,
|
||||
"description": "Email address and domain validation",
|
||||
"devDependencies": {
|
||||
"@hapi/code": "6.x.x",
|
||||
"@hapi/lab": "20.x.x"
|
||||
},
|
||||
"homepage": "https://github.com/hapijs/address#readme",
|
||||
"keywords": [
|
||||
"email",
|
||||
"domain",
|
||||
"address",
|
||||
"validation"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "lib/index.js",
|
||||
"name": "@hapi/address",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/hapijs/address.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -a @hapi/code -t 100 -L",
|
||||
"test-cov-html": "lab -a @hapi/code -t 100 -L -r html -o coverage.html"
|
||||
},
|
||||
"version": "2.1.4"
|
||||
}
|
||||
Generated
Vendored
+451
@@ -0,0 +1,451 @@
|
||||
'use strict';
|
||||
|
||||
const Punycode = require('punycode');
|
||||
|
||||
const Code = require('@hapi/code');
|
||||
const Address = require('..');
|
||||
const Lab = require('@hapi/lab');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
const { describe, it } = exports.lab = Lab.script();
|
||||
const expect = Code.expect;
|
||||
|
||||
|
||||
describe('email', () => {
|
||||
|
||||
it('available as direct require', () => {
|
||||
|
||||
expect(require('../lib/email').isValid('test@example.com')).to.be.true();
|
||||
});
|
||||
|
||||
describe('analyze()', () => {
|
||||
|
||||
it('identifies error', () => {
|
||||
|
||||
const tests = [
|
||||
['', 'Address must be a non-empty string'],
|
||||
['êjness@iana.org', 'Address contains forbidden Unicode characters', { allowUnicode: false }],
|
||||
['test@test@test', 'Address cannot contain more than one @ character'],
|
||||
['test', 'Address must contain one @ character'],
|
||||
['@example.com', 'Address local part cannot be empty'],
|
||||
['test@', 'Domain must be a non-empty string'],
|
||||
['1234567890@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz.com', 'Address too long'],
|
||||
['1234567890123456789012345678901234567890123456789012345678901234567890@example.com', 'Address local part too long'],
|
||||
['x..y@example.com', 'Address local part contains empty dot-separated segment'],
|
||||
['x:y@example.com', 'Address local part contains invalid character'],
|
||||
['ê:y@example.com', 'Address local part contains invalid character'],
|
||||
['test@com', 'Domain lacks the minimum required number of segments'],
|
||||
['test@x.no-such-tld', 'Domain uses forbidden TLD'],
|
||||
['test@example..com', 'Domain contains empty dot-separated segment'],
|
||||
['test@1234567890123456789012345678901234567890123456789012345678901234567890.com', 'Domain contains dot-separated segment that is too long'],
|
||||
['test@example+.com', 'Domain contains invalid character', { tlds: false }],
|
||||
['test@example.com_', 'Domain contains invalid tld character', { tlds: false }]
|
||||
];
|
||||
|
||||
for (let i = 0; i < tests.length; ++i) {
|
||||
const email = tests[i];
|
||||
const output = Address.email.analyze(email[0], email[2]);
|
||||
const result = email[1];
|
||||
|
||||
if (!output ||
|
||||
output.error !== result) {
|
||||
|
||||
console.log(i, email[0]);
|
||||
}
|
||||
|
||||
expect(output.error).to.equal(result);
|
||||
}
|
||||
});
|
||||
|
||||
it('validates options', () => {
|
||||
|
||||
const tests = [
|
||||
['test@example.com', 'Invalid options: tlds must be a boolean or an object', { tlds: 1 }],
|
||||
['test@example.com', 'Invalid options: tlds.allow must be a Set object or true', { tlds: { allow: ['test'] } }],
|
||||
['test@example.com', 'Invalid options: tlds.deny must be a Set object', { tlds: { deny: ['test'] } }],
|
||||
['test@example.com', 'Invalid options: cannot specify both tlds.allow and tlds.deny lists', { tlds: { allow: new Set(), deny: new Set() } }],
|
||||
[1, 'Invalid input: email must be a string']
|
||||
];
|
||||
|
||||
for (let i = 0; i < tests.length; ++i) {
|
||||
const email = tests[i];
|
||||
expect(() => Address.email.analyze(email[0], email[2])).to.throw(email[1]);
|
||||
}
|
||||
});
|
||||
|
||||
describe('validated TLD', () => {
|
||||
|
||||
it('applies built-in list', () => {
|
||||
|
||||
expect(Address.email.analyze('test@example.com')).to.not.exist();
|
||||
expect(Address.email.analyze('test@example.com', { tlds: true })).to.not.exist();
|
||||
expect(Address.email.analyze('test@example.com', { tlds: { allow: true } })).to.not.exist();
|
||||
});
|
||||
|
||||
it('ignores built-in list', () => {
|
||||
|
||||
expect(Address.email.analyze('test@example.invalid-top', { tlds: false })).to.not.exist();
|
||||
});
|
||||
|
||||
it('denies listed tls', () => {
|
||||
|
||||
expect(Address.email.analyze('test@example.com', { tlds: { deny: new Set(['test']) } })).to.not.exist();
|
||||
expect(Address.email.analyze('test@example.com', { tlds: { deny: new Set(['com']) } })).to.equal({ error: 'Domain uses forbidden TLD' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValid()', () => {
|
||||
|
||||
it('validates email', () => {
|
||||
|
||||
// Tests adapted from https://github.com/skeggse/isemail
|
||||
// Copyright (c) 2008-2019, Eli Skeggs, Dominic Sayers, GlobeSherpa
|
||||
|
||||
const tests = [
|
||||
['\r', false],
|
||||
['test', false],
|
||||
['@', false],
|
||||
['test@', false],
|
||||
['test@io', false],
|
||||
['test@io', true, { minDomainSegments: 1 }],
|
||||
['@io', false],
|
||||
['@iana.org', false],
|
||||
['test@iana.org', true],
|
||||
['test@nominet.org.uk', true],
|
||||
['test@about.museum', true],
|
||||
['a@iana.org', true],
|
||||
['êjness@iana.org', true],
|
||||
['ñoñó1234@iana.org', true],
|
||||
['ñoñó1234@something.com', true],
|
||||
['伊昭傑@郵件.商務', true, { tlds: { allow: new Set([Punycode.toASCII('商務')]) } }],
|
||||
['\ud801\udc37\ud852\udf62@iana.org', true],
|
||||
['test.test@iana.org', true],
|
||||
['.test@iana.org', false],
|
||||
['test.@iana.org', false],
|
||||
['test..iana.org', false],
|
||||
['test_exa-mple.com', false],
|
||||
['!#$%&`*+/=?^`{|}~@iana.org', true],
|
||||
['test\\@test@iana.org', false],
|
||||
['123@iana.org', true],
|
||||
['test@123.com', true],
|
||||
['test@iana.123', false],
|
||||
['test@255.255.255.255', false],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org', true],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklmn@iana.org', false],
|
||||
['\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06@iana.org', false],
|
||||
['test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm', false],
|
||||
['test@\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06.org', true],
|
||||
['test@abcdefghijklmnopqrstuvwxyzabcdefghijklmno\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06.org', false],
|
||||
['test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm.com', false],
|
||||
['test@mason-dixon.com', true],
|
||||
['test@-iana.org', false],
|
||||
['test@iana-.com', false],
|
||||
['test@.iana.org', false],
|
||||
['test@iana.org.', false],
|
||||
['test@iana..com', false],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmno', false],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.\ud83d\ude06\ud83d\ude06\ud83d\ude06\ud83d\ude06', false],
|
||||
['abcdef@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef.hijklmnopqrstuv', false],
|
||||
['abcdef@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghi.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd\ud83d\ude06', false],
|
||||
['abcdef@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghi.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ud83d\ude06', false],
|
||||
['a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijkl.hijk', false],
|
||||
['a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijkl.\ud83d\ude06', false],
|
||||
['\"\r', false],
|
||||
['\"test\"@iana.org', false],
|
||||
['\"\"@iana.org', false],
|
||||
['\"\"\"@iana.org', false],
|
||||
['\"\\a\"@iana.org', false],
|
||||
['\"\\\"\"@iana.org', false],
|
||||
['\"\\\"@iana.org', false],
|
||||
['\"\\\\\"@iana.org', false],
|
||||
['test\"@iana.org', false],
|
||||
['\"test@iana.org', false],
|
||||
['\"test\"test@iana.org', false],
|
||||
['test\"text\"@iana.org', false],
|
||||
['\"test\"\"test\"@iana.org', false],
|
||||
['\"test\".\"test\"@iana.org', false],
|
||||
['\"test\\ test\"@iana.org', false],
|
||||
['\"test\".test@iana.org', false],
|
||||
['\"test\u0000\"@iana.org', false],
|
||||
['\"test\\\u0000\"@iana.org', false],
|
||||
['\"test\r\n test\"@iana.org', false],
|
||||
['\"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghj\"@iana.org', false],
|
||||
['\"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefg\\h\"@iana.org', false],
|
||||
['test@[255.255.255.255]', false],
|
||||
['test@a[255.255.255.255]', false],
|
||||
['test@[255.255.255]', false],
|
||||
['test@[255.255.255.255.255]', false],
|
||||
['test@[255.255.255.256]', false],
|
||||
['test@[1111:2222:3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:888G]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666::8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555::8888]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666::7777:8888]', false],
|
||||
['test@[IPv6::3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:::3333:4444:5555:6666:7777:8888]', false],
|
||||
['test@[IPv6:1111::4444:5555::8888]', false],
|
||||
['test@[IPv6:::]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666:7777:255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444::255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:5555:6666::255.255.255.255]', false],
|
||||
['test@[IPv6:1111:2222:3333:4444:::255.255.255.255]', false],
|
||||
['test@[IPv6::255.255.255.255]', false],
|
||||
['test@[255.255.255.255].local', false],
|
||||
['test@local.[255.255.255.255]', false],
|
||||
['test@local.[255.255.255.255].local', false],
|
||||
['test@local.(comment)[255.255.255.255].local', false],
|
||||
['test@local. [255.255.255.255].local', false],
|
||||
['test@local.[255.255.255.255](comment).local', false],
|
||||
['test@local.[255.255.255.255] .local', false],
|
||||
[' test @iana.org', false],
|
||||
['test@ iana .com', false],
|
||||
['test . test@iana.org', false],
|
||||
['\r\n test@iana.org', false],
|
||||
['\r\n \r\n test@iana.org', false],
|
||||
['(\r', false],
|
||||
['(comment)test@iana.org', false],
|
||||
['((comment)test@iana.org', false],
|
||||
['(comment(comment))test@iana.org', false],
|
||||
['test@(comment)iana.org', false],
|
||||
['test(comment)@iana.org', false],
|
||||
['test(comment)test@iana.org', false],
|
||||
['test@(comment)[255.255.255.255]', false],
|
||||
['(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org', false],
|
||||
['test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.com', false],
|
||||
['(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrst', false],
|
||||
['test@iana.org\n', false],
|
||||
['xn--test@iana.org', true],
|
||||
['test@iana.org-', false],
|
||||
['\"test@iana.org', false],
|
||||
['(test@iana.org', false],
|
||||
['test@(iana.org', false],
|
||||
['test@[1.2.3.4', false],
|
||||
['\"test\\\"@iana.org', false],
|
||||
['(comment\\)test@iana.org', false],
|
||||
['test@iana.org(comment\\)', false],
|
||||
['test@iana.org(comment\\', false],
|
||||
['test@[RFC-5322-domain-literal]', false],
|
||||
['test@[RFC-5322-郵件ñó-domain-literal]', false],
|
||||
['test@[RFC-5322]-domain-literal]', false],
|
||||
['test@[RFC-5322].domain-literal]', false],
|
||||
['test@[RFC-5322-[domain-literal]', false],
|
||||
['test@[', false],
|
||||
['test@[\u0007]', false],
|
||||
['test@[RFC-5322-\\\u0007-domain-literal]', false],
|
||||
['test@[RFC-5322-\\\t-domain-literal]', false],
|
||||
['test@[RFC-5322-\\]-domain-literal]', false],
|
||||
['test@[RFC-5322-\\郵-no-domain-literal]', false],
|
||||
['test@[RFC-5322--domain-literal]', false],
|
||||
['test@[RFC-5322-domain-literal\\]', false],
|
||||
['test@[RFC-5322-domain-literal\\', false],
|
||||
['test@[RFC 5322 domain literal]', false],
|
||||
['test@[RFC-5322-domain-literal] (comment)', false],
|
||||
['@iana.org', false],
|
||||
['test@.org', false],
|
||||
['\"\"@iana.org', false],
|
||||
['\"\"@iana.org', false],
|
||||
['\"\\\"@iana.org', false],
|
||||
['()test@iana.org', false],
|
||||
['()test@iana.org', false],
|
||||
['test@iana.org\r', false],
|
||||
['\rtest@iana.org', false],
|
||||
['\"\rtest\"@iana.org', false],
|
||||
['(\r)test@iana.org', false],
|
||||
['test@iana.org(\r)', false],
|
||||
['test@<iana>.org', false],
|
||||
['\ntest@iana.org', false],
|
||||
['\"\n\"@iana.org', false],
|
||||
['\"\\\n\"@iana.org', false],
|
||||
['(\n)test@iana.org', false],
|
||||
['\u0007@iana.org', false],
|
||||
['test@\u0007.org', false],
|
||||
['\"\u0007\"@iana.org', false],
|
||||
['\"\\\u0007\"@iana.org', false],
|
||||
['(\u0007)test@iana.org', false],
|
||||
['\r\ntest@iana.org', false],
|
||||
['\r\n \r\ntest@iana.org', false],
|
||||
[' \r\ntest@iana.org', false],
|
||||
[' \r\n test@iana.org', false],
|
||||
[' \r\n \r\ntest@iana.org', false],
|
||||
[' \r\n\r\ntest@iana.org', false],
|
||||
[' \r\n\r\n test@iana.org', false],
|
||||
['test@iana.org\r\n ', false],
|
||||
['test@iana.org\r\n \r\n ', false],
|
||||
['test@iana.org\r\n', false],
|
||||
['test@iana.org \r', false],
|
||||
['test@iana.org\r\n \r\n', false],
|
||||
['test@iana.org \r\n', false],
|
||||
['test@iana.org \r\n ', false],
|
||||
['test@iana.org \r\n \r\n', false],
|
||||
['test@iana.org \r\n\r\n', false],
|
||||
['test@iana.org \r\n\r\n ', false],
|
||||
['test@iana. org', false],
|
||||
['test@[\r', false],
|
||||
['test@[\r\n', false],
|
||||
[' test@iana.org', false],
|
||||
['test@iana.org ', false],
|
||||
['test@[IPv6:1::2:]', false],
|
||||
['\"test\\\u0094\"@iana.org', false],
|
||||
['test@iana/icann.org', false],
|
||||
['test@iana!icann.org', false],
|
||||
['test@iana?icann.org', false],
|
||||
['test@iana^icann.org', false],
|
||||
['test@iana{icann}.org', false],
|
||||
['test.(comment)test@iana.org', false],
|
||||
['test@iana.(comment)org', false],
|
||||
['test@iana(comment)iana.org', false],
|
||||
['(comment\r\n comment)test@iana.org', false],
|
||||
['test@org', true, { minDomainSegments: 1 }],
|
||||
['test\ud800@invalid', false],
|
||||
['\"\ud800\"@invalid', false],
|
||||
['\"\\\ud800\"@invalid', false],
|
||||
['(\ud800)thing@invalid', false],
|
||||
['\"\\\ud800\"@invalid', false],
|
||||
['test@\ud800\udfffñoñó郵件ñoñó郵件.郵件ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.noñó郵件ñoñó郵.商務', false, { tlds: { allow: new Set([Punycode.toASCII('商務')]) } }],
|
||||
['test@\ud800\udfffñoñó郵件ñoñó郵件.郵件ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.ñoñó郵件ñoñó郵件.oñó郵件ñoñó郵件ñoñó郵件.商務', false, { tlds: { allow: new Set([Punycode.toASCII('商務')]) } }],
|
||||
['test@ñoñoñó郵件\ud83d\ude06ñoñ.oñó郵件\uc138ñoñ.oñó郵件\u0644\u4eec\u010dñoñoñó郵件\u05dcño.ñoñó郵件\u092f\u672cñoñoñó郵件\uc138añoñ.oñó郵件\ud83d\ude06bc\uc138郵\ud83d\ude06ño.ñoñó郵件ñoñoñó郵件\ud83d\ude06ñoñoñó郵件\uc138ñoñ.oñó郵件\u0644\u4eecñoñoñó.郵件\ud83d\ude06ñoñoñó郵件郵\uc138ñoñoñó郵件\u0644\u4eecñoñoñó郵件.\ud83d\ude06ñoñoñó郵件郵\uc138\u0644\u4eec.郵件\ud83d\ude06ñoñoñó郵.件郵\uc138\u4eec\ud83d\ude06ñoñoñó件郵\uc138ñoñoñó郵件', false, { tlds: { allow: new Set([Punycode.toASCII('商務')]) } }],
|
||||
['test@ñoñó郵件ñoñó郵件ñoñó郵件ñoñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件ñoñó郵件.商務', false, { tlds: { allow: new Set([Punycode.toASCII('商務')]) } }],
|
||||
['\ud83d\ude06ñoñó郵件ñoñó郵件ñoñó\ud83d\ude06郵件ñoñoñó郵@\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06郵件ñoñó郵件ñoñó.郵件ñoñó郵件ñoñó\ud83d\ude06.郵件ñoñó郵件ñoñó.郵件ñoñó郵件.ñoñó郵件ñoñó.郵件ñoñó郵件.\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06郵件ñoñó郵件ñoñó.\ud83d\ude06商務.郵件ñoñó郵件ñoñó郵件.\ud83d\ude06商務.\ud83d\ude06商務.\ud83d\ude06商務', false, { tlds: { allow: new Set([Punycode.toASCII('商務')]) } }],
|
||||
['test@[\0', false],
|
||||
['(\0)test@example.com', false],
|
||||
['shouldbe@invalid', false],
|
||||
['shouldbe@INVALID', false],
|
||||
['shouldbe@example.com', true],
|
||||
['shouldbe@example.COM', true],
|
||||
['apple-touch-icon-60x60@2x.png', false],
|
||||
['shouldbe@XN--UNUP4Y', true, { minDomainSegments: 1 }],
|
||||
['shouldbe@xn--unup4y', true, { minDomainSegments: 1 }],
|
||||
['shouldbe@\u6e38\u620f', true, { minDomainSegments: 1 }],
|
||||
['æøå', false],
|
||||
['1234567890abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvw@xyz.com', true, { ignoreLength: true }],
|
||||
['test@example.com@example.com', false],
|
||||
['test@example.com/path', false],
|
||||
['test@example.com:123', false]
|
||||
];
|
||||
|
||||
for (let i = 0; i < tests.length; ++i) {
|
||||
const email = tests[i];
|
||||
const valid = Address.email.isValid(email[0], email[2]);
|
||||
const result = email[1];
|
||||
|
||||
if (valid !== result) {
|
||||
const outcome = Address.email.analyze(email[0], email[2]);
|
||||
if (outcome) {
|
||||
console.log(i, email[0], outcome.error);
|
||||
}
|
||||
else {
|
||||
console.log(i, email[0]);
|
||||
}
|
||||
}
|
||||
|
||||
expect(valid).to.equal(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('domain', () => {
|
||||
|
||||
it('available as direct require', () => {
|
||||
|
||||
expect(require('../lib/domain').isValid('example.com')).to.be.true();
|
||||
});
|
||||
|
||||
describe('analyze()', () => {
|
||||
|
||||
it('identifies error', () => {
|
||||
|
||||
const tests = [
|
||||
['', 'Domain must be a non-empty string'],
|
||||
['êiana.org', 'Domain contains forbidden Unicode characters', { allowUnicode: false }],
|
||||
['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz.com', 'Domain too long'],
|
||||
['com', 'Domain lacks the minimum required number of segments'],
|
||||
['x.no-such-tld', 'Domain uses forbidden TLD'],
|
||||
['example..com', 'Domain contains empty dot-separated segment'],
|
||||
['1234567890123456789012345678901234567890123456789012345678901234567890.com', 'Domain contains dot-separated segment that is too long'],
|
||||
['example+.com', 'Domain contains invalid character', { tlds: false }],
|
||||
['example.com_', 'Domain contains invalid tld character', { tlds: false }]
|
||||
];
|
||||
|
||||
for (let i = 0; i < tests.length; ++i) {
|
||||
const domain = tests[i];
|
||||
const output = Address.domain.analyze(domain[0], domain[2]);
|
||||
const result = domain[1];
|
||||
|
||||
if (!output ||
|
||||
output.error !== result) {
|
||||
|
||||
console.log(i, domain[0]);
|
||||
}
|
||||
|
||||
expect(output.error).to.equal(result);
|
||||
}
|
||||
});
|
||||
|
||||
it('validates options', () => {
|
||||
|
||||
const tests = [
|
||||
['example.com', 'Invalid options: tlds must be a boolean or an object', { tlds: 1 }],
|
||||
['example.com', 'Invalid options: tlds.allow must be a Set object or true', { tlds: { allow: ['test'] } }],
|
||||
['example.com', 'Invalid options: tlds.deny must be a Set object', { tlds: { deny: ['test'] } }],
|
||||
['example.com', 'Invalid options: cannot specify both tlds.allow and tlds.deny lists', { tlds: { allow: new Set(), deny: new Set() } }],
|
||||
[1, 'Invalid input: domain must be a string']
|
||||
];
|
||||
|
||||
for (let i = 0; i < tests.length; ++i) {
|
||||
const domain = tests[i];
|
||||
expect(() => Address.domain.analyze(domain[0], domain[2])).to.throw(domain[1]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValid()', () => {
|
||||
|
||||
it('validates domain', () => {
|
||||
|
||||
const tests = [
|
||||
['\r', false],
|
||||
['test', false],
|
||||
['@', false],
|
||||
['iana.org', true],
|
||||
['nominet.org.uk', true],
|
||||
['about.museum', true],
|
||||
['x.商務', true, { tlds: { allow: new Set([Punycode.toASCII('商務')]) } }],
|
||||
['iana.123', false],
|
||||
['255.255.255.255', false],
|
||||
['XN--UNUP4Y', true, { minDomainSegments: 1 }],
|
||||
['test@example.com', false],
|
||||
['test:example.com', false],
|
||||
['example.com:123', false],
|
||||
['example.com/path', false]
|
||||
];
|
||||
|
||||
for (let i = 0; i < tests.length; ++i) {
|
||||
const domain = tests[i];
|
||||
const valid = Address.domain.isValid(domain[0], domain[2]);
|
||||
const result = domain[1];
|
||||
|
||||
if (valid !== result) {
|
||||
const outcome = Address.domain.analyze(domain[0], domain[2]);
|
||||
if (outcome) {
|
||||
console.log(i, domain[0], outcome.error);
|
||||
}
|
||||
else {
|
||||
console.log(i, domain[0]);
|
||||
}
|
||||
}
|
||||
|
||||
expect(valid).to.equal(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Breaking changes are documented using GitHub issues, see [issues labeled "release notes"](https://github.com/hapijs/hoek/issues?q=is%3Aissue+label%3A%22release+notes%22).
|
||||
|
||||
If you want changes of a specific minor or patch release, you can browse the [GitHub milestones](https://github.com/hapijs/hoek/milestones?state=closed&direction=asc&sort=due_date).
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Copyright (c) 2011-2019, Sideway Inc, and project contributors
|
||||
Copyright (c) 2011-2014, Walmart
|
||||
Copyright (c) 2011, Yahoo Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<a href="https://hapi.dev"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# @hapi/hoek
|
||||
|
||||
#### Utility methods for the hapi ecosystem.
|
||||
|
||||
**hoek** is part of the **hapi** ecosystem and was designed to work seamlessly with the [hapi web framework](https://hapi.dev) and its other components (but works great on its own or with other frameworks). If you are using a different web framework and find this module useful, check out [hapi](https://hapi.dev) – they work even better together.
|
||||
|
||||
This module is not intended to solve every problem for everyone, but rather as a central place to store hapi-specific methods. If you're looking for a general purpose utility module, check out [lodash](https://github.com/lodash/lodash).
|
||||
|
||||
### Visit the [hapi.dev](https://hapi.dev) Developer Portal for tutorials, documentation, and support
|
||||
|
||||
## Useful resources
|
||||
|
||||
- [Documentation and API](https://hapi.dev/family/hoek/)
|
||||
- [Version status](https://hapi.dev/resources/status/#hoek) (builds, dependencies, node versions, licenses, eol)
|
||||
- [Project policies](https://hapi.dev/policies/)
|
||||
- [Free and commercial support options](https://hapi.dev/support/)
|
||||
Generated
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
const Clone = require('./clone');
|
||||
const Merge = require('./merge');
|
||||
const Utils = require('./utils');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (defaults, source, options = {}) {
|
||||
|
||||
Assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
|
||||
Assert(!source || source === true || typeof source === 'object', 'Invalid source value: must be true, falsy or an object');
|
||||
Assert(typeof options === 'object', 'Invalid options: must be an object');
|
||||
|
||||
if (!source) { // If no source, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.shallow) {
|
||||
return internals.applyToDefaultsWithShallow(defaults, source, options);
|
||||
}
|
||||
|
||||
const copy = Clone(defaults);
|
||||
|
||||
if (source === true) { // If source is set to true, use defaults
|
||||
return copy;
|
||||
}
|
||||
|
||||
const nullOverride = options.nullOverride !== undefined ? options.nullOverride : false;
|
||||
return Merge(copy, source, { nullOverride, mergeArrays: false });
|
||||
};
|
||||
|
||||
|
||||
internals.applyToDefaultsWithShallow = function (defaults, source, options) {
|
||||
|
||||
const keys = options.shallow;
|
||||
Assert(Array.isArray(keys), 'Invalid keys');
|
||||
|
||||
options = Object.assign({}, options);
|
||||
options.shallow = false;
|
||||
|
||||
const copy = Clone(defaults, { shallow: keys });
|
||||
|
||||
if (source === true) { // If source is set to true, use defaults
|
||||
return copy;
|
||||
}
|
||||
|
||||
const storage = Utils.store(source, keys); // Move shallow copy items to storage
|
||||
Merge(copy, source, { mergeArrays: false, nullOverride: false }); // Deep copy the rest
|
||||
Utils.restore(copy, source, storage); // Shallow copy the stored items and restore
|
||||
return copy;
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const AssertError = require('./error');
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (condition, ...args) {
|
||||
|
||||
if (condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length === 1 &&
|
||||
args[0] instanceof Error) {
|
||||
|
||||
throw args[0];
|
||||
}
|
||||
|
||||
throw new AssertError(args);
|
||||
};
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = internals.Bench = class {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.ts = 0;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
this.ts = internals.Bench.now();
|
||||
}
|
||||
|
||||
elapsed() {
|
||||
|
||||
return internals.Bench.now() - this.ts;
|
||||
}
|
||||
|
||||
static now() {
|
||||
|
||||
const ts = process.hrtime();
|
||||
return (ts[0] * 1e3) + (ts[1] / 1e6);
|
||||
}
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const Ignore = require('./ignore');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function () {
|
||||
|
||||
return new Promise(Ignore); // $lab:coverage:ignore$
|
||||
};
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
'use strict';
|
||||
|
||||
const Types = require('./types');
|
||||
const Utils = require('./utils');
|
||||
|
||||
|
||||
const internals = {
|
||||
needsProtoHack: new Set([Types.set, Types.map, Types.weakSet, Types.weakMap])
|
||||
};
|
||||
|
||||
|
||||
module.exports = internals.clone = function (obj, options = {}, _seen = null) {
|
||||
|
||||
if (typeof obj !== 'object' ||
|
||||
obj === null) {
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
let clone = internals.clone;
|
||||
let seen = _seen;
|
||||
|
||||
if (options.shallow) {
|
||||
if (options.shallow !== true) {
|
||||
return internals.cloneWithShallow(obj, options);
|
||||
}
|
||||
|
||||
clone = (value) => value;
|
||||
}
|
||||
else {
|
||||
seen = seen || new Map();
|
||||
|
||||
const lookup = seen.get(obj);
|
||||
if (lookup) {
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
|
||||
// Built-in object types
|
||||
|
||||
const baseProto = Types.getInternalProto(obj);
|
||||
if (baseProto === Types.buffer) {
|
||||
return Buffer && Buffer.from(obj); // $lab:coverage:ignore$
|
||||
}
|
||||
|
||||
if (baseProto === Types.date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
|
||||
if (baseProto === Types.regex) {
|
||||
return new RegExp(obj);
|
||||
}
|
||||
|
||||
// Generic objects
|
||||
|
||||
const newObj = internals.base(obj, baseProto, options);
|
||||
if (newObj === obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (seen) {
|
||||
seen.set(obj, newObj); // Set seen, since obj could recurse
|
||||
}
|
||||
|
||||
if (baseProto === Types.set) {
|
||||
for (const value of obj) {
|
||||
newObj.add(clone(value, options, seen));
|
||||
}
|
||||
}
|
||||
else if (baseProto === Types.map) {
|
||||
for (const [key, value] of obj) {
|
||||
newObj.set(key, clone(value, options, seen));
|
||||
}
|
||||
}
|
||||
|
||||
const keys = Utils.keys(obj, options);
|
||||
for (const key of keys) {
|
||||
if (key === '__proto__') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (baseProto === Types.array &&
|
||||
key === 'length') {
|
||||
|
||||
newObj.length = obj.length;
|
||||
continue;
|
||||
}
|
||||
|
||||
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
|
||||
if (descriptor) {
|
||||
if (descriptor.get ||
|
||||
descriptor.set) {
|
||||
|
||||
Object.defineProperty(newObj, key, descriptor);
|
||||
}
|
||||
else if (descriptor.enumerable) {
|
||||
newObj[key] = clone(obj[key], options, seen);
|
||||
}
|
||||
else {
|
||||
Object.defineProperty(newObj, key, { enumerable: false, writable: true, configurable: true, value: clone(obj[key], options, seen) });
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object.defineProperty(newObj, key, {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: clone(obj[key], options, seen)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
};
|
||||
|
||||
|
||||
internals.cloneWithShallow = function (source, options) {
|
||||
|
||||
const keys = options.shallow;
|
||||
options = Object.assign({}, options);
|
||||
options.shallow = false;
|
||||
|
||||
const storage = Utils.store(source, keys); // Move shallow copy items to storage
|
||||
const copy = internals.clone(source, options); // Deep copy the rest
|
||||
Utils.restore(copy, source, storage); // Shallow copy the stored items and restore
|
||||
return copy;
|
||||
};
|
||||
|
||||
|
||||
internals.base = function (obj, baseProto, options) {
|
||||
|
||||
if (baseProto === Types.array) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (options.prototype === false) { // Defaults to true
|
||||
if (internals.needsProtoHack.has(baseProto)) {
|
||||
return new baseProto.constructor();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const proto = Object.getPrototypeOf(obj);
|
||||
if (proto &&
|
||||
proto.isImmutable) {
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (internals.needsProtoHack.has(baseProto)) {
|
||||
const newObj = new proto.constructor();
|
||||
if (proto !== baseProto) {
|
||||
Object.setPrototypeOf(newObj, proto);
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
return Object.create(proto);
|
||||
};
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
const DeepEqual = require('./deepEqual');
|
||||
const EscapeRegex = require('./escapeRegex');
|
||||
const Utils = require('./utils');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (ref, values, options = {}) { // options: { deep, once, only, part, symbols }
|
||||
|
||||
/*
|
||||
string -> string(s)
|
||||
array -> item(s)
|
||||
object -> key(s)
|
||||
object -> object (key:value)
|
||||
*/
|
||||
|
||||
if (typeof values !== 'object') {
|
||||
values = [values];
|
||||
}
|
||||
|
||||
Assert(!Array.isArray(values) || values.length, 'Values array cannot be empty');
|
||||
|
||||
// String
|
||||
|
||||
if (typeof ref === 'string') {
|
||||
return internals.string(ref, values, options);
|
||||
}
|
||||
|
||||
// Array
|
||||
|
||||
if (Array.isArray(ref)) {
|
||||
return internals.array(ref, values, options);
|
||||
}
|
||||
|
||||
// Object
|
||||
|
||||
Assert(typeof ref === 'object', 'Reference must be string or an object');
|
||||
return internals.object(ref, values, options);
|
||||
};
|
||||
|
||||
|
||||
internals.array = function (ref, values, options) {
|
||||
|
||||
if (!Array.isArray(values)) {
|
||||
values = [values];
|
||||
}
|
||||
|
||||
if (!ref.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.only &&
|
||||
options.once &&
|
||||
ref.length !== values.length) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let compare;
|
||||
|
||||
// Map values
|
||||
|
||||
const map = new Map();
|
||||
for (const value of values) {
|
||||
if (!options.deep ||
|
||||
!value ||
|
||||
typeof value !== 'object') {
|
||||
|
||||
const existing = map.get(value);
|
||||
if (existing) {
|
||||
++existing.allowed;
|
||||
}
|
||||
else {
|
||||
map.set(value, { allowed: 1, hits: 0 });
|
||||
}
|
||||
}
|
||||
else {
|
||||
compare = compare || internals.compare(options);
|
||||
|
||||
let found = false;
|
||||
for (const [key, existing] of map.entries()) {
|
||||
if (compare(key, value)) {
|
||||
++existing.allowed;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
map.set(value, { allowed: 1, hits: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup values
|
||||
|
||||
let hits = 0;
|
||||
for (const item of ref) {
|
||||
let match;
|
||||
if (!options.deep ||
|
||||
!item ||
|
||||
typeof item !== 'object') {
|
||||
|
||||
match = map.get(item);
|
||||
}
|
||||
else {
|
||||
for (const [key, existing] of map.entries()) {
|
||||
if (compare(key, item)) {
|
||||
match = existing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
++match.hits;
|
||||
++hits;
|
||||
|
||||
if (options.once &&
|
||||
match.hits > match.allowed) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate results
|
||||
|
||||
if (options.only &&
|
||||
hits !== ref.length) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const match of map.values()) {
|
||||
if (match.hits === match.allowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match.hits < match.allowed &&
|
||||
!options.part) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !!hits;
|
||||
};
|
||||
|
||||
|
||||
internals.object = function (ref, values, options) {
|
||||
|
||||
Assert(options.once === undefined, 'Cannot use option once with object');
|
||||
|
||||
const keys = Utils.keys(ref, options);
|
||||
if (!keys.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keys list
|
||||
|
||||
if (Array.isArray(values)) {
|
||||
return internals.array(keys, values, options);
|
||||
}
|
||||
|
||||
// Key value pairs
|
||||
|
||||
const symbols = Object.getOwnPropertySymbols(values).filter((sym) => values.propertyIsEnumerable(sym));
|
||||
const targets = [...Object.keys(values), ...symbols];
|
||||
|
||||
const compare = internals.compare(options);
|
||||
const set = new Set(targets);
|
||||
|
||||
for (const key of keys) {
|
||||
if (!set.has(key)) {
|
||||
if (options.only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!compare(values[key], ref[key])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set.delete(key);
|
||||
}
|
||||
|
||||
if (set.size) {
|
||||
return options.part ? set.size < targets.length : false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
internals.string = function (ref, values, options) {
|
||||
|
||||
// Empty string
|
||||
|
||||
if (ref === '') {
|
||||
return values.length === 1 && values[0] === '' || // '' contains ''
|
||||
!options.once && !values.some((v) => v !== ''); // '' contains multiple '' if !once
|
||||
}
|
||||
|
||||
// Map values
|
||||
|
||||
const map = new Map();
|
||||
const patterns = [];
|
||||
|
||||
for (const value of values) {
|
||||
Assert(typeof value === 'string', 'Cannot compare string reference to non-string value');
|
||||
|
||||
if (value) {
|
||||
const existing = map.get(value);
|
||||
if (existing) {
|
||||
++existing.allowed;
|
||||
}
|
||||
else {
|
||||
map.set(value, { allowed: 1, hits: 0 });
|
||||
patterns.push(EscapeRegex(value));
|
||||
}
|
||||
}
|
||||
else if (options.once ||
|
||||
options.only) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!patterns.length) { // Non-empty string contains unlimited empty string
|
||||
return true;
|
||||
}
|
||||
|
||||
// Match patterns
|
||||
|
||||
const regex = new RegExp(`(${patterns.join('|')})`, 'g');
|
||||
const leftovers = ref.replace(regex, ($0, $1) => {
|
||||
|
||||
++map.get($1).hits;
|
||||
return ''; // Remove from string
|
||||
});
|
||||
|
||||
// Validate results
|
||||
|
||||
if (options.only &&
|
||||
leftovers) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let any = false;
|
||||
for (const match of map.values()) {
|
||||
if (match.hits) {
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (match.hits === match.allowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match.hits < match.allowed &&
|
||||
!options.part) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// match.hits > match.allowed
|
||||
|
||||
if (options.once) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !!any;
|
||||
};
|
||||
|
||||
|
||||
internals.compare = function (options) {
|
||||
|
||||
if (!options.deep) {
|
||||
return internals.shallow;
|
||||
}
|
||||
|
||||
const hasOnly = options.only !== undefined;
|
||||
const hasPart = options.part !== undefined;
|
||||
|
||||
const flags = {
|
||||
prototype: hasOnly ? options.only : hasPart ? !options.part : false,
|
||||
part: hasOnly ? !options.only : hasPart ? options.part : false
|
||||
};
|
||||
|
||||
return (a, b) => DeepEqual(a, b, flags);
|
||||
};
|
||||
|
||||
|
||||
internals.shallow = function (a, b) {
|
||||
|
||||
return a === b;
|
||||
};
|
||||
Generated
Vendored
+317
@@ -0,0 +1,317 @@
|
||||
'use strict';
|
||||
|
||||
const Types = require('./types');
|
||||
|
||||
|
||||
const internals = {
|
||||
mismatched: null
|
||||
};
|
||||
|
||||
|
||||
module.exports = function (obj, ref, options) {
|
||||
|
||||
options = Object.assign({ prototype: true }, options);
|
||||
|
||||
return !!internals.isDeepEqual(obj, ref, options, []);
|
||||
};
|
||||
|
||||
|
||||
internals.isDeepEqual = function (obj, ref, options, seen) {
|
||||
|
||||
if (obj === ref) { // Copied from Deep-eql, copyright(c) 2013 Jake Luer, jake@alogicalparadox.com, MIT Licensed, https://github.com/chaijs/deep-eql
|
||||
return obj !== 0 || 1 / obj === 1 / ref;
|
||||
}
|
||||
|
||||
const type = typeof obj;
|
||||
|
||||
if (type !== typeof ref) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj === null ||
|
||||
ref === null) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type === 'function') {
|
||||
if (!options.deepFunction ||
|
||||
obj.toString() !== ref.toString()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Continue as object
|
||||
}
|
||||
else if (type !== 'object') {
|
||||
return obj !== obj && ref !== ref; // NaN
|
||||
}
|
||||
|
||||
const instanceType = internals.getSharedType(obj, ref, !!options.prototype);
|
||||
switch (instanceType) {
|
||||
case Types.buffer:
|
||||
return Buffer && Buffer.prototype.equals.call(obj, ref); // $lab:coverage:ignore$
|
||||
case Types.promise:
|
||||
return obj === ref;
|
||||
case Types.regex:
|
||||
return obj.toString() === ref.toString();
|
||||
case internals.mismatched:
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = seen.length - 1; i >= 0; --i) {
|
||||
if (seen[i].isSame(obj, ref)) {
|
||||
return true; // If previous comparison failed, it would have stopped execution
|
||||
}
|
||||
}
|
||||
|
||||
seen.push(new internals.SeenEntry(obj, ref));
|
||||
|
||||
try {
|
||||
return !!internals.isDeepEqualObj(instanceType, obj, ref, options, seen);
|
||||
}
|
||||
finally {
|
||||
seen.pop();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.getSharedType = function (obj, ref, checkPrototype) {
|
||||
|
||||
if (checkPrototype) {
|
||||
if (Object.getPrototypeOf(obj) !== Object.getPrototypeOf(ref)) {
|
||||
return internals.mismatched;
|
||||
}
|
||||
|
||||
return Types.getInternalProto(obj);
|
||||
}
|
||||
|
||||
const type = Types.getInternalProto(obj);
|
||||
if (type !== Types.getInternalProto(ref)) {
|
||||
return internals.mismatched;
|
||||
}
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
|
||||
internals.valueOf = function (obj) {
|
||||
|
||||
const objValueOf = obj.valueOf;
|
||||
if (objValueOf === undefined) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
try {
|
||||
return objValueOf.call(obj);
|
||||
}
|
||||
catch (err) {
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.hasOwnEnumerableProperty = function (obj, key) {
|
||||
|
||||
return Object.prototype.propertyIsEnumerable.call(obj, key);
|
||||
};
|
||||
|
||||
|
||||
internals.isSetSimpleEqual = function (obj, ref) {
|
||||
|
||||
for (const entry of obj) {
|
||||
if (!ref.has(entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
internals.isDeepEqualObj = function (instanceType, obj, ref, options, seen) {
|
||||
|
||||
const { isDeepEqual, valueOf, hasOwnEnumerableProperty } = internals;
|
||||
const { keys, getOwnPropertySymbols } = Object;
|
||||
|
||||
if (instanceType === Types.array) {
|
||||
if (options.part) {
|
||||
|
||||
// Check if any index match any other index
|
||||
|
||||
for (const objValue of obj) {
|
||||
for (const refValue of ref) {
|
||||
if (isDeepEqual(objValue, refValue, options, seen)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (obj.length !== ref.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < obj.length; ++i) {
|
||||
if (!isDeepEqual(obj[i], ref[i], options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (instanceType === Types.set) {
|
||||
if (obj.size !== ref.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!internals.isSetSimpleEqual(obj, ref)) {
|
||||
|
||||
// Check for deep equality
|
||||
|
||||
const ref2 = new Set(ref);
|
||||
for (const objEntry of obj) {
|
||||
if (ref2.delete(objEntry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let found = false;
|
||||
for (const refEntry of ref2) {
|
||||
if (isDeepEqual(objEntry, refEntry, options, seen)) {
|
||||
ref2.delete(refEntry);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (instanceType === Types.map) {
|
||||
if (obj.size !== ref.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const [key, value] of obj) {
|
||||
if (value === undefined && !ref.has(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeepEqual(value, ref.get(key), options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (instanceType === Types.error) {
|
||||
|
||||
// Always check name and message
|
||||
|
||||
if (obj.name !== ref.name ||
|
||||
obj.message !== ref.message) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check .valueOf()
|
||||
|
||||
const valueOfObj = valueOf(obj);
|
||||
const valueOfRef = valueOf(ref);
|
||||
if ((obj !== valueOfObj || ref !== valueOfRef) &&
|
||||
!isDeepEqual(valueOfObj, valueOfRef, options, seen)) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check properties
|
||||
|
||||
const objKeys = keys(obj);
|
||||
if (!options.part &&
|
||||
objKeys.length !== keys(ref).length &&
|
||||
!options.skip) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let skipped = 0;
|
||||
for (const key of objKeys) {
|
||||
if (options.skip &&
|
||||
options.skip.includes(key)) {
|
||||
|
||||
if (ref[key] === undefined) {
|
||||
++skipped;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeepEqual(obj[key], ref[key], options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.part &&
|
||||
objKeys.length - skipped !== keys(ref).length) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check symbols
|
||||
|
||||
if (options.symbols !== false) { // Defaults to true
|
||||
const objSymbols = getOwnPropertySymbols(obj);
|
||||
const refSymbols = new Set(getOwnPropertySymbols(ref));
|
||||
|
||||
for (const key of objSymbols) {
|
||||
if (!options.skip ||
|
||||
!options.skip.includes(key)) {
|
||||
|
||||
if (hasOwnEnumerableProperty(obj, key)) {
|
||||
if (!hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeepEqual(obj[key], ref[key], options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
refSymbols.delete(key);
|
||||
}
|
||||
|
||||
for (const key of refSymbols) {
|
||||
if (hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
internals.SeenEntry = class {
|
||||
|
||||
constructor(obj, ref) {
|
||||
|
||||
this.obj = obj;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
isSame(obj, ref) {
|
||||
|
||||
return this.obj === obj && this.ref === ref;
|
||||
}
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const Stringify = require('./stringify');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = class extends Error {
|
||||
|
||||
constructor(args) {
|
||||
|
||||
const msgs = args
|
||||
.filter((arg) => arg !== '')
|
||||
.map((arg) => {
|
||||
|
||||
return typeof arg === 'string' ? arg : arg instanceof Error ? arg.message : Stringify(arg);
|
||||
});
|
||||
|
||||
super(msgs.join(' ') || 'Unknown error');
|
||||
|
||||
if (typeof Error.captureStackTrace === 'function') { // $lab:coverage:ignore$
|
||||
Error.captureStackTrace(this, exports.assert);
|
||||
}
|
||||
}
|
||||
};
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (attribute) {
|
||||
|
||||
// Allowed value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9, \, "
|
||||
|
||||
Assert(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~\"\\]*$/.test(attribute), 'Bad attribute value (' + attribute + ')');
|
||||
|
||||
return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"'); // Escape quotes and slash
|
||||
};
|
||||
Generated
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (input) {
|
||||
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let escaped = '';
|
||||
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
|
||||
const charCode = input.charCodeAt(i);
|
||||
|
||||
if (internals.isSafe(charCode)) {
|
||||
escaped += input[i];
|
||||
}
|
||||
else {
|
||||
escaped += internals.escapeHtmlChar(charCode);
|
||||
}
|
||||
}
|
||||
|
||||
return escaped;
|
||||
};
|
||||
|
||||
|
||||
internals.escapeHtmlChar = function (charCode) {
|
||||
|
||||
const namedEscape = internals.namedHtml[charCode];
|
||||
if (typeof namedEscape !== 'undefined') {
|
||||
return namedEscape;
|
||||
}
|
||||
|
||||
if (charCode >= 256) {
|
||||
return '&#' + charCode + ';';
|
||||
}
|
||||
|
||||
const hexValue = charCode.toString(16).padStart(2, '0');
|
||||
return `&#x${hexValue};`;
|
||||
};
|
||||
|
||||
|
||||
internals.isSafe = function (charCode) {
|
||||
|
||||
return (typeof internals.safeCharCodes[charCode] !== 'undefined');
|
||||
};
|
||||
|
||||
|
||||
internals.namedHtml = {
|
||||
'38': '&',
|
||||
'60': '<',
|
||||
'62': '>',
|
||||
'34': '"',
|
||||
'160': ' ',
|
||||
'162': '¢',
|
||||
'163': '£',
|
||||
'164': '¤',
|
||||
'169': '©',
|
||||
'174': '®'
|
||||
};
|
||||
|
||||
|
||||
internals.safeCharCodes = (function () {
|
||||
|
||||
const safe = {};
|
||||
|
||||
for (let i = 32; i < 123; ++i) {
|
||||
|
||||
if ((i >= 97) || // a-z
|
||||
(i >= 65 && i <= 90) || // A-Z
|
||||
(i >= 48 && i <= 57) || // 0-9
|
||||
i === 32 || // space
|
||||
i === 46 || // .
|
||||
i === 44 || // ,
|
||||
i === 45 || // -
|
||||
i === 58 || // :
|
||||
i === 95) { // _
|
||||
|
||||
safe[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return safe;
|
||||
}());
|
||||
Generated
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (input) {
|
||||
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const lessThan = 0x3C;
|
||||
const greaterThan = 0x3E;
|
||||
const andSymbol = 0x26;
|
||||
const lineSeperator = 0x2028;
|
||||
|
||||
// replace method
|
||||
let charCode;
|
||||
return input.replace(/[<>&\u2028\u2029]/g, (match) => {
|
||||
|
||||
charCode = match.charCodeAt(0);
|
||||
|
||||
if (charCode === lessThan) {
|
||||
return '\\u003c';
|
||||
}
|
||||
|
||||
if (charCode === greaterThan) {
|
||||
return '\\u003e';
|
||||
}
|
||||
|
||||
if (charCode === andSymbol) {
|
||||
return '\\u0026';
|
||||
}
|
||||
|
||||
if (charCode === lineSeperator) {
|
||||
return '\\u2028';
|
||||
}
|
||||
|
||||
return '\\u2029';
|
||||
});
|
||||
};
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (string) {
|
||||
|
||||
// Escape ^$.*+-?=!:|\/()[]{},
|
||||
|
||||
return string.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&');
|
||||
};
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = internals.flatten = function (array, target) {
|
||||
|
||||
const result = target || [];
|
||||
|
||||
for (let i = 0; i < array.length; ++i) {
|
||||
if (Array.isArray(array[i])) {
|
||||
internals.flatten(array[i], result);
|
||||
}
|
||||
else {
|
||||
result.push(array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function () { };
|
||||
+470
@@ -0,0 +1,470 @@
|
||||
/// <reference types="node" />
|
||||
|
||||
|
||||
/**
|
||||
* Performs a deep comparison of the two values including support for circular dependencies, prototype, and enumerable properties.
|
||||
*
|
||||
* @param obj - The value being compared.
|
||||
* @param ref - The reference value used for comparison.
|
||||
*
|
||||
* @return true when the two values are equal, otherwise false.
|
||||
*/
|
||||
export function deepEqual(obj: any, ref: any, options?: deepEqual.Options): boolean;
|
||||
|
||||
export namespace deepEqual {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* Compare functions with difference references by comparing their internal code and properties.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly deepFunction?: boolean;
|
||||
|
||||
/**
|
||||
* Allow partial match.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly part?: boolean;
|
||||
|
||||
/**
|
||||
* Compare the objects' prototypes.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly prototype?: boolean;
|
||||
|
||||
/**
|
||||
* List of object keys to ignore different values of.
|
||||
*
|
||||
* @default null
|
||||
*/
|
||||
readonly skip?: (string | symbol)[];
|
||||
|
||||
/**
|
||||
* Compare symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clone any value, object, or array.
|
||||
*
|
||||
* @param obj - The value being cloned.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @returns A deep clone of `obj`.
|
||||
*/
|
||||
export function clone<T>(obj: T, options?: clone.Options): T;
|
||||
|
||||
export namespace clone {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* Clone the object's prototype.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly prototype?: boolean;
|
||||
|
||||
/**
|
||||
* Include symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
|
||||
/**
|
||||
* Shallow clone the specified keys.
|
||||
*
|
||||
* @default undefined
|
||||
*/
|
||||
readonly shallow?: string[] | string[][] | boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge all the properties of source into target.
|
||||
*
|
||||
* @param target - The object being modified.
|
||||
* @param source - The object used to copy properties from.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @returns The `target` object.
|
||||
*/
|
||||
export function merge<T1 extends object, T2 extends object>(target: T1, source: T2, options?: merge.Options): T1 & T2;
|
||||
|
||||
export namespace merge {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* When true, null value from `source` overrides existing value in `target`.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly nullOverride?: boolean;
|
||||
|
||||
/**
|
||||
* When true, array value from `source` is merged with the existing value in `target`.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly mergeArrays?: boolean;
|
||||
|
||||
/**
|
||||
* Compare symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply source to a copy of the defaults.
|
||||
*
|
||||
* @param defaults - An object with the default values to use of `options` does not contain the same keys.
|
||||
* @param source - The source used to override the `defaults`.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @returns A copy of `defaults` with `source` keys overriding any conflicts.
|
||||
*/
|
||||
export function applyToDefaults<T extends object>(defaults: Partial<T>, source: Partial<T> | boolean | null, options?: applyToDefaults.Options): Partial<T>;
|
||||
|
||||
export namespace applyToDefaults {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* When true, null value from `source` overrides existing value in `target`.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly nullOverride?: boolean;
|
||||
|
||||
/**
|
||||
* Shallow clone the specified keys.
|
||||
*
|
||||
* @default undefined
|
||||
*/
|
||||
readonly shallow?: string[] | string[][];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the common unique items in two arrays.
|
||||
*
|
||||
* @param array1 - The first array to compare.
|
||||
* @param array2 - The second array to compare.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @return - An array of the common items. If `justFirst` is true, returns the first common item.
|
||||
*/
|
||||
export function intersect<T1, T2>(array1: intersect.Array<T1>, array2: intersect.Array<T2>, options?: intersect.Options): Array<T1 | T2>;
|
||||
export function intersect<T1, T2>(array1: intersect.Array<T1>, array2: intersect.Array<T2>, options?: intersect.Options): T1 | T2;
|
||||
|
||||
export namespace intersect {
|
||||
|
||||
type Array<T> = ArrayLike<T> | Set<T> | null;
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* When true, return the first overlapping value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly first?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the reference value contains the provided values.
|
||||
*
|
||||
* @param ref - The reference string, array, or object.
|
||||
* @param values - A single or array of values to find within `ref`. If `ref` is an object, `values` can be a key name, an array of key names, or an object with key-value pairs to compare.
|
||||
*
|
||||
* @return true if the value contains the provided values, otherwise false.
|
||||
*/
|
||||
export function contain(ref: string, values: string | string[], options?: contain.Options): boolean;
|
||||
export function contain(ref: any[], values: any, options?: contain.Options): boolean;
|
||||
export function contain(ref: object, values: string | string[] | object, options?: Omit<contain.Options, 'once'>): boolean;
|
||||
|
||||
export namespace contain {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* Perform a deep comparison.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly deep?: boolean;
|
||||
|
||||
/**
|
||||
* Allow only one occurrence of each value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly once?: boolean;
|
||||
|
||||
/**
|
||||
* Allow only values explicitly listed.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly only?: boolean;
|
||||
|
||||
/**
|
||||
* Allow partial match.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly part?: boolean;
|
||||
|
||||
/**
|
||||
* Include symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flatten an array with sub arrays
|
||||
*
|
||||
* @param array - an array of items or other arrays to flatten.
|
||||
* @param target - if provided, an array to shallow copy the flattened `array` items to
|
||||
*
|
||||
* @return a flat array of the provided values (appended to `target` is provided).
|
||||
*/
|
||||
export function flatten<T>(array: ArrayLike<T | ReadonlyArray<T>>, target?: ArrayLike<T | ReadonlyArray<T>>): T[];
|
||||
|
||||
|
||||
/**
|
||||
* Convert an object key chain string to reference.
|
||||
*
|
||||
* @param obj - the object from which to look up the value.
|
||||
* @param chain - the string path of the requested value. The chain string is split into key names using `options.separator`, or an array containing each individual key name. A chain including negative numbers will work like a negative index on an array.
|
||||
*
|
||||
* @return The value referenced by the chain if found, otherwise undefined. If chain is null, undefined, or false, the object itself will be returned.
|
||||
*/
|
||||
export function reach(obj: object | null, chain: string | (string | number)[] | false | null | undefined, options?: reach.Options): any;
|
||||
|
||||
export namespace reach {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* String to split chain path on. Defaults to '.'.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly separator?: string;
|
||||
|
||||
/**
|
||||
* Value to return if the path or value is not present. No default value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly default?: any;
|
||||
|
||||
/**
|
||||
* If true, will throw an error on missing member in the chain. Default to false.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly strict?: boolean;
|
||||
|
||||
/**
|
||||
* If true, allows traversing functions for properties. false will throw an error if a function is part of the chain.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly functions?: boolean;
|
||||
|
||||
/**
|
||||
* If true, allows traversing Set and Map objects for properties. false will return undefined regardless of the Set or Map passed.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly iterables?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace string parameters (using format "{path.to.key}") with their corresponding object key values using `Hoek.reach()`.
|
||||
*
|
||||
* @param obj - the object from which to look up the value.
|
||||
* @param template - the string containing {} enclosed key paths to be replaced.
|
||||
*
|
||||
* @return The template string with the {} enclosed keys replaced with looked-up values.
|
||||
*/
|
||||
export function reachTemplate(obj: object | null, template: string, options?: reach.Options): string;
|
||||
|
||||
|
||||
/**
|
||||
* Throw an error if condition is falsy.
|
||||
*
|
||||
* @param condition - If `condition` is not truthy, an exception is thrown.
|
||||
* @param error - The error thrown if the condition fails.
|
||||
*
|
||||
* @return Does not return a value but throws if the `condition` is falsy.
|
||||
*/
|
||||
export function assert(condition: any, error: Error): void;
|
||||
|
||||
|
||||
/**
|
||||
* Throw an error if condition is falsy.
|
||||
*
|
||||
* @param condition - If `condition` is not truthy, an exception is thrown.
|
||||
* @param args - Any number of values, concatenated together (space separated) to create the error message.
|
||||
*
|
||||
* @return Does not return a value but throws if the `condition` is falsy.
|
||||
*/
|
||||
export function assert(condition: any, ...args: any): void;
|
||||
|
||||
|
||||
/**
|
||||
* A benchmarking timer, using the internal node clock for maximum accuracy.
|
||||
*/
|
||||
export class Bench {
|
||||
|
||||
constructor();
|
||||
|
||||
/** The starting timestamp expressed in the number of milliseconds since the epoch. */
|
||||
ts: number;
|
||||
|
||||
/** The time in milliseconds since the object was created. */
|
||||
elapsed(): number;
|
||||
|
||||
/** Reset the `ts` value to now. */
|
||||
reset(): void;
|
||||
|
||||
/** The current time in milliseconds since the epoch. */
|
||||
static now(): number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for Regex construction by prefixing all reserved characters with a backslash.
|
||||
*
|
||||
* @param string - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string.
|
||||
*/
|
||||
export function escapeRegex(string: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for usage as an attribute value in HTTP headers.
|
||||
*
|
||||
* @param attribute - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string. Will throw on invalid characters that are not supported to be escaped.
|
||||
*/
|
||||
export function escapeHeaderAttribute(attribute: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for usage in HTML.
|
||||
*
|
||||
* @param string - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string.
|
||||
*/
|
||||
export function escapeHtml(string: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for usage in JSON.
|
||||
*
|
||||
* @param string - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string.
|
||||
*/
|
||||
export function escapeJson(string: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a function to ensure it can only execute once.
|
||||
*
|
||||
* @param method - The function to be wrapped.
|
||||
*
|
||||
* @return The wrapped function.
|
||||
*/
|
||||
export function once<T extends Function>(method: T): T;
|
||||
|
||||
|
||||
/**
|
||||
* A reusable no-op function.
|
||||
*/
|
||||
export function ignore(...ignore: any): void;
|
||||
|
||||
|
||||
/**
|
||||
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string with protection against thrown errors.
|
||||
*
|
||||
* @param value A JavaScript value, usually an object or array, to be converted.
|
||||
* @param replacer The JSON.stringify() `replacer` argument.
|
||||
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
||||
*
|
||||
* @return The JSON string. If the operation fails, an error string value is returned (no exception thrown).
|
||||
*/
|
||||
export function stringify(value: any, replacer?: any, space?: string | number): string;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves after the requested timeout.
|
||||
*
|
||||
* @param timeout - The number of milliseconds to wait before resolving the Promise.
|
||||
*
|
||||
* @return A Promise.
|
||||
*/
|
||||
export function wait(timeout?: number): Promise<void>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a Promise that never resolves.
|
||||
*/
|
||||
export function block(): Promise<void>;
|
||||
|
||||
|
||||
/**
|
||||
* Determines if an object is a promise.
|
||||
*
|
||||
* @param promise - the object tested.
|
||||
*
|
||||
* @returns true if the object is a promise, otherwise false.
|
||||
*/
|
||||
export function isPromise(promise: any): boolean;
|
||||
|
||||
|
||||
export namespace ts {
|
||||
|
||||
/**
|
||||
* Defines a type that can must be one of T or U but not both.
|
||||
*/
|
||||
type XOR<T, U> = (T | U) extends object ? (internals.Without<T, U> & U) | (internals.Without<U, T> & T) : T | U;
|
||||
}
|
||||
|
||||
|
||||
declare namespace internals {
|
||||
|
||||
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = {
|
||||
applyToDefaults: require('./applyToDefaults'),
|
||||
assert: require('./assert'),
|
||||
Bench: require('./bench'),
|
||||
block: require('./block'),
|
||||
clone: require('./clone'),
|
||||
contain: require('./contain'),
|
||||
deepEqual: require('./deepEqual'),
|
||||
Error: require('./error'),
|
||||
escapeHeaderAttribute: require('./escapeHeaderAttribute'),
|
||||
escapeHtml: require('./escapeHtml'),
|
||||
escapeJson: require('./escapeJson'),
|
||||
escapeRegex: require('./escapeRegex'),
|
||||
flatten: require('./flatten'),
|
||||
ignore: require('./ignore'),
|
||||
intersect: require('./intersect'),
|
||||
isPromise: require('./isPromise'),
|
||||
merge: require('./merge'),
|
||||
once: require('./once'),
|
||||
reach: require('./reach'),
|
||||
reachTemplate: require('./reachTemplate'),
|
||||
stringify: require('./stringify'),
|
||||
wait: require('./wait')
|
||||
};
|
||||
Generated
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (array1, array2, options = {}) {
|
||||
|
||||
if (!array1 ||
|
||||
!array2) {
|
||||
|
||||
return (options.first ? null : []);
|
||||
}
|
||||
|
||||
const common = [];
|
||||
const hash = (Array.isArray(array1) ? new Set(array1) : array1);
|
||||
const found = new Set();
|
||||
for (const value of array2) {
|
||||
if (internals.has(hash, value) &&
|
||||
!found.has(value)) {
|
||||
|
||||
if (options.first) {
|
||||
return value;
|
||||
}
|
||||
|
||||
common.push(value);
|
||||
found.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return (options.first ? null : common);
|
||||
};
|
||||
|
||||
|
||||
internals.has = function (ref, key) {
|
||||
|
||||
if (typeof ref.has === 'function') {
|
||||
return ref.has(key);
|
||||
}
|
||||
|
||||
return ref[key] !== undefined;
|
||||
};
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (promise) {
|
||||
|
||||
return !!promise && typeof promise.then === 'function';
|
||||
};
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
const Clone = require('./clone');
|
||||
const Utils = require('./utils');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = internals.merge = function (target, source, options) {
|
||||
|
||||
Assert(target && typeof target === 'object', 'Invalid target value: must be an object');
|
||||
Assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object');
|
||||
|
||||
if (!source) {
|
||||
return target;
|
||||
}
|
||||
|
||||
options = Object.assign({ nullOverride: true, mergeArrays: true }, options);
|
||||
|
||||
if (Array.isArray(source)) {
|
||||
Assert(Array.isArray(target), 'Cannot merge array onto an object');
|
||||
if (!options.mergeArrays) {
|
||||
target.length = 0; // Must not change target assignment
|
||||
}
|
||||
|
||||
for (let i = 0; i < source.length; ++i) {
|
||||
target.push(Clone(source[i], { symbols: options.symbols }));
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
const keys = Utils.keys(source, options);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
if (key === '__proto__' ||
|
||||
!Object.prototype.propertyIsEnumerable.call(source, key)) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = source[key];
|
||||
if (value &&
|
||||
typeof value === 'object') {
|
||||
|
||||
if (!target[key] ||
|
||||
typeof target[key] !== 'object' ||
|
||||
(Array.isArray(target[key]) !== Array.isArray(value)) ||
|
||||
value instanceof Date ||
|
||||
(Buffer && Buffer.isBuffer(value)) || // $lab:coverage:ignore$
|
||||
value instanceof RegExp) {
|
||||
|
||||
target[key] = Clone(value, { symbols: options.symbols });
|
||||
}
|
||||
else {
|
||||
internals.merge(target[key], value, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value !== null &&
|
||||
value !== undefined) { // Explicit to preserve empty strings
|
||||
|
||||
target[key] = value;
|
||||
}
|
||||
else if (options.nullOverride) {
|
||||
target[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (method) {
|
||||
|
||||
if (method._hoekOnce) {
|
||||
return method;
|
||||
}
|
||||
|
||||
let once = false;
|
||||
const wrapped = function (...args) {
|
||||
|
||||
if (!once) {
|
||||
once = true;
|
||||
method(...args);
|
||||
}
|
||||
};
|
||||
|
||||
wrapped._hoekOnce = true;
|
||||
return wrapped;
|
||||
};
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (obj, chain, options) {
|
||||
|
||||
if (chain === false ||
|
||||
chain === null ||
|
||||
chain === undefined) {
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
if (typeof options === 'string') {
|
||||
options = { separator: options };
|
||||
}
|
||||
|
||||
const isChainArray = Array.isArray(chain);
|
||||
|
||||
Assert(!isChainArray || !options.separator, 'Separator option no valid for array-based chain');
|
||||
|
||||
const path = isChainArray ? chain : chain.split(options.separator || '.');
|
||||
let ref = obj;
|
||||
for (let i = 0; i < path.length; ++i) {
|
||||
let key = path[i];
|
||||
const type = options.iterables && internals.iterables(ref);
|
||||
|
||||
if (Array.isArray(ref) ||
|
||||
type === 'set') {
|
||||
|
||||
const number = Number(key);
|
||||
if (Number.isInteger(number)) {
|
||||
key = number < 0 ? ref.length + number : number;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref ||
|
||||
typeof ref === 'function' && options.functions === false || // Defaults to true
|
||||
!type && ref[key] === undefined) {
|
||||
|
||||
Assert(!options.strict || i + 1 === path.length, 'Missing segment', key, 'in reach path ', chain);
|
||||
Assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', key, 'in reach path ', chain);
|
||||
ref = options.default;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
ref = ref[key];
|
||||
}
|
||||
else if (type === 'set') {
|
||||
ref = [...ref][key];
|
||||
}
|
||||
else { // type === 'map'
|
||||
ref = ref.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
return ref;
|
||||
};
|
||||
|
||||
|
||||
internals.iterables = function (ref) {
|
||||
|
||||
if (ref instanceof Set) {
|
||||
return 'set';
|
||||
}
|
||||
|
||||
if (ref instanceof Map) {
|
||||
return 'map';
|
||||
}
|
||||
};
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const Reach = require('./reach');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (obj, template, options) {
|
||||
|
||||
return template.replace(/{([^}]+)}/g, ($0, chain) => {
|
||||
|
||||
const value = Reach(obj, chain, options);
|
||||
return (value === undefined || value === null ? '' : value);
|
||||
});
|
||||
};
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (...args) {
|
||||
|
||||
try {
|
||||
return JSON.stringify.apply(null, args);
|
||||
}
|
||||
catch (err) {
|
||||
return '[Cannot display object: ' + err.message + ']';
|
||||
}
|
||||
};
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports = module.exports = {
|
||||
array: Array.prototype,
|
||||
buffer: Buffer && Buffer.prototype, // $lab:coverage:ignore$
|
||||
date: Date.prototype,
|
||||
error: Error.prototype,
|
||||
generic: Object.prototype,
|
||||
map: Map.prototype,
|
||||
promise: Promise.prototype,
|
||||
regex: RegExp.prototype,
|
||||
set: Set.prototype,
|
||||
weakMap: WeakMap.prototype,
|
||||
weakSet: WeakSet.prototype
|
||||
};
|
||||
|
||||
|
||||
internals.typeMap = new Map([
|
||||
['[object Error]', exports.error],
|
||||
['[object Map]', exports.map],
|
||||
['[object Promise]', exports.promise],
|
||||
['[object Set]', exports.set],
|
||||
['[object WeakMap]', exports.weakMap],
|
||||
['[object WeakSet]', exports.weakSet]
|
||||
]);
|
||||
|
||||
|
||||
exports.getInternalProto = function (obj) {
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return exports.array;
|
||||
}
|
||||
|
||||
if (Buffer && obj instanceof Buffer) { // $lab:coverage:ignore$
|
||||
return exports.buffer;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return exports.date;
|
||||
}
|
||||
|
||||
if (obj instanceof RegExp) {
|
||||
return exports.regex;
|
||||
}
|
||||
|
||||
if (obj instanceof Error) {
|
||||
return exports.error;
|
||||
}
|
||||
|
||||
const objName = Object.prototype.toString.call(obj);
|
||||
return internals.typeMap.get(objName) || exports.generic;
|
||||
};
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
const Reach = require('./reach');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.keys = function (obj, options = {}) {
|
||||
|
||||
return options.symbols !== false ? Reflect.ownKeys(obj) : Object.getOwnPropertyNames(obj); // Defaults to true
|
||||
};
|
||||
|
||||
|
||||
exports.store = function (source, keys) {
|
||||
|
||||
const storage = new Map();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
const value = Reach(source, key);
|
||||
if (typeof value === 'object' ||
|
||||
typeof value === 'function') {
|
||||
|
||||
storage.set(key, value);
|
||||
internals.reachSet(source, key, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
return storage;
|
||||
};
|
||||
|
||||
|
||||
exports.restore = function (copy, source, storage) {
|
||||
|
||||
for (const [key, value] of storage) {
|
||||
internals.reachSet(copy, key, value);
|
||||
internals.reachSet(source, key, value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.reachSet = function (obj, key, value) {
|
||||
|
||||
const path = Array.isArray(key) ? key : key.split('.');
|
||||
let ref = obj;
|
||||
for (let i = 0; i < path.length; ++i) {
|
||||
const segment = path[i];
|
||||
if (i + 1 === path.length) {
|
||||
ref[segment] = value;
|
||||
}
|
||||
|
||||
ref = ref[segment];
|
||||
}
|
||||
};
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (timeout) {
|
||||
|
||||
return new Promise((resolve) => setTimeout(resolve, timeout));
|
||||
};
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"_from": "@hapi/hoek@8.x.x",
|
||||
"_id": "@hapi/hoek@8.5.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==",
|
||||
"_location": "/simple-oauth2/@hapi/hoek",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "@hapi/hoek@8.x.x",
|
||||
"name": "@hapi/hoek",
|
||||
"escapedName": "@hapi%2fhoek",
|
||||
"scope": "@hapi",
|
||||
"rawSpec": "8.x.x",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "8.x.x"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/simple-oauth2/@hapi/joi",
|
||||
"/simple-oauth2/@hapi/topo"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz",
|
||||
"_shasum": "fde96064ca446dec8c55a8c2f130957b070c6e06",
|
||||
"_spec": "@hapi/hoek@8.x.x",
|
||||
"_where": "C:\\Daten\\Git\\Tumortisch\\node_modules\\simple-oauth2\\node_modules\\@hapi\\joi",
|
||||
"bugs": {
|
||||
"url": "https://github.com/hapijs/hoek/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {},
|
||||
"deprecated": "This version has been deprecated and is no longer supported or maintained",
|
||||
"description": "General purpose node utilities",
|
||||
"devDependencies": {
|
||||
"@hapi/code": "6.x.x",
|
||||
"@hapi/lab": "20.x.x"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"homepage": "https://github.com/hapijs/hoek#readme",
|
||||
"keywords": [
|
||||
"utilities"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "lib/index.js",
|
||||
"name": "@hapi/hoek",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/hapijs/hoek.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -a @hapi/code -t 100 -L -Y",
|
||||
"test-cov-html": "lab -a @hapi/code -t 100 -L -r html -o coverage.html"
|
||||
},
|
||||
"types": "lib/index.d.ts",
|
||||
"version": "8.5.1"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Breaking changes are documented using GitHub issues, see [issues labeled "release notes"](https://github.com/hapijs/joi/issues?q=is%3Aissue+label%3A%22release+notes%22).
|
||||
|
||||
If you want changes of a specific minor or patch release, you can browse the [GitHub milestones](https://github.com/hapijs/joi/milestones?state=closed&direction=asc&sort=due_date).
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
Copyright (c) 2012-2019, Sideway Inc, and project contributors
|
||||
Copyright (c) 2012-2014, Walmart.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
<a href="http://hapijs.com"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# joi
|
||||
|
||||
Object schema description language and validator for JavaScript objects.
|
||||
|
||||
[](https://travis-ci.org/hapijs/joi)
|
||||
|
||||
## Introduction
|
||||
|
||||
Imagine you run facebook and you want visitors to sign up on the website with real names and not something like `l337_p@nda` in the first name field. How would you define the limitations of what can be inputted and validate it against the set rules?
|
||||
|
||||
This is joi, joi allows you to create *blueprints* or *schemas* for JavaScript objects (an object that stores information) to ensure *validation* of key information.
|
||||
|
||||
# Installation
|
||||
|
||||
```cli
|
||||
npm install --save @hapi/joi
|
||||
```
|
||||
|
||||
## API
|
||||
See the detailed [API Reference](https://github.com/hapijs/joi/blob/v15.1.0/API.md).
|
||||
|
||||
## Example
|
||||
|
||||
```javascript
|
||||
const Joi = require('@hapi/joi');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
username: Joi.string().alphanum().min(3).max(30).required(),
|
||||
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
|
||||
access_token: [Joi.string(), Joi.number()],
|
||||
birthyear: Joi.number().integer().min(1900).max(2013),
|
||||
email: Joi.string().email({ minDomainSegments: 2 })
|
||||
}).with('username', 'birthyear').without('password', 'access_token');
|
||||
|
||||
// Return result.
|
||||
const result = Joi.validate({ username: 'abc', birthyear: 1994 }, schema);
|
||||
// result.error === null -> valid
|
||||
|
||||
// You can also pass a callback which will be called synchronously with the validation result.
|
||||
Joi.validate({ username: 'abc', birthyear: 1994 }, schema, function (err, value) { }); // err === null -> valid
|
||||
|
||||
```
|
||||
|
||||
The above schema defines the following constraints:
|
||||
* `username`
|
||||
* a required string
|
||||
* must contain only alphanumeric characters
|
||||
* at least 3 characters long but no more than 30
|
||||
* must be accompanied by `birthyear`
|
||||
* `password`
|
||||
* an optional string
|
||||
* must satisfy the custom regex
|
||||
* cannot appear together with `access_token`
|
||||
* `access_token`
|
||||
* an optional, unconstrained string or number
|
||||
* `birthyear`
|
||||
* an integer between 1900 and 2013
|
||||
* `email`
|
||||
* a valid email address string
|
||||
* must have two domain parts e.g. `example.com`
|
||||
|
||||
## Usage
|
||||
|
||||
Usage is a two steps process. First, a schema is constructed using the provided types and constraints:
|
||||
|
||||
```javascript
|
||||
const schema = {
|
||||
a: Joi.string()
|
||||
};
|
||||
```
|
||||
|
||||
Note that **joi** schema objects are immutable which means every additional rule added (e.g. `.min(5)`) will return a
|
||||
new schema object.
|
||||
|
||||
Second, the value is validated against the defined schema:
|
||||
|
||||
```javascript
|
||||
const {error, value} = Joi.validate({ a: 'a string' }, schema);
|
||||
|
||||
// or
|
||||
|
||||
Joi.validate({ a: 'a string' }, schema, function (error, value) { });
|
||||
```
|
||||
|
||||
If the input is valid, then the `error` will be `null`, otherwise it will be an `Error` object providing more information.
|
||||
|
||||
The schema can be a plain JavaScript object where every key is assigned a **joi** type, or it can be a **joi** type directly:
|
||||
|
||||
```javascript
|
||||
const schema = Joi.string().min(10);
|
||||
```
|
||||
|
||||
If the schema is a **joi** type, the `schema.validate(value, callback)` can be called directly on the type. When passing a non-type schema object,
|
||||
the module converts it internally to an object() type equivalent to:
|
||||
|
||||
```javascript
|
||||
const schema = Joi.object().keys({
|
||||
a: Joi.string()
|
||||
});
|
||||
```
|
||||
|
||||
When validating a schema:
|
||||
|
||||
* Values (or keys in case of objects) are optional by default.
|
||||
|
||||
```javascript
|
||||
Joi.validate(undefined, Joi.string()); // validates fine
|
||||
```
|
||||
|
||||
To disallow this behavior, you can either set the schema as `required()`, or set `presence` to `"required"` when passing `options`:
|
||||
|
||||
```javascript
|
||||
Joi.validate(undefined, Joi.string().required());
|
||||
// or
|
||||
Joi.validate(undefined, Joi.string(), /* options */ { presence: "required" });
|
||||
```
|
||||
|
||||
* Strings are utf-8 encoded by default.
|
||||
* Rules are defined in an additive fashion and evaluated in order, first the inclusive rules, then the exclusive rules.
|
||||
|
||||
## Browsers
|
||||
|
||||
Joi doesn't directly support browsers, but you could use [joi-browser](https://github.com/jeffbski/joi-browser) for an ES5 build of Joi that works in browsers, or as a source of inspiration for your own builds.
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Ref = require('./ref');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.schema = function (Joi, config) {
|
||||
|
||||
if (config !== undefined && config !== null && typeof config === 'object') {
|
||||
|
||||
if (config.isJoi) {
|
||||
return config;
|
||||
}
|
||||
|
||||
if (Array.isArray(config)) {
|
||||
return Joi.alternatives().try(config);
|
||||
}
|
||||
|
||||
if (config instanceof RegExp) {
|
||||
return Joi.string().regex(config);
|
||||
}
|
||||
|
||||
if (config instanceof Date) {
|
||||
return Joi.date().valid(config);
|
||||
}
|
||||
|
||||
return Joi.object().keys(config);
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
return Joi.string().valid(config);
|
||||
}
|
||||
|
||||
if (typeof config === 'number') {
|
||||
return Joi.number().valid(config);
|
||||
}
|
||||
|
||||
if (typeof config === 'boolean') {
|
||||
return Joi.boolean().valid(config);
|
||||
}
|
||||
|
||||
if (Ref.isRef(config)) {
|
||||
return Joi.valid(config);
|
||||
}
|
||||
|
||||
Hoek.assert(config === null, 'Invalid schema content:', config);
|
||||
|
||||
return Joi.valid(null);
|
||||
};
|
||||
|
||||
|
||||
exports.ref = function (id) {
|
||||
|
||||
return Ref.isRef(id) ? id : Ref.create(id);
|
||||
};
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Language = require('./language');
|
||||
|
||||
|
||||
const internals = {
|
||||
annotations: Symbol('joi-annotations')
|
||||
};
|
||||
|
||||
|
||||
internals.stringify = function (value, wrapArrays) {
|
||||
|
||||
const type = typeof value;
|
||||
|
||||
if (value === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value instanceof exports.Err || type === 'function' || type === 'symbol') {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (type === 'object') {
|
||||
if (Array.isArray(value)) {
|
||||
let partial = '';
|
||||
|
||||
for (let i = 0; i < value.length; ++i) {
|
||||
partial = partial + (partial.length ? ', ' : '') + internals.stringify(value[i], wrapArrays);
|
||||
}
|
||||
|
||||
return wrapArrays ? '[' + partial + ']' : partial;
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
return JSON.stringify(value);
|
||||
};
|
||||
|
||||
|
||||
exports.Err = class {
|
||||
|
||||
constructor(type, context, state, options, flags, message, template) {
|
||||
|
||||
this.isJoi = true;
|
||||
this.type = type;
|
||||
this.context = context || {};
|
||||
this.context.key = state.path[state.path.length - 1];
|
||||
this.context.label = state.key;
|
||||
this.path = state.path;
|
||||
this.options = options;
|
||||
this.flags = flags;
|
||||
this.message = message;
|
||||
this.template = template;
|
||||
|
||||
const localized = this.options.language;
|
||||
|
||||
if (this.flags.label) {
|
||||
this.context.label = this.flags.label;
|
||||
}
|
||||
else if (localized && // language can be null for arrays exclusion check
|
||||
(this.context.label === '' ||
|
||||
this.context.label === null)) {
|
||||
this.context.label = localized.root || Language.errors.root;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
||||
if (this.message) {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
let format;
|
||||
|
||||
if (this.template) {
|
||||
format = this.template;
|
||||
}
|
||||
|
||||
const localized = this.options.language;
|
||||
|
||||
format = format || Hoek.reach(localized, this.type) || Hoek.reach(Language.errors, this.type);
|
||||
|
||||
if (format === undefined) {
|
||||
return `Error code "${this.type}" is not defined, your custom type is missing the correct language definition`;
|
||||
}
|
||||
|
||||
let wrapArrays = Hoek.reach(localized, 'messages.wrapArrays');
|
||||
if (typeof wrapArrays !== 'boolean') {
|
||||
wrapArrays = Language.errors.messages.wrapArrays;
|
||||
}
|
||||
|
||||
if (format === null) {
|
||||
const childrenString = internals.stringify(this.context.reason, wrapArrays);
|
||||
if (wrapArrays) {
|
||||
return childrenString.slice(1, -1);
|
||||
}
|
||||
|
||||
return childrenString;
|
||||
}
|
||||
|
||||
const hasKey = /{{!?label}}/.test(format);
|
||||
const skipKey = format.length > 2 && format[0] === '!' && format[1] === '!';
|
||||
|
||||
if (skipKey) {
|
||||
format = format.slice(2);
|
||||
}
|
||||
|
||||
if (!hasKey && !skipKey) {
|
||||
const localizedKey = Hoek.reach(localized, 'key');
|
||||
if (typeof localizedKey === 'string') {
|
||||
format = localizedKey + format;
|
||||
}
|
||||
else {
|
||||
format = Hoek.reach(Language.errors, 'key') + format;
|
||||
}
|
||||
}
|
||||
|
||||
const message = format.replace(/{{(!?)([^}]+)}}/g, ($0, isSecure, name) => {
|
||||
|
||||
const value = Hoek.reach(this.context, name);
|
||||
const normalized = internals.stringify(value, wrapArrays);
|
||||
return (isSecure && this.options.escapeHtml ? Hoek.escapeHtml(normalized) : normalized);
|
||||
});
|
||||
|
||||
this.toString = () => message; // Persist result of last toString call, it won't change
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
exports.create = function (type, context, state, options, flags, message, template) {
|
||||
|
||||
return new exports.Err(type, context, state, options, flags, message, template);
|
||||
};
|
||||
|
||||
|
||||
exports.process = function (errors, object) {
|
||||
|
||||
if (!errors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Construct error
|
||||
|
||||
let message = '';
|
||||
const details = [];
|
||||
|
||||
const processErrors = function (localErrors, parent, overrideMessage) {
|
||||
|
||||
for (let i = 0; i < localErrors.length; ++i) {
|
||||
const item = localErrors[i];
|
||||
|
||||
if (item instanceof Error) {
|
||||
return item;
|
||||
}
|
||||
|
||||
if (item.flags.error && typeof item.flags.error !== 'function') {
|
||||
if (!item.flags.selfError || !item.context.reason) {
|
||||
return item.flags.error;
|
||||
}
|
||||
}
|
||||
|
||||
let itemMessage;
|
||||
if (parent === undefined) {
|
||||
itemMessage = item.toString();
|
||||
message = message + (message ? '. ' : '') + itemMessage;
|
||||
}
|
||||
|
||||
// Do not push intermediate errors, we're only interested in leafs
|
||||
|
||||
if (item.context.reason) {
|
||||
const override = processErrors(item.context.reason, item.path, item.type === 'override' ? item.message : null);
|
||||
if (override) {
|
||||
return override;
|
||||
}
|
||||
}
|
||||
else {
|
||||
details.push({
|
||||
message: overrideMessage || itemMessage || item.toString(),
|
||||
path: item.path,
|
||||
type: item.type,
|
||||
context: item.context
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const override = processErrors(errors);
|
||||
if (override) {
|
||||
return override;
|
||||
}
|
||||
|
||||
const error = new Error(message);
|
||||
error.isJoi = true;
|
||||
error.name = 'ValidationError';
|
||||
error.details = details;
|
||||
error._object = object;
|
||||
error.annotate = internals.annotate;
|
||||
return error;
|
||||
};
|
||||
|
||||
|
||||
// Inspired by json-stringify-safe
|
||||
|
||||
internals.safeStringify = function (obj, spaces) {
|
||||
|
||||
return JSON.stringify(obj, internals.serializer(), spaces);
|
||||
};
|
||||
|
||||
|
||||
internals.serializer = function () {
|
||||
|
||||
const keys = [];
|
||||
const stack = [];
|
||||
|
||||
const cycleReplacer = (key, value) => {
|
||||
|
||||
if (stack[0] === value) {
|
||||
return '[Circular ~]';
|
||||
}
|
||||
|
||||
return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';
|
||||
};
|
||||
|
||||
return function (key, value) {
|
||||
|
||||
if (stack.length > 0) {
|
||||
const thisPos = stack.indexOf(this);
|
||||
if (~thisPos) {
|
||||
stack.length = thisPos + 1;
|
||||
keys.length = thisPos + 1;
|
||||
keys[thisPos] = key;
|
||||
}
|
||||
else {
|
||||
stack.push(this);
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
if (~stack.indexOf(value)) {
|
||||
value = cycleReplacer.call(this, key, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
stack.push(value);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
const annotations = value[internals.annotations];
|
||||
if (annotations) {
|
||||
if (Array.isArray(value)) {
|
||||
const annotated = [];
|
||||
|
||||
for (let i = 0; i < value.length; ++i) {
|
||||
if (annotations.errors[i]) {
|
||||
annotated.push(`_$idx$_${annotations.errors[i].sort().join(', ')}_$end$_`);
|
||||
}
|
||||
|
||||
annotated.push(value[i]);
|
||||
}
|
||||
|
||||
value = annotated;
|
||||
}
|
||||
else {
|
||||
const errorKeys = Object.keys(annotations.errors);
|
||||
for (let i = 0; i < errorKeys.length; ++i) {
|
||||
const errorKey = errorKeys[i];
|
||||
value[`${errorKey}_$key$_${annotations.errors[errorKey].sort().join(', ')}_$end$_`] = value[errorKey];
|
||||
value[errorKey] = undefined;
|
||||
}
|
||||
|
||||
const missingKeys = Object.keys(annotations.missing);
|
||||
for (let i = 0; i < missingKeys.length; ++i) {
|
||||
const missingKey = missingKeys[i];
|
||||
value[`_$miss$_${missingKey}|${annotations.missing[missingKey]}_$end$_`] = '__missing__';
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (value === Infinity || value === -Infinity || Number.isNaN(value) ||
|
||||
typeof value === 'function' || typeof value === 'symbol') {
|
||||
return '[' + value.toString() + ']';
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
internals.annotate = function (stripColorCodes) {
|
||||
|
||||
const redFgEscape = stripColorCodes ? '' : '\u001b[31m';
|
||||
const redBgEscape = stripColorCodes ? '' : '\u001b[41m';
|
||||
const endColor = stripColorCodes ? '' : '\u001b[0m';
|
||||
|
||||
if (typeof this._object !== 'object') {
|
||||
return this.details[0].message;
|
||||
}
|
||||
|
||||
const obj = Hoek.clone(this._object || {});
|
||||
|
||||
for (let i = this.details.length - 1; i >= 0; --i) { // Reverse order to process deepest child first
|
||||
const pos = i + 1;
|
||||
const error = this.details[i];
|
||||
const path = error.path;
|
||||
let ref = obj;
|
||||
for (let j = 0; ; ++j) {
|
||||
const seg = path[j];
|
||||
|
||||
if (ref.isImmutable) {
|
||||
ref = ref.clone(); // joi schemas are not cloned by hoek, we have to take this extra step
|
||||
}
|
||||
|
||||
if (j + 1 < path.length &&
|
||||
ref[seg] &&
|
||||
typeof ref[seg] !== 'string') {
|
||||
|
||||
ref = ref[seg];
|
||||
}
|
||||
else {
|
||||
const refAnnotations = ref[internals.annotations] = ref[internals.annotations] || { errors: {}, missing: {} };
|
||||
const value = ref[seg];
|
||||
const cacheKey = seg || error.context.label;
|
||||
|
||||
if (value !== undefined) {
|
||||
refAnnotations.errors[cacheKey] = refAnnotations.errors[cacheKey] || [];
|
||||
refAnnotations.errors[cacheKey].push(pos);
|
||||
}
|
||||
else {
|
||||
refAnnotations.missing[cacheKey] = pos;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const replacers = {
|
||||
key: /_\$key\$_([, \d]+)_\$end\$_"/g,
|
||||
missing: /"_\$miss\$_([^|]+)\|(\d+)_\$end\$_": "__missing__"/g,
|
||||
arrayIndex: /\s*"_\$idx\$_([, \d]+)_\$end\$_",?\n(.*)/g,
|
||||
specials: /"\[(NaN|Symbol.*|-?Infinity|function.*|\(.*)]"/g
|
||||
};
|
||||
|
||||
let message = internals.safeStringify(obj, 2)
|
||||
.replace(replacers.key, ($0, $1) => `" ${redFgEscape}[${$1}]${endColor}`)
|
||||
.replace(replacers.missing, ($0, $1, $2) => `${redBgEscape}"${$1}"${endColor}${redFgEscape} [${$2}]: -- missing --${endColor}`)
|
||||
.replace(replacers.arrayIndex, ($0, $1, $2) => `\n${$2} ${redFgEscape}[${$1}]${endColor}`)
|
||||
.replace(replacers.specials, ($0, $1) => $1);
|
||||
|
||||
message = `${message}\n${redFgEscape}`;
|
||||
|
||||
for (let i = 0; i < this.details.length; ++i) {
|
||||
const pos = i + 1;
|
||||
message = `${message}\n[${pos}] ${this.details[i].message}`;
|
||||
}
|
||||
|
||||
message = message + endColor;
|
||||
|
||||
return message;
|
||||
};
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('./types/any');
|
||||
const Cast = require('./cast');
|
||||
const Errors = require('./errors');
|
||||
const Lazy = require('./types/lazy');
|
||||
const Ref = require('./ref');
|
||||
|
||||
|
||||
const internals = {
|
||||
alternatives: require('./types/alternatives'),
|
||||
array: require('./types/array'),
|
||||
boolean: require('./types/boolean'),
|
||||
binary: require('./types/binary'),
|
||||
date: require('./types/date'),
|
||||
func: require('./types/func'),
|
||||
number: require('./types/number'),
|
||||
object: require('./types/object'),
|
||||
string: require('./types/string'),
|
||||
symbol: require('./types/symbol')
|
||||
};
|
||||
|
||||
|
||||
internals.callWithDefaults = function (schema, args) {
|
||||
|
||||
Hoek.assert(this, 'Must be invoked on a Joi instance.');
|
||||
|
||||
if (this._defaults) {
|
||||
schema = this._defaults(schema);
|
||||
}
|
||||
|
||||
schema._currentJoi = this;
|
||||
|
||||
return schema._init(...args);
|
||||
};
|
||||
|
||||
|
||||
internals.root = function () {
|
||||
|
||||
const any = new Any();
|
||||
|
||||
const root = any.clone();
|
||||
Any.prototype._currentJoi = root;
|
||||
root._currentJoi = root;
|
||||
root._binds = new Set(['any', 'alternatives', 'alt', 'array', 'bool', 'boolean', 'binary', 'date', 'func', 'number', 'object', 'string', 'symbol', 'validate', 'describe', 'compile', 'assert', 'attempt', 'lazy', 'defaults', 'extend', 'allow', 'valid', 'only', 'equal', 'invalid', 'disallow', 'not', 'required', 'exist', 'optional', 'forbidden', 'strip', 'when', 'empty', 'default']);
|
||||
|
||||
root.any = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.any() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, any, args);
|
||||
};
|
||||
|
||||
root.alternatives = root.alt = function (...args) {
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.alternatives, args);
|
||||
};
|
||||
|
||||
root.array = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.array() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.array, args);
|
||||
};
|
||||
|
||||
root.boolean = root.bool = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.boolean() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.boolean, args);
|
||||
};
|
||||
|
||||
root.binary = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.binary() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.binary, args);
|
||||
};
|
||||
|
||||
root.date = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.date() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.date, args);
|
||||
};
|
||||
|
||||
root.func = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.func() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.func, args);
|
||||
};
|
||||
|
||||
root.number = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.number() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.number, args);
|
||||
};
|
||||
|
||||
root.object = function (...args) {
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.object, args);
|
||||
};
|
||||
|
||||
root.string = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.string() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.string, args);
|
||||
};
|
||||
|
||||
root.symbol = function (...args) {
|
||||
|
||||
Hoek.assert(args.length === 0, 'Joi.symbol() does not allow arguments.');
|
||||
|
||||
return internals.callWithDefaults.call(this, internals.symbol, args);
|
||||
};
|
||||
|
||||
root.ref = function (...args) {
|
||||
|
||||
return Ref.create(...args);
|
||||
};
|
||||
|
||||
root.isRef = function (ref) {
|
||||
|
||||
return Ref.isRef(ref);
|
||||
};
|
||||
|
||||
root.validate = function (value, ...args /*, [schema], [options], callback */) {
|
||||
|
||||
const last = args[args.length - 1];
|
||||
const callback = typeof last === 'function' ? last : null;
|
||||
|
||||
const count = args.length - (callback ? 1 : 0);
|
||||
if (count === 0) {
|
||||
return any.validate(value, callback);
|
||||
}
|
||||
|
||||
const options = count === 2 ? args[1] : undefined;
|
||||
const schema = this.compile(args[0]);
|
||||
|
||||
return schema._validateWithOptions(value, options, callback);
|
||||
};
|
||||
|
||||
root.describe = function (...args) {
|
||||
|
||||
const schema = args.length ? this.compile(args[0]) : any;
|
||||
return schema.describe();
|
||||
};
|
||||
|
||||
root.compile = function (schema) {
|
||||
|
||||
try {
|
||||
return Cast.schema(this, schema);
|
||||
}
|
||||
catch (err) {
|
||||
if (err.hasOwnProperty('path')) {
|
||||
err.message = err.message + '(' + err.path + ')';
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
root.assert = function (value, schema, message) {
|
||||
|
||||
this.attempt(value, schema, message);
|
||||
};
|
||||
|
||||
root.attempt = function (value, schema, message) {
|
||||
|
||||
const result = this.validate(value, schema);
|
||||
const error = result.error;
|
||||
if (error) {
|
||||
if (!message) {
|
||||
if (typeof error.annotate === 'function') {
|
||||
error.message = error.annotate();
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!(message instanceof Error)) {
|
||||
if (typeof error.annotate === 'function') {
|
||||
error.message = `${message} ${error.annotate()}`;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw message;
|
||||
}
|
||||
|
||||
return result.value;
|
||||
};
|
||||
|
||||
root.reach = function (schema, path) {
|
||||
|
||||
Hoek.assert(schema && schema instanceof Any, 'you must provide a joi schema');
|
||||
Hoek.assert(Array.isArray(path) || typeof path === 'string', 'path must be a string or an array of strings');
|
||||
|
||||
const reach = (sourceSchema, schemaPath) => {
|
||||
|
||||
if (!schemaPath.length) {
|
||||
return sourceSchema;
|
||||
}
|
||||
|
||||
const children = sourceSchema._inner.children;
|
||||
if (!children) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = schemaPath.shift();
|
||||
for (let i = 0; i < children.length; ++i) {
|
||||
const child = children[i];
|
||||
if (child.key === key) {
|
||||
return reach(child.schema, schemaPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const schemaPath = typeof path === 'string' ? (path ? path.split('.') : []) : path.slice();
|
||||
|
||||
return reach(schema, schemaPath);
|
||||
};
|
||||
|
||||
root.lazy = function (...args) {
|
||||
|
||||
return internals.callWithDefaults.call(this, Lazy, args);
|
||||
};
|
||||
|
||||
root.defaults = function (fn) {
|
||||
|
||||
Hoek.assert(typeof fn === 'function', 'Defaults must be a function');
|
||||
|
||||
let joi = Object.create(this.any());
|
||||
joi = fn(joi);
|
||||
|
||||
Hoek.assert(joi && joi instanceof this.constructor, 'defaults() must return a schema');
|
||||
|
||||
Object.assign(joi, this, joi.clone()); // Re-add the types from `this` but also keep the settings from joi's potential new defaults
|
||||
|
||||
joi._defaults = (schema) => {
|
||||
|
||||
if (this._defaults) {
|
||||
schema = this._defaults(schema);
|
||||
Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema');
|
||||
}
|
||||
|
||||
schema = fn(schema);
|
||||
Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema');
|
||||
return schema;
|
||||
};
|
||||
|
||||
return joi;
|
||||
};
|
||||
|
||||
root.bind = function () {
|
||||
|
||||
const joi = Object.create(this);
|
||||
|
||||
joi._binds.forEach((bind) => {
|
||||
|
||||
joi[bind] = joi[bind].bind(joi);
|
||||
});
|
||||
|
||||
return joi;
|
||||
};
|
||||
|
||||
root.extend = function (...args) {
|
||||
|
||||
const extensions = Hoek.flatten(args);
|
||||
Hoek.assert(extensions.length > 0, 'You need to provide at least one extension');
|
||||
|
||||
this.assert(extensions, root.extensionsSchema);
|
||||
|
||||
const joi = Object.create(this.any());
|
||||
Object.assign(joi, this);
|
||||
joi._currentJoi = joi;
|
||||
joi._binds = new Set(joi._binds);
|
||||
|
||||
for (let i = 0; i < extensions.length; ++i) {
|
||||
let extension = extensions[i];
|
||||
|
||||
if (typeof extension === 'function') {
|
||||
extension = extension(joi);
|
||||
}
|
||||
|
||||
this.assert(extension, root.extensionSchema);
|
||||
|
||||
const base = (extension.base || this.any()).clone(); // Cloning because we're going to override language afterwards
|
||||
const ctor = base.constructor;
|
||||
const type = class extends ctor { // eslint-disable-line no-loop-func
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
if (extension.base) {
|
||||
Object.assign(this, base);
|
||||
}
|
||||
|
||||
this._type = extension.name;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (extension.language) {
|
||||
const lang = {
|
||||
[extension.name]: extension.language
|
||||
};
|
||||
type.prototype._language = Hoek.applyToDefaults(type.prototype._language || (base._settings && base._settings.language) || {}, lang);
|
||||
}
|
||||
|
||||
|
||||
if (extension.coerce) {
|
||||
type.prototype._coerce = function (value, state, options) {
|
||||
|
||||
if (ctor.prototype._coerce) {
|
||||
const baseRet = ctor.prototype._coerce.call(this, value, state, options);
|
||||
|
||||
if (baseRet.errors) {
|
||||
return baseRet;
|
||||
}
|
||||
|
||||
value = baseRet.value;
|
||||
}
|
||||
|
||||
const ret = extension.coerce.call(this, value, state, options);
|
||||
if (ret instanceof Errors.Err) {
|
||||
return { value, errors: ret };
|
||||
}
|
||||
|
||||
return { value: ret };
|
||||
};
|
||||
}
|
||||
|
||||
if (extension.pre) {
|
||||
type.prototype._base = function (value, state, options) {
|
||||
|
||||
if (ctor.prototype._base) {
|
||||
const baseRet = ctor.prototype._base.call(this, value, state, options);
|
||||
|
||||
if (baseRet.errors) {
|
||||
return baseRet;
|
||||
}
|
||||
|
||||
value = baseRet.value;
|
||||
}
|
||||
|
||||
const ret = extension.pre.call(this, value, state, options);
|
||||
if (ret instanceof Errors.Err) {
|
||||
return { value, errors: ret };
|
||||
}
|
||||
|
||||
return { value: ret };
|
||||
};
|
||||
}
|
||||
|
||||
if (extension.rules) {
|
||||
for (let j = 0; j < extension.rules.length; ++j) {
|
||||
const rule = extension.rules[j];
|
||||
const ruleArgs = rule.params ?
|
||||
(rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) :
|
||||
[];
|
||||
const validateArgs = rule.params ? Cast.schema(this, rule.params) : null;
|
||||
|
||||
type.prototype[rule.name] = function (...rArgs) { // eslint-disable-line no-loop-func
|
||||
|
||||
if (rArgs.length > ruleArgs.length) {
|
||||
throw new Error('Unexpected number of arguments');
|
||||
}
|
||||
|
||||
let hasRef = false;
|
||||
let arg = {};
|
||||
|
||||
for (let k = 0; k < ruleArgs.length; ++k) {
|
||||
arg[ruleArgs[k]] = rArgs[k];
|
||||
if (!hasRef && Ref.isRef(rArgs[k])) {
|
||||
hasRef = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (validateArgs) {
|
||||
arg = joi.attempt(arg, validateArgs);
|
||||
}
|
||||
|
||||
let schema;
|
||||
if (rule.validate && !rule.setup) {
|
||||
const validate = function (value, state, options) {
|
||||
|
||||
return rule.validate.call(this, arg, value, state, options);
|
||||
};
|
||||
|
||||
schema = this._test(rule.name, arg, validate, {
|
||||
description: rule.description,
|
||||
hasRef
|
||||
});
|
||||
}
|
||||
else {
|
||||
schema = this.clone();
|
||||
}
|
||||
|
||||
if (rule.setup) {
|
||||
const newSchema = rule.setup.call(schema, arg);
|
||||
if (newSchema !== undefined) {
|
||||
Hoek.assert(newSchema instanceof Any, `Setup of extension Joi.${this._type}().${rule.name}() must return undefined or a Joi object`);
|
||||
schema = newSchema;
|
||||
}
|
||||
|
||||
if (rule.validate) {
|
||||
const validate = function (value, state, options) {
|
||||
|
||||
return rule.validate.call(this, arg, value, state, options);
|
||||
};
|
||||
|
||||
schema = schema._test(rule.name, arg, validate, {
|
||||
description: rule.description,
|
||||
hasRef
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return schema;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (extension.describe) {
|
||||
type.prototype.describe = function () {
|
||||
|
||||
const description = ctor.prototype.describe.call(this);
|
||||
return extension.describe.call(this, description);
|
||||
};
|
||||
}
|
||||
|
||||
const instance = new type();
|
||||
joi[extension.name] = function (...extArgs) {
|
||||
|
||||
return internals.callWithDefaults.call(this, instance, extArgs);
|
||||
};
|
||||
|
||||
joi._binds.add(extension.name);
|
||||
}
|
||||
|
||||
return joi;
|
||||
};
|
||||
|
||||
root.extensionSchema = internals.object.keys({
|
||||
base: internals.object.type(Any, 'Joi object'),
|
||||
name: internals.string.required(),
|
||||
coerce: internals.func.arity(3),
|
||||
pre: internals.func.arity(3),
|
||||
language: internals.object,
|
||||
describe: internals.func.arity(1),
|
||||
rules: internals.array.items(internals.object.keys({
|
||||
name: internals.string.required(),
|
||||
setup: internals.func.arity(1),
|
||||
validate: internals.func.arity(4),
|
||||
params: [
|
||||
internals.object.pattern(/.*/, internals.object.type(Any, 'Joi object')),
|
||||
internals.object.type(internals.object.constructor, 'Joi object')
|
||||
],
|
||||
description: [internals.string, internals.func.arity(1)]
|
||||
}).or('setup', 'validate'))
|
||||
}).strict();
|
||||
|
||||
root.extensionsSchema = internals.array.items([internals.object, internals.func.arity(1)]).strict();
|
||||
|
||||
root.version = require('../package.json').version;
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
|
||||
module.exports = internals.root();
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.errors = {
|
||||
root: 'value',
|
||||
key: '"{{!label}}" ',
|
||||
messages: {
|
||||
wrapArrays: true
|
||||
},
|
||||
any: {
|
||||
unknown: 'is not allowed',
|
||||
invalid: 'contains an invalid value',
|
||||
empty: 'is not allowed to be empty',
|
||||
required: 'is required',
|
||||
allowOnly: 'must be one of {{valids}}',
|
||||
default: 'threw an error when running default method'
|
||||
},
|
||||
alternatives: {
|
||||
base: 'not matching any of the allowed alternatives',
|
||||
child: null
|
||||
},
|
||||
array: {
|
||||
base: 'must be an array',
|
||||
includes: 'at position {{pos}} does not match any of the allowed types',
|
||||
includesSingle: 'single value of "{{!label}}" does not match any of the allowed types',
|
||||
includesOne: 'at position {{pos}} fails because {{reason}}',
|
||||
includesOneSingle: 'single value of "{{!label}}" fails because {{reason}}',
|
||||
includesRequiredUnknowns: 'does not contain {{unknownMisses}} required value(s)',
|
||||
includesRequiredKnowns: 'does not contain {{knownMisses}}',
|
||||
includesRequiredBoth: 'does not contain {{knownMisses}} and {{unknownMisses}} other required value(s)',
|
||||
excludes: 'at position {{pos}} contains an excluded value',
|
||||
excludesSingle: 'single value of "{{!label}}" contains an excluded value',
|
||||
hasKnown: 'does not contain at least one required match for type "{{!patternLabel}}"',
|
||||
hasUnknown: 'does not contain at least one required match',
|
||||
min: 'must contain at least {{limit}} items',
|
||||
max: 'must contain less than or equal to {{limit}} items',
|
||||
length: 'must contain {{limit}} items',
|
||||
ordered: 'at position {{pos}} fails because {{reason}}',
|
||||
orderedLength: 'at position {{pos}} fails because array must contain at most {{limit}} items',
|
||||
ref: 'references "{{ref}}" which is not a positive integer',
|
||||
sparse: 'must not be a sparse array',
|
||||
unique: 'position {{pos}} contains a duplicate value'
|
||||
},
|
||||
boolean: {
|
||||
base: 'must be a boolean'
|
||||
},
|
||||
binary: {
|
||||
base: 'must be a buffer or a string',
|
||||
min: 'must be at least {{limit}} bytes',
|
||||
max: 'must be less than or equal to {{limit}} bytes',
|
||||
length: 'must be {{limit}} bytes'
|
||||
},
|
||||
date: {
|
||||
base: 'must be a number of milliseconds or valid date string',
|
||||
strict: 'must be a valid date',
|
||||
min: 'must be larger than or equal to "{{limit}}"',
|
||||
max: 'must be less than or equal to "{{limit}}"',
|
||||
less: 'must be less than "{{limit}}"',
|
||||
greater: 'must be greater than "{{limit}}"',
|
||||
isoDate: 'must be a valid ISO 8601 date',
|
||||
timestamp: {
|
||||
javascript: 'must be a valid timestamp or number of milliseconds',
|
||||
unix: 'must be a valid timestamp or number of seconds'
|
||||
},
|
||||
ref: 'references "{{ref}}" which is not a date'
|
||||
},
|
||||
function: {
|
||||
base: 'must be a Function',
|
||||
arity: 'must have an arity of {{n}}',
|
||||
minArity: 'must have an arity greater or equal to {{n}}',
|
||||
maxArity: 'must have an arity lesser or equal to {{n}}',
|
||||
ref: 'must be a Joi reference',
|
||||
class: 'must be a class'
|
||||
},
|
||||
lazy: {
|
||||
base: '!!schema error: lazy schema must be set',
|
||||
schema: '!!schema error: lazy schema function must return a schema'
|
||||
},
|
||||
object: {
|
||||
base: 'must be an object',
|
||||
child: '!!child "{{!child}}" fails because {{reason}}',
|
||||
min: 'must have at least {{limit}} children',
|
||||
max: 'must have less than or equal to {{limit}} children',
|
||||
length: 'must have {{limit}} children',
|
||||
allowUnknown: '!!"{{!child}}" is not allowed',
|
||||
with: '!!"{{mainWithLabel}}" missing required peer "{{peerWithLabel}}"',
|
||||
without: '!!"{{mainWithLabel}}" conflict with forbidden peer "{{peerWithLabel}}"',
|
||||
missing: 'must contain at least one of {{peersWithLabels}}',
|
||||
xor: 'contains a conflict between exclusive peers {{peersWithLabels}}',
|
||||
oxor: 'contains a conflict between optional exclusive peers {{peersWithLabels}}',
|
||||
and: 'contains {{presentWithLabels}} without its required peers {{missingWithLabels}}',
|
||||
nand: '!!"{{mainWithLabel}}" must not exist simultaneously with {{peersWithLabels}}',
|
||||
assert: '!!"{{ref}}" validation failed because "{{ref}}" failed to {{message}}',
|
||||
rename: {
|
||||
multiple: 'cannot rename child "{{from}}" because multiple renames are disabled and another key was already renamed to "{{to}}"',
|
||||
override: 'cannot rename child "{{from}}" because override is disabled and target "{{to}}" exists',
|
||||
regex: {
|
||||
multiple: 'cannot rename children {{from}} because multiple renames are disabled and another key was already renamed to "{{to}}"',
|
||||
override: 'cannot rename children {{from}} because override is disabled and target "{{to}}" exists'
|
||||
}
|
||||
},
|
||||
type: 'must be an instance of "{{type}}"',
|
||||
schema: 'must be a Joi instance'
|
||||
},
|
||||
number: {
|
||||
base: 'must be a number',
|
||||
unsafe: 'must be a safe number',
|
||||
min: 'must be larger than or equal to {{limit}}',
|
||||
max: 'must be less than or equal to {{limit}}',
|
||||
less: 'must be less than {{limit}}',
|
||||
greater: 'must be greater than {{limit}}',
|
||||
integer: 'must be an integer',
|
||||
negative: 'must be a negative number',
|
||||
positive: 'must be a positive number',
|
||||
precision: 'must have no more than {{limit}} decimal places',
|
||||
ref: 'references "{{ref}}" which is not a number',
|
||||
multiple: 'must be a multiple of {{multiple}}',
|
||||
port: 'must be a valid port'
|
||||
},
|
||||
string: {
|
||||
base: 'must be a string',
|
||||
min: 'length must be at least {{limit}} characters long',
|
||||
max: 'length must be less than or equal to {{limit}} characters long',
|
||||
length: 'length must be {{limit}} characters long',
|
||||
alphanum: 'must only contain alpha-numeric characters',
|
||||
token: 'must only contain alpha-numeric and underscore characters',
|
||||
regex: {
|
||||
base: 'with value "{{!value}}" fails to match the required pattern: {{pattern}}',
|
||||
name: 'with value "{{!value}}" fails to match the {{name}} pattern',
|
||||
invert: {
|
||||
base: 'with value "{{!value}}" matches the inverted pattern: {{pattern}}',
|
||||
name: 'with value "{{!value}}" matches the inverted {{name}} pattern'
|
||||
}
|
||||
},
|
||||
email: 'must be a valid email',
|
||||
uri: 'must be a valid uri',
|
||||
uriRelativeOnly: 'must be a valid relative uri',
|
||||
uriCustomScheme: 'must be a valid uri with a scheme matching the {{scheme}} pattern',
|
||||
isoDate: 'must be a valid ISO 8601 date',
|
||||
guid: 'must be a valid GUID',
|
||||
hex: 'must only contain hexadecimal characters',
|
||||
hexAlign: 'hex decoded representation must be byte aligned',
|
||||
base64: 'must be a valid base64 string',
|
||||
dataUri: 'must be a valid dataUri string',
|
||||
hostname: 'must be a valid hostname',
|
||||
normalize: 'must be unicode normalized in the {{form}} form',
|
||||
lowercase: 'must only contain lowercase characters',
|
||||
uppercase: 'must only contain uppercase characters',
|
||||
trim: 'must not have leading or trailing whitespace',
|
||||
creditCard: 'must be a credit card',
|
||||
ref: 'references "{{ref}}" which is not a number',
|
||||
ip: 'must be a valid ip address with a {{cidr}} CIDR',
|
||||
ipVersion: 'must be a valid ip address of one of the following versions {{version}} with a {{cidr}} CIDR'
|
||||
},
|
||||
symbol: {
|
||||
base: 'must be a symbol',
|
||||
map: 'must be one of {{map}}'
|
||||
}
|
||||
};
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.create = function (key, options) {
|
||||
|
||||
Hoek.assert(typeof key === 'string', 'Invalid reference key:', key);
|
||||
|
||||
const settings = Hoek.clone(options); // options can be reused and modified
|
||||
|
||||
const ref = function (value, validationOptions) {
|
||||
|
||||
return Hoek.reach(ref.isContext ? validationOptions.context : value, ref.key, settings);
|
||||
};
|
||||
|
||||
ref.isContext = (key[0] === ((settings && settings.contextPrefix) || '$'));
|
||||
ref.key = (ref.isContext ? key.slice(1) : key);
|
||||
ref.path = ref.key.split((settings && settings.separator) || '.');
|
||||
ref.depth = ref.path.length;
|
||||
ref.root = ref.path[0];
|
||||
ref.isJoi = true;
|
||||
|
||||
ref.toString = function () {
|
||||
|
||||
return (ref.isContext ? 'context:' : 'ref:') + ref.key;
|
||||
};
|
||||
|
||||
return ref;
|
||||
};
|
||||
|
||||
|
||||
exports.isRef = function (ref) {
|
||||
|
||||
return typeof ref === 'function' && ref.isJoi;
|
||||
};
|
||||
|
||||
|
||||
exports.push = function (array, ref) {
|
||||
|
||||
if (exports.isRef(ref) &&
|
||||
!ref.isContext) {
|
||||
|
||||
array.push(ref.root);
|
||||
}
|
||||
};
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const Joi = require('./index');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.options = Joi.object({
|
||||
abortEarly: Joi.boolean(),
|
||||
convert: Joi.boolean(),
|
||||
allowUnknown: Joi.boolean(),
|
||||
skipFunctions: Joi.boolean(),
|
||||
stripUnknown: [Joi.boolean(), Joi.object({ arrays: Joi.boolean(), objects: Joi.boolean() }).or('arrays', 'objects')],
|
||||
language: Joi.object(),
|
||||
presence: Joi.string().only('required', 'optional', 'forbidden', 'ignore'),
|
||||
context: Joi.object(),
|
||||
noDefaults: Joi.boolean(),
|
||||
escapeHtml: Joi.boolean()
|
||||
}).strict();
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
'use strict';
|
||||
|
||||
const Ref = require('./ref');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.extendedCheckForValue = function (value, insensitive) {
|
||||
|
||||
const valueType = typeof value;
|
||||
|
||||
if (valueType === 'object') {
|
||||
if (value instanceof Date) {
|
||||
return (item) => {
|
||||
|
||||
return item instanceof Date && value.getTime() === item.getTime();
|
||||
};
|
||||
}
|
||||
|
||||
if (Buffer.isBuffer(value)) {
|
||||
return (item) => {
|
||||
|
||||
return Buffer.isBuffer(item) && value.length === item.length && value.toString('binary') === item.toString('binary');
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (insensitive && valueType === 'string') {
|
||||
const lowercaseValue = value.toLowerCase();
|
||||
return (item) => {
|
||||
|
||||
return typeof item === 'string' && lowercaseValue === item.toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
module.exports = class InternalSet {
|
||||
|
||||
constructor(from) {
|
||||
|
||||
this._set = new Set(from);
|
||||
this._hasRef = false;
|
||||
}
|
||||
|
||||
add(value, refs) {
|
||||
|
||||
const isRef = Ref.isRef(value);
|
||||
if (!isRef && this.has(value, null, null, false)) {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (refs !== undefined) { // If it's a merge, we don't have any refs
|
||||
Ref.push(refs, value);
|
||||
}
|
||||
|
||||
this._set.add(value);
|
||||
|
||||
this._hasRef |= isRef;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
merge(add, remove) {
|
||||
|
||||
for (const item of add._set) {
|
||||
this.add(item);
|
||||
}
|
||||
|
||||
for (const item of remove._set) {
|
||||
this.remove(item);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
remove(value) {
|
||||
|
||||
this._set.delete(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
has(value, state, options, insensitive) {
|
||||
|
||||
return !!this.get(value, state, options, insensitive);
|
||||
}
|
||||
|
||||
get(value, state, options, insensitive) {
|
||||
|
||||
if (!this._set.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasValue = this._set.has(value);
|
||||
if (hasValue) {
|
||||
return { value };
|
||||
}
|
||||
|
||||
const extendedCheck = internals.extendedCheckForValue(value, insensitive);
|
||||
if (!extendedCheck) {
|
||||
if (state && this._hasRef) {
|
||||
for (let item of this._set) {
|
||||
if (Ref.isRef(item)) {
|
||||
item = [].concat(item(state.reference || state.parent, options));
|
||||
const found = item.indexOf(value);
|
||||
if (found >= 0) {
|
||||
return { value: item[found] };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._has(value, state, options, extendedCheck);
|
||||
}
|
||||
|
||||
_has(value, state, options, check) {
|
||||
|
||||
const checkRef = !!(state && this._hasRef);
|
||||
|
||||
const isReallyEqual = function (item) {
|
||||
|
||||
if (value === item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return check(item);
|
||||
};
|
||||
|
||||
for (let item of this._set) {
|
||||
if (checkRef && Ref.isRef(item)) { // Only resolve references if there is a state, otherwise it's a merge
|
||||
item = item(state.reference || state.parent, options);
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
const found = item.findIndex(isReallyEqual);
|
||||
if (found >= 0) {
|
||||
return {
|
||||
value: item[found]
|
||||
};
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isReallyEqual(item)) {
|
||||
return {
|
||||
value: item
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
values(options) {
|
||||
|
||||
if (options && options.stripUndefined) {
|
||||
const values = [];
|
||||
|
||||
for (const item of this._set) {
|
||||
if (item !== undefined) {
|
||||
values.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
return Array.from(this._set);
|
||||
}
|
||||
|
||||
slice() {
|
||||
|
||||
const set = new InternalSet(this._set);
|
||||
set._hasRef = this._hasRef;
|
||||
return set;
|
||||
}
|
||||
|
||||
concat(source) {
|
||||
|
||||
const set = new InternalSet([...this._set, ...source._set]);
|
||||
set._hasRef = !!(this._hasRef | source._hasRef);
|
||||
return set;
|
||||
}
|
||||
};
|
||||
Generated
Vendored
+215
@@ -0,0 +1,215 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
const Cast = require('../../cast');
|
||||
const Ref = require('../../ref');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.Alternatives = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'alternatives';
|
||||
this._invalids.remove(null);
|
||||
this._inner.matches = [];
|
||||
}
|
||||
|
||||
_init(...args) {
|
||||
|
||||
return args.length ? this.try(...args) : this;
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
const errors = [];
|
||||
const il = this._inner.matches.length;
|
||||
const baseType = this._baseType;
|
||||
|
||||
for (let i = 0; i < il; ++i) {
|
||||
const item = this._inner.matches[i];
|
||||
if (!item.schema) {
|
||||
const schema = item.peek || item.is;
|
||||
const input = item.is ? item.ref(state.reference || state.parent, options) : value;
|
||||
const failed = schema._validate(input, null, options, state.parent).errors;
|
||||
|
||||
if (failed) {
|
||||
if (item.otherwise) {
|
||||
return item.otherwise._validate(value, state, options);
|
||||
}
|
||||
}
|
||||
else if (item.then) {
|
||||
return item.then._validate(value, state, options);
|
||||
}
|
||||
|
||||
if (i === (il - 1) && baseType) {
|
||||
return baseType._validate(value, state, options);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = item.schema._validate(value, state, options);
|
||||
if (!result.errors) { // Found a valid match
|
||||
return result;
|
||||
}
|
||||
|
||||
errors.push(...result.errors);
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
return { errors: this.createError('alternatives.child', { reason: errors }, state, options) };
|
||||
}
|
||||
|
||||
return { errors: this.createError('alternatives.base', null, state, options) };
|
||||
}
|
||||
|
||||
try(...schemas) {
|
||||
|
||||
schemas = Hoek.flatten(schemas);
|
||||
Hoek.assert(schemas.length, 'Cannot add other alternatives without at least one schema');
|
||||
|
||||
const obj = this.clone();
|
||||
|
||||
for (let i = 0; i < schemas.length; ++i) {
|
||||
const cast = Cast.schema(this._currentJoi, schemas[i]);
|
||||
if (cast._refs.length) {
|
||||
obj._refs.push(...cast._refs);
|
||||
}
|
||||
|
||||
obj._inner.matches.push({ schema: cast });
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
when(condition, options) {
|
||||
|
||||
let schemaCondition = false;
|
||||
Hoek.assert(Ref.isRef(condition) || typeof condition === 'string' || (schemaCondition = condition instanceof Any), 'Invalid condition:', condition);
|
||||
Hoek.assert(options, 'Missing options');
|
||||
Hoek.assert(typeof options === 'object', 'Invalid options');
|
||||
if (schemaCondition) {
|
||||
Hoek.assert(!options.hasOwnProperty('is'), '"is" can not be used with a schema condition');
|
||||
}
|
||||
else {
|
||||
Hoek.assert(options.hasOwnProperty('is'), 'Missing "is" directive');
|
||||
}
|
||||
|
||||
Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');
|
||||
|
||||
const obj = this.clone();
|
||||
let is;
|
||||
if (!schemaCondition) {
|
||||
is = Cast.schema(this._currentJoi, options.is);
|
||||
|
||||
if (options.is === null || !(Ref.isRef(options.is) || options.is instanceof Any)) {
|
||||
|
||||
// Only apply required if this wasn't already a schema or a ref, we'll suppose people know what they're doing
|
||||
is = is.required();
|
||||
}
|
||||
}
|
||||
|
||||
const item = {
|
||||
ref: schemaCondition ? null : Cast.ref(condition),
|
||||
peek: schemaCondition ? condition : null,
|
||||
is,
|
||||
then: options.then !== undefined ? Cast.schema(this._currentJoi, options.then) : undefined,
|
||||
otherwise: options.otherwise !== undefined ? Cast.schema(this._currentJoi, options.otherwise) : undefined
|
||||
};
|
||||
|
||||
if (obj._baseType) {
|
||||
|
||||
item.then = item.then && obj._baseType.concat(item.then);
|
||||
item.otherwise = item.otherwise && obj._baseType.concat(item.otherwise);
|
||||
}
|
||||
|
||||
if (!schemaCondition) {
|
||||
Ref.push(obj._refs, item.ref);
|
||||
obj._refs.push(...item.is._refs);
|
||||
}
|
||||
|
||||
if (item.then && item.then._refs.length) {
|
||||
obj._refs.push(...item.then._refs);
|
||||
}
|
||||
|
||||
if (item.otherwise && item.otherwise._refs.length) {
|
||||
obj._refs.push(...item.otherwise._refs);
|
||||
}
|
||||
|
||||
obj._inner.matches.push(item);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
label(name) {
|
||||
|
||||
const obj = super.label(name);
|
||||
obj._inner.matches = obj._inner.matches.map((match) => {
|
||||
|
||||
if (match.schema) {
|
||||
return { schema: match.schema.label(name) };
|
||||
}
|
||||
|
||||
match = Object.assign({}, match);
|
||||
if (match.then) {
|
||||
match.then = match.then.label(name);
|
||||
}
|
||||
|
||||
if (match.otherwise) {
|
||||
match.otherwise = match.otherwise.label(name);
|
||||
}
|
||||
|
||||
return match;
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
describe() {
|
||||
|
||||
const description = super.describe();
|
||||
const alternatives = [];
|
||||
for (let i = 0; i < this._inner.matches.length; ++i) {
|
||||
const item = this._inner.matches[i];
|
||||
if (item.schema) {
|
||||
|
||||
// try()
|
||||
|
||||
alternatives.push(item.schema.describe());
|
||||
}
|
||||
else {
|
||||
|
||||
// when()
|
||||
|
||||
const when = item.is ? {
|
||||
ref: item.ref.toString(),
|
||||
is: item.is.describe()
|
||||
} : {
|
||||
peek: item.peek.describe()
|
||||
};
|
||||
|
||||
if (item.then) {
|
||||
when.then = item.then.describe();
|
||||
}
|
||||
|
||||
if (item.otherwise) {
|
||||
when.otherwise = item.otherwise.describe();
|
||||
}
|
||||
|
||||
alternatives.push(when);
|
||||
}
|
||||
}
|
||||
|
||||
description.alternatives = alternatives;
|
||||
return description;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = new internals.Alternatives();
|
||||
Generated
Vendored
+984
@@ -0,0 +1,984 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Cast = require('../../cast');
|
||||
const Settings = require('./settings');
|
||||
const Ref = require('../../ref');
|
||||
const Errors = require('../../errors');
|
||||
const State = require('../state');
|
||||
const Symbols = require('../symbols');
|
||||
|
||||
const Pkg = require('../../../package.json');
|
||||
|
||||
let Alternatives = null; // Delay-loaded to prevent circular dependencies
|
||||
let Schemas = null;
|
||||
|
||||
|
||||
const internals = {
|
||||
Set: require('../../set'),
|
||||
symbol: Symbol.for('@hapi/joi/schema')
|
||||
};
|
||||
|
||||
|
||||
internals.defaults = {
|
||||
abortEarly: true,
|
||||
convert: true,
|
||||
allowUnknown: false,
|
||||
skipFunctions: false,
|
||||
stripUnknown: false,
|
||||
language: {},
|
||||
presence: 'optional',
|
||||
noDefaults: false,
|
||||
escapeHtml: false
|
||||
|
||||
// context: null
|
||||
};
|
||||
|
||||
|
||||
module.exports = internals.Any = class {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.isJoi = true;
|
||||
this._type = 'any';
|
||||
this._settings = null;
|
||||
this._valids = new internals.Set();
|
||||
this._invalids = new internals.Set();
|
||||
this._tests = [];
|
||||
this._refs = [];
|
||||
this._flags = {
|
||||
/*
|
||||
presence: 'optional', // optional, required, forbidden, ignore
|
||||
allowOnly: false,
|
||||
allowUnknown: undefined,
|
||||
default: undefined,
|
||||
forbidden: false,
|
||||
encoding: undefined,
|
||||
insensitive: false,
|
||||
trim: false,
|
||||
normalize: undefined, // NFC, NFD, NFKC, NFKD
|
||||
case: undefined, // upper, lower
|
||||
empty: undefined,
|
||||
func: false,
|
||||
raw: false
|
||||
*/
|
||||
};
|
||||
|
||||
this._description = null;
|
||||
this._unit = null;
|
||||
this._notes = [];
|
||||
this._tags = [];
|
||||
this._examples = [];
|
||||
this._meta = [];
|
||||
|
||||
this._inner = {}; // Hash of arrays of immutable objects
|
||||
}
|
||||
|
||||
_init() {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
get schemaType() {
|
||||
|
||||
return this._type;
|
||||
}
|
||||
|
||||
createError(type, context, state, options, flags = this._flags) {
|
||||
|
||||
return Errors.create(type, context, state, options, flags);
|
||||
}
|
||||
|
||||
createOverrideError(type, context, state, options, message, template) {
|
||||
|
||||
return Errors.create(type, context, state, options, this._flags, message, template);
|
||||
}
|
||||
|
||||
checkOptions(options) {
|
||||
|
||||
Schemas = Schemas || require('../../schemas');
|
||||
|
||||
const result = Schemas.options.validate(options);
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(result.error.details[0].message);
|
||||
}
|
||||
}
|
||||
|
||||
clone() {
|
||||
|
||||
const obj = Object.create(Object.getPrototypeOf(this));
|
||||
|
||||
obj.isJoi = true;
|
||||
obj._currentJoi = this._currentJoi;
|
||||
obj._type = this._type;
|
||||
obj._settings = this._settings;
|
||||
obj._baseType = this._baseType;
|
||||
obj._valids = this._valids.slice();
|
||||
obj._invalids = this._invalids.slice();
|
||||
obj._tests = this._tests.slice();
|
||||
obj._refs = this._refs.slice();
|
||||
obj._flags = Hoek.clone(this._flags);
|
||||
|
||||
obj._description = this._description;
|
||||
obj._unit = this._unit;
|
||||
obj._notes = this._notes.slice();
|
||||
obj._tags = this._tags.slice();
|
||||
obj._examples = this._examples.slice();
|
||||
obj._meta = this._meta.slice();
|
||||
|
||||
obj._inner = {};
|
||||
const inners = Object.keys(this._inner);
|
||||
for (let i = 0; i < inners.length; ++i) {
|
||||
const key = inners[i];
|
||||
obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
concat(schema) {
|
||||
|
||||
Hoek.assert(schema instanceof internals.Any, 'Invalid schema object');
|
||||
Hoek.assert(this._type === 'any' || schema._type === 'any' || schema._type === this._type, 'Cannot merge type', this._type, 'with another type:', schema._type);
|
||||
|
||||
let obj = this.clone();
|
||||
|
||||
if (this._type === 'any' && schema._type !== 'any') {
|
||||
|
||||
// Reset values as if we were "this"
|
||||
const tmpObj = schema.clone();
|
||||
const keysToRestore = ['_settings', '_valids', '_invalids', '_tests', '_refs', '_flags', '_description', '_unit',
|
||||
'_notes', '_tags', '_examples', '_meta', '_inner'];
|
||||
|
||||
for (let i = 0; i < keysToRestore.length; ++i) {
|
||||
tmpObj[keysToRestore[i]] = obj[keysToRestore[i]];
|
||||
}
|
||||
|
||||
obj = tmpObj;
|
||||
}
|
||||
|
||||
obj._settings = obj._settings ? Settings.concat(obj._settings, schema._settings) : schema._settings;
|
||||
obj._valids.merge(schema._valids, schema._invalids);
|
||||
obj._invalids.merge(schema._invalids, schema._valids);
|
||||
obj._tests.push(...schema._tests);
|
||||
obj._refs.push(...schema._refs);
|
||||
if (obj._flags.empty && schema._flags.empty) {
|
||||
obj._flags.empty = obj._flags.empty.concat(schema._flags.empty);
|
||||
const flags = Object.assign({}, schema._flags);
|
||||
delete flags.empty;
|
||||
Hoek.merge(obj._flags, flags);
|
||||
}
|
||||
else if (schema._flags.empty) {
|
||||
obj._flags.empty = schema._flags.empty;
|
||||
const flags = Object.assign({}, schema._flags);
|
||||
delete flags.empty;
|
||||
Hoek.merge(obj._flags, flags);
|
||||
}
|
||||
else {
|
||||
Hoek.merge(obj._flags, schema._flags);
|
||||
}
|
||||
|
||||
obj._description = schema._description || obj._description;
|
||||
obj._unit = schema._unit || obj._unit;
|
||||
obj._notes.push(...schema._notes);
|
||||
obj._tags.push(...schema._tags);
|
||||
obj._examples.push(...schema._examples);
|
||||
obj._meta.push(...schema._meta);
|
||||
|
||||
const inners = Object.keys(schema._inner);
|
||||
const isObject = obj._type === 'object';
|
||||
for (let i = 0; i < inners.length; ++i) {
|
||||
const key = inners[i];
|
||||
const source = schema._inner[key];
|
||||
if (source) {
|
||||
const target = obj._inner[key];
|
||||
if (target) {
|
||||
if (isObject && key === 'children') {
|
||||
const keys = {};
|
||||
|
||||
for (let j = 0; j < target.length; ++j) {
|
||||
keys[target[j].key] = j;
|
||||
}
|
||||
|
||||
for (let j = 0; j < source.length; ++j) {
|
||||
const sourceKey = source[j].key;
|
||||
if (keys[sourceKey] >= 0) {
|
||||
target[keys[sourceKey]] = {
|
||||
key: sourceKey,
|
||||
schema: target[keys[sourceKey]].schema.concat(source[j].schema)
|
||||
};
|
||||
}
|
||||
else {
|
||||
target.push(source[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
obj._inner[key] = obj._inner[key].concat(source);
|
||||
}
|
||||
}
|
||||
else {
|
||||
obj._inner[key] = source.slice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
_test(name, arg, func, options) {
|
||||
|
||||
const obj = this.clone();
|
||||
obj._tests.push({ func, name, arg, options });
|
||||
return obj;
|
||||
}
|
||||
|
||||
_testUnique(name, arg, func, options) {
|
||||
|
||||
const obj = this.clone();
|
||||
obj._tests = obj._tests.filter((test) => test.name !== name);
|
||||
obj._tests.push({ func, name, arg, options });
|
||||
return obj;
|
||||
}
|
||||
|
||||
options(options) {
|
||||
|
||||
Hoek.assert(!options.context, 'Cannot override context');
|
||||
this.checkOptions(options);
|
||||
|
||||
const obj = this.clone();
|
||||
obj._settings = Settings.concat(obj._settings, options);
|
||||
return obj;
|
||||
}
|
||||
|
||||
strict(isStrict) {
|
||||
|
||||
const obj = this.clone();
|
||||
|
||||
const convert = isStrict === undefined ? false : !isStrict;
|
||||
obj._settings = Settings.concat(obj._settings, { convert });
|
||||
return obj;
|
||||
}
|
||||
|
||||
raw(isRaw) {
|
||||
|
||||
const value = isRaw === undefined ? true : isRaw;
|
||||
|
||||
if (this._flags.raw === value) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.raw = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
error(err, options = { self: false }) {
|
||||
|
||||
Hoek.assert(err && (err instanceof Error || typeof err === 'function'), 'Must provide a valid Error object or a function');
|
||||
|
||||
const unknownKeys = Object.keys(options).filter((k) => !['self'].includes(k));
|
||||
Hoek.assert(unknownKeys.length === 0, `Options ${unknownKeys} are unknown`);
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.error = err;
|
||||
|
||||
if (options.self) {
|
||||
obj._flags.selfError = true;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
allow(...values) {
|
||||
|
||||
const obj = this.clone();
|
||||
values = Hoek.flatten(values);
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
const value = values[i];
|
||||
|
||||
Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');
|
||||
obj._invalids.remove(value);
|
||||
obj._valids.add(value, obj._refs);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
valid(...values) {
|
||||
|
||||
const obj = this.allow(...values);
|
||||
obj._flags.allowOnly = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
invalid(...values) {
|
||||
|
||||
const obj = this.clone();
|
||||
values = Hoek.flatten(values);
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
const value = values[i];
|
||||
|
||||
Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');
|
||||
obj._valids.remove(value);
|
||||
obj._invalids.add(value, obj._refs);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
required() {
|
||||
|
||||
if (this._flags.presence === 'required') {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.presence = 'required';
|
||||
return obj;
|
||||
}
|
||||
|
||||
optional() {
|
||||
|
||||
if (this._flags.presence === 'optional') {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.presence = 'optional';
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
forbidden() {
|
||||
|
||||
if (this._flags.presence === 'forbidden') {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.presence = 'forbidden';
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
strip() {
|
||||
|
||||
if (this._flags.strip) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.strip = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
applyFunctionToChildren(children, fn, args = [], root) {
|
||||
|
||||
children = [].concat(children);
|
||||
|
||||
if (children.length !== 1 || children[0] !== '') {
|
||||
root = root ? (root + '.') : '';
|
||||
|
||||
const extraChildren = (children[0] === '' ? children.slice(1) : children).map((child) => {
|
||||
|
||||
return root + child;
|
||||
});
|
||||
|
||||
throw new Error('unknown key(s) ' + extraChildren.join(', '));
|
||||
}
|
||||
|
||||
return this[fn](...args);
|
||||
}
|
||||
|
||||
default(value, description) {
|
||||
|
||||
if (typeof value === 'function' &&
|
||||
!Ref.isRef(value)) {
|
||||
|
||||
if (!value.description &&
|
||||
description) {
|
||||
|
||||
value.description = description;
|
||||
}
|
||||
|
||||
if (!this._flags.func) {
|
||||
Hoek.assert(typeof value.description === 'string' && value.description.length > 0, 'description must be provided when default value is a function');
|
||||
}
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.default = value;
|
||||
Ref.push(obj._refs, value);
|
||||
return obj;
|
||||
}
|
||||
|
||||
empty(schema) {
|
||||
|
||||
const obj = this.clone();
|
||||
if (schema === undefined) {
|
||||
delete obj._flags.empty;
|
||||
}
|
||||
else {
|
||||
obj._flags.empty = Cast.schema(this._currentJoi, schema);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
when(condition, options) {
|
||||
|
||||
Hoek.assert(options && typeof options === 'object', 'Invalid options');
|
||||
Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');
|
||||
|
||||
const then = options.hasOwnProperty('then') ? this.concat(Cast.schema(this._currentJoi, options.then)) : undefined;
|
||||
const otherwise = options.hasOwnProperty('otherwise') ? this.concat(Cast.schema(this._currentJoi, options.otherwise)) : undefined;
|
||||
|
||||
Alternatives = Alternatives || require('../alternatives');
|
||||
|
||||
const alternativeOptions = { then, otherwise };
|
||||
if (Object.prototype.hasOwnProperty.call(options, 'is')) {
|
||||
alternativeOptions.is = options.is;
|
||||
}
|
||||
|
||||
const obj = Alternatives.when(condition, alternativeOptions);
|
||||
obj._flags.presence = 'ignore';
|
||||
obj._baseType = this;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
description(desc) {
|
||||
|
||||
Hoek.assert(desc && typeof desc === 'string', 'Description must be a non-empty string');
|
||||
|
||||
const obj = this.clone();
|
||||
obj._description = desc;
|
||||
return obj;
|
||||
}
|
||||
|
||||
notes(notes) {
|
||||
|
||||
Hoek.assert(notes && (typeof notes === 'string' || Array.isArray(notes)), 'Notes must be a non-empty string or array');
|
||||
|
||||
const obj = this.clone();
|
||||
obj._notes = obj._notes.concat(notes);
|
||||
return obj;
|
||||
}
|
||||
|
||||
tags(tags) {
|
||||
|
||||
Hoek.assert(tags && (typeof tags === 'string' || Array.isArray(tags)), 'Tags must be a non-empty string or array');
|
||||
|
||||
const obj = this.clone();
|
||||
obj._tags = obj._tags.concat(tags);
|
||||
return obj;
|
||||
}
|
||||
|
||||
meta(meta) {
|
||||
|
||||
Hoek.assert(meta !== undefined, 'Meta cannot be undefined');
|
||||
|
||||
const obj = this.clone();
|
||||
obj._meta = obj._meta.concat(meta);
|
||||
return obj;
|
||||
}
|
||||
|
||||
example(...examples) {
|
||||
|
||||
Hoek.assert(examples.length > 0, 'Missing examples');
|
||||
|
||||
const processed = [];
|
||||
for (let i = 0; i < examples.length; ++i) {
|
||||
const example = [].concat(examples[i]);
|
||||
Hoek.assert(example.length <= 2, `Bad example format at index ${i}`);
|
||||
|
||||
const value = example[0];
|
||||
let options = example[1];
|
||||
if (options !== undefined) {
|
||||
Hoek.assert(options && typeof options === 'object', `Options for example at index ${i} must be an object`);
|
||||
const unknownOptions = Object.keys(options).filter((option) => !['parent', 'context'].includes(option));
|
||||
Hoek.assert(unknownOptions.length === 0, `Unknown example options ${unknownOptions} at index ${i}`);
|
||||
}
|
||||
else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
const localState = new State('', [], options.parent || null);
|
||||
const result = this._validate(value, localState, Settings.concat(internals.defaults, options.context ? { context: options.context } : null));
|
||||
Hoek.assert(!result.errors, `Bad example at index ${i}:`, result.errors && Errors.process(result.errors, value));
|
||||
|
||||
const ex = { value };
|
||||
if (Object.keys(options).length) {
|
||||
ex.options = options;
|
||||
}
|
||||
|
||||
processed.push(ex);
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._examples = processed;
|
||||
return obj;
|
||||
}
|
||||
|
||||
unit(name) {
|
||||
|
||||
Hoek.assert(name && typeof name === 'string', 'Unit name must be a non-empty string');
|
||||
|
||||
const obj = this.clone();
|
||||
obj._unit = name;
|
||||
return obj;
|
||||
}
|
||||
|
||||
_prepareEmptyValue(value) {
|
||||
|
||||
if (typeof value === 'string' && this._flags.trim) {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
_validate(value, state, options, reference) {
|
||||
|
||||
const originalValue = value;
|
||||
|
||||
// Setup state and settings
|
||||
|
||||
state = state || new State('', [], null, reference);
|
||||
|
||||
if (this._settings) {
|
||||
const isDefaultOptions = options === internals.defaults;
|
||||
if (isDefaultOptions && this._settings[Symbols.settingsCache]) {
|
||||
options = this._settings[Symbols.settingsCache];
|
||||
}
|
||||
else {
|
||||
options = Settings.concat(this._language ? Settings.concat({ language: this._language }, options) : options, this._settings);
|
||||
if (isDefaultOptions) {
|
||||
this._settings[Symbols.settingsCache] = options;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this._language) {
|
||||
options = Settings.concat({ language: this._language }, options);
|
||||
}
|
||||
|
||||
let errors = [];
|
||||
|
||||
if (this._coerce) {
|
||||
const coerced = this._coerce(value, state, options);
|
||||
if (coerced.errors) {
|
||||
value = coerced.value;
|
||||
errors = errors.concat(coerced.errors);
|
||||
return this._finalizeValue(value, originalValue, errors, state, options); // Coerced error always aborts early
|
||||
}
|
||||
|
||||
value = coerced.value;
|
||||
}
|
||||
|
||||
if (this._flags.empty && !this._flags.empty._validate(this._prepareEmptyValue(value), null, internals.defaults).errors) {
|
||||
value = undefined;
|
||||
}
|
||||
|
||||
// Check presence requirements
|
||||
|
||||
const presence = this._flags.presence || options.presence;
|
||||
if (presence === 'optional') {
|
||||
if (value === undefined) {
|
||||
const isDeepDefault = this._flags.hasOwnProperty('default') && this._flags.default === undefined;
|
||||
if (isDeepDefault && this._type === 'object') {
|
||||
value = {};
|
||||
}
|
||||
else {
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (presence === 'required' &&
|
||||
value === undefined) {
|
||||
|
||||
errors.push(this.createError('any.required', null, state, options));
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
else if (presence === 'forbidden') {
|
||||
if (value === undefined) {
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
|
||||
errors.push(this.createError('any.unknown', null, state, options));
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
|
||||
// Check allowed and denied values using the original value
|
||||
|
||||
let match = this._valids.get(value, state, options, this._flags.insensitive);
|
||||
if (match) {
|
||||
if (options.convert) {
|
||||
value = match.value;
|
||||
}
|
||||
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
|
||||
if (this._invalids.has(value, state, options, this._flags.insensitive)) {
|
||||
errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));
|
||||
if (options.abortEarly) {
|
||||
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert value and validate type
|
||||
|
||||
if (this._base) {
|
||||
const base = this._base(value, state, options);
|
||||
if (base.errors) {
|
||||
value = base.value;
|
||||
errors = errors.concat(base.errors);
|
||||
return this._finalizeValue(value, originalValue, errors, state, options); // Base error always aborts early
|
||||
}
|
||||
|
||||
if (base.value !== value) {
|
||||
value = base.value;
|
||||
|
||||
// Check allowed and denied values using the converted value
|
||||
|
||||
match = this._valids.get(value, state, options, this._flags.insensitive);
|
||||
if (match) {
|
||||
value = match.value;
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
|
||||
if (this._invalids.has(value, state, options, this._flags.insensitive)) {
|
||||
errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));
|
||||
if (options.abortEarly) {
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Required values did not match
|
||||
|
||||
if (this._flags.allowOnly) {
|
||||
errors.push(this.createError('any.allowOnly', { value, valids: this._valids.values({ stripUndefined: true }) }, state, options));
|
||||
if (options.abortEarly) {
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate tests
|
||||
|
||||
for (let i = 0; i < this._tests.length; ++i) {
|
||||
const test = this._tests[i];
|
||||
const ret = test.func.call(this, value, state, options);
|
||||
if (ret instanceof Errors.Err) {
|
||||
errors.push(ret);
|
||||
if (options.abortEarly) {
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = ret;
|
||||
}
|
||||
}
|
||||
|
||||
return this._finalizeValue(value, originalValue, errors, state, options);
|
||||
}
|
||||
|
||||
_finalizeValue(value, originalValue, errors, state, options) {
|
||||
|
||||
let finalValue;
|
||||
|
||||
if (value !== undefined) {
|
||||
finalValue = this._flags.raw ? originalValue : value;
|
||||
}
|
||||
else if (options.noDefaults) {
|
||||
finalValue = value;
|
||||
}
|
||||
else if (Ref.isRef(this._flags.default)) {
|
||||
finalValue = this._flags.default(state.parent, options);
|
||||
}
|
||||
else if (typeof this._flags.default === 'function' &&
|
||||
!(this._flags.func && !this._flags.default.description)) {
|
||||
|
||||
let args;
|
||||
|
||||
if (state.parent !== null &&
|
||||
this._flags.default.length > 0) {
|
||||
|
||||
args = [Hoek.clone(state.parent), options];
|
||||
}
|
||||
|
||||
const defaultValue = internals._try(this._flags.default, args);
|
||||
finalValue = defaultValue.value;
|
||||
if (defaultValue.error) {
|
||||
errors.push(this.createError('any.default', { error: defaultValue.error }, state, options));
|
||||
}
|
||||
}
|
||||
else {
|
||||
finalValue = Hoek.clone(this._flags.default);
|
||||
}
|
||||
|
||||
if (errors.length &&
|
||||
typeof this._flags.error === 'function' &&
|
||||
(
|
||||
!this._flags.selfError ||
|
||||
errors.some((e) => state.path.length === e.path.length)
|
||||
)
|
||||
) {
|
||||
const change = this._flags.error.call(this, errors);
|
||||
|
||||
if (typeof change === 'string') {
|
||||
errors = [this.createOverrideError('override', { reason: errors }, state, options, change)];
|
||||
}
|
||||
else {
|
||||
errors = [].concat(change)
|
||||
.map((err) => {
|
||||
|
||||
return err instanceof Error ?
|
||||
err :
|
||||
this.createOverrideError(err.type || 'override', err.context, state, options, err.message, err.template);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
value: this._flags.strip ? undefined : finalValue,
|
||||
finalValue,
|
||||
errors: errors.length ? errors : null
|
||||
};
|
||||
}
|
||||
|
||||
_validateWithOptions(value, options, callback) {
|
||||
|
||||
if (options) {
|
||||
this.checkOptions(options);
|
||||
}
|
||||
|
||||
const settings = Settings.concat(internals.defaults, options);
|
||||
const result = this._validate(value, null, settings);
|
||||
const errors = Errors.process(result.errors, value);
|
||||
|
||||
if (callback) {
|
||||
return callback(errors, result.value);
|
||||
}
|
||||
|
||||
return {
|
||||
error: errors,
|
||||
value: result.value,
|
||||
then(resolve, reject) {
|
||||
|
||||
if (errors) {
|
||||
return Promise.reject(errors).catch(reject);
|
||||
}
|
||||
|
||||
return Promise.resolve(result.value).then(resolve);
|
||||
},
|
||||
catch(reject) {
|
||||
|
||||
if (errors) {
|
||||
return Promise.reject(errors).catch(reject);
|
||||
}
|
||||
|
||||
return Promise.resolve(result.value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
validate(value, options, callback) {
|
||||
|
||||
if (typeof options === 'function') {
|
||||
return this._validateWithOptions(value, null, options);
|
||||
}
|
||||
|
||||
return this._validateWithOptions(value, options, callback);
|
||||
}
|
||||
|
||||
describe() {
|
||||
|
||||
const description = {
|
||||
type: this._type
|
||||
};
|
||||
|
||||
const flags = Object.keys(this._flags);
|
||||
if (flags.length) {
|
||||
if (['empty', 'default', 'lazy', 'label'].some((flag) => this._flags.hasOwnProperty(flag))) {
|
||||
description.flags = {};
|
||||
for (let i = 0; i < flags.length; ++i) {
|
||||
const flag = flags[i];
|
||||
if (flag === 'empty') {
|
||||
description.flags[flag] = this._flags[flag].describe();
|
||||
}
|
||||
else if (flag === 'default') {
|
||||
if (Ref.isRef(this._flags[flag])) {
|
||||
description.flags[flag] = this._flags[flag].toString();
|
||||
}
|
||||
else if (typeof this._flags[flag] === 'function') {
|
||||
description.flags[flag] = {
|
||||
description: this._flags[flag].description,
|
||||
function : this._flags[flag]
|
||||
};
|
||||
}
|
||||
else {
|
||||
description.flags[flag] = this._flags[flag];
|
||||
}
|
||||
}
|
||||
else if (flag === 'lazy' || flag === 'label') {
|
||||
// We don't want it in the description
|
||||
}
|
||||
else {
|
||||
description.flags[flag] = this._flags[flag];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
description.flags = this._flags;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._settings) {
|
||||
description.options = Hoek.clone(this._settings);
|
||||
}
|
||||
|
||||
if (this._baseType) {
|
||||
description.base = this._baseType.describe();
|
||||
}
|
||||
|
||||
if (this._description) {
|
||||
description.description = this._description;
|
||||
}
|
||||
|
||||
if (this._notes.length) {
|
||||
description.notes = this._notes;
|
||||
}
|
||||
|
||||
if (this._tags.length) {
|
||||
description.tags = this._tags;
|
||||
}
|
||||
|
||||
if (this._meta.length) {
|
||||
description.meta = this._meta;
|
||||
}
|
||||
|
||||
if (this._examples.length) {
|
||||
description.examples = this._examples;
|
||||
}
|
||||
|
||||
if (this._unit) {
|
||||
description.unit = this._unit;
|
||||
}
|
||||
|
||||
const valids = this._valids.values();
|
||||
if (valids.length) {
|
||||
description.valids = valids.map((v) => {
|
||||
|
||||
return Ref.isRef(v) ? v.toString() : v;
|
||||
});
|
||||
}
|
||||
|
||||
const invalids = this._invalids.values();
|
||||
if (invalids.length) {
|
||||
description.invalids = invalids.map((v) => {
|
||||
|
||||
return Ref.isRef(v) ? v.toString() : v;
|
||||
});
|
||||
}
|
||||
|
||||
description.rules = [];
|
||||
|
||||
for (let i = 0; i < this._tests.length; ++i) {
|
||||
const validator = this._tests[i];
|
||||
const item = { name: validator.name };
|
||||
|
||||
if (validator.arg !== void 0) {
|
||||
item.arg = Ref.isRef(validator.arg) ? validator.arg.toString() : validator.arg;
|
||||
}
|
||||
|
||||
const options = validator.options;
|
||||
if (options) {
|
||||
if (options.hasRef) {
|
||||
item.arg = {};
|
||||
const keys = Object.keys(validator.arg);
|
||||
for (let j = 0; j < keys.length; ++j) {
|
||||
const key = keys[j];
|
||||
const value = validator.arg[key];
|
||||
item.arg[key] = Ref.isRef(value) ? value.toString() : value;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof options.description === 'string') {
|
||||
item.description = options.description;
|
||||
}
|
||||
else if (typeof options.description === 'function') {
|
||||
item.description = options.description(item.arg);
|
||||
}
|
||||
}
|
||||
|
||||
description.rules.push(item);
|
||||
}
|
||||
|
||||
if (!description.rules.length) {
|
||||
delete description.rules;
|
||||
}
|
||||
|
||||
const label = this._getLabel();
|
||||
if (label) {
|
||||
description.label = label;
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
label(name) {
|
||||
|
||||
Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.label = name;
|
||||
return obj;
|
||||
}
|
||||
|
||||
_getLabel(def) {
|
||||
|
||||
return this._flags.label || def;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
internals.Any.prototype.isImmutable = true; // Prevents Hoek from deep cloning schema objects
|
||||
|
||||
// Aliases
|
||||
|
||||
internals.Any.prototype.only = internals.Any.prototype.equal = internals.Any.prototype.valid;
|
||||
internals.Any.prototype.disallow = internals.Any.prototype.not = internals.Any.prototype.invalid;
|
||||
internals.Any.prototype.exist = internals.Any.prototype.required;
|
||||
|
||||
|
||||
internals.Any.prototype[internals.symbol] = {
|
||||
version: Pkg.version,
|
||||
compile: Cast.schema,
|
||||
root: '_currentJoi'
|
||||
};
|
||||
|
||||
|
||||
internals._try = function (fn, args = []) {
|
||||
|
||||
let err;
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = fn(...args);
|
||||
}
|
||||
catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
return {
|
||||
value: result,
|
||||
error: err
|
||||
};
|
||||
};
|
||||
Generated
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Symbols = require('../symbols');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.concat = function (target, source) {
|
||||
|
||||
if (!source) {
|
||||
return target;
|
||||
}
|
||||
|
||||
const obj = Object.assign({}, target);
|
||||
|
||||
const language = source.language;
|
||||
|
||||
Object.assign(obj, source);
|
||||
|
||||
if (language && target && target.language) {
|
||||
obj.language = Hoek.applyToDefaults(target.language, language);
|
||||
}
|
||||
|
||||
if (obj[Symbols.settingsCache]) {
|
||||
delete obj[Symbols.settingsCache];
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
Generated
Vendored
+699
@@ -0,0 +1,699 @@
|
||||
'use strict';
|
||||
|
||||
const Bourne = require('@hapi/bourne');
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
const Cast = require('../../cast');
|
||||
const Ref = require('../../ref');
|
||||
const State = require('../state');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.fastSplice = function (arr, i) {
|
||||
|
||||
let pos = i;
|
||||
while (pos < arr.length) {
|
||||
arr[pos++] = arr[pos];
|
||||
}
|
||||
|
||||
--arr.length;
|
||||
};
|
||||
|
||||
|
||||
internals.Array = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'array';
|
||||
this._inner.items = [];
|
||||
this._inner.ordereds = [];
|
||||
this._inner.inclusions = [];
|
||||
this._inner.exclusions = [];
|
||||
this._inner.requireds = [];
|
||||
this._flags.sparse = false;
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
const result = {
|
||||
value
|
||||
};
|
||||
|
||||
if (typeof value === 'string' &&
|
||||
options.convert) {
|
||||
|
||||
if (value.length > 1 &&
|
||||
(value[0] === '[' || /^\s*\[/.test(value))) {
|
||||
|
||||
try {
|
||||
result.value = Bourne.parse(value);
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
}
|
||||
|
||||
let isArray = Array.isArray(result.value);
|
||||
const wasArray = isArray;
|
||||
if (options.convert && this._flags.single && !isArray) {
|
||||
result.value = [result.value];
|
||||
isArray = true;
|
||||
}
|
||||
|
||||
if (!isArray) {
|
||||
result.errors = this.createError('array.base', null, state, options);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this._inner.inclusions.length ||
|
||||
this._inner.exclusions.length ||
|
||||
this._inner.requireds.length ||
|
||||
this._inner.ordereds.length ||
|
||||
!this._flags.sparse) {
|
||||
|
||||
// Clone the array so that we don't modify the original
|
||||
if (wasArray) {
|
||||
result.value = result.value.slice(0);
|
||||
}
|
||||
|
||||
result.errors = this._checkItems(result.value, wasArray, state, options);
|
||||
|
||||
if (result.errors && wasArray && options.convert && this._flags.single) {
|
||||
|
||||
// Attempt a 2nd pass by putting the array inside one.
|
||||
const previousErrors = result.errors;
|
||||
|
||||
result.value = [result.value];
|
||||
result.errors = this._checkItems(result.value, wasArray, state, options);
|
||||
|
||||
if (result.errors) {
|
||||
|
||||
// Restore previous errors and value since this didn't validate either.
|
||||
result.errors = previousErrors;
|
||||
result.value = result.value[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_checkItems(items, wasArray, state, options) {
|
||||
|
||||
const errors = [];
|
||||
let errored;
|
||||
|
||||
const requireds = this._inner.requireds.slice();
|
||||
const ordereds = this._inner.ordereds.slice();
|
||||
const inclusions = [...this._inner.inclusions, ...requireds];
|
||||
|
||||
let il = items.length;
|
||||
for (let i = 0; i < il; ++i) {
|
||||
errored = false;
|
||||
const item = items[i];
|
||||
let isValid = false;
|
||||
const key = wasArray ? i : state.key;
|
||||
const path = wasArray ? [...state.path, i] : state.path;
|
||||
const localState = new State(key, path, state.parent, state.reference);
|
||||
let res;
|
||||
|
||||
// Sparse
|
||||
|
||||
if (!this._flags.sparse && item === undefined) {
|
||||
errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
|
||||
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
ordereds.shift();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclusions
|
||||
|
||||
for (let j = 0; j < this._inner.exclusions.length; ++j) {
|
||||
res = this._inner.exclusions[j]._validate(item, localState, {}); // Not passing options to use defaults
|
||||
|
||||
if (!res.errors) {
|
||||
errors.push(this.createError(wasArray ? 'array.excludes' : 'array.excludesSingle', { pos: i, value: item }, { key: state.key, path: localState.path }, options));
|
||||
errored = true;
|
||||
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
ordereds.shift();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errored) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ordered
|
||||
if (this._inner.ordereds.length) {
|
||||
if (ordereds.length > 0) {
|
||||
const ordered = ordereds.shift();
|
||||
res = ordered._validate(item, localState, options);
|
||||
if (!res.errors) {
|
||||
if (ordered._flags.strip) {
|
||||
internals.fastSplice(items, i);
|
||||
--i;
|
||||
--il;
|
||||
}
|
||||
else if (!this._flags.sparse && res.value === undefined) {
|
||||
errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
|
||||
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
items[i] = res.value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
errors.push(this.createError('array.ordered', { pos: i, reason: res.errors, value: item }, { key: state.key, path: localState.path }, options));
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (!this._inner.items.length) {
|
||||
errors.push(this.createError('array.orderedLength', { pos: i, limit: this._inner.ordereds.length }, { key: state.key, path: localState.path }, options));
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Requireds
|
||||
|
||||
const requiredChecks = [];
|
||||
let jl = requireds.length;
|
||||
for (let j = 0; j < jl; ++j) {
|
||||
res = requiredChecks[j] = requireds[j]._validate(item, localState, options);
|
||||
if (!res.errors) {
|
||||
items[i] = res.value;
|
||||
isValid = true;
|
||||
internals.fastSplice(requireds, j);
|
||||
--j;
|
||||
--jl;
|
||||
|
||||
if (!this._flags.sparse && res.value === undefined) {
|
||||
errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
|
||||
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Inclusions
|
||||
|
||||
const stripUnknown = options.stripUnknown && !!options.stripUnknown.arrays || false;
|
||||
|
||||
jl = inclusions.length;
|
||||
for (let j = 0; j < jl; ++j) {
|
||||
const inclusion = inclusions[j];
|
||||
|
||||
// Avoid re-running requireds that already didn't match in the previous loop
|
||||
const previousCheck = requireds.indexOf(inclusion);
|
||||
if (previousCheck !== -1) {
|
||||
res = requiredChecks[previousCheck];
|
||||
}
|
||||
else {
|
||||
res = inclusion._validate(item, localState, options);
|
||||
|
||||
if (!res.errors) {
|
||||
if (inclusion._flags.strip) {
|
||||
internals.fastSplice(items, i);
|
||||
--i;
|
||||
--il;
|
||||
}
|
||||
else if (!this._flags.sparse && res.value === undefined) {
|
||||
errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
|
||||
errored = true;
|
||||
}
|
||||
else {
|
||||
items[i] = res.value;
|
||||
}
|
||||
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the actual error if only one inclusion defined
|
||||
if (jl === 1) {
|
||||
if (stripUnknown) {
|
||||
internals.fastSplice(items, i);
|
||||
--i;
|
||||
--il;
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
errors.push(this.createError(wasArray ? 'array.includesOne' : 'array.includesOneSingle', { pos: i, reason: res.errors, value: item }, { key: state.key, path: localState.path }, options));
|
||||
errored = true;
|
||||
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errored) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this._inner.inclusions.length && !isValid) {
|
||||
if (stripUnknown) {
|
||||
internals.fastSplice(items, i);
|
||||
--i;
|
||||
--il;
|
||||
continue;
|
||||
}
|
||||
|
||||
errors.push(this.createError(wasArray ? 'array.includes' : 'array.includesSingle', { pos: i, value: item }, { key: state.key, path: localState.path }, options));
|
||||
|
||||
if (options.abortEarly) {
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requireds.length) {
|
||||
this._fillMissedErrors(errors, requireds, state, options);
|
||||
}
|
||||
|
||||
if (ordereds.length) {
|
||||
this._fillOrderedErrors(errors, ordereds, state, options);
|
||||
}
|
||||
|
||||
return errors.length ? errors : null;
|
||||
}
|
||||
|
||||
describe() {
|
||||
|
||||
const description = super.describe();
|
||||
|
||||
if (this._inner.ordereds.length) {
|
||||
description.orderedItems = [];
|
||||
|
||||
for (let i = 0; i < this._inner.ordereds.length; ++i) {
|
||||
description.orderedItems.push(this._inner.ordereds[i].describe());
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inner.items.length) {
|
||||
description.items = [];
|
||||
|
||||
for (let i = 0; i < this._inner.items.length; ++i) {
|
||||
description.items.push(this._inner.items[i].describe());
|
||||
}
|
||||
}
|
||||
|
||||
if (description.rules) {
|
||||
for (let i = 0; i < description.rules.length; ++i) {
|
||||
const rule = description.rules[i];
|
||||
if (rule.name === 'has') {
|
||||
rule.arg = rule.arg.describe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
items(...schemas) {
|
||||
|
||||
const obj = this.clone();
|
||||
|
||||
Hoek.flatten(schemas).forEach((type, index) => {
|
||||
|
||||
try {
|
||||
type = Cast.schema(this._currentJoi, type);
|
||||
}
|
||||
catch (castErr) {
|
||||
if (castErr.hasOwnProperty('path')) {
|
||||
castErr.path = index + '.' + castErr.path;
|
||||
}
|
||||
else {
|
||||
castErr.path = index;
|
||||
}
|
||||
|
||||
castErr.message = `${castErr.message}(${castErr.path})`;
|
||||
throw castErr;
|
||||
}
|
||||
|
||||
obj._inner.items.push(type);
|
||||
|
||||
if (type._flags.presence === 'required') {
|
||||
obj._inner.requireds.push(type);
|
||||
}
|
||||
else if (type._flags.presence === 'forbidden') {
|
||||
obj._inner.exclusions.push(type.optional());
|
||||
}
|
||||
else {
|
||||
obj._inner.inclusions.push(type);
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
ordered(...schemas) {
|
||||
|
||||
const obj = this.clone();
|
||||
|
||||
Hoek.flatten(schemas).forEach((type, index) => {
|
||||
|
||||
try {
|
||||
type = Cast.schema(this._currentJoi, type);
|
||||
}
|
||||
catch (castErr) {
|
||||
if (castErr.hasOwnProperty('path')) {
|
||||
castErr.path = index + '.' + castErr.path;
|
||||
}
|
||||
else {
|
||||
castErr.path = index;
|
||||
}
|
||||
|
||||
castErr.message = `${castErr.message}(${castErr.path})`;
|
||||
throw castErr;
|
||||
}
|
||||
|
||||
obj._inner.ordereds.push(type);
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
min(limit) {
|
||||
|
||||
const isRef = Ref.isRef(limit);
|
||||
|
||||
Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
|
||||
|
||||
return this._testUnique('min', limit, function (value, state, options) {
|
||||
|
||||
let compareTo;
|
||||
if (isRef) {
|
||||
compareTo = limit(state.reference || state.parent, options);
|
||||
|
||||
if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
|
||||
return this.createError('array.ref', { ref: limit, value: compareTo }, state, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
compareTo = limit;
|
||||
}
|
||||
|
||||
if (value.length >= compareTo) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('array.min', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
max(limit) {
|
||||
|
||||
const isRef = Ref.isRef(limit);
|
||||
|
||||
Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
|
||||
|
||||
return this._testUnique('max', limit, function (value, state, options) {
|
||||
|
||||
let compareTo;
|
||||
if (isRef) {
|
||||
compareTo = limit(state.reference || state.parent, options);
|
||||
|
||||
if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
|
||||
return this.createError('array.ref', { ref: limit.key }, state, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
compareTo = limit;
|
||||
}
|
||||
|
||||
if (value.length <= compareTo) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('array.max', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
length(limit) {
|
||||
|
||||
const isRef = Ref.isRef(limit);
|
||||
|
||||
Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
|
||||
|
||||
return this._testUnique('length', limit, function (value, state, options) {
|
||||
|
||||
let compareTo;
|
||||
if (isRef) {
|
||||
compareTo = limit(state.reference || state.parent, options);
|
||||
|
||||
if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
|
||||
return this.createError('array.ref', { ref: limit.key }, state, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
compareTo = limit;
|
||||
}
|
||||
|
||||
if (value.length === compareTo) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('array.length', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
has(schema) {
|
||||
|
||||
try {
|
||||
schema = Cast.schema(this._currentJoi, schema);
|
||||
}
|
||||
catch (castErr) {
|
||||
if (castErr.hasOwnProperty('path')) {
|
||||
castErr.message = `${castErr.message}(${castErr.path})`;
|
||||
}
|
||||
|
||||
throw castErr;
|
||||
}
|
||||
|
||||
return this._test('has', schema, function (value, state, options) {
|
||||
|
||||
const isValid = value.some((item, idx) => {
|
||||
|
||||
const localState = new State(idx, [...state.path, idx], state.key, state.reference);
|
||||
return !schema._validate(item, localState, options).errors;
|
||||
});
|
||||
|
||||
if (isValid) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const patternLabel = schema._getLabel();
|
||||
if (patternLabel) {
|
||||
return this.createError('array.hasKnown', { patternLabel }, state, options);
|
||||
}
|
||||
|
||||
return this.createError('array.hasUnknown', null, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
unique(comparator, configs) {
|
||||
|
||||
Hoek.assert(comparator === undefined ||
|
||||
typeof comparator === 'function' ||
|
||||
typeof comparator === 'string', 'comparator must be a function or a string');
|
||||
|
||||
Hoek.assert(configs === undefined ||
|
||||
typeof configs === 'object', 'configs must be an object');
|
||||
|
||||
const settings = {
|
||||
ignoreUndefined: (configs && configs.ignoreUndefined) || false
|
||||
};
|
||||
|
||||
|
||||
if (typeof comparator === 'string') {
|
||||
settings.path = comparator;
|
||||
}
|
||||
else if (typeof comparator === 'function') {
|
||||
settings.comparator = comparator;
|
||||
}
|
||||
|
||||
return this._test('unique', settings, function (value, state, options) {
|
||||
|
||||
const found = {
|
||||
string: Object.create(null),
|
||||
number: Object.create(null),
|
||||
undefined: Object.create(null),
|
||||
boolean: Object.create(null),
|
||||
object: new Map(),
|
||||
function: new Map(),
|
||||
custom: new Map()
|
||||
};
|
||||
|
||||
const compare = settings.comparator || Hoek.deepEqual;
|
||||
const ignoreUndefined = settings.ignoreUndefined;
|
||||
|
||||
for (let i = 0; i < value.length; ++i) {
|
||||
const item = settings.path ? Hoek.reach(value[i], settings.path) : value[i];
|
||||
const records = settings.comparator ? found.custom : found[typeof item];
|
||||
|
||||
// All available types are supported, so it's not possible to reach 100% coverage without ignoring this line.
|
||||
// I still want to keep the test for future js versions with new types (eg. Symbol).
|
||||
if (/* $lab:coverage:off$ */ records /* $lab:coverage:on$ */) {
|
||||
if (records instanceof Map) {
|
||||
const entries = records.entries();
|
||||
let current;
|
||||
while (!(current = entries.next()).done) {
|
||||
if (compare(current.value[0], item)) {
|
||||
const localState = new State(state.key, [...state.path, i], state.parent, state.reference);
|
||||
const context = {
|
||||
pos: i,
|
||||
value: value[i],
|
||||
dupePos: current.value[1],
|
||||
dupeValue: value[current.value[1]]
|
||||
};
|
||||
|
||||
if (settings.path) {
|
||||
context.path = settings.path;
|
||||
}
|
||||
|
||||
return this.createError('array.unique', context, localState, options);
|
||||
}
|
||||
}
|
||||
|
||||
records.set(item, i);
|
||||
}
|
||||
else {
|
||||
if ((!ignoreUndefined || item !== undefined) && records[item] !== undefined) {
|
||||
const localState = new State(state.key, [...state.path, i], state.parent, state.reference);
|
||||
|
||||
const context = {
|
||||
pos: i,
|
||||
value: value[i],
|
||||
dupePos: records[item],
|
||||
dupeValue: value[records[item]]
|
||||
};
|
||||
|
||||
if (settings.path) {
|
||||
context.path = settings.path;
|
||||
}
|
||||
|
||||
return this.createError('array.unique', context, localState, options);
|
||||
}
|
||||
|
||||
records[item] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
sparse(enabled) {
|
||||
|
||||
const value = enabled === undefined ? true : !!enabled;
|
||||
|
||||
if (this._flags.sparse === value) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.sparse = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
single(enabled) {
|
||||
|
||||
const value = enabled === undefined ? true : !!enabled;
|
||||
|
||||
if (this._flags.single === value) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.single = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
_fillMissedErrors(errors, requireds, state, options) {
|
||||
|
||||
const knownMisses = [];
|
||||
let unknownMisses = 0;
|
||||
for (let i = 0; i < requireds.length; ++i) {
|
||||
const label = requireds[i]._getLabel();
|
||||
if (label) {
|
||||
knownMisses.push(label);
|
||||
}
|
||||
else {
|
||||
++unknownMisses;
|
||||
}
|
||||
}
|
||||
|
||||
if (knownMisses.length) {
|
||||
if (unknownMisses) {
|
||||
errors.push(this.createError('array.includesRequiredBoth', { knownMisses, unknownMisses }, { key: state.key, path: state.path }, options));
|
||||
}
|
||||
else {
|
||||
errors.push(this.createError('array.includesRequiredKnowns', { knownMisses }, { key: state.key, path: state.path }, options));
|
||||
}
|
||||
}
|
||||
else {
|
||||
errors.push(this.createError('array.includesRequiredUnknowns', { unknownMisses }, { key: state.key, path: state.path }, options));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_fillOrderedErrors(errors, ordereds, state, options) {
|
||||
|
||||
const requiredOrdereds = [];
|
||||
|
||||
for (let i = 0; i < ordereds.length; ++i) {
|
||||
const presence = Hoek.reach(ordereds[i], '_flags.presence');
|
||||
if (presence === 'required') {
|
||||
requiredOrdereds.push(ordereds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (requiredOrdereds.length) {
|
||||
this._fillMissedErrors(errors, requiredOrdereds, state, options);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = new internals.Array();
|
||||
Generated
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.Binary = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'binary';
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
const result = {
|
||||
value
|
||||
};
|
||||
|
||||
if (typeof value === 'string' &&
|
||||
options.convert) {
|
||||
|
||||
try {
|
||||
result.value = Buffer.from(value, this._flags.encoding);
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
|
||||
result.errors = Buffer.isBuffer(result.value) ? null : this.createError('binary.base', null, state, options);
|
||||
return result;
|
||||
}
|
||||
|
||||
encoding(encoding) {
|
||||
|
||||
Hoek.assert(Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);
|
||||
|
||||
if (this._flags.encoding === encoding) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.encoding = encoding;
|
||||
return obj;
|
||||
}
|
||||
|
||||
min(limit) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
|
||||
|
||||
return this._test('min', limit, function (value, state, options) {
|
||||
|
||||
if (value.length >= limit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('binary.min', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
max(limit) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
|
||||
|
||||
return this._test('max', limit, function (value, state, options) {
|
||||
|
||||
if (value.length <= limit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('binary.max', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
length(limit) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
|
||||
|
||||
return this._test('length', limit, function (value, state, options) {
|
||||
|
||||
if (value.length === limit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('binary.length', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = new internals.Binary();
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
|
||||
|
||||
const internals = {
|
||||
Set: require('../../set')
|
||||
};
|
||||
|
||||
|
||||
internals.Boolean = class extends Any {
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'boolean';
|
||||
this._flags.insensitive = true;
|
||||
this._inner.truthySet = new internals.Set();
|
||||
this._inner.falsySet = new internals.Set();
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
const result = {
|
||||
value
|
||||
};
|
||||
|
||||
if (typeof value === 'string' &&
|
||||
options.convert) {
|
||||
|
||||
const normalized = this._flags.insensitive ? value.toLowerCase() : value;
|
||||
result.value = (normalized === 'true' ? true
|
||||
: (normalized === 'false' ? false : value));
|
||||
}
|
||||
|
||||
if (typeof result.value !== 'boolean') {
|
||||
result.value = (this._inner.truthySet.has(value, null, null, this._flags.insensitive) ? true
|
||||
: (this._inner.falsySet.has(value, null, null, this._flags.insensitive) ? false : value));
|
||||
}
|
||||
|
||||
result.errors = (typeof result.value === 'boolean') ? null : this.createError('boolean.base', { value }, state, options);
|
||||
return result;
|
||||
}
|
||||
|
||||
truthy(...values) {
|
||||
|
||||
const obj = this.clone();
|
||||
values = Hoek.flatten(values);
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
const value = values[i];
|
||||
|
||||
Hoek.assert(value !== undefined, 'Cannot call truthy with undefined');
|
||||
obj._inner.truthySet.add(value);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
falsy(...values) {
|
||||
|
||||
const obj = this.clone();
|
||||
values = Hoek.flatten(values);
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
const value = values[i];
|
||||
|
||||
Hoek.assert(value !== undefined, 'Cannot call falsy with undefined');
|
||||
obj._inner.falsySet.add(value);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
insensitive(enabled) {
|
||||
|
||||
const insensitive = enabled === undefined ? true : !!enabled;
|
||||
|
||||
if (this._flags.insensitive === insensitive) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.insensitive = insensitive;
|
||||
return obj;
|
||||
}
|
||||
|
||||
describe() {
|
||||
|
||||
const description = super.describe();
|
||||
description.truthy = [true, ...this._inner.truthySet.values()];
|
||||
description.falsy = [false, ...this._inner.falsySet.values()];
|
||||
return description;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = new internals.Boolean();
|
||||
Generated
Vendored
+179
@@ -0,0 +1,179 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
const Ref = require('../../ref');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
internals.isoDate = /^(?:[-+]\d{2})?(?:\d{4}(?!\d{2}\b))(?:(-?)(?:(?:0[1-9]|1[0-2])(?:\1(?:[12]\d|0[1-9]|3[01]))?|W(?:[0-4]\d|5[0-2])(?:-?[1-7])?|(?:00[1-9]|0[1-9]\d|[12]\d{2}|3(?:[0-5]\d|6[1-6])))(?![T]$|[T][\d]+Z$)(?:[T\s](?:(?:(?:[01]\d|2[0-3])(?:(:?)[0-5]\d)?|24\:?00)(?:[.,]\d+(?!:))?)(?:\2[0-5]\d(?:[.,]\d+)?)?(?:[Z]|(?:[+-])(?:[01]\d|2[0-3])(?::?[0-5]\d)?)?)?)?$/;
|
||||
internals.invalidDate = new Date('');
|
||||
internals.isIsoDate = (() => {
|
||||
|
||||
const isoString = internals.isoDate.toString();
|
||||
|
||||
return (date) => {
|
||||
|
||||
return date && (date.toString() === isoString);
|
||||
};
|
||||
})();
|
||||
|
||||
internals.Date = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'date';
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
const result = {
|
||||
value: (options.convert && internals.Date.toDate(value, this._flags.format, this._flags.timestamp, this._flags.multiplier)) || value
|
||||
};
|
||||
|
||||
if (result.value instanceof Date && !isNaN(result.value.getTime())) {
|
||||
result.errors = null;
|
||||
}
|
||||
else if (!options.convert) {
|
||||
result.errors = this.createError('date.strict', { value }, state, options);
|
||||
}
|
||||
else {
|
||||
let type;
|
||||
if (internals.isIsoDate(this._flags.format)) {
|
||||
type = 'isoDate';
|
||||
}
|
||||
else if (this._flags.timestamp) {
|
||||
type = `timestamp.${this._flags.timestamp}`;
|
||||
}
|
||||
else {
|
||||
type = 'base';
|
||||
}
|
||||
|
||||
result.errors = this.createError(`date.${type}`, { value }, state, options);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static toDate(value, format, timestamp, multiplier) {
|
||||
|
||||
if (value instanceof Date) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string' ||
|
||||
(typeof value === 'number' && !isNaN(value) && isFinite(value))) {
|
||||
|
||||
const isIsoDate = format && internals.isIsoDate(format);
|
||||
if (!isIsoDate &&
|
||||
typeof value === 'string' &&
|
||||
/^[+-]?\d+(\.\d+)?$/.test(value)) {
|
||||
|
||||
value = parseFloat(value);
|
||||
}
|
||||
|
||||
let date;
|
||||
if (isIsoDate) {
|
||||
date = format.test(value) ? new Date(value.toString()) : internals.invalidDate;
|
||||
}
|
||||
else if (timestamp) {
|
||||
date = /^\s*$/.test(value) ? internals.invalidDate : new Date(value * multiplier);
|
||||
}
|
||||
else {
|
||||
date = new Date(value);
|
||||
}
|
||||
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
iso() {
|
||||
|
||||
if (this._flags.format === internals.isoDate) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.format = internals.isoDate;
|
||||
return obj;
|
||||
}
|
||||
|
||||
timestamp(type = 'javascript') {
|
||||
|
||||
const allowed = ['javascript', 'unix'];
|
||||
Hoek.assert(allowed.includes(type), '"type" must be one of "' + allowed.join('", "') + '"');
|
||||
|
||||
if (this._flags.timestamp === type) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.timestamp = type;
|
||||
obj._flags.multiplier = type === 'unix' ? 1000 : 1;
|
||||
return obj;
|
||||
}
|
||||
|
||||
_isIsoDate(value) {
|
||||
|
||||
return internals.isoDate.test(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
internals.compare = function (type, compare) {
|
||||
|
||||
return function (date) {
|
||||
|
||||
const isNow = date === 'now';
|
||||
const isRef = Ref.isRef(date);
|
||||
|
||||
if (!isNow && !isRef) {
|
||||
date = internals.Date.toDate(date);
|
||||
}
|
||||
|
||||
Hoek.assert(date, 'Invalid date format');
|
||||
|
||||
return this._test(type, date, function (value, state, options) {
|
||||
|
||||
let compareTo;
|
||||
if (isNow) {
|
||||
compareTo = Date.now();
|
||||
}
|
||||
else if (isRef) {
|
||||
const refValue = date(state.reference || state.parent, options);
|
||||
compareTo = internals.Date.toDate(refValue);
|
||||
|
||||
if (!compareTo) {
|
||||
return this.createError('date.ref', { ref: date, value: refValue }, state, options);
|
||||
}
|
||||
|
||||
compareTo = compareTo.getTime();
|
||||
}
|
||||
else {
|
||||
compareTo = date.getTime();
|
||||
}
|
||||
|
||||
if (compare(value.getTime(), compareTo)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('date.' + type, { limit: new Date(compareTo), value }, state, options);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
internals.Date.prototype.min = internals.compare('min', (value, date) => value >= date);
|
||||
internals.Date.prototype.max = internals.compare('max', (value, date) => value <= date);
|
||||
internals.Date.prototype.greater = internals.compare('greater', (value, date) => value > date);
|
||||
internals.Date.prototype.less = internals.compare('less', (value, date) => value < date);
|
||||
|
||||
|
||||
module.exports = new internals.Date();
|
||||
Generated
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const ObjectType = require('../object');
|
||||
const Ref = require('../../ref');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.Func = class extends ObjectType.constructor {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._flags.func = true;
|
||||
}
|
||||
|
||||
arity(n) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(n) && n >= 0, 'n must be a positive integer');
|
||||
|
||||
return this._test('arity', n, function (value, state, options) {
|
||||
|
||||
if (value.length === n) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('function.arity', { n }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
minArity(n) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(n) && n > 0, 'n must be a strict positive integer');
|
||||
|
||||
return this._test('minArity', n, function (value, state, options) {
|
||||
|
||||
if (value.length >= n) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('function.minArity', { n }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
maxArity(n) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(n) && n >= 0, 'n must be a positive integer');
|
||||
|
||||
return this._test('maxArity', n, function (value, state, options) {
|
||||
|
||||
if (value.length <= n) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('function.maxArity', { n }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
ref() {
|
||||
|
||||
return this._test('ref', null, function (value, state, options) {
|
||||
|
||||
if (Ref.isRef(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('function.ref', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
class() {
|
||||
|
||||
return this._test('class', null, function (value, state, options) {
|
||||
|
||||
if ((/^\s*class\s/).test(value.toString())) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('function.class', { value }, state, options);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = new internals.Func();
|
||||
Generated
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.Lazy = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'lazy';
|
||||
this._flags.once = true;
|
||||
this._cache = null;
|
||||
}
|
||||
|
||||
_init(fn, options) {
|
||||
|
||||
return this.set(fn, options);
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
let schema;
|
||||
if (this._cache) {
|
||||
schema = this._cache;
|
||||
}
|
||||
else {
|
||||
const result = { value };
|
||||
const lazy = this._flags.lazy;
|
||||
|
||||
if (!lazy) {
|
||||
result.errors = this.createError('lazy.base', null, state, options);
|
||||
return result;
|
||||
}
|
||||
|
||||
schema = lazy();
|
||||
|
||||
if (!(schema instanceof Any)) {
|
||||
result.errors = this.createError('lazy.schema', { schema }, state, options);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this._flags.once) {
|
||||
this._cache = schema;
|
||||
}
|
||||
}
|
||||
|
||||
return schema._validate(value, state, options);
|
||||
}
|
||||
|
||||
set(fn, options) {
|
||||
|
||||
Hoek.assert(typeof fn === 'function', 'You must provide a function as first argument');
|
||||
Hoek.assert(options === undefined || (options && typeof options === 'object' && !Array.isArray(options)), `Options must be an object`);
|
||||
|
||||
if (options) {
|
||||
const unknownOptions = Object.keys(options).filter((key) => !['once'].includes(key));
|
||||
Hoek.assert(unknownOptions.length === 0, `Options contain unknown keys: ${unknownOptions}`);
|
||||
Hoek.assert(options.once === undefined || typeof options.once === 'boolean', 'Option "once" must be a boolean');
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.lazy = fn;
|
||||
|
||||
if (options && options.once !== obj._flags.once) {
|
||||
obj._flags.once = options.once;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = new internals.Lazy();
|
||||
Generated
Vendored
+245
@@ -0,0 +1,245 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
const Ref = require('../../ref');
|
||||
|
||||
|
||||
const internals = {
|
||||
precisionRx: /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/,
|
||||
normalizeExponent(str) {
|
||||
|
||||
return str
|
||||
.replace(/\.?0+e/, 'e')
|
||||
.replace(/e\+/, 'e')
|
||||
.replace(/^\+/, '')
|
||||
.replace(/^(-?)0+([1-9])/, '$1$2');
|
||||
},
|
||||
normalizeDecimal(str) {
|
||||
|
||||
str = str
|
||||
.replace(/^\+/, '')
|
||||
.replace(/\.0+$/, '')
|
||||
.replace(/^(-?)0+([1-9])/, '$1$2');
|
||||
|
||||
if (str.includes('.') && str.endsWith('0')) {
|
||||
str = str.replace(/0+$/, '');
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.Number = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'number';
|
||||
this._flags.unsafe = false;
|
||||
this._invalids.add(Infinity);
|
||||
this._invalids.add(-Infinity);
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
const result = {
|
||||
errors: null,
|
||||
value
|
||||
};
|
||||
|
||||
if (typeof value === 'string' &&
|
||||
options.convert) {
|
||||
|
||||
const matches = value.match(/^\s*[+-]?\d+(?:\.\d+)?(?:e([+-]?\d+))?\s*$/i);
|
||||
if (matches) {
|
||||
|
||||
value = value.trim();
|
||||
result.value = parseFloat(value);
|
||||
|
||||
if (!this._flags.unsafe) {
|
||||
if (value.includes('e')) {
|
||||
if (internals.normalizeExponent(`${result.value / Math.pow(10, matches[1])}e${matches[1]}`) !== internals.normalizeExponent(value)) {
|
||||
result.errors = this.createError('number.unsafe', { value }, state, options);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (result.value.toString() !== internals.normalizeDecimal(value)) {
|
||||
result.errors = this.createError('number.unsafe', { value }, state, options);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isNumber = typeof result.value === 'number' && !isNaN(result.value);
|
||||
|
||||
if (options.convert && 'precision' in this._flags && isNumber) {
|
||||
|
||||
// This is conceptually equivalent to using toFixed but it should be much faster
|
||||
const precision = Math.pow(10, this._flags.precision);
|
||||
result.value = Math.round(result.value * precision) / precision;
|
||||
}
|
||||
|
||||
if (isNumber) {
|
||||
if (!this._flags.unsafe &&
|
||||
(value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER)) {
|
||||
result.errors = this.createError('number.unsafe', { value }, state, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.errors = this.createError('number.base', { value }, state, options);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
multiple(base) {
|
||||
|
||||
const isRef = Ref.isRef(base);
|
||||
|
||||
if (!isRef) {
|
||||
Hoek.assert(typeof base === 'number' && isFinite(base), 'multiple must be a number');
|
||||
Hoek.assert(base > 0, 'multiple must be greater than 0');
|
||||
}
|
||||
|
||||
return this._test('multiple', base, function (value, state, options) {
|
||||
|
||||
const divisor = isRef ? base(state.reference || state.parent, options) : base;
|
||||
|
||||
if (isRef && (typeof divisor !== 'number' || !isFinite(divisor))) {
|
||||
return this.createError('number.ref', { ref: base.key }, state, options);
|
||||
}
|
||||
|
||||
if (value % divisor === 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('number.multiple', { multiple: base, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
integer() {
|
||||
|
||||
return this._test('integer', undefined, function (value, state, options) {
|
||||
|
||||
return Math.trunc(value) - value === 0 ? value : this.createError('number.integer', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
unsafe(enabled = true) {
|
||||
|
||||
Hoek.assert(typeof enabled === 'boolean', 'enabled must be a boolean');
|
||||
|
||||
if (this._flags.unsafe === enabled) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.unsafe = enabled;
|
||||
return obj;
|
||||
}
|
||||
|
||||
negative() {
|
||||
|
||||
return this._test('negative', undefined, function (value, state, options) {
|
||||
|
||||
if (value < 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('number.negative', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
positive() {
|
||||
|
||||
return this._test('positive', undefined, function (value, state, options) {
|
||||
|
||||
if (value > 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('number.positive', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
precision(limit) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(limit), 'limit must be an integer');
|
||||
Hoek.assert(!('precision' in this._flags), 'precision already set');
|
||||
|
||||
const obj = this._test('precision', limit, function (value, state, options) {
|
||||
|
||||
const places = value.toString().match(internals.precisionRx);
|
||||
const decimals = Math.max((places[1] ? places[1].length : 0) - (places[2] ? parseInt(places[2], 10) : 0), 0);
|
||||
if (decimals <= limit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('number.precision', { limit, value }, state, options);
|
||||
});
|
||||
|
||||
obj._flags.precision = limit;
|
||||
return obj;
|
||||
}
|
||||
|
||||
port() {
|
||||
|
||||
return this._test('port', undefined, function (value, state, options) {
|
||||
|
||||
if (!Number.isSafeInteger(value) || value < 0 || value > 65535) {
|
||||
return this.createError('number.port', { value }, state, options);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
internals.compare = function (type, compare) {
|
||||
|
||||
return function (limit) {
|
||||
|
||||
const isRef = Ref.isRef(limit);
|
||||
const isNumber = typeof limit === 'number' && !isNaN(limit);
|
||||
|
||||
Hoek.assert(isNumber || isRef, 'limit must be a number or reference');
|
||||
|
||||
return this._test(type, limit, function (value, state, options) {
|
||||
|
||||
let compareTo;
|
||||
if (isRef) {
|
||||
compareTo = limit(state.reference || state.parent, options);
|
||||
|
||||
if (!(typeof compareTo === 'number' && !isNaN(compareTo))) {
|
||||
return this.createError('number.ref', { ref: limit.key }, state, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
compareTo = limit;
|
||||
}
|
||||
|
||||
if (compare(value, compareTo)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('number.' + type, { limit: compareTo, value }, state, options);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
internals.Number.prototype.min = internals.compare('min', (value, limit) => value >= limit);
|
||||
internals.Number.prototype.max = internals.compare('max', (value, limit) => value <= limit);
|
||||
internals.Number.prototype.greater = internals.compare('greater', (value, limit) => value > limit);
|
||||
internals.Number.prototype.less = internals.compare('less', (value, limit) => value < limit);
|
||||
|
||||
|
||||
module.exports = new internals.Number();
|
||||
Generated
Vendored
+952
@@ -0,0 +1,952 @@
|
||||
'use strict';
|
||||
|
||||
const Bourne = require('@hapi/bourne');
|
||||
const Hoek = require('@hapi/hoek');
|
||||
const Topo = require('@hapi/topo');
|
||||
|
||||
const Any = require('../any');
|
||||
const Errors = require('../../errors');
|
||||
const Cast = require('../../cast');
|
||||
const State = require('../state');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.Object = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'object';
|
||||
this._inner.children = null;
|
||||
this._inner.renames = [];
|
||||
this._inner.dependencies = [];
|
||||
this._inner.patterns = [];
|
||||
}
|
||||
|
||||
_init(...args) {
|
||||
|
||||
return args.length ? this.keys(...args) : this;
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
let target = value;
|
||||
const errors = [];
|
||||
const finish = () => {
|
||||
|
||||
return {
|
||||
value: target,
|
||||
errors: errors.length ? errors : null
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof value === 'string' &&
|
||||
options.convert) {
|
||||
|
||||
if (value.length > 1 &&
|
||||
(value[0] === '{' || /^\s*\{/.test(value))) {
|
||||
|
||||
try {
|
||||
value = Bourne.parse(value);
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
}
|
||||
|
||||
const type = this._flags.func ? 'function' : 'object';
|
||||
if (!value ||
|
||||
typeof value !== type ||
|
||||
Array.isArray(value)) {
|
||||
|
||||
errors.push(this.createError(type + '.base', { value }, state, options));
|
||||
return finish();
|
||||
}
|
||||
|
||||
// Skip if there are no other rules to test
|
||||
|
||||
if (!this._inner.renames.length &&
|
||||
!this._inner.dependencies.length &&
|
||||
!this._inner.children && // null allows any keys
|
||||
!this._inner.patterns.length) {
|
||||
|
||||
target = value;
|
||||
return finish();
|
||||
}
|
||||
|
||||
// Ensure target is a local copy (parsed) or shallow copy
|
||||
|
||||
if (target === value) {
|
||||
if (type === 'object') {
|
||||
target = Object.create(Object.getPrototypeOf(value));
|
||||
}
|
||||
else {
|
||||
target = function (...args) {
|
||||
|
||||
return value.apply(this, args);
|
||||
};
|
||||
|
||||
target.prototype = Hoek.clone(value.prototype);
|
||||
}
|
||||
|
||||
const valueKeys = Object.keys(value);
|
||||
for (let i = 0; i < valueKeys.length; ++i) {
|
||||
target[valueKeys[i]] = value[valueKeys[i]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
target = value;
|
||||
}
|
||||
|
||||
// Rename keys
|
||||
|
||||
const renamed = {};
|
||||
for (let i = 0; i < this._inner.renames.length; ++i) {
|
||||
const rename = this._inner.renames[i];
|
||||
|
||||
if (rename.isRegExp) {
|
||||
const targetKeys = Object.keys(target);
|
||||
const matchedTargetKeys = [];
|
||||
|
||||
for (let j = 0; j < targetKeys.length; ++j) {
|
||||
if (rename.from.test(targetKeys[j])) {
|
||||
matchedTargetKeys.push(targetKeys[j]);
|
||||
}
|
||||
}
|
||||
|
||||
const allUndefined = matchedTargetKeys.every((key) => target[key] === undefined);
|
||||
if (rename.options.ignoreUndefined && allUndefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rename.options.multiple &&
|
||||
renamed[rename.to]) {
|
||||
|
||||
errors.push(this.createError('object.rename.regex.multiple', { from: matchedTargetKeys, to: rename.to }, state, options));
|
||||
if (options.abortEarly) {
|
||||
return finish();
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(target, rename.to) &&
|
||||
!rename.options.override &&
|
||||
!renamed[rename.to]) {
|
||||
|
||||
errors.push(this.createError('object.rename.regex.override', { from: matchedTargetKeys, to: rename.to }, state, options));
|
||||
if (options.abortEarly) {
|
||||
return finish();
|
||||
}
|
||||
}
|
||||
|
||||
if (allUndefined) {
|
||||
delete target[rename.to];
|
||||
}
|
||||
else {
|
||||
target[rename.to] = target[matchedTargetKeys[matchedTargetKeys.length - 1]];
|
||||
}
|
||||
|
||||
renamed[rename.to] = true;
|
||||
|
||||
if (!rename.options.alias) {
|
||||
for (let j = 0; j < matchedTargetKeys.length; ++j) {
|
||||
delete target[matchedTargetKeys[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rename.options.ignoreUndefined && target[rename.from] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rename.options.multiple &&
|
||||
renamed[rename.to]) {
|
||||
|
||||
errors.push(this.createError('object.rename.multiple', { from: rename.from, to: rename.to }, state, options));
|
||||
if (options.abortEarly) {
|
||||
return finish();
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(target, rename.to) &&
|
||||
!rename.options.override &&
|
||||
!renamed[rename.to]) {
|
||||
|
||||
errors.push(this.createError('object.rename.override', { from: rename.from, to: rename.to }, state, options));
|
||||
if (options.abortEarly) {
|
||||
return finish();
|
||||
}
|
||||
}
|
||||
|
||||
if (target[rename.from] === undefined) {
|
||||
delete target[rename.to];
|
||||
}
|
||||
else {
|
||||
target[rename.to] = target[rename.from];
|
||||
}
|
||||
|
||||
renamed[rename.to] = true;
|
||||
|
||||
if (!rename.options.alias) {
|
||||
delete target[rename.from];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate schema
|
||||
|
||||
if (!this._inner.children && // null allows any keys
|
||||
!this._inner.patterns.length &&
|
||||
!this._inner.dependencies.length) {
|
||||
|
||||
return finish();
|
||||
}
|
||||
|
||||
const unprocessed = new Set(Object.keys(target));
|
||||
|
||||
if (this._inner.children) {
|
||||
const stripProps = [];
|
||||
|
||||
for (let i = 0; i < this._inner.children.length; ++i) {
|
||||
const child = this._inner.children[i];
|
||||
const key = child.key;
|
||||
const item = target[key];
|
||||
|
||||
unprocessed.delete(key);
|
||||
|
||||
const localState = new State(key, [...state.path, key], target, state.reference);
|
||||
const result = child.schema._validate(item, localState, options);
|
||||
if (result.errors) {
|
||||
errors.push(this.createError('object.child', { key, child: child.schema._getLabel(key), reason: result.errors }, localState, options));
|
||||
|
||||
if (options.abortEarly) {
|
||||
return finish();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (child.schema._flags.strip || (result.value === undefined && result.value !== item)) {
|
||||
stripProps.push(key);
|
||||
target[key] = result.finalValue;
|
||||
}
|
||||
else if (result.value !== undefined) {
|
||||
target[key] = result.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < stripProps.length; ++i) {
|
||||
delete target[stripProps[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown keys
|
||||
|
||||
if (unprocessed.size && this._inner.patterns.length) {
|
||||
|
||||
for (const key of unprocessed) {
|
||||
const localState = new State(key, [...state.path, key], target, state.reference);
|
||||
const item = target[key];
|
||||
|
||||
for (let i = 0; i < this._inner.patterns.length; ++i) {
|
||||
const pattern = this._inner.patterns[i];
|
||||
|
||||
if (pattern.regex ?
|
||||
pattern.regex.test(key) :
|
||||
!pattern.schema._validate(key, state, { ...options, abortEarly:true }).errors) {
|
||||
|
||||
unprocessed.delete(key);
|
||||
|
||||
const result = pattern.rule._validate(item, localState, options);
|
||||
if (result.errors) {
|
||||
errors.push(this.createError('object.child', {
|
||||
key,
|
||||
child: pattern.rule._getLabel(key),
|
||||
reason: result.errors
|
||||
}, localState, options));
|
||||
|
||||
if (options.abortEarly) {
|
||||
return finish();
|
||||
}
|
||||
}
|
||||
|
||||
target[key] = result.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unprocessed.size && (this._inner.children || this._inner.patterns.length)) {
|
||||
if ((options.stripUnknown && this._flags.allowUnknown !== true) ||
|
||||
options.skipFunctions) {
|
||||
|
||||
const stripUnknown = options.stripUnknown
|
||||
? (options.stripUnknown === true ? true : !!options.stripUnknown.objects)
|
||||
: false;
|
||||
|
||||
|
||||
for (const key of unprocessed) {
|
||||
if (stripUnknown) {
|
||||
delete target[key];
|
||||
unprocessed.delete(key);
|
||||
}
|
||||
else if (typeof target[key] === 'function') {
|
||||
unprocessed.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((this._flags.allowUnknown !== undefined ? !this._flags.allowUnknown : !options.allowUnknown)) {
|
||||
|
||||
for (const unprocessedKey of unprocessed) {
|
||||
errors.push(this.createError('object.allowUnknown', { child: unprocessedKey, value: target[unprocessedKey] }, {
|
||||
key: unprocessedKey,
|
||||
path: [...state.path, unprocessedKey]
|
||||
}, options, {}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate dependencies
|
||||
|
||||
for (let i = 0; i < this._inner.dependencies.length; ++i) {
|
||||
const dep = this._inner.dependencies[i];
|
||||
const hasKey = dep.key !== null;
|
||||
const splitKey = hasKey && dep.key.split('.');
|
||||
const localState = hasKey ? new State(splitKey[splitKey.length - 1], [...state.path, ...splitKey]) : new State(null, state.path);
|
||||
const err = internals[dep.type].call(this, dep.key, hasKey && Hoek.reach(target, dep.key, { functions: true }), dep.peers, target, localState, options);
|
||||
if (err instanceof Errors.Err) {
|
||||
errors.push(err);
|
||||
if (options.abortEarly) {
|
||||
return finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finish();
|
||||
}
|
||||
|
||||
keys(schema) {
|
||||
|
||||
Hoek.assert(schema === null || schema === undefined || typeof schema === 'object', 'Object schema must be a valid object');
|
||||
Hoek.assert(!schema || !(schema instanceof Any), 'Object schema cannot be a joi schema');
|
||||
|
||||
const obj = this.clone();
|
||||
|
||||
if (!schema) {
|
||||
obj._inner.children = null;
|
||||
return obj;
|
||||
}
|
||||
|
||||
const children = Object.keys(schema);
|
||||
|
||||
if (!children.length) {
|
||||
obj._inner.children = [];
|
||||
return obj;
|
||||
}
|
||||
|
||||
const topo = new Topo();
|
||||
if (obj._inner.children) {
|
||||
for (let i = 0; i < obj._inner.children.length; ++i) {
|
||||
const child = obj._inner.children[i];
|
||||
|
||||
// Only add the key if we are not going to replace it later
|
||||
if (!children.includes(child.key)) {
|
||||
topo.add(child, { after: child._refs, group: child.key });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < children.length; ++i) {
|
||||
const key = children[i];
|
||||
const child = schema[key];
|
||||
try {
|
||||
const cast = Cast.schema(this._currentJoi, child);
|
||||
topo.add({ key, schema: cast }, { after: cast._refs, group: key });
|
||||
}
|
||||
catch (castErr) {
|
||||
if (castErr.hasOwnProperty('path')) {
|
||||
castErr.path = key + '.' + castErr.path;
|
||||
}
|
||||
else {
|
||||
castErr.path = key;
|
||||
}
|
||||
|
||||
throw castErr;
|
||||
}
|
||||
}
|
||||
|
||||
obj._inner.children = topo.nodes;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
append(schema) {
|
||||
// Skip any changes
|
||||
if (schema === null || schema === undefined || Object.keys(schema).length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return this.keys(schema);
|
||||
}
|
||||
|
||||
unknown(allow) {
|
||||
|
||||
const value = allow !== false;
|
||||
|
||||
if (this._flags.allowUnknown === value) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.allowUnknown = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
length(limit) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
|
||||
|
||||
return this._test('length', limit, function (value, state, options) {
|
||||
|
||||
if (Object.keys(value).length === limit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('object.length', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
min(limit) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
|
||||
|
||||
return this._test('min', limit, function (value, state, options) {
|
||||
|
||||
if (Object.keys(value).length >= limit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('object.min', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
max(limit) {
|
||||
|
||||
Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
|
||||
|
||||
return this._test('max', limit, function (value, state, options) {
|
||||
|
||||
if (Object.keys(value).length <= limit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('object.max', { limit, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
pattern(pattern, schema) {
|
||||
|
||||
const isRegExp = pattern instanceof RegExp;
|
||||
Hoek.assert(isRegExp || pattern instanceof Any, 'pattern must be a regex or schema');
|
||||
Hoek.assert(schema !== undefined, 'Invalid rule');
|
||||
|
||||
if (isRegExp) {
|
||||
Hoek.assert(!pattern.flags.includes('g') && !pattern.flags.includes('y'), 'pattern should not use global or sticky mode');
|
||||
}
|
||||
|
||||
try {
|
||||
schema = Cast.schema(this._currentJoi, schema);
|
||||
}
|
||||
catch (castErr) {
|
||||
if (castErr.hasOwnProperty('path')) {
|
||||
castErr.message = `${castErr.message}(${castErr.path})`;
|
||||
}
|
||||
|
||||
throw castErr;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
if (isRegExp) {
|
||||
obj._inner.patterns.push({ regex: pattern, rule: schema });
|
||||
}
|
||||
else {
|
||||
obj._inner.patterns.push({ schema: pattern, rule: schema });
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
schema() {
|
||||
|
||||
return this._test('schema', null, function (value, state, options) {
|
||||
|
||||
if (value instanceof Any) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('object.schema', null, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
with(key, peers) {
|
||||
|
||||
Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');
|
||||
|
||||
return this._dependency('with', key, peers);
|
||||
}
|
||||
|
||||
without(key, peers) {
|
||||
|
||||
Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');
|
||||
|
||||
return this._dependency('without', key, peers);
|
||||
}
|
||||
|
||||
xor(...peers) {
|
||||
|
||||
peers = Hoek.flatten(peers);
|
||||
return this._dependency('xor', null, peers);
|
||||
}
|
||||
|
||||
oxor(...peers) {
|
||||
|
||||
return this._dependency('oxor', null, peers);
|
||||
}
|
||||
|
||||
or(...peers) {
|
||||
|
||||
peers = Hoek.flatten(peers);
|
||||
return this._dependency('or', null, peers);
|
||||
}
|
||||
|
||||
and(...peers) {
|
||||
|
||||
peers = Hoek.flatten(peers);
|
||||
return this._dependency('and', null, peers);
|
||||
}
|
||||
|
||||
nand(...peers) {
|
||||
|
||||
peers = Hoek.flatten(peers);
|
||||
return this._dependency('nand', null, peers);
|
||||
}
|
||||
|
||||
requiredKeys(...children) {
|
||||
|
||||
children = Hoek.flatten(children);
|
||||
return this.applyFunctionToChildren(children, 'required');
|
||||
}
|
||||
|
||||
optionalKeys(...children) {
|
||||
|
||||
children = Hoek.flatten(children);
|
||||
return this.applyFunctionToChildren(children, 'optional');
|
||||
}
|
||||
|
||||
forbiddenKeys(...children) {
|
||||
|
||||
children = Hoek.flatten(children);
|
||||
return this.applyFunctionToChildren(children, 'forbidden');
|
||||
}
|
||||
|
||||
rename(from, to, options) {
|
||||
|
||||
Hoek.assert(typeof from === 'string' || from instanceof RegExp, 'Rename missing the from argument');
|
||||
Hoek.assert(typeof to === 'string', 'Rename missing the to argument');
|
||||
Hoek.assert(to !== from, 'Cannot rename key to same name:', from);
|
||||
|
||||
for (let i = 0; i < this._inner.renames.length; ++i) {
|
||||
Hoek.assert(this._inner.renames[i].from !== from, 'Cannot rename the same key multiple times');
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
|
||||
obj._inner.renames.push({
|
||||
from,
|
||||
to,
|
||||
options: Hoek.applyToDefaults(internals.renameDefaults, options || {}),
|
||||
isRegExp: from instanceof RegExp
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
applyFunctionToChildren(children, fn, args = [], root) {
|
||||
|
||||
children = [].concat(children);
|
||||
Hoek.assert(children.length > 0, 'expected at least one children');
|
||||
|
||||
const groupedChildren = internals.groupChildren(children);
|
||||
let obj;
|
||||
|
||||
if ('' in groupedChildren) {
|
||||
obj = this[fn](...args);
|
||||
delete groupedChildren[''];
|
||||
}
|
||||
else {
|
||||
obj = this.clone();
|
||||
}
|
||||
|
||||
if (obj._inner.children) {
|
||||
root = root ? (root + '.') : '';
|
||||
|
||||
for (let i = 0; i < obj._inner.children.length; ++i) {
|
||||
const child = obj._inner.children[i];
|
||||
const group = groupedChildren[child.key];
|
||||
|
||||
if (group) {
|
||||
obj._inner.children[i] = {
|
||||
key: child.key,
|
||||
_refs: child._refs,
|
||||
schema: child.schema.applyFunctionToChildren(group, fn, args, root + child.key)
|
||||
};
|
||||
|
||||
delete groupedChildren[child.key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const remaining = Object.keys(groupedChildren);
|
||||
Hoek.assert(remaining.length === 0, 'unknown key(s)', remaining.join(', '));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
_dependency(type, key, peers) {
|
||||
|
||||
peers = [].concat(peers);
|
||||
for (let i = 0; i < peers.length; ++i) {
|
||||
Hoek.assert(typeof peers[i] === 'string', type, 'peers must be a string or array of strings');
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._inner.dependencies.push({ type, key, peers });
|
||||
return obj;
|
||||
}
|
||||
|
||||
describe(shallow) {
|
||||
|
||||
const description = super.describe();
|
||||
|
||||
if (description.rules) {
|
||||
for (let i = 0; i < description.rules.length; ++i) {
|
||||
const rule = description.rules[i];
|
||||
// Coverage off for future-proof descriptions, only object().assert() is use right now
|
||||
if (/* $lab:coverage:off$ */rule.arg &&
|
||||
typeof rule.arg === 'object' &&
|
||||
rule.arg.schema &&
|
||||
rule.arg.ref /* $lab:coverage:on$ */) {
|
||||
rule.arg = {
|
||||
schema: rule.arg.schema.describe(),
|
||||
ref: rule.arg.ref.toString()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inner.children &&
|
||||
!shallow) {
|
||||
|
||||
description.children = {};
|
||||
for (let i = 0; i < this._inner.children.length; ++i) {
|
||||
const child = this._inner.children[i];
|
||||
description.children[child.key] = child.schema.describe();
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inner.dependencies.length) {
|
||||
description.dependencies = Hoek.clone(this._inner.dependencies);
|
||||
}
|
||||
|
||||
if (this._inner.patterns.length) {
|
||||
description.patterns = [];
|
||||
|
||||
for (let i = 0; i < this._inner.patterns.length; ++i) {
|
||||
const pattern = this._inner.patterns[i];
|
||||
if (pattern.regex) {
|
||||
description.patterns.push({ regex: pattern.regex.toString(), rule: pattern.rule.describe() });
|
||||
}
|
||||
else {
|
||||
description.patterns.push({ schema: pattern.schema.describe(), rule: pattern.rule.describe() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inner.renames.length > 0) {
|
||||
description.renames = Hoek.clone(this._inner.renames);
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
assert(ref, schema, message) {
|
||||
|
||||
ref = Cast.ref(ref);
|
||||
Hoek.assert(ref.isContext || ref.depth > 1, 'Cannot use assertions for root level references - use direct key rules instead');
|
||||
message = message || 'pass the assertion test';
|
||||
Hoek.assert(typeof message === 'string', 'Message must be a string');
|
||||
|
||||
try {
|
||||
schema = Cast.schema(this._currentJoi, schema);
|
||||
}
|
||||
catch (castErr) {
|
||||
if (castErr.hasOwnProperty('path')) {
|
||||
castErr.message = `${castErr.message}(${castErr.path})`;
|
||||
}
|
||||
|
||||
throw castErr;
|
||||
}
|
||||
|
||||
const key = ref.path[ref.path.length - 1];
|
||||
const path = ref.path.join('.');
|
||||
|
||||
return this._test('assert', { schema, ref }, function (value, state, options) {
|
||||
|
||||
const result = schema._validate(ref(value), null, options, value);
|
||||
if (!result.errors) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const localState = new State(key, ref.path, state.parent, state.reference);
|
||||
return this.createError('object.assert', { ref: path, message }, localState, options);
|
||||
});
|
||||
}
|
||||
|
||||
type(constructor, name = constructor.name) {
|
||||
|
||||
Hoek.assert(typeof constructor === 'function', 'type must be a constructor function');
|
||||
const typeData = {
|
||||
name,
|
||||
ctor: constructor
|
||||
};
|
||||
|
||||
return this._test('type', typeData, function (value, state, options) {
|
||||
|
||||
if (value instanceof constructor) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('object.type', { type: typeData.name, value }, state, options);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.renameDefaults = {
|
||||
alias: false, // Keep old value in place
|
||||
multiple: false, // Allow renaming multiple keys into the same target
|
||||
override: false // Overrides an existing key
|
||||
};
|
||||
|
||||
|
||||
internals.groupChildren = function (children) {
|
||||
|
||||
children.sort();
|
||||
|
||||
const grouped = {};
|
||||
|
||||
for (let i = 0; i < children.length; ++i) {
|
||||
const child = children[i];
|
||||
Hoek.assert(typeof child === 'string', 'children must be strings');
|
||||
const group = child.split('.')[0];
|
||||
const childGroup = grouped[group] = (grouped[group] || []);
|
||||
childGroup.push(child.substring(group.length + 1));
|
||||
}
|
||||
|
||||
return grouped;
|
||||
};
|
||||
|
||||
|
||||
internals.keysToLabels = function (schema, keys) {
|
||||
|
||||
const children = schema._inner.children;
|
||||
|
||||
if (!children) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
const findLabel = function (key) {
|
||||
|
||||
const matchingChild = schema._currentJoi.reach(schema, key);
|
||||
return matchingChild ? matchingChild._getLabel(key) : key;
|
||||
};
|
||||
|
||||
if (Array.isArray(keys)) {
|
||||
return keys.map(findLabel);
|
||||
}
|
||||
|
||||
return findLabel(keys);
|
||||
};
|
||||
|
||||
|
||||
internals.with = function (key, value, peers, parent, state, options) {
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < peers.length; ++i) {
|
||||
|
||||
const peer = peers[i];
|
||||
const keysExist = Hoek.reach(parent, peer, { functions: true });
|
||||
if (keysExist === undefined) {
|
||||
|
||||
return this.createError('object.with', {
|
||||
main: key,
|
||||
mainWithLabel: internals.keysToLabels(this, key),
|
||||
peer,
|
||||
peerWithLabel: internals.keysToLabels(this, peer)
|
||||
}, state, options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.without = function (key, value, peers, parent, state, options) {
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < peers.length; ++i) {
|
||||
const peer = peers[i];
|
||||
const keysExist = Hoek.reach(parent, peer, { functions: true });
|
||||
if (keysExist !== undefined) {
|
||||
|
||||
return this.createError('object.without', {
|
||||
main: key,
|
||||
mainWithLabel: internals.keysToLabels(this, key),
|
||||
peer,
|
||||
peerWithLabel: internals.keysToLabels(this, peer)
|
||||
}, state, options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.xor = function (key, value, peers, parent, state, options) {
|
||||
|
||||
const present = [];
|
||||
for (let i = 0; i < peers.length; ++i) {
|
||||
const peer = peers[i];
|
||||
const keysExist = Hoek.reach(parent, peer, { functions: true });
|
||||
if (keysExist !== undefined) {
|
||||
present.push(peer);
|
||||
}
|
||||
}
|
||||
|
||||
if (present.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = { peers, peersWithLabels: internals.keysToLabels(this, peers) };
|
||||
|
||||
if (present.length === 0) {
|
||||
return this.createError('object.missing', context, state, options);
|
||||
}
|
||||
|
||||
context.present = present;
|
||||
context.presentWithLabels = internals.keysToLabels(this, present);
|
||||
|
||||
return this.createError('object.xor', context, state, options);
|
||||
};
|
||||
|
||||
|
||||
internals.oxor = function (key, value, peers, parent, state, options) {
|
||||
|
||||
const present = [];
|
||||
for (let i = 0; i < peers.length; ++i) {
|
||||
const peer = peers[i];
|
||||
const keysExist = Hoek.reach(parent, peer, { functions: true });
|
||||
if (keysExist !== undefined) {
|
||||
present.push(peer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!present.length ||
|
||||
present.length === 1) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const context = { peers, peersWithLabels: internals.keysToLabels(this, peers) };
|
||||
context.present = present;
|
||||
context.presentWithLabels = internals.keysToLabels(this, present);
|
||||
|
||||
return this.createError('object.oxor', context, state, options);
|
||||
};
|
||||
|
||||
|
||||
internals.or = function (key, value, peers, parent, state, options) {
|
||||
|
||||
for (let i = 0; i < peers.length; ++i) {
|
||||
const peer = peers[i];
|
||||
const keysExist = Hoek.reach(parent, peer, { functions: true });
|
||||
if (keysExist !== undefined) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return this.createError('object.missing', {
|
||||
peers,
|
||||
peersWithLabels: internals.keysToLabels(this, peers)
|
||||
}, state, options);
|
||||
};
|
||||
|
||||
|
||||
internals.and = function (key, value, peers, parent, state, options) {
|
||||
|
||||
const missing = [];
|
||||
const present = [];
|
||||
const count = peers.length;
|
||||
for (let i = 0; i < count; ++i) {
|
||||
const peer = peers[i];
|
||||
const keysExist = Hoek.reach(parent, peer, { functions: true });
|
||||
if (keysExist === undefined) {
|
||||
|
||||
missing.push(peer);
|
||||
}
|
||||
else {
|
||||
present.push(peer);
|
||||
}
|
||||
}
|
||||
|
||||
const aon = (missing.length === count || present.length === count);
|
||||
|
||||
if (!aon) {
|
||||
|
||||
return this.createError('object.and', {
|
||||
present,
|
||||
presentWithLabels: internals.keysToLabels(this, present),
|
||||
missing,
|
||||
missingWithLabels: internals.keysToLabels(this, missing)
|
||||
}, state, options);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.nand = function (key, value, peers, parent, state, options) {
|
||||
|
||||
const present = [];
|
||||
for (let i = 0; i < peers.length; ++i) {
|
||||
const peer = peers[i];
|
||||
const keysExist = Hoek.reach(parent, peer, { functions: true });
|
||||
if (keysExist !== undefined) {
|
||||
|
||||
present.push(peer);
|
||||
}
|
||||
}
|
||||
|
||||
const main = peers[0];
|
||||
const values = peers.slice(1);
|
||||
const allPresent = (present.length === peers.length);
|
||||
return allPresent ? this.createError('object.nand', {
|
||||
main,
|
||||
mainWithLabel: internals.keysToLabels(this, main),
|
||||
peers: values,
|
||||
peersWithLabels: internals.keysToLabels(this, values)
|
||||
}, state, options) : null;
|
||||
};
|
||||
|
||||
|
||||
module.exports = new internals.Object();
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = class {
|
||||
constructor(key, path, parent, reference) {
|
||||
|
||||
this.key = key;
|
||||
this.path = path;
|
||||
this.parent = parent;
|
||||
this.reference = reference;
|
||||
}
|
||||
};
|
||||
Generated
Vendored
+724
@@ -0,0 +1,724 @@
|
||||
'use strict';
|
||||
|
||||
const Net = require('net');
|
||||
|
||||
const Address = require('@hapi/address');
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
const Ref = require('../../ref');
|
||||
const JoiDate = require('../date');
|
||||
|
||||
const Uri = require('./uri');
|
||||
const Ip = require('./ip');
|
||||
|
||||
|
||||
const internals = {
|
||||
uriRegex: Uri.createUriRegex(),
|
||||
ipRegex: Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], 'optional'),
|
||||
guidBrackets: {
|
||||
'{': '}', '[': ']', '(': ')', '': ''
|
||||
},
|
||||
guidVersions: {
|
||||
uuidv1: '1',
|
||||
uuidv2: '2',
|
||||
uuidv3: '3',
|
||||
uuidv4: '4',
|
||||
uuidv5: '5'
|
||||
},
|
||||
cidrPresences: ['required', 'optional', 'forbidden'],
|
||||
normalizationForms: ['NFC', 'NFD', 'NFKC', 'NFKD']
|
||||
};
|
||||
|
||||
|
||||
internals.String = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'string';
|
||||
this._invalids.add('');
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
if (typeof value === 'string' &&
|
||||
options.convert) {
|
||||
|
||||
if (this._flags.normalize) {
|
||||
value = value.normalize(this._flags.normalize);
|
||||
}
|
||||
|
||||
if (this._flags.case) {
|
||||
value = (this._flags.case === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase());
|
||||
}
|
||||
|
||||
if (this._flags.trim) {
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
if (this._inner.replacements) {
|
||||
|
||||
for (let i = 0; i < this._inner.replacements.length; ++i) {
|
||||
const replacement = this._inner.replacements[i];
|
||||
value = value.replace(replacement.pattern, replacement.replacement);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._flags.truncate) {
|
||||
for (let i = 0; i < this._tests.length; ++i) {
|
||||
const test = this._tests[i];
|
||||
if (test.name === 'max') {
|
||||
value = value.slice(0, test.arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._flags.byteAligned && value.length % 2 !== 0) {
|
||||
value = `0${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
value,
|
||||
errors: (typeof value === 'string') ? null : this.createError('string.base', { value }, state, options)
|
||||
};
|
||||
}
|
||||
|
||||
insensitive() {
|
||||
|
||||
if (this._flags.insensitive) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.insensitive = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
creditCard() {
|
||||
|
||||
return this._test('creditCard', undefined, function (value, state, options) {
|
||||
|
||||
let i = value.length;
|
||||
let sum = 0;
|
||||
let mul = 1;
|
||||
|
||||
while (i--) {
|
||||
const char = value.charAt(i) * mul;
|
||||
sum = sum + (char - (char > 9) * 9);
|
||||
mul = mul ^ 3;
|
||||
}
|
||||
|
||||
const check = (sum % 10 === 0) && (sum > 0);
|
||||
return check ? value : this.createError('string.creditCard', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
regex(pattern, patternOptions) {
|
||||
|
||||
Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp');
|
||||
Hoek.assert(!pattern.flags.includes('g') && !pattern.flags.includes('y'), 'pattern should not use global or sticky mode');
|
||||
|
||||
const patternObject = { pattern };
|
||||
|
||||
if (typeof patternOptions === 'string') {
|
||||
patternObject.name = patternOptions;
|
||||
}
|
||||
else if (typeof patternOptions === 'object') {
|
||||
patternObject.invert = !!patternOptions.invert;
|
||||
|
||||
if (patternOptions.name) {
|
||||
patternObject.name = patternOptions.name;
|
||||
}
|
||||
}
|
||||
|
||||
const errorCode = ['string.regex', patternObject.invert ? '.invert' : '', patternObject.name ? '.name' : '.base'].join('');
|
||||
|
||||
return this._test('regex', patternObject, function (value, state, options) {
|
||||
|
||||
const patternMatch = patternObject.pattern.test(value);
|
||||
|
||||
if (patternMatch ^ patternObject.invert) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError(errorCode, { name: patternObject.name, pattern: patternObject.pattern, value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
alphanum() {
|
||||
|
||||
return this._test('alphanum', undefined, function (value, state, options) {
|
||||
|
||||
if (/^[a-zA-Z0-9]+$/.test(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.alphanum', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
token() {
|
||||
|
||||
return this._test('token', undefined, function (value, state, options) {
|
||||
|
||||
if (/^\w+$/.test(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.token', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
email(validationOptions) {
|
||||
|
||||
if (validationOptions) {
|
||||
Hoek.assert(typeof validationOptions === 'object', 'email options must be an object');
|
||||
|
||||
// Migration validation for unsupported options
|
||||
|
||||
Hoek.assert(validationOptions.checkDNS === undefined, 'checkDNS option is not supported');
|
||||
Hoek.assert(validationOptions.errorLevel === undefined, 'errorLevel option is not supported');
|
||||
Hoek.assert(validationOptions.minDomainAtoms === undefined, 'minDomainAtoms option is not supported, use minDomainSegments instead');
|
||||
Hoek.assert(validationOptions.tldBlacklist === undefined, 'tldBlacklist option is not supported, use tlds.deny instead');
|
||||
Hoek.assert(validationOptions.tldWhitelist === undefined, 'tldWhitelist option is not supported, use tlds.allow instead');
|
||||
|
||||
// Validate options
|
||||
|
||||
if (validationOptions.tlds &&
|
||||
typeof validationOptions.tlds === 'object') {
|
||||
|
||||
Hoek.assert(validationOptions.tlds.allow === undefined ||
|
||||
validationOptions.tlds.allow === false ||
|
||||
validationOptions.tlds.allow === true ||
|
||||
Array.isArray(validationOptions.tlds.allow) ||
|
||||
validationOptions.tlds.allow instanceof Set, 'tlds.allow must be an array, Set, or boolean');
|
||||
|
||||
Hoek.assert(validationOptions.tlds.deny === undefined ||
|
||||
Array.isArray(validationOptions.tlds.deny) ||
|
||||
validationOptions.tlds.deny instanceof Set, 'tlds.deny must be an array or Set');
|
||||
|
||||
const normalizeTable = (table) => {
|
||||
|
||||
if (table === undefined ||
|
||||
typeof table === 'boolean' ||
|
||||
table instanceof Set) {
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
return new Set(table);
|
||||
};
|
||||
|
||||
validationOptions = Object.assign({}, validationOptions); // Shallow cloned
|
||||
validationOptions.tlds = {
|
||||
allow: normalizeTable(validationOptions.tlds.allow),
|
||||
deny: normalizeTable(validationOptions.tlds.deny)
|
||||
};
|
||||
}
|
||||
|
||||
Hoek.assert(validationOptions.minDomainSegments === undefined ||
|
||||
Number.isSafeInteger(validationOptions.minDomainSegments) && validationOptions.minDomainSegments > 0, 'minDomainSegments must be a positive integer');
|
||||
}
|
||||
|
||||
return this._test('email', validationOptions, function (value, state, options) {
|
||||
|
||||
if (Address.email.isValid(value, validationOptions)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.email', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
ip(ipOptions = {}) {
|
||||
|
||||
let regex = internals.ipRegex;
|
||||
Hoek.assert(typeof ipOptions === 'object', 'options must be an object');
|
||||
|
||||
if (ipOptions.cidr) {
|
||||
Hoek.assert(typeof ipOptions.cidr === 'string', 'cidr must be a string');
|
||||
ipOptions.cidr = ipOptions.cidr.toLowerCase();
|
||||
|
||||
Hoek.assert(Hoek.contain(internals.cidrPresences, ipOptions.cidr), 'cidr must be one of ' + internals.cidrPresences.join(', '));
|
||||
|
||||
// If we only received a `cidr` setting, create a regex for it. But we don't need to create one if `cidr` is "optional" since that is the default
|
||||
if (!ipOptions.version && ipOptions.cidr !== 'optional') {
|
||||
regex = Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], ipOptions.cidr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Set our default cidr strategy
|
||||
ipOptions.cidr = 'optional';
|
||||
}
|
||||
|
||||
let versions;
|
||||
if (ipOptions.version) {
|
||||
if (!Array.isArray(ipOptions.version)) {
|
||||
ipOptions.version = [ipOptions.version];
|
||||
}
|
||||
|
||||
Hoek.assert(ipOptions.version.length >= 1, 'version must have at least 1 version specified');
|
||||
|
||||
versions = [];
|
||||
for (let i = 0; i < ipOptions.version.length; ++i) {
|
||||
let version = ipOptions.version[i];
|
||||
Hoek.assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
|
||||
version = version.toLowerCase();
|
||||
Hoek.assert(Ip.versions[version], 'version at position ' + i + ' must be one of ' + Object.keys(Ip.versions).join(', '));
|
||||
versions.push(version);
|
||||
}
|
||||
|
||||
// Make sure we have a set of versions
|
||||
versions = Array.from(new Set(versions));
|
||||
|
||||
regex = Ip.createIpRegex(versions, ipOptions.cidr);
|
||||
}
|
||||
|
||||
return this._test('ip', ipOptions, function (value, state, options) {
|
||||
|
||||
if (regex.test(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (versions) {
|
||||
return this.createError('string.ipVersion', { value, cidr: ipOptions.cidr, version: versions }, state, options);
|
||||
}
|
||||
|
||||
return this.createError('string.ip', { value, cidr: ipOptions.cidr }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
uri(uriOptions) {
|
||||
|
||||
let customScheme = '';
|
||||
let allowRelative = false;
|
||||
let relativeOnly = false;
|
||||
let allowQuerySquareBrackets = false;
|
||||
let regex = internals.uriRegex;
|
||||
|
||||
if (uriOptions) {
|
||||
Hoek.assert(typeof uriOptions === 'object', 'options must be an object');
|
||||
|
||||
const unknownOptions = Object.keys(uriOptions).filter((key) => !['scheme', 'allowRelative', 'relativeOnly', 'allowQuerySquareBrackets'].includes(key));
|
||||
Hoek.assert(unknownOptions.length === 0, `options contain unknown keys: ${unknownOptions}`);
|
||||
|
||||
if (uriOptions.scheme) {
|
||||
Hoek.assert(uriOptions.scheme instanceof RegExp || typeof uriOptions.scheme === 'string' || Array.isArray(uriOptions.scheme), 'scheme must be a RegExp, String, or Array');
|
||||
|
||||
if (!Array.isArray(uriOptions.scheme)) {
|
||||
uriOptions.scheme = [uriOptions.scheme];
|
||||
}
|
||||
|
||||
Hoek.assert(uriOptions.scheme.length >= 1, 'scheme must have at least 1 scheme specified');
|
||||
|
||||
// Flatten the array into a string to be used to match the schemes.
|
||||
for (let i = 0; i < uriOptions.scheme.length; ++i) {
|
||||
const scheme = uriOptions.scheme[i];
|
||||
Hoek.assert(scheme instanceof RegExp || typeof scheme === 'string', 'scheme at position ' + i + ' must be a RegExp or String');
|
||||
|
||||
// Add OR separators if a value already exists
|
||||
customScheme = customScheme + (customScheme ? '|' : '');
|
||||
|
||||
// If someone wants to match HTTP or HTTPS for example then we need to support both RegExp and String so we don't escape their pattern unknowingly.
|
||||
if (scheme instanceof RegExp) {
|
||||
customScheme = customScheme + scheme.source;
|
||||
}
|
||||
else {
|
||||
Hoek.assert(/[a-zA-Z][a-zA-Z0-9+-\.]*/.test(scheme), 'scheme at position ' + i + ' must be a valid scheme');
|
||||
customScheme = customScheme + Hoek.escapeRegex(scheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uriOptions.allowRelative) {
|
||||
allowRelative = true;
|
||||
}
|
||||
|
||||
if (uriOptions.relativeOnly) {
|
||||
relativeOnly = true;
|
||||
}
|
||||
|
||||
if (uriOptions.allowQuerySquareBrackets) {
|
||||
allowQuerySquareBrackets = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (customScheme || allowRelative || relativeOnly || allowQuerySquareBrackets) {
|
||||
regex = Uri.createUriRegex(customScheme, allowRelative, relativeOnly, allowQuerySquareBrackets);
|
||||
}
|
||||
|
||||
return this._test('uri', uriOptions, function (value, state, options) {
|
||||
|
||||
if (regex.test(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (relativeOnly) {
|
||||
return this.createError('string.uriRelativeOnly', { value }, state, options);
|
||||
}
|
||||
|
||||
if (customScheme) {
|
||||
return this.createError('string.uriCustomScheme', { scheme: customScheme, value }, state, options);
|
||||
}
|
||||
|
||||
return this.createError('string.uri', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
isoDate() {
|
||||
|
||||
return this._test('isoDate', undefined, function (value, state, options) {
|
||||
|
||||
if (JoiDate._isIsoDate(value)) {
|
||||
if (!options.convert) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const d = new Date(value);
|
||||
if (!isNaN(d.getTime())) {
|
||||
return d.toISOString();
|
||||
}
|
||||
}
|
||||
|
||||
return this.createError('string.isoDate', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
guid(guidOptions) {
|
||||
|
||||
let versionNumbers = '';
|
||||
|
||||
if (guidOptions && guidOptions.version) {
|
||||
if (!Array.isArray(guidOptions.version)) {
|
||||
guidOptions.version = [guidOptions.version];
|
||||
}
|
||||
|
||||
Hoek.assert(guidOptions.version.length >= 1, 'version must have at least 1 valid version specified');
|
||||
const versions = new Set();
|
||||
|
||||
for (let i = 0; i < guidOptions.version.length; ++i) {
|
||||
let version = guidOptions.version[i];
|
||||
Hoek.assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
|
||||
version = version.toLowerCase();
|
||||
const versionNumber = internals.guidVersions[version];
|
||||
Hoek.assert(versionNumber, 'version at position ' + i + ' must be one of ' + Object.keys(internals.guidVersions).join(', '));
|
||||
Hoek.assert(!(versions.has(versionNumber)), 'version at position ' + i + ' must not be a duplicate.');
|
||||
|
||||
versionNumbers += versionNumber;
|
||||
versions.add(versionNumber);
|
||||
}
|
||||
}
|
||||
|
||||
const guidRegex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}([:-]?)[0-9A-F]{4}\\2?[${versionNumbers || '0-9A-F'}][0-9A-F]{3}\\2?[${versionNumbers ? '89AB' : '0-9A-F'}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`, 'i');
|
||||
|
||||
return this._test('guid', guidOptions, function (value, state, options) {
|
||||
|
||||
const results = guidRegex.exec(value);
|
||||
|
||||
if (!results) {
|
||||
return this.createError('string.guid', { value }, state, options);
|
||||
}
|
||||
|
||||
// Matching braces
|
||||
if (internals.guidBrackets[results[1]] !== results[results.length - 1]) {
|
||||
return this.createError('string.guid', { value }, state, options);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
hex(hexOptions = {}) {
|
||||
|
||||
Hoek.assert(typeof hexOptions === 'object', 'hex options must be an object');
|
||||
Hoek.assert(typeof hexOptions.byteAligned === 'undefined' || typeof hexOptions.byteAligned === 'boolean',
|
||||
'byteAligned must be boolean');
|
||||
|
||||
const byteAligned = hexOptions.byteAligned === true;
|
||||
const regex = /^[a-f0-9]+$/i;
|
||||
|
||||
const obj = this._test('hex', regex, function (value, state, options) {
|
||||
|
||||
if (regex.test(value)) {
|
||||
if (byteAligned && value.length % 2 !== 0) {
|
||||
return this.createError('string.hexAlign', { value }, state, options);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.hex', { value }, state, options);
|
||||
});
|
||||
|
||||
if (byteAligned) {
|
||||
obj._flags.byteAligned = true;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
base64(base64Options = {}) {
|
||||
|
||||
// Validation.
|
||||
Hoek.assert(typeof base64Options === 'object', 'base64 options must be an object');
|
||||
Hoek.assert(typeof base64Options.paddingRequired === 'undefined' || typeof base64Options.paddingRequired === 'boolean',
|
||||
'paddingRequired must be boolean');
|
||||
|
||||
// Determine if padding is required.
|
||||
const paddingRequired = base64Options.paddingRequired === false ?
|
||||
base64Options.paddingRequired
|
||||
: base64Options.paddingRequired || true;
|
||||
|
||||
// Set validation based on preference.
|
||||
const regex = paddingRequired ?
|
||||
// Padding is required.
|
||||
/^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
|
||||
// Padding is optional.
|
||||
: /^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/;
|
||||
|
||||
return this._test('base64', regex, function (value, state, options) {
|
||||
|
||||
if (regex.test(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.base64', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
dataUri(dataUriOptions = {}) {
|
||||
|
||||
const regex = /^data:[\w+.-]+\/[\w+.-]+;((charset=[\w-]+|base64),)?(.*)$/;
|
||||
|
||||
// Determine if padding is required.
|
||||
const paddingRequired = dataUriOptions.paddingRequired === false ?
|
||||
dataUriOptions.paddingRequired
|
||||
: dataUriOptions.paddingRequired || true;
|
||||
|
||||
const base64regex = paddingRequired ?
|
||||
/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
|
||||
: /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/;
|
||||
|
||||
return this._test('dataUri', regex, function (value, state, options) {
|
||||
|
||||
const matches = value.match(regex);
|
||||
|
||||
if (matches) {
|
||||
if (!matches[2]) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (matches[2] !== 'base64') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (base64regex.test(matches[3])) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return this.createError('string.dataUri', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
hostname() {
|
||||
|
||||
const regex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
|
||||
|
||||
return this._test('hostname', undefined, function (value, state, options) {
|
||||
|
||||
if ((value.length <= 255 && regex.test(value)) ||
|
||||
Net.isIPv6(value)) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.hostname', { value }, state, options);
|
||||
});
|
||||
}
|
||||
|
||||
normalize(form = 'NFC') {
|
||||
|
||||
Hoek.assert(Hoek.contain(internals.normalizationForms, form), 'normalization form must be one of ' + internals.normalizationForms.join(', '));
|
||||
|
||||
const obj = this._test('normalize', form, function (value, state, options) {
|
||||
|
||||
if (options.convert ||
|
||||
value === value.normalize(form)) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.normalize', { value, form }, state, options);
|
||||
});
|
||||
|
||||
obj._flags.normalize = form;
|
||||
return obj;
|
||||
}
|
||||
|
||||
lowercase() {
|
||||
|
||||
const obj = this._test('lowercase', undefined, function (value, state, options) {
|
||||
|
||||
if (options.convert ||
|
||||
value === value.toLocaleLowerCase()) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.lowercase', { value }, state, options);
|
||||
});
|
||||
|
||||
obj._flags.case = 'lower';
|
||||
return obj;
|
||||
}
|
||||
|
||||
uppercase() {
|
||||
|
||||
const obj = this._test('uppercase', undefined, function (value, state, options) {
|
||||
|
||||
if (options.convert ||
|
||||
value === value.toLocaleUpperCase()) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.uppercase', { value }, state, options);
|
||||
});
|
||||
|
||||
obj._flags.case = 'upper';
|
||||
return obj;
|
||||
}
|
||||
|
||||
trim(enabled = true) {
|
||||
|
||||
Hoek.assert(typeof enabled === 'boolean', 'option must be a boolean');
|
||||
|
||||
if ((this._flags.trim && enabled) || (!this._flags.trim && !enabled)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
let obj;
|
||||
if (enabled) {
|
||||
obj = this._test('trim', undefined, function (value, state, options) {
|
||||
|
||||
if (options.convert ||
|
||||
value === value.trim()) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.trim', { value }, state, options);
|
||||
});
|
||||
}
|
||||
else {
|
||||
obj = this.clone();
|
||||
obj._tests = obj._tests.filter((test) => test.name !== 'trim');
|
||||
}
|
||||
|
||||
obj._flags.trim = enabled;
|
||||
return obj;
|
||||
}
|
||||
|
||||
replace(pattern, replacement) {
|
||||
|
||||
if (typeof pattern === 'string') {
|
||||
pattern = new RegExp(Hoek.escapeRegex(pattern), 'g');
|
||||
}
|
||||
|
||||
Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp');
|
||||
Hoek.assert(typeof replacement === 'string', 'replacement must be a String');
|
||||
|
||||
// This can not be considere a test like trim, we can't "reject"
|
||||
// anything from this rule, so just clone the current object
|
||||
const obj = this.clone();
|
||||
|
||||
if (!obj._inner.replacements) {
|
||||
obj._inner.replacements = [];
|
||||
}
|
||||
|
||||
obj._inner.replacements.push({
|
||||
pattern,
|
||||
replacement
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
truncate(enabled) {
|
||||
|
||||
const value = enabled === undefined ? true : !!enabled;
|
||||
|
||||
if (this._flags.truncate === value) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const obj = this.clone();
|
||||
obj._flags.truncate = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
internals.compare = function (type, compare) {
|
||||
|
||||
return function (limit, encoding) {
|
||||
|
||||
const isRef = Ref.isRef(limit);
|
||||
|
||||
Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
|
||||
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);
|
||||
|
||||
return this._test(type, limit, function (value, state, options) {
|
||||
|
||||
let compareTo;
|
||||
if (isRef) {
|
||||
compareTo = limit(state.reference || state.parent, options);
|
||||
|
||||
if (!Number.isSafeInteger(compareTo)) {
|
||||
return this.createError('string.ref', { ref: limit, value: compareTo }, state, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
compareTo = limit;
|
||||
}
|
||||
|
||||
if (compare(value, compareTo, encoding)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.createError('string.' + type, { limit: compareTo, value, encoding }, state, options);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
internals.String.prototype.min = internals.compare('min', (value, limit, encoding) => {
|
||||
|
||||
const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
|
||||
return length >= limit;
|
||||
});
|
||||
|
||||
|
||||
internals.String.prototype.max = internals.compare('max', (value, limit, encoding) => {
|
||||
|
||||
const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
|
||||
return length <= limit;
|
||||
});
|
||||
|
||||
|
||||
internals.String.prototype.length = internals.compare('length', (value, limit, encoding) => {
|
||||
|
||||
const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
|
||||
return length === limit;
|
||||
});
|
||||
|
||||
// Aliases
|
||||
|
||||
internals.String.prototype.uuid = internals.String.prototype.guid;
|
||||
|
||||
module.exports = new internals.String();
|
||||
Generated
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
const RFC3986 = require('./rfc3986');
|
||||
|
||||
|
||||
const internals = {
|
||||
Ip: {
|
||||
cidrs: {
|
||||
ipv4: {
|
||||
required: '\\/(?:' + RFC3986.ipv4Cidr + ')',
|
||||
optional: '(?:\\/(?:' + RFC3986.ipv4Cidr + '))?',
|
||||
forbidden: ''
|
||||
},
|
||||
ipv6: {
|
||||
required: '\\/' + RFC3986.ipv6Cidr,
|
||||
optional: '(?:\\/' + RFC3986.ipv6Cidr + ')?',
|
||||
forbidden: ''
|
||||
},
|
||||
ipvfuture: {
|
||||
required: '\\/' + RFC3986.ipv6Cidr,
|
||||
optional: '(?:\\/' + RFC3986.ipv6Cidr + ')?',
|
||||
forbidden: ''
|
||||
}
|
||||
},
|
||||
versions: {
|
||||
ipv4: RFC3986.IPv4address,
|
||||
ipv6: RFC3986.IPv6address,
|
||||
ipvfuture: RFC3986.IPvFuture
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.Ip.createIpRegex = function (versions, cidr) {
|
||||
|
||||
let regex;
|
||||
for (let i = 0; i < versions.length; ++i) {
|
||||
const version = versions[i];
|
||||
if (!regex) {
|
||||
regex = '^(?:' + internals.Ip.versions[version] + internals.Ip.cidrs[version][cidr];
|
||||
}
|
||||
else {
|
||||
regex += '|' + internals.Ip.versions[version] + internals.Ip.cidrs[version][cidr];
|
||||
}
|
||||
}
|
||||
|
||||
return new RegExp(regex + ')$');
|
||||
};
|
||||
|
||||
module.exports = internals.Ip;
|
||||
Generated
Vendored
+214
@@ -0,0 +1,214 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {
|
||||
rfc3986: {}
|
||||
};
|
||||
|
||||
|
||||
internals.generate = function () {
|
||||
|
||||
/**
|
||||
* elements separated by forward slash ("/") are alternatives.
|
||||
*/
|
||||
const or = '|';
|
||||
|
||||
/**
|
||||
* Rule to support zero-padded addresses.
|
||||
*/
|
||||
const zeroPad = '0?';
|
||||
|
||||
/**
|
||||
* DIGIT = %x30-39 ; 0-9
|
||||
*/
|
||||
const digit = '0-9';
|
||||
const digitOnly = '[' + digit + ']';
|
||||
|
||||
/**
|
||||
* ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
|
||||
*/
|
||||
const alpha = 'a-zA-Z';
|
||||
const alphaOnly = '[' + alpha + ']';
|
||||
|
||||
/**
|
||||
* IPv4
|
||||
* cidr = DIGIT ; 0-9
|
||||
* / %x31-32 DIGIT ; 10-29
|
||||
* / "3" %x30-32 ; 30-32
|
||||
*/
|
||||
internals.rfc3986.ipv4Cidr = digitOnly + or + '[1-2]' + digitOnly + or + '3' + '[0-2]';
|
||||
|
||||
/**
|
||||
* IPv6
|
||||
* cidr = DIGIT ; 0-9
|
||||
* / %x31-39 DIGIT ; 10-99
|
||||
* / "1" %x0-1 DIGIT ; 100-119
|
||||
* / "12" %x0-8 ; 120-128
|
||||
*/
|
||||
internals.rfc3986.ipv6Cidr = '(?:' + zeroPad + zeroPad + digitOnly + or + zeroPad + '[1-9]' + digitOnly + or + '1' + '[01]' + digitOnly + or + '12[0-8])';
|
||||
|
||||
/**
|
||||
* HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
|
||||
*/
|
||||
const hexDigit = digit + 'A-Fa-f';
|
||||
const hexDigitOnly = '[' + hexDigit + ']';
|
||||
|
||||
/**
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
*/
|
||||
const unreserved = alpha + digit + '-\\._~';
|
||||
|
||||
/**
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
const subDelims = '!\\$&\'\\(\\)\\*\\+,;=';
|
||||
|
||||
/**
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
*/
|
||||
const pctEncoded = '%' + hexDigit;
|
||||
|
||||
/**
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
*/
|
||||
const pchar = unreserved + pctEncoded + subDelims + ':@';
|
||||
const pcharOnly = '[' + pchar + ']';
|
||||
|
||||
/**
|
||||
* squareBrackets example: []
|
||||
*/
|
||||
const squareBrackets = '\\[\\]';
|
||||
|
||||
/**
|
||||
* dec-octet = DIGIT ; 0-9
|
||||
* / %x31-39 DIGIT ; 10-99
|
||||
* / "1" 2DIGIT ; 100-199
|
||||
* / "2" %x30-34 DIGIT ; 200-249
|
||||
* / "25" %x30-35 ; 250-255
|
||||
*/
|
||||
const decOctect = '(?:' + zeroPad + zeroPad + digitOnly + or + zeroPad + '[1-9]' + digitOnly + or + '1' + digitOnly + digitOnly + or + '2' + '[0-4]' + digitOnly + or + '25' + '[0-5])';
|
||||
|
||||
/**
|
||||
* IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
|
||||
*/
|
||||
internals.rfc3986.IPv4address = '(?:' + decOctect + '\\.){3}' + decOctect;
|
||||
|
||||
/**
|
||||
* h16 = 1*4HEXDIG ; 16 bits of address represented in hexadecimal
|
||||
* ls32 = ( h16 ":" h16 ) / IPv4address ; least-significant 32 bits of address
|
||||
* IPv6address = 6( h16 ":" ) ls32
|
||||
* / "::" 5( h16 ":" ) ls32
|
||||
* / [ h16 ] "::" 4( h16 ":" ) ls32
|
||||
* / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
|
||||
* / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
|
||||
* / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
|
||||
* / [ *4( h16 ":" ) h16 ] "::" ls32
|
||||
* / [ *5( h16 ":" ) h16 ] "::" h16
|
||||
* / [ *6( h16 ":" ) h16 ] "::"
|
||||
*/
|
||||
const h16 = hexDigitOnly + '{1,4}';
|
||||
const ls32 = '(?:' + h16 + ':' + h16 + '|' + internals.rfc3986.IPv4address + ')';
|
||||
const IPv6SixHex = '(?:' + h16 + ':){6}' + ls32;
|
||||
const IPv6FiveHex = '::(?:' + h16 + ':){5}' + ls32;
|
||||
const IPv6FourHex = '(?:' + h16 + ')?::(?:' + h16 + ':){4}' + ls32;
|
||||
const IPv6ThreeHex = '(?:(?:' + h16 + ':){0,1}' + h16 + ')?::(?:' + h16 + ':){3}' + ls32;
|
||||
const IPv6TwoHex = '(?:(?:' + h16 + ':){0,2}' + h16 + ')?::(?:' + h16 + ':){2}' + ls32;
|
||||
const IPv6OneHex = '(?:(?:' + h16 + ':){0,3}' + h16 + ')?::' + h16 + ':' + ls32;
|
||||
const IPv6NoneHex = '(?:(?:' + h16 + ':){0,4}' + h16 + ')?::' + ls32;
|
||||
const IPv6NoneHex2 = '(?:(?:' + h16 + ':){0,5}' + h16 + ')?::' + h16;
|
||||
const IPv6NoneHex3 = '(?:(?:' + h16 + ':){0,6}' + h16 + ')?::';
|
||||
internals.rfc3986.IPv6address = '(?:' + IPv6SixHex + or + IPv6FiveHex + or + IPv6FourHex + or + IPv6ThreeHex + or + IPv6TwoHex + or + IPv6OneHex + or + IPv6NoneHex + or + IPv6NoneHex2 + or + IPv6NoneHex3 + ')';
|
||||
|
||||
/**
|
||||
* IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
|
||||
*/
|
||||
internals.rfc3986.IPvFuture = 'v' + hexDigitOnly + '+\\.[' + unreserved + subDelims + ':]+';
|
||||
|
||||
/**
|
||||
* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
*/
|
||||
internals.rfc3986.scheme = alphaOnly + '[' + alpha + digit + '+-\\.]*';
|
||||
|
||||
/**
|
||||
* userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
|
||||
*/
|
||||
const userinfo = '[' + unreserved + pctEncoded + subDelims + ':]*';
|
||||
|
||||
/**
|
||||
* IP-literal = "[" ( IPv6address / IPvFuture ) "]"
|
||||
*/
|
||||
const IPLiteral = '\\[(?:' + internals.rfc3986.IPv6address + or + internals.rfc3986.IPvFuture + ')\\]';
|
||||
|
||||
/**
|
||||
* reg-name = *( unreserved / pct-encoded / sub-delims )
|
||||
*/
|
||||
const regName = '[' + unreserved + pctEncoded + subDelims + ']{0,255}';
|
||||
|
||||
/**
|
||||
* host = IP-literal / IPv4address / reg-name
|
||||
*/
|
||||
const host = '(?:' + IPLiteral + or + internals.rfc3986.IPv4address + or + regName + ')';
|
||||
|
||||
/**
|
||||
* port = *DIGIT
|
||||
*/
|
||||
const port = digitOnly + '*';
|
||||
|
||||
/**
|
||||
* authority = [ userinfo "@" ] host [ ":" port ]
|
||||
*/
|
||||
const authority = '(?:' + userinfo + '@)?' + host + '(?::' + port + ')?';
|
||||
|
||||
/**
|
||||
* segment = *pchar
|
||||
* segment-nz = 1*pchar
|
||||
* path = path-abempty ; begins with "/" or is empty
|
||||
* / path-absolute ; begins with "/" but not "//"
|
||||
* / path-noscheme ; begins with a non-colon segment
|
||||
* / path-rootless ; begins with a segment
|
||||
* / path-empty ; zero characters
|
||||
* path-abempty = *( "/" segment )
|
||||
* path-absolute = "/" [ segment-nz *( "/" segment ) ]
|
||||
* path-rootless = segment-nz *( "/" segment )
|
||||
*/
|
||||
const segment = pcharOnly + '*';
|
||||
const segmentNz = pcharOnly + '+';
|
||||
const segmentNzNc = '[' + unreserved + pctEncoded + subDelims + '@' + ']+';
|
||||
const pathEmpty = '';
|
||||
const pathAbEmpty = '(?:\\/' + segment + ')*';
|
||||
const pathAbsolute = '\\/(?:' + segmentNz + pathAbEmpty + ')?';
|
||||
const pathRootless = segmentNz + pathAbEmpty;
|
||||
const pathNoScheme = segmentNzNc + pathAbEmpty;
|
||||
|
||||
/**
|
||||
* hier-part = "//" authority path
|
||||
*/
|
||||
internals.rfc3986.hierPart = '(?:' + '(?:\\/\\/' + authority + pathAbEmpty + ')' + or + pathAbsolute + or + pathRootless + ')';
|
||||
|
||||
/**
|
||||
* relative-part = "//" authority path-abempty
|
||||
* / path-absolute
|
||||
* / path-noscheme
|
||||
* / path-empty
|
||||
*/
|
||||
internals.rfc3986.relativeRef = '(?:' + '(?:\\/\\/' + authority + pathAbEmpty + ')' + or + pathAbsolute + or + pathNoScheme + or + pathEmpty + ')';
|
||||
|
||||
/**
|
||||
* query = *( pchar / "/" / "?" )
|
||||
*/
|
||||
internals.rfc3986.query = '[' + pchar + '\\/\\?]*(?=#|$)'; //Finish matching either at the fragment part or end of the line.
|
||||
|
||||
/**
|
||||
* query = *( pchar / "[" / "]" / "/" / "?" )
|
||||
*/
|
||||
internals.rfc3986.queryWithSquareBrackets = '[' + pchar + squareBrackets + '\\/\\?]*(?=#|$)'; //Finish matching either at the fragment part or end of the line.
|
||||
|
||||
/**
|
||||
* fragment = *( pchar / "/" / "?" )
|
||||
*/
|
||||
internals.rfc3986.fragment = '[' + pchar + '\\/\\?]*';
|
||||
};
|
||||
|
||||
|
||||
internals.generate();
|
||||
|
||||
module.exports = internals.rfc3986;
|
||||
Generated
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const RFC3986 = require('./rfc3986');
|
||||
|
||||
|
||||
const internals = {
|
||||
Uri: {
|
||||
createUriRegex: function (optionalScheme, allowRelative, relativeOnly, allowQuerySquareBrackets) {
|
||||
|
||||
let scheme = RFC3986.scheme;
|
||||
let prefix;
|
||||
|
||||
if (relativeOnly) {
|
||||
prefix = '(?:' + RFC3986.relativeRef + ')';
|
||||
}
|
||||
else {
|
||||
// If we were passed a scheme, use it instead of the generic one
|
||||
if (optionalScheme) {
|
||||
|
||||
// Have to put this in a non-capturing group to handle the OR statements
|
||||
scheme = '(?:' + optionalScheme + ')';
|
||||
}
|
||||
|
||||
const withScheme = '(?:' + scheme + ':' + RFC3986.hierPart + ')';
|
||||
|
||||
prefix = allowRelative ? '(?:' + withScheme + '|' + RFC3986.relativeRef + ')' : withScheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
|
||||
*
|
||||
* OR
|
||||
*
|
||||
* relative-ref = relative-part [ "?" query ] [ "#" fragment ]
|
||||
*/
|
||||
return new RegExp('^' + prefix + '(?:\\?' + (allowQuerySquareBrackets ? RFC3986.queryWithSquareBrackets : RFC3986.query) + ')?' + '(?:#' + RFC3986.fragment + ')?$');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = internals.Uri;
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
|
||||
const Util = require('util');
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Any = require('../any');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
internals.Map = class extends Map {
|
||||
|
||||
slice() {
|
||||
|
||||
return new internals.Map(this);
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
||||
return Util.inspect(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.Symbol = class extends Any {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._type = 'symbol';
|
||||
this._inner.map = new internals.Map();
|
||||
}
|
||||
|
||||
_base(value, state, options) {
|
||||
|
||||
if (options.convert) {
|
||||
const lookup = this._inner.map.get(value);
|
||||
if (lookup) {
|
||||
value = lookup;
|
||||
}
|
||||
|
||||
if (this._flags.allowOnly) {
|
||||
return {
|
||||
value,
|
||||
errors: (typeof value === 'symbol') ? null : this.createError('symbol.map', { value, map: this._inner.map }, state, options)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
value,
|
||||
errors: (typeof value === 'symbol') ? null : this.createError('symbol.base', { value }, state, options)
|
||||
};
|
||||
}
|
||||
|
||||
map(iterable) {
|
||||
|
||||
if (iterable && !iterable[Symbol.iterator] && typeof iterable === 'object') {
|
||||
iterable = Object.entries(iterable);
|
||||
}
|
||||
|
||||
Hoek.assert(iterable && iterable[Symbol.iterator], 'Iterable must be an iterable or object');
|
||||
const obj = this.clone();
|
||||
|
||||
const symbols = [];
|
||||
for (const entry of iterable) {
|
||||
Hoek.assert(entry && entry[Symbol.iterator], 'Entry must be an iterable');
|
||||
const [key, value] = entry;
|
||||
|
||||
Hoek.assert(typeof key !== 'object' && typeof key !== 'function' && typeof key !== 'symbol', 'Key must not be an object, function, or Symbol');
|
||||
Hoek.assert(typeof value === 'symbol', 'Value must be a Symbol');
|
||||
obj._inner.map.set(key, value);
|
||||
symbols.push(value);
|
||||
}
|
||||
|
||||
return obj.valid(...symbols);
|
||||
}
|
||||
|
||||
describe() {
|
||||
|
||||
const description = super.describe();
|
||||
description.map = new Map(this._inner.map);
|
||||
return description;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = new internals.Symbol();
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = {
|
||||
settingsCache: Symbol('settingsCache')
|
||||
};
|
||||
resources/app/node_modules/simple-oauth2/node_modules/@hapi/joi/node_modules/@hapi/bourne/.npmignore
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!lib/**
|
||||
!.npmignore
|
||||
resources/app/node_modules/simple-oauth2/node_modules/@hapi/joi/node_modules/@hapi/bourne/LICENSE.md
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
Copyright (c) 2019, Sideway Inc, and project contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Generated
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
<a href="http://hapijs.com"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# Bourne. JSON Bourne.
|
||||
|
||||
`JSON.parse()` drop-in replacement with prototype poisoning protection
|
||||
|
||||
[](https://travis-ci.org/hapijs/bourne)
|
||||
|
||||
## Introduction
|
||||
|
||||
Consider this:
|
||||
|
||||
```
|
||||
> const a = '{"__proto__":{ "b":5}}';
|
||||
'{"__proto__":{ "b":5}}'
|
||||
|
||||
> const b = JSON.parse(a);
|
||||
{ __proto__: { b: 5 } }
|
||||
|
||||
> b.b;
|
||||
undefined
|
||||
|
||||
> const c = Object.assign({}, b);
|
||||
{}
|
||||
|
||||
> c.b
|
||||
5
|
||||
```
|
||||
|
||||
The problem is that `JSON.parse()` retains the `__proto__` property as a plain object key. By
|
||||
itself, this is not a security issue. However, as soon as that object is assigned to another or
|
||||
iterated on and values copied, the `__proto__` property leaks and becomes the object's prototype.
|
||||
|
||||
## API
|
||||
|
||||
### `Bourne.parse(text, [reviver], [options])`
|
||||
|
||||
Parses a given JSON-formatted text into an object where:
|
||||
- `text` - the JSON text string.
|
||||
- `reviver` - the `JSON.parse()` optional `reviver` argument.
|
||||
- `options` - optional configuration object where:
|
||||
- `protoAction` - optional string with one of:
|
||||
- `'error'` - throw a `SyntaxError` when a `__proto__` key is found. This is the default value.
|
||||
- `'remove'` - deletes any `__proto__` keys from the result object.
|
||||
- `'ignore'` - skips all validation (same as calling `JSON.parse()` directly).
|
||||
|
||||
### `Bourne.scan(obj, [options])`
|
||||
|
||||
Scans a given object for prototype properties where:
|
||||
- `obj` - the object being scanned.
|
||||
- `options` - optional configuration object where:
|
||||
- `protoAction` - optional string with one of:
|
||||
- `'error'` - throw a `SyntaxError` when a `__proto__` key is found. This is the default value.
|
||||
- `'remove'` - deletes any `__proto__` keys from the input `obj`.
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
const internals = {
|
||||
suspectRx: /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*\:/
|
||||
};
|
||||
|
||||
|
||||
exports.parse = function (text, reviver, options) {
|
||||
|
||||
// Normalize arguments
|
||||
|
||||
if (!options) {
|
||||
if (reviver &&
|
||||
typeof reviver === 'object') {
|
||||
|
||||
options = reviver;
|
||||
reviver = undefined;
|
||||
}
|
||||
else {
|
||||
options = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Parse normally, allowing exceptions
|
||||
|
||||
const obj = JSON.parse(text, reviver);
|
||||
|
||||
// options.protoAction: 'error' (default) / 'remove' / 'ignore'
|
||||
|
||||
if (options.protoAction === 'ignore') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Ignore null and non-objects
|
||||
|
||||
if (!obj ||
|
||||
typeof obj !== 'object') {
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Check original string for potential exploit
|
||||
|
||||
if (!text.match(internals.suspectRx)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Scan result for proto keys
|
||||
|
||||
exports.scan(obj, options);
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
||||
exports.scan = function (obj, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
let next = [obj];
|
||||
|
||||
while (next.length) {
|
||||
const nodes = next;
|
||||
next = [];
|
||||
|
||||
for (const node of nodes) {
|
||||
if (Object.prototype.hasOwnProperty.call(node, '__proto__')) { // Avoid calling node.hasOwnProperty directly
|
||||
if (options.protoAction !== 'remove') {
|
||||
throw new SyntaxError('Object contains forbidden prototype property');
|
||||
}
|
||||
|
||||
delete node.__proto__;
|
||||
}
|
||||
|
||||
for (const key in node) {
|
||||
const value = node[key];
|
||||
if (value &&
|
||||
typeof value === 'object') {
|
||||
|
||||
next.push(node[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.safeParse = function (text, reviver) {
|
||||
|
||||
try {
|
||||
return exports.parse(text, reviver);
|
||||
}
|
||||
catch (ignoreError) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
Generated
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"_from": "@hapi/bourne@1.x.x",
|
||||
"_id": "@hapi/bourne@1.3.2",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==",
|
||||
"_location": "/simple-oauth2/@hapi/joi/@hapi/bourne",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "@hapi/bourne@1.x.x",
|
||||
"name": "@hapi/bourne",
|
||||
"escapedName": "@hapi%2fbourne",
|
||||
"scope": "@hapi",
|
||||
"rawSpec": "1.x.x",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.x.x"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/simple-oauth2/@hapi/joi"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz",
|
||||
"_shasum": "0a7095adea067243ce3283e1b56b8a8f453b242a",
|
||||
"_spec": "@hapi/bourne@1.x.x",
|
||||
"_where": "C:\\Daten\\Git\\Tumortisch\\node_modules\\simple-oauth2\\node_modules\\@hapi\\joi",
|
||||
"bugs": {
|
||||
"url": "https://github.com/hapijs/bourne/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {},
|
||||
"deprecated": false,
|
||||
"description": "JSON parse with prototype poisoning protection",
|
||||
"devDependencies": {
|
||||
"@hapi/code": "5.x.x",
|
||||
"@hapi/lab": "18.x.x",
|
||||
"benchmark": "^2.1.4"
|
||||
},
|
||||
"homepage": "https://github.com/hapijs/bourne#readme",
|
||||
"keywords": [
|
||||
"JSON",
|
||||
"parse",
|
||||
"safe",
|
||||
"prototype"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "lib/index.js",
|
||||
"name": "@hapi/bourne",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/hapijs/bourne.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -a @hapi/code -t 100 -L",
|
||||
"test-cov-html": "lab -a @hapi/code -r html -o coverage.html"
|
||||
},
|
||||
"version": "1.3.2"
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"_from": "@hapi/joi@^15.1.1",
|
||||
"_id": "@hapi/joi@15.1.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==",
|
||||
"_location": "/simple-oauth2/@hapi/joi",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "@hapi/joi@^15.1.1",
|
||||
"name": "@hapi/joi",
|
||||
"escapedName": "@hapi%2fjoi",
|
||||
"scope": "@hapi",
|
||||
"rawSpec": "^15.1.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^15.1.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/simple-oauth2"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz",
|
||||
"_shasum": "c675b8a71296f02833f8d6d243b34c57b8ce19d7",
|
||||
"_spec": "@hapi/joi@^15.1.1",
|
||||
"_where": "C:\\Daten\\Git\\Tumortisch\\node_modules\\simple-oauth2",
|
||||
"bugs": {
|
||||
"url": "https://github.com/hapijs/joi/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"@hapi/address": "2.x.x",
|
||||
"@hapi/bourne": "1.x.x",
|
||||
"@hapi/hoek": "8.x.x",
|
||||
"@hapi/topo": "3.x.x"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Object schema validation",
|
||||
"devDependencies": {
|
||||
"@hapi/code": "6.x.x",
|
||||
"@hapi/lab": "20.x.x"
|
||||
},
|
||||
"homepage": "https://github.com/hapijs/joi",
|
||||
"keywords": [
|
||||
"schema",
|
||||
"validation"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "lib/index.js",
|
||||
"name": "@hapi/joi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/hapijs/joi.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -t 100 -a @hapi/code -L",
|
||||
"test-cov-html": "lab -r html -o coverage.html -a @hapi/code"
|
||||
},
|
||||
"version": "15.1.1"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Breaking changes are documented using GitHub issues, see [issues labeled "release notes"](https://github.com/hapijs/topo/issues?q=is%3Aissue+label%3A%22release+notes%22).
|
||||
|
||||
If you want changes of a specific minor or patch release, you can browse the [GitHub milestones](https://github.com/hapijs/topo/milestones?state=closed&direction=asc&sort=due_date).
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
Copyright (c) 2012-2019, Sideway Inc, and project contributors
|
||||
Copyright (c) 2012-2014, Walmart.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<a href="http://hapijs.com"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# @hapi/topo
|
||||
|
||||
Topological sorting with grouping support.
|
||||
|
||||
[](http://travis-ci.org/hapijs/topo)
|
||||
|
||||
## Usage
|
||||
|
||||
See the [API Reference](API.md)
|
||||
|
||||
**Example**
|
||||
```js
|
||||
const Topo = require('topo');
|
||||
|
||||
const morning = new Topo();
|
||||
|
||||
morning.add('Nap', { after: ['breakfast', 'prep'] });
|
||||
|
||||
morning.add([
|
||||
'Make toast',
|
||||
'Pour juice'
|
||||
], { before: 'breakfast', group: 'prep' });
|
||||
|
||||
morning.add('Eat breakfast', { group: 'breakfast' });
|
||||
|
||||
morning.nodes; // ['Make toast', 'Pour juice', 'Eat breakfast', 'Nap']
|
||||
```
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('@hapi/hoek/lib/assert');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = class Topo {
|
||||
|
||||
constructor() {
|
||||
|
||||
this._items = [];
|
||||
this.nodes = [];
|
||||
}
|
||||
|
||||
add(nodes, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
// Validate rules
|
||||
|
||||
const before = [].concat(options.before || []);
|
||||
const after = [].concat(options.after || []);
|
||||
const group = options.group || '?';
|
||||
const sort = options.sort || 0; // Used for merging only
|
||||
|
||||
Assert(!before.includes(group), `Item cannot come before itself: ${group}`);
|
||||
Assert(!before.includes('?'), 'Item cannot come before unassociated items');
|
||||
Assert(!after.includes(group), `Item cannot come after itself: ${group}`);
|
||||
Assert(!after.includes('?'), 'Item cannot come after unassociated items');
|
||||
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes];
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
const item = {
|
||||
seq: this._items.length,
|
||||
sort,
|
||||
before,
|
||||
after,
|
||||
group,
|
||||
node
|
||||
};
|
||||
|
||||
this._items.push(item);
|
||||
}
|
||||
|
||||
// Insert event
|
||||
|
||||
const valid = this._sort();
|
||||
Assert(valid, 'item', group !== '?' ? `added into group ${group}` : '', 'created a dependencies error');
|
||||
|
||||
return this.nodes;
|
||||
}
|
||||
|
||||
merge(others) {
|
||||
|
||||
if (!Array.isArray(others)) {
|
||||
others = [others];
|
||||
}
|
||||
|
||||
for (const other of others) {
|
||||
if (other) {
|
||||
for (const item of other._items) {
|
||||
this._items.push(Object.assign({}, item)); // Shallow cloned
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort items
|
||||
|
||||
this._items.sort(internals.mergeSort);
|
||||
for (let i = 0; i < this._items.length; ++i) {
|
||||
this._items[i].seq = i;
|
||||
}
|
||||
|
||||
const valid = this._sort();
|
||||
Assert(valid, 'merge created a dependencies error');
|
||||
|
||||
return this.nodes;
|
||||
}
|
||||
|
||||
_sort() {
|
||||
|
||||
// Construct graph
|
||||
|
||||
const graph = {};
|
||||
const graphAfters = Object.create(null); // A prototype can bungle lookups w/ false positives
|
||||
const groups = Object.create(null);
|
||||
|
||||
for (const item of this._items) {
|
||||
const seq = item.seq; // Unique across all items
|
||||
const group = item.group;
|
||||
|
||||
// Determine Groups
|
||||
|
||||
groups[group] = groups[group] || [];
|
||||
groups[group].push(seq);
|
||||
|
||||
// Build intermediary graph using 'before'
|
||||
|
||||
graph[seq] = item.before;
|
||||
|
||||
// Build second intermediary graph with 'after'
|
||||
|
||||
for (const after of item.after) {
|
||||
graphAfters[after] = graphAfters[after] || [];
|
||||
graphAfters[after].push(seq);
|
||||
}
|
||||
}
|
||||
|
||||
// Expand intermediary graph
|
||||
|
||||
for (const node in graph) {
|
||||
const expandedGroups = [];
|
||||
|
||||
for (const graphNodeItem in graph[node]) {
|
||||
const group = graph[node][graphNodeItem];
|
||||
groups[group] = groups[group] || [];
|
||||
expandedGroups.push(...groups[group]);
|
||||
}
|
||||
|
||||
graph[node] = expandedGroups;
|
||||
}
|
||||
|
||||
// Merge intermediary graph using graphAfters into final graph
|
||||
|
||||
for (const group in graphAfters) {
|
||||
if (groups[group]) {
|
||||
for (const node of groups[group]) {
|
||||
graph[node].push(...graphAfters[group]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compile ancestors
|
||||
|
||||
const ancestors = {};
|
||||
for (const node in graph) {
|
||||
const children = graph[node];
|
||||
for (const child of children) {
|
||||
ancestors[child] = ancestors[child] || [];
|
||||
ancestors[child].push(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Topo sort
|
||||
|
||||
const visited = {};
|
||||
const sorted = [];
|
||||
|
||||
for (let i = 0; i < this._items.length; ++i) { // Looping through item.seq values out of order
|
||||
let next = i;
|
||||
|
||||
if (ancestors[i]) {
|
||||
next = null;
|
||||
for (let j = 0; j < this._items.length; ++j) { // As above, these are item.seq values
|
||||
if (visited[j] === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ancestors[j]) {
|
||||
ancestors[j] = [];
|
||||
}
|
||||
|
||||
const shouldSeeCount = ancestors[j].length;
|
||||
let seenCount = 0;
|
||||
for (let k = 0; k < shouldSeeCount; ++k) {
|
||||
if (visited[ancestors[j][k]]) {
|
||||
++seenCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (seenCount === shouldSeeCount) {
|
||||
next = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next !== null) {
|
||||
visited[next] = true;
|
||||
sorted.push(next);
|
||||
}
|
||||
}
|
||||
|
||||
if (sorted.length !== this._items.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const seqIndex = {};
|
||||
for (const item of this._items) {
|
||||
seqIndex[item.seq] = item;
|
||||
}
|
||||
|
||||
this._items = [];
|
||||
this.nodes = [];
|
||||
|
||||
for (const value of sorted) {
|
||||
const sortedItem = seqIndex[value];
|
||||
this.nodes.push(sortedItem.node);
|
||||
this._items.push(sortedItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.mergeSort = (a, b) => {
|
||||
|
||||
return a.sort === b.sort ? 0 : (a.sort < b.sort ? -1 : 1);
|
||||
};
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"_from": "@hapi/topo@3.x.x",
|
||||
"_id": "@hapi/topo@3.1.6",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==",
|
||||
"_location": "/simple-oauth2/@hapi/topo",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "@hapi/topo@3.x.x",
|
||||
"name": "@hapi/topo",
|
||||
"escapedName": "@hapi%2ftopo",
|
||||
"scope": "@hapi",
|
||||
"rawSpec": "3.x.x",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "3.x.x"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/simple-oauth2/@hapi/joi"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz",
|
||||
"_shasum": "68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29",
|
||||
"_spec": "@hapi/topo@3.x.x",
|
||||
"_where": "C:\\Daten\\Git\\Tumortisch\\node_modules\\simple-oauth2\\node_modules\\@hapi\\joi",
|
||||
"bugs": {
|
||||
"url": "https://github.com/hapijs/topo/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^8.3.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Topological sorting with grouping support",
|
||||
"devDependencies": {
|
||||
"@hapi/code": "6.x.x",
|
||||
"@hapi/lab": "20.x.x"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"homepage": "https://github.com/hapijs/topo#readme",
|
||||
"keywords": [
|
||||
"topological",
|
||||
"sort",
|
||||
"toposort",
|
||||
"topsort"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "lib/index.js",
|
||||
"name": "@hapi/topo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/hapijs/topo.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -a @hapi/code -t 100 -L",
|
||||
"test-cov-html": "lab -a @hapi/code -t 100 -L -r html -o coverage.html"
|
||||
},
|
||||
"version": "3.1.6"
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
|
||||
3.1.0 / 2017-09-26
|
||||
==================
|
||||
|
||||
* Add `DEBUG_HIDE_DATE` env var (#486)
|
||||
* Remove ReDoS regexp in %o formatter (#504)
|
||||
* Remove "component" from package.json
|
||||
* Remove `component.json`
|
||||
* Ignore package-lock.json
|
||||
* Examples: fix colors printout
|
||||
* Fix: browser detection
|
||||
* Fix: spelling mistake (#496, @EdwardBetts)
|
||||
|
||||
3.0.1 / 2017-08-24
|
||||
==================
|
||||
|
||||
* Fix: Disable colors in Edge and Internet Explorer (#489)
|
||||
|
||||
3.0.0 / 2017-08-08
|
||||
==================
|
||||
|
||||
* Breaking: Remove DEBUG_FD (#406)
|
||||
* Breaking: Use `Date#toISOString()` instead to `Date#toUTCString()` when output is not a TTY (#418)
|
||||
* Breaking: Make millisecond timer namespace specific and allow 'always enabled' output (#408)
|
||||
* Addition: document `enabled` flag (#465)
|
||||
* Addition: add 256 colors mode (#481)
|
||||
* Addition: `enabled()` updates existing debug instances, add `destroy()` function (#440)
|
||||
* Update: component: update "ms" to v2.0.0
|
||||
* Update: separate the Node and Browser tests in Travis-CI
|
||||
* Update: refactor Readme, fixed documentation, added "Namespace Colors" section, redid screenshots
|
||||
* Update: separate Node.js and web browser examples for organization
|
||||
* Update: update "browserify" to v14.4.0
|
||||
* Fix: fix Readme typo (#473)
|
||||
|
||||
2.6.9 / 2017-09-22
|
||||
==================
|
||||
|
||||
* remove ReDoS regexp in %o formatter (#504)
|
||||
|
||||
2.6.8 / 2017-05-18
|
||||
==================
|
||||
|
||||
* Fix: Check for undefined on browser globals (#462, @marbemac)
|
||||
|
||||
2.6.7 / 2017-05-16
|
||||
==================
|
||||
|
||||
* Fix: Update ms to 2.0.0 to fix regular expression denial of service vulnerability (#458, @hubdotcom)
|
||||
* Fix: Inline extend function in node implementation (#452, @dougwilson)
|
||||
* Docs: Fix typo (#455, @msasad)
|
||||
|
||||
2.6.5 / 2017-04-27
|
||||
==================
|
||||
|
||||
* Fix: null reference check on window.documentElement.style.WebkitAppearance (#447, @thebigredgeek)
|
||||
* Misc: clean up browser reference checks (#447, @thebigredgeek)
|
||||
* Misc: add npm-debug.log to .gitignore (@thebigredgeek)
|
||||
|
||||
|
||||
2.6.4 / 2017-04-20
|
||||
==================
|
||||
|
||||
* Fix: bug that would occur if process.env.DEBUG is a non-string value. (#444, @LucianBuzzo)
|
||||
* Chore: ignore bower.json in npm installations. (#437, @joaovieira)
|
||||
* Misc: update "ms" to v0.7.3 (@tootallnate)
|
||||
|
||||
2.6.3 / 2017-03-13
|
||||
==================
|
||||
|
||||
* Fix: Electron reference to `process.env.DEBUG` (#431, @paulcbetts)
|
||||
* Docs: Changelog fix (@thebigredgeek)
|
||||
|
||||
2.6.2 / 2017-03-10
|
||||
==================
|
||||
|
||||
* Fix: DEBUG_MAX_ARRAY_LENGTH (#420, @slavaGanzin)
|
||||
* Docs: Add backers and sponsors from Open Collective (#422, @piamancini)
|
||||
* Docs: Add Slackin invite badge (@tootallnate)
|
||||
|
||||
2.6.1 / 2017-02-10
|
||||
==================
|
||||
|
||||
* Fix: Module's `export default` syntax fix for IE8 `Expected identifier` error
|
||||
* Fix: Whitelist DEBUG_FD for values 1 and 2 only (#415, @pi0)
|
||||
* Fix: IE8 "Expected identifier" error (#414, @vgoma)
|
||||
* Fix: Namespaces would not disable once enabled (#409, @musikov)
|
||||
|
||||
2.6.0 / 2016-12-28
|
||||
==================
|
||||
|
||||
* Fix: added better null pointer checks for browser useColors (@thebigredgeek)
|
||||
* Improvement: removed explicit `window.debug` export (#404, @tootallnate)
|
||||
* Improvement: deprecated `DEBUG_FD` environment variable (#405, @tootallnate)
|
||||
|
||||
2.5.2 / 2016-12-25
|
||||
==================
|
||||
|
||||
* Fix: reference error on window within webworkers (#393, @KlausTrainer)
|
||||
* Docs: fixed README typo (#391, @lurch)
|
||||
* Docs: added notice about v3 api discussion (@thebigredgeek)
|
||||
|
||||
2.5.1 / 2016-12-20
|
||||
==================
|
||||
|
||||
* Fix: babel-core compatibility
|
||||
|
||||
2.5.0 / 2016-12-20
|
||||
==================
|
||||
|
||||
* Fix: wrong reference in bower file (@thebigredgeek)
|
||||
* Fix: webworker compatibility (@thebigredgeek)
|
||||
* Fix: output formatting issue (#388, @kribblo)
|
||||
* Fix: babel-loader compatibility (#383, @escwald)
|
||||
* Misc: removed built asset from repo and publications (@thebigredgeek)
|
||||
* Misc: moved source files to /src (#378, @yamikuronue)
|
||||
* Test: added karma integration and replaced babel with browserify for browser tests (#378, @yamikuronue)
|
||||
* Test: coveralls integration (#378, @yamikuronue)
|
||||
* Docs: simplified language in the opening paragraph (#373, @yamikuronue)
|
||||
|
||||
2.4.5 / 2016-12-17
|
||||
==================
|
||||
|
||||
* Fix: `navigator` undefined in Rhino (#376, @jochenberger)
|
||||
* Fix: custom log function (#379, @hsiliev)
|
||||
* Improvement: bit of cleanup + linting fixes (@thebigredgeek)
|
||||
* Improvement: rm non-maintainted `dist/` dir (#375, @freewil)
|
||||
* Docs: simplified language in the opening paragraph. (#373, @yamikuronue)
|
||||
|
||||
2.4.4 / 2016-12-14
|
||||
==================
|
||||
|
||||
* Fix: work around debug being loaded in preload scripts for electron (#368, @paulcbetts)
|
||||
|
||||
2.4.3 / 2016-12-14
|
||||
==================
|
||||
|
||||
* Fix: navigation.userAgent error for react native (#364, @escwald)
|
||||
|
||||
2.4.2 / 2016-12-14
|
||||
==================
|
||||
|
||||
* Fix: browser colors (#367, @tootallnate)
|
||||
* Misc: travis ci integration (@thebigredgeek)
|
||||
* Misc: added linting and testing boilerplate with sanity check (@thebigredgeek)
|
||||
|
||||
2.4.1 / 2016-12-13
|
||||
==================
|
||||
|
||||
* Fix: typo that broke the package (#356)
|
||||
|
||||
2.4.0 / 2016-12-13
|
||||
==================
|
||||
|
||||
* Fix: bower.json references unbuilt src entry point (#342, @justmatt)
|
||||
* Fix: revert "handle regex special characters" (@tootallnate)
|
||||
* Feature: configurable util.inspect()`options for NodeJS (#327, @tootallnate)
|
||||
* Feature: %O`(big O) pretty-prints objects (#322, @tootallnate)
|
||||
* Improvement: allow colors in workers (#335, @botverse)
|
||||
* Improvement: use same color for same namespace. (#338, @lchenay)
|
||||
|
||||
2.3.3 / 2016-11-09
|
||||
==================
|
||||
|
||||
* Fix: Catch `JSON.stringify()` errors (#195, Jovan Alleyne)
|
||||
* Fix: Returning `localStorage` saved values (#331, Levi Thomason)
|
||||
* Improvement: Don't create an empty object when no `process` (Nathan Rajlich)
|
||||
|
||||
2.3.2 / 2016-11-09
|
||||
==================
|
||||
|
||||
* Fix: be super-safe in index.js as well (@TooTallNate)
|
||||
* Fix: should check whether process exists (Tom Newby)
|
||||
|
||||
2.3.1 / 2016-11-09
|
||||
==================
|
||||
|
||||
* Fix: Added electron compatibility (#324, @paulcbetts)
|
||||
* Improvement: Added performance optimizations (@tootallnate)
|
||||
* Readme: Corrected PowerShell environment variable example (#252, @gimre)
|
||||
* Misc: Removed yarn lock file from source control (#321, @fengmk2)
|
||||
|
||||
2.3.0 / 2016-11-07
|
||||
==================
|
||||
|
||||
* Fix: Consistent placement of ms diff at end of output (#215, @gorangajic)
|
||||
* Fix: Escaping of regex special characters in namespace strings (#250, @zacronos)
|
||||
* Fix: Fixed bug causing crash on react-native (#282, @vkarpov15)
|
||||
* Feature: Enabled ES6+ compatible import via default export (#212 @bucaran)
|
||||
* Feature: Added %O formatter to reflect Chrome's console.log capability (#279, @oncletom)
|
||||
* Package: Update "ms" to 0.7.2 (#315, @DevSide)
|
||||
* Package: removed superfluous version property from bower.json (#207 @kkirsche)
|
||||
* Readme: fix USE_COLORS to DEBUG_COLORS
|
||||
* Readme: Doc fixes for format string sugar (#269, @mlucool)
|
||||
* Readme: Updated docs for DEBUG_FD and DEBUG_COLORS environment variables (#232, @mattlyons0)
|
||||
* Readme: doc fixes for PowerShell (#271 #243, @exoticknight @unreadable)
|
||||
* Readme: better docs for browser support (#224, @matthewmueller)
|
||||
* Tooling: Added yarn integration for development (#317, @thebigredgeek)
|
||||
* Misc: Renamed History.md to CHANGELOG.md (@thebigredgeek)
|
||||
* Misc: Added license file (#226 #274, @CantemoInternal @sdaitzman)
|
||||
* Misc: Updated contributors (@thebigredgeek)
|
||||
|
||||
2.2.0 / 2015-05-09
|
||||
==================
|
||||
|
||||
* package: update "ms" to v0.7.1 (#202, @dougwilson)
|
||||
* README: add logging to file example (#193, @DanielOchoa)
|
||||
* README: fixed a typo (#191, @amir-s)
|
||||
* browser: expose `storage` (#190, @stephenmathieson)
|
||||
* Makefile: add a `distclean` target (#189, @stephenmathieson)
|
||||
|
||||
2.1.3 / 2015-03-13
|
||||
==================
|
||||
|
||||
* Updated stdout/stderr example (#186)
|
||||
* Updated example/stdout.js to match debug current behaviour
|
||||
* Renamed example/stderr.js to stdout.js
|
||||
* Update Readme.md (#184)
|
||||
* replace high intensity foreground color for bold (#182, #183)
|
||||
|
||||
2.1.2 / 2015-03-01
|
||||
==================
|
||||
|
||||
* dist: recompile
|
||||
* update "ms" to v0.7.0
|
||||
* package: update "browserify" to v9.0.3
|
||||
* component: fix "ms.js" repo location
|
||||
* changed bower package name
|
||||
* updated documentation about using debug in a browser
|
||||
* fix: security error on safari (#167, #168, @yields)
|
||||
|
||||
2.1.1 / 2014-12-29
|
||||
==================
|
||||
|
||||
* browser: use `typeof` to check for `console` existence
|
||||
* browser: check for `console.log` truthiness (fix IE 8/9)
|
||||
* browser: add support for Chrome apps
|
||||
* Readme: added Windows usage remarks
|
||||
* Add `bower.json` to properly support bower install
|
||||
|
||||
2.1.0 / 2014-10-15
|
||||
==================
|
||||
|
||||
* node: implement `DEBUG_FD` env variable support
|
||||
* package: update "browserify" to v6.1.0
|
||||
* package: add "license" field to package.json (#135, @panuhorsmalahti)
|
||||
|
||||
2.0.0 / 2014-09-01
|
||||
==================
|
||||
|
||||
* package: update "browserify" to v5.11.0
|
||||
* node: use stderr rather than stdout for logging (#29, @stephenmathieson)
|
||||
|
||||
1.0.4 / 2014-07-15
|
||||
==================
|
||||
|
||||
* dist: recompile
|
||||
* example: remove `console.info()` log usage
|
||||
* example: add "Content-Type" UTF-8 header to browser example
|
||||
* browser: place %c marker after the space character
|
||||
* browser: reset the "content" color via `color: inherit`
|
||||
* browser: add colors support for Firefox >= v31
|
||||
* debug: prefer an instance `log()` function over the global one (#119)
|
||||
* Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
|
||||
|
||||
1.0.3 / 2014-07-09
|
||||
==================
|
||||
|
||||
* Add support for multiple wildcards in namespaces (#122, @seegno)
|
||||
* browser: fix lint
|
||||
|
||||
1.0.2 / 2014-06-10
|
||||
==================
|
||||
|
||||
* browser: update color palette (#113, @gscottolson)
|
||||
* common: make console logging function configurable (#108, @timoxley)
|
||||
* node: fix %o colors on old node <= 0.8.x
|
||||
* Makefile: find node path using shell/which (#109, @timoxley)
|
||||
|
||||
1.0.1 / 2014-06-06
|
||||
==================
|
||||
|
||||
* browser: use `removeItem()` to clear localStorage
|
||||
* browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
|
||||
* package: add "contributors" section
|
||||
* node: fix comment typo
|
||||
* README: list authors
|
||||
|
||||
1.0.0 / 2014-06-04
|
||||
==================
|
||||
|
||||
* make ms diff be global, not be scope
|
||||
* debug: ignore empty strings in enable()
|
||||
* node: make DEBUG_COLORS able to disable coloring
|
||||
* *: export the `colors` array
|
||||
* npmignore: don't publish the `dist` dir
|
||||
* Makefile: refactor to use browserify
|
||||
* package: add "browserify" as a dev dependency
|
||||
* Readme: add Web Inspector Colors section
|
||||
* node: reset terminal color for the debug content
|
||||
* node: map "%o" to `util.inspect()`
|
||||
* browser: map "%j" to `JSON.stringify()`
|
||||
* debug: add custom "formatters"
|
||||
* debug: use "ms" module for humanizing the diff
|
||||
* Readme: add "bash" syntax highlighting
|
||||
* browser: add Firebug color support
|
||||
* browser: add colors for WebKit browsers
|
||||
* node: apply log to `console`
|
||||
* rewrite: abstract common logic for Node & browsers
|
||||
* add .jshintrc file
|
||||
|
||||
0.8.1 / 2014-04-14
|
||||
==================
|
||||
|
||||
* package: re-add the "component" section
|
||||
|
||||
0.8.0 / 2014-03-30
|
||||
==================
|
||||
|
||||
* add `enable()` method for nodejs. Closes #27
|
||||
* change from stderr to stdout
|
||||
* remove unnecessary index.js file
|
||||
|
||||
0.7.4 / 2013-11-13
|
||||
==================
|
||||
|
||||
* remove "browserify" key from package.json (fixes something in browserify)
|
||||
|
||||
0.7.3 / 2013-10-30
|
||||
==================
|
||||
|
||||
* fix: catch localStorage security error when cookies are blocked (Chrome)
|
||||
* add debug(err) support. Closes #46
|
||||
* add .browser prop to package.json. Closes #42
|
||||
|
||||
0.7.2 / 2013-02-06
|
||||
==================
|
||||
|
||||
* fix package.json
|
||||
* fix: Mobile Safari (private mode) is broken with debug
|
||||
* fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
|
||||
|
||||
0.7.1 / 2013-02-05
|
||||
==================
|
||||
|
||||
* add repository URL to package.json
|
||||
* add DEBUG_COLORED to force colored output
|
||||
* add browserify support
|
||||
* fix component. Closes #24
|
||||
|
||||
0.7.0 / 2012-05-04
|
||||
==================
|
||||
|
||||
* Added .component to package.json
|
||||
* Added debug.component.js build
|
||||
|
||||
0.6.0 / 2012-03-16
|
||||
==================
|
||||
|
||||
* Added support for "-" prefix in DEBUG [Vinay Pulim]
|
||||
* Added `.enabled` flag to the node version [TooTallNate]
|
||||
|
||||
0.5.0 / 2012-02-02
|
||||
==================
|
||||
|
||||
* Added: humanize diffs. Closes #8
|
||||
* Added `debug.disable()` to the CS variant
|
||||
* Removed padding. Closes #10
|
||||
* Fixed: persist client-side variant again. Closes #9
|
||||
|
||||
0.4.0 / 2012-02-01
|
||||
==================
|
||||
|
||||
* Added browser variant support for older browsers [TooTallNate]
|
||||
* Added `debug.enable('project:*')` to browser variant [TooTallNate]
|
||||
* Added padding to diff (moved it to the right)
|
||||
|
||||
0.3.0 / 2012-01-26
|
||||
==================
|
||||
|
||||
* Added millisecond diff when isatty, otherwise UTC string
|
||||
|
||||
0.2.0 / 2012-01-22
|
||||
==================
|
||||
|
||||
* Added wildcard support
|
||||
|
||||
0.1.0 / 2011-12-02
|
||||
==================
|
||||
|
||||
* Added: remove colors unless stderr isatty [TooTallNate]
|
||||
|
||||
0.0.1 / 2010-01-03
|
||||
==================
|
||||
|
||||
* Initial release
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the 'Software'), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
+455
@@ -0,0 +1,455 @@
|
||||
# debug
|
||||
[](https://travis-ci.org/visionmedia/debug) [](https://coveralls.io/github/visionmedia/debug?branch=master) [](https://visionmedia-community-slackin.now.sh/) [](#backers)
|
||||
[](#sponsors)
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
|
||||
|
||||
A tiny JavaScript debugging utility modelled after Node.js core's debugging
|
||||
technique. Works in Node.js and web browsers.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install debug
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
|
||||
|
||||
Example [_app.js_](./examples/node/app.js):
|
||||
|
||||
```js
|
||||
var debug = require('debug')('http')
|
||||
, http = require('http')
|
||||
, name = 'My App';
|
||||
|
||||
// fake app
|
||||
|
||||
debug('booting %o', name);
|
||||
|
||||
http.createServer(function(req, res){
|
||||
debug(req.method + ' ' + req.url);
|
||||
res.end('hello\n');
|
||||
}).listen(3000, function(){
|
||||
debug('listening');
|
||||
});
|
||||
|
||||
// fake worker of some kind
|
||||
|
||||
require('./worker');
|
||||
```
|
||||
|
||||
Example [_worker.js_](./examples/node/worker.js):
|
||||
|
||||
```js
|
||||
var a = require('debug')('worker:a')
|
||||
, b = require('debug')('worker:b');
|
||||
|
||||
function work() {
|
||||
a('doing lots of uninteresting work');
|
||||
setTimeout(work, Math.random() * 1000);
|
||||
}
|
||||
|
||||
work();
|
||||
|
||||
function workb() {
|
||||
b('doing some work');
|
||||
setTimeout(workb, Math.random() * 2000);
|
||||
}
|
||||
|
||||
workb();
|
||||
```
|
||||
|
||||
The `DEBUG` environment variable is then used to enable these based on space or
|
||||
comma-delimited names.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 04 pm" src="https://user-images.githubusercontent.com/71256/29091703-a6302cdc-7c38-11e7-8304-7c0b3bc600cd.png">
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 38 pm" src="https://user-images.githubusercontent.com/71256/29091700-a62a6888-7c38-11e7-800b-db911291ca2b.png">
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 25 pm" src="https://user-images.githubusercontent.com/71256/29091701-a62ea114-7c38-11e7-826a-2692bedca740.png">
|
||||
|
||||
#### Windows command prompt notes
|
||||
|
||||
##### CMD
|
||||
|
||||
On Windows the environment variable is set using the `set` command.
|
||||
|
||||
```cmd
|
||||
set DEBUG=*,-not_this
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cmd
|
||||
set DEBUG=* & node app.js
|
||||
```
|
||||
|
||||
##### PowerShell (VS Code default)
|
||||
|
||||
PowerShell uses different syntax to set environment variables.
|
||||
|
||||
```cmd
|
||||
$env:DEBUG = "*,-not_this"
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cmd
|
||||
$env:DEBUG='app';node app.js
|
||||
```
|
||||
|
||||
Then, run the program to be debugged as usual.
|
||||
|
||||
npm script example:
|
||||
```js
|
||||
"windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js",
|
||||
```
|
||||
|
||||
## Namespace Colors
|
||||
|
||||
Every debug instance has a color generated for it based on its namespace name.
|
||||
This helps when visually parsing the debug output to identify which debug instance
|
||||
a debug line belongs to.
|
||||
|
||||
#### Node.js
|
||||
|
||||
In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
|
||||
the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
|
||||
otherwise debug will only use a small handful of basic colors.
|
||||
|
||||
<img width="521" src="https://user-images.githubusercontent.com/71256/29092181-47f6a9e6-7c3a-11e7-9a14-1928d8a711cd.png">
|
||||
|
||||
#### Web Browser
|
||||
|
||||
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
|
||||
option. These are WebKit web inspectors, Firefox ([since version
|
||||
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
|
||||
and the Firebug plugin for Firefox (any version).
|
||||
|
||||
<img width="524" src="https://user-images.githubusercontent.com/71256/29092033-b65f9f2e-7c39-11e7-8e32-f6f0d8e865c1.png">
|
||||
|
||||
|
||||
## Millisecond diff
|
||||
|
||||
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
|
||||
|
||||
When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091956-6bd78372-7c39-11e7-8c55-c948396d6edd.png">
|
||||
|
||||
|
||||
## Conventions
|
||||
|
||||
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.
|
||||
|
||||
## Wildcards
|
||||
|
||||
The `*` character may be used as a wildcard. Suppose for example your library has
|
||||
debuggers named "connect:bodyParser", "connect:compress", "connect:session",
|
||||
instead of listing all three with
|
||||
`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
|
||||
`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
|
||||
|
||||
You can also exclude specific debuggers by prefixing them with a "-" character.
|
||||
For example, `DEBUG=*,-connect:*` would include all debuggers except those
|
||||
starting with "connect:".
|
||||
|
||||
## Environment Variables
|
||||
|
||||
When running through Node.js, you can set a few environment variables that will
|
||||
change the behavior of the debug logging:
|
||||
|
||||
| Name | Purpose |
|
||||
|-----------|-------------------------------------------------|
|
||||
| `DEBUG` | Enables/disables specific debugging namespaces. |
|
||||
| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
|
||||
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
|
||||
| `DEBUG_DEPTH` | Object inspection depth. |
|
||||
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
|
||||
|
||||
|
||||
__Note:__ The environment variables beginning with `DEBUG_` end up being
|
||||
converted into an Options object that gets used with `%o`/`%O` formatters.
|
||||
See the Node.js documentation for
|
||||
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
|
||||
for the complete list.
|
||||
|
||||
## Formatters
|
||||
|
||||
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
|
||||
Below are the officially supported formatters:
|
||||
|
||||
| Formatter | Representation |
|
||||
|-----------|----------------|
|
||||
| `%O` | Pretty-print an Object on multiple lines. |
|
||||
| `%o` | Pretty-print an Object all on a single line. |
|
||||
| `%s` | String. |
|
||||
| `%d` | Number (both integer and float). |
|
||||
| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
|
||||
| `%%` | Single percent sign ('%'). This does not consume an argument. |
|
||||
|
||||
|
||||
### Custom formatters
|
||||
|
||||
You can add custom formatters by extending the `debug.formatters` object.
|
||||
For example, if you wanted to add support for rendering a Buffer as hex with
|
||||
`%h`, you could do something like:
|
||||
|
||||
```js
|
||||
const createDebug = require('debug')
|
||||
createDebug.formatters.h = (v) => {
|
||||
return v.toString('hex')
|
||||
}
|
||||
|
||||
// …elsewhere
|
||||
const debug = createDebug('foo')
|
||||
debug('this is hex: %h', new Buffer('hello world'))
|
||||
// foo this is hex: 68656c6c6f20776f726c6421 +0ms
|
||||
```
|
||||
|
||||
|
||||
## Browser Support
|
||||
|
||||
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
|
||||
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
|
||||
if you don't want to build it yourself.
|
||||
|
||||
Debug's enable state is currently persisted by `localStorage`.
|
||||
Consider the situation shown below where you have `worker:a` and `worker:b`,
|
||||
and wish to debug both. You can enable this using `localStorage.debug`:
|
||||
|
||||
```js
|
||||
localStorage.debug = 'worker:*'
|
||||
```
|
||||
|
||||
And then refresh the page.
|
||||
|
||||
```js
|
||||
a = debug('worker:a');
|
||||
b = debug('worker:b');
|
||||
|
||||
setInterval(function(){
|
||||
a('doing some work');
|
||||
}, 1000);
|
||||
|
||||
setInterval(function(){
|
||||
b('doing some work');
|
||||
}, 1200);
|
||||
```
|
||||
|
||||
|
||||
## Output streams
|
||||
|
||||
By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
|
||||
|
||||
Example [_stdout.js_](./examples/node/stdout.js):
|
||||
|
||||
```js
|
||||
var debug = require('debug');
|
||||
var error = debug('app:error');
|
||||
|
||||
// by default stderr is used
|
||||
error('goes to stderr!');
|
||||
|
||||
var log = debug('app:log');
|
||||
// set this namespace to log via console.log
|
||||
log.log = console.log.bind(console); // don't forget to bind to console!
|
||||
log('goes to stdout');
|
||||
error('still goes to stderr!');
|
||||
|
||||
// set all output to go via console.info
|
||||
// overrides all per-namespace log settings
|
||||
debug.log = console.info.bind(console);
|
||||
error('now goes to stdout via console.info');
|
||||
log('still goes to stdout, but via console.info now');
|
||||
```
|
||||
|
||||
## Extend
|
||||
You can simply extend debugger
|
||||
```js
|
||||
const log = require('debug')('auth');
|
||||
|
||||
//creates new debug instance with extended namespace
|
||||
const logSign = log.extend('sign');
|
||||
const logLogin = log.extend('login');
|
||||
|
||||
log('hello'); // auth hello
|
||||
logSign('hello'); //auth:sign hello
|
||||
logLogin('hello'); //auth:login hello
|
||||
```
|
||||
|
||||
## Set dynamically
|
||||
|
||||
You can also enable debug dynamically by calling the `enable()` method :
|
||||
|
||||
```js
|
||||
let debug = require('debug');
|
||||
|
||||
console.log(1, debug.enabled('test'));
|
||||
|
||||
debug.enable('test');
|
||||
console.log(2, debug.enabled('test'));
|
||||
|
||||
debug.disable();
|
||||
console.log(3, debug.enabled('test'));
|
||||
|
||||
```
|
||||
|
||||
print :
|
||||
```
|
||||
1 false
|
||||
2 true
|
||||
3 false
|
||||
```
|
||||
|
||||
Usage :
|
||||
`enable(namespaces)`
|
||||
`namespaces` can include modes separated by a colon and wildcards.
|
||||
|
||||
Note that calling `enable()` completely overrides previously set DEBUG variable :
|
||||
|
||||
```
|
||||
$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))'
|
||||
=> false
|
||||
```
|
||||
|
||||
`disable()`
|
||||
|
||||
Will disable all namespaces. The functions returns the namespaces currently
|
||||
enabled (and skipped). This can be useful if you want to disable debugging
|
||||
temporarily without knowing what was enabled to begin with.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
let debug = require('debug');
|
||||
debug.enable('foo:*,-foo:bar');
|
||||
let namespaces = debug.disable();
|
||||
debug.enable(namespaces);
|
||||
```
|
||||
|
||||
Note: There is no guarantee that the string will be identical to the initial
|
||||
enable string, but semantically they will be identical.
|
||||
|
||||
## Checking whether a debug target is enabled
|
||||
|
||||
After you've created a debug instance, you can determine whether or not it is
|
||||
enabled by checking the `enabled` property:
|
||||
|
||||
```javascript
|
||||
const debug = require('debug')('http');
|
||||
|
||||
if (debug.enabled) {
|
||||
// do stuff...
|
||||
}
|
||||
```
|
||||
|
||||
You can also manually toggle this property to force the debug instance to be
|
||||
enabled or disabled.
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
- TJ Holowaychuk
|
||||
- Nathan Rajlich
|
||||
- Andrew Rhyne
|
||||
|
||||
## Backers
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
|
||||
|
||||
<a href="https://opencollective.com/debug/backer/0/website" target="_blank"><img src="https://opencollective.com/debug/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/1/website" target="_blank"><img src="https://opencollective.com/debug/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/2/website" target="_blank"><img src="https://opencollective.com/debug/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/3/website" target="_blank"><img src="https://opencollective.com/debug/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/4/website" target="_blank"><img src="https://opencollective.com/debug/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/5/website" target="_blank"><img src="https://opencollective.com/debug/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/6/website" target="_blank"><img src="https://opencollective.com/debug/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/7/website" target="_blank"><img src="https://opencollective.com/debug/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/8/website" target="_blank"><img src="https://opencollective.com/debug/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/9/website" target="_blank"><img src="https://opencollective.com/debug/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/10/website" target="_blank"><img src="https://opencollective.com/debug/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/11/website" target="_blank"><img src="https://opencollective.com/debug/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/12/website" target="_blank"><img src="https://opencollective.com/debug/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/13/website" target="_blank"><img src="https://opencollective.com/debug/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/14/website" target="_blank"><img src="https://opencollective.com/debug/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/15/website" target="_blank"><img src="https://opencollective.com/debug/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/16/website" target="_blank"><img src="https://opencollective.com/debug/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/17/website" target="_blank"><img src="https://opencollective.com/debug/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/18/website" target="_blank"><img src="https://opencollective.com/debug/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/19/website" target="_blank"><img src="https://opencollective.com/debug/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/20/website" target="_blank"><img src="https://opencollective.com/debug/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/21/website" target="_blank"><img src="https://opencollective.com/debug/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/22/website" target="_blank"><img src="https://opencollective.com/debug/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/23/website" target="_blank"><img src="https://opencollective.com/debug/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/24/website" target="_blank"><img src="https://opencollective.com/debug/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/25/website" target="_blank"><img src="https://opencollective.com/debug/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/26/website" target="_blank"><img src="https://opencollective.com/debug/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/27/website" target="_blank"><img src="https://opencollective.com/debug/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/28/website" target="_blank"><img src="https://opencollective.com/debug/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/29/website" target="_blank"><img src="https://opencollective.com/debug/backer/29/avatar.svg"></a>
|
||||
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/debug/sponsor/0/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/1/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/2/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/3/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/4/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/5/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/6/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/7/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/8/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/9/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/10/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/11/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/12/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/13/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/14/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/15/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/16/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/17/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/18/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/19/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/20/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/21/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/22/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/23/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/24/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/25/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/26/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/27/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/28/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/29/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/29/avatar.svg"></a>
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+912
@@ -0,0 +1,912 @@
|
||||
"use strict";
|
||||
|
||||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
|
||||
|
||||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
|
||||
|
||||
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
|
||||
|
||||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
|
||||
|
||||
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
||||
|
||||
(function (f) {
|
||||
if ((typeof exports === "undefined" ? "undefined" : _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.debug = 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) {
|
||||
/**
|
||||
* Helpers.
|
||||
*/
|
||||
var s = 1000;
|
||||
var m = s * 60;
|
||||
var h = m * 60;
|
||||
var d = h * 24;
|
||||
var w = d * 7;
|
||||
var y = d * 365.25;
|
||||
/**
|
||||
* Parse or format the given `val`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `long` verbose formatting [false]
|
||||
*
|
||||
* @param {String|Number} val
|
||||
* @param {Object} [options]
|
||||
* @throws {Error} throw an error if val is not a non-empty string or a number
|
||||
* @return {String|Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function (val, options) {
|
||||
options = options || {};
|
||||
|
||||
var type = _typeof(val);
|
||||
|
||||
if (type === 'string' && val.length > 0) {
|
||||
return parse(val);
|
||||
} else if (type === 'number' && isNaN(val) === false) {
|
||||
return options.long ? fmtLong(val) : fmtShort(val);
|
||||
}
|
||||
|
||||
throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val));
|
||||
};
|
||||
/**
|
||||
* Parse the given `str` and return milliseconds.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Number}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function parse(str) {
|
||||
str = String(str);
|
||||
|
||||
if (str.length > 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
var match = /^((?:\d+)?\-?\d?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str);
|
||||
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
var n = parseFloat(match[1]);
|
||||
var type = (match[2] || 'ms').toLowerCase();
|
||||
|
||||
switch (type) {
|
||||
case 'years':
|
||||
case 'year':
|
||||
case 'yrs':
|
||||
case 'yr':
|
||||
case 'y':
|
||||
return n * y;
|
||||
|
||||
case 'weeks':
|
||||
case 'week':
|
||||
case 'w':
|
||||
return n * w;
|
||||
|
||||
case 'days':
|
||||
case 'day':
|
||||
case 'd':
|
||||
return n * d;
|
||||
|
||||
case 'hours':
|
||||
case 'hour':
|
||||
case 'hrs':
|
||||
case 'hr':
|
||||
case 'h':
|
||||
return n * h;
|
||||
|
||||
case 'minutes':
|
||||
case 'minute':
|
||||
case 'mins':
|
||||
case 'min':
|
||||
case 'm':
|
||||
return n * m;
|
||||
|
||||
case 'seconds':
|
||||
case 'second':
|
||||
case 'secs':
|
||||
case 'sec':
|
||||
case 's':
|
||||
return n * s;
|
||||
|
||||
case 'milliseconds':
|
||||
case 'millisecond':
|
||||
case 'msecs':
|
||||
case 'msec':
|
||||
case 'ms':
|
||||
return n;
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Short format for `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function fmtShort(ms) {
|
||||
var msAbs = Math.abs(ms);
|
||||
|
||||
if (msAbs >= d) {
|
||||
return Math.round(ms / d) + 'd';
|
||||
}
|
||||
|
||||
if (msAbs >= h) {
|
||||
return Math.round(ms / h) + 'h';
|
||||
}
|
||||
|
||||
if (msAbs >= m) {
|
||||
return Math.round(ms / m) + 'm';
|
||||
}
|
||||
|
||||
if (msAbs >= s) {
|
||||
return Math.round(ms / s) + 's';
|
||||
}
|
||||
|
||||
return ms + 'ms';
|
||||
}
|
||||
/**
|
||||
* Long format for `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function fmtLong(ms) {
|
||||
var msAbs = Math.abs(ms);
|
||||
|
||||
if (msAbs >= d) {
|
||||
return plural(ms, msAbs, d, 'day');
|
||||
}
|
||||
|
||||
if (msAbs >= h) {
|
||||
return plural(ms, msAbs, h, 'hour');
|
||||
}
|
||||
|
||||
if (msAbs >= m) {
|
||||
return plural(ms, msAbs, m, 'minute');
|
||||
}
|
||||
|
||||
if (msAbs >= s) {
|
||||
return plural(ms, msAbs, s, 'second');
|
||||
}
|
||||
|
||||
return ms + ' ms';
|
||||
}
|
||||
/**
|
||||
* Pluralization helper.
|
||||
*/
|
||||
|
||||
|
||||
function plural(ms, msAbs, n, name) {
|
||||
var isPlural = msAbs >= n * 1.5;
|
||||
return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
|
||||
}
|
||||
}, {}],
|
||||
2: [function (require, module, exports) {
|
||||
// shim for using process in browser
|
||||
var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it
|
||||
// don't break things. But we need to wrap it in a try catch in case it is
|
||||
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
||||
// function because try/catches deoptimize in certain engines.
|
||||
|
||||
var cachedSetTimeout;
|
||||
var cachedClearTimeout;
|
||||
|
||||
function defaultSetTimout() {
|
||||
throw new Error('setTimeout has not been defined');
|
||||
}
|
||||
|
||||
function defaultClearTimeout() {
|
||||
throw new Error('clearTimeout has not been defined');
|
||||
}
|
||||
|
||||
(function () {
|
||||
try {
|
||||
if (typeof setTimeout === 'function') {
|
||||
cachedSetTimeout = setTimeout;
|
||||
} else {
|
||||
cachedSetTimeout = defaultSetTimout;
|
||||
}
|
||||
} catch (e) {
|
||||
cachedSetTimeout = defaultSetTimout;
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof clearTimeout === 'function') {
|
||||
cachedClearTimeout = clearTimeout;
|
||||
} else {
|
||||
cachedClearTimeout = defaultClearTimeout;
|
||||
}
|
||||
} catch (e) {
|
||||
cachedClearTimeout = defaultClearTimeout;
|
||||
}
|
||||
})();
|
||||
|
||||
function runTimeout(fun) {
|
||||
if (cachedSetTimeout === setTimeout) {
|
||||
//normal enviroments in sane situations
|
||||
return setTimeout(fun, 0);
|
||||
} // if setTimeout wasn't available but was latter defined
|
||||
|
||||
|
||||
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
||||
cachedSetTimeout = setTimeout;
|
||||
return setTimeout(fun, 0);
|
||||
}
|
||||
|
||||
try {
|
||||
// when when somebody has screwed with setTimeout but no I.E. maddness
|
||||
return cachedSetTimeout(fun, 0);
|
||||
} catch (e) {
|
||||
try {
|
||||
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
||||
return cachedSetTimeout.call(null, fun, 0);
|
||||
} catch (e) {
|
||||
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
||||
return cachedSetTimeout.call(this, fun, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runClearTimeout(marker) {
|
||||
if (cachedClearTimeout === clearTimeout) {
|
||||
//normal enviroments in sane situations
|
||||
return clearTimeout(marker);
|
||||
} // if clearTimeout wasn't available but was latter defined
|
||||
|
||||
|
||||
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
||||
cachedClearTimeout = clearTimeout;
|
||||
return clearTimeout(marker);
|
||||
}
|
||||
|
||||
try {
|
||||
// when when somebody has screwed with setTimeout but no I.E. maddness
|
||||
return cachedClearTimeout(marker);
|
||||
} catch (e) {
|
||||
try {
|
||||
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
||||
return cachedClearTimeout.call(null, marker);
|
||||
} catch (e) {
|
||||
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
||||
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
||||
return cachedClearTimeout.call(this, marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var queue = [];
|
||||
var draining = false;
|
||||
var currentQueue;
|
||||
var queueIndex = -1;
|
||||
|
||||
function cleanUpNextTick() {
|
||||
if (!draining || !currentQueue) {
|
||||
return;
|
||||
}
|
||||
|
||||
draining = false;
|
||||
|
||||
if (currentQueue.length) {
|
||||
queue = currentQueue.concat(queue);
|
||||
} else {
|
||||
queueIndex = -1;
|
||||
}
|
||||
|
||||
if (queue.length) {
|
||||
drainQueue();
|
||||
}
|
||||
}
|
||||
|
||||
function drainQueue() {
|
||||
if (draining) {
|
||||
return;
|
||||
}
|
||||
|
||||
var timeout = runTimeout(cleanUpNextTick);
|
||||
draining = true;
|
||||
var len = queue.length;
|
||||
|
||||
while (len) {
|
||||
currentQueue = queue;
|
||||
queue = [];
|
||||
|
||||
while (++queueIndex < len) {
|
||||
if (currentQueue) {
|
||||
currentQueue[queueIndex].run();
|
||||
}
|
||||
}
|
||||
|
||||
queueIndex = -1;
|
||||
len = queue.length;
|
||||
}
|
||||
|
||||
currentQueue = null;
|
||||
draining = false;
|
||||
runClearTimeout(timeout);
|
||||
}
|
||||
|
||||
process.nextTick = function (fun) {
|
||||
var args = new Array(arguments.length - 1);
|
||||
|
||||
if (arguments.length > 1) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
args[i - 1] = arguments[i];
|
||||
}
|
||||
}
|
||||
|
||||
queue.push(new Item(fun, args));
|
||||
|
||||
if (queue.length === 1 && !draining) {
|
||||
runTimeout(drainQueue);
|
||||
}
|
||||
}; // v8 likes predictible objects
|
||||
|
||||
|
||||
function Item(fun, array) {
|
||||
this.fun = fun;
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
Item.prototype.run = function () {
|
||||
this.fun.apply(null, this.array);
|
||||
};
|
||||
|
||||
process.title = 'browser';
|
||||
process.browser = true;
|
||||
process.env = {};
|
||||
process.argv = [];
|
||||
process.version = ''; // empty string to avoid regexp issues
|
||||
|
||||
process.versions = {};
|
||||
|
||||
function noop() {}
|
||||
|
||||
process.on = noop;
|
||||
process.addListener = noop;
|
||||
process.once = noop;
|
||||
process.off = noop;
|
||||
process.removeListener = noop;
|
||||
process.removeAllListeners = noop;
|
||||
process.emit = noop;
|
||||
process.prependListener = noop;
|
||||
process.prependOnceListener = noop;
|
||||
|
||||
process.listeners = function (name) {
|
||||
return [];
|
||||
};
|
||||
|
||||
process.binding = function (name) {
|
||||
throw new Error('process.binding is not supported');
|
||||
};
|
||||
|
||||
process.cwd = function () {
|
||||
return '/';
|
||||
};
|
||||
|
||||
process.chdir = function (dir) {
|
||||
throw new Error('process.chdir is not supported');
|
||||
};
|
||||
|
||||
process.umask = function () {
|
||||
return 0;
|
||||
};
|
||||
}, {}],
|
||||
3: [function (require, module, exports) {
|
||||
/**
|
||||
* This is the common logic for both the Node.js and web browser
|
||||
* implementations of `debug()`.
|
||||
*/
|
||||
function setup(env) {
|
||||
createDebug.debug = createDebug;
|
||||
createDebug.default = createDebug;
|
||||
createDebug.coerce = coerce;
|
||||
createDebug.disable = disable;
|
||||
createDebug.enable = enable;
|
||||
createDebug.enabled = enabled;
|
||||
createDebug.humanize = require('ms');
|
||||
Object.keys(env).forEach(function (key) {
|
||||
createDebug[key] = env[key];
|
||||
});
|
||||
/**
|
||||
* Active `debug` instances.
|
||||
*/
|
||||
|
||||
createDebug.instances = [];
|
||||
/**
|
||||
* The currently active debug mode names, and names to skip.
|
||||
*/
|
||||
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
/**
|
||||
* Map of special "%n" handling functions, for the debug "format" argument.
|
||||
*
|
||||
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
|
||||
*/
|
||||
|
||||
createDebug.formatters = {};
|
||||
/**
|
||||
* Selects a color for a debug namespace
|
||||
* @param {String} namespace The namespace string for the for the debug instance to be colored
|
||||
* @return {Number|String} An ANSI color code for the given namespace
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function selectColor(namespace) {
|
||||
var hash = 0;
|
||||
|
||||
for (var i = 0; i < namespace.length; i++) {
|
||||
hash = (hash << 5) - hash + namespace.charCodeAt(i);
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
|
||||
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
||||
}
|
||||
|
||||
createDebug.selectColor = selectColor;
|
||||
/**
|
||||
* Create a debugger with the given `namespace`.
|
||||
*
|
||||
* @param {String} namespace
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function createDebug(namespace) {
|
||||
var prevTime;
|
||||
|
||||
function debug() {
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
// Disabled?
|
||||
if (!debug.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = debug; // Set `diff` timestamp
|
||||
|
||||
var curr = Number(new Date());
|
||||
var ms = curr - (prevTime || curr);
|
||||
self.diff = ms;
|
||||
self.prev = prevTime;
|
||||
self.curr = curr;
|
||||
prevTime = curr;
|
||||
args[0] = createDebug.coerce(args[0]);
|
||||
|
||||
if (typeof args[0] !== 'string') {
|
||||
// Anything else let's inspect with %O
|
||||
args.unshift('%O');
|
||||
} // Apply any `formatters` transformations
|
||||
|
||||
|
||||
var index = 0;
|
||||
args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) {
|
||||
// If we encounter an escaped % then don't increase the array index
|
||||
if (match === '%%') {
|
||||
return match;
|
||||
}
|
||||
|
||||
index++;
|
||||
var formatter = createDebug.formatters[format];
|
||||
|
||||
if (typeof formatter === 'function') {
|
||||
var val = args[index];
|
||||
match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format`
|
||||
|
||||
args.splice(index, 1);
|
||||
index--;
|
||||
}
|
||||
|
||||
return match;
|
||||
}); // Apply env-specific formatting (colors, etc.)
|
||||
|
||||
createDebug.formatArgs.call(self, args);
|
||||
var logFn = self.log || createDebug.log;
|
||||
logFn.apply(self, args);
|
||||
}
|
||||
|
||||
debug.namespace = namespace;
|
||||
debug.enabled = createDebug.enabled(namespace);
|
||||
debug.useColors = createDebug.useColors();
|
||||
debug.color = selectColor(namespace);
|
||||
debug.destroy = destroy;
|
||||
debug.extend = extend; // Debug.formatArgs = formatArgs;
|
||||
// debug.rawLog = rawLog;
|
||||
// env-specific initialization logic for debug instances
|
||||
|
||||
if (typeof createDebug.init === 'function') {
|
||||
createDebug.init(debug);
|
||||
}
|
||||
|
||||
createDebug.instances.push(debug);
|
||||
return debug;
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
var index = createDebug.instances.indexOf(this);
|
||||
|
||||
if (index !== -1) {
|
||||
createDebug.instances.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function extend(namespace, delimiter) {
|
||||
var newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
|
||||
newDebug.log = this.log;
|
||||
return newDebug;
|
||||
}
|
||||
/**
|
||||
* Enables a debug mode by namespaces. This can include modes
|
||||
* separated by a colon and wildcards.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
function enable(namespaces) {
|
||||
createDebug.save(namespaces);
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
var i;
|
||||
var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
|
||||
var len = split.length;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!split[i]) {
|
||||
// ignore empty strings
|
||||
continue;
|
||||
}
|
||||
|
||||
namespaces = split[i].replace(/\*/g, '.*?');
|
||||
|
||||
if (namespaces[0] === '-') {
|
||||
createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
|
||||
} else {
|
||||
createDebug.names.push(new RegExp('^' + namespaces + '$'));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < createDebug.instances.length; i++) {
|
||||
var instance = createDebug.instances[i];
|
||||
instance.enabled = createDebug.enabled(instance.namespace);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Disable debug output.
|
||||
*
|
||||
* @return {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
function disable() {
|
||||
var namespaces = [].concat(_toConsumableArray(createDebug.names.map(toNamespace)), _toConsumableArray(createDebug.skips.map(toNamespace).map(function (namespace) {
|
||||
return '-' + namespace;
|
||||
}))).join(',');
|
||||
createDebug.enable('');
|
||||
return namespaces;
|
||||
}
|
||||
/**
|
||||
* Returns true if the given mode name is enabled, false otherwise.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
function enabled(name) {
|
||||
if (name[name.length - 1] === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
var i;
|
||||
var len;
|
||||
|
||||
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
||||
if (createDebug.skips[i].test(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, len = createDebug.names.length; i < len; i++) {
|
||||
if (createDebug.names[i].test(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Convert regexp to namespace
|
||||
*
|
||||
* @param {RegExp} regxep
|
||||
* @return {String} namespace
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function toNamespace(regexp) {
|
||||
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, '*');
|
||||
}
|
||||
/**
|
||||
* Coerce `val`.
|
||||
*
|
||||
* @param {Mixed} val
|
||||
* @return {Mixed}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function coerce(val) {
|
||||
if (val instanceof Error) {
|
||||
return val.stack || val.message;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
createDebug.enable(createDebug.load());
|
||||
return createDebug;
|
||||
}
|
||||
|
||||
module.exports = setup;
|
||||
}, {
|
||||
"ms": 1
|
||||
}],
|
||||
4: [function (require, module, exports) {
|
||||
(function (process) {
|
||||
/* eslint-env browser */
|
||||
|
||||
/**
|
||||
* This is the web browser implementation of `debug()`.
|
||||
*/
|
||||
exports.log = log;
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.storage = localstorage();
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33'];
|
||||
/**
|
||||
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
|
||||
* and the Firebug extension (any Firefox version) are known
|
||||
* to support "%c" CSS customizations.
|
||||
*
|
||||
* TODO: add a `localStorage` variable to explicitly enable/disable colors
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
|
||||
function useColors() {
|
||||
// NB: In an Electron preload script, document will be defined but not fully
|
||||
// initialized. Since we know we're in Chrome, we'll just detect this case
|
||||
// explicitly
|
||||
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
|
||||
return true;
|
||||
} // Internet Explorer and Edge do not support colors.
|
||||
|
||||
|
||||
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
||||
return false;
|
||||
} // Is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
|
||||
|
||||
return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773
|
||||
typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
||||
}
|
||||
/**
|
||||
* Colorize log arguments if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
function formatArgs(args) {
|
||||
args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff);
|
||||
|
||||
if (!this.useColors) {
|
||||
return;
|
||||
}
|
||||
|
||||
var c = 'color: ' + this.color;
|
||||
args.splice(1, 0, c, 'color: inherit'); // The final "%c" is somewhat tricky, because there could be other
|
||||
// arguments passed either before or after the %c, so we need to
|
||||
// figure out the correct index to insert the CSS into
|
||||
|
||||
var index = 0;
|
||||
var lastC = 0;
|
||||
args[0].replace(/%[a-zA-Z%]/g, function (match) {
|
||||
if (match === '%%') {
|
||||
return;
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (match === '%c') {
|
||||
// We only are interested in the *last* %c
|
||||
// (the user may have provided their own)
|
||||
lastC = index;
|
||||
}
|
||||
});
|
||||
args.splice(lastC, 0, c);
|
||||
}
|
||||
/**
|
||||
* Invokes `console.log()` when available.
|
||||
* No-op when `console.log` is not a "function".
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
function log() {
|
||||
var _console;
|
||||
|
||||
// This hackery is required for IE8/9, where
|
||||
// the `console.log` function doesn't have 'apply'
|
||||
return (typeof console === "undefined" ? "undefined" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments);
|
||||
}
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function save(namespaces) {
|
||||
try {
|
||||
if (namespaces) {
|
||||
exports.storage.setItem('debug', namespaces);
|
||||
} else {
|
||||
exports.storage.removeItem('debug');
|
||||
}
|
||||
} catch (error) {// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function load() {
|
||||
var r;
|
||||
|
||||
try {
|
||||
r = exports.storage.getItem('debug');
|
||||
} catch (error) {} // Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
|
||||
|
||||
|
||||
if (!r && typeof process !== 'undefined' && 'env' in process) {
|
||||
r = process.env.DEBUG;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
/**
|
||||
* Localstorage attempts to return the localstorage.
|
||||
*
|
||||
* This is necessary because safari throws
|
||||
* when a user disables cookies/localstorage
|
||||
* and you attempt to access it.
|
||||
*
|
||||
* @return {LocalStorage}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
|
||||
function localstorage() {
|
||||
try {
|
||||
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
|
||||
// The Browser also has localStorage in the global context.
|
||||
return localStorage;
|
||||
} catch (error) {// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
var formatters = module.exports.formatters;
|
||||
/**
|
||||
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
|
||||
*/
|
||||
|
||||
formatters.j = function (v) {
|
||||
try {
|
||||
return JSON.stringify(v);
|
||||
} catch (error) {
|
||||
return '[UnexpectedJSONParseError]: ' + error.message;
|
||||
}
|
||||
};
|
||||
}).call(this, require('_process'));
|
||||
}, {
|
||||
"./common": 3,
|
||||
"_process": 2
|
||||
}]
|
||||
}, {}, [4])(4);
|
||||
});
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"_from": "debug@^4.1.1",
|
||||
"_id": "debug@4.1.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"_location": "/simple-oauth2/debug",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "debug@^4.1.1",
|
||||
"name": "debug",
|
||||
"escapedName": "debug",
|
||||
"rawSpec": "^4.1.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^4.1.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/simple-oauth2"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"_shasum": "3b72260255109c6b589cee050f1d516139664791",
|
||||
"_spec": "debug@^4.1.1",
|
||||
"_where": "C:\\Daten\\Git\\Tumortisch\\node_modules\\simple-oauth2",
|
||||
"author": {
|
||||
"name": "TJ Holowaychuk",
|
||||
"email": "tj@vision-media.ca"
|
||||
},
|
||||
"browser": "./src/browser.js",
|
||||
"bugs": {
|
||||
"url": "https://github.com/visionmedia/debug/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Nathan Rajlich",
|
||||
"email": "nathan@tootallnate.net",
|
||||
"url": "http://n8.io"
|
||||
},
|
||||
{
|
||||
"name": "Andrew Rhyne",
|
||||
"email": "rhyneandrew@gmail.com"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "small debugging utility",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"browserify": "14.4.0",
|
||||
"chai": "^3.5.0",
|
||||
"concurrently": "^3.1.0",
|
||||
"coveralls": "^3.0.2",
|
||||
"istanbul": "^0.4.5",
|
||||
"karma": "^3.0.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-phantomjs-launcher": "^1.0.2",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-lcov-reporter": "^1.2.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"xo": "^0.23.0"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist/debug.js",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"homepage": "https://github.com/visionmedia/debug#readme",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"log",
|
||||
"debugger"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "./src/index.js",
|
||||
"name": "debug",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/visionmedia/debug.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:debug && npm run build:test",
|
||||
"build:debug": "babel -o dist/debug.js dist/debug.es6.js > dist/debug.js",
|
||||
"build:test": "babel -d dist test.js",
|
||||
"clean": "rimraf dist coverage",
|
||||
"lint": "xo",
|
||||
"prebuild:debug": "mkdir -p dist && browserify --standalone debug -o dist/debug.es6.js .",
|
||||
"pretest:browser": "npm run build",
|
||||
"test": "npm run test:node && npm run test:browser",
|
||||
"test:browser": "karma start --single-run",
|
||||
"test:coverage": "cat ./coverage/lcov.info | coveralls",
|
||||
"test:node": "istanbul cover _mocha -- test.js"
|
||||
},
|
||||
"unpkg": "./dist/debug.js",
|
||||
"version": "4.1.1"
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/* eslint-env browser */
|
||||
|
||||
/**
|
||||
* This is the web browser implementation of `debug()`.
|
||||
*/
|
||||
|
||||
exports.log = log;
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.storage = localstorage();
|
||||
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = [
|
||||
'#0000CC',
|
||||
'#0000FF',
|
||||
'#0033CC',
|
||||
'#0033FF',
|
||||
'#0066CC',
|
||||
'#0066FF',
|
||||
'#0099CC',
|
||||
'#0099FF',
|
||||
'#00CC00',
|
||||
'#00CC33',
|
||||
'#00CC66',
|
||||
'#00CC99',
|
||||
'#00CCCC',
|
||||
'#00CCFF',
|
||||
'#3300CC',
|
||||
'#3300FF',
|
||||
'#3333CC',
|
||||
'#3333FF',
|
||||
'#3366CC',
|
||||
'#3366FF',
|
||||
'#3399CC',
|
||||
'#3399FF',
|
||||
'#33CC00',
|
||||
'#33CC33',
|
||||
'#33CC66',
|
||||
'#33CC99',
|
||||
'#33CCCC',
|
||||
'#33CCFF',
|
||||
'#6600CC',
|
||||
'#6600FF',
|
||||
'#6633CC',
|
||||
'#6633FF',
|
||||
'#66CC00',
|
||||
'#66CC33',
|
||||
'#9900CC',
|
||||
'#9900FF',
|
||||
'#9933CC',
|
||||
'#9933FF',
|
||||
'#99CC00',
|
||||
'#99CC33',
|
||||
'#CC0000',
|
||||
'#CC0033',
|
||||
'#CC0066',
|
||||
'#CC0099',
|
||||
'#CC00CC',
|
||||
'#CC00FF',
|
||||
'#CC3300',
|
||||
'#CC3333',
|
||||
'#CC3366',
|
||||
'#CC3399',
|
||||
'#CC33CC',
|
||||
'#CC33FF',
|
||||
'#CC6600',
|
||||
'#CC6633',
|
||||
'#CC9900',
|
||||
'#CC9933',
|
||||
'#CCCC00',
|
||||
'#CCCC33',
|
||||
'#FF0000',
|
||||
'#FF0033',
|
||||
'#FF0066',
|
||||
'#FF0099',
|
||||
'#FF00CC',
|
||||
'#FF00FF',
|
||||
'#FF3300',
|
||||
'#FF3333',
|
||||
'#FF3366',
|
||||
'#FF3399',
|
||||
'#FF33CC',
|
||||
'#FF33FF',
|
||||
'#FF6600',
|
||||
'#FF6633',
|
||||
'#FF9900',
|
||||
'#FF9933',
|
||||
'#FFCC00',
|
||||
'#FFCC33'
|
||||
];
|
||||
|
||||
/**
|
||||
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
|
||||
* and the Firebug extension (any Firefox version) are known
|
||||
* to support "%c" CSS customizations.
|
||||
*
|
||||
* TODO: add a `localStorage` variable to explicitly enable/disable colors
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
function useColors() {
|
||||
// NB: In an Electron preload script, document will be defined but not fully
|
||||
// initialized. Since we know we're in Chrome, we'll just detect this case
|
||||
// explicitly
|
||||
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internet Explorer and Edge do not support colors.
|
||||
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
|
||||
// Is firebug? http://stackoverflow.com/a/398120/376773
|
||||
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
|
||||
// Is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
|
||||
// Double check webkit in userAgent just in case we are in a worker
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorize log arguments if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function formatArgs(args) {
|
||||
args[0] = (this.useColors ? '%c' : '') +
|
||||
this.namespace +
|
||||
(this.useColors ? ' %c' : ' ') +
|
||||
args[0] +
|
||||
(this.useColors ? '%c ' : ' ') +
|
||||
'+' + module.exports.humanize(this.diff);
|
||||
|
||||
if (!this.useColors) {
|
||||
return;
|
||||
}
|
||||
|
||||
const c = 'color: ' + this.color;
|
||||
args.splice(1, 0, c, 'color: inherit');
|
||||
|
||||
// The final "%c" is somewhat tricky, because there could be other
|
||||
// arguments passed either before or after the %c, so we need to
|
||||
// figure out the correct index to insert the CSS into
|
||||
let index = 0;
|
||||
let lastC = 0;
|
||||
args[0].replace(/%[a-zA-Z%]/g, match => {
|
||||
if (match === '%%') {
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
if (match === '%c') {
|
||||
// We only are interested in the *last* %c
|
||||
// (the user may have provided their own)
|
||||
lastC = index;
|
||||
}
|
||||
});
|
||||
|
||||
args.splice(lastC, 0, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes `console.log()` when available.
|
||||
* No-op when `console.log` is not a "function".
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
function log(...args) {
|
||||
// This hackery is required for IE8/9, where
|
||||
// the `console.log` function doesn't have 'apply'
|
||||
return typeof console === 'object' &&
|
||||
console.log &&
|
||||
console.log(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
function save(namespaces) {
|
||||
try {
|
||||
if (namespaces) {
|
||||
exports.storage.setItem('debug', namespaces);
|
||||
} else {
|
||||
exports.storage.removeItem('debug');
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
function load() {
|
||||
let r;
|
||||
try {
|
||||
r = exports.storage.getItem('debug');
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
|
||||
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
|
||||
if (!r && typeof process !== 'undefined' && 'env' in process) {
|
||||
r = process.env.DEBUG;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localstorage attempts to return the localstorage.
|
||||
*
|
||||
* This is necessary because safari throws
|
||||
* when a user disables cookies/localstorage
|
||||
* and you attempt to access it.
|
||||
*
|
||||
* @return {LocalStorage}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function localstorage() {
|
||||
try {
|
||||
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
|
||||
// The Browser also has localStorage in the global context.
|
||||
return localStorage;
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
|
||||
const {formatters} = module.exports;
|
||||
|
||||
/**
|
||||
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
|
||||
*/
|
||||
|
||||
formatters.j = function (v) {
|
||||
try {
|
||||
return JSON.stringify(v);
|
||||
} catch (error) {
|
||||
return '[UnexpectedJSONParseError]: ' + error.message;
|
||||
}
|
||||
};
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
|
||||
/**
|
||||
* This is the common logic for both the Node.js and web browser
|
||||
* implementations of `debug()`.
|
||||
*/
|
||||
|
||||
function setup(env) {
|
||||
createDebug.debug = createDebug;
|
||||
createDebug.default = createDebug;
|
||||
createDebug.coerce = coerce;
|
||||
createDebug.disable = disable;
|
||||
createDebug.enable = enable;
|
||||
createDebug.enabled = enabled;
|
||||
createDebug.humanize = require('ms');
|
||||
|
||||
Object.keys(env).forEach(key => {
|
||||
createDebug[key] = env[key];
|
||||
});
|
||||
|
||||
/**
|
||||
* Active `debug` instances.
|
||||
*/
|
||||
createDebug.instances = [];
|
||||
|
||||
/**
|
||||
* The currently active debug mode names, and names to skip.
|
||||
*/
|
||||
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
|
||||
/**
|
||||
* Map of special "%n" handling functions, for the debug "format" argument.
|
||||
*
|
||||
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
|
||||
*/
|
||||
createDebug.formatters = {};
|
||||
|
||||
/**
|
||||
* Selects a color for a debug namespace
|
||||
* @param {String} namespace The namespace string for the for the debug instance to be colored
|
||||
* @return {Number|String} An ANSI color code for the given namespace
|
||||
* @api private
|
||||
*/
|
||||
function selectColor(namespace) {
|
||||
let hash = 0;
|
||||
|
||||
for (let i = 0; i < namespace.length; i++) {
|
||||
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
|
||||
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
||||
}
|
||||
createDebug.selectColor = selectColor;
|
||||
|
||||
/**
|
||||
* Create a debugger with the given `namespace`.
|
||||
*
|
||||
* @param {String} namespace
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
function createDebug(namespace) {
|
||||
let prevTime;
|
||||
|
||||
function debug(...args) {
|
||||
// Disabled?
|
||||
if (!debug.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const self = debug;
|
||||
|
||||
// Set `diff` timestamp
|
||||
const curr = Number(new Date());
|
||||
const ms = curr - (prevTime || curr);
|
||||
self.diff = ms;
|
||||
self.prev = prevTime;
|
||||
self.curr = curr;
|
||||
prevTime = curr;
|
||||
|
||||
args[0] = createDebug.coerce(args[0]);
|
||||
|
||||
if (typeof args[0] !== 'string') {
|
||||
// Anything else let's inspect with %O
|
||||
args.unshift('%O');
|
||||
}
|
||||
|
||||
// Apply any `formatters` transformations
|
||||
let index = 0;
|
||||
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
|
||||
// If we encounter an escaped % then don't increase the array index
|
||||
if (match === '%%') {
|
||||
return match;
|
||||
}
|
||||
index++;
|
||||
const formatter = createDebug.formatters[format];
|
||||
if (typeof formatter === 'function') {
|
||||
const val = args[index];
|
||||
match = formatter.call(self, val);
|
||||
|
||||
// Now we need to remove `args[index]` since it's inlined in the `format`
|
||||
args.splice(index, 1);
|
||||
index--;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
// Apply env-specific formatting (colors, etc.)
|
||||
createDebug.formatArgs.call(self, args);
|
||||
|
||||
const logFn = self.log || createDebug.log;
|
||||
logFn.apply(self, args);
|
||||
}
|
||||
|
||||
debug.namespace = namespace;
|
||||
debug.enabled = createDebug.enabled(namespace);
|
||||
debug.useColors = createDebug.useColors();
|
||||
debug.color = selectColor(namespace);
|
||||
debug.destroy = destroy;
|
||||
debug.extend = extend;
|
||||
// Debug.formatArgs = formatArgs;
|
||||
// debug.rawLog = rawLog;
|
||||
|
||||
// env-specific initialization logic for debug instances
|
||||
if (typeof createDebug.init === 'function') {
|
||||
createDebug.init(debug);
|
||||
}
|
||||
|
||||
createDebug.instances.push(debug);
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
const index = createDebug.instances.indexOf(this);
|
||||
if (index !== -1) {
|
||||
createDebug.instances.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function extend(namespace, delimiter) {
|
||||
const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
|
||||
newDebug.log = this.log;
|
||||
return newDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables a debug mode by namespaces. This can include modes
|
||||
* separated by a colon and wildcards.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
function enable(namespaces) {
|
||||
createDebug.save(namespaces);
|
||||
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
|
||||
let i;
|
||||
const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
|
||||
const len = split.length;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!split[i]) {
|
||||
// ignore empty strings
|
||||
continue;
|
||||
}
|
||||
|
||||
namespaces = split[i].replace(/\*/g, '.*?');
|
||||
|
||||
if (namespaces[0] === '-') {
|
||||
createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
|
||||
} else {
|
||||
createDebug.names.push(new RegExp('^' + namespaces + '$'));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < createDebug.instances.length; i++) {
|
||||
const instance = createDebug.instances[i];
|
||||
instance.enabled = createDebug.enabled(instance.namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable debug output.
|
||||
*
|
||||
* @return {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
function disable() {
|
||||
const namespaces = [
|
||||
...createDebug.names.map(toNamespace),
|
||||
...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
|
||||
].join(',');
|
||||
createDebug.enable('');
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode name is enabled, false otherwise.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
function enabled(name) {
|
||||
if (name[name.length - 1] === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
let i;
|
||||
let len;
|
||||
|
||||
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
||||
if (createDebug.skips[i].test(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, len = createDebug.names.length; i < len; i++) {
|
||||
if (createDebug.names[i].test(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert regexp to namespace
|
||||
*
|
||||
* @param {RegExp} regxep
|
||||
* @return {String} namespace
|
||||
* @api private
|
||||
*/
|
||||
function toNamespace(regexp) {
|
||||
return regexp.toString()
|
||||
.substring(2, regexp.toString().length - 2)
|
||||
.replace(/\.\*\?$/, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce `val`.
|
||||
*
|
||||
* @param {Mixed} val
|
||||
* @return {Mixed}
|
||||
* @api private
|
||||
*/
|
||||
function coerce(val) {
|
||||
if (val instanceof Error) {
|
||||
return val.stack || val.message;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
createDebug.enable(createDebug.load());
|
||||
|
||||
return createDebug;
|
||||
}
|
||||
|
||||
module.exports = setup;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Detect Electron renderer / nwjs process, which is node, but we should
|
||||
* treat as a browser.
|
||||
*/
|
||||
|
||||
if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
|
||||
module.exports = require('./browser.js');
|
||||
} else {
|
||||
module.exports = require('./node.js');
|
||||
}
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const tty = require('tty');
|
||||
const util = require('util');
|
||||
|
||||
/**
|
||||
* This is the Node.js implementation of `debug()`.
|
||||
*/
|
||||
|
||||
exports.init = init;
|
||||
exports.log = log;
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = [6, 2, 3, 4, 5, 1];
|
||||
|
||||
try {
|
||||
// Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json)
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const supportsColor = require('supports-color');
|
||||
|
||||
if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
|
||||
exports.colors = [
|
||||
20,
|
||||
21,
|
||||
26,
|
||||
27,
|
||||
32,
|
||||
33,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
45,
|
||||
56,
|
||||
57,
|
||||
62,
|
||||
63,
|
||||
68,
|
||||
69,
|
||||
74,
|
||||
75,
|
||||
76,
|
||||
77,
|
||||
78,
|
||||
79,
|
||||
80,
|
||||
81,
|
||||
92,
|
||||
93,
|
||||
98,
|
||||
99,
|
||||
112,
|
||||
113,
|
||||
128,
|
||||
129,
|
||||
134,
|
||||
135,
|
||||
148,
|
||||
149,
|
||||
160,
|
||||
161,
|
||||
162,
|
||||
163,
|
||||
164,
|
||||
165,
|
||||
166,
|
||||
167,
|
||||
168,
|
||||
169,
|
||||
170,
|
||||
171,
|
||||
172,
|
||||
173,
|
||||
178,
|
||||
179,
|
||||
184,
|
||||
185,
|
||||
196,
|
||||
197,
|
||||
198,
|
||||
199,
|
||||
200,
|
||||
201,
|
||||
202,
|
||||
203,
|
||||
204,
|
||||
205,
|
||||
206,
|
||||
207,
|
||||
208,
|
||||
209,
|
||||
214,
|
||||
215,
|
||||
220,
|
||||
221
|
||||
];
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow - we only care if `supports-color` is available; it doesn't have to be.
|
||||
}
|
||||
|
||||
/**
|
||||
* Build up the default `inspectOpts` object from the environment variables.
|
||||
*
|
||||
* $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
|
||||
*/
|
||||
|
||||
exports.inspectOpts = Object.keys(process.env).filter(key => {
|
||||
return /^debug_/i.test(key);
|
||||
}).reduce((obj, key) => {
|
||||
// Camel-case
|
||||
const prop = key
|
||||
.substring(6)
|
||||
.toLowerCase()
|
||||
.replace(/_([a-z])/g, (_, k) => {
|
||||
return k.toUpperCase();
|
||||
});
|
||||
|
||||
// Coerce string value into JS value
|
||||
let val = process.env[key];
|
||||
if (/^(yes|on|true|enabled)$/i.test(val)) {
|
||||
val = true;
|
||||
} else if (/^(no|off|false|disabled)$/i.test(val)) {
|
||||
val = false;
|
||||
} else if (val === 'null') {
|
||||
val = null;
|
||||
} else {
|
||||
val = Number(val);
|
||||
}
|
||||
|
||||
obj[prop] = val;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* Is stdout a TTY? Colored output is enabled when `true`.
|
||||
*/
|
||||
|
||||
function useColors() {
|
||||
return 'colors' in exports.inspectOpts ?
|
||||
Boolean(exports.inspectOpts.colors) :
|
||||
tty.isatty(process.stderr.fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds ANSI color escape codes if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function formatArgs(args) {
|
||||
const {namespace: name, useColors} = this;
|
||||
|
||||
if (useColors) {
|
||||
const c = this.color;
|
||||
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
|
||||
const prefix = ` ${colorCode};1m${name} \u001B[0m`;
|
||||
|
||||
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
|
||||
args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
|
||||
} else {
|
||||
args[0] = getDate() + name + ' ' + args[0];
|
||||
}
|
||||
}
|
||||
|
||||
function getDate() {
|
||||
if (exports.inspectOpts.hideDate) {
|
||||
return '';
|
||||
}
|
||||
return new Date().toISOString() + ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes `util.format()` with the specified arguments and writes to stderr.
|
||||
*/
|
||||
|
||||
function log(...args) {
|
||||
return process.stderr.write(util.format(...args) + '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
function save(namespaces) {
|
||||
if (namespaces) {
|
||||
process.env.DEBUG = namespaces;
|
||||
} else {
|
||||
// If you set a process.env field to null or undefined, it gets cast to the
|
||||
// string 'null' or 'undefined'. Just delete instead.
|
||||
delete process.env.DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function load() {
|
||||
return process.env.DEBUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init logic for `debug` instances.
|
||||
*
|
||||
* Create a new `inspectOpts` object in case `useColors` is set
|
||||
* differently for a particular `debug` instance.
|
||||
*/
|
||||
|
||||
function init(debug) {
|
||||
debug.inspectOpts = {};
|
||||
|
||||
const keys = Object.keys(exports.inspectOpts);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
|
||||
const {formatters} = module.exports;
|
||||
|
||||
/**
|
||||
* Map %o to `util.inspect()`, all on a single line.
|
||||
*/
|
||||
|
||||
formatters.o = function (v) {
|
||||
this.inspectOpts.colors = this.useColors;
|
||||
return util.inspect(v, this.inspectOpts)
|
||||
.replace(/\s*\n\s*/g, ' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Map %O to `util.inspect()`, allowing multiple lines if needed.
|
||||
*/
|
||||
|
||||
formatters.O = function (v) {
|
||||
this.inspectOpts.colors = this.useColors;
|
||||
return util.inspect(v, this.inspectOpts);
|
||||
};
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"_from": "simple-oauth2@2.5.2",
|
||||
"_id": "simple-oauth2@2.5.2",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-8qjf+nHRdSUllFjjfpnonrU1oF/HNVbDle5HIbvXRYiy38C7KUvYe6w0ZZ//g4AFB6VNWuiZ80HmnycR8ZFDyQ==",
|
||||
"_location": "/simple-oauth2",
|
||||
"_phantomChildren": {
|
||||
"ms": "2.1.1"
|
||||
},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "simple-oauth2@2.5.2",
|
||||
"name": "simple-oauth2",
|
||||
"escapedName": "simple-oauth2",
|
||||
"rawSpec": "2.5.2",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "2.5.2"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/simple-oauth2/-/simple-oauth2-2.5.2.tgz",
|
||||
"_shasum": "337bcd4b44c76e976caa77a1c7b00612b2500aeb",
|
||||
"_spec": "simple-oauth2@2.5.2",
|
||||
"_where": "C:\\Daten\\Git\\Tumortisch",
|
||||
"author": {
|
||||
"name": "Andrea Reginato",
|
||||
"email": "andrea.reginato@gmail.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/lelylan/simple-oauth2/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Jonathan Samines",
|
||||
"email": "jn.samines@gmail.com"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@hapi/joi": "^15.1.1",
|
||||
"date-fns": "^2.2.1",
|
||||
"debug": "^4.1.1",
|
||||
"wreck": "^14.0.2"
|
||||
},
|
||||
"deprecated": "simple-oauth2 v2 is no longer supported. Please upgrade to v3 for further support",
|
||||
"description": "Node.js client for OAuth2",
|
||||
"devDependencies": {
|
||||
"ava": "^2.4.0",
|
||||
"chance": "^1.0.18",
|
||||
"chance-access-token": "^1.0.1",
|
||||
"doctoc": "^1.4.0",
|
||||
"eslint": "^6.4.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-plugin-import": "^2.9.0",
|
||||
"lodash": "^4.17.15",
|
||||
"nock": "^11.3.5",
|
||||
"nyc": "^14.1.1"
|
||||
},
|
||||
"engine": {
|
||||
"node": ">=8.0"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"homepage": "https://github.com/lelylan/simple-oauth2",
|
||||
"keywords": [
|
||||
"oauth2",
|
||||
"authorization",
|
||||
"password",
|
||||
"client",
|
||||
"credentials"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"main": "index.js",
|
||||
"name": "simple-oauth2",
|
||||
"nyc": {
|
||||
"check-coverage": true,
|
||||
"lines": 100,
|
||||
"statements": 100,
|
||||
"functions": 100,
|
||||
"branches": 100,
|
||||
"reporter": [
|
||||
"lcov",
|
||||
"text-summary"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lelylan/simple-oauth2.git"
|
||||
},
|
||||
"scripts": {
|
||||
"clean-install": "rm -rf node_modules && npm install",
|
||||
"docs-gen": "doctoc README.md --github --no-title",
|
||||
"lint": "eslint .",
|
||||
"pretest": "npm run lint",
|
||||
"test": "nyc ava"
|
||||
},
|
||||
"version": "2.5.2"
|
||||
}
|
||||
Reference in New Issue
Block a user