UNPKG

mlproj-core

Version:

Project management for MarkLogic, core implementation

1,082 lines (1,002 loc) 42.4 kB
"use strict"; (function() { const act = require('./action'); const err = require('./error'); /*~ * The base, abstract config item. */ class ConfigItem { constructor(multiline) { this.multiline = multiline ? true : false; } type(type) { throw err.abstractFun('ConfigItem.type'); } handle(result, value, key) { throw err.abstractFun('ConfigItem.handle'); } } /*~ * What is returned as the result of `parse()`. */ class Result { constructor(prop, value) { this.prop = prop; this.value = value; } show(pf, level) { if ( ! level ) { level = 1; } if ( Array.isArray(this.value) ) { this.value.forEach(v => { pf.line(level, this.prop.label); Object.keys(v).forEach(n => v[n].show(pf, level + 1)); }); } else { pf.line(level, this.prop.label, this.value); } } create(obj) { const impl = value => { if ( Array.isArray(value) ) { return value.map(v => impl(v)); } else if ( typeof value === 'object' ) { let obj = {}; Object.keys(value).forEach(p => { value[p].create(obj); }); return obj; } else { return value; } }; obj[this.prop.name] = impl(this.value); } rawValue() { const impl = value => { if ( Array.isArray(value) ) { return value.map(v => impl(v)); } else if ( typeof value === 'object' ) { let obj = {}; Object.keys(value).forEach(p => obj[p] = value[p].rawValue()); return obj; } else { return value; } }; return impl(this.value); } update(actions, display, body, comp) { var val = this.rawValue(); if ( ! this.prop.compare(val, body[this.prop.name]) ) { if ( this.prop.frozen ) { throw new Error('Property differ but is frozen on ' + comp.name + ': ' + this.prop.name + ', please proceed manually'); } display.add('forest' === this.prop._type ? 2 : 1, 'update', this.prop.label); if ( 'database' === this.prop._type ) { actions.add(new act.DatabaseUpdate(comp, this.prop.name, val)); } else if ( 'forest' === this.prop._type ) { actions.add(new act.ForestUpdate(comp, this.prop.name, val)); } else if ( 'server' === this.prop._type ) { actions.add(new act.ServerUpdate(comp, this.prop.name, val)); } else if ( 'user' === this.prop._type ) { actions.add(new act.UserUpdate(comp, this.prop.name, val)); } else if ( 'privilege' === this.prop._type ) { actions.add(new act.PrivilegeUpdate(comp, this.prop.name, val)); } else if ( 'role' === this.prop._type ) { actions.add(new act.RoleUpdate(comp, this.prop.name, val)); } else { let msg = 'Unsupported component type: ' + this.prop._type; if ( display.verbose ) { msg += ' - '; msg += JSON.stringify(this.prop); } throw new Error(msg); } } } } /*~ * The config item for objects, including databases, servers and source sets themselves. */ class ConfigObject { constructor(type) { this._type = type; this.props = {}; this.mandatories = {}; this.defaults = {}; this.frozen = {}; } type(type) { this._type = type + '.' + this._type; Object.keys(this.props).forEach(p => this.props[p].type(type)); } add(path, mandatory, prop) { if ( this.props[path] !== undefined ) { throw new Error('Property already configured: ' + path); } this.props[path] = prop; if ( mandatory ) { this.mandatories[path] = prop; } prop.type(this._type); return this; } dflt(path, val) { if ( this.defaults[path] !== undefined ) { throw new Error('Property default already configured: ' + path); } if ( this.mandatories[path] !== undefined ) { throw new Error('Cannot have default for a mandatory property: ' + path); } this.defaults[path] = val; return this; } freeze(path) { if ( this.frozen[path] !== undefined ) { throw new Error('Property already frozen: ' + path); } this.frozen[path] = true; return this; } parse(config, result, ctxt) { // do not provide any when calling top-level if ( ! result ) { result = {}; } // extract values Object.keys(config).forEach(cfg => { var prop = this.props[cfg]; if ( ! prop ) { throw new Error('Unknwon config property: ' + this._type + '.' + cfg); } var value = config[cfg]; if ( value !== null ) { prop.handle(result, value, cfg, ctxt); } }); // chack mandatory properties this.ensure(result); // add the default values Object.keys(this.defaults).forEach(dflt => { if ( config[dflt] === undefined ) { var prop = this.props[dflt]; var value = this.defaults[dflt]; if ( ! prop ) { throw new Error('Setting the default value for unknown property: ' + dflt); } if ( 'function' === typeof value ) { value = value(result); } prop.handle(result, value, dflt, ctxt); } }); // return it return result; } ensure(result) { Object.keys(this.mandatories).forEach(p => { var prop = this.mandatories[p]; if ( prop instanceof Ignore ) { // nothing } else if ( prop instanceof ConfigObject ) { prop.ensure(result); } else if ( ! Object.keys(result).find(res => res === prop.name) ) { throw new Error('Mandatory config prop ' + this._type + '.' + prop.name + ' not set'); } }); } handle(result, value, key) { // recurse parsing this.parse(value, result); } } /*~ * A config property to ignore. * * This is for pieces of the config files taken care of specially, in the * code. For instance `compose`, which defines how to compose several * components, based on their "inheritence". */ class Ignore extends ConfigItem { handle() { // ignore } type() { // ignore } } /*~ * A database. * * For now, it is not used, it is handled in the code, so use `Ignore`. */ class Database extends ConfigItem { constructor(name) { super(); this.name = name; } handle(result, value, key) { console.log('TODO: implement Database.handle(): ' + this.name + ' / ' + key); } type() { console.log('TODO: implement Database.type(): ' + this.name + ' / ' + key); } } /*~ * An array of objects, each of a different type in a defined set. * * This is used for range indexes, which are all in one single array. But * each can be of one of 3 types: element range, attribute range or path * range. */ class MultiArray extends ConfigItem { constructor() { super(true); this.items = []; } add(pred, item) { this.items.push({ pred: pred, prop: item }); return this; } type(type) { this.items.forEach(item => { item.prop.type(type); }); } handle(result, value, key) { // make sure all items are initialized to empty list as soon as this // one is encountered this.items.forEach(item => { item.prop.init(result); }); value.forEach(v => { var k = 0; var item; do { item = this.items[k++]; } while ( k < this.items.length && ! item.pred(v) ); if ( item ) { item.prop.handle(result, v, key); } else { throw new Error('No predicate matches the value in multi array: ' + key); } }); } } /*~ * An array of objects. Supports `Multiplexer`. */ class ObjectArray extends ConfigItem { constructor(name, label, prop) { super(true); this.name = name; this.label = label; this.prop = prop; } type(type) { this.prop.type(type); } init(result) { if ( ! result[this.name] ) { result[this.name] = new Result(this, []); } } handle(result, values, key) { this.init(result); if ( ! Array.isArray(values) ) { values = [ values ]; } const all = []; const multi = Object.keys(this.prop.props).filter(p => { return this.prop.props[p] instanceof Multiplexer; }); values.forEach(value => { const r = this.prop.parse(value); if ( ! multi.length ) { all.push(r); } else if ( multi.length === 1 ) { const name = this.prop.props[multi[0]].name; const val = r[name].value; if ( Array.isArray(val) ) { val.forEach(v => { const o = {}; Object.keys(r).filter(n => n !== name).forEach(n => o[n] = r[n]); o[name] = new Result(r[name].prop, v); all.push(o); }); } else { all.push(r); } } else { throw new Error('Several multiplexer in the same object not supported'); } }); all.forEach(one => result[this.name].value.push(one)); } // undefined compares equal to the empty array compare(lhs, rhs) { if ( lhs === undefined ) { lhs = []; } if ( rhs === undefined ) { rhs = []; } if ( ! Array.isArray(lhs) ) { throw new Error('lhs is not an array'); } if ( ! Array.isArray(rhs) ) { throw new Error('rhs is not an array'); } if ( lhs.length !== rhs.length ) { return false; } var found = true; for ( var l = 0; l < lhs.length && found; ++l ) { found = false; var left = lhs[l]; var lprops = Object.keys(left).sort(); for ( var r = 0; r < rhs.length && ! found; ++r ) { var right = rhs[r]; var rprops = Object.keys(right).sort(); if ( lprops.length === rprops.length ) { var equal = true; for ( var i = 0; i < lprops.length && equal; ++i ) { var name = lprops[i]; equal = ( rprops[i] === name ) && ( left[name] === right[name] ); } found = equal; } } } return found; } } /*~ * In an `ObjectArray`, marks a config item to demultiply its enclosing object. * * This is used for `name` in range indexes, for instance. In that case, * the name of one range index is a string. But with the Multiplexer, it * can be an array of strings instead, which will result in creating an * array of the object containing it, having all the some values, except * each has a different name. */ class Multiplexer extends ConfigItem { constructor(prop) { super(); this.prop = prop; this.name = prop.name; } handle(result, value, key) { this.prop.handle(result, value, key); } type(type) { this.prop.type(type); } } /*~ * An object, to represent an array of 2 properties (as key and value). */ class CouplesAsMap extends ConfigItem { constructor(name, label, key, value) { super(); this.name = name; this.label = label; this.keyProp = key; this.valProp = value; } handle(result, value, key) { if ( result[this.name] !== undefined ) { throw new Error('Property already exists: ' + this.name); } const prefix = new String('prefix', 'namespace prefix'); const uri = new String('namespace-uri', 'namespace uri'); let v = Object.keys(value).map(p => { // return { "prefix": p, "namespace-uri": value[p] }; return { "prefix" : new Result(prefix, p), "namespace-uri" : new Result(uri, value[p]) }; }); result[this.name] = new Result(this, v); } compare(lhs, rhs) { if ( lhs === undefined ) { lhs = []; } if ( rhs === undefined ) { rhs = []; } if ( lhs.length !== rhs.length ) { return false; } let lhs_obj = {}; let rhs_obj = {}; lhs.forEach(item => lhs_obj[item.prefix] = item["namespace-uri"]); rhs.forEach(item => rhs_obj[item.prefix] = item["namespace-uri"]); let lhs_keys = Object.keys(lhs_obj).sort(); let rhs_keys = Object.keys(rhs_obj).sort(); if ( lhs_keys.length !== rhs_keys.length ) { return false; } for ( let i = 0; i < lhs_keys.length; ++i ) { let key = lhs_keys[i]; if ( key !== rhs_keys[i] || lhs_obj[key] !== rhs_obj[key] ) { return false; } } return true; } type(type) { if ( this._type ) { throw new Error('Type already set on ' + this.name + ': ' + this._type); } this._type = type; } } /*~ * An object, to represent a list of permissions. */ class Perms extends ConfigItem { constructor(name, label) { super(); this.name = name; this.label = label; } type(type) { if ( this._type ) { throw new Error('Type already set on ' + this.name + ': ' + this._type); } this._type = type; } handle(result, value, key) { if ( result[this.name] !== undefined ) { throw new Error('Property already exists: ' + this.name); } const role = new String('role-name', 'role'); const cap = new StringList('capability', 'capability', /\s*,\s*/); let res = []; Object.keys(value).forEach(p => { cap.value(value[p]).forEach(v => { if ( v !== 'update' && v !== 'insert' && v !== 'read' && v !== 'execute' && v !== 'node-update' ) { throw new Error('Unknwon permission capability: ' + v); } res.push({ "role-name" : new Result(role, p), "capability" : new Result(cap, v) }); }); }); result[this.name] = new Result(this, res); } compare(lhs, rhs) { if ( lhs === undefined && rhs === undefined ) { return true; } if ( lhs === undefined || rhs === undefined ) { return false; } if ( lhs.length !== rhs.length ) { return false; } for ( let i = 0; i < lhs.length; ++i ) { const equal = item => { return lhs[i]['role-name'] === item['role-name'] && lhs[i].capability === item.capability; }; if ( ! rhs.find(equal) ) { return false; } } return true; } } /*~ * An object, to represent a list of privileges, e.g. for a role. */ class Privileges extends ConfigItem { constructor(name, label) { super(); this.name = name; this.label = label; } type(type) { if ( this._type ) { throw new Error('Type already set on ' + this.name + ': ' + this._type); } this._type = type; } handle(result, value, key, ctxt) { if ( result[this.name] !== undefined ) { throw new Error('Property already exists: ' + this.name); } const nameProp = new String('privilege-name'); const kindProp = new String('kind'); const impl = (res, value, kind) => { new StringList(null, null, /\s*,\s*/) .value(value || []) .forEach(val => { const priv = { "privilege-name": new Result(nameProp, val), kind: new Result(kindProp, kind) }; res.push(priv); Privileges.cache(priv); }); }; const res = []; impl(res, value.execute, 'execute'); impl(res, value.uri, 'uri'); result[this.name] = new Result(this, res); } compare(lhs, rhs) { const empty = a => a === undefined || ! a.length; if ( empty(lhs) && empty(rhs) ) { return true; } if ( empty(lhs) || empty(rhs) ) { return false; } if ( lhs.length !== rhs.length ) { return false; } for ( let i = 0; i < lhs.length; ++i ) { const equal = item => { return lhs[i]['role-name'] === item['role-name'] && lhs[i].capability === item.capability; }; if ( ! rhs.find(equal) ) { return false; } } return true; } } Privileges.cache = (priv) => { Privileges._privRefs.push(priv); }; Privileges._privRefs = []; Privileges.register = (name, kind, action) => { if ( kind === 'uri' && ! action.endsWith('/') ) { throw new Error(`URI privilege action must ends with a slash: ${action}`); } Privileges._allPrivs[kind][name] = action; }; Privileges._allPrivs = { execute: {}, uri: {} }; Privileges.resolve = (ctxt) => { let resp = new act.PrivilegeList().retrieve(ctxt); resp['privilege-default-list']['list-items']['list-item'].forEach(item => { if ( item.kind !== 'execute' && item.kind !== 'uri' ) { throw new Error(`Unknown kind in privilege list: ${item.kind}`); } const slot = Privileges._allPrivs[item.kind]; // do not override privileges already cached, because declared in the environ if ( ! slot[item.nameref] ) { slot[item.nameref] = item.action; } }); const actionProp = new String('action'); Privileges._privRefs.forEach(priv => { const name = priv['privilege-name'].value; const kind = priv['kind'].value; const action = Privileges._allPrivs[kind][name]; if ( priv.action ) { throw new Error(`Privilege reference already resolved: ${name}/${kind}`); } if ( ! action ) { throw new Error(`Privilege reference not resolved: ${name}/${kind}`); } priv.action = new Result(actionProp, action); }); }; /*~ * A simple, atomic config item (base for string, integer, etc.) */ class Simple extends ConfigItem { constructor(name, label) { super(); this.name = name; this.label = label; this.frozen = false; } freeze() { this.frozen = true; return this; } handle(result, value, key) { if ( result[this.name] !== undefined ) { throw new Error('Property already exists: ' + this.name); } result[this.name] = new Result(this, this.value(value)); } compare(lhs, rhs) { return lhs === rhs; } type(type) { if ( this._type ) { throw new Error('Type already set on ' + this.name + ': ' + this._type); } this._type = type; } } /*~ * A closed enumeration of strings. */ class Enum extends Simple { constructor(name, label, values) { super(name, label); this.values = values; } value(val) { if ( ! this.values.includes(val) ) { throw new Error('Invalid value ' + val + ' in enum ' + this.name + ': ' + this.values); } return val; } } /*~ * A simple boolean. */ class Boolean extends Simple { constructor(name, label) { super(name, label); } value(val) { var type = typeof val; if ( 'boolean' === type ) { // great, nothing to do } else if ( 'string' === type ) { if ( 'false' === val ) { val = false; } else if ( 'true' === val ) { val = true; } else { throw new Error('Invalid boolean value: ' + val); } } else { throw new Error('Boolean value neither a string or a boolean: ' + type); } return val; } } /*~ * A simple integer. If given as a string, it is parsed. */ class Integer extends Simple { constructor(name, label) { super(name, label); } value(val) { var type = typeof val; if ( 'number' === type ) { if ( ! Number.isInteger(val) ) { throw new Error('Integer value is a non-integer number: ' + val); } } else if ( 'string' === type ) { if ( ! /^[0-9]+$/.test(val) ) { throw new Error('Not a lexically valid integer value: ' + val); } val = Number.parseInt(val, 10); } else { throw new Error('Integer value neither a string or a number: ' + type); } return val; } } /*~ * A simple string. */ class String extends Simple { constructor(name, label) { super(name, label); } value(val) { return val; } } /*~ * A list of strings, as a string with a delimiter, or as an array of strings. */ class StringList extends Simple { constructor(name, label, delim) { super(name, label); this.delim = delim; } value(val) { if ( Array.isArray(val) ) { return val; } else if ( 'string' === typeof val ) { return val.split(this.delim); } else { throw new Error('String list value neither a string or an array: ' + type); } } // compare unordered, undefined compares equal to the empty array compare(lhs, rhs) { if ( lhs === undefined ) { lhs = []; } if ( rhs === undefined ) { rhs = []; } if ( lhs.length !== rhs.length ) { return false; } for ( let i = 0; i < lhs.length; ++i ) { if ( ! rhs.includes(lhs[i]) ) { return false; } } return true; } } /*~ * The host properties and config format. */ var host = new ConfigObject('host') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('name', true, new Ignore()) .add('apis', false, new Ignore()) .add('host', false, new String('host', 'host')) .add('group', false, new String('group', 'group')) .dflt('group', 'Default'); // same base for 3 types of range indexes, below function rangeBase() { return new ConfigObject(/*'db.range'*/) .add('type', true, new String('scalar-type', 'type')) .add('positions', false, new String('range-value-positions', 'positions')) .add('invalid', false, new Enum('invalid-values', 'invalid', [ 'ignore', 'reject' ])) .add('collation', false, new String('collation', 'collation')) .dflt('collation', res => { return res['scalar-type'].value === 'string' ? 'http://marklogic.com/collation/' : ''; }); } /*~ * The database properties and config format. */ var database = new ConfigObject('database') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('id', false, new Ignore()) .add('name', true, new Ignore()) .add('properties', false, new Ignore()) .add('forests', false, new Ignore()) // .add('schema', false, new Database('schema-database')) // .add('security', false, new Database('security-database')) // .add('triggers', false, new Database('triggers-database')) .add('schema', false, new Ignore()) .add('security', false, new Ignore()) .add('triggers', false, new Ignore()) .add('sources', false, new Ignore()) .add('indexes', false, new ConfigObject(/*'db.indexes'*/) .add('namespaces', false, new CouplesAsMap('path-namespace', 'path namespaces', 'prefix', 'namespace-uri')) .add('ranges', false, new MultiArray() .add(item => item.field, new ObjectArray('range-field-index', 'field range index', rangeBase() .add('field', true, new Multiplexer(new String('field-name', 'field'))))) .add(item => item.path, new ObjectArray('range-path-index', 'path range index', rangeBase() .add('path', true, new Multiplexer(new String('path-expression', 'path'))))) .add(item => item.parent, new ObjectArray('range-element-attribute-index', 'Attribute range index', rangeBase() .add('name', true, new Multiplexer(new String('localname', 'name'))) .add('namespace', false, new String('namespace-uri', 'ns')) .add('parent', true, new ConfigObject(/*'db.parent'*/ undefined, 'parent') .add('name', true, new String('parent-localname', 'parent name')) .add('namespace', false, new String('parent-namespace-uri', 'parent ns')) .dflt('namespace', '')) .dflt('namespace', ''))) .add(item => true, new ObjectArray('range-element-index', 'element range index', rangeBase() .add('name', true, new Multiplexer(new String('localname', 'name'))) .add('namespace', false, new String('namespace-uri', 'ns')) .dflt('namespace', ''))))) .add('fields', false, new ObjectArray('field', 'fields', new ConfigObject(/*'db.fields'*/) .add('name', false, new String('field-name', 'name')))) .add('searches', false, new ConfigObject(/*'db.searches'*/) .add('one-character', false, new Boolean('one-character-searches', 'one-character searches')) .add('two-character', false, new Boolean('two-character-searches', 'two-character searches')) .add('three-character', false, new Boolean('three-character-searches', 'three-character searches')) .add('trailing-wildcard', false, new Boolean('trailing-wildcard-searches', 'searches with trailing wildcard')) .add('word', false, new Boolean('word-searches', 'unstemmed word searches')) .add('stemmed', false, new Enum('stemmed-searches', 'stemmed word searches', ['off', 'basic', 'advanced', 'decompounding'])) .add('fast', false, new ConfigObject() .add('case-sensitive', false, new Boolean('fast-case-sensitive-searches', 'fast case sensitive searches')) .add('diacritic-sensitive', false, new Boolean('fast-diacritic-sensitive-searches', 'fast diacritic sensitive searches')) .add('element-character', false, new Boolean('fast-element-character-searches', 'fast element character searches')) .add('element-phrase', false, new Boolean('fast-element-phrase-searches', 'fast element phrase searches')) .add('element-trailing-wildcard', false, new Boolean('fast-element-trailing-wildcard-searches', 'fast element trailing wildcard searches')) .add('element-word', false, new Boolean('fast-element-word-searches', 'fast element word searches')) .add('phrase', false, new Boolean('fast-phrase-searches', 'fast phrase searches')) .add('reverse', false, new Boolean('fast-reverse-searches', 'fast reverse searches')))) .add('lexicons', false, new ConfigObject(/*'db.lexicons'*/) .add('uri', false, new Boolean('uri-lexicon', 'URI lexicon')) .add('collection', false, new Boolean('collection-lexicon', 'collection lexicon'))); /*~ * The forest properties and config format. */ var forest = new ConfigObject('forest') .add('comment', false, new Ignore()) .add('name', true, new Ignore()) .add('properties', false, new Ignore()) .add('host', false, new Ignore()) .add('replica', false, new Ignore()) .add('replicas', false, new Ignore()) .add('dir', false, new String('data-directory', 'data directory')) .add('large-dir', false, new String('large-data-directory', 'large data directory')) .add('fast-dir', false, new String('fast-data-directory', 'fast data directory')); /*~ * The server properties and config format. */ var server = new ConfigObject('server') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('id', false, new Ignore()) .add('name', true, new Ignore()) .add('group', false, new Ignore()) .add('properties', false, new Ignore()) .add('rest-config', false, new Ignore()) // .add('content', true, new Database('content-database')) // .add('modules', false, new Database('modules-database')) .add('content', true, new Ignore()) .add('modules', false, new Ignore()) .add('type', true, new Enum('server-type', 'type', [ 'http', 'rest', 'xdbc' ]).freeze()) .add('port', true, new Integer('port', 'port')) .add('root', false, new String('root', 'root')) .add('rewriter', false, new String('url-rewriter', 'url rewriter')) .add('handler', false, new String('error-handler', 'error handler')) .add('output', false, new ConfigObject() .add('byte-order-mark', false, new Enum('output-byte-order-mark', 'output byte order mark', [ 'yes', 'no', 'default' ])) .add('cdata-section-localname', false, new String('output-cdata-section-localname', 'output cdata section localname')) .add('cdata-section-namespace-uri', false, new String('output-cdata-section-namespace-uri', 'output cdata section namespace uri')) .add('doctype-public', false, new String('output-doctype-public', 'output doctype public')) .add('doctype-system', false, new String('output-doctype-system', 'output doctype system')) .add('encoding', false, new Enum('output-encoding', 'output encoding', [ 'UTF-8', 'ASCII', 'ISO-8859-1', 'ISO-8859-5', 'ISO-8859-6', 'ISO-2022-KR', 'ISO-2022-JP', 'EUC-CN', 'EUC-KR', 'EUC-JP', 'CP932', 'CP936', 'CP949', 'CP950', 'CP1252', 'CP1256', 'KOI8-R', 'GB12052', 'GB18030', 'GB2312', 'HZ-GB-2312', 'BIG5', 'BIG5-HKSCS', 'Shift_JIS' ])) .add('escape-uri-attributes', false, new Enum('output-escape-uri-attributes', 'output escape uri attributes', [ 'yes', 'no', 'default' ])) .add('include-content-type', false, new Enum('output-include-content-type', 'output include content type', [ 'yes', 'no', 'default' ])) .add('include-default-attributes', false, new Enum('output-include-default-attributes', 'output include default attributes', [ 'yes', 'no', 'default' ])) .add('indent', false, new Enum('output-indent', 'output indent', [ 'yes', 'no', 'default' ])) .add('indent-tabs', false, new Enum('output-indent-tabs', 'output indent tabs', [ 'yes', 'no', 'default' ])) .add('indent-untyped', false, new Enum('output-indent-untyped', 'output indent untyped', [ 'yes', 'no', 'default' ])) .add('media-type', false, new String('output-media-type', 'output media type')) .add('method', false, new Enum('output-method', 'output method', [ 'default', 'xml', 'xhtml', 'html', 'text', 'sparql-results-json', 'sparql-results-csv', 'n-triples', 'n-quads' ])) .add('normalization-form', false, new Enum('output-normalization-form', 'output normalization form', [ 'none', 'NFC', 'NFD', 'NFKD' ])) .add('omit-xml-declaration', false, new Enum('output-omit-xml-declaration', 'output omit xml declaration', [ 'yes', 'no', 'default' ])) .add('sgml-character-entities', false, new Enum('output-sgml-character-entities', 'output sgml character entities', [ 'none', 'normal', 'math', 'pub' ])) .add('standalone', false, new Enum('output-standalone', 'output standalone', [ 'yes', 'no', 'omit' ])) .add('undeclare-prefixes', false, new Enum('output-undeclare-prefixes', 'output undeclare prefixes', [ 'yes', 'no', 'default' ])) .add('version', false, new String('output-version', 'output version'))); /*~ * The source properties and config format. */ var source = new ConfigObject('source') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('name', true, new Ignore()) .add('filter', false, new Ignore()) .add('dir', false, new String('dir', 'directory')) .add('type', false, new Enum('type', 'type', [ 'plain', 'rest-src', 'tde' ])) .add('garbage', false, new StringList('garbage', 'garbage patterns', /\s*,\s*/)) .add('include', false, new StringList('include', 'include patterns', /\s*,\s*/)) .add('exclude', false, new StringList('exclude', 'exclude patterns', /\s*,\s*/)) .add('target', false, new StringList('target', 'target database or server', /\s*,\s*/)) .add('collections', false, new StringList('collection', 'collections', /\s*,\s*/)) .add('permissions', false, new Perms('permission', 'permissions')); /*~ * The mime properties and config format. */ var mime = new ConfigObject('mime') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('name', true, new Ignore()) .add('extensions', true, new StringList('extension', 'extensions', /\s*,\s*/).freeze()) .add('format', true, new Enum('format', 'format', ['binary', 'json', 'text', 'xml']).freeze()); /*~ * The privilege properties and config format. */ var privilege = new ConfigObject('privilege') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('name', true, new String('privilege-name', 'privilege name')) .add('action', true, new String('action', 'action')) .add('roles', false, new StringList('role', 'roles', /\s*,\s*/)); privilege.register = (name, kind, action) => { Privileges.register(name, kind, action); }; privilege.resolve = (ctxt) => { Privileges.resolve(ctxt); }; /*~ * The role properties and config format. */ var role = new ConfigObject('role') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('name', true, new String('role-name', 'role name')) .add('desc', false, new String('description', 'description')) .add('compartment', false, new String('compartment', 'compartment')) .add('permissions', false, new Perms('permission', 'permissions')) .add('roles', false, new StringList('role', 'roles', /\s*,\s*/)) .add('collections', false, new StringList('collection', 'collections', /\s*,\s*/)) .add('external', false, new StringList('external-name', 'external names', /\s*,\s*/)) .add('privileges', false, new Privileges('privilege', 'privileges')); /*~ * The user properties and config format. */ var user = new ConfigObject('user') .add('compose', false, new Ignore()) .add('comment', false, new Ignore()) .add('name', true, new String('user-name', 'user name')) .add('password', false, new String('password', 'password')) .add('desc', false, new String('description', 'description')) .add('roles', false, new StringList('role', 'roles', /\s*,\s*/)) .add('collections', false, new StringList('collection', 'collections', /\s*,\s*/)) .add('permissions', false, new Perms('permission', 'permissions')); module.exports = { host : host, database : database, forest : forest, server : server, source : source, mime : mime, user : user, privilege : privilege, role : role, Result : Result, String : String } })();