mirror of
https://github.com/xuxiaobo-bobo/boda_jsEnv.git
synced 2025-04-22 07:47:27 +08:00
248 lines
6.1 KiB
JavaScript
248 lines
6.1 KiB
JavaScript
/**
|
|
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
|
|
* Use of this source code is governed by a license that
|
|
* can be found in the LICENSE file.
|
|
*/
|
|
var util = require('util');
|
|
|
|
/**
|
|
* Validator of configured file / commander options.
|
|
*/
|
|
var Config = module.exports = {
|
|
_errMsgs: {
|
|
'require': '"%s" is required',
|
|
'type' : 'Expect "%s" to be a typeof %s, but now is %s',
|
|
'regex' : 'Verify "%s" with regex failed, %s',
|
|
'max' : 'The maximum of "%s" is %s, but now is %s',
|
|
'min' : 'The minimum of "%s" is %s, but now is %s'
|
|
},
|
|
/**
|
|
* Schema definition.
|
|
* @returns {exports|*}
|
|
*/
|
|
get schema(){
|
|
// Cache.
|
|
if (this._schema) {
|
|
return this._schema;
|
|
}
|
|
// Render aliases.
|
|
this._schema = require('../API/schema');
|
|
for (var k in this._schema) {
|
|
if (k.indexOf('\\') > 0) {
|
|
continue;
|
|
}
|
|
var aliases = [
|
|
k.split('_').map(function(n, i){
|
|
if (i != 0 && n && n.length > 1) {
|
|
return n[0].toUpperCase() + n.slice(1);
|
|
}
|
|
return n;
|
|
}).join('')
|
|
];
|
|
|
|
if (this._schema[k].alias && Array.isArray(this._schema[k].alias)) {
|
|
// If multiple aliases, merge
|
|
this._schema[k].alias.forEach(function(alias) {
|
|
aliases.splice(0, 0, alias);
|
|
});
|
|
}
|
|
else if (this._schema[k].alias)
|
|
aliases.splice(0, 0, this._schema[k].alias);
|
|
|
|
this._schema[k].alias = aliases;
|
|
}
|
|
return this._schema;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Filter / Alias options
|
|
*/
|
|
Config.filterOptions = function(cmd) {
|
|
var conf = {};
|
|
var schema = this.schema;
|
|
|
|
for (var key in schema) {
|
|
var aliases = schema[key].alias;
|
|
aliases && aliases.forEach(function(alias){
|
|
if (typeof(cmd[alias]) !== 'undefined') {
|
|
conf[key] || (conf[key] = cmd[alias]);
|
|
}
|
|
});
|
|
}
|
|
|
|
return conf;
|
|
};
|
|
|
|
/**
|
|
* Verify JSON configurations.
|
|
* @param {Object} json
|
|
* @returns {{errors: Array, config: {}}}
|
|
*/
|
|
Config.validateJSON = function(json){
|
|
// clone config
|
|
var conf = Object.assign({}, json),
|
|
res = {};
|
|
this._errors = [];
|
|
|
|
var regexKeys = {}, defines = this.schema;
|
|
|
|
for (var sk in defines) {
|
|
// Pick up RegExp keys.
|
|
if (sk.indexOf('\\') >= 0) {
|
|
regexKeys[sk] = false;
|
|
continue;
|
|
}
|
|
|
|
var aliases = defines[sk].alias;
|
|
|
|
aliases && aliases.forEach(function(alias){
|
|
conf[sk] || (conf[sk] = json[alias]);
|
|
})
|
|
|
|
var val = conf[sk];
|
|
delete conf[sk];
|
|
|
|
// Validate key-value pairs.
|
|
if (val === undefined ||
|
|
val === null ||
|
|
((val = this._valid(sk, val)) === null)) {
|
|
|
|
// If value is not defined
|
|
// Set default value (via schema.json)
|
|
if (typeof(defines[sk].default) !== 'undefined')
|
|
res[sk] = defines[sk].default;
|
|
continue;
|
|
}
|
|
//console.log(sk, val, val === null, val === undefined);
|
|
res[sk] = val;
|
|
}
|
|
|
|
// Validate RegExp values.
|
|
var hasRegexKey = false;
|
|
for (var k in regexKeys) {
|
|
hasRegexKey = true;
|
|
regexKeys[k] = new RegExp(k);
|
|
}
|
|
if (hasRegexKey) {
|
|
for (var k in conf) {
|
|
for (var rk in regexKeys) {
|
|
if (regexKeys[rk].test(k))
|
|
if (this._valid(k, conf[k], defines[rk])) {
|
|
res[k] = conf[k];
|
|
delete conf[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {errors: this._errors, config: res};
|
|
};
|
|
|
|
/**
|
|
* Validate key-value pairs by specific schema
|
|
* @param {String} key
|
|
* @param {Mixed} value
|
|
* @param {Object} sch
|
|
* @returns {*}
|
|
* @private
|
|
*/
|
|
Config._valid = function(key, value, sch){
|
|
var sch = sch || this.schema[key],
|
|
scht = typeof sch.type == 'string' ? [sch.type] : sch.type;
|
|
|
|
// Required value.
|
|
var undef = typeof value == 'undefined';
|
|
if(this._error(sch.require && undef, 'require', key)){
|
|
return null;
|
|
}
|
|
|
|
// If undefined, make a break.
|
|
if (undef) {
|
|
return null;
|
|
}
|
|
|
|
// Wrap schema types.
|
|
scht = scht.map(function(t){
|
|
return '[object ' + t[0].toUpperCase() + t.slice(1) + ']'
|
|
});
|
|
|
|
// Typeof value.
|
|
var type = Object.prototype.toString.call(value), nt = '[object Number]';
|
|
|
|
// Auto parse Number
|
|
if (type != '[object Boolean]' && scht.indexOf(nt) >= 0 && !isNaN(value)) {
|
|
value = parseFloat(value);
|
|
type = nt;
|
|
}
|
|
|
|
// Verify types.
|
|
if (this._error(!~scht.indexOf(type), 'type', key, scht.join(' / '), type)) {
|
|
return null;
|
|
}
|
|
|
|
// Verify RegExp if exists.
|
|
if (this._error(type == '[object String]' && sch.regex && !(new RegExp(sch.regex)).test(value),
|
|
'regex', key, sch.desc || ('should match ' + sch.regex))) {
|
|
return null;
|
|
}
|
|
|
|
// Verify maximum / minimum of Number value.
|
|
if (type == '[object Number]') {
|
|
if (this._error(typeof sch.max != 'undefined' && value > sch.max, 'max', key, sch.max, value)) {
|
|
return null;
|
|
}
|
|
if (this._error(typeof sch.min != 'undefined' && value < sch.min, 'min', key, sch.min, value)) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// If first type is Array, but current is String, try to split them.
|
|
if(scht.length > 1 && type != scht[0] && type == '[object String]'){
|
|
if(scht[0] == '[object Array]') {
|
|
// unfortunately, js does not support lookahead RegExp (/(?<!\\)\s+/) now (until next ver).
|
|
value = value.split(/([\w\-]+\="[^"]*")|([\w\-]+\='[^']*')|"([^"]*)"|'([^']*)'|\s/)
|
|
.filter(function(v){
|
|
return v && v.trim();
|
|
});
|
|
}
|
|
}
|
|
|
|
// Custom types: sbyte && stime.
|
|
if(sch.ext_type && type == '[object String]' && value.length >= 2) {
|
|
var seed = {
|
|
'sbyte': {
|
|
'G': 1024 * 1024 * 1024,
|
|
'M': 1024 * 1024,
|
|
'K': 1024
|
|
},
|
|
'stime': {
|
|
'h': 60 * 60 * 1000,
|
|
'm': 60 * 1000,
|
|
's': 1000
|
|
}
|
|
}[sch.ext_type];
|
|
|
|
if(seed){
|
|
value = parseFloat(value.slice(0, -1)) * (seed[value.slice(-1)]);
|
|
}
|
|
}
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* Wrap errors.
|
|
* @param {Boolean} possible A value indicates whether it is an error or not.
|
|
* @param {String} type
|
|
* @returns {*}
|
|
* @private
|
|
*/
|
|
Config._error = function(possible, type){
|
|
if (possible) {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
args.splice(0, 2, this._errMsgs[type]);
|
|
this._errors && this._errors.push(util.format.apply(null, args));
|
|
}
|
|
return possible;
|
|
}
|