I_Jemin

Validate Input

Showing 49 changed files with 2987 additions and 11 deletions
const Joi = require('joi');
const express = require('express');
const app = express();
app.use(express.json());
const courses = [
{ id: 1, name: 'course1'},
{ id: 2, name: 'course2'},
{ id: 3, name: 'course3'},
{ id: 1, name: 'course1' },
{ id: 2, name: 'course2' },
{ id: 3, name: 'course3' },
];
// Corespond to HTTP
......@@ -15,21 +16,34 @@ const courses = [
// app.put();
// app.delete()
app.get('/',(req,res)=> {
app.get('/', (req, res) => {
res.send('Hello World!!!');
});
app.get('/api/courses',(req,res) => {
app.get('/api/courses', (req, res) => {
res.send(courses);
});
app.get('/api/courses/:id',(req,res) => {
const course = courses.find(c=> c.id === parseInt(req.params.id));
if(!course) res.status(404).send('The course with the given ID was not found');
app.get('/api/courses/:id', (req, res) => {
const course = courses.find(c => c.id === parseInt(req.params.id));
if (!course) res.status(404).send('The course with the given ID was not found');
res.send(course);
});
app.post('/api/courses',(req,res)=> {
app.post('/api/courses', (req, res) => {
// Define Schema
const schema = {
name: Joi.string().min(3).required()
};
const result = Joi.validate(req.body, schema);
if (result.error) {
// 400 Bad Request
res.status(400).send(result.error.details[0].message);
return;
}
const course = {
id: courses.length + 1,
name: req.body.name
......@@ -42,4 +56,4 @@ app.post('/api/courses',(req,res)=> {
// PORT
const port = process.env.PORT || 3000;
app.listen(port,()=> console.log(`Listening on port ${port}...`));
app.listen(port, () => console.log(`Listening on port ${port}...`));
......
Copyright (c) 2011-2017, 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
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * *
The complete list of contributors can be found at: https://github.com/hapijs/hapi/graphs/contributors
Portions of this project were initially based on the Yahoo! Inc. Postmile project,
published at https://github.com/yahoo/postmile.
![hoek Logo](https://raw.github.com/hapijs/hoek/master/images/hoek.png)
Utility methods for the hapi ecosystem. 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) or [underscore](https://github.com/jashkenas/underscore).
[![Build Status](https://secure.travis-ci.org/hapijs/hoek.svg)](http://travis-ci.org/hapijs/hoek)
<a href="https://andyet.com"><img src="https://s3.amazonaws.com/static.andyet.com/images/%26yet-logo.svg" align="right" /></a>
Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf)
**hoek** is sponsored by [&yet](https://andyet.com)
## Usage
The *Hoek* library contains some common functions used within the hapi ecosystem. It comes with useful methods for Arrays (clone, merge, applyToDefaults), Objects (removeKeys, copy), Asserting and more.
For example, to use Hoek to set configuration with default options:
```javascript
const Hoek = require('hoek');
const default = {url : "www.github.com", port : "8000", debug : true};
const config = Hoek.applyToDefaults(default, {port : "3000", admin : true});
// In this case, config would be { url: 'www.github.com', port: '3000', debug: true, admin: true }
```
## Documentation
[**API Reference**](API.md)
'use strict';
// Declare internals
const internals = {};
exports.escapeJavaScript = 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.escapeJavaScriptChar(charCode);
}
}
return escaped;
};
exports.escapeHtml = 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;
};
exports.escapeJson = 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';
}
else if (charCode === greaterThan) {
return '\\u003e';
}
else if (charCode === andSymbol) {
return '\\u0026';
}
else if (charCode === lineSeperator) {
return '\\u2028';
}
return '\\u2029';
});
};
internals.escapeJavaScriptChar = function (charCode) {
if (charCode >= 256) {
return '\\u' + internals.padLeft('' + charCode, 4);
}
const hexValue = Buffer.from(String.fromCharCode(charCode), 'ascii').toString('hex');
return '\\x' + internals.padLeft(hexValue, 2);
};
internals.escapeHtmlChar = function (charCode) {
const namedEscape = internals.namedHtml[charCode];
if (typeof namedEscape !== 'undefined') {
return namedEscape;
}
if (charCode >= 256) {
return '&#' + charCode + ';';
}
const hexValue = Buffer.from(String.fromCharCode(charCode), 'ascii').toString('hex');
return '&#x' + internals.padLeft(hexValue, 2) + ';';
};
internals.padLeft = function (str, len) {
while (str.length < len) {
str = '0' + str;
}
return str;
};
internals.isSafe = function (charCode) {
return (typeof internals.safeCharCodes[charCode] !== 'undefined');
};
internals.namedHtml = {
'38': '&amp;',
'60': '&lt;',
'62': '&gt;',
'34': '&quot;',
'160': '&nbsp;',
'162': '&cent;',
'163': '&pound;',
'164': '&curren;',
'169': '&copy;',
'174': '&reg;'
};
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;
}());
This diff is collapsed. Click to expand it.
{
"_from": "hoek@5.x.x",
"_id": "hoek@5.0.3",
"_inBundle": false,
"_integrity": "sha512-Bmr56pxML1c9kU+NS51SMFkiVQAb+9uFfXwyqR2tn4w2FPvmPt65eZ9aCcEfRXd9G74HkZnILC6p967pED4aiw==",
"_location": "/hoek",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "hoek@5.x.x",
"name": "hoek",
"escapedName": "hoek",
"rawSpec": "5.x.x",
"saveSpec": null,
"fetchSpec": "5.x.x"
},
"_requiredBy": [
"/joi",
"/topo"
],
"_resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.3.tgz",
"_shasum": "b71d40d943d0a95da01956b547f83c4a5b4a34ac",
"_spec": "hoek@5.x.x",
"_where": "/Users/jeminlee/git-projects/node-practice/express-demo/node_modules/joi",
"bugs": {
"url": "https://github.com/hapijs/hoek/issues"
},
"bundleDependencies": false,
"dependencies": {},
"deprecated": false,
"description": "General purpose node utilities",
"devDependencies": {
"code": "5.x.x",
"lab": "15.x.x"
},
"engines": {
"node": ">=8.9.0"
},
"homepage": "https://github.com/hapijs/hoek#readme",
"keywords": [
"utilities"
],
"license": "BSD-3-Clause",
"main": "lib/index.js",
"name": "hoek",
"repository": {
"type": "git",
"url": "git://github.com/hapijs/hoek.git"
},
"scripts": {
"test": "lab -a code -t 100 -L",
"test-cov-html": "lab -a code -t 100 -L -r html -o coverage.html"
},
"version": "5.0.3"
}
Copyright (c) 2014-2015, Eli Skeggs and Project contributors
Copyright (c) 2013-2014, GlobeSherpa
Copyright (c) 2008-2011, Dominic Sayers
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.
* * *
The complete list of contributors can be found at: https://github.com/hapijs/isemail/graphs/contributors
Previously published under the 2-Clause-BSD license published here: https://github.com/hapijs/isemail/blob/v1.2.0/LICENSE
# isemail
Node email address validation library
[![Build Status](https://travis-ci.org/hapijs/isemail.svg?branch=master)](https://travis-ci.org/hapijs/isemail)<a href="#footnote-1"><sup>&#91;1&#93;</sup></a>
Lead Maintainer: [Eli Skeggs][skeggse]
This library is a port of the PHP `is_email` function by Dominic Sayers.
Install
=======
```sh
$ npm install isemail
```
Test
====
The tests were pulled from `is_email`'s extensive [test suite][tests] on October 15, 2013. Many thanks to the contributors! Additional tests have been added to increase code coverage and verify edge-cases.
Run any of the following.
```sh
$ lab
$ npm test
$ make test
```
_remember to_ `npm install` to get the development dependencies!
API
===
validate(email, [options])
--------------------------
Determines whether the `email` is valid or not, for various definitions thereof. Optionally accepts an `options` object. Options may include `errorLevel`.
Use `errorLevel` to specify the type of result for `validate()`. Passing a `false` literal will result in a true or false boolean indicating whether the email address is sufficiently defined for use in sending an email. Passing a `true` literal will result in a more granular numeric status, with zero being a perfectly valid email address. Passing a number will return `0` if the numeric status is below the `errorLevel` and the numeric status otherwise.
The `tldBlacklist` option can be either an object lookup table or an array of invalid top-level domains. If the email address has a top-level domain that is in the whitelist, the email will be marked as invalid.
The `tldWhitelist` option can be either an object lookup table or an array of valid top-level domains. If the email address has a top-level domain that is not in the whitelist, the email will be marked as invalid.
The `allowUnicode` option governs whether non-ASCII characters are allowed. Defaults to `true` per RFC 6530.
Only one of `tldBlacklist` and `tldWhitelist` will be consulted for TLD validity.
The `minDomainAtoms` option is an optional positive integer that specifies the minimum number of domain atoms that must be included for the email address to be considered valid. Be careful with the option, as some top-level domains, like `io`, directly support email addresses.
As of `3.1.1`, the `callback` parameter is deprecated, and will be removed in `4.0.0`.
### Examples
```js
$ node
> var Isemail = require('isemail');
undefined
> Isemail.validate('test@iana.org');
true
> Isemail.validate('test@iana.org', {errorLevel: true});
0
> Isemail.validate('test@e.com', {errorLevel: true});
6
> Isemail.validate('test@e.com', {errorLevel: 7});
0
> Isemail.validate('test@e.com', {errorLevel: 6});
6
```
<sup name="footnote-1">&#91;1&#93;</sup>: if this badge indicates the build is passing, then isemail has 100% code coverage.
[skeggse]: https://github.com/skeggse "Eli Skeggs"
[tests]: http://isemail.info/_system/is_email/test/?all‎ "is_email test suite"
// Code shows a string is valid,
// but docs require string array or object.
type TLDList = string | string[] | { [topLevelDomain: string]: any };
type BaseOptions = {
tldWhitelist?: TLDList;
tldBlacklist?: TLDList;
minDomainAtoms?: number;
allowUnicode?: boolean;
};
type OptionsWithBool = BaseOptions & {
errorLevel?: false;
};
type OptionsWithNumThreshold = BaseOptions & {
errorLevel?: true | number;
};
interface Validator {
/**
* Check that an email address conforms to RFCs 5321, 5322, 6530 and others.
*
* The callback function will always be called
* with the result of the operation.
*
* ```
* import * as IsEmail from "isemail";
*
* const log = result => console.log(`Result: ${result}`);
* IsEmail.validate("test@e.com");
* // => true
* ```
*/
validate(email: string): boolean;
/**
* Check that an email address conforms to RFCs 5321, 5322, 6530 and others.
*
* The callback function will always be called
* with the result of the operation.
*
* ```
* import * as IsEmail from "isemail";
*
* IsEmail.validate("test@iana.org", { errorLevel: false });
* // => true
* ```
*/
validate(email: string, options: OptionsWithBool): boolean;
/**
* Check that an email address conforms to RFCs 5321, 5322, 6530 and others.
*
* The callback function will always be called
* with the result of the operation.
*
* ```
* import * as IsEmail from "isemail";
*
* IsEmail.validate("test@iana.org", { errorLevel: true });
* // => 0
* IsEmail.validate("test @e.com", { errorLevel: 50 });
* // => 0
* IsEmail.validate('test @e.com', { errorLevel: true })
* // => 49
* ```
*/
validate(email: string, options: OptionsWithNumThreshold): number;
}
declare const IsEmail: Validator;
export = IsEmail;
This diff is collapsed. Click to expand it.
{
"_from": "isemail@3.x.x",
"_id": "isemail@3.1.1",
"_inBundle": false,
"_integrity": "sha512-mVjAjvdPkpwXW61agT2E9AkGoegZO7SdJGCezWwxnETL58f5KwJ4vSVAMBUL5idL6rTlYAIGkX3n4suiviMLNw==",
"_location": "/isemail",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "isemail@3.x.x",
"name": "isemail",
"escapedName": "isemail",
"rawSpec": "3.x.x",
"saveSpec": null,
"fetchSpec": "3.x.x"
},
"_requiredBy": [
"/joi"
],
"_resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.1.tgz",
"_shasum": "e8450fe78ff1b48347db599122adcd0668bd92b5",
"_spec": "isemail@3.x.x",
"_where": "/Users/jeminlee/git-projects/node-practice/express-demo/node_modules/joi",
"bugs": {
"url": "https://github.com/hapijs/isemail/issues"
},
"bundleDependencies": false,
"dependencies": {
"punycode": "2.x.x"
},
"deprecated": false,
"description": "Validate an email address according to RFCs 5321, 5322, and others",
"devDependencies": {
"code": "3.x.x",
"lab": "15.x.x"
},
"engines": {
"node": ">=4.0.0"
},
"homepage": "https://github.com/hapijs/isemail#readme",
"keywords": [
"isemail",
"validation",
"check",
"checking",
"verification",
"email",
"address",
"email address"
],
"license": "BSD-3-Clause",
"main": "lib/index.js",
"name": "isemail",
"repository": {
"type": "git",
"url": "git://github.com/hapijs/isemail.git"
},
"scripts": {
"test": "lab -a code -t 100 -L -m 5000",
"test-cov-html": "lab -a code -r html -o coverage.html -m 5000"
},
"types": "lib/index.d.ts",
"version": "3.1.1"
}
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).
Copyright (c) 2012-2017, 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
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * *
The complete list of contributors can be found at: https://github.com/hapijs/joi/graphs/contributors
![joi Logo](https://raw.github.com/hapijs/joi/master/images/joi.png)
Object schema description language and validator for JavaScript objects.
[![npm version](https://badge.fury.io/js/joi.svg)](http://badge.fury.io/js/joi)
[![Build Status](https://travis-ci.org/hapijs/joi.svg?branch=master)](https://travis-ci.org/hapijs/joi)
[![NSP Status](https://nodesecurity.io/orgs/hapijs/projects/0394bf83-b5bc-410b-878c-e8cf1b92033e/badge)](https://nodesecurity.io/orgs/hapijs/projects/0394bf83-b5bc-410b-878c-e8cf1b92033e)
[![Known Vulnerabilities](https://snyk.io/test/github/hapijs/joi/badge.svg)](https://snyk.io/test/github/hapijs/joi)
Lead Maintainer: [Nicolas Morel](https://github.com/marsup)
# 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.
# API
See the detailed [API Reference](https://github.com/hapijs/joi/blob/v13.1.2/API.md).
# Example
```javascript
const Joi = require('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()
}).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
# 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.
Then the value is validated against the schema:
```javascript
const {error, value} = Joi.validate({ a: 'a string' }, schema);
// or
Joi.validate({ a: 'a string' }, schema, function (err, value) { });
```
If the input is valid, then the error will be `null`, otherwise it will be an Error object.
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 after whitelist and blacklist checks.
# 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.
'use strict';
// Load modules
const Hoek = require('hoek');
const Ref = require('./ref');
// Type modules are delay-loaded to prevent circular dependencies
// Declare internals
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);
};
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
'use strict';
// Load modules
// Declare internals
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',
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',
format: 'must be a string with one of the following formats {{format}}',
strict: 'must be a valid date',
min: 'must be larger than or equal to "{{limit}}"',
max: 'must be less than or equal to "{{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}}',
or: 'must contain at least one of {{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',
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}}',
float: 'must be a float or double',
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}}'
},
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',
base64: 'must be a valid base64 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'
}
};
'use strict';
// Load modules
const Hoek = require('hoek');
// Declare internals
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);
}
};
'use strict';
// Load modules
const Joi = require('../');
// Declare internals
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'),
raw: Joi.boolean(),
context: Joi.object(),
strip: Joi.boolean(),
noDefaults: Joi.boolean(),
escapeHtml: Joi.boolean()
}).strict();
'use strict';
const Ref = require('./ref');
module.exports = class Set {
constructor() {
this._set = [];
}
add(value, refs) {
if (!Ref.isRef(value) && this.has(value, null, null, false)) {
return;
}
if (refs !== undefined) { // If it's a merge, we don't have any refs
Ref.push(refs, value);
}
this._set.push(value);
return this;
}
merge(add, remove) {
for (let i = 0; i < add._set.length; ++i) {
this.add(add._set[i]);
}
for (let i = 0; i < remove._set.length; ++i) {
this.remove(remove._set[i]);
}
return this;
}
remove(value) {
this._set = this._set.filter((item) => value !== item);
return this;
}
has(value, state, options, insensitive) {
for (let i = 0; i < this._set.length; ++i) {
let items = this._set[i];
if (state && Ref.isRef(items)) { // Only resolve references if there is a state, otherwise it's a merge
items = items(state.reference || state.parent, options);
}
if (!Array.isArray(items)) {
items = [items];
}
for (let j = 0; j < items.length; ++j) {
const item = items[j];
if (typeof value !== typeof item) {
continue;
}
if (value === item ||
(value instanceof Date && item instanceof Date && value.getTime() === item.getTime()) ||
(insensitive && typeof value === 'string' && value.toLowerCase() === item.toLowerCase()) ||
(Buffer.isBuffer(value) && Buffer.isBuffer(item) && value.length === item.length && value.toString('binary') === item.toString('binary'))) {
return true;
}
}
}
return false;
}
values(options) {
if (options && options.stripUndefined) {
const values = [];
for (let i = 0; i < this._set.length; ++i) {
const item = this._set[i];
if (item !== undefined) {
values.push(item);
}
}
return values;
}
return this._set.slice();
}
slice() {
const newSet = new Set();
newSet._set = this._set.slice();
return newSet;
}
concat(source) {
const newSet = new Set();
newSet._set = this._set.concat(source._set);
return newSet;
}
};
'use strict';
// Load modules
const Hoek = require('hoek');
const Any = require('../any');
const Cast = require('../../cast');
const Ref = require('../../ref');
// Declare internals
const internals = {};
internals.Alternatives = class extends Any {
constructor() {
super();
this._type = 'alternatives';
this._invalids.remove(null);
this._inner.matches = [];
}
_base(value, state, options) {
let 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 = errors.concat(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 = obj._refs.concat(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 = obj._refs.concat(item.is._refs);
}
if (item.then && item.then._refs) {
obj._refs = obj._refs.concat(item.then._refs);
}
if (item.otherwise && item.otherwise._refs) {
obj._refs = obj._refs.concat(item.otherwise._refs);
}
obj._inner.matches.push(item);
return obj;
}
describe() {
const description = Any.prototype.describe.call(this);
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();
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
'use strict';
// Load modules
const Any = require('../any');
const Hoek = require('hoek');
// Declare internals
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 = new Buffer(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();
'use strict';
// Load modules
const Any = require('../any');
const Hoek = require('hoek');
// Declare internals
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', null, 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 = Any.prototype.describe.call(this);
description.truthy = [true].concat(this._inner.truthySet.values());
description.falsy = [false].concat(this._inner.falsySet.values());
return description;
}
};
module.exports = new internals.Boolean();
'use strict';
// Load modules
const Any = require('../any');
const Ref = require('../../ref');
const Hoek = require('hoek');
// Declare internals
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', null, 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}`, null, 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))) {
if (typeof value === 'string' &&
/^[+-]?\d+(\.\d+)?$/.test(value)) {
value = parseFloat(value);
}
let date;
if (format && internals.isIsoDate(format)) {
date = format.test(value) ? new Date(value) : internals.invalidDate;
}
else if (timestamp && multiplier) {
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) {
compareTo = internals.Date.toDate(date(state.reference || state.parent, options));
if (!compareTo) {
return this.createError('date.ref', { ref: date.key }, 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) }, state, options);
});
};
};
internals.Date.prototype.min = internals.compare('min', (value, date) => value >= date);
internals.Date.prototype.max = internals.compare('max', (value, date) => value <= date);
module.exports = new internals.Date();
'use strict';
// Load modules
const Hoek = require('hoek');
const ObjectType = require('../object');
const Ref = require('../../ref');
// Declare internals
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', null, 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', null, state, options);
});
}
};
module.exports = new internals.Func();
'use strict';
// Load modules
const Any = require('../any');
const Hoek = require('hoek');
// Declare internals
const internals = {};
internals.Lazy = class extends Any {
constructor() {
super();
this._type = 'lazy';
}
_base(value, state, options) {
const result = { value };
const lazy = this._flags.lazy;
if (!lazy) {
result.errors = this.createError('lazy.base', null, state, options);
return result;
}
const schema = lazy();
if (!(schema instanceof Any)) {
result.errors = this.createError('lazy.schema', null, state, options);
return result;
}
return schema._validate(value, state, options);
}
set(fn) {
Hoek.assert(typeof fn === 'function', 'You must provide a function as first argument');
const obj = this.clone();
obj._flags.lazy = fn;
return obj;
}
};
module.exports = new internals.Lazy();
'use strict';
// Load modules
const Any = require('../any');
const Ref = require('../../ref');
const Hoek = require('hoek');
// Declare internals
const internals = {
precisionRx: /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/
};
internals.Number = class extends Any {
constructor() {
super();
this._type = 'number';
this._invalids.add(Infinity);
this._invalids.add(-Infinity);
}
_base(value, state, options) {
const result = {
errors: null,
value
};
if (typeof value === 'string' &&
options.convert) {
const number = parseFloat(value);
result.value = (isNaN(number) || !isFinite(value)) ? NaN : number;
}
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;
}
result.errors = isNumber ? null : this.createError('number.base', null, 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 Number.isSafeInteger(value) ? value : this.createError('number.integer', { value }, state, options);
});
}
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;
}
};
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();
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
'use strict';
// Load modules
const RFC3986 = require('./rfc3986');
// Declare internals
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;
'use strict';
// Load modules
// Delcare internals
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 + ']';
/**
* 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.
/**
* fragment = *( pchar / "/" / "?" )
*/
internals.rfc3986.fragment = '[' + pchar + '\\/\\?]*';
};
internals.generate();
module.exports = internals.rfc3986;
'use strict';
// Load Modules
const RFC3986 = require('./rfc3986');
// Declare internals
const internals = {
Uri: {
createUriRegex: function (optionalScheme, allowRelative, relativeOnly) {
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 + '(?:\\?' + RFC3986.query + ')?' + '(?:#' + RFC3986.fragment + ')?$');
}
}
};
module.exports = internals.Uri;
{
"_from": "joi",
"_id": "joi@13.1.2",
"_inBundle": false,
"_integrity": "sha512-bZZSQYW5lPXenOfENvgCBPb9+H6E6MeNWcMtikI04fKphj5tvFL9TOb+H2apJzbCrRw/jebjTH8z6IHLpBytGg==",
"_location": "/joi",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "joi",
"name": "joi",
"escapedName": "joi",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/joi/-/joi-13.1.2.tgz",
"_shasum": "b2db260323cc7f919fafa51e09e2275bd089a97e",
"_spec": "joi",
"_where": "/Users/jeminlee/git-projects/node-practice/express-demo",
"bugs": {
"url": "https://github.com/hapijs/joi/issues"
},
"bundleDependencies": false,
"dependencies": {
"hoek": "5.x.x",
"isemail": "3.x.x",
"topo": "3.x.x"
},
"deprecated": false,
"description": "Object schema validation",
"devDependencies": {
"code": "5.x.x",
"hapitoc": "1.x.x",
"lab": "15.x.x"
},
"engines": {
"node": ">=8.9.0"
},
"homepage": "https://github.com/hapijs/joi",
"keywords": [
"hapi",
"schema",
"validation"
],
"license": "BSD-3-Clause",
"main": "lib/index.js",
"name": "joi",
"repository": {
"type": "git",
"url": "git://github.com/hapijs/joi.git"
},
"scripts": {
"test": "lab -t 100 -a code -L",
"test-cov-html": "lab -r html -o coverage.html -a code",
"test-debug": "lab -a code",
"toc": "hapitoc",
"version": "npm run toc && git add API.md README.md"
},
"version": "13.1.2"
}
Copyright Mathias Bynens <https://mathiasbynens.be/>
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.
# Punycode.js [![Build status](https://travis-ci.org/bestiejs/punycode.js.svg?branch=master)](https://travis-ci.org/bestiejs/punycode.js) [![Code coverage status](http://img.shields.io/codecov/c/github/bestiejs/punycode.js.svg)](https://codecov.io/gh/bestiejs/punycode.js) [![Dependency status](https://gemnasium.com/bestiejs/punycode.js.svg)](https://gemnasium.com/bestiejs/punycode.js)
Punycode.js is a robust Punycode converter that fully complies to [RFC 3492](https://tools.ietf.org/html/rfc3492) and [RFC 5891](https://tools.ietf.org/html/rfc5891).
This JavaScript library is the result of comparing, optimizing and documenting different open-source implementations of the Punycode algorithm:
* [The C example code from RFC 3492](https://tools.ietf.org/html/rfc3492#appendix-C)
* [`punycode.c` by _Markus W. Scherer_ (IBM)](http://opensource.apple.com/source/ICU/ICU-400.42/icuSources/common/punycode.c)
* [`punycode.c` by _Ben Noordhuis_](https://github.com/bnoordhuis/punycode/blob/master/punycode.c)
* [JavaScript implementation by _some_](http://stackoverflow.com/questions/183485/can-anyone-recommend-a-good-free-javascript-for-punycode-to-unicode-conversion/301287#301287)
* [`punycode.js` by _Ben Noordhuis_](https://github.com/joyent/node/blob/426298c8c1c0d5b5224ac3658c41e7c2a3fe9377/lib/punycode.js) (note: [not fully compliant](https://github.com/joyent/node/issues/2072))
This project was [bundled](https://github.com/joyent/node/blob/master/lib/punycode.js) with Node.js from [v0.6.2+](https://github.com/joyent/node/compare/975f1930b1...61e796decc) until [v7](https://github.com/nodejs/node/pull/7941) (soft-deprecated).
The current version supports recent versions of Node.js only. It provides a CommonJS module and an ES6 module. For the old version that offers the same functionality with broader support, including Rhino, Ringo, Narwhal, and web browsers, see [v1.4.1](https://github.com/bestiejs/punycode.js/releases/tag/v1.4.1).
## Installation
Via [npm](https://www.npmjs.com/):
```bash
npm install punycode --save
```
In [Node.js](https://nodejs.org/):
```js
const punycode = require('punycode');
```
## API
### `punycode.decode(string)`
Converts a Punycode string of ASCII symbols to a string of Unicode symbols.
```js
// decode domain name parts
punycode.decode('maana-pta'); // 'mañana'
punycode.decode('--dqo34k'); // '☃-⌘'
```
### `punycode.encode(string)`
Converts a string of Unicode symbols to a Punycode string of ASCII symbols.
```js
// encode domain name parts
punycode.encode('mañana'); // 'maana-pta'
punycode.encode('☃-⌘'); // '--dqo34k'
```
### `punycode.toUnicode(input)`
Converts a Punycode string representing a domain name or an email address to Unicode. Only the Punycoded parts of the input will be converted, i.e. it doesn’t matter if you call it on a string that has already been converted to Unicode.
```js
// decode domain names
punycode.toUnicode('xn--maana-pta.com');
// → 'mañana.com'
punycode.toUnicode('xn----dqo34k.com');
// → '☃-⌘.com'
// decode email addresses
punycode.toUnicode('джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq');
// → 'джумла@джpумлатест.bрфa'
```
### `punycode.toASCII(input)`
Converts a lowercased Unicode string representing a domain name or an email address to Punycode. Only the non-ASCII parts of the input will be converted, i.e. it doesn’t matter if you call it with a domain that’s already in ASCII.
```js
// encode domain names
punycode.toASCII('mañana.com');
// → 'xn--maana-pta.com'
punycode.toASCII('☃-⌘.com');
// → 'xn----dqo34k.com'
// encode email addresses
punycode.toASCII('джумла@джpумлатест.bрфa');
// → 'джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq'
```
### `punycode.ucs2`
#### `punycode.ucs2.decode(string)`
Creates an array containing the numeric code point values of each Unicode symbol in the string. While [JavaScript uses UCS-2 internally](https://mathiasbynens.be/notes/javascript-encoding), this function will convert a pair of surrogate halves (each of which UCS-2 exposes as separate characters) into a single code point, matching UTF-16.
```js
punycode.ucs2.decode('abc');
// → [0x61, 0x62, 0x63]
// surrogate pair for U+1D306 TETRAGRAM FOR CENTRE:
punycode.ucs2.decode('\uD834\uDF06');
// → [0x1D306]
```
#### `punycode.ucs2.encode(codePoints)`
Creates a string based on an array of numeric code point values.
```js
punycode.ucs2.encode([0x61, 0x62, 0x63]);
// → 'abc'
punycode.ucs2.encode([0x1D306]);
// → '\uD834\uDF06'
```
### `punycode.version`
A string representing the current Punycode.js version number.
## Author
| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") |
|---|
| [Mathias Bynens](https://mathiasbynens.be/) |
## License
Punycode.js is available under the [MIT](https://mths.be/mit) license.
{
"_from": "punycode@2.x.x",
"_id": "punycode@2.1.0",
"_inBundle": false,
"_integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=",
"_location": "/punycode",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "punycode@2.x.x",
"name": "punycode",
"escapedName": "punycode",
"rawSpec": "2.x.x",
"saveSpec": null,
"fetchSpec": "2.x.x"
},
"_requiredBy": [
"/isemail"
],
"_resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz",
"_shasum": "5f863edc89b96db09074bad7947bf09056ca4e7d",
"_spec": "punycode@2.x.x",
"_where": "/Users/jeminlee/git-projects/node-practice/express-demo/node_modules/isemail",
"author": {
"name": "Mathias Bynens",
"url": "https://mathiasbynens.be/"
},
"bugs": {
"url": "https://github.com/bestiejs/punycode.js/issues"
},
"bundleDependencies": false,
"contributors": [
{
"name": "Mathias Bynens",
"url": "https://mathiasbynens.be/"
}
],
"deprecated": false,
"description": "A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.",
"devDependencies": {
"codecov": "^1.0.1",
"istanbul": "^0.4.1",
"mocha": "^2.5.3"
},
"engines": {
"node": ">=6"
},
"files": [
"LICENSE-MIT.txt",
"punycode.js",
"punycode.es6.js"
],
"homepage": "https://mths.be/punycode",
"jsnext:main": "punycode.es6.js",
"jspm": {
"map": {
"./punycode.js": {
"node": "@node/punycode"
}
}
},
"keywords": [
"punycode",
"unicode",
"idn",
"idna",
"dns",
"url",
"domain"
],
"license": "MIT",
"main": "punycode.js",
"name": "punycode",
"repository": {
"type": "git",
"url": "git+https://github.com/bestiejs/punycode.js.git"
},
"scripts": {
"prepublish": "node scripts/prepublish.js",
"test": "mocha tests"
},
"version": "2.1.0"
}
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Copyright (c) 2012-2016, 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
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * *
The complete list of contributors can be found at: https://github.com/hapijs/topo/graphs/contributors
# topo
Topological sorting with grouping support.
[![Build Status](https://secure.travis-ci.org/hapijs/topo.svg?branch=master)](http://travis-ci.org/hapijs/topo)
Lead Maintainer: [Devin Ivy](https://github.com/devinivy)
## Usage
See the [API Reference](API.md)
**Example**
```node
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']
```
'use strict';
// Load modules
const Hoek = require('hoek');
// Declare internals
const internals = {};
exports = module.exports = internals.Topo = function () {
this._items = [];
this.nodes = [];
};
internals.Topo.prototype.add = function (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
Hoek.assert(before.indexOf(group) === -1, 'Item cannot come before itself:', group);
Hoek.assert(before.indexOf('?') === -1, 'Item cannot come before unassociated items');
Hoek.assert(after.indexOf(group) === -1, 'Item cannot come after itself:', group);
Hoek.assert(after.indexOf('?') === -1, 'Item cannot come after unassociated items');
([].concat(nodes)).forEach((node, i) => {
const item = {
seq: this._items.length,
sort,
before,
after,
group,
node
};
this._items.push(item);
});
// Insert event
const error = this._sort();
Hoek.assert(!error, 'item', (group !== '?' ? 'added into group ' + group : ''), 'created a dependencies error');
return this.nodes;
};
internals.Topo.prototype.merge = function (others) {
others = [].concat(others);
for (let i = 0; i < others.length; ++i) {
const other = others[i];
if (other) {
for (let j = 0; j < other._items.length; ++j) {
const item = Hoek.shallow(other._items[j]);
this._items.push(item);
}
}
}
// Sort items
this._items.sort(internals.mergeSort);
for (let i = 0; i < this._items.length; ++i) {
this._items[i].seq = i;
}
const error = this._sort();
Hoek.assert(!error, 'merge created a dependencies error');
return this.nodes;
};
internals.mergeSort = function (a, b) {
return a.sort === b.sort ? 0 : (a.sort < b.sort ? -1 : 1);
};
internals.Topo.prototype._sort = function () {
// Construct graph
const graph = {};
const graphAfters = Object.create(null); // A prototype can bungle lookups w/ false positives
const groups = Object.create(null);
for (let i = 0; i < this._items.length; ++i) {
const item = this._items[i];
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'
const after = item.after;
for (let j = 0; j < after.length; ++j) {
graphAfters[after[j]] = (graphAfters[after[j]] || []).concat(seq);
}
}
// Expand intermediary graph
let graphNodes = Object.keys(graph);
for (let i = 0; i < graphNodes.length; ++i) {
const node = graphNodes[i];
const expandedGroups = [];
const graphNodeItems = Object.keys(graph[node]);
for (let j = 0; j < graphNodeItems.length; ++j) {
const group = graph[node][graphNodeItems[j]];
groups[group] = groups[group] || [];
for (let k = 0; k < groups[group].length; ++k) {
expandedGroups.push(groups[group][k]);
}
}
graph[node] = expandedGroups;
}
// Merge intermediary graph using graphAfters into final graph
const afterNodes = Object.keys(graphAfters);
for (let i = 0; i < afterNodes.length; ++i) {
const group = afterNodes[i];
if (groups[group]) {
for (let j = 0; j < groups[group].length; ++j) {
const node = groups[group][j];
graph[node] = graph[node].concat(graphAfters[group]);
}
}
}
// Compile ancestors
let children;
const ancestors = {};
graphNodes = Object.keys(graph);
for (let i = 0; i < graphNodes.length; ++i) {
const node = graphNodes[i];
children = graph[node];
for (let j = 0; j < children.length; ++j) {
ancestors[children[j]] = (ancestors[children[j]] || []).concat(node);
}
}
// Topo sort
const visited = {};
const sorted = [];
for (let i = 0; i < this._items.length; ++i) { // Really looping thru 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 new Error('Invalid dependencies');
}
const seqIndex = {};
for (let i = 0; i < this._items.length; ++i) {
const item = this._items[i];
seqIndex[item.seq] = item;
}
const sortedNodes = [];
this._items = sorted.map((value) => {
const sortedItem = seqIndex[value];
sortedNodes.push(sortedItem.node);
return sortedItem;
});
this.nodes = sortedNodes;
};
{
"_from": "topo@3.x.x",
"_id": "topo@3.0.0",
"_inBundle": false,
"_integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==",
"_location": "/topo",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "topo@3.x.x",
"name": "topo",
"escapedName": "topo",
"rawSpec": "3.x.x",
"saveSpec": null,
"fetchSpec": "3.x.x"
},
"_requiredBy": [
"/joi"
],
"_resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz",
"_shasum": "37e48c330efeac784538e0acd3e62ca5e231fe7a",
"_spec": "topo@3.x.x",
"_where": "/Users/jeminlee/git-projects/node-practice/express-demo/node_modules/joi",
"bugs": {
"url": "https://github.com/hapijs/topo/issues"
},
"bundleDependencies": false,
"dependencies": {
"hoek": "5.x.x"
},
"deprecated": false,
"description": "Topological sorting with grouping support",
"devDependencies": {
"code": "5.x.x",
"lab": "14.x.x"
},
"engines": {
"node": ">=8.0.0"
},
"homepage": "https://github.com/hapijs/topo#readme",
"keywords": [
"topological",
"sort",
"toposort",
"topsort"
],
"license": "BSD-3-Clause",
"main": "lib/index.js",
"name": "topo",
"repository": {
"type": "git",
"url": "git://github.com/hapijs/topo.git"
},
"scripts": {
"test": "lab -a code -t 100 -L",
"test-cov-html": "lab -a code -t 100 -L -r html -o coverage.html"
},
"version": "3.0.0"
}
......@@ -159,6 +159,11 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"hoek": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.3.tgz",
"integrity": "sha512-Bmr56pxML1c9kU+NS51SMFkiVQAb+9uFfXwyqR2tn4w2FPvmPt65eZ9aCcEfRXd9G74HkZnILC6p967pED4aiw=="
},
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
......@@ -197,6 +202,24 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
"integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
},
"isemail": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.1.tgz",
"integrity": "sha512-mVjAjvdPkpwXW61agT2E9AkGoegZO7SdJGCezWwxnETL58f5KwJ4vSVAMBUL5idL6rTlYAIGkX3n4suiviMLNw==",
"requires": {
"punycode": "2.1.0"
}
},
"joi": {
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/joi/-/joi-13.1.2.tgz",
"integrity": "sha512-bZZSQYW5lPXenOfENvgCBPb9+H6E6MeNWcMtikI04fKphj5tvFL9TOb+H2apJzbCrRw/jebjTH8z6IHLpBytGg==",
"requires": {
"hoek": "5.0.3",
"isemail": "3.1.1",
"topo": "3.0.0"
}
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
......@@ -267,6 +290,11 @@
"ipaddr.js": "1.6.0"
}
},
"punycode": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz",
"integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0="
},
"qs": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
......@@ -334,6 +362,14 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"topo": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz",
"integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==",
"requires": {
"hoek": "5.0.3"
}
},
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
......
......@@ -10,6 +10,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.3"
"express": "^4.16.3",
"joi": "^13.1.2"
}
}
......