const Joi = require('joi');
const Hoek = require('hoek');
exports.apply = function (type, options, message) {
const result = Joi.validate(options, internals[type]);
Hoek.assert(!result.error, 'Invalid', type, 'options', message ? '(' + message + ')' : '', result.error && result.error.annotate());
internals.access = Joi.object({
entity: Joi.string().valid('user', 'app', 'any'),
scope: [false, Joi.array().items(Joi.string()).single().min(1)]
internals.auth = Joi.alternatives([
mode: Joi.string().valid('required', 'optional', 'try'),
strategies: Joi.array().items(Joi.string()).min(1),
access: Joi.array().items(internals.access.min(1)).single().min(1),
Joi.string().valid('required', 'optional'),
.without('strategy', 'strategies')
.without('access', ['scope', 'entity'])
internals.event = Joi.object({
method: Joi.array().items(Joi.func()).single(),
before: Joi.array().items(Joi.string()).single(),
after: Joi.array().items(Joi.string()).single(),
sandbox: Joi.string().valid('connection', 'plugin')
internals.exts = Joi.array().items(internals.event.keys({ type: Joi.string().required() })).single();
internals.routeBase = Joi.object({
app: Joi.object().allow(null),
auth: internals.auth.allow(false),
bind: Joi.object().allow(null),
privacy: Joi.string().valid('default', 'public', 'private'),
statuses: Joi.array().items(Joi.number().integer().min(200)).min(1).single(),
otherwise: Joi.string().required()
origin: Joi.array().min(1),
headers: Joi.array().items(Joi.string()),
additionalHeaders: Joi.array().items(Joi.string()),
exposedHeaders: Joi.array().items(Joi.string()),
additionalExposedHeaders: Joi.array().items(Joi.string()),
credentials: Joi.boolean()
onPreAuth: Joi.array().items(internals.event).single(),
onPostAuth: Joi.array().items(internals.event).single(),
onPreHandler: Joi.array().items(internals.event).single(),
onPostHandler: Joi.array().items(internals.event).single(),
onPreResponse: Joi.array().items(internals.event).single()
relativeTo: Joi.string().regex(/^([\/\.])|([A-Za-z]:\\)|(\\\\)/).required()
replacer: Joi.alternatives(Joi.func(), Joi.array()).allow(null),
space: Joi.number().allow(null),
suffix: Joi.string().allow(null)
output: Joi.string().valid('data', 'stream', 'file'),
parse: Joi.boolean().allow('gunzip'),
failAction: Joi.string().valid('error', 'log', 'ignore'),
timeout: Joi.number().integer().positive().allow(false),
defaultContentType: Joi.string()
emptyStatusCode: Joi.number().valid(200, 204),
failAction: Joi.string().valid('error', 'log'),
sample: Joi.number().min(0).max(100),
schema: Joi.alternatives(Joi.object(), Joi.array(), Joi.func()).allow(true, false),
status: Joi.object().pattern(/\d\d\d/, Joi.alternatives(Joi.object(), Joi.array(), Joi.func()).allow(true, false))
.without('modify', 'sample')
.assert('options.stripUnknown', Joi.ref('modify'), 'meet requirement of having peer modify set to true'),
includeSubdomains: Joi.boolean(),
includeSubDomains: Joi.boolean(),
Joi.string().valid('sameorigin', 'deny'),
rule: Joi.string().valid('sameorigin', 'deny', 'allow-from'),
.allow(null, false, true),
failAction: Joi.string().valid('error', 'log', 'ignore')
socket: Joi.number().integer().positive().allow(false),
server: Joi.number().integer().positive().allow(false).required()
headers: Joi.alternatives(Joi.object(), Joi.array(), Joi.func()).allow(null, false, true),
params: Joi.alternatives(Joi.object(), Joi.array(), Joi.func()).allow(null, false, true),
query: Joi.alternatives(Joi.object(), Joi.array(), Joi.func()).allow(null, false, true),
payload: Joi.alternatives(Joi.object(), Joi.array(), Joi.func()).allow(null, false, true),
Joi.string().valid('error', 'log', 'ignore'),
errorFields: Joi.object(),
internals.connectionBase = Joi.object({
app: Joi.object().allow(null),
compression: Joi.boolean(),
isCaseSensitive: Joi.boolean(),
stripTrailingSlash: Joi.boolean()
routes: internals.routeBase,
internals.server = Joi.object({
app: Joi.object().allow(null),
connections: internals.connectionBase,
request: Joi.array().allow(false),
log: Joi.array().allow(false)
useDomains: Joi.boolean()
internals.connection = internals.connectionBase.keys({
autoListen: Joi.boolean(),
host: Joi.string().hostname(),
address: Joi.string().hostname(),
labels: Joi.array().items(Joi.string()).single(),
Joi.number().integer().min(0),
Joi.string().regex(/\//),
Joi.string().regex(/^\\\\\.\\pipe\\/)
Joi.object().allow(null),
uri: Joi.string().regex(/[^/]$/)
internals.vhost = Joi.alternatives([
Joi.array().items(Joi.string().hostname()).min(1)
internals.route = Joi.object({
method: Joi.string().regex(/^[a-zA-Z0-9!#\$%&'\*\+\-\.^_`\|~]+$/).required(),
path: Joi.string().required(),
config: Joi.alternatives([
method: Joi.alternatives(Joi.string(), Joi.func()).required(),
mode: Joi.string().valid('serial', 'parallel'),
failAction: Joi.string().valid('error', 'log', 'ignore')
internals.routeConfig = internals.routeBase.keys({
isInternal: Joi.boolean(),
pre: Joi.array().items(internals.pre.concat(Joi.array().items(internals.pre).min(1))),
description: Joi.string(),
Joi.array().items(Joi.string())
Joi.array().items(Joi.string())
internals.cacheConfig = Joi.object({
name: Joi.string().invalid('_default'),
engine: Joi.alternatives([
internals.cache = Joi.array().items(internals.cacheConfig, Joi.func()).min(1).single();
internals.cachePolicy = Joi.object({
cache: Joi.string().allow(null).allow(''),
.options({ allowUnknown: true });
internals.method = Joi.object({
bind: Joi.object().allow(null),
cache: internals.cachePolicy,
internals.methodObject = Joi.object({
name: Joi.string().required(),
method: Joi.func().required(),
internals.register = Joi.object({
prefix: Joi.string().regex(/^\/.+/),
select: Joi.array().items(Joi.string()).single()
internals.plugin = internals.register.keys({
register: Joi.func().keys({
version: Joi.string().default('0.0.0')
.when('pkg.name', { is: Joi.exist(), otherwise: Joi.required() }),
multiple: Joi.boolean().default(false),
dependencies: Joi.array().items(Joi.string()).single(),
connections: Joi.boolean().default(true),
once: Joi.boolean().valid(true)
.without('once', 'options')