142 lines
3.8 KiB
JavaScript
142 lines
3.8 KiB
JavaScript
'use strict';
|
|
|
|
const Assert = require('@hapi/hoek/assert');
|
|
const Clone = require('@hapi/hoek/clone');
|
|
|
|
const Common = require('./common');
|
|
const Messages = require('./messages');
|
|
|
|
|
|
const internals = {};
|
|
|
|
|
|
exports.type = function (from, options) {
|
|
|
|
const base = Object.getPrototypeOf(from);
|
|
const prototype = Clone(base);
|
|
const schema = from._assign(Object.create(prototype));
|
|
const def = Object.assign({}, options); // Shallow cloned
|
|
delete def.base;
|
|
|
|
prototype._definition = def;
|
|
|
|
const parent = base._definition || {};
|
|
def.messages = Messages.merge(parent.messages, def.messages);
|
|
def.properties = Object.assign({}, parent.properties, def.properties);
|
|
|
|
// Type
|
|
|
|
schema.type = def.type;
|
|
|
|
// Flags
|
|
|
|
def.flags = Object.assign({}, parent.flags, def.flags);
|
|
|
|
// Terms
|
|
|
|
const terms = Object.assign({}, parent.terms);
|
|
if (def.terms) {
|
|
for (const name in def.terms) { // Only apply own terms
|
|
const term = def.terms[name];
|
|
Assert(schema.$_terms[name] === undefined, 'Invalid term override for', def.type, name);
|
|
schema.$_terms[name] = term.init;
|
|
terms[name] = term;
|
|
}
|
|
}
|
|
|
|
def.terms = terms;
|
|
|
|
// Constructor arguments
|
|
|
|
if (!def.args) {
|
|
def.args = parent.args;
|
|
}
|
|
|
|
// Coerce
|
|
|
|
if (def.coerce) {
|
|
if (typeof def.coerce === 'function') {
|
|
def.coerce = { method: def.coerce };
|
|
}
|
|
|
|
if (def.coerce.from &&
|
|
!Array.isArray(def.coerce.from)) {
|
|
|
|
def.coerce = { method: def.coerce.method, from: [].concat(def.coerce.from) };
|
|
}
|
|
}
|
|
|
|
def.coerce = def.coerce || parent.coerce;
|
|
|
|
// Validate
|
|
|
|
def.validate = def.validate || parent.validate;
|
|
|
|
// Rules
|
|
|
|
const rules = Object.assign({}, parent.rules);
|
|
if (def.rules) {
|
|
for (const name in def.rules) {
|
|
const rule = def.rules[name];
|
|
Assert(typeof rule === 'object', 'Invalid rule definition for', def.type, name);
|
|
|
|
const method = rule.method;
|
|
if (method) {
|
|
Assert(!prototype[name], 'Rule conflict in', def.type, name);
|
|
prototype[name] = method;
|
|
}
|
|
|
|
Assert(!rules[name], 'Rule conflict in', def.type, name);
|
|
rules[name] = rule;
|
|
|
|
if (rule.alias) {
|
|
const aliases = [].concat(rule.alias);
|
|
for (const alias of aliases) {
|
|
prototype[alias] = rule.method;
|
|
}
|
|
}
|
|
|
|
if (rule.args) {
|
|
rule.argsByName = new Map();
|
|
rule.args = rule.args.map((arg) => {
|
|
|
|
if (typeof arg === 'string') {
|
|
arg = { name: arg };
|
|
}
|
|
|
|
Assert(!rule.argsByName.has(arg.name), 'Duplicated argument name', arg.name);
|
|
|
|
rule.argsByName.set(arg.name, arg);
|
|
return arg;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
def.rules = rules;
|
|
|
|
// Overrides
|
|
|
|
if (def.overrides) {
|
|
prototype._super = base;
|
|
schema.$_super = {}; // Backwards compatibility
|
|
for (const override in def.overrides) {
|
|
Assert(base[override], 'Cannot override missing', override);
|
|
def.overrides[override][Common.symbols.parent] = base[override];
|
|
schema.$_super[override] = base[override].bind(schema); // Backwards compatibility
|
|
}
|
|
|
|
Object.assign(prototype, def.overrides);
|
|
}
|
|
|
|
// Casts
|
|
|
|
def.cast = Object.assign({}, parent.cast, def.cast);
|
|
|
|
// Rebuild
|
|
|
|
def.rebuild = def.rebuild || parent.rebuild;
|
|
|
|
return schema;
|
|
};
|