cs-element
Version:
Advanced reactive data management library with state machines, blueprints, persistence, compression, networking, and multithreading support
8,944 lines • 476 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.CSElementMinimal = {}));
})(this, (function (exports) { 'use strict';
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var eventemitter3 = {exports: {}};
(function (module) {
var has = Object.prototype.hasOwnProperty
, prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {}
//
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null);
//
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== 'function') {
throw new TypeError('The listener must be a function');
}
var listener = new EE(fn, context || emitter, once)
, evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();
else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter.prototype.eventNames = function eventNames() {
var names = []
, events
, name;
if (this._eventsCount === 0) return names;
for (name in (events = this._events)) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
EventEmitter.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event
, handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
};
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
EventEmitter.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event
, listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt]
, len = arguments.length
, args
, i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1: return listeners.fn.call(listeners.context), true;
case 2: return listeners.fn.call(listeners.context, a1), true;
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len -1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length
, j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1: listeners[i].fn.call(listeners[i].context); break;
case 2: listeners[i].fn.call(listeners[i].context, a1); break;
case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
default:
if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
return addListener(this, event, fn, context, true);
};
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (
listeners.fn === fn &&
(!once || listeners.once) &&
(!context || listeners.context === context)
) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (
listeners[i].fn !== fn ||
(once && !listeners[i].once) ||
(context && listeners[i].context !== context)
) {
events.push(listeners[i]);
}
}
//
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
else clearEvent(this, evt);
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
};
//
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
//
// Expose the prefix.
//
EventEmitter.prefixed = prefix;
//
// Allow `EventEmitter` to be imported as module namespace.
//
EventEmitter.EventEmitter = EventEmitter;
//
// Expose the module.
//
{
module.exports = EventEmitter;
}
} (eventemitter3));
var eventemitter3Exports = eventemitter3.exports;
var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
/**
* Основные типы и интерфейсы для библиотеки CSElement
*/
/**
* Типы событий элемента
*/
exports.ElementEventType = void 0;
(function (ElementEventType) {
ElementEventType["ElementAdded"] = "element:added";
ElementEventType["ElementRemoved"] = "element:removed";
ElementEventType["DataChanged"] = "data:changed";
ElementEventType["OwnerChanged"] = "owner:changed";
ElementEventType["Locked"] = "locked";
ElementEventType["Unlocked"] = "unlocked";
})(exports.ElementEventType || (exports.ElementEventType = {}));
/**
* Генерация уникального идентификатора
*/
function generateId() {
return `cs_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
var jspath$1 = {exports: {}};
(function (module) {
/**
* JSPath
*
* Copyright (c) 2012 Filatov Dmitry (dfilatov@yandex-team.ru)
* With parts by Marat Dulin (mdevils@gmail.com)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* @version 0.4.0
*/
(function() {
var SYNTAX = {
PATH : 1,
SELECTOR : 2,
OBJ_PRED : 3,
POS_PRED : 4,
LOGICAL_EXPR : 5,
COMPARISON_EXPR : 6,
MATH_EXPR : 7,
CONCAT_EXPR : 8,
UNARY_EXPR : 9,
POS_EXPR : 10,
LITERAL : 11
};
// parser
var parse = (function() {
var TOKEN = {
ID : 1,
NUM : 2,
STR : 3,
BOOL : 4,
NULL : 5,
PUNCT : 6,
EOP : 7
},
MESSAGES = {
UNEXP_TOKEN : 'Unexpected token "%0"',
UNEXP_EOP : 'Unexpected end of path'
};
var path, idx, buf, len;
function parse(_path) {
path = _path.split('');
idx = 0;
buf = null;
len = path.length;
var res = parsePathConcatExpr(),
token = lex();
if(token.type !== TOKEN.EOP) {
throwUnexpected(token);
}
return res;
}
function parsePathConcatExpr() {
var expr = parsePathConcatPartExpr(),
operands;
while(match('|')) {
lex();
(operands || (operands = [expr])).push(parsePathConcatPartExpr());
}
return operands?
{
type : SYNTAX.CONCAT_EXPR,
args : operands
} :
expr;
}
function parsePathConcatPartExpr() {
return match('(')?
parsePathGroupExpr() :
parsePath();
}
function parsePathGroupExpr() {
expect('(');
var expr = parsePathConcatExpr();
expect(')');
var parts = [],
part;
while((part = parsePredicate())) {
parts.push(part);
}
if(!parts.length) {
return expr;
}
else if(expr.type === SYNTAX.PATH) {
expr.parts = expr.parts.concat(parts);
return expr;
}
parts.unshift(expr);
return {
type : SYNTAX.PATH,
parts : parts
};
}
function parsePredicate() {
if(match('[')) {
return parsePosPredicate();
}
if(match('{')) {
return parseObjectPredicate();
}
if(match('(')) {
return parsePathGroupExpr();
}
}
function parsePath() {
if(!matchPath()) {
throwUnexpected(lex());
}
var fromRoot = false,
subst;
if(match('^')) {
lex();
fromRoot = true;
}
else if(matchSubst()) {
subst = lex().val.substr(1);
}
var parts = [],
part;
while((part = parsePathPart())) {
parts.push(part);
}
return {
type : SYNTAX.PATH,
fromRoot : fromRoot,
subst : subst,
parts : parts
};
}
function parsePathPart() {
return matchSelector()?
parseSelector() :
parsePredicate();
}
function parseSelector() {
var selector = lex().val,
token = lookahead(),
prop;
if(match('*') || token.type === TOKEN.ID || token.type === TOKEN.STR) {
prop = lex().val;
}
return {
type : SYNTAX.SELECTOR,
selector : selector,
prop : prop
};
}
function parsePosPredicate() {
expect('[');
var expr = parsePosExpr();
expect(']');
return {
type : SYNTAX.POS_PRED,
arg : expr
};
}
function parseObjectPredicate() {
expect('{');
var expr = parseLogicalORExpr();
expect('}');
return {
type : SYNTAX.OBJ_PRED,
arg : expr
};
}
function parseLogicalORExpr() {
var expr = parseLogicalANDExpr(),
operands;
while(match('||')) {
lex();
(operands || (operands = [expr])).push(parseLogicalANDExpr());
}
return operands?
{
type : SYNTAX.LOGICAL_EXPR,
op : '||',
args : operands
} :
expr;
}
function parseLogicalANDExpr() {
var expr = parseEqualityExpr(),
operands;
while(match('&&')) {
lex();
(operands || (operands = [expr])).push(parseEqualityExpr());
}
return operands?
{
type : SYNTAX.LOGICAL_EXPR,
op : '&&',
args : operands
} :
expr;
}
function parseEqualityExpr() {
var expr = parseRelationalExpr();
while(
match('==') || match('!=') || match('===') || match('!==') ||
match('^==') || match('==^') ||match('^=') || match('=^') ||
match('$==') || match('==$') || match('$=') || match('=$') ||
match('*==') || match('==*')|| match('*=') || match('=*')
) {
expr = {
type : SYNTAX.COMPARISON_EXPR,
op : lex().val,
args : [expr, parseEqualityExpr()]
};
}
return expr;
}
function parseRelationalExpr() {
var expr = parseAdditiveExpr();
while(match('<') || match('>') || match('<=') || match('>=')) {
expr = {
type : SYNTAX.COMPARISON_EXPR,
op : lex().val,
args : [expr, parseRelationalExpr()]
};
}
return expr;
}
function parseAdditiveExpr() {
var expr = parseMultiplicativeExpr();
while(match('+') || match('-')) {
expr = {
type : SYNTAX.MATH_EXPR,
op : lex().val,
args : [expr, parseMultiplicativeExpr()]
};
}
return expr;
}
function parseMultiplicativeExpr() {
var expr = parseUnaryExpr();
while(match('*') || match('/') || match('%')) {
expr = {
type : SYNTAX.MATH_EXPR,
op : lex().val,
args : [expr, parseMultiplicativeExpr()]
};
}
return expr;
}
function parsePosExpr() {
if(match(':')) {
lex();
return {
type : SYNTAX.POS_EXPR,
toIdx : parseUnaryExpr()
};
}
var fromExpr = parseUnaryExpr();
if(match(':')) {
lex();
if(match(']')) {
return {
type : SYNTAX.POS_EXPR,
fromIdx : fromExpr
};
}
return {
type : SYNTAX.POS_EXPR,
fromIdx : fromExpr,
toIdx : parseUnaryExpr()
};
}
return {
type : SYNTAX.POS_EXPR,
idx : fromExpr
};
}
function parseUnaryExpr() {
if(match('!') || match('-')) {
return {
type : SYNTAX.UNARY_EXPR,
op : lex().val,
arg : parseUnaryExpr()
};
}
return parsePrimaryExpr();
}
function parsePrimaryExpr() {
var token = lookahead(),
type = token.type;
if(type === TOKEN.STR || type === TOKEN.NUM || type === TOKEN.BOOL || type === TOKEN.NULL) {
return {
type : SYNTAX.LITERAL,
val : lex().val
};
}
if(matchPath()) {
return parsePath();
}
if(match('(')) {
return parseGroupExpr();
}
return throwUnexpected(lex());
}
function parseGroupExpr() {
expect('(');
var expr = parseLogicalORExpr();
expect(')');
return expr;
}
function match(val) {
var token = lookahead();
return token.type === TOKEN.PUNCT && token.val === val;
}
function matchPath() {
return matchSelector() || matchSubst() || match('^');
}
function matchSelector() {
var token = lookahead();
if(token.type === TOKEN.PUNCT) {
var val = token.val;
return val === '.' || val === '..';
}
return false;
}
function matchSubst() {
var token = lookahead();
return token.type === TOKEN.ID && token.val[0] === '$';
}
function expect(val) {
var token = lex();
if(token.type !== TOKEN.PUNCT || token.val !== val) {
throwUnexpected(token);
}
}
function lookahead() {
if(buf !== null) {
return buf;
}
var pos = idx;
buf = advance();
idx = pos;
return buf;
}
function advance() {
while(isWhiteSpace(path[idx])) {
++idx;
}
if(idx >= len) {
return {
type : TOKEN.EOP,
range : [idx, idx]
};
}
var token = scanPunctuator();
if(token ||
(token = scanId()) ||
(token = scanString()) ||
(token = scanNumeric())) {
return token;
}
token = { range : [idx, idx] };
idx >= len?
token.type = TOKEN.EOP :
token.val = path[idx];
throwUnexpected(token);
}
function lex() {
var token;
if(buf) {
idx = buf.range[1];
token = buf;
buf = null;
return token;
}
return advance();
}
function isDigit(ch) {
return '0123456789'.indexOf(ch) >= 0;
}
function isWhiteSpace(ch) {
return ' \r\n\t'.indexOf(ch) > -1;
}
function isIdStart(ch) {
return ch === '$' || ch === '@' || ch === '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}
function isIdPart(ch) {
return isIdStart(ch) || (ch >= '0' && ch <= '9');
}
function scanId() {
var ch = path[idx];
if(!isIdStart(ch)) {
return;
}
var start = idx,
id = ch;
while(++idx < len) {
ch = path[idx];
if(!isIdPart(ch)) {
break;
}
id += ch;
}
switch(id) {
case 'true':
case 'false':
return {
type : TOKEN.BOOL,
val : id === 'true',
range : [start, idx]
};
case 'null':
return {
type : TOKEN.NULL,
val : null,
range : [start, idx]
};
default:
return {
type : TOKEN.ID,
val : id,
range : [start, idx]
};
}
}
function scanString() {
if(path[idx] !== '"' && path[idx] !== '\'') {
return;
}
var orig = path[idx],
start = ++idx,
str = '',
eosFound = false,
ch;
while(idx < len) {
ch = path[idx++];
if(ch === '\\') {
ch = path[idx++];
}
else if((ch === '"' || ch === '\'') && ch === orig) {
eosFound = true;
break;
}
str += ch;
}
if(eosFound) {
return {
type : TOKEN.STR,
val : str,
range : [start, idx]
};
}
}
function scanNumeric() {
var start = idx,
ch = path[idx],
isFloat = ch === '.';
if(isFloat || isDigit(ch)) {
var num = ch;
while(++idx < len) {
ch = path[idx];
if(ch === '.') {
if(isFloat) {
return;
}
isFloat = true;
}
else if(!isDigit(ch)) {
break;
}
num += ch;
}
return {
type : TOKEN.NUM,
val : isFloat? parseFloat(num) : parseInt(num, 10),
range : [start, idx]
};
}
}
function scanPunctuator() {
var start = idx,
ch1 = path[idx],
ch2 = path[idx + 1];
if(ch1 === '.') {
if(isDigit(ch2)) {
return;
}
return path[++idx] === '.'?
{
type : TOKEN.PUNCT,
val : '..',
range : [start, ++idx]
} :
{
type : TOKEN.PUNCT,
val : '.',
range : [start, idx]
};
}
if(ch2 === '=') {
var ch3 = path[idx + 2];
if(ch3 === '=') {
if('=!^$*'.indexOf(ch1) >= 0) {
return {
type : TOKEN.PUNCT,
val : ch1 + ch2 + ch3,
range : [start, idx += 3]
};
}
}
else if('^$*'.indexOf(ch3) >= 0) {
if(ch1 === '=') {
return {
type : TOKEN.PUNCT,
val : ch1 + ch2 + ch3,
range : [start, idx += 3]
};
}
}
else if('=!^$*><'.indexOf(ch1) >= 0) {
return {
type : TOKEN.PUNCT,
val : ch1 + ch2,
range : [start, idx += 2]
};
}
}
else if(ch1 === '=' && '^$*'.indexOf(ch2) >= 0) {
return {
type : TOKEN.PUNCT,
val : ch1 + ch2,
range : [start, idx += 2]
};
}
if(ch1 === ch2 && (ch1 === '|' || ch1 === '&')) {
return {
type : TOKEN.PUNCT,
val : ch1 + ch2,
range : [start, idx += 2]
};
}
if(':{}()[]^+-*/%!><|'.indexOf(ch1) >= 0) {
return {
type : TOKEN.PUNCT,
val : ch1,
range : [start, ++idx]
};
}
}
function throwUnexpected(token) {
if(token.type === TOKEN.EOP) {
throwError(token, MESSAGES.UNEXP_EOP);
}
throwError(token, MESSAGES.UNEXP_TOKEN, token.val);
}
function throwError(token, messageFormat) {
var args = Array.prototype.slice.call(arguments, 2),
msg = messageFormat.replace(
/%(\d)/g,
function(_, idx) {
return args[idx] || '';
}),
error = new Error(msg);
error.column = token.range[0];
throw error;
}
return parse;
})();
// translator
var translate = (function() {
var body, vars, lastVarId, unusedVars;
function acquireVar() {
if(unusedVars.length) {
return unusedVars.shift();
}
var varName = 'v' + ++lastVarId;
vars.push(varName);
return varName;
}
function releaseVars() {
var args = arguments, i = args.length;
while(i--) {
unusedVars.push(args[i]);
}
}
function translate(ast) {
body = [];
vars = ['res'];
lastVarId = 0;
unusedVars = [];
translateExpr(ast, 'res', 'data');
body.unshift(
'var ',
Array.isArray?
'isArr = Array.isArray' :
'toStr = Object.prototype.toString, isArr = function(o) { return toStr.call(o) === "[object Array]"; }',
', concat = Array.prototype.concat',
',', vars.join(','), ';');
if(ast.type === SYNTAX.PATH) {
var lastPart = ast.parts[ast.parts.length - 1];
if(lastPart && lastPart.type === SYNTAX.POS_PRED && 'idx' in lastPart.arg) {
body.push('res = res[0];');
}
}
body.push('return res;');
return body.join('');
}
function translatePath(path, dest, ctx) {
var parts = path.parts,
i = 0, len = parts.length;
body.push(
dest, '=', path.fromRoot? 'data' : path.subst? 'subst.' + path.subst : ctx, ';',
'isArr(' + dest + ') || (' + dest + ' = [' + dest + ']);');
while(i < len) {
var item = parts[i++];
switch(item.type) {
case SYNTAX.SELECTOR:
item.selector === '..'?
translateDescendantSelector(item, dest, dest) :
translateSelector(item, dest, dest);
break;
case SYNTAX.OBJ_PRED:
translateObjectPredicate(item, dest, dest);
break;
case SYNTAX.POS_PRED:
translatePosPredicate(item, dest, dest);
break;
case SYNTAX.CONCAT_EXPR:
translateConcatExpr(item, dest, dest);
break;
}
}
}
function translateSelector(sel, dest, ctx) {
if(sel.prop) {
var propStr = escapeStr(sel.prop),
res = acquireVar(), i = acquireVar(), len = acquireVar(),
curCtx = acquireVar(),
j = acquireVar(), val = acquireVar(), tmpArr = acquireVar();
body.push(
res, '= [];', i, '= 0;', len, '=', ctx, '.length;', tmpArr, '= [];',
'while(', i, '<', len, ') {',
curCtx, '=', ctx, '[', i, '++];',
'if(', curCtx, '!= null) {');
if(sel.prop === '*') {
body.push(
'if(typeof ', curCtx, '=== "object") {',
'if(isArr(', curCtx, ')) {',
res, '=', res, '.concat(', curCtx, ');',
'}',
'else {',
'for(', j, ' in ', curCtx, ') {',
'if(', curCtx, '.hasOwnProperty(', j, ')) {',
val, '=', curCtx, '[', j, '];');
inlineAppendToArray(res, val);
body.push(
'}',
'}',
'}',
'}');
}
else {
body.push(
val, '=', curCtx, '[', propStr, '];');
inlineAppendToArray(res, val, tmpArr, len);
}
body.push(
'}',
'}',
dest, '=', len, '> 1 &&', tmpArr, '.length?', tmpArr, '.length > 1?',
'concat.apply(', res, ',', tmpArr, ') :', res, '.concat(', tmpArr, '[0]) :', res, ';');
releaseVars(res, i, len, curCtx, j, val, tmpArr);
}
}
function translateDescendantSelector(sel, dest, baseCtx) {
var prop = sel.prop,
ctx = acquireVar(), curCtx = acquireVar(), childCtxs = acquireVar(),
i = acquireVar(), j = acquireVar(), val = acquireVar(),
len = acquireVar(), res = acquireVar();
body.push(
ctx, '=', baseCtx, '.slice(),', res, '= [];',
'while(', ctx, '.length) {',
curCtx, '=', ctx, '.shift();');
prop?
body.push(
'if(typeof ', curCtx, '=== "object" &&', curCtx, ') {') :
body.push(
'if(typeof ', curCtx, '!= null) {');
body.push(
childCtxs, '= [];',
'if(isArr(', curCtx, ')) {',
i, '= 0,', len, '=', curCtx, '.length;',
'while(', i, '<', len, ') {',
val, '=', curCtx, '[', i, '++];');
prop && body.push(
'if(typeof ', val, '=== "object") {');
inlineAppendToArray(childCtxs, val);
prop && body.push(
'}');
body.push(
'}',
'}',
'else {');
if(prop) {
if(prop !== '*') {
body.push(
val, '=', curCtx, '["' + prop + '"];');
inlineAppendToArray(res, val);
}
}
else {
inlineAppendToArray(res, curCtx);
body.push(
'if(typeof ', curCtx, '=== "object") {');
}
body.push(
'for(', j, ' in ', curCtx, ') {',
'if(', curCtx, '.hasOwnProperty(', j, ')) {',
val, '=', curCtx, '[', j, '];');
inlineAppendToArray(childCtxs, val);
prop === '*' && inlineAppendToArray(res, val);
body.push(
'}',
'}');
prop || body.push(
'}');
body.push(
'}',
childCtxs, '.length &&', ctx, '.unshift.apply(', ctx, ',', childCtxs, ');',
'}',
'}',
dest, '=', res, ';');
releaseVars(ctx, curCtx, childCtxs, i, j, val, len, res);
}
function translateObjectPredicate(expr, dest, ctx) {
var resVar = acquireVar(), i = acquireVar(), len = acquireVar(),
cond = acquireVar(), curItem = acquireVar();
body.push(
resVar, '= [];',
i, '= 0;',
len, '=', ctx, '.length;',
'while(', i, '<', len, ') {',
curItem, '=', ctx, '[', i, '++];');
translateExpr(expr.arg, cond, curItem);
body.push(
convertToBool(expr.arg, cond), '&&', resVar, '.push(', curItem, ');',
'}',
dest, '=', resVar, ';');
releaseVars(resVar, i, len, curItem, cond);
}
function translatePosPredicate(item, dest, ctx) {
var arrayExpr = item.arg, fromIdx, toIdx;
if(arrayExpr.idx) {
var idx = acquireVar();
translateExpr(arrayExpr.idx, idx, ctx);
body.push(
idx, '< 0 && (', idx, '=', ctx, '.length +', idx, ');',
dest, '=', ctx, '[', idx, '] == null? [] : [', ctx, '[', idx, ']];');
releaseVars(idx);
return false;
}
else if(arrayExpr.fromIdx) {
if(arrayExpr.toIdx) {
translateExpr(arrayExpr.fromIdx, fromIdx = acquireVar(), ctx);
translateExpr(arrayExpr.toIdx, toIdx = acquireVar(), ctx);
body.push(dest, '=', ctx, '.slice(', fromIdx, ',', toIdx, ');');
releaseVars(fromIdx, toIdx);
}
else {
translateExpr(arrayExpr.fromIdx, fromIdx = acquireVar(), ctx);
body.push(dest, '=', ctx, '.slice(', fromIdx, ');');
releaseVars(fromIdx);
}
}
else {
translateExpr(arrayExpr.toIdx, toIdx = acquireVar(), ctx);
body.push(dest, '=', ctx, '.slice(0,', toIdx, ');');
releaseVars(toIdx);
}
}
function translateExpr(expr, dest, ctx) {
switch(expr.type) {
case SYNTAX.PATH:
translatePath(expr, dest, ctx);
break;
case SYNTAX.CONCAT_EXPR:
translateConcatExpr(expr, dest, ctx);
break;
case SYNTAX.COMPARISON_EXPR:
translateComparisonExpr(expr, dest, ctx);
break;
case SYNTAX.MATH_EXPR:
translateMathExpr(expr, dest, ctx);
break;
case SYNTAX.LOGICAL_EXPR:
translateLogicalExpr(expr, dest, ctx);
break;
case SYNTAX.UNARY_EXPR:
translateUnaryExpr(expr, dest, ctx);
break;
case SYNTAX.LITERAL:
body.push(dest, '=');
translateLiteral(expr.val);
body.push(';');
break;
}
}
function translateLiteral(val) {
body.push(typeof val === 'string'? escapeStr(val) : val === null? 'null' : val);
}
function translateComparisonExpr(expr, dest, ctx) {
var val1 = acquireVar(), val2 = acquireVar(),
isVal1Array = acquireVar(), isVal2Array = acquireVar(),
i = acquireVar(), j = acquireVar(),
len1 = acquireVar(), len2 = acquireVar(),
leftArg = expr.args[0], rightArg = expr.args[1];
body.push(dest, '= false;');
translateExpr(leftArg, val1, ctx);
translateExpr(rightArg, val2, ctx);
var isLeftArgPath = leftArg.type === SYNTAX.PATH,
isRightArgLiteral = rightArg.type === SYNTAX.LITERAL;
body.push(isVal1Array, '=');
isLeftArgPath? body.push('true;') : body.push('isArr(', val1, ');');
body.push(isVal2Array, '=');
isRightArgLiteral? body.push('false;') : body.push('isArr(', val2, ');');
body.push(
'if(');
isLeftArgPath || body.push(isVal1Array, '&&');
body.push(val1, '.length === 1) {',
val1, '=', val1, '[0];',
isVal1Array, '= false;',
'}');
isRightArgLiteral || body.push(
'if(', isVal2Array, '&&', val2, '.length === 1) {',
val2, '=', val2, '[0];',
isVal2Array, '= false;',
'}');
body.push(i, '= 0;',
'if(', isVal1Array, ') {',
len1, '=', val1, '.length;');
if(!isRightArgLiteral) {
body.push(
'if(', isVal2Array, ') {',
len2, '=', val2, '.length;',
'while(', i, '<', len1, '&& !', dest, ') {',
j, '= 0;',
'while(', j, '<', len2, ') {');
writeCondition(expr.op, [val1, '[', i, ']'].join(''), [val2, '[', j, ']'].join(''));
body.push(
dest, '= true;',
'break;',
'}',
'++', j, ';',
'}',
'++', i, ';',
'}',
'}',
'else {');
}
body.push(
'while(', i, '<', len1, ') {');
writeCondition(expr.op, [val1, '[', i, ']'].join(''), val2);
body.push(
dest, '= true;',
'break;',
'}',
'++', i, ';',
'}');
isRightArgLiteral || body.push(
'}');
body.push(
'}');
if(!isRightArgLiteral) {
body.push(
'else if(', isVal2Array,') {',
len2, '=', val2, '.length;',
'while(', i, '<', len2, ') {');
writeCondition(expr.op, val1, [val2, '[', i, ']'].join(''));
body.push(
dest, '= true;',
'break;',
'}',
'++', i, ';',
'}',
'}');
}
body.push(
'else {',
dest, '=', binaryOperators[expr.op](val1, val2), ';',
'}');
releaseVars(val1, val2, isVal1Array, isVal2Array, i, j, len1, len2);
}
function writeCondition(op, val1Expr, val2Expr) {
body.push('if(', binaryOperators[op](val1Expr, val2Expr), ') {');
}
function translateLogicalExpr(expr, dest, ctx) {
var conditionVars = [],
args = expr.args, len = args.length,
i = 0, val;
body.push(dest, '= false;');
switch(expr.op) {
case '&&':
while(i < len) {
conditionVars.push(val = acquireVar());
translateExpr(args[i], val, ctx);
body.push('if(', convertToBool(args[i++], val), ') {');
}
body.push(dest, '= true;');
break;
case '||':
while(i < len) {
conditionVars.push(val = acquireVar());
translateExpr(args[i], val, ctx);
body.push(
'if(', convertToBool(args[i], val), ') {',
dest, '= true;',
'}');
if(i++ + 1 < len) {
body.push('else {');
}
}
--len;
break;
}
while(len--) {
body.push('}');
}
releaseVars.apply(null, conditionVars);
}
function translateMathExpr(expr, dest, ctx) {
var val1 = acquireVar(),
val2 = acquireVar(),
args = expr.args;
translateExpr(args[0], val1, ctx);
translateExpr(args[1], val2, ctx);
body.push(
dest, '=',
binaryOperators[expr.op](
convertToSingleValue(args[0], val1),
convertToSingleValue(args[1], val2)),
';');
releaseVars(val1, val2);
}
function translateUnaryExpr(expr, dest, ctx) {
var val = acquireVar(),
arg = expr.arg;
translateExpr(arg, val, ctx);
switch(expr.op) {
case '!':
body.push(dest, '= !', convertToBool(arg, val) + ';');
break;
case '-':
body.push(dest, '= -', convertToSingleValue(arg, val) + ';');
break;
}
releaseVars(val);
}
function translateConcatExpr(expr, dest, ctx) {
var argVars = [],
args = expr.args,
len = args.length,
i = 0;
while(i < len) {
argVars.push(acquireVar());
translateExpr(args[i], argVars[i++], ctx);
}
body.push(dest, '= concat.call(', argVars.join(','), ');');
releaseVars.apply(null, argVars);
}
function escapeStr(s) {
return '\'' + s.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + '\'';
}
function inlineAppendToArray(res, val, tmpArr, len) {
body.push(
'if(typeof ', val, '!== "undefined") {',
'if(isArr(', val, ')) {');
if(tmpArr) {
body.push(
len, '> 1?');
inlinePushToArray(tmpArr, val);
body.push(
':');
}
body.push(
res, '=', res, '.length?', res, '.concat(', val, ') :', val, '.slice()', ';',
'}',
'else {');
tmpArr && body.push(
'if(', tmpArr, '.length) {',
res, '= concat.apply(', res, ',', tmpArr, ');',
tmpArr, '= [];',
'}');
inlinePushToArray(res, val);
body.push(';',
'}',
'}');
}
function inlinePushToArray(res, val) {
body.push(res, '.length?', res, '.push(', val, ') :', res, '[0] =', val);
}
function convertToBool(arg, varName) {
switch(arg.type) {
case SYNTAX.LOGICAL_EXPR:
return varName;
case SYNTAX.LITERAL:
return '!!' + varName;
case SYNTAX.PATH:
return varName + '.length > 0';
default:
return ['(typeof ', varName, '=== "boolean"?',
varName, ':',
'isArr(', varName, ')?', varName, '.length > 0 : !!', varName, ')'].join('');
}
}
function convertToSingleValue(arg, varName) {
switch(arg.type) {
case SYNTAX.LITERAL:
return varName;
case SYNTAX.PATH:
return varName + '[0]';
default:
return ['(isArr(', varName, ')?', varName, '[0] : ', varName, ')'].join('');
}
}
function startsWithStrict(val1, val2) {
return ['typeof ', val1, '=== "string" && typeof ', val2, '=== "string" &&',
val1, '.indexOf(', val2, ') === 0'].join('');
}
function startsWith(val1, val2) {
return [val1, '!= null &&', val2, '!= null &&',
val1, '.toString().toLowerCase().indexOf(', val2, '.toString().toLowerCase()) === 0'].join('');
}
function endsWithStrict(val1, val2) {
return ['typeof ', val1, '=== "string" && typeof ', val2, '=== "string" &&',
val1, '.length >=', val2, '.length &&',
val1, '.lastIndexOf(', val2, ') ===', val1, '.length -', val2, '.length'].join('');
}
function endsWith(val1, val2) {
return [val1, '!= null &&', val2, '!= null &&',
'(', val1, '=', val1, '.toString()).length >=', '(', val2, '=', val2, '.toString()).length &&',
'(', val1, '.toLowerCase()).lastIndexOf(', '(', val2, '.toLowerCase())) ===',
val1, '.length -', val2, '.length'].join('');
}
function containsStrict(val1, val2) {
return ['typeof ', val1, '=== "string" && typeof ', val2, '=== "string" &&',
val1, '.indexOf(', val2, ') > -1'].join('');
}
function contains(val1, val2) {
return [val1, '!= null && ', val2, '!= null &&',
val1, '.toString().toLowerCase().indexOf(', val2, '.toString().toLowerCase()) > -1'].join('');
}
var binaryOperators = {
'===' : function(val1, val2) {
return val1 + '===' + val2;
},
'==' : function(val1, val2) {
return ['typeof ', val1, '=== "string" && typeof ', val2, '=== "string"?',
val1, '.toLowerCase() ===', val2, '.toLowerCase() :' +
val1, '==', val2].join('');
},
'>=' : function(val1, val2) {
return val1 + '>=' + val2;
},
'>' : function(val1, val2) {
return val1 + '>' + val2;
},
'<=' : function(val1, val2) {
return val1 + '<=' + val2;
},
'<' : function(val1, val2) {
return val1 + '<' + val2;
},
'!==' : function(val1, val2) {
return val1 + '!==' + val2;
},
'!=' : function(val1, val2) {
return val1 + '!=' + val2;
},
'^==' : startsWithStrict,
'==^' : function(val1, val2) {
return startsWithStrict(val2, val1);
},
'^=' : startsWith,
'=^' : function(val1, val2) {
return startsWith(val2, val1);
},
'$==' : endsWithStrict,
'==$' : function(val1, val2) {
return endsWithStrict(val2, val1);
},
'$=' : endsWith,
'=$' : function(val1, val2) {
return endsWith(val2, val1);
},
'*==' : containsStrict,
'==*' : function(val1, val2) {
return containsStrict(val2, val1);
},
'=*' : function(val1, val2) {
return contains(val2, val1);
},
'*=' : contains,
'+' : function(val1, val2) {
return val1 + '+' + val2;
},
'-' : function(val1, val2) {
return val1 + '-' + val2;
},
'*' : function(val1, val2) {
return val1 + '*' + val2;
},
'/' : function(val1, val2) {
return val1 + '/' + val2;
},
'%' : function(val1, val2) {
return val1 + '%' + val2;
}
};
return translate;
})();
function compile(path) {
return Function('data,subst', translate(parse(path)));
}
var cache = {},
cacheKeys = [],
params = {
cacheSize : 100
},
setParamsHooks = {
cacheSize : function(oldVal, newVal) {
if(newVal < oldVal && cacheKeys.length > newVal) {
var removedKeys = cacheKeys.splice(0, cacheKeys.length - newVal),
i = removedKeys.length;
while(i--) {
delete cache[removedKeys[i]];
}
}
}
};
var decl = function(path, ctx, substs) {
if(!cache[path]) {
cache[path] = compile(path);
if(cacheKeys.push(path) > params.cacheSize) {
delete cache[cacheKeys.shift()];
}
}
return cache[path](ctx, substs || {});
};
decl.version = '0.3.4';
decl.params = function(_params) {
if(!arguments.length) {
return params;
}
for(var name in _params) {
if(_params.hasOwnProperty(name)) {
setParamsHooks[name] && setParamsHooks[name](params[name], _params[name]);
params[name] = _params[name];
}
}
};
decl.compile = compile;
decl.apply = decl;
{
module.exports = decl;
}
})();
} (jspath$1));
var jspathExports = jspath$1.exports;
var jspath = jspathExports;
/**
* Универсальный движок запросов для CSElement
* Поддерживает CSS-селекторы, XPath, объектные селекторы, индексирование и оптимизацию запросов
*/
/**
* Типы селекторов
*/
var SelectorType;
(function (SelectorType) {
SelectorType["CSS"] = "css";
SelectorType["XPATH"] = "xpath";
SelectorType["OBJECT"] = "object";
})(SelectorType || (SelectorType = {}));
/**
* Расширенный движок запросов
*/
class QueryEngine {
/**
* Выполняет поиск элементов по универсальному селектору
*/
static query(root, selector) {
const startTime = performance.now();
const selectorString = this.getSelectorString(selector);
// Создаем ключ кэша, который включает ID корневого элемента
const cacheKey = `${root.id}:${selectorString}`;
// Проверяем кэш
const cached = this.getCachedResult(cacheKey);
if (cached) {
this.stats.cacheHits++;
this.stats.totalQueries++; // Кэшированные запросы тоже считаются как запросы
// Обновляем частоту селекторов для кэшированных запросов
const currentCount = this.selectorFrequency.get(selectorString) || 0;
this.selectorFrequency.set(selectorString, currentCount + 1);
// Обновляем топ селекторов
this.stats.mostFrequentSelectors = Array.from(this.selectorFrequency.entries())
.map(([sel, count]) => ({ selector: sel, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
return cached;
}
let result;
if (typeof selector === 'string') {
// Автоматически определяем тип селектора
if (selector.startsWith('//') || selector.startsWith('/')) {
result = this.queryXPath(root, selector);
}
else {
result = this.queryCSS(root, selector);
}
}
else {
switch (selector.type) {
case SelectorType.CSS:
result = this.queryCSS(root, selector.selector);
break;
case SelectorType.XPATH:
result = this.queryXPath(root, selector.expression);
break;
case SelectorType.OBJECT:
result = this.queryObject(root, selector.selector);
break;
default:
throw new Error(`Неподдерживаемый тип селектора: ${selector.type}`);
}
}
// Обновляем статистику
const executionTime = performance.now() - startTime;
this.updateStats(selectorString, executionTime);
// Кэшируем результат с учетом корневого элемента
this.cacheResult(cacheKey, result);
return result;
}
/**
* Находит первый элемент по селектору
*/
static queryOne(root, selector) {
const results = this.query(root, selector);
return results.length > 0 ? results[0] : null;
}
/**
* Выполняет поиск по CSS-селектору
*/
static queryCSS(root, selector) {
const parsed = this.parseCSS(selector);
return this.executeCSS(root, parsed);
}
/**
* Выполняет поиск по XPath
*/
static queryXPath(root, expression) {
const context = this.createXPathContext(root);
return this.evaluateXPath(context, expression);
}
/**
* Выполняет поиск по объектному селектору
*/
static queryObject(root, selector) {
return this.queryBySelector(root, selector);
}
/**
* Включить/выключить индексирование
*/
static setIndexingEnabled(enabled) {
// В данной реализации индексирование всегда включено
// Этот метод оставлен для совместимости API
console.log(`Индексирование ${enabled ? 'включено' : 'выключено'}`);
}
/**
* Создает или обновляет индекс для элемента
*/
static buildIndex(root) {
const index = {
byName: new Map(),
byId: new Map(),
byClass: new Map(),
byAttribute: new Map(),
byDepth: new Map(),
all: new Set()
};
this.traverseAndIndex(root, index, 0);
this.indices.set(root, index);
}
/**
* Получает индекс для элемента
*/
static getIndex(root) {
let index = this.indices.get(root);
if (!index) {
this.buildIndex(root);
index = this.indices.get(root);
}
return index;
}
/**
* Очищает кэш запросов
*/
static clearCache() {
this.queryCache.clear();
}
/**
* Получает статистику запросов
*/
static getStats() {
return { ...this.stats };
}
/**
* Сбрасывает статистику
*/
static resetStats() {
this.stats = {
totalQueries: 0,
cacheHits: 0,
averageExecutionTime: 0,
slowestQuery: { selector: '', time: 0 },
mostFrequentSelectors: []
};
this.selectorFrequency.clear();
}
/**
* Приватные методы
*/
static getSelectorString(selector) {
if (typeof selector === 'string') {
return selector;
}
switch (selector.type) {
case SelectorType.CSS:
return `css:${selector.selector}`;
case SelectorType.XPATH:
return `xpath:${selector.expression}`;
case SelectorType.OBJECT:
return `object:${JSON.stringify(selector.selector)}`;
default:
return 'unknown';
}
}
static getCachedResult(selector) {
const cached = this.queryCache.get(selector);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.result;
}
if (cached) {
this.queryCache.delete(selector);
}
return null;
}
static cacheResult(selector, result) {
this.queryCache.set(selector, {
result: [...result],
timestamp: Date.now()
});
}
static updateStats(selector, executionTime) {
this.stats.totalQueries++;
// Обновляем среднее время выполнения
this.stats.averageExecutionTime =
(this.stats.averageExecutionTime * (this.stats.totalQueries - 1) + executionTime) / this.stats.totalQueries;
// Обновляем самый медленный запрос
if (executionTime > this.stats.slowestQuery.time) {
this.stats.slowestQuery = { selector, time: executionTime };
}
// Обновляем частоту селекторов
const currentCount = this.selectorFrequency.get(selector) || 0;
this.selectorFrequency.set(selector, currentCount + 1);
// Обновляем топ селекторов
this.stats.mostFrequentSelectors = Array.from(this.selectorFrequency.entries())
.map(([sel, count]) => ({ selector: sel, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
}
static parseCSS(selector) {
// Улучшенный CSS парсер
const result = {};
// Убираем лишние пробелы и разбиваем по комбинаторам
const trimmed = selector.trim();
// Измененный regex, чтобы поддерживать селекторы, начинающиеся с комбинатора
const combinatorMatch = trimmed.match(/^([>+~])?\s*(.*)$/);
if (!combinatorMatch) {
this.parseSelectorPart(trimmed, result);
return result;
}
const [, combinator, rest] = combinatorMatch;
if (combinator) {
result.combinator = combinator;
const nextParts = rest.split(/\s*([>+~])\s*/);
result.next = this.parseCSS(nextParts.join(' '));
return result;
}
// Старая логика для селекторов, не начинающихся с комбинатора
const simpleMatch = trimmed.match(/^([^>+~\s]+)(?:\s*([>+~]|\s+)\s*(.+))?$/);
if (!simpleMatch) {
this.parseSelectorPart(trimmed, result);
return result;
}
const [, currentPart, simpleCombinator, simpleRest] = simpleMatch;
this.parseSelectorPart(currentPart, result);
if (simpleCombinator && simpleRest) {
result.combinator = simpleCombinator.trim() === '' ? ' ' : simpleCombinator.trim();
result.next = this.parseCSS(simpleRest);
}
return result;
}
static parseSelectorPart(part, result) {
let remaining = part;
// Парсим элемент, ID, классы, атрибуты и псевдо-классы
while (remaining) {
if (remaining.startsWith('#')) {
// ID селектор
const match = remaining.match(/^#([^.:\[]+)/);
if (match) {
result.id = match[1];
remaining = remaining.substring(match[0].length);
}
else {
break;
}
}
else if (remaining.startsWith('.')) {
// Class селектор
const match = remaining.match(/^\.([^.:\[#]+)/);
if (match) {
if (!result.classes)
result.classes = [];
result.classes.push(match[1]);
remaining = remaining.substring(match[0].length);
}
else {
break;
}
}
else if (remaining.startsWith('[')) {
// Атрибутный селектор
const match = remaining.match(/^\[([^=\]]+)(?:(=|~=|\|=|\^=|\$=|\*=)"?([^"\]]+)"?)?\]/);
if (match) {
if (!result.attributes)
result.attributes = [];
result.attributes.push({
name: match[1],
operator: match[2],
value: match[3]
});
remaining = remaining.substring(match[0].length);
}
else {
break;
}
}
else if (remaining.startsWith(':')) {
// Псевдо-класс
const match = remaining.match(/^:([^:(]+)(?:\(([^)]+)\))?/);
if (match) {
if (!result.pseudoClasses)
result.pseudoClasses = [];
result.pseudoClasses.push({
name: match[1],
argument: match[2]
});
remaining = remaining.substring(match[0].length);
}
else {
break;
}
}
else {
// Имя элемента (должно быть в начале)
const match = remaining.match(/^([a-zA-Z][a-zA-Z0-9-_]*)/);
if (match && !result.element && !result.id && !result.classes && !result.attributes && !result.pseudoClasses) {
result.element = match[1];
remaining = remaining.substring(match[0].length);
}
else {
break;
}
}
}
}
static executeCSS(root, parsed) {
// Phase 1: Find elements matching the first part of the selector.
// If the selector part is empty (e.g., '> .foo'), the initial context is just the root.
const isPartEmpty = !parsed.element && !parsed.id && !parsed.classes && !parsed.attributes && !parsed.pseudoClasses;
const initialMatches = isPartEmpty ? [root] : this.findDescendantsAndSelf(root, parsed);
// If there's no combinator, we're done.
if (!parsed.combinator || !parsed.next) {
return initialMatches;
}
// Phase 2: Apply combinator to find the next set of elements.
const finalMatches = new Set();
for (const element of initialMatches) {
switch (parsed.combinator) {
case '>':
const children = element.getAllElements();
for (const child of children) {
if (this.matchesParsedSelector(child, parsed.next)) {
finalMatches.add(child);
}
}
break;
case ' ':
const descendants = this.findDescendantsAndSelf(element, parsed.next);
// Exclude the element itself from its descendants
descendants.forEach(d => {
if (d.id !== element.id)
finalMatches.add(d);
});
break;
case '+':
const parentPlus = element.mainOwner;
if (parentPlus) {
const siblings = parentPlus.getAllElements();
const elementIndex = siblings.findIndex(el => el.id === element.id);
if (elementIndex > -1 && elementIndex + 1 < siblings.length) {
const nextSibling = siblings[elementIndex + 1];
if (this.matchesParsedSelector(nextSibling, parsed.next)) {
finalMatches.add(nextSibling);
}
}
}
break;
case '~':
const parentTilde = element.mainOwner;
if (parentTilde) {
const siblings = parentTilde.getAllElements();
const elementIndex = siblings.findIndex(el => el.id === element.id);
if (elementIndex > -1) {
for (let i = elementIndex + 1; i < siblings.length; i++) {
const nextSibling = siblings[i];
if (this.matchesParsedSelector(nextSibling, parsed.next)) {
finalMatches.add(nextSibling);
}
}
}
}
break;
}
}
return Array.from(finalMatches);
}
// Helper to find all descendants (and self) that match a simple selector part
static findDescendantsAndSelf(root, parsed) {
const results = [];
// Проверяем сам корневой элемент
if (this.matchesParsedSelector(root, parsed)) {
results.push(root);
}
// Рекурсивно ищем в потомках
const searchInChildren = (element) => {
const children = element.getAllElements();
for (const child of children) {
if (this.matchesParsedSelector(child, parsed)) {
results.push(child);
}
// Рекурсивно ищем в потомках
searchInChildren(child);
}
};
searchInChildren(root);
return results;
}
static findDescendantsByParsedSelector(root, parsed) {
const results = [];
// Рекурсивно ищем среди потомков, не включая сам root
const searchInChildren = (element) => {
const children = element.getAllElements();
for (const child of children) {
if (this.matchesParsedSelector(child, parsed)) {
results.push(child);
}
// Рекурсивно ищем в потомках
searchInChildren(child);
}
};
searchInChildren(root);
return results;
}
static matchesParsedSelector(element, parsed) {
// Проверяем имя элемента
if (parsed.element && element.name !== parsed.element) {
return false;
}
// Проверяем ID
if (parsed.id && element.getData('id') !== parsed.id) {
return false;
}
// Проверяем классы
if (parsed.classes) {
const elementClasses = element.getData('class');
if (!elementClasses)
return false;
const classList = typeof elementClasses === 'string' ? elementClasses.split(' ') : [];
for (const className of parsed.classes) {
if (!classList.includes(className)) {
return false;
}
}
}
// Проверяем атрибуты
if (parsed.attributes) {
for (const attr of parsed.attributes) {
const value = element.getData(attr.name);
// Если нет оператора, просто проверяем наличие атрибута
if (!attr.operator) {
if (value === undefined || value === null) {
return false;
}
continue;
}
// Если есть оператор, проверяем значение
if (!value)
return false;
if (attr.operator && attr.value) {
switch (attr.operator) {
case '=':
if (value !== attr.value)
return false;
break;
case '~=':
if (!value.toString().split(' ').includes(attr.value))
return false;
break;
case '|=':
if (!value.toString().startsWith(attr.value + '-') && value !== attr.value)
return false;
break;
case '^=':
if (!value.toString().startsWith(attr.value))
return false;
break;
case '$=':
if (!value.toString().endsWith(attr.value))
return false;
break;
case '*=':
if (!value.toString().includes(attr.value))
return false;
break;
}
}
}
}
// Проверяем псевдо-классы
if (parsed.pseudoClasses) {
for (const pseudo of parsed.pseudoClasses) {
if (!this.matchesPseudoClass(element, pseudo)) {
return false;
}
}
}
return true;
}
static matchesPseudoClass(element, pseudo) {
switch (pseudo.name) {
case 'first-child':
return element.index === 0;
case 'last-child':
const parent = element.mainOwner;
return parent ? element.index === parent.elementsCount() - 1 : true;
case 'nth-child':
if (pseudo.argument) {
const n = parseInt(pseudo.argument);
return element.index === n - 1; // CSS использует 1-based индексы
}
return false;
case 'empty':
return element.elementsCount() === 0;
case 'not':
// Реализация :not() селектора
if (pseudo.argument) {
const notParsed = this.parseCSS(pseudo.argument);
return !this.matchesParsedSelector(element, notParsed);
}
return false;
case 'has':
// Реализация :has() селектора
if (pseudo.argument) {
const hasParsed = this.parseCSS(pseudo.argument);
// Ищем только среди потомков, не включая сам элемент
const descendants = this.findDescendantsByParsedSelector(element, hasParsed);
return descendants.length > 0;
}
return false;
default:
return false;
}
}
static createXPathContext(root) {
// Этот метод больше не нужен, так как мы используем JSPath
// который работает с JSON-объектами напрямую.
return { root };
}
static evaluateXPath(context, expression) {
const rootElement = context.root;
// 1. Создаем карту всех элементов для быстрого доступа по ID
const elementMap = new Map();
const traverseAndMap = (element) => {
elementMap.set(element.id, element);
element.getAllElements().forEach(child => traverseAndMap(child));
};
traverseAndMap(rootElement);
// 2. Преобразуем структуру в JSON
const jsonObject = rootElement.toJSPathObject();
// 3. Применяем JSPath
let results = jspath.apply(expression, jsonObject);
if (results === undefined) {
results = [];
}
else if (!Array.isArray(results)) {
results = [results];
}
// 4. Сопоставляем результаты с реальными элементами CSElement
const finalElements = [];
const seenIds = new Set();
for (const res of results) {
if (res && typeof res === 'object' && res.___id) {
const elementId = res.___id;
if (!seenIds.has(elementId)) {
const element = elementMap.get(elementId);
if (element) {
finalElements.push(element);
seenIds.add(elementId);
}
}
}
}
return finalElements;
}
static traverseAndIndex(element, index, depth) {
// Добавляем в общий индекс
index.all.add(element);
// Индексируем по имени
if (element.name) {
if (!index.byName.has(element.name)) {
index.byName.set(element.name, new Set());
}
index.byName.get(element.name).add(element);
}
// Индексируем по ID
const id = element.getData('id');
if (id) {
index.byId.set(id, element);
}
// Индексируем по классам
const classes = element.getData('class');
if (classes) {
const classList = typeof classes === 'string' ? classes.split(' ') : [classes];
for (const className of classList) {
if (!index.byClass.has(className)) {
index.byClass.set(className, new Set());
}
index.byClass.get(className).add(element);
}
}
// Индексируем по атрибутам
const data = element.data;
for (const [key, value] of data) {
if (!index.byAttribute.has(key)) {
index.byAttribute.set(key, new Map());
}
const attrMap = index.byAttribute.get(key);
if (!attrMap.has(value)) {
attrMap.set(value, new Set());
}
attrMap.get(value).add(element);
}
// Индексируем по глубине
if (!index.byDepth.has(depth)) {
index.byDepth.set(depth, new Set());
}
index.byDepth.get(depth).add(element);
// Рекурсивно обрабатываем дочерние элементы
const children = element.getAllElements();
for (const child of children) {
this.traverseAndIndex(child, index, depth + 1);
}
}
// ===== Methods from old QueryEngine =====
/**
* Выполняет поиск по объекту селектора
*/
static queryBySelector(root, selector) {
const results = [];
this.traverseAndMatchObject(root, selector, results);
return results;
}
/**
* Рекурсивно обходит дерево и собирает подходящие элементы для объектного селектора
*/
static traverseAndMatchObject(element, selector, results) {
if (this.matchesObjectSelector(element, selector)) {
results.push(element);
}
const children = element.getAllElements();
for (const child of children) {
this.traverseAndMatchObject(child, selector, results);
}
}
/**
* Проверяет, соответствует ли элемент объектному селектору
*/
static matchesObjectSelector(element, selector) {
if (selector.name !== undefined && element.name !== selector.name) {
return false;
}
if (selector.index !== undefined && element.index !== selector.index) {
return false;
}
if (selector.hasData) {
for (const key of selector.hasData) {
if (!element.getData(key)) {
return false;
}
}
}
if (selector.depth !== undefined) {
const depth = this.getElementDepth(element);
if (typeof selector.depth === 'number') {
if (depth !== selector.depth)
return false;
}
else {
const { min, max } = selector.depth;
if (min !== undefined && depth < min)
return false;
if (max !== undefined && depth > max)
return false;
}
}
if (selector.custom && !selector.custom(element)) {
return false;
}
return true;
}
/**
* Вычисляет глубину элемента в дереве
*/
static getElementDepth(element) {
let depth = 0;
let current = element.mainOwner;
while (current) {
depth++;
current = current.mainOwner;
}
return depth;
}
/**
* Создает комплексный селектор для поиска элементов
*/
static createSelector() {
return new SelectorBuilder();
}
}
QueryEngine.indices = new WeakMap();
QueryEngine.queryCache = new Map();
QueryEngine.cacheTimeout = 5000; // 5 секунд
QueryEngine.stats = {
totalQueries: 0,
cacheHits: 0,
averageExecutionTime: 0,
slowestQuery: { selector: '', time: 0 },
mostFrequentSelectors: []
};
QueryEngine.selectorFrequency = new Map();
/**
* Builder для создания сложных селекторов
*/
class SelectorBuilder {
constructor() {
this.selector = {};
}
withName(name) {
this.selector.name = name;
return this;
}
withIndex(index) {
this.selector.index = index;
return this;
}
withData(...keys) {
this.selector.hasData = [...(this.selector.hasData || []), ...keys];
return this;
}
withDepth(depth) {
this.selector.depth = depth;
return this;
}
withCustom(predicate) {
const existing = this.selector.custom;
if (existing) {
this.selector.custom = (element) => existing(element) && predicate(element);
}
else {
this.selector.custom = predicate;
}
return this;
}
build() {
return { ...this.selector };
}
}
/**
* Предопределенные селекторы
*/
const CommonSelectors = {
byName: (name) => ({ name }),
byIndex: (index) => ({ index }),
leaves: () => ({
custom: (element) => element.elementsCount() === 0
}),
roots: () => ({
custom: (element) => element.mainOwner === null
}),
withChildrenCount: (count) => ({
custom: (element) => element.elementsCount() === count
}),
atDepth: (depth) => ({ depth }),
withDataType: (key, type) => ({
custom: (element) => typeof element.getData(key) === type
})
};
/**
* Реализация менеджера Live queries (реактивных запросов)
*/
class LiveQueryManagerImpl extends EventEmitter {
constructor() {
super();
this.queries = new Map();
this.subscriptions = new Map();
this.debounceTimers = new Map();
this.updateBatches = new Map();
this.indexes = new Map();
this.CSElementClass = null;
this.stats = this.createEmptyStats();
}
setCSElementClass(cls) {
this.CSElementClass = cls;
}
createLiveQuery(selector, options = {}, config = {}) {
const id = generateId();
const query = {
id,
selector,
options,
config: {
autoStart: true,
debounce: 100,
cache: true,
cacheTTL: 5000,
deep: false,
batch: true,
...config
},
results: [],
active: false,
lastUpdated: 0,
updateCount: 0,
watchedElements: new Set(),
filters: [],
transforms: [],
sort: undefined,
onResults: undefined,
onError: undefined
};
this.queries.set(id, query);
this.subscriptions.set(id, new Map());
this.stats.totalQueries++;
// Обновляем статистику селекторов
this.updateSelectorStats(selector);
// Автоматический запуск если включен
if (query.config.autoStart) {
this.start(id);
}
this.emit('query-created', { queryId: id, query });
this.emitEvent({
type: 'query-created',
queryId: id,
data: { selector, options, config },
timestamp: Date.now()
});
return query;
}
start(queryId) {
const query = this.queries.get(queryId);
if (!query || query.active) {
return;
}
query.active = true;
this.stats.activeQueries++;
// Выполняем первоначальный запрос
this.executeQuery(queryId);
// Настраиваем наблюдение за изменениями
this.setupQueryWatching(query);
this.emitEvent({
type: 'query-started',
queryId,
timestamp: Date.now()
});
}
stop(queryId) {
const query = this.queries.get(queryId);
if (!query || !query.active) {
return;
}
query.active = false;
this.stats.activeQueries--;
// Останавливаем наблюдение
this.stopQueryWatching(query);
// Очищаем таймеры debounce
const timer = this.debounceTimers.get(queryId);
if (timer) {
clearTimeout(timer);
this.debounceTimers.delete(queryId);
}
this.emitEvent({
type: 'query-stopped',
queryId,
timestamp: Date.now()
});
}
getLiveQuery(queryId) {
return this.queries.get(queryId);
}
getAllLiveQueries() {
return Array.from(this.queries.values());
}
removeLiveQuery(queryId) {
const query = this.queries.get(queryId);
if (!query) {
return false;
}
// Останавливаем запрос
if (query.active) {
this.stop(queryId);
}
// Удаляем все подписки
this.subscriptions.delete(queryId);
// Удаляем запрос
this.queries.delete(queryId);
this.stats.totalQueries--;
this.emitEvent({
type: 'query-removed',
queryId,
timestamp: Date.now()
});
return true;
}
updateQuery(queryId) {
const query = this.queries.get(queryId);
if (!query || !query.active) {
return;
}
if (query.config.debounce && query.config.debounce > 0) {
// Используем debounce
const existingTimer = this.debounceTimers.get(queryId);
if (existingTimer) {
clearTimeout(existingTimer);
}
const timer = setTimeout(() => {
this.executeQuery(queryId);
this.debounceTimers.delete(queryId);
}, query.config.debounce);
this.debounceTimers.set(queryId, timer);
}
else {
// Немедленное обновление
this.executeQuery(queryId);
}
}
updateAllQueries() {
for (const [queryId, query] of this.queries) {
if (query.active) {
this.updateQuery(queryId);
}
}
}
addFilter(queryId, filter) {
const query = this.queries.get(queryId);
if (query) {
query.filters.push(filter);
this.updateQuery(queryId);
}
}
removeFilter(queryId, filterIndex) {
const query = this.queries.get(queryId);
if (query && filterIndex >= 0 && filterIndex < query.filters.length) {
query.filters.splice(filterIndex, 1);
this.updateQuery(queryId);
}
}
addTransform(queryId, transform) {
const query = this.queries.get(queryId);
if (query) {
query.transforms.push(transform);
this.updateQuery(queryId);
}
}
setSort(queryId, sort) {
const query = this.queries.get(queryId);
if (query) {
query.sort = sort;
this.updateQuery(queryId);
}
}
subscribe(queryId, callback) {
const subscriptionId = generateId();
const subscriptions = this.subscriptions.get(queryId);
if (subscriptions) {
const subscription = {
id: subscriptionId,
queryId,
callback,
active: true,
createdAt: Date.now()
};
subscriptions.set(subscriptionId, subscription);
}
return subscriptionId;
}
unsubscribe(queryId, subscriptionId) {
const subscriptions = this.subscriptions.get(queryId);
if (subscriptions) {
return subscriptions.delete(subscriptionId);
}
return false;
}
getStats() {
// Обновляем актуальную статистику
this.updateStats();
return { ...this.stats };
}
clear() {
// Останавливаем все активные запросы
for (const [queryId, query] of this.queries) {
if (query.active) {
this.stop(queryId);
}
}
// Очищаем все данные
this.queries.clear();
this.subscriptions.clear();
this.debounceTimers.clear();
this.updateBatches.clear();
this.indexes.clear();
// Сбрасываем статистику
this.stats = this.createEmptyStats();
}
notifyElementChange(element, _changeType) {
// Находим все запросы, которые могут быть затронуты этим изменением
const affectedQueries = this.findAffectedQueries(element);
for (const queryId of affectedQueries) {
this.updateQuery(queryId);
}
}
notifyDataChange(element, key, _newValue, _oldValue) {
// Находим запросы, которые используют этот ключ данных
const affectedQueries = this.findQueriesUsingDataKey(element, key);
for (const queryId of affectedQueries) {
this.updateQuery(queryId);
}
}
// Приватные методы
async executeQuery(queryId) {
const query = this.queries.get(queryId);
if (!query) {
return;
}
const startTime = Date.now();
try {
// Выполняем базовый запрос
let results = await this.performQuery(query);
// Применяем фильтры
for (const filter of query.filters) {
results = results.filter(filter);
}
// Применяем трансформации
for (const transform of query.transforms) {
results = results.map(transform);
}
// Применяем сортировку
if (query.sort) {
results.sort(query.sort);
}
// Применяем лимит
if (query.config.limit && query.config.limit > 0) {
results = results.slice(0, query.config.limit);
}
// Обновляем результаты
const oldResults = query.results;
query.results = results;
query.lastUpdated = Date.now();
query.updateCount++;
// Обновляем статистику
this.stats.totalUpdates++;
const executionTime = Date.now() - startTime;
this.updateExecutionTimeStats(executionTime);
// Уведомляем подписчиков
this.notifySubscribers(queryId, results, query);
// Вызываем callback если установлен
if (query.onResults) {
query.onResults(results, query);
}
this.emitEvent({
type: 'query-updated',
queryId,
data: {
results,
oldResults,
executionTime,
changeCount: this.calculateChangeCount(oldResults, results)
},
timestamp: Date.now()
});
this.emitEvent({
type: 'results-changed',
queryId,
data: { results, executionTime },
timestamp: Date.now()
});
}
catch (error) {
// Обработка ошибок
if (query.onError) {
query.onError(error, query);
}
this.emit('query-error', { queryId, error });
console.error(`Live query ${queryId} error:`, error);
}
}
async performQuery(query) {
if (!this.CSElementClass) {
throw new Error('CSElementClass has not been set on LiveQueryManager.');
}
try {
const root = query.options?.root;
if (!root) {
// Fallback to searching all elements if no root is provided.
const allElements = this.CSElementClass.getAllElements();
const results = [];
allElements.forEach((el) => {
results.push(...QueryEngine.query(el, query.selector));
});
return Array.from(new Set(results)); // Remove duplicates
}
// Если есть корневой элемент, ищем только в его контексте
return QueryEngine.query(root, query.selector);
}
catch (error) {
console.warn('Error executing query:', error);
return [];
}
}
setupQueryWatching(_query) {
// Эта логика теперь полностью управляется через
// вызовы notifyElementChange и notifyDataChange,
// которые инициируют updateQuery.
// Использование computed здесь было бы некорректным,
// так как система реактивности не отслеживает
// добавление/удаление дочерних элементов напрямую.
}
stopQueryWatching(_query) {
// Останавливаем наблюдение за элементами
// Это будет реализовано при интеграции с ReactivityManager
}
findAffectedQueries(element) {
const affected = [];
for (const [queryId, query] of this.queries) {
if (query.active && this.queryAffectedByElement(query, element)) {
affected.push(queryId);
}
}
return affected;
}
findQueriesUsingDataKey(_element, key) {
const affected = [];
for (const [queryId, query] of this.queries) {
if (query.active && this.queryUsesDataKey(query, key)) {
affected.push(queryId);
}
}
return affected;
}
queryAffectedByElement(query, element) {
// Проверяем, может ли изменение элемента повлиять на результаты запроса
// Это упрощенная логика, в реальности будет более сложная
return query.watchedElements.has(element.id) ||
query.selector.includes(element.name) ||
query.selector.includes('*');
}
queryUsesDataKey(query, key) {
// Проверяем, использует ли запрос определенный ключ данных
return query.selector.includes(`[${key}]`) ||
query.selector.includes(`data-${key}`) ||
query.selector.includes(`@${key}`);
}
notifySubscribers(queryId, results, query) {
const subscriptions = this.subscriptions.get(queryId);
if (!subscriptions) {
return;
}
for (const subscription of subscriptions.values()) {
if (subscription.active) {
try {
subscription.callback(results, query);
}
catch (error) {
console.error(`Subscription callback error for query ${queryId}:`, error);
}
}
}
}
calculateChangeCount(oldResults, newResults) {
// Простой подсчет изменений
if (oldResults.length !== newResults.length) {
return Math.abs(oldResults.length - newResults.length);
}
let changes = 0;
for (let i = 0; i < oldResults.length; i++) {
if (oldResults[i] !== newResults[i]) {
changes++;
}
}
return changes;
}
updateSelectorStats(selector) {
let selectorStats = this.stats.selectorStats.get(selector);
if (!selectorStats) {
selectorStats = {
selector,
queryCount: 0,
averageResults: 0,
lastUsed: Date.now(),
totalExecutionTime: 0
};
this.stats.selectorStats.set(selector, selectorStats);
}
selectorStats.queryCount++;
selectorStats.lastUsed = Date.now();
}
updateExecutionTimeStats(executionTime) {
const currentAverage = this.stats.averageQueryTime;
const totalQueries = this.stats.totalUpdates;
this.stats.averageQueryTime =
(currentAverage * (totalQueries - 1) + executionTime) / totalQueries;
}
updateStats() {
this.stats.watchedElements = 0;
this.stats.memoryUsage = this.calculateMemoryUsage();
for (const query of this.queries.values()) {
this.stats.watchedElements += query.watchedElements.size;
}
}
calculateMemoryUsage() {
// Упрощенный расчет использования памяти
let usage = 0;
for (const query of this.queries.values()) {
usage += JSON.stringify(query.results).length;
usage += query.selector.length;
usage += query.watchedElements.size * 50; // Примерный размер ID элемента
}
return usage;
}
createEmptyStats() {
return {
totalQueries: 0,
activeQueries: 0,
totalUpdates: 0,
averageQueryTime: 0,
memoryUsage: 0,
watchedElements: 0,
selectorStats: new Map()
};
}
emitEvent(event) {
this.emit('event', event);
this.emit(event.type, event);
}
}
// Builder для удобного создания Live queries
class LiveQueryBuilderImpl {
constructor(manager) {
this._selector = '';
this._options = {};
this._config = {};
this._filters = [];
this._transforms = [];
this.manager = manager;
}
selector(selector) {
this._selector = selector;
return this;
}
options(options) {
this._options = { ...this._options, ...options };
return this;
}
config(config) {
this._config = { ...this._config, ...config };
return this;
}
filter(filter) {
this._filters.push(filter);
return this;
}
transform(transform) {
this._transforms.push(transform);
return this;
}
sort(sort) {
this._sort = sort;
return this;
}
limit(limit) {
this._config.limit = limit;
return this;
}
debounce(ms) {
this._config.debounce = ms;
return this;
}
cache(ttl) {
this._config.cache = true;
if (ttl !== undefined) {
this._config.cacheTTL = ttl;
}
return this;
}
subscribe(callback) {
this._onResults = callback;
return this;
}
onError(callback) {
this._onError = callback;
return this;
}
start() {
const query = this.build();
this.manager.start(query.id);
return query;
}
build() {
const query = this.manager.createLiveQuery(this._selector, this._options, { ...this._config, autoStart: false });
// Применяем фильтры, трансформации и сортировку
for (const filter of this._filters) {
this.manager.addFilter(query.id, filter);
}
for (const transform of this._transforms) {
this.manager.addTransform(query.id, transform);
}
if (this._sort) {
this.manager.setSort(query.id, this._sort);
}
// Устанавливаем callbacks
if (this._onResults) {
query.onResults = this._onResults;
}
if (this._onError) {
query.onError = this._onError;
}
return query;
}
}
/**
* Асинхронная блокировка для обеспечения потокобезопасности
*/
class AsyncLock {
constructor() {
this._queue = [];
this._locked = false;
}
/**
* Получить блокировку
*/
async acquire() {
return new Promise((resolve) => {
if (!this._locked) {
this._locked = true;
resolve();
}
else {
this._queue.push(resolve);
}
});
}
/**
* Освободить блокировку
*/
release() {
if (!this._locked) {
throw new Error('Cannot release an unlocked lock');
}
const next = this._queue.shift();
if (next) {
next();
}
else {
this._locked = false;
}
}
/**
* Проверить заблокирована ли блокировка
*/
isLocked() {
return this._locked;
}
/**
* Выполнить функцию с блокировкой
*/
async withLock(fn) {
await this.acquire();
try {
return await fn();
}
finally {
this.release();
}
}
}
var joiBrowser_min = {exports: {}};
(function (module, exports) {
!function(e,t){module.exports=t();}(self,(()=>{return e={7629:(e,t,r)=>{const s=r(375),n=r(8571),a=r(9474),i=r(1687),o=r(8652),l=r(8160),c=r(3292),u=r(6354),f=r(8901),m=r(9708),h=r(6914),d=r(2294),p=r(6133),g=r(1152),y=r(8863),b=r(2036),v={Base:class{constructor(e){this.type=e,this.$_root=null,this._definition={},this._reset();}_reset(){this._ids=new d.Ids,this._preferences=null,this._refs=new p.Manager,this._cache=null,this._valids=null,this._invalids=null,this._flags={},this._rules=[],this._singleRules=new Map,this.$_terms={},this.$_temp={ruleset:null,whens:{}};}describe(){return s("function"==typeof m.describe,"Manifest functionality disabled"),m.describe(this)}allow(...e){return l.verifyFlat(e,"allow"),this._values(e,"_valids")}alter(e){s(e&&"object"==typeof e&&!Array.isArray(e),"Invalid targets argument"),s(!this._inRuleset(),"Cannot set alterations inside a ruleset");const t=this.clone();t.$_terms.alterations=t.$_terms.alterations||[];for(const r in e){const n=e[r];s("function"==typeof n,"Alteration adjuster for",r,"must be a function"),t.$_terms.alterations.push({target:r,adjuster:n});}return t.$_temp.ruleset=!1,t}artifact(e){return s(void 0!==e,"Artifact cannot be undefined"),s(!this._cache,"Cannot set an artifact with a rule cache"),this.$_setFlag("artifact",e)}cast(e){return s(!1===e||"string"==typeof e,"Invalid to value"),s(!1===e||this._definition.cast[e],"Type",this.type,"does not support casting to",e),this.$_setFlag("cast",!1===e?void 0:e)}default(e,t){return this._default("default",e,t)}description(e){return s(e&&"string"==typeof e,"Description must be a non-empty string"),this.$_setFlag("description",e)}empty(e){const t=this.clone();return void 0!==e&&(e=t.$_compile(e,{override:!1})),t.$_setFlag("empty",e,{clone:!1})}error(e){return s(e,"Missing error"),s(e instanceof Error||"function"==typeof e,"Must provide a valid Error object or a function"),this.$_setFlag("error",e)}example(e,t={}){return s(void 0!==e,"Missing example"),l.assertOptions(t,["override"]),this._inner("examples",e,{single:!0,override:t.override})}external(e,t){return "object"==typeof e&&(s(!t,"Cannot combine options with description"),t=e.description,e=e.method),s("function"==typeof e,"Method must be a function"),s(void 0===t||t&&"string"==typeof t,"Description must be a non-empty string"),this._inner("externals",{method:e,description:t},{single:!0})}failover(e,t){return this._default("failover",e,t)}forbidden(){return this.presence("forbidden")}id(e){return e?(s("string"==typeof e,"id must be a non-empty string"),s(/^[^\.]+$/.test(e),"id cannot contain period character"),this.$_setFlag("id",e)):this.$_setFlag("id",void 0)}invalid(...e){return this._values(e,"_invalids")}label(e){return s(e&&"string"==typeof e,"Label name must be a non-empty string"),this.$_setFlag("label",e)}meta(e){return s(void 0!==e,"Meta cannot be undefined"),this._inner("metas",e,{single:!0})}note(...e){s(e.length,"Missing notes");for(const t of e)s(t&&"string"==typeof t,"Notes must be non-empty strings");return this._inner("notes",e)}only(e=!0){return s("boolean"==typeof e,"Invalid mode:",e),this.$_setFlag("only",e)}optional(){return this.presence("optional")}prefs(e){s(e,"Missing preferences"),s(void 0===e.context,"Cannot override context"),s(void 0===e.externals,"Cannot override externals"),s(void 0===e.warnings,"Cannot override warnings"),s(void 0===e.debug,"Cannot override debug"),l.checkPreferences(e);const t=this.clone();return t._preferences=l.preferences(t._preferences,e),t}presence(e){return s(["optional","required","forbidden"].includes(e),"Unknown presence mode",e),this.$_setFlag("presence",e)}raw(e=!0){return this.$_setFlag("result",e?"raw":void 0)}result(e){return s(["raw","strip"].includes(e),"Unknown result mode",e),this.$_setFlag("result",e)}required(){return this.presence("required")}strict(e){const t=this.clone(),r=void 0!==e&&!e;return t._preferences=l.preferences(t._preferences,{convert:r}),t}strip(e=!0){return this.$_setFlag("result",e?"strip":void 0)}tag(...e){s(e.length,"Missing tags");for(const t of e)s(t&&"string"==typeof t,"Tags must be non-empty strings");return this._inner("tags",e)}unit(e){return s(e&&"string"==typeof e,"Unit name must be a non-empty string"),this.$_setFlag("unit",e)}valid(...e){l.verifyFlat(e,"valid");const t=this.allow(...e);return t.$_setFlag("only",!!t._valids,{clone:!1}),t}when(e,t){const r=this.clone();r.$_terms.whens||(r.$_terms.whens=[]);const n=c.when(r,e,t);if(!["any","link"].includes(r.type)){const e=n.is?[n]:n.switch;for(const t of e)s(!t.then||"any"===t.then.type||t.then.type===r.type,"Cannot combine",r.type,"with",t.then&&t.then.type),s(!t.otherwise||"any"===t.otherwise.type||t.otherwise.type===r.type,"Cannot combine",r.type,"with",t.otherwise&&t.otherwise.type);}return r.$_terms.whens.push(n),r.$_mutateRebuild()}cache(e){s(!this._inRuleset(),"Cannot set caching inside a ruleset"),s(!this._cache,"Cannot override schema cache"),s(void 0===this._flags.artifact,"Cannot cache a rule with an artifact");const t=this.clone();return t._cache=e||o.provider.provision(),t.$_temp.ruleset=!1,t}clone(){const e=Object.create(Object.getPrototypeOf(this));return this._assign(e)}concat(e){s(l.isSchema(e),"Invalid schema object"),s("any"===this.type||"any"===e.type||e.type===this.type,"Cannot merge type",this.type,"with another type:",e.type),s(!this._inRuleset(),"Cannot concatenate onto a schema with open ruleset"),s(!e._inRuleset(),"Cannot concatenate a schema with open ruleset");let t=this.clone();if("any"===this.type&&"any"!==e.type){const r=e.clone();for(const e of Object.keys(t))"type"!==e&&(r[e]=t[e]);t=r;}t._ids.concat(e._ids),t._refs.register(e,p.toSibling),t._preferences=t._preferences?l.preferences(t._preferences,e._preferences):e._preferences,t._valids=b.merge(t._valids,e._valids,e._invalids),t._invalids=b.merge(t._invalids,e._invalids,e._valids);for(const r of e._singleRules.keys())t._singleRules.has(r)&&(t._rules=t._rules.filter((e=>e.keep||e.name!==r)),t._singleRules.delete(r));for(const r of e._rules)e._definition.rules[r.method].multi||t._singleRules.set(r.name,r),t._rules.push(r);if(t._flags.empty&&e._flags.empty){t._flags.empty=t._flags.empty.concat(e._flags.empty);const r=Object.assign({},e._flags);delete r.empty,i(t._flags,r);}else if(e._flags.empty){t._flags.empty=e._flags.empty;const r=Object.assign({},e._flags);delete r.empty,i(t._flags,r);}else i(t._flags,e._flags);for(const r in e.$_terms){const s=e.$_terms[r];s?t.$_terms[r]?t.$_terms[r]=t.$_terms[r].concat(s):t.$_terms[r]=s.slice():t.$_terms[r]||(t.$_terms[r]=s);}return this.$_root._tracer&&this.$_root._tracer._combine(t,[this,e]),t.$_mutateRebuild()}extend(e){return s(!e.base,"Cannot extend type with another base"),f.type(this,e)}extract(e){return e=Array.isArray(e)?e:e.split("."),this._ids.reach(e)}fork(e,t){s(!this._inRuleset(),"Cannot fork inside a ruleset");let r=this;for(let s of [].concat(e))s=Array.isArray(s)?s:s.split("."),r=r._ids.fork(s,t,r);return r.$_temp.ruleset=!1,r}rule(e){const t=this._definition;l.assertOptions(e,Object.keys(t.modifiers)),s(!1!==this.$_temp.ruleset,"Cannot apply rules to empty ruleset or the last rule added does not support rule properties");const r=null===this.$_temp.ruleset?this._rules.length-1:this.$_temp.ruleset;s(r>=0&&r<this._rules.length,"Cannot apply rules to empty ruleset");const a=this.clone();for(let i=r;i<a._rules.length;++i){const r=a._rules[i],o=n(r);for(const n in e)t.modifiers[n](o,e[n]),s(o.name===r.name,"Cannot change rule name");a._rules[i]=o,a._singleRules.get(o.name)===r&&a._singleRules.set(o.name,o);}return a.$_temp.ruleset=!1,a.$_mutateRebuild()}get ruleset(){s(!this._inRuleset(),"Cannot start a new ruleset without closing the previous one");const e=this.clone();return e.$_temp.ruleset=e._rules.length,e}get $(){return this.ruleset}tailor(e){e=[].concat(e),s(!this._inRuleset(),"Cannot tailor inside a ruleset");let t=this;if(this.$_terms.alterations)for(const{target:r,adjuster:n}of this.$_terms.alterations)e.includes(r)&&(t=n(t),s(l.isSchema(t),"Alteration adjuster for",r,"failed to return a schema object"));return t=t.$_modify({each:t=>t.tailor(e),ref:!1}),t.$_temp.ruleset=!1,t.$_mutateRebuild()}tracer(){return g.location?g.location(this):this}validate(e,t){return y.entry(e,this,t)}validateAsync(e,t){return y.entryAsync(e,this,t)}$_addRule(e){"string"==typeof e&&(e={name:e}),s(e&&"object"==typeof e,"Invalid options"),s(e.name&&"string"==typeof e.name,"Invalid rule name");for(const t in e)s("_"!==t[0],"Cannot set private rule properties");const t=Object.assign({},e);t._resolve=[],t.method=t.method||t.name;const r=this._definition.rules[t.method],n=t.args;s(r,"Unknown rule",t.method);const a=this.clone();if(n){s(1===Object.keys(n).length||Object.keys(n).length===this._definition.rules[t.name].args.length,"Invalid rule definition for",this.type,t.name);for(const e in n){let i=n[e];if(r.argsByName){const o=r.argsByName.get(e);if(o.ref&&l.isResolvable(i))t._resolve.push(e),a.$_mutateRegister(i);else if(o.normalize&&(i=o.normalize(i),n[e]=i),o.assert){const t=l.validateArg(i,e,o);s(!t,t,"or reference");}}void 0!==i?n[e]=i:delete n[e];}}return r.multi||(a._ruleRemove(t.name,{clone:!1}),a._singleRules.set(t.name,t)),!1===a.$_temp.ruleset&&(a.$_temp.ruleset=null),r.priority?a._rules.unshift(t):a._rules.push(t),a}$_compile(e,t){return c.schema(this.$_root,e,t)}$_createError(e,t,r,s,n,a={}){const i=!1!==a.flags?this._flags:{},o=a.messages?h.merge(this._definition.messages,a.messages):this._definition.messages;return new u.Report(e,t,r,i,o,s,n)}$_getFlag(e){return this._flags[e]}$_getRule(e){return this._singleRules.get(e)}$_mapLabels(e){return e=Array.isArray(e)?e:e.split("."),this._ids.labels(e)}$_match(e,t,r,s){(r=Object.assign({},r)).abortEarly=!0,r._externals=!1,t.snapshot();const n=!y.validate(e,this,t,r,s).errors;return t.restore(),n}$_modify(e){return l.assertOptions(e,["each","once","ref","schema"]),d.schema(this,e)||this}$_mutateRebuild(){return s(!this._inRuleset(),"Cannot add this rule inside a ruleset"),this._refs.reset(),this._ids.reset(),this.$_modify({each:(e,{source:t,name:r,path:s,key:n})=>{const a=this._definition[t][r]&&this._definition[t][r].register;!1!==a&&this.$_mutateRegister(e,{family:a,key:n});}}),this._definition.rebuild&&this._definition.rebuild(this),this.$_temp.ruleset=!1,this}$_mutateRegister(e,{family:t,key:r}={}){this._refs.register(e,t),this._ids.register(e,{key:r});}$_property(e){return this._definition.properties[e]}$_reach(e){return this._ids.reach(e)}$_rootReferences(){return this._refs.roots()}$_setFlag(e,t,r={}){s("_"===e[0]||!this._inRuleset(),"Cannot set flag inside a ruleset");const n=this._definition.flags[e]||{};if(a(t,n.default)&&(t=void 0),a(t,this._flags[e]))return this;const i=!1!==r.clone?this.clone():this;return void 0!==t?(i._flags[e]=t,i.$_mutateRegister(t)):delete i._flags[e],"_"!==e[0]&&(i.$_temp.ruleset=!1),i}$_parent(e,...t){return this[e][l.symbols.parent].call(this,...t)}$_validate(e,t,r){return y.validate(e,this,t,r)}_assign(e){e.type=this.type,e.$_root=this.$_root,e.$_temp=Object.assign({},this.$_temp),e.$_temp.whens={},e._ids=this._ids.clone(),e._preferences=this._preferences,e._valids=this._valids&&this._valids.clone(),e._invalids=this._invalids&&this._invalids.clone(),e._rules=this._rules.slice(),e._singleRules=n(this._singleRules,{shallow:!0}),e._refs=this._refs.clone(),e._flags=Object.assign({},this._flags),e._cache=null,e.$_terms={};for(const t in this.$_terms)e.$_terms[t]=this.$_terms[t]?this.$_terms[t].slice():null;e.$_super={};for(const t in this.$_super)e.$_super[t]=this._super[t].bind(e);return e}_bare(){const e=this.clone();e._reset();const t=e._definition.terms;for(const r in t){const s=t[r];e.$_terms[r]=s.init;}return e.$_mutateRebuild()}_default(e,t,r={}){return l.assertOptions(r,"literal"),s(void 0!==t,"Missing",e,"value"),s("function"==typeof t||!r.literal,"Only function value supports literal option"),"function"==typeof t&&r.literal&&(t={[l.symbols.literal]:!0,literal:t}),this.$_setFlag(e,t)}_generate(e,t,r){if(!this.$_terms.whens)return {schema:this};const s=[],n=[];for(let a=0;a<this.$_terms.whens.length;++a){const i=this.$_terms.whens[a];if(i.concat){s.push(i.concat),n.push(`${a}.concat`);continue}const o=i.ref?i.ref.resolve(e,t,r):e,l=i.is?[i]:i.switch,c=n.length;for(let c=0;c<l.length;++c){const{is:u,then:f,otherwise:m}=l[c],h=`${a}${i.switch?"."+c:""}`;if(u.$_match(o,t.nest(u,`${h}.is`),r)){if(f){const a=t.localize([...t.path,`${h}.then`],t.ancestors,t.schemas),{schema:i,id:o}=f._generate(e,a,r);s.push(i),n.push(`${h}.then${o?`(${o})`:""}`);break}}else if(m){const a=t.localize([...t.path,`${h}.otherwise`],t.ancestors,t.schemas),{schema:i,id:o}=m._generate(e,a,r);s.push(i),n.push(`${h}.otherwise${o?`(${o})`:""}`);break}}if(i.break&&n.length>c)break}const a=n.join(", ");if(t.mainstay.tracer.debug(t,"rule","when",a),!a)return {schema:this};if(!t.mainstay.tracer.active&&this.$_temp.whens[a])return {schema:this.$_temp.whens[a],id:a};let i=this;this._definition.generate&&(i=this._definition.generate(this,e,t,r));for(const e of s)i=i.concat(e);return this.$_root._tracer&&this.$_root._tracer._combine(i,[this,...s]),this.$_temp.whens[a]=i,{schema:i,id:a}}_inner(e,t,r={}){s(!this._inRuleset(),`Cannot set ${e} inside a ruleset`);const n=this.clone();return n.$_terms[e]&&!r.override||(n.$_terms[e]=[]),r.single?n.$_terms[e].push(t):n.$_terms[e].push(...t),n.$_temp.ruleset=!1,n}_inRuleset(){return null!==this.$_temp.ruleset&&!1!==this.$_temp.ruleset}_ruleRemove(e,t={}){if(!this._singleRules.has(e))return this;const r=!1!==t.clone?this.clone():this;r._singleRules.delete(e);const s=[];for(let t=0;t<r._rules.length;++t){const n=r._rules[t];n.name!==e||n.keep?s.push(n):r._inRuleset()&&t<r.$_temp.ruleset&&--r.$_temp.ruleset;}return r._rules=s,r}_values(e,t){l.verifyFlat(e,t.slice(1,-1));const r=this.clone(),n=e[0]===l.symbols.override;if(n&&(e=e.slice(1)),!r[t]&&e.length?r[t]=new b:n&&(r[t]=e.length?new b:null,r.$_mutateRebuild()),!r[t])return r;n&&r[t].override();for(const n of e){s(void 0!==n,"Cannot call allow/valid/invalid with undefined"),s(n!==l.symbols.override,"Override must be the first value");const e="_invalids"===t?"_valids":"_invalids";r[e]&&(r[e].remove(n),r[e].length||(s("_valids"===t||!r._flags.only,"Setting invalid value",n,"leaves schema rejecting all values due to previous valid rule"),r[e]=null)),r[t].add(n,r._refs);}return r}}};v.Base.prototype[l.symbols.any]={version:l.version,compile:c.compile,root:"$_root"},v.Base.prototype.isImmutable=!0,v.Base.prototype.deny=v.Base.prototype.invalid,v.Base.prototype.disallow=v.Base.prototype.invalid,v.Base.prototype.equal=v.Base.prototype.valid,v.Base.prototype.exist=v.Base.prototype.required,v.Base.prototype.not=v.Base.prototype.invalid,v.Base.prototype.options=v.Base.prototype.prefs,v.Base.prototype.preferences=v.Base.prototype.prefs,e.exports=new v.Base;},8652:(e,t,r)=>{const s=r(375),n=r(8571),a=r(8160),i={max:1e3,supported:new Set(["undefined","boolean","number","string"])};t.provider={provision:e=>new i.Cache(e)},i.Cache=class{constructor(e={}){a.assertOptions(e,["max"]),s(void 0===e.max||e.max&&e.max>0&&isFinite(e.max),"Invalid max cache size"),this._max=e.max||i.max,this._map=new Map,this._list=new i.List;}get length(){return this._map.size}set(e,t){if(null!==e&&!i.supported.has(typeof e))return;let r=this._map.get(e);if(r)return r.value=t,void this._list.first(r);r=this._list.unshift({key:e,value:t}),this._map.set(e,r),this._compact();}get(e){const t=this._map.get(e);if(t)return this._list.first(t),n(t.value)}_compact(){if(this._map.size>this._max){const e=this._list.pop();this._map.delete(e.key);}}},i.List=class{constructor(){this.tail=null,this.head=null;}unshift(e){return e.next=null,e.prev=this.head,this.head&&(this.head.next=e),this.head=e,this.tail||(this.tail=e),e}first(e){e!==this.head&&(this._remove(e),this.unshift(e));}pop(){return this._remove(this.tail)}_remove(e){const{next:t,prev:r}=e;return t.prev=r,r&&(r.next=t),e===this.tail&&(this.tail=t),e.prev=null,e.next=null,e}};},8160:(e,t,r)=>{const s=r(375),n=r(7916),a=r(5934);let i,o;const l={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)?)?)?)?$/};t.version=a.version,t.defaults={abortEarly:!0,allowUnknown:!1,artifacts:!1,cache:!0,context:null,convert:!0,dateFormat:"iso",errors:{escapeHtml:!1,label:"path",language:null,render:!0,stack:!1,wrap:{label:'"',array:"[]"}},externals:!0,messages:{},nonEnumerables:!1,noDefaults:!1,presence:"optional",skipFunctions:!1,stripUnknown:!1,warnings:!1},t.symbols={any:Symbol.for("@hapi/joi/schema"),arraySingle:Symbol("arraySingle"),deepDefault:Symbol("deepDefault"),errors:Symbol("errors"),literal:Symbol("literal"),override:Symbol("override"),parent:Symbol("parent"),prefs:Symbol("prefs"),ref:Symbol("ref"),template:Symbol("template"),values:Symbol("values")},t.assertOptions=function(e,t,r="Options"){s(e&&"object"==typeof e&&!Array.isArray(e),"Options must be of type object");const n=Object.keys(e).filter((e=>!t.includes(e)));s(0===n.length,`${r} contain unknown keys: ${n}`);},t.checkPreferences=function(e){o=o||r(3378);const t=o.preferences.validate(e);if(t.error)throw new n([t.error.details[0].message])},t.compare=function(e,t,r){switch(r){case"=":return e===t;case">":return e>t;case"<":return e<t;case">=":return e>=t;case"<=":return e<=t}},t.default=function(e,t){return void 0===e?t:e},t.isIsoDate=function(e){return l.isoDate.test(e)},t.isNumber=function(e){return "number"==typeof e&&!isNaN(e)},t.isResolvable=function(e){return !!e&&(e[t.symbols.ref]||e[t.symbols.template])},t.isSchema=function(e,r={}){const n=e&&e[t.symbols.any];return !!n&&(s(r.legacy||n.version===t.version,"Cannot mix different versions of joi schemas"),!0)},t.isValues=function(e){return e[t.symbols.values]},t.limit=function(e){return Number.isSafeInteger(e)&&e>=0},t.preferences=function(e,s){i=i||r(6914),e=e||{},s=s||{};const n=Object.assign({},e,s);return s.errors&&e.errors&&(n.errors=Object.assign({},e.errors,s.errors),n.errors.wrap=Object.assign({},e.errors.wrap,s.errors.wrap)),s.messages&&(n.messages=i.compile(s.messages,e.messages)),delete n[t.symbols.prefs],n},t.tryWithPath=function(e,t,r={}){try{return e()}catch(e){throw void 0!==e.path?e.path=t+"."+e.path:e.path=t,r.append&&(e.message=`${e.message} (${e.path})`),e}},t.validateArg=function(e,r,{assert:s,message:n}){if(t.isSchema(s)){const t=s.validate(e);if(!t.error)return;return t.error.message}if(!s(e))return r?`${r} ${n}`:n},t.verifyFlat=function(e,t){for(const r of e)s(!Array.isArray(r),"Method no longer accepts array arguments:",t);};},3292:(e,t,r)=>{const s=r(375),n=r(8160),a=r(6133),i={};t.schema=function(e,t,r={}){n.assertOptions(r,["appendPath","override"]);try{return i.schema(e,t,r)}catch(e){throw r.appendPath&&void 0!==e.path&&(e.message=`${e.message} (${e.path})`),e}},i.schema=function(e,t,r){s(void 0!==t,"Invalid undefined schema"),Array.isArray(t)&&(s(t.length,"Invalid empty array schema"),1===t.length&&(t=t[0]));const a=(t,...s)=>!1!==r.override?t.valid(e.override,...s):t.valid(...s);if(i.simple(t))return a(e,t);if("function"==typeof t)return e.custom(t);if(s("object"==typeof t,"Invalid schema content:",typeof t),n.isResolvable(t))return a(e,t);if(n.isSchema(t))return t;if(Array.isArray(t)){for(const r of t)if(!i.simple(r))return e.alternatives().try(...t);return a(e,...t)}return t instanceof RegExp?e.string().regex(t):t instanceof Date?a(e.date(),t):(s(Object.getPrototypeOf(t)===Object.getPrototypeOf({}),"Schema can only contain plain objects"),e.object().keys(t))},t.ref=function(e,t){return a.isRef(e)?e:a.create(e,t)},t.compile=function(e,r,a={}){n.assertOptions(a,["legacy"]);const o=r&&r[n.symbols.any];if(o)return s(a.legacy||o.version===n.version,"Cannot mix different versions of joi schemas:",o.version,n.version),r;if("object"!=typeof r||!a.legacy)return t.schema(e,r,{appendPath:!0});const l=i.walk(r);return l?l.compile(l.root,r):t.schema(e,r,{appendPath:!0})},i.walk=function(e){if("object"!=typeof e)return null;if(Array.isArray(e)){for(const t of e){const e=i.walk(t);if(e)return e}return null}const t=e[n.symbols.any];if(t)return {root:e[t.root],compile:t.compile};s(Object.getPrototypeOf(e)===Object.getPrototypeOf({}),"Schema can only contain plain objects");for(const t in e){const r=i.walk(e[t]);if(r)return r}return null},i.simple=function(e){return null===e||["boolean","string","number"].includes(typeof e)},t.when=function(e,r,o){if(void 0===o&&(s(r&&"object"==typeof r,"Missing options"),o=r,r=a.create(".")),Array.isArray(o)&&(o={switch:o}),n.assertOptions(o,["is","not","then","otherwise","switch","break"]),n.isSchema(r))return s(void 0===o.is,'"is" can not be used with a schema condition'),s(void 0===o.not,'"not" can not be used with a schema condition'),s(void 0===o.switch,'"switch" can not be used with a schema condition'),i.condition(e,{is:r,then:o.then,otherwise:o.otherwise,break:o.break});if(s(a.isRef(r)||"string"==typeof r,"Invalid condition:",r),s(void 0===o.not||void 0===o.is,'Cannot combine "is" with "not"'),void 0===o.switch){let l=o;void 0!==o.not&&(l={is:o.not,then:o.otherwise,otherwise:o.then,break:o.break});let c=void 0!==l.is?e.$_compile(l.is):e.$_root.invalid(null,!1,0,"").required();return s(void 0!==l.then||void 0!==l.otherwise,'options must have at least one of "then", "otherwise", or "switch"'),s(void 0===l.break||void 0===l.then||void 0===l.otherwise,"Cannot specify then, otherwise, and break all together"),void 0===o.is||a.isRef(o.is)||n.isSchema(o.is)||(c=c.required()),i.condition(e,{ref:t.ref(r),is:c,then:l.then,otherwise:l.otherwise,break:l.break})}s(Array.isArray(o.switch),'"switch" must be an array'),s(void 0===o.is,'Cannot combine "switch" with "is"'),s(void 0===o.not,'Cannot combine "switch" with "not"'),s(void 0===o.then,'Cannot combine "switch" with "then"');const l={ref:t.ref(r),switch:[],break:o.break};for(let t=0;t<o.switch.length;++t){const r=o.switch[t],i=t===o.switch.length-1;n.assertOptions(r,i?["is","then","otherwise"]:["is","then"]),s(void 0!==r.is,'Switch statement missing "is"'),s(void 0!==r.then,'Switch statement missing "then"');const c={is:e.$_compile(r.is),then:e.$_compile(r.then)};if(a.isRef(r.is)||n.isSchema(r.is)||(c.is=c.is.required()),i){s(void 0===o.otherwise||void 0===r.otherwise,'Cannot specify "otherwise" inside and outside a "switch"');const t=void 0!==o.otherwise?o.otherwise:r.otherwise;void 0!==t&&(s(void 0===l.break,"Cannot specify both otherwise and break"),c.otherwise=e.$_compile(t));}l.switch.push(c);}return l},i.condition=function(e,t){for(const r of ["then","otherwise"])void 0===t[r]?delete t[r]:t[r]=e.$_compile(t[r]);return t};},6354:(e,t,r)=>{const s=r(5688),n=r(8160),a=r(3328);t.Report=class{constructor(e,r,s,n,a,i,o){if(this.code=e,this.flags=n,this.messages=a,this.path=i.path,this.prefs=o,this.state=i,this.value=r,this.message=null,this.template=null,this.local=s||{},this.local.label=t.label(this.flags,this.state,this.prefs,this.messages),void 0===this.value||this.local.hasOwnProperty("value")||(this.local.value=this.value),this.path.length){const e=this.path[this.path.length-1];"object"!=typeof e&&(this.local.key=e);}}_setTemplate(e){if(this.template=e,!this.flags.label&&0===this.path.length){const e=this._template(this.template,"root");e&&(this.local.label=e);}}toString(){if(this.message)return this.message;const e=this.code;if(!this.prefs.errors.render)return this.code;const t=this._template(this.template)||this._template(this.prefs.messages)||this._template(this.messages);return void 0===t?`Error code "${e}" is not defined, your custom type is missing the correct messages definition`:(this.message=t.render(this.value,this.state,this.prefs,this.local,{errors:this.prefs.errors,messages:[this.prefs.messages,this.messages]}),this.prefs.errors.label||(this.message=this.message.replace(/^"" /,"").trim()),this.message)}_template(e,r){return t.template(this.value,e,r||this.code,this.state,this.prefs)}},t.path=function(e){let t="";for(const r of e)"object"!=typeof r&&("string"==typeof r?(t&&(t+="."),t+=r):t+=`[${r}]`);return t},t.template=function(e,t,r,s,i){if(!t)return;if(a.isTemplate(t))return "root"!==r?t:null;let o=i.errors.language;if(n.isResolvable(o)&&(o=o.resolve(e,s,i)),o&&t[o]){if(void 0!==t[o][r])return t[o][r];if(void 0!==t[o]["*"])return t[o]["*"]}return t[r]?t[r]:t["*"]},t.label=function(e,r,s,n){if(!s.errors.label)return "";if(e.label)return e.label;let a=r.path;"key"===s.errors.label&&r.path.length>1&&(a=r.path.slice(-1));return t.path(a)||t.template(null,s.messages,"root",r,s)||n&&t.template(null,n,"root",r,s)||"value"},t.process=function(e,r,s){if(!e)return null;const{override:n,message:a,details:i}=t.details(e);if(n)return n;if(s.errors.stack)return new t.ValidationError(a,i,r);const o=Error.stackTraceLimit;Error.stackTraceLimit=0;const l=new t.ValidationError(a,i,r);return Error.stackTraceLimit=o,l},t.details=function(e,t={}){let r=[];const s=[];for(const n of e){if(n instanceof Error){if(!1!==t.override)return {override:n};const e=n.toString();r.push(e),s.push({message:e,type:"override",context:{error:n}});continue}const e=n.toString();r.push(e),s.push({message:e,path:n.path.filter((e=>"object"!=typeof e)),type:n.code,context:n.local});}return r.length>1&&(r=[...new Set(r)]),{message:r.join(". "),details:s}},t.ValidationError=class extends Error{constructor(e,t,r){super(e),this._original=r,this.details=t;}static isError(e){return e instanceof t.ValidationError}},t.ValidationError.prototype.isJoi=!0,t.ValidationError.prototype.name="ValidationError",t.ValidationError.prototype.annotate=s.error;},8901:(e,t,r)=>{const s=r(375),n=r(8571),a=r(8160),i=r(6914),o={};t.type=function(e,t){const r=Object.getPrototypeOf(e),l=n(r),c=e._assign(Object.create(l)),u=Object.assign({},t);delete u.base,l._definition=u;const f=r._definition||{};u.messages=i.merge(f.messages,u.messages),u.properties=Object.assign({},f.properties,u.properties),c.type=u.type,u.flags=Object.assign({},f.flags,u.flags);const m=Object.assign({},f.terms);if(u.terms)for(const e in u.terms){const t=u.terms[e];s(void 0===c.$_terms[e],"Invalid term override for",u.type,e),c.$_terms[e]=t.init,m[e]=t;}u.terms=m,u.args||(u.args=f.args),u.prepare=o.prepare(u.prepare,f.prepare),u.coerce&&("function"==typeof u.coerce&&(u.coerce={method:u.coerce}),u.coerce.from&&!Array.isArray(u.coerce.from)&&(u.coerce={method:u.coerce.method,from:[].concat(u.coerce.from)})),u.coerce=o.coerce(u.coerce,f.coerce),u.validate=o.validate(u.validate,f.validate);const h=Object.assign({},f.rules);if(u.rules)for(const e in u.rules){const t=u.rules[e];s("object"==typeof t,"Invalid rule definition for",u.type,e);let r=t.method;if(void 0===r&&(r=function(){return this.$_addRule(e)}),r&&(s(!l[e],"Rule conflict in",u.type,e),l[e]=r),s(!h[e],"Rule conflict in",u.type,e),h[e]=t,t.alias){const e=[].concat(t.alias);for(const r of e)l[r]=t.method;}t.args&&(t.argsByName=new Map,t.args=t.args.map((e=>("string"==typeof e&&(e={name:e}),s(!t.argsByName.has(e.name),"Duplicated argument name",e.name),a.isSchema(e.assert)&&(e.assert=e.assert.strict().label(e.name)),t.argsByName.set(e.name,e),e))));}u.rules=h;const d=Object.assign({},f.modifiers);if(u.modifiers)for(const e in u.modifiers){s(!l[e],"Rule conflict in",u.type,e);const t=u.modifiers[e];s("function"==typeof t,"Invalid modifier definition for",u.type,e);const r=function(t){return this.rule({[e]:t})};l[e]=r,d[e]=t;}if(u.modifiers=d,u.overrides){l._super=r,c.$_super={};for(const e in u.overrides)s(r[e],"Cannot override missing",e),u.overrides[e][a.symbols.parent]=r[e],c.$_super[e]=r[e].bind(c);Object.assign(l,u.overrides);}u.cast=Object.assign({},f.cast,u.cast);const p=Object.assign({},f.manifest,u.manifest);return p.build=o.build(u.manifest&&u.manifest.build,f.manifest&&f.manifest.build),u.manifest=p,u.rebuild=o.rebuild(u.rebuild,f.rebuild),c},o.build=function(e,t){return e&&t?function(r,s){return t(e(r,s),s)}:e||t},o.coerce=function(e,t){return e&&t?{from:e.from&&t.from?[...new Set([...e.from,...t.from])]:null,method(r,s){let n;if((!t.from||t.from.includes(typeof r))&&(n=t.method(r,s),n)){if(n.errors||void 0===n.value)return n;r=n.value;}if(!e.from||e.from.includes(typeof r)){const t=e.method(r,s);if(t)return t}return n}}:e||t},o.prepare=function(e,t){return e&&t?function(r,s){const n=e(r,s);if(n){if(n.errors||void 0===n.value)return n;r=n.value;}return t(r,s)||n}:e||t},o.rebuild=function(e,t){return e&&t?function(r){t(r),e(r);}:e||t},o.validate=function(e,t){return e&&t?function(r,s){const n=t(r,s);if(n){if(n.errors&&(!Array.isArray(n.errors)||n.errors.length))return n;r=n.value;}return e(r,s)||n}:e||t};},5107:(e,t,r)=>{const s=r(375),n=r(8571),a=r(8652),i=r(8160),o=r(3292),l=r(6354),c=r(8901),u=r(9708),f=r(6133),m=r(3328),h=r(1152);let d;const p={types:{alternatives:r(4946),any:r(8068),array:r(546),boolean:r(4937),date:r(7500),function:r(390),link:r(8785),number:r(3832),object:r(8966),string:r(7417),symbol:r(8826)},aliases:{alt:"alternatives",bool:"boolean",func:"function"},root:function(){const e={_types:new Set(Object.keys(p.types))};for(const t of e._types)e[t]=function(...e){return s(!e.length||["alternatives","link","object"].includes(t),"The",t,"type does not allow arguments"),p.generate(this,p.types[t],e)};for(const t of ["allow","custom","disallow","equal","exist","forbidden","invalid","not","only","optional","options","prefs","preferences","required","strip","valid","when"])e[t]=function(...e){return this.any()[t](...e)};Object.assign(e,p.methods);for(const t in p.aliases){const r=p.aliases[t];e[t]=e[r];}return e.x=e.expression,h.setup&&h.setup(e),e}};p.methods={ValidationError:l.ValidationError,version:i.version,cache:a.provider,assert(e,t,...r){p.assert(e,t,!0,r);},attempt:(e,t,...r)=>p.assert(e,t,!1,r),build(e){return s("function"==typeof u.build,"Manifest functionality disabled"),u.build(this,e)},checkPreferences(e){i.checkPreferences(e);},compile(e,t){return o.compile(this,e,t)},defaults(e){s("function"==typeof e,"modifier must be a function");const t=Object.assign({},this);for(const r of t._types){const n=e(t[r]());s(i.isSchema(n),"modifier must return a valid schema object"),t[r]=function(...e){return p.generate(this,n,e)};}return t},expression:(...e)=>new m(...e),extend(...e){i.verifyFlat(e,"extend"),d=d||r(3378),s(e.length,"You need to provide at least one extension"),this.assert(e,d.extensions);const t=Object.assign({},this);t._types=new Set(t._types);for(let r of e){"function"==typeof r&&(r=r(t)),this.assert(r,d.extension);const e=p.expandExtension(r,t);for(const r of e){s(void 0===t[r.type]||t._types.has(r.type),"Cannot override name",r.type);const e=r.base||this.any(),n=c.type(e,r);t._types.add(r.type),t[r.type]=function(...e){return p.generate(this,n,e)};}}return t},isError:l.ValidationError.isError,isExpression:m.isTemplate,isRef:f.isRef,isSchema:i.isSchema,in:(...e)=>f.in(...e),override:i.symbols.override,ref:(...e)=>f.create(...e),types(){const e={};for(const t of this._types)e[t]=this[t]();for(const t in p.aliases)e[t]=this[t]();return e}},p.assert=function(e,t,r,s){const a=s[0]instanceof Error||"string"==typeof s[0]?s[0]:null,o=null!==a?s[1]:s[0],c=t.validate(e,i.preferences({errors:{stack:!0}},o||{}));let u=c.error;if(!u)return c.value;if(a instanceof Error)throw a;const f=r&&"function"==typeof u.annotate?u.annotate():u.message;throw u instanceof l.ValidationError==0&&(u=n(u)),u.message=a?`${a} ${f}`:f,u},p.generate=function(e,t,r){return s(e,"Must be invoked on a Joi instance."),t.$_root=e,t._definition.args&&r.length?t._definition.args(t,...r):t},p.expandExtension=function(e,t){if("string"==typeof e.type)return [e];const r=[];for(const s of t._types)if(e.type.test(s)){const n=Object.assign({},e);n.type=s,n.base=t[s](),r.push(n);}return r},e.exports=p.root();},6914:(e,t,r)=>{const s=r(375),n=r(8571),a=r(3328);t.compile=function(e,t){if("string"==typeof e)return s(!t,"Cannot set single message string"),new a(e);if(a.isTemplate(e))return s(!t,"Cannot set single message template"),e;s("object"==typeof e&&!Array.isArray(e),"Invalid message options"),t=t?n(t):{};for(let r in e){const n=e[r];if("root"===r||a.isTemplate(n)){t[r]=n;continue}if("string"==typeof n){t[r]=new a(n);continue}s("object"==typeof n&&!Array.isArray(n),"Invalid message for",r);const i=r;for(r in t[i]=t[i]||{},n){const e=n[r];"root"===r||a.isTemplate(e)?t[i][r]=e:(s("string"==typeof e,"Invalid message for",r,"in",i),t[i][r]=new a(e));}}return t},t.decompile=function(e){const t={};for(let r in e){const s=e[r];if("root"===r){t.root=s;continue}if(a.isTemplate(s)){t[r]=s.describe({compact:!0});continue}const n=r;for(r in t[n]={},s){const e=s[r];"root"!==r?t[n][r]=e.describe({compact:!0}):t[n].root=e;}}return t},t.merge=function(e,r){if(!e)return t.compile(r);if(!r)return e;if("string"==typeof r)return new a(r);if(a.isTemplate(r))return r;const i=n(e);for(let e in r){const t=r[e];if("root"===e||a.isTemplate(t)){i[e]=t;continue}if("string"==typeof t){i[e]=new a(t);continue}s("object"==typeof t&&!Array.isArray(t),"Invalid message for",e);const n=e;for(e in i[n]=i[n]||{},t){const r=t[e];"root"===e||a.isTemplate(r)?i[n][e]=r:(s("string"==typeof r,"Invalid message for",e,"in",n),i[n][e]=new a(r));}}return i};},2294:(e,t,r)=>{const s=r(375),n=r(8160),a=r(6133),i={};t.Ids=i.Ids=class{constructor(){this._byId=new Map,this._byKey=new Map,this._schemaChain=!1;}clone(){const e=new i.Ids;return e._byId=new Map(this._byId),e._byKey=new Map(this._byKey),e._schemaChain=this._schemaChain,e}concat(e){e._schemaChain&&(this._schemaChain=!0);for(const[t,r]of e._byId.entries())s(!this._byKey.has(t),"Schema id conflicts with existing key:",t),this._byId.set(t,r);for(const[t,r]of e._byKey.entries())s(!this._byId.has(t),"Schema key conflicts with existing id:",t),this._byKey.set(t,r);}fork(e,t,r){const a=this._collect(e);a.push({schema:r});const o=a.shift();let l={id:o.id,schema:t(o.schema)};s(n.isSchema(l.schema),"adjuster function failed to return a joi schema type");for(const e of a)l={id:e.id,schema:i.fork(e.schema,l.id,l.schema)};return l.schema}labels(e,t=[]){const r=e[0],s=this._get(r);if(!s)return [...t,...e].join(".");const n=e.slice(1);return t=[...t,s.schema._flags.label||r],n.length?s.schema._ids.labels(n,t):t.join(".")}reach(e,t=[]){const r=e[0],n=this._get(r);s(n,"Schema does not contain path",[...t,...e].join("."));const a=e.slice(1);return a.length?n.schema._ids.reach(a,[...t,r]):n.schema}register(e,{key:t}={}){if(!e||!n.isSchema(e))return;(e.$_property("schemaChain")||e._ids._schemaChain)&&(this._schemaChain=!0);const r=e._flags.id;if(r){const t=this._byId.get(r);s(!t||t.schema===e,"Cannot add different schemas with the same id:",r),s(!this._byKey.has(r),"Schema id conflicts with existing key:",r),this._byId.set(r,{schema:e,id:r});}t&&(s(!this._byKey.has(t),"Schema already contains key:",t),s(!this._byId.has(t),"Schema key conflicts with existing id:",t),this._byKey.set(t,{schema:e,id:t}));}reset(){this._byId=new Map,this._byKey=new Map,this._schemaChain=!1;}_collect(e,t=[],r=[]){const n=e[0],a=this._get(n);s(a,"Schema does not contain path",[...t,...e].join(".")),r=[a,...r];const i=e.slice(1);return i.length?a.schema._ids._collect(i,[...t,n],r):r}_get(e){return this._byId.get(e)||this._byKey.get(e)}},i.fork=function(e,r,s){const n=t.schema(e,{each:(e,{key:t})=>{if(r===(e._flags.id||t))return s},ref:!1});return n?n.$_mutateRebuild():e},t.schema=function(e,t){let r;for(const s in e._flags){if("_"===s[0])continue;const n=i.scan(e._flags[s],{source:"flags",name:s},t);void 0!==n&&(r=r||e.clone(),r._flags[s]=n);}for(let s=0;s<e._rules.length;++s){const n=e._rules[s],a=i.scan(n.args,{source:"rules",name:n.name},t);if(void 0!==a){r=r||e.clone();const t=Object.assign({},n);t.args=a,r._rules[s]=t,r._singleRules.get(n.name)===n&&r._singleRules.set(n.name,t);}}for(const s in e.$_terms){if("_"===s[0])continue;const n=i.scan(e.$_terms[s],{source:"terms",name:s},t);void 0!==n&&(r=r||e.clone(),r.$_terms[s]=n);}return r},i.scan=function(e,t,r,s,o){const l=s||[];if(null===e||"object"!=typeof e)return;let c;if(Array.isArray(e)){for(let s=0;s<e.length;++s){const n="terms"===t.source&&"keys"===t.name&&e[s].key,a=i.scan(e[s],t,r,[s,...l],n);void 0!==a&&(c=c||e.slice(),c[s]=a);}return c}if(!1!==r.schema&&n.isSchema(e)||!1!==r.ref&&a.isRef(e)){const s=r.each(e,{...t,path:l,key:o});if(s===e)return;return s}for(const s in e){if("_"===s[0])continue;const n=i.scan(e[s],t,r,[s,...l],o);void 0!==n&&(c=c||Object.assign({},e),c[s]=n);}return c};},6133:(e,t,r)=>{const s=r(375),n=r(8571),a=r(9621),i=r(8160);let o;const l={symbol:Symbol("ref"),defaults:{adjust:null,in:!1,iterables:null,map:null,separator:".",type:"value"}};t.create=function(e,t={}){s("string"==typeof e,"Invalid reference key:",e),i.assertOptions(t,["adjust","ancestor","in","iterables","map","prefix","render","separator"]),s(!t.prefix||"object"==typeof t.prefix,"options.prefix must be of type object");const r=Object.assign({},l.defaults,t);delete r.prefix;const n=r.separator,a=l.context(e,n,t.prefix);if(r.type=a.type,e=a.key,"value"===r.type)if(a.root&&(s(!n||e[0]!==n,"Cannot specify relative path with root prefix"),r.ancestor="root",e||(e=null)),n&&n===e)e=null,r.ancestor=0;else if(void 0!==r.ancestor)s(!n||!e||e[0]!==n,"Cannot combine prefix with ancestor option");else {const[t,s]=l.ancestor(e,n);s&&""===(e=e.slice(s))&&(e=null),r.ancestor=t;}return r.path=n?null===e?[]:e.split(n):[e],new l.Ref(r)},t.in=function(e,r={}){return t.create(e,{...r,in:!0})},t.isRef=function(e){return !!e&&!!e[i.symbols.ref]},l.Ref=class{constructor(e){s("object"==typeof e,"Invalid reference construction"),i.assertOptions(e,["adjust","ancestor","in","iterables","map","path","render","separator","type","depth","key","root","display"]),s([!1,void 0].includes(e.separator)||"string"==typeof e.separator&&1===e.separator.length,"Invalid separator"),s(!e.adjust||"function"==typeof e.adjust,"options.adjust must be a function"),s(!e.map||Array.isArray(e.map),"options.map must be an array"),s(!e.map||!e.adjust,"Cannot set both map and adjust options"),Object.assign(this,l.defaults,e),s("value"===this.type||void 0===this.ancestor,"Non-value references cannot reference ancestors"),Array.isArray(this.map)&&(this.map=new Map(this.map)),this.depth=this.path.length,this.key=this.path.length?this.path.join(this.separator):null,this.root=this.path[0],this.updateDisplay();}resolve(e,t,r,n,a={}){return s(!this.in||a.in,"Invalid in() reference usage"),"global"===this.type?this._resolve(r.context,t,a):"local"===this.type?this._resolve(n,t,a):this.ancestor?"root"===this.ancestor?this._resolve(t.ancestors[t.ancestors.length-1],t,a):(s(this.ancestor<=t.ancestors.length,"Invalid reference exceeds the schema root:",this.display),this._resolve(t.ancestors[this.ancestor-1],t,a)):this._resolve(e,t,a)}_resolve(e,t,r){let s;if("value"===this.type&&t.mainstay.shadow&&!1!==r.shadow&&(s=t.mainstay.shadow.get(this.absolute(t))),void 0===s&&(s=a(e,this.path,{iterables:this.iterables,functions:!0})),this.adjust&&(s=this.adjust(s)),this.map){const e=this.map.get(s);void 0!==e&&(s=e);}return t.mainstay&&t.mainstay.tracer.resolve(t,this,s),s}toString(){return this.display}absolute(e){return [...e.path.slice(0,-this.ancestor),...this.path]}clone(){return new l.Ref(this)}describe(){const e={path:this.path};"value"!==this.type&&(e.type=this.type),"."!==this.separator&&(e.separator=this.separator),"value"===this.type&&1!==this.ancestor&&(e.ancestor=this.ancestor),this.map&&(e.map=[...this.map]);for(const t of ["adjust","iterables","render"])null!==this[t]&&void 0!==this[t]&&(e[t]=this[t]);return !1!==this.in&&(e.in=!0),{ref:e}}updateDisplay(){const e=null!==this.key?this.key:"";if("value"!==this.type)return void(this.display=`ref:${this.type}:${e}`);if(!this.separator)return void(this.display=`ref:${e}`);if(!this.ancestor)return void(this.display=`ref:${this.separator}${e}`);if("root"===this.ancestor)return void(this.display=`ref:root:${e}`);if(1===this.ancestor)return void(this.display=`ref:${e||".."}`);const t=new Array(this.ancestor+1).fill(this.separator).join("");this.display=`ref:${t}${e||""}`;}},l.Ref.prototype[i.symbols.ref]=!0,t.build=function(e){return "value"===(e=Object.assign({},l.defaults,e)).type&&void 0===e.ancestor&&(e.ancestor=1),new l.Ref(e)},l.context=function(e,t,r={}){if(e=e.trim(),r){const s=void 0===r.global?"$":r.global;if(s!==t&&e.startsWith(s))return {key:e.slice(s.length),type:"global"};const n=void 0===r.local?"#":r.local;if(n!==t&&e.startsWith(n))return {key:e.slice(n.length),type:"local"};const a=void 0===r.root?"/":r.root;if(a!==t&&e.startsWith(a))return {key:e.slice(a.length),type:"value",root:!0}}return {key:e,type:"value"}},l.ancestor=function(e,t){if(!t)return [1,0];if(e[0]!==t)return [1,0];if(e[1]!==t)return [0,1];let r=2;for(;e[r]===t;)++r;return [r-1,r]},t.toSibling=0,t.toParent=1,t.Manager=class{constructor(){this.refs=[];}register(e,s){if(e)if(s=void 0===s?t.toParent:s,Array.isArray(e))for(const t of e)this.register(t,s);else if(i.isSchema(e))for(const t of e._refs.refs)t.ancestor-s>=0&&this.refs.push({ancestor:t.ancestor-s,root:t.root});else t.isRef(e)&&"value"===e.type&&e.ancestor-s>=0&&this.refs.push({ancestor:e.ancestor-s,root:e.root}),o=o||r(3328),o.isTemplate(e)&&this.register(e.refs(),s);}get length(){return this.refs.length}clone(){const e=new t.Manager;return e.refs=n(this.refs),e}reset(){this.refs=[];}roots(){return this.refs.filter((e=>!e.ancestor)).map((e=>e.root))}};},3378:(e,t,r)=>{const s=r(5107),n={};n.wrap=s.string().min(1).max(2).allow(!1),t.preferences=s.object({allowUnknown:s.boolean(),abortEarly:s.boolean(),artifacts:s.boolean(),cache:s.boolean(),context:s.object(),convert:s.boolean(),dateFormat:s.valid("date","iso","string","time","utc"),debug:s.boolean(),errors:{escapeHtml:s.boolean(),label:s.valid("path","key",!1),language:[s.string(),s.object().ref()],render:s.boolean(),stack:s.boolean(),wrap:{label:n.wrap,array:n.wrap,string:n.wrap}},externals:s.boolean(),messages:s.object(),noDefaults:s.boolean(),nonEnumerables:s.boolean(),presence:s.valid("required","optional","forbidden"),skipFunctions:s.boolean(),stripUnknown:s.object({arrays:s.boolean(),objects:s.boolean()}).or("arrays","objects").allow(!0,!1),warnings:s.boolean()}).strict(),n.nameRx=/^[a-zA-Z0-9]\w*$/,n.rule=s.object({alias:s.array().items(s.string().pattern(n.nameRx)).single(),args:s.array().items(s.string(),s.object({name:s.string().pattern(n.nameRx).required(),ref:s.boolean(),assert:s.alternatives([s.function(),s.object().schema()]).conditional("ref",{is:!0,then:s.required()}),normalize:s.function(),message:s.string().when("assert",{is:s.function(),then:s.required()})})),convert:s.boolean(),manifest:s.boolean(),method:s.function().allow(!1),multi:s.boolean(),validate:s.function()}),t.extension=s.object({type:s.alternatives([s.string(),s.object().regex()]).required(),args:s.function(),cast:s.object().pattern(n.nameRx,s.object({from:s.function().maxArity(1).required(),to:s.function().minArity(1).maxArity(2).required()})),base:s.object().schema().when("type",{is:s.object().regex(),then:s.forbidden()}),coerce:[s.function().maxArity(3),s.object({method:s.function().maxArity(3).required(),from:s.array().items(s.string()).single()})],flags:s.object().pattern(n.nameRx,s.object({setter:s.string(),default:s.any()})),manifest:{build:s.function().arity(2)},messages:[s.object(),s.string()],modifiers:s.object().pattern(n.nameRx,s.function().minArity(1).maxArity(2)),overrides:s.object().pattern(n.nameRx,s.function()),prepare:s.function().maxArity(3),rebuild:s.function().arity(1),rules:s.object().pattern(n.nameRx,n.rule),terms:s.object().pattern(n.nameRx,s.object({init:s.array().allow(null).required(),manifest:s.object().pattern(/.+/,[s.valid("schema","single"),s.object({mapped:s.object({from:s.string().required(),to:s.string().required()}).required()})])})),validate:s.function().maxArity(3)}).strict(),t.extensions=s.array().items(s.object(),s.function().arity(1)).strict(),n.desc={buffer:s.object({buffer:s.string()}),func:s.object({function:s.function().required(),options:{literal:!0}}),override:s.object({override:!0}),ref:s.object({ref:s.object({type:s.valid("value","global","local"),path:s.array().required(),separator:s.string().length(1).allow(!1),ancestor:s.number().min(0).integer().allow("root"),map:s.array().items(s.array().length(2)).min(1),adjust:s.function(),iterables:s.boolean(),in:s.boolean(),render:s.boolean()}).required()}),regex:s.object({regex:s.string().min(3)}),special:s.object({special:s.valid("deep").required()}),template:s.object({template:s.string().required(),options:s.object()}),value:s.object({value:s.alternatives([s.object(),s.array()]).required()})},n.desc.entity=s.alternatives([s.array().items(s.link("...")),s.boolean(),s.function(),s.number(),s.string(),n.desc.buffer,n.desc.func,n.desc.ref,n.desc.regex,n.desc.special,n.desc.template,n.desc.value,s.link("/")]),n.desc.values=s.array().items(null,s.boolean(),s.function(),s.number().allow(1/0,-1/0),s.string().allow(""),s.symbol(),n.desc.buffer,n.desc.func,n.desc.override,n.desc.ref,n.desc.regex,n.desc.template,n.desc.value),n.desc.messages=s.object().pattern(/.+/,[s.string(),n.desc.template,s.object().pattern(/.+/,[s.string(),n.desc.template])]),t.description=s.object({type:s.string().required(),flags:s.object({cast:s.string(),default:s.any(),description:s.string(),empty:s.link("/"),failover:n.desc.entity,id:s.string(),label:s.string(),only:!0,presence:["optional","required","forbidden"],result:["raw","strip"],strip:s.boolean(),unit:s.string()}).unknown(),preferences:{allowUnknown:s.boolean(),abortEarly:s.boolean(),artifacts:s.boolean(),cache:s.boolean(),convert:s.boolean(),dateFormat:["date","iso","string","time","utc"],errors:{escapeHtml:s.boolean(),label:["path","key"],language:[s.string(),n.desc.ref],wrap:{label:n.wrap,array:n.wrap}},externals:s.boolean(),messages:n.desc.messages,noDefaults:s.boolean(),nonEnumerables:s.boolean(),presence:["required","optional","forbidden"],skipFunctions:s.boolean(),stripUnknown:s.object({arrays:s.boolean(),objects:s.boolean()}).or("arrays","objects").allow(!0,!1),warnings:s.boolean()},allow:n.desc.values,invalid:n.desc.values,rules:s.array().min(1).items({name:s.string().required(),args:s.object().min(1),keep:s.boolean(),message:[s.string(),n.desc.messages],warn:s.boolean()}),keys:s.object().pattern(/.*/,s.link("/")),link:n.desc.ref}).pattern(/^[a-z]\w*$/,s.any());},493:(e,t,r)=>{const s=r(8571),n=r(9621),a=r(8160),i={value:Symbol("value")};e.exports=i.State=class{constructor(e,t,r){this.path=e,this.ancestors=t,this.mainstay=r.mainstay,this.schemas=r.schemas,this.debug=null;}localize(e,t=null,r=null){const s=new i.State(e,t,this);return r&&s.schemas&&(s.schemas=[i.schemas(r),...s.schemas]),s}nest(e,t){const r=new i.State(this.path,this.ancestors,this);return r.schemas=r.schemas&&[i.schemas(e),...r.schemas],r.debug=t,r}shadow(e,t){this.mainstay.shadow=this.mainstay.shadow||new i.Shadow,this.mainstay.shadow.set(this.path,e,t);}snapshot(){this.mainstay.shadow&&(this._snapshot=s(this.mainstay.shadow.node(this.path))),this.mainstay.snapshot();}restore(){this.mainstay.shadow&&(this.mainstay.shadow.override(this.path,this._snapshot),this._snapshot=void 0),this.mainstay.restore();}commit(){this.mainstay.shadow&&(this.mainstay.shadow.override(this.path,this._snapshot),this._snapshot=void 0),this.mainstay.commit();}},i.schemas=function(e){return a.isSchema(e)?{schema:e}:e},i.Shadow=class{constructor(){this._values=null;}set(e,t,r){if(!e.length)return;if("strip"===r&&"number"==typeof e[e.length-1])return;this._values=this._values||new Map;let s=this._values;for(let t=0;t<e.length;++t){const r=e[t];let n=s.get(r);n||(n=new Map,s.set(r,n)),s=n;}s[i.value]=t;}get(e){const t=this.node(e);if(t)return t[i.value]}node(e){if(this._values)return n(this._values,e,{iterables:!0})}override(e,t){if(!this._values)return;const r=e.slice(0,-1),s=e[e.length-1],a=n(this._values,r,{iterables:!0});t?a.set(s,t):a&&a.delete(s);}};},3328:(e,t,r)=>{const s=r(375),n=r(8571),a=r(5277),i=r(1447),o=r(8160),l=r(6354),c=r(6133),u={symbol:Symbol("template"),opens:new Array(1e3).join("\0"),closes:new Array(1e3).join(""),dateFormat:{date:Date.prototype.toDateString,iso:Date.prototype.toISOString,string:Date.prototype.toString,time:Date.prototype.toTimeString,utc:Date.prototype.toUTCString}};e.exports=u.Template=class{constructor(e,t){if(s("string"==typeof e,"Template source must be a string"),s(!e.includes("\0")&&!e.includes(""),"Template source cannot contain reserved control characters"),this.source=e,this.rendered=e,this._template=null,t){const{functions:e,...r}=t;this._settings=Object.keys(r).length?n(r):void 0,this._functions=e,this._functions&&(s(Object.keys(this._functions).every((e=>"string"==typeof e)),"Functions keys must be strings"),s(Object.values(this._functions).every((e=>"function"==typeof e)),"Functions values must be functions"));}else this._settings=void 0,this._functions=void 0;this._parse();}_parse(){if(!this.source.includes("{"))return;const e=u.encode(this.source),t=u.split(e);let r=!1;const s=[],n=t.shift();n&&s.push(n);for(const e of t){const t="{"!==e[0],n=t?"}":"}}",a=e.indexOf(n);if(-1===a||"{"===e[1]){s.push(`{${u.decode(e)}`);continue}let i=e.slice(t?0:1,a);const o=":"===i[0];o&&(i=i.slice(1));const l=this._ref(u.decode(i),{raw:t,wrapped:o});s.push(l),"string"!=typeof l&&(r=!0);const c=e.slice(a+n.length);c&&s.push(u.decode(c));}r?this._template=s:this.rendered=s.join("");}static date(e,t){return u.dateFormat[t.dateFormat].call(e)}describe(e={}){if(!this._settings&&e.compact)return this.source;const t={template:this.source};return this._settings&&(t.options=this._settings),this._functions&&(t.functions=this._functions),t}static build(e){return new u.Template(e.template,e.options||e.functions?{...e.options,functions:e.functions}:void 0)}isDynamic(){return !!this._template}static isTemplate(e){return !!e&&!!e[o.symbols.template]}refs(){if(!this._template)return;const e=[];for(const t of this._template)"string"!=typeof t&&e.push(...t.refs);return e}resolve(e,t,r,s){return this._template&&1===this._template.length?this._part(this._template[0],e,t,r,s,{}):this.render(e,t,r,s)}_part(e,...t){return e.ref?e.ref.resolve(...t):e.formula.evaluate(t)}render(e,t,r,s,n={}){if(!this.isDynamic())return this.rendered;const i=[];for(const o of this._template)if("string"==typeof o)i.push(o);else {const l=this._part(o,e,t,r,s,n),c=u.stringify(l,e,t,r,s,n);if(void 0!==c){const e=o.raw||!1===(n.errors&&n.errors.escapeHtml)?c:a(c);i.push(u.wrap(e,o.wrapped&&r.errors.wrap.label));}}return i.join("")}_ref(e,{raw:t,wrapped:r}){const s=[],n=e=>{const t=c.create(e,this._settings);return s.push(t),e=>{const r=t.resolve(...e);return void 0!==r?r:null}};try{const t=this._functions?{...u.functions,...this._functions}:u.functions;var a=new i.Parser(e,{reference:n,functions:t,constants:u.constants});}catch(t){throw t.message=`Invalid template variable "${e}" fails due to: ${t.message}`,t}if(a.single){if("reference"===a.single.type){const e=s[0];return {ref:e,raw:t,refs:s,wrapped:r||"local"===e.type&&"label"===e.key}}return u.stringify(a.single.value)}return {formula:a,raw:t,refs:s}}toString(){return this.source}},u.Template.prototype[o.symbols.template]=!0,u.Template.prototype.isImmutable=!0,u.encode=function(e){return e.replace(/\\(\{+)/g,((e,t)=>u.opens.slice(0,t.length))).replace(/\\(\}+)/g,((e,t)=>u.closes.slice(0,t.length)))},u.decode=function(e){return e.replace(/\u0000/g,"{").replace(/\u0001/g,"}")},u.split=function(e){const t=[];let r="";for(let s=0;s<e.length;++s){const n=e[s];if("{"===n){let n="";for(;s+1<e.length&&"{"===e[s+1];)n+="{",++s;t.push(r),r=n;}else r+=n;}return t.push(r),t},u.wrap=function(e,t){return t?1===t.length?`${t}${e}${t}`:`${t[0]}${e}${t[1]}`:e},u.stringify=function(e,t,r,s,n,a={}){const i=typeof e,o=s&&s.errors&&s.errors.wrap||{};let l=!1;if(c.isRef(e)&&e.render&&(l=e.in,e=e.resolve(t,r,s,n,{in:e.in,...a})),null===e)return "null";if("string"===i)return u.wrap(e,a.arrayItems&&o.string);if("number"===i||"function"===i||"symbol"===i)return e.toString();if("object"!==i)return JSON.stringify(e);if(e instanceof Date)return u.Template.date(e,s);if(e instanceof Map){const t=[];for(const[r,s]of e.entries())t.push(`${r.toString()} -> ${s.toString()}`);e=t;}if(!Array.isArray(e))return e.toString();const f=[];for(const i of e)f.push(u.stringify(i,t,r,s,n,{arrayItems:!0,...a}));return u.wrap(f.join(", "),!l&&o.array)},u.constants={true:!0,false:!1,null:null,second:1e3,minute:6e4,hour:36e5,day:864e5},u.functions={if:(e,t,r)=>e?t:r,length:e=>"string"==typeof e?e.length:e&&"object"==typeof e?Array.isArray(e)?e.length:Object.keys(e).length:null,msg(e){const[t,r,s,n,a]=this,i=a.messages;if(!i)return "";const o=l.template(t,i[0],e,r,s)||l.template(t,i[1],e,r,s);return o?o.render(t,r,s,n,a):""},number:e=>"number"==typeof e?e:"string"==typeof e?parseFloat(e):"boolean"==typeof e?e?1:0:e instanceof Date?e.getTime():null};},4946:(e,t,r)=>{const s=r(375),n=r(1687),a=r(8068),i=r(8160),o=r(3292),l=r(6354),c=r(6133),u={};e.exports=a.extend({type:"alternatives",flags:{match:{default:"any"}},terms:{matches:{init:[],register:c.toSibling}},args:(e,...t)=>1===t.length&&Array.isArray(t[0])?e.try(...t[0]):e.try(...t),validate(e,t){const{schema:r,error:s,state:a,prefs:i}=t;if(r._flags.match){const t=[],o=[];for(let s=0;s<r.$_terms.matches.length;++s){const n=r.$_terms.matches[s],l=a.nest(n.schema,`match.${s}`);l.snapshot();const c=n.schema.$_validate(e,l,i);c.errors?(o.push(c.errors),l.restore()):(t.push(c.value),l.commit());}if(0===t.length)return {errors:s("alternatives.any",{details:o.map((e=>l.details(e,{override:!1})))})};if("one"===r._flags.match)return 1===t.length?{value:t[0]}:{errors:s("alternatives.one")};if(t.length!==r.$_terms.matches.length)return {errors:s("alternatives.all",{details:o.map((e=>l.details(e,{override:!1})))})};const c=e=>e.$_terms.matches.some((e=>"object"===e.schema.type||"alternatives"===e.schema.type&&c(e.schema)));return c(r)?{value:t.reduce(((e,t)=>n(e,t,{mergeArrays:!1})))}:{value:t[t.length-1]}}const o=[];for(let t=0;t<r.$_terms.matches.length;++t){const s=r.$_terms.matches[t];if(s.schema){const r=a.nest(s.schema,`match.${t}`);r.snapshot();const n=s.schema.$_validate(e,r,i);if(!n.errors)return r.commit(),n;r.restore(),o.push({schema:s.schema,reports:n.errors});continue}const n=s.ref?s.ref.resolve(e,a,i):e,l=s.is?[s]:s.switch;for(let r=0;r<l.length;++r){const o=l[r],{is:c,then:u,otherwise:f}=o,m=`match.${t}${s.switch?"."+r:""}`;if(c.$_match(n,a.nest(c,`${m}.is`),i)){if(u)return u.$_validate(e,a.nest(u,`${m}.then`),i)}else if(f)return f.$_validate(e,a.nest(f,`${m}.otherwise`),i)}}return u.errors(o,t)},rules:{conditional:{method(e,t){s(!this._flags._endedSwitch,"Unreachable condition"),s(!this._flags.match,"Cannot combine match mode",this._flags.match,"with conditional rule"),s(void 0===t.break,"Cannot use break option with alternatives conditional");const r=this.clone(),n=o.when(r,e,t),a=n.is?[n]:n.switch;for(const e of a)if(e.then&&e.otherwise){r.$_setFlag("_endedSwitch",!0,{clone:!1});break}return r.$_terms.matches.push(n),r.$_mutateRebuild()}},match:{method(e){if(s(["any","one","all"].includes(e),"Invalid alternatives match mode",e),"any"!==e)for(const t of this.$_terms.matches)s(t.schema,"Cannot combine match mode",e,"with conditional rules");return this.$_setFlag("match",e)}},try:{method(...e){s(e.length,"Missing alternative schemas"),i.verifyFlat(e,"try"),s(!this._flags._endedSwitch,"Unreachable condition");const t=this.clone();for(const r of e)t.$_terms.matches.push({schema:t.$_compile(r)});return t.$_mutateRebuild()}}},overrides:{label(e){return this.$_parent("label",e).$_modify({each:(t,r)=>"is"!==r.path[0]&&"string"!=typeof t._flags.label?t.label(e):void 0,ref:!1})}},rebuild(e){e.$_modify({each:t=>{i.isSchema(t)&&"array"===t.type&&e.$_setFlag("_arrayItems",!0,{clone:!1});}});},manifest:{build(e,t){if(t.matches)for(const r of t.matches){const{schema:t,ref:s,is:n,not:a,then:i,otherwise:o}=r;e=t?e.try(t):s?e.conditional(s,{is:n,then:i,not:a,otherwise:o,switch:r.switch}):e.conditional(n,{then:i,otherwise:o});}return e}},messages:{"alternatives.all":"{{#label}} does not match all of the required types","alternatives.any":"{{#label}} does not match any of the allowed types","alternatives.match":"{{#label}} does not match any of the allowed types","alternatives.one":"{{#label}} matches more than one allowed type","alternatives.types":"{{#label}} must be one of {{#types}}"}}),u.errors=function(e,{error:t,state:r}){if(!e.length)return {errors:t("alternatives.any")};if(1===e.length)return {errors:e[0].reports};const s=new Set,n=[];for(const{reports:a,schema:i}of e){if(a.length>1)return u.unmatched(e,t);const o=a[0];if(o instanceof l.Report==0)return u.unmatched(e,t);if(o.state.path.length!==r.path.length){n.push({type:i.type,report:o});continue}if("any.only"===o.code){for(const e of o.local.valids)s.add(e);continue}const[c,f]=o.code.split(".");"base"!==f?n.push({type:i.type,report:o}):"object.base"===o.code?s.add(o.local.type):s.add(c);}return n.length?1===n.length?{errors:n[0].report}:u.unmatched(e,t):{errors:t("alternatives.types",{types:[...s]})}},u.unmatched=function(e,t){const r=[];for(const t of e)r.push(...t.reports);return {errors:t("alternatives.match",l.details(r,{override:!1}))}};},8068:(e,t,r)=>{const s=r(375),n=r(7629),a=r(8160),i=r(6914);e.exports=n.extend({type:"any",flags:{only:{default:!1}},terms:{alterations:{init:null},examples:{init:null},externals:{init:null},metas:{init:[]},notes:{init:[]},shared:{init:null},tags:{init:[]},whens:{init:null}},rules:{custom:{method(e,t){return s("function"==typeof e,"Method must be a function"),s(void 0===t||t&&"string"==typeof t,"Description must be a non-empty string"),this.$_addRule({name:"custom",args:{method:e,description:t}})},validate(e,t,{method:r}){try{return r(e,t)}catch(e){return t.error("any.custom",{error:e})}},args:["method","description"],multi:!0},messages:{method(e){return this.prefs({messages:e})}},shared:{method(e){s(a.isSchema(e)&&e._flags.id,"Schema must be a schema with an id");const t=this.clone();return t.$_terms.shared=t.$_terms.shared||[],t.$_terms.shared.push(e),t.$_mutateRegister(e),t}},warning:{method(e,t){return s(e&&"string"==typeof e,"Invalid warning code"),this.$_addRule({name:"warning",args:{code:e,local:t},warn:!0})},validate:(e,t,{code:r,local:s})=>t.error(r,s),args:["code","local"],multi:!0}},modifiers:{keep(e,t=!0){e.keep=t;},message(e,t){e.message=i.compile(t);},warn(e,t=!0){e.warn=t;}},manifest:{build(e,t){for(const r in t){const s=t[r];if(["examples","externals","metas","notes","tags"].includes(r))for(const t of s)e=e[r.slice(0,-1)](t);else if("alterations"!==r)if("whens"!==r){if("shared"===r)for(const t of s)e=e.shared(t);}else for(const t of s){const{ref:r,is:s,not:n,then:a,otherwise:i,concat:o}=t;e=o?e.concat(o):r?e.when(r,{is:s,not:n,then:a,otherwise:i,switch:t.switch,break:t.break}):e.when(s,{then:a,otherwise:i,break:t.break});}else {const t={};for(const{target:e,adjuster:r}of s)t[e]=r;e=e.alter(t);}}return e}},messages:{"any.custom":"{{#label}} failed custom validation because {{#error.message}}","any.default":"{{#label}} threw an error when running default method","any.failover":"{{#label}} threw an error when running failover method","any.invalid":"{{#label}} contains an invalid value","any.only":'{{#label}} must be {if(#valids.length == 1, "", "one of ")}{{#valids}}',"any.ref":"{{#label}} {{#arg}} references {{:#ref}} which {{#reason}}","any.required":"{{#label}} is required","any.unknown":"{{#label}} is not allowed"}});},546:(e,t,r)=>{const s=r(375),n=r(9474),a=r(9621),i=r(8068),o=r(8160),l=r(3292),c={};e.exports=i.extend({type:"array",flags:{single:{default:!1},sparse:{default:!1}},terms:{items:{init:[],manifest:"schema"},ordered:{init:[],manifest:"schema"},_exclusions:{init:[]},_inclusions:{init:[]},_requireds:{init:[]}},coerce:{from:"object",method(e,{schema:t,state:r,prefs:s}){if(!Array.isArray(e))return;const n=t.$_getRule("sort");return n?c.sort(t,e,n.args.options,r,s):void 0}},validate(e,{schema:t,error:r}){if(!Array.isArray(e)){if(t._flags.single){const t=[e];return t[o.symbols.arraySingle]=!0,{value:t}}return {errors:r("array.base")}}if(t.$_getRule("items")||t.$_terms.externals)return {value:e.slice()}},rules:{has:{method(e){e=this.$_compile(e,{appendPath:!0});const t=this.$_addRule({name:"has",args:{schema:e}});return t.$_mutateRegister(e),t},validate(e,{state:t,prefs:r,error:s},{schema:n}){const a=[e,...t.ancestors];for(let s=0;s<e.length;++s){const i=t.localize([...t.path,s],a,n);if(n.$_match(e[s],i,r))return e}const i=n._flags.label;return i?s("array.hasKnown",{patternLabel:i}):s("array.hasUnknown",null)},multi:!0},items:{method(...e){o.verifyFlat(e,"items");const t=this.$_addRule("items");for(let r=0;r<e.length;++r){const s=o.tryWithPath((()=>this.$_compile(e[r])),r,{append:!0});t.$_terms.items.push(s);}return t.$_mutateRebuild()},validate(e,{schema:t,error:r,state:s,prefs:n,errorsArray:a}){const i=t.$_terms._requireds.slice(),l=t.$_terms.ordered.slice(),u=[...t.$_terms._inclusions,...i],f=!e[o.symbols.arraySingle];delete e[o.symbols.arraySingle];const m=a();let h=e.length;for(let a=0;a<h;++a){const o=e[a];let d=!1,p=!1;const g=f?a:new Number(a),y=[...s.path,g];if(!t._flags.sparse&&void 0===o){if(m.push(r("array.sparse",{key:g,path:y,pos:a,value:void 0},s.localize(y))),n.abortEarly)return m;l.shift();continue}const b=[e,...s.ancestors];for(const e of t.$_terms._exclusions)if(e.$_match(o,s.localize(y,b,e),n,{presence:"ignore"})){if(m.push(r("array.excludes",{pos:a,value:o},s.localize(y))),n.abortEarly)return m;d=!0,l.shift();break}if(d)continue;if(t.$_terms.ordered.length){if(l.length){const i=l.shift(),u=i.$_validate(o,s.localize(y,b,i),n);if(u.errors){if(m.push(...u.errors),n.abortEarly)return m}else if("strip"===i._flags.result)c.fastSplice(e,a),--a,--h;else {if(!t._flags.sparse&&void 0===u.value){if(m.push(r("array.sparse",{key:g,path:y,pos:a,value:void 0},s.localize(y))),n.abortEarly)return m;continue}e[a]=u.value;}continue}if(!t.$_terms.items.length){if(m.push(r("array.orderedLength",{pos:a,limit:t.$_terms.ordered.length})),n.abortEarly)return m;break}}const v=[];let _=i.length;for(let l=0;l<_;++l){const u=s.localize(y,b,i[l]);u.snapshot();const f=i[l].$_validate(o,u,n);if(v[l]=f,!f.errors){if(u.commit(),e[a]=f.value,p=!0,c.fastSplice(i,l),--l,--_,!t._flags.sparse&&void 0===f.value&&(m.push(r("array.sparse",{key:g,path:y,pos:a,value:void 0},s.localize(y))),n.abortEarly))return m;break}u.restore();}if(p)continue;const w=n.stripUnknown&&!!n.stripUnknown.arrays||!1;_=u.length;for(const l of u){let u;const f=i.indexOf(l);if(-1!==f)u=v[f];else {const i=s.localize(y,b,l);if(i.snapshot(),u=l.$_validate(o,i,n),!u.errors){i.commit(),"strip"===l._flags.result?(c.fastSplice(e,a),--a,--h):t._flags.sparse||void 0!==u.value?e[a]=u.value:(m.push(r("array.sparse",{key:g,path:y,pos:a,value:void 0},s.localize(y))),d=!0),p=!0;break}i.restore();}if(1===_){if(w){c.fastSplice(e,a),--a,--h,p=!0;break}if(m.push(...u.errors),n.abortEarly)return m;d=!0;break}}if(!d&&(t.$_terms._inclusions.length||t.$_terms._requireds.length)&&!p){if(w){c.fastSplice(e,a),--a,--h;continue}if(m.push(r("array.includes",{pos:a,value:o},s.localize(y))),n.abortEarly)return m}}return i.length&&c.fillMissedErrors(t,m,i,e,s,n),l.length&&(c.fillOrderedErrors(t,m,l,e,s,n),m.length||c.fillDefault(l,e,s,n)),m.length?m:e},priority:!0,manifest:!1},length:{method(e){return this.$_addRule({name:"length",args:{limit:e},operator:"="})},validate:(e,t,{limit:r},{name:s,operator:n,args:a})=>o.compare(e.length,r,n)?e:t.error("array."+s,{limit:a.limit,value:e}),args:[{name:"limit",ref:!0,assert:o.limit,message:"must be a positive integer"}]},max:{method(e){return this.$_addRule({name:"max",method:"length",args:{limit:e},operator:"<="})}},min:{method(e){return this.$_addRule({name:"min",method:"length",args:{limit:e},operator:">="})}},ordered:{method(...e){o.verifyFlat(e,"ordered");const t=this.$_addRule("items");for(let r=0;r<e.length;++r){const s=o.tryWithPath((()=>this.$_compile(e[r])),r,{append:!0});c.validateSingle(s,t),t.$_mutateRegister(s),t.$_terms.ordered.push(s);}return t.$_mutateRebuild()}},single:{method(e){const t=void 0===e||!!e;return s(!t||!this._flags._arrayItems,"Cannot specify single rule when array has array items"),this.$_setFlag("single",t)}},sort:{method(e={}){o.assertOptions(e,["by","order"]);const t={order:e.order||"ascending"};return e.by&&(t.by=l.ref(e.by,{ancestor:0}),s(!t.by.ancestor,"Cannot sort by ancestor")),this.$_addRule({name:"sort",args:{options:t}})},validate(e,{error:t,state:r,prefs:s,schema:n},{options:a}){const{value:i,errors:o}=c.sort(n,e,a,r,s);if(o)return o;for(let r=0;r<e.length;++r)if(e[r]!==i[r])return t("array.sort",{order:a.order,by:a.by?a.by.key:"value"});return e},convert:!0},sparse:{method(e){const t=void 0===e||!!e;return this._flags.sparse===t?this:(t?this.clone():this.$_addRule("items")).$_setFlag("sparse",t,{clone:!1})}},unique:{method(e,t={}){s(!e||"function"==typeof e||"string"==typeof e,"comparator must be a function or a string"),o.assertOptions(t,["ignoreUndefined","separator"]);const r={name:"unique",args:{options:t,comparator:e}};if(e)if("string"==typeof e){const s=o.default(t.separator,".");r.path=s?e.split(s):[e];}else r.comparator=e;return this.$_addRule(r)},validate(e,{state:t,error:r,schema:i},{comparator:o,options:l},{comparator:c,path:u}){const f={string:Object.create(null),number:Object.create(null),undefined:Object.create(null),boolean:Object.create(null),bigint:Object.create(null),object:new Map,function:new Map,custom:new Map},m=c||n,h=l.ignoreUndefined;for(let n=0;n<e.length;++n){const i=u?a(e[n],u):e[n],l=c?f.custom:f[typeof i];if(s(l,"Failed to find unique map container for type",typeof i),l instanceof Map){const s=l.entries();let a;for(;!(a=s.next()).done;)if(m(a.value[0],i)){const s=t.localize([...t.path,n],[e,...t.ancestors]),i={pos:n,value:e[n],dupePos:a.value[1],dupeValue:e[a.value[1]]};return u&&(i.path=o),r("array.unique",i,s)}l.set(i,n);}else {if((!h||void 0!==i)&&void 0!==l[i]){const s={pos:n,value:e[n],dupePos:l[i],dupeValue:e[l[i]]};return u&&(s.path=o),r("array.unique",s,t.localize([...t.path,n],[e,...t.ancestors]))}l[i]=n;}}return e},args:["comparator","options"],multi:!0}},cast:{set:{from:Array.isArray,to:(e,t)=>new Set(e)}},rebuild(e){e.$_terms._inclusions=[],e.$_terms._exclusions=[],e.$_terms._requireds=[];for(const t of e.$_terms.items)c.validateSingle(t,e),"required"===t._flags.presence?e.$_terms._requireds.push(t):"forbidden"===t._flags.presence?e.$_terms._exclusions.push(t):e.$_terms._inclusions.push(t);for(const t of e.$_terms.ordered)c.validateSingle(t,e);},manifest:{build:(e,t)=>(t.items&&(e=e.items(...t.items)),t.ordered&&(e=e.ordered(...t.ordered)),e)},messages:{"array.base":"{{#label}} must be an array","array.excludes":"{{#label}} contains an excluded value","array.hasKnown":"{{#label}} does not contain at least one required match for type {:#patternLabel}","array.hasUnknown":"{{#label}} does not contain at least one required match","array.includes":"{{#label}} does not match any of the allowed types","array.includesRequiredBoth":"{{#label}} does not contain {{#knownMisses}} and {{#unknownMisses}} other required value(s)","array.includesRequiredKnowns":"{{#label}} does not contain {{#knownMisses}}","array.includesRequiredUnknowns":"{{#label}} does not contain {{#unknownMisses}} required value(s)","array.length":"{{#label}} must contain {{#limit}} items","array.max":"{{#label}} must contain less than or equal to {{#limit}} items","array.min":"{{#label}} must contain at least {{#limit}} items","array.orderedLength":"{{#label}} must contain at most {{#limit}} items","array.sort":"{{#label}} must be sorted in {#order} order by {{#by}}","array.sort.mismatching":"{{#label}} cannot be sorted due to mismatching types","array.sort.unsupported":"{{#label}} cannot be sorted due to unsupported type {#type}","array.sparse":"{{#label}} must not be a sparse array item","array.unique":"{{#label}} contains a duplicate value"}}),c.fillMissedErrors=function(e,t,r,s,n,a){const i=[];let o=0;for(const e of r){const t=e._flags.label;t?i.push(t):++o;}i.length?o?t.push(e.$_createError("array.includesRequiredBoth",s,{knownMisses:i,unknownMisses:o},n,a)):t.push(e.$_createError("array.includesRequiredKnowns",s,{knownMisses:i},n,a)):t.push(e.$_createError("array.includesRequiredUnknowns",s,{unknownMisses:o},n,a));},c.fillOrderedErrors=function(e,t,r,s,n,a){const i=[];for(const e of r)"required"===e._flags.presence&&i.push(e);i.length&&c.fillMissedErrors(e,t,i,s,n,a);},c.fillDefault=function(e,t,r,s){const n=[];let a=!0;for(let i=e.length-1;i>=0;--i){const o=e[i],l=[t,...r.ancestors],c=o.$_validate(void 0,r.localize(r.path,l,o),s).value;if(a){if(void 0===c)continue;a=!1;}n.unshift(c);}n.length&&t.push(...n);},c.fastSplice=function(e,t){let r=t;for(;r<e.length;)e[r++]=e[r];--e.length;},c.validateSingle=function(e,t){("array"===e.type||e._flags._arrayItems)&&(s(!t._flags.single,"Cannot specify array item with single rule enabled"),t.$_setFlag("_arrayItems",!0,{clone:!1}));},c.sort=function(e,t,r,s,n){const a="ascending"===r.order?1:-1,i=-1*a,o=a,l=(l,u)=>{let f=c.compare(l,u,i,o);if(null!==f)return f;if(r.by&&(l=r.by.resolve(l,s,n),u=r.by.resolve(u,s,n)),f=c.compare(l,u,i,o),null!==f)return f;const m=typeof l;if(m!==typeof u)throw e.$_createError("array.sort.mismatching",t,null,s,n);if("number"!==m&&"string"!==m)throw e.$_createError("array.sort.unsupported",t,{type:m},s,n);return "number"===m?(l-u)*a:l<u?i:o};try{return {value:t.slice().sort(l)}}catch(e){return {errors:e}}},c.compare=function(e,t,r,s){return e===t?0:void 0===e?1:void 0===t?-1:null===e?s:null===t?r:null};},4937:(e,t,r)=>{const s=r(375),n=r(8068),a=r(8160),i=r(2036),o={isBool:function(e){return "boolean"==typeof e}};e.exports=n.extend({type:"boolean",flags:{sensitive:{default:!1}},terms:{falsy:{init:null,manifest:"values"},truthy:{init:null,manifest:"values"}},coerce(e,{schema:t}){if("boolean"!=typeof e){if("string"==typeof e){const r=t._flags.sensitive?e:e.toLowerCase();e="true"===r||"false"!==r&&e;}return "boolean"!=typeof e&&(e=t.$_terms.truthy&&t.$_terms.truthy.has(e,null,null,!t._flags.sensitive)||(!t.$_terms.falsy||!t.$_terms.falsy.has(e,null,null,!t._flags.sensitive))&&e),{value:e}}},validate(e,{error:t}){if("boolean"!=typeof e)return {value:e,errors:t("boolean.base")}},rules:{truthy:{method(...e){a.verifyFlat(e,"truthy");const t=this.clone();t.$_terms.truthy=t.$_terms.truthy||new i;for(let r=0;r<e.length;++r){const n=e[r];s(void 0!==n,"Cannot call truthy with undefined"),t.$_terms.truthy.add(n);}return t}},falsy:{method(...e){a.verifyFlat(e,"falsy");const t=this.clone();t.$_terms.falsy=t.$_terms.falsy||new i;for(let r=0;r<e.length;++r){const n=e[r];s(void 0!==n,"Cannot call falsy with undefined"),t.$_terms.falsy.add(n);}return t}},sensitive:{method(e=!0){return this.$_setFlag("sensitive",e)}}},cast:{number:{from:o.isBool,to:(e,t)=>e?1:0},string:{from:o.isBool,to:(e,t)=>e?"true":"false"}},manifest:{build:(e,t)=>(t.truthy&&(e=e.truthy(...t.truthy)),t.falsy&&(e=e.falsy(...t.falsy)),e)},messages:{"boolean.base":"{{#label}} must be a boolean"}});},7500:(e,t,r)=>{const s=r(375),n=r(8068),a=r(8160),i=r(3328),o={isDate:function(e){return e instanceof Date}};e.exports=n.extend({type:"date",coerce:{from:["number","string"],method:(e,{schema:t})=>({value:o.parse(e,t._flags.format)||e})},validate(e,{schema:t,error:r,prefs:s}){if(e instanceof Date&&!isNaN(e.getTime()))return;const n=t._flags.format;return s.convert&&n&&"string"==typeof e?{value:e,errors:r("date.format",{format:n})}:{value:e,errors:r("date.base")}},rules:{compare:{method:!1,validate(e,t,{date:r},{name:s,operator:n,args:i}){const o="now"===r?Date.now():r.getTime();return a.compare(e.getTime(),o,n)?e:t.error("date."+s,{limit:i.date,value:e})},args:[{name:"date",ref:!0,normalize:e=>"now"===e?e:o.parse(e),assert:e=>null!==e,message:"must have a valid date format"}]},format:{method(e){return s(["iso","javascript","unix"].includes(e),"Unknown date format",e),this.$_setFlag("format",e)}},greater:{method(e){return this.$_addRule({name:"greater",method:"compare",args:{date:e},operator:">"})}},iso:{method(){return this.format("iso")}},less:{method(e){return this.$_addRule({name:"less",method:"compare",args:{date:e},operator:"<"})}},max:{method(e){return this.$_addRule({name:"max",method:"compare",args:{date:e},operator:"<="})}},min:{method(e){return this.$_addRule({name:"min",method:"compare",args:{date:e},operator:">="})}},timestamp:{method(e="javascript"){return s(["javascript","unix"].includes(e),'"type" must be one of "javascript, unix"'),this.format(e)}}},cast:{number:{from:o.isDate,to:(e,t)=>e.getTime()},string:{from:o.isDate,to:(e,{prefs:t})=>i.date(e,t)}},messages:{"date.base":"{{#label}} must be a valid date","date.format":'{{#label}} must be in {msg("date.format." + #format) || #format} format',"date.greater":"{{#label}} must be greater than {{:#limit}}","date.less":"{{#label}} must be less than {{:#limit}}","date.max":"{{#label}} must be less than or equal to {{:#limit}}","date.min":"{{#label}} must be greater than or equal to {{:#limit}}","date.format.iso":"ISO 8601 date","date.format.javascript":"timestamp or number of milliseconds","date.format.unix":"timestamp or number of seconds"}}),o.parse=function(e,t){if(e instanceof Date)return e;if("string"!=typeof e&&(isNaN(e)||!isFinite(e)))return null;if(/^\s*$/.test(e))return null;if("iso"===t)return a.isIsoDate(e)?o.date(e.toString()):null;const r=e;if("string"==typeof e&&/^[+-]?\d+(\.\d+)?$/.test(e)&&(e=parseFloat(e)),t){if("javascript"===t)return o.date(1*e);if("unix"===t)return o.date(1e3*e);if("string"==typeof r)return null}return o.date(e)},o.date=function(e){const t=new Date(e);return isNaN(t.getTime())?null:t};},390:(e,t,r)=>{const s=r(375),n=r(7824);e.exports=n.extend({type:"function",properties:{typeof:"function"},rules:{arity:{method(e){return s(Number.isSafeInteger(e)&&e>=0,"n must be a positive integer"),this.$_addRule({name:"arity",args:{n:e}})},validate:(e,t,{n:r})=>e.length===r?e:t.error("function.arity",{n:r})},class:{method(){return this.$_addRule("class")},validate:(e,t)=>/^\s*class\s/.test(e.toString())?e:t.error("function.class",{value:e})},minArity:{method(e){return s(Number.isSafeInteger(e)&&e>0,"n must be a strict positive integer"),this.$_addRule({name:"minArity",args:{n:e}})},validate:(e,t,{n:r})=>e.length>=r?e:t.error("function.minArity",{n:r})},maxArity:{method(e){return s(Number.isSafeInteger(e)&&e>=0,"n must be a positive integer"),this.$_addRule({name:"maxArity",args:{n:e}})},validate:(e,t,{n:r})=>e.length<=r?e:t.error("function.maxArity",{n:r})}},messages:{"function.arity":"{{#label}} must have an arity of {{#n}}","function.class":"{{#label}} must be a class","function.maxArity":"{{#label}} must have an arity lesser or equal to {{#n}}","function.minArity":"{{#label}} must have an arity greater or equal to {{#n}}"}});},7824:(e,t,r)=>{const s=r(978),n=r(375),a=r(8571),i=r(3652),o=r(8068),l=r(8160),c=r(3292),u=r(6354),f=r(6133),m=r(3328),h={renameDefaults:{alias:!1,multiple:!1,override:!1}};e.exports=o.extend({type:"_keys",properties:{typeof:"object"},flags:{unknown:{default:void 0}},terms:{dependencies:{init:null},keys:{init:null,manifest:{mapped:{from:"schema",to:"key"}}},patterns:{init:null},renames:{init:null}},args:(e,t)=>e.keys(t),validate(e,{schema:t,error:r,state:s,prefs:n}){if(!e||typeof e!==t.$_property("typeof")||Array.isArray(e))return {value:e,errors:r("object.base",{type:t.$_property("typeof")})};if(!(t.$_terms.renames||t.$_terms.dependencies||t.$_terms.keys||t.$_terms.patterns||t.$_terms.externals))return;e=h.clone(e,n);const a=[];if(t.$_terms.renames&&!h.rename(t,e,s,n,a))return {value:e,errors:a};if(!t.$_terms.keys&&!t.$_terms.patterns&&!t.$_terms.dependencies)return {value:e,errors:a};const i=new Set(Object.keys(e));if(t.$_terms.keys){const r=[e,...s.ancestors];for(const o of t.$_terms.keys){const t=o.key,l=e[t];i.delete(t);const c=s.localize([...s.path,t],r,o),u=o.schema.$_validate(l,c,n);if(u.errors){if(n.abortEarly)return {value:e,errors:u.errors};void 0!==u.value&&(e[t]=u.value),a.push(...u.errors);}else "strip"===o.schema._flags.result||void 0===u.value&&void 0!==l?delete e[t]:void 0!==u.value&&(e[t]=u.value);}}if(i.size||t._flags._hasPatternMatch){const r=h.unknown(t,e,i,a,s,n);if(r)return r}if(t.$_terms.dependencies)for(const r of t.$_terms.dependencies){if(null!==r.key&&!1===h.isPresent(r.options)(r.key.resolve(e,s,n,null,{shadow:!1})))continue;const i=h.dependencies[r.rel](t,r,e,s,n);if(i){const r=t.$_createError(i.code,e,i.context,s,n);if(n.abortEarly)return {value:e,errors:r};a.push(r);}}return {value:e,errors:a}},rules:{and:{method(...e){return l.verifyFlat(e,"and"),h.dependency(this,"and",null,e)}},append:{method(e){return null==e||0===Object.keys(e).length?this:this.keys(e)}},assert:{method(e,t,r){m.isTemplate(e)||(e=c.ref(e)),n(void 0===r||"string"==typeof r,"Message must be a string"),t=this.$_compile(t,{appendPath:!0});const s=this.$_addRule({name:"assert",args:{subject:e,schema:t,message:r}});return s.$_mutateRegister(e),s.$_mutateRegister(t),s},validate(e,{error:t,prefs:r,state:s},{subject:n,schema:a,message:i}){const o=n.resolve(e,s,r),l=f.isRef(n)?n.absolute(s):[];return a.$_match(o,s.localize(l,[e,...s.ancestors],a),r)?e:t("object.assert",{subject:n,message:i})},args:["subject","schema","message"],multi:!0},instance:{method(e,t){return n("function"==typeof e,"constructor must be a function"),t=t||e.name,this.$_addRule({name:"instance",args:{constructor:e,name:t}})},validate:(e,t,{constructor:r,name:s})=>e instanceof r?e:t.error("object.instance",{type:s,value:e}),args:["constructor","name"]},keys:{method(e){n(void 0===e||"object"==typeof e,"Object schema must be a valid object"),n(!l.isSchema(e),"Object schema cannot be a joi schema");const t=this.clone();if(e)if(Object.keys(e).length){t.$_terms.keys=t.$_terms.keys?t.$_terms.keys.filter((t=>!e.hasOwnProperty(t.key))):new h.Keys;for(const r in e)l.tryWithPath((()=>t.$_terms.keys.push({key:r,schema:this.$_compile(e[r])})),r);}else t.$_terms.keys=new h.Keys;else t.$_terms.keys=null;return t.$_mutateRebuild()}},length:{method(e){return this.$_addRule({name:"length",args:{limit:e},operator:"="})},validate:(e,t,{limit:r},{name:s,operator:n,args:a})=>l.compare(Object.keys(e).length,r,n)?e:t.error("object."+s,{limit:a.limit,value:e}),args:[{name:"limit",ref:!0,assert:l.limit,message:"must be a positive integer"}]},max:{method(e){return this.$_addRule({name:"max",method:"length",args:{limit:e},operator:"<="})}},min:{method(e){return this.$_addRule({name:"min",method:"length",args:{limit:e},operator:">="})}},nand:{method(...e){return l.verifyFlat(e,"nand"),h.dependency(this,"nand",null,e)}},or:{method(...e){return l.verifyFlat(e,"or"),h.dependency(this,"or",null,e)}},oxor:{method(...e){return h.dependency(this,"oxor",null,e)}},pattern:{method(e,t,r={}){const s=e instanceof RegExp;s||(e=this.$_compile(e,{appendPath:!0})),n(void 0!==t,"Invalid rule"),l.assertOptions(r,["fallthrough","matches"]),s&&n(!e.flags.includes("g")&&!e.flags.includes("y"),"pattern should not use global or sticky mode"),t=this.$_compile(t,{appendPath:!0});const a=this.clone();a.$_terms.patterns=a.$_terms.patterns||[];const i={[s?"regex":"schema"]:e,rule:t};return r.matches&&(i.matches=this.$_compile(r.matches),"array"!==i.matches.type&&(i.matches=i.matches.$_root.array().items(i.matches)),a.$_mutateRegister(i.matches),a.$_setFlag("_hasPatternMatch",!0,{clone:!1})),r.fallthrough&&(i.fallthrough=!0),a.$_terms.patterns.push(i),a.$_mutateRegister(t),a}},ref:{method(){return this.$_addRule("ref")},validate:(e,t)=>f.isRef(e)?e:t.error("object.refType",{value:e})},regex:{method(){return this.$_addRule("regex")},validate:(e,t)=>e instanceof RegExp?e:t.error("object.regex",{value:e})},rename:{method(e,t,r={}){n("string"==typeof e||e instanceof RegExp,"Rename missing the from argument"),n("string"==typeof t||t instanceof m,"Invalid rename to argument"),n(t!==e,"Cannot rename key to same name:",e),l.assertOptions(r,["alias","ignoreUndefined","override","multiple"]);const a=this.clone();a.$_terms.renames=a.$_terms.renames||[];for(const t of a.$_terms.renames)n(t.from!==e,"Cannot rename the same key multiple times");return t instanceof m&&a.$_mutateRegister(t),a.$_terms.renames.push({from:e,to:t,options:s(h.renameDefaults,r)}),a}},schema:{method(e="any"){return this.$_addRule({name:"schema",args:{type:e}})},validate:(e,t,{type:r})=>!l.isSchema(e)||"any"!==r&&e.type!==r?t.error("object.schema",{type:r}):e},unknown:{method(e){return this.$_setFlag("unknown",!1!==e)}},with:{method(e,t,r={}){return h.dependency(this,"with",e,t,r)}},without:{method(e,t,r={}){return h.dependency(this,"without",e,t,r)}},xor:{method(...e){return l.verifyFlat(e,"xor"),h.dependency(this,"xor",null,e)}}},overrides:{default(e,t){return void 0===e&&(e=l.symbols.deepDefault),this.$_parent("default",e,t)}},rebuild(e){if(e.$_terms.keys){const t=new i.Sorter;for(const r of e.$_terms.keys)l.tryWithPath((()=>t.add(r,{after:r.schema.$_rootReferences(),group:r.key})),r.key);e.$_terms.keys=new h.Keys(...t.nodes);}},manifest:{build(e,t){if(t.keys&&(e=e.keys(t.keys)),t.dependencies)for(const{rel:r,key:s=null,peers:n,options:a}of t.dependencies)e=h.dependency(e,r,s,n,a);if(t.patterns)for(const{regex:r,schema:s,rule:n,fallthrough:a,matches:i}of t.patterns)e=e.pattern(r||s,n,{fallthrough:a,matches:i});if(t.renames)for(const{from:r,to:s,options:n}of t.renames)e=e.rename(r,s,n);return e}},messages:{"object.and":"{{#label}} contains {{#presentWithLabels}} without its required peers {{#missingWithLabels}}","object.assert":'{{#label}} is invalid because {if(#subject.key, `"` + #subject.key + `" failed to ` + (#message || "pass the assertion test"), #message || "the assertion failed")}',"object.base":"{{#label}} must be of type {{#type}}","object.instance":"{{#label}} must be an instance of {{:#type}}","object.length":'{{#label}} must have {{#limit}} key{if(#limit == 1, "", "s")}',"object.max":'{{#label}} must have less than or equal to {{#limit}} key{if(#limit == 1, "", "s")}',"object.min":'{{#label}} must have at least {{#limit}} key{if(#limit == 1, "", "s")}',"object.missing":"{{#label}} must contain at least one of {{#peersWithLabels}}","object.nand":"{{:#mainWithLabel}} must not exist simultaneously with {{#peersWithLabels}}","object.oxor":"{{#label}} contains a conflict between optional exclusive peers {{#peersWithLabels}}","object.pattern.match":"{{#label}} keys failed to match pattern requirements","object.refType":"{{#label}} must be a Joi reference","object.regex":"{{#label}} must be a RegExp object","object.rename.multiple":"{{#label}} cannot rename {{:#from}} because multiple renames are disabled and another key was already renamed to {{:#to}}","object.rename.override":"{{#label}} cannot rename {{:#from}} because override is disabled and target {{:#to}} exists","object.schema":"{{#label}} must be a Joi schema of {{#type}} type","object.unknown":"{{#label}} is not allowed","object.with":"{{:#mainWithLabel}} missing required peer {{:#peerWithLabel}}","object.without":"{{:#mainWithLabel}} conflict with forbidden peer {{:#peerWithLabel}}","object.xor":"{{#label}} contains a conflict between exclusive peers {{#peersWithLabels}}"}}),h.clone=function(e,t){if("object"==typeof e){if(t.nonEnumerables)return a(e,{shallow:!0});const r=Object.create(Object.getPrototypeOf(e));return Object.assign(r,e),r}const r=function(...t){return e.apply(this,t)};return r.prototype=a(e.prototype),Object.defineProperty(r,"name",{value:e.name,writable:!1}),Object.defineProperty(r,"length",{value:e.length,writable:!1}),Object.assign(r,e),r},h.dependency=function(e,t,r,s,a){n(null===r||"string"==typeof r,t,"key must be a strings"),a||(a=s.length>1&&"object"==typeof s[s.length-1]?s.pop():{}),l.assertOptions(a,["separator","isPresent"]),s=[].concat(s);const i=l.default(a.separator,"."),o=[];for(const e of s)n("string"==typeof e,t,"peers must be strings"),o.push(c.ref(e,{separator:i,ancestor:0,prefix:!1}));null!==r&&(r=c.ref(r,{separator:i,ancestor:0,prefix:!1}));const u=e.clone();return u.$_terms.dependencies=u.$_terms.dependencies||[],u.$_terms.dependencies.push(new h.Dependency(t,r,o,s,a)),u},h.dependencies={and(e,t,r,s,n){const a=[],i=[],o=t.peers.length,l=h.isPresent(t.options);for(const e of t.peers)!1===l(e.resolve(r,s,n,null,{shadow:!1}))?a.push(e.key):i.push(e.key);if(a.length!==o&&i.length!==o)return {code:"object.and",context:{present:i,presentWithLabels:h.keysToLabels(e,i),missing:a,missingWithLabels:h.keysToLabels(e,a)}}},nand(e,t,r,s,n){const a=[],i=h.isPresent(t.options);for(const e of t.peers)i(e.resolve(r,s,n,null,{shadow:!1}))&&a.push(e.key);if(a.length!==t.peers.length)return;const o=t.paths[0],l=t.paths.slice(1);return {code:"object.nand",context:{main:o,mainWithLabel:h.keysToLabels(e,o),peers:l,peersWithLabels:h.keysToLabels(e,l)}}},or(e,t,r,s,n){const a=h.isPresent(t.options);for(const e of t.peers)if(a(e.resolve(r,s,n,null,{shadow:!1})))return;return {code:"object.missing",context:{peers:t.paths,peersWithLabels:h.keysToLabels(e,t.paths)}}},oxor(e,t,r,s,n){const a=[],i=h.isPresent(t.options);for(const e of t.peers)i(e.resolve(r,s,n,null,{shadow:!1}))&&a.push(e.key);if(!a.length||1===a.length)return;const o={peers:t.paths,peersWithLabels:h.keysToLabels(e,t.paths)};return o.present=a,o.presentWithLabels=h.keysToLabels(e,a),{code:"object.oxor",context:o}},with(e,t,r,s,n){const a=h.isPresent(t.options);for(const i of t.peers)if(!1===a(i.resolve(r,s,n,null,{shadow:!1})))return {code:"object.with",context:{main:t.key.key,mainWithLabel:h.keysToLabels(e,t.key.key),peer:i.key,peerWithLabel:h.keysToLabels(e,i.key)}}},without(e,t,r,s,n){const a=h.isPresent(t.options);for(const i of t.peers)if(a(i.resolve(r,s,n,null,{shadow:!1})))return {code:"object.without",context:{main:t.key.key,mainWithLabel:h.keysToLabels(e,t.key.key),peer:i.key,peerWithLabel:h.keysToLabels(e,i.key)}}},xor(e,t,r,s,n){const a=[],i=h.isPresent(t.options);for(const e of t.peers)i(e.resolve(r,s,n,null,{shadow:!1}))&&a.push(e.key);if(1===a.length)return;const o={peers:t.paths,peersWithLabels:h.keysToLabels(e,t.paths)};return 0===a.length?{code:"object.missing",context:o}:(o.present=a,o.presentWithLabels=h.keysToLabels(e,a),{code:"object.xor",context:o})}},h.keysToLabels=function(e,t){return Array.isArray(t)?t.map((t=>e.$_mapLabels(t))):e.$_mapLabels(t)},h.isPresent=function(e){return "function"==typeof e.isPresent?e.isPresent:e=>void 0!==e},h.rename=function(e,t,r,s,n){const a={};for(const i of e.$_terms.renames){const o=[],l="string"!=typeof i.from;if(l)for(const e in t){if(void 0===t[e]&&i.options.ignoreUndefined)continue;if(e===i.to)continue;const r=i.from.exec(e);r&&o.push({from:e,to:i.to,match:r});}else !Object.prototype.hasOwnProperty.call(t,i.from)||void 0===t[i.from]&&i.options.ignoreUndefined||o.push(i);for(const c of o){const o=c.from;let u=c.to;if(u instanceof m&&(u=u.render(t,r,s,c.match)),o!==u){if(!i.options.multiple&&a[u]&&(n.push(e.$_createError("object.rename.multiple",t,{from:o,to:u,pattern:l},r,s)),s.abortEarly))return !1;if(Object.prototype.hasOwnProperty.call(t,u)&&!i.options.override&&!a[u]&&(n.push(e.$_createError("object.rename.override",t,{from:o,to:u,pattern:l},r,s)),s.abortEarly))return !1;void 0===t[o]?delete t[u]:t[u]=t[o],a[u]=!0,i.options.alias||delete t[o];}}}return !0},h.unknown=function(e,t,r,s,n,a){if(e.$_terms.patterns){let i=!1;const o=e.$_terms.patterns.map((e=>{if(e.matches)return i=!0,[]})),l=[t,...n.ancestors];for(const i of r){const c=t[i],u=[...n.path,i];for(let f=0;f<e.$_terms.patterns.length;++f){const m=e.$_terms.patterns[f];if(m.regex){const e=m.regex.test(i);if(n.mainstay.tracer.debug(n,"rule",`pattern.${f}`,e?"pass":"error"),!e)continue}else if(!m.schema.$_match(i,n.nest(m.schema,`pattern.${f}`),a))continue;r.delete(i);const h=n.localize(u,l,{schema:m.rule,key:i}),d=m.rule.$_validate(c,h,a);if(d.errors){if(a.abortEarly)return {value:t,errors:d.errors};s.push(...d.errors);}if(m.matches&&o[f].push(i),t[i]=d.value,!m.fallthrough)break}}if(i)for(let r=0;r<o.length;++r){const i=o[r];if(!i)continue;const c=e.$_terms.patterns[r].matches,f=n.localize(n.path,l,c),m=c.$_validate(i,f,a);if(m.errors){const r=u.details(m.errors,{override:!1});r.matches=i;const o=e.$_createError("object.pattern.match",t,r,n,a);if(a.abortEarly)return {value:t,errors:o};s.push(o);}}}if(r.size&&(e.$_terms.keys||e.$_terms.patterns)){if(a.stripUnknown&&void 0===e._flags.unknown||a.skipFunctions){const e=!(!a.stripUnknown||!0!==a.stripUnknown&&!a.stripUnknown.objects);for(const s of r)e?(delete t[s],r.delete(s)):"function"==typeof t[s]&&r.delete(s);}if(!l.default(e._flags.unknown,a.allowUnknown))for(const i of r){const r=n.localize([...n.path,i],[]),o=e.$_createError("object.unknown",t[i],{child:i},r,a,{flags:!1});if(a.abortEarly)return {value:t,errors:o};s.push(o);}}},h.Dependency=class{constructor(e,t,r,s,n){this.rel=e,this.key=t,this.peers=r,this.paths=s,this.options=n;}describe(){const e={rel:this.rel,peers:this.paths};return null!==this.key&&(e.key=this.key.key),"."!==this.peers[0].separator&&(e.options={...e.options,separator:this.peers[0].separator}),this.options.isPresent&&(e.options={...e.options,isPresent:this.options.isPresent}),e}},h.Keys=class extends Array{concat(e){const t=this.slice(),r=new Map;for(let e=0;e<t.length;++e)r.set(t[e].key,e);for(const s of e){const e=s.key,n=r.get(e);void 0!==n?t[n]={key:e,schema:t[n].schema.concat(s.schema)}:t.push(s);}return t}};},8785:(e,t,r)=>{const s=r(375),n=r(8068),a=r(8160),i=r(3292),o=r(6354),l={};e.exports=n.extend({type:"link",properties:{schemaChain:!0},terms:{link:{init:null,manifest:"single",register:!1}},args:(e,t)=>e.ref(t),validate(e,{schema:t,state:r,prefs:n}){s(t.$_terms.link,"Uninitialized link schema");const a=l.generate(t,e,r,n),i=t.$_terms.link[0].ref;return a.$_validate(e,r.nest(a,`link:${i.display}:${a.type}`),n)},generate:(e,t,r,s)=>l.generate(e,t,r,s),rules:{ref:{method(e){s(!this.$_terms.link,"Cannot reinitialize schema"),e=i.ref(e),s("value"===e.type||"local"===e.type,"Invalid reference type:",e.type),s("local"===e.type||"root"===e.ancestor||e.ancestor>0,"Link cannot reference itself");const t=this.clone();return t.$_terms.link=[{ref:e}],t}},relative:{method(e=!0){return this.$_setFlag("relative",e)}}},overrides:{concat(e){s(this.$_terms.link,"Uninitialized link schema"),s(a.isSchema(e),"Invalid schema object"),s("link"!==e.type,"Cannot merge type link with another link");const t=this.clone();return t.$_terms.whens||(t.$_terms.whens=[]),t.$_terms.whens.push({concat:e}),t.$_mutateRebuild()}},manifest:{build:(e,t)=>(s(t.link,"Invalid link description missing link"),e.ref(t.link))}}),l.generate=function(e,t,r,s){let n=r.mainstay.links.get(e);if(n)return n._generate(t,r,s).schema;const a=e.$_terms.link[0].ref,{perspective:i,path:o}=l.perspective(a,r);l.assert(i,"which is outside of schema boundaries",a,e,r,s);try{n=o.length?i.$_reach(o):i;}catch(t){l.assert(!1,"to non-existing schema",a,e,r,s);}return l.assert("link"!==n.type,"which is another link",a,e,r,s),e._flags.relative||r.mainstay.links.set(e,n),n._generate(t,r,s).schema},l.perspective=function(e,t){if("local"===e.type){for(const{schema:r,key:s}of t.schemas){if((r._flags.id||s)===e.path[0])return {perspective:r,path:e.path.slice(1)};if(r.$_terms.shared)for(const t of r.$_terms.shared)if(t._flags.id===e.path[0])return {perspective:t,path:e.path.slice(1)}}return {perspective:null,path:null}}return "root"===e.ancestor?{perspective:t.schemas[t.schemas.length-1].schema,path:e.path}:{perspective:t.schemas[e.ancestor]&&t.schemas[e.ancestor].schema,path:e.path}},l.assert=function(e,t,r,n,a,i){e||s(!1,`"${o.label(n._flags,a,i)}" contains link reference "${r.display}" ${t}`);};},3832:(e,t,r)=>{const s=r(375),n=r(8068),a=r(8160),i={numberRx:/^\s*[+-]?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+))(?:e([+-]?\d+))?\s*$/i,precisionRx:/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/,exponentialPartRegex:/[eE][+-]?\d+$/,leadingSignAndZerosRegex:/^[+-]?(0*)?/,dotRegex:/\./,trailingZerosRegex:/0+$/,decimalPlaces(e){const t=e.toString(),r=t.indexOf("."),s=t.indexOf("e");return (r<0?0:(s<0?t.length:s)-r-1)+(s<0?0:Math.max(0,-parseInt(t.slice(s+1))))}};e.exports=n.extend({type:"number",flags:{unsafe:{default:!1}},coerce:{from:"string",method(e,{schema:t,error:r}){if(!e.match(i.numberRx))return;e=e.trim();const s={value:parseFloat(e)};if(0===s.value&&(s.value=0),!t._flags.unsafe)if(e.match(/e/i)){if(i.extractSignificantDigits(e)!==i.extractSignificantDigits(String(s.value)))return s.errors=r("number.unsafe"),s}else {const t=s.value.toString();if(t.match(/e/i))return s;if(t!==i.normalizeDecimal(e))return s.errors=r("number.unsafe"),s}return s}},validate(e,{schema:t,error:r,prefs:s}){if(e===1/0||e===-1/0)return {value:e,errors:r("number.infinity")};if(!a.isNumber(e))return {value:e,errors:r("number.base")};const n={value:e};if(s.convert){const e=t.$_getRule("precision");if(e){const t=Math.pow(10,e.args.limit);n.value=Math.round(n.value*t)/t;}}return 0===n.value&&(n.value=0),!t._flags.unsafe&&(e>Number.MAX_SAFE_INTEGER||e<Number.MIN_SAFE_INTEGER)&&(n.errors=r("number.unsafe")),n},rules:{compare:{method:!1,validate:(e,t,{limit:r},{name:s,operator:n,args:i})=>a.compare(e,r,n)?e:t.error("number."+s,{limit:i.limit,value:e}),args:[{name:"limit",ref:!0,assert:a.isNumber,message:"must be a number"}]},greater:{method(e){return this.$_addRule({name:"greater",method:"compare",args:{limit:e},operator:">"})}},integer:{method(){return this.$_addRule("integer")},validate:(e,t)=>Math.trunc(e)-e==0?e:t.error("number.integer")},less:{method(e){return this.$_addRule({name:"less",method:"compare",args:{limit:e},operator:"<"})}},max:{method(e){return this.$_addRule({name:"max",method:"compare",args:{limit:e},operator:"<="})}},min:{method(e){return this.$_addRule({name:"min",method:"compare",args:{limit:e},operator:">="})}},multiple:{method(e){const t="number"==typeof e?i.decimalPlaces(e):null,r=Math.pow(10,t);return this.$_addRule({name:"multiple",args:{base:e,baseDecimalPlace:t,pfactor:r}})},validate:(e,t,{base:r,baseDecimalPlace:s,pfactor:n},a)=>i.decimalPlaces(e)>s?t.error("number.multiple",{multiple:a.args.base,value:e}):Math.round(n*e)%Math.round(n*r)==0?e:t.error("number.multiple",{multiple:a.args.base,value:e}),args:[{name:"base",ref:!0,assert:e=>"number"==typeof e&&isFinite(e)&&e>0,message:"must be a positive number"},"baseDecimalPlace","pfactor"],multi:!0},negative:{method(){return this.sign("negative")}},port:{method(){return this.$_addRule("port")},validate:(e,t)=>Number.isSafeInteger(e)&&e>=0&&e<=65535?e:t.error("number.port")},positive:{method(){return this.sign("positive")}},precision:{method(e){return s(Number.isSafeInteger(e),"limit must be an integer"),this.$_addRule({name:"precision",args:{limit:e}})},validate(e,t,{limit:r}){const s=e.toString().match(i.precisionRx);return Math.max((s[1]?s[1].length:0)-(s[2]?parseInt(s[2],10):0),0)<=r?e:t.error("number.precision",{limit:r,value:e})},convert:!0},sign:{method(e){return s(["negative","positive"].includes(e),"Invalid sign",e),this.$_addRule({name:"sign",args:{sign:e}})},validate:(e,t,{sign:r})=>"negative"===r&&e<0||"positive"===r&&e>0?e:t.error(`number.${r}`)},unsafe:{method(e=!0){return s("boolean"==typeof e,"enabled must be a boolean"),this.$_setFlag("unsafe",e)}}},cast:{string:{from:e=>"number"==typeof e,to:(e,t)=>e.toString()}},messages:{"number.base":"{{#label}} must be a number","number.greater":"{{#label}} must be greater than {{#limit}}","number.infinity":"{{#label}} cannot be infinity","number.integer":"{{#label}} must be an integer","number.less":"{{#label}} must be less than {{#limit}}","number.max":"{{#label}} must be less than or equal to {{#limit}}","number.min":"{{#label}} must be greater than or equal to {{#limit}}","number.multiple":"{{#label}} must be a multiple of {{#multiple}}","number.negative":"{{#label}} must be a negative number","number.port":"{{#label}} must be a valid port","number.positive":"{{#label}} must be a positive number","number.precision":"{{#label}} must have no more than {{#limit}} decimal places","number.unsafe":"{{#label}} must be a safe number"}}),i.extractSignificantDigits=function(e){return e.replace(i.exponentialPartRegex,"").replace(i.dotRegex,"").replace(i.trailingZerosRegex,"").replace(i.leadingSignAndZerosRegex,"")},i.normalizeDecimal=function(e){return (e=e.replace(/^\+/,"").replace(/\.0*$/,"").replace(/^(-?)\.([^\.]*)$/,"$10.$2").replace(/^(-?)0+([0-9])/,"$1$2")).includes(".")&&e.endsWith("0")&&(e=e.replace(/0+$/,"")),"-0"===e?"0":e};},8966:(e,t,r)=>{const s=r(7824);e.exports=s.extend({type:"object",cast:{map:{from:e=>e&&"object"==typeof e,to:(e,t)=>new Map(Object.entries(e))}}});},7417:(e,t,r)=>{const s=r(375),n=r(5380),a=r(1745),i=r(9959),o=r(6064),l=r(9926),c=r(5752),u=r(8068),f=r(8160),m={tlds:l instanceof Set&&{tlds:{allow:l,deny:null}},base64Regex:{true:{true:/^(?:[\w\-]{2}[\w\-]{2})*(?:[\w\-]{2}==|[\w\-]{3}=)?$/,false:/^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/},false:{true:/^(?:[\w\-]{2}[\w\-]{2})*(?:[\w\-]{2}(==)?|[\w\-]{3}=?)?$/,false:/^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/}},dataUriRegex:/^data:[\w+.-]+\/[\w+.-]+;((charset=[\w-]+|base64),)?(.*)$/,hexRegex:{withPrefix:/^0x[0-9a-f]+$/i,withOptionalPrefix:/^(?:0x)?[0-9a-f]+$/i,withoutPrefix:/^[0-9a-f]+$/i},ipRegex:i.regex({cidr:"forbidden"}).regex,isoDurationRegex:/^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/,guidBrackets:{"{":"}","[":"]","(":")","":""},guidVersions:{uuidv1:"1",uuidv2:"2",uuidv3:"3",uuidv4:"4",uuidv5:"5",uuidv6:"6",uuidv7:"7",uuidv8:"8"},guidSeparators:new Set([void 0,!0,!1,"-",":"]),normalizationForms:["NFC","NFD","NFKC","NFKD"]};e.exports=u.extend({type:"string",flags:{insensitive:{default:!1},truncate:{default:!1}},terms:{replacements:{init:null}},coerce:{from:"string",method(e,{schema:t,state:r,prefs:s}){const n=t.$_getRule("normalize");n&&(e=e.normalize(n.args.form));const a=t.$_getRule("case");a&&(e="upper"===a.args.direction?e.toLocaleUpperCase():e.toLocaleLowerCase());const i=t.$_getRule("trim");if(i&&i.args.enabled&&(e=e.trim()),t.$_terms.replacements)for(const r of t.$_terms.replacements)e=e.replace(r.pattern,r.replacement);const o=t.$_getRule("hex");if(o&&o.args.options.byteAligned&&e.length%2!=0&&(e=`0${e}`),t.$_getRule("isoDate")){const t=m.isoDate(e);t&&(e=t);}if(t._flags.truncate){const n=t.$_getRule("max");if(n){let a=n.args.limit;if(f.isResolvable(a)&&(a=a.resolve(e,r,s),!f.limit(a)))return {value:e,errors:t.$_createError("any.ref",a,{ref:n.args.limit,arg:"limit",reason:"must be a positive integer"},r,s)};e=e.slice(0,a);}}return {value:e}}},validate(e,{schema:t,error:r}){if("string"!=typeof e)return {value:e,errors:r("string.base")};if(""===e){const s=t.$_getRule("min");if(s&&0===s.args.limit)return;return {value:e,errors:r("string.empty")}}},rules:{alphanum:{method(){return this.$_addRule("alphanum")},validate:(e,t)=>/^[a-zA-Z0-9]+$/.test(e)?e:t.error("string.alphanum")},base64:{method(e={}){return f.assertOptions(e,["paddingRequired","urlSafe"]),e={urlSafe:!1,paddingRequired:!0,...e},s("boolean"==typeof e.paddingRequired,"paddingRequired must be boolean"),s("boolean"==typeof e.urlSafe,"urlSafe must be boolean"),this.$_addRule({name:"base64",args:{options:e}})},validate:(e,t,{options:r})=>m.base64Regex[r.paddingRequired][r.urlSafe].test(e)?e:t.error("string.base64")},case:{method(e){return s(["lower","upper"].includes(e),"Invalid case:",e),this.$_addRule({name:"case",args:{direction:e}})},validate:(e,t,{direction:r})=>"lower"===r&&e===e.toLocaleLowerCase()||"upper"===r&&e===e.toLocaleUpperCase()?e:t.error(`string.${r}case`),convert:!0},creditCard:{method(){return this.$_addRule("creditCard")},validate(e,t){let r=e.length,s=0,n=1;for(;r--;){const t=e.charAt(r)*n;s+=t-9*(t>9),n^=3;}return s>0&&s%10==0?e:t.error("string.creditCard")}},dataUri:{method(e={}){return f.assertOptions(e,["paddingRequired"]),e={paddingRequired:!0,...e},s("boolean"==typeof e.paddingRequired,"paddingRequired must be boolean"),this.$_addRule({name:"dataUri",args:{options:e}})},validate(e,t,{options:r}){const s=e.match(m.dataUriRegex);if(s){if(!s[2])return e;if("base64"!==s[2])return e;if(m.base64Regex[r.paddingRequired].false.test(s[3]))return e}return t.error("string.dataUri")}},domain:{method(e){e&&f.assertOptions(e,["allowFullyQualified","allowUnicode","maxDomainSegments","minDomainSegments","tlds"]);const t=m.addressOptions(e);return this.$_addRule({name:"domain",args:{options:e},address:t})},validate:(e,t,r,{address:s})=>n.isValid(e,s)?e:t.error("string.domain")},email:{method(e={}){f.assertOptions(e,["allowFullyQualified","allowUnicode","ignoreLength","maxDomainSegments","minDomainSegments","multiple","separator","tlds"]),s(void 0===e.multiple||"boolean"==typeof e.multiple,"multiple option must be an boolean");const t=m.addressOptions(e),r=new RegExp(`\\s*[${e.separator?o(e.separator):","}]\\s*`);return this.$_addRule({name:"email",args:{options:e},regex:r,address:t})},validate(e,t,{options:r},{regex:s,address:n}){const i=r.multiple?e.split(s):[e],o=[];for(const e of i)a.isValid(e,n)||o.push(e);return o.length?t.error("string.email",{value:e,invalids:o}):e}},guid:{alias:"uuid",method(e={}){f.assertOptions(e,["version","separator"]);let t="";if(e.version){const r=[].concat(e.version);s(r.length>=1,"version must have at least 1 valid version specified");const n=new Set;for(let e=0;e<r.length;++e){const a=r[e];s("string"==typeof a,"version at position "+e+" must be a string");const i=m.guidVersions[a.toLowerCase()];s(i,"version at position "+e+" must be one of "+Object.keys(m.guidVersions).join(", ")),s(!n.has(i),"version at position "+e+" must not be a duplicate"),t+=i,n.add(i);}}s(m.guidSeparators.has(e.separator),'separator must be one of true, false, "-", or ":"');const r=void 0===e.separator?"[:-]?":!0===e.separator?"[:-]":!1===e.separator?"[]?":`\\${e.separator}`,n=new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}(${r})[0-9A-F]{4}\\2?[${t||"0-9A-F"}][0-9A-F]{3}\\2?[${t?"89AB":"0-9A-F"}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`,"i");return this.$_addRule({name:"guid",args:{options:e},regex:n})},validate(e,t,r,{regex:s}){const n=s.exec(e);return n?m.guidBrackets[n[1]]!==n[n.length-1]?t.error("string.guid"):e:t.error("string.guid")}},hex:{method(e={}){return f.assertOptions(e,["byteAligned","prefix"]),e={byteAligned:!1,prefix:!1,...e},s("boolean"==typeof e.byteAligned,"byteAligned must be boolean"),s("boolean"==typeof e.prefix||"optional"===e.prefix,'prefix must be boolean or "optional"'),this.$_addRule({name:"hex",args:{options:e}})},validate:(e,t,{options:r})=>("optional"===r.prefix?m.hexRegex.withOptionalPrefix:!0===r.prefix?m.hexRegex.withPrefix:m.hexRegex.withoutPrefix).test(e)?r.byteAligned&&e.length%2!=0?t.error("string.hexAlign"):e:t.error("string.hex")},hostname:{method(){return this.$_addRule("hostname")},validate:(e,t)=>n.isValid(e,{minDomainSegments:1})||m.ipRegex.test(e)?e:t.error("string.hostname")},insensitive:{method(){return this.$_setFlag("insensitive",!0)}},ip:{method(e={}){f.assertOptions(e,["cidr","version"]);const{cidr:t,versions:r,regex:s}=i.regex(e),n=e.version?r:void 0;return this.$_addRule({name:"ip",args:{options:{cidr:t,version:n}},regex:s})},validate:(e,t,{options:r},{regex:s})=>s.test(e)?e:r.version?t.error("string.ipVersion",{value:e,cidr:r.cidr,version:r.version}):t.error("string.ip",{value:e,cidr:r.cidr})},isoDate:{method(){return this.$_addRule("isoDate")},validate:(e,{error:t})=>m.isoDate(e)?e:t("string.isoDate")},isoDuration:{method(){return this.$_addRule("isoDuration")},validate:(e,t)=>m.isoDurationRegex.test(e)?e:t.error("string.isoDuration")},length:{method(e,t){return m.length(this,"length",e,"=",t)},validate(e,t,{limit:r,encoding:s},{name:n,operator:a,args:i}){const o=!s&&e.length;return f.compare(o,r,a)?e:t.error("string."+n,{limit:i.limit,value:e,encoding:s})},args:[{name:"limit",ref:!0,assert:f.limit,message:"must be a positive integer"},"encoding"]},lowercase:{method(){return this.case("lower")}},max:{method(e,t){return m.length(this,"max",e,"<=",t)},args:["limit","encoding"]},min:{method(e,t){return m.length(this,"min",e,">=",t)},args:["limit","encoding"]},normalize:{method(e="NFC"){return s(m.normalizationForms.includes(e),"normalization form must be one of "+m.normalizationForms.join(", ")),this.$_addRule({name:"normalize",args:{form:e}})},validate:(e,{error:t},{form:r})=>e===e.normalize(r)?e:t("string.normalize",{value:e,form:r}),convert:!0},pattern:{alias:"regex",method(e,t={}){s(e instanceof RegExp,"regex must be a RegExp"),s(!e.flags.includes("g")&&!e.flags.includes("y"),"regex should not use global or sticky mode"),"string"==typeof t&&(t={name:t}),f.assertOptions(t,["invert","name"]);const r=["string.pattern",t.invert?".invert":"",t.name?".name":".base"].join("");return this.$_addRule({name:"pattern",args:{regex:e,options:t},errorCode:r})},validate:(e,t,{regex:r,options:s},{errorCode:n})=>r.test(e)^s.invert?e:t.error(n,{name:s.name,regex:r,value:e}),args:["regex","options"],multi:!0},replace:{method(e,t){"string"==typeof e&&(e=new RegExp(o(e),"g")),s(e instanceof RegExp,"pattern must be a RegExp"),s("string"==typeof t,"replacement must be a String");const r=this.clone();return r.$_terms.replacements||(r.$_terms.replacements=[]),r.$_terms.replacements.push({pattern:e,replacement:t}),r}},token:{method(){return this.$_addRule("token")},validate:(e,t)=>/^\w+$/.test(e)?e:t.error("string.token")},trim:{method(e=!0){return s("boolean"==typeof e,"enabled must be a boolean"),this.$_addRule({name:"trim",args:{enabled:e}})},validate:(e,t,{enabled:r})=>r&&e!==e.trim()?t.error("string.trim"):e,convert:!0},truncate:{method(e=!0){return s("boolean"==typeof e,"enabled must be a boolean"),this.$_setFlag("truncate",e)}},uppercase:{method(){return this.case("upper")}},uri:{method(e={}){f.assertOptions(e,["allowRelative","allowQuerySquareBrackets","domain","relativeOnly","scheme","encodeUri"]),e.domain&&f.assertOptions(e.domain,["allowFullyQualified","allowUnicode","maxDomainSegments","minDomainSegments","tlds"]);const{regex:t,scheme:r}=c.regex(e),s=e.domain?m.addressOptions(e.domain):null;return this.$_addRule({name:"uri",args:{options:e},regex:t,domain:s,scheme:r})},validate(e,t,{options:r},{regex:s,domain:a,scheme:i}){if(["http:/","https:/"].includes(e))return t.error("string.uri");let o=s.exec(e);if(!o&&t.prefs.convert&&r.encodeUri){const t=encodeURI(e);o=s.exec(t),o&&(e=t);}if(o){const s=o[1]||o[2];return !a||r.allowRelative&&!s||n.isValid(s,a)?e:t.error("string.domain",{value:s})}return r.relativeOnly?t.error("string.uriRelativeOnly"):r.scheme?t.error("string.uriCustomScheme",{scheme:i,value:e}):t.error("string.uri")}}},manifest:{build(e,t){if(t.replacements)for(const{pattern:r,replacement:s}of t.replacements)e=e.replace(r,s);return e}},messages:{"string.alphanum":"{{#label}} must only contain alpha-numeric characters","string.base":"{{#label}} must be a string","string.base64":"{{#label}} must be a valid base64 string","string.creditCard":"{{#label}} must be a credit card","string.dataUri":"{{#label}} must be a valid dataUri string","string.domain":"{{#label}} must contain a valid domain name","string.email":"{{#label}} must be a valid email","string.empty":"{{#label}} is not allowed to be empty","string.guid":"{{#label}} must be a valid GUID","string.hex":"{{#label}} must only contain hexadecimal characters","string.hexAlign":"{{#label}} hex decoded representation must be byte aligned","string.hostname":"{{#label}} must be a valid hostname","string.ip":"{{#label}} must be a valid ip address with a {{#cidr}} CIDR","string.ipVersion":"{{#label}} must be a valid ip address of one of the following versions {{#version}} with a {{#cidr}} CIDR","string.isoDate":"{{#label}} must be in iso format","string.isoDuration":"{{#label}} must be a valid ISO 8601 duration","string.length":"{{#label}} length must be {{#limit}} characters long","string.lowercase":"{{#label}} must only contain lowercase characters","string.max":"{{#label}} length must be less than or equal to {{#limit}} characters long","string.min":"{{#label}} length must be at least {{#limit}} characters long","string.normalize":"{{#label}} must be unicode normalized in the {{#form}} form","string.token":"{{#label}} must only contain alpha-numeric and underscore characters","string.pattern.base":"{{#label}} with value {:[.]} fails to match the required pattern: {{#regex}}","string.pattern.name":"{{#label}} with value {:[.]} fails to match the {{#name}} pattern","string.pattern.invert.base":"{{#label}} with value {:[.]} matches the inverted pattern: {{#regex}}","string.pattern.invert.name":"{{#label}} with value {:[.]} matches the inverted {{#name}} pattern","string.trim":"{{#label}} must not have leading or trailing whitespace","string.uri":"{{#label}} must be a valid uri","string.uriCustomScheme":"{{#label}} must be a valid uri with a scheme matching the {{#scheme}} pattern","string.uriRelativeOnly":"{{#label}} must be a valid relative uri","string.uppercase":"{{#label}} must only contain uppercase characters"}}),m.addressOptions=function(e){if(!e)return m.tlds||e;if(s(void 0===e.minDomainSegments||Number.isSafeInteger(e.minDomainSegments)&&e.minDomainSegments>0,"minDomainSegments must be a positive integer"),s(void 0===e.maxDomainSegments||Number.isSafeInteger(e.maxDomainSegments)&&e.maxDomainSegments>0,"maxDomainSegments must be a positive integer"),!1===e.tlds)return e;if(!0===e.tlds||void 0===e.tlds)return s(m.tlds,"Built-in TLD list disabled"),Object.assign({},e,m.tlds);s("object"==typeof e.tlds,"tlds must be true, false, or an object");const t=e.tlds.deny;if(t)return Array.isArray(t)&&(e=Object.assign({},e,{tlds:{deny:new Set(t)}})),s(e.tlds.deny instanceof Set,"tlds.deny must be an array, Set, or boolean"),s(!e.tlds.allow,"Cannot specify both tlds.allow and tlds.deny lists"),m.validateTlds(e.tlds.deny,"tlds.deny"),e;const r=e.tlds.allow;return r?!0===r?(s(m.tlds,"Built-in TLD list disabled"),Object.assign({},e,m.tlds)):(Array.isArray(r)&&(e=Object.assign({},e,{tlds:{allow:new Set(r)}})),s(e.tlds.allow instanceof Set,"tlds.allow must be an array, Set, or boolean"),m.validateTlds(e.tlds.allow,"tlds.allow"),e):e},m.validateTlds=function(e,t){for(const r of e)s(n.isValid(r,{minDomainSegments:1,maxDomainSegments:1}),`${t} must contain valid top level domain names`);},m.isoDate=function(e){if(!f.isIsoDate(e))return null;/.*T.*[+-]\d\d$/.test(e)&&(e+="00");const t=new Date(e);return isNaN(t.getTime())?null:t.toISOString()},m.length=function(e,t,r,n,a){return s(!a||!1,"Invalid encoding:",a),e.$_addRule({name:t,method:"length",args:{limit:r,encoding:a},operator:n})};},8826:(e,t,r)=>{const s=r(375),n=r(8068),a={};a.Map=class extends Map{slice(){return new a.Map(this)}},e.exports=n.extend({type:"symbol",terms:{map:{init:new a.Map}},coerce:{method(e,{schema:t,error:r}){const s=t.$_terms.map.get(e);return s&&(e=s),t._flags.only&&"symbol"!=typeof e?{value:e,errors:r("symbol.map",{map:t.$_terms.map})}:{value:e}}},validate(e,{error:t}){if("symbol"!=typeof e)return {value:e,errors:t("symbol.base")}},rules:{map:{method(e){e&&!e[Symbol.iterator]&&"object"==typeof e&&(e=Object.entries(e)),s(e&&e[Symbol.iterator],"Iterable must be an iterable or object");const t=this.clone(),r=[];for(const n of e){s(n&&n[Symbol.iterator],"Entry must be an iterable");const[e,a]=n;s("object"!=typeof e&&"function"!=typeof e&&"symbol"!=typeof e,"Key must not be of type object, function, or Symbol"),s("symbol"==typeof a,"Value must be a Symbol"),t.$_terms.map.set(e,a),r.push(a);}return t.valid(...r)}}},manifest:{build:(e,t)=>(t.map&&(e=e.map(t.map)),e)},messages:{"symbol.base":"{{#label}} must be a symbol","symbol.map":"{{#label}} must be one of {{#map}}"}});},8863:(e,t,r)=>{const s=r(375),n=r(8571),a=r(738),i=r(9621),o=r(8160),l=r(6354),c=r(493),u={result:Symbol("result")};t.entry=function(e,t,r){let n=o.defaults;r&&(s(void 0===r.warnings,"Cannot override warnings preference in synchronous validation"),s(void 0===r.artifacts,"Cannot override artifacts preference in synchronous validation"),n=o.preferences(o.defaults,r));const a=u.entry(e,t,n);s(!a.mainstay.externals.length,"Schema with external rules must use validateAsync()");const i={value:a.value};return a.error&&(i.error=a.error),a.mainstay.warnings.length&&(i.warning=l.details(a.mainstay.warnings)),a.mainstay.debug&&(i.debug=a.mainstay.debug),a.mainstay.artifacts&&(i.artifacts=a.mainstay.artifacts),i},t.entryAsync=async function(e,t,r){let s=o.defaults;r&&(s=o.preferences(o.defaults,r));const n=u.entry(e,t,s),a=n.mainstay;if(n.error)throw a.debug&&(n.error.debug=a.debug),n.error;if(a.externals.length){let t=n.value;const c=[];for(const n of a.externals){const f=n.state.path,m="link"===n.schema.type?a.links.get(n.schema):null;let h,d,p=t;const g=f.length?[t]:[],y=f.length?i(e,f):e;if(f.length){h=f[f.length-1];let e=t;for(const t of f.slice(0,-1))e=e[t],g.unshift(e);d=g[0],p=d[h];}try{const e=(e,t)=>(m||n.schema).$_createError(e,p,t,n.state,s),i=await n.method(p,{schema:n.schema,linked:m,state:n.state,prefs:r,original:y,error:e,errorsArray:u.errorsArray,warn:(e,t)=>a.warnings.push((m||n.schema).$_createError(e,p,t,n.state,s)),message:(e,t)=>(m||n.schema).$_createError("external",p,t,n.state,s,{messages:e})});if(void 0===i||i===p)continue;if(i instanceof l.Report){if(a.tracer.log(n.schema,n.state,"rule","external","error"),c.push(i),s.abortEarly)break;continue}if(Array.isArray(i)&&i[o.symbols.errors]){if(a.tracer.log(n.schema,n.state,"rule","external","error"),c.push(...i),s.abortEarly)break;continue}d?(a.tracer.value(n.state,"rule",p,i,"external"),d[h]=i):(a.tracer.value(n.state,"rule",t,i,"external"),t=i);}catch(e){throw s.errors.label&&(e.message+=` (${n.label})`),e}}if(n.value=t,c.length)throw n.error=l.process(c,e,s),a.debug&&(n.error.debug=a.debug),n.error}if(!s.warnings&&!s.debug&&!s.artifacts)return n.value;const c={value:n.value};return a.warnings.length&&(c.warning=l.details(a.warnings)),a.debug&&(c.debug=a.debug),a.artifacts&&(c.artifacts=a.artifacts),c},u.Mainstay=class{constructor(e,t,r){this.externals=[],this.warnings=[],this.tracer=e,this.debug=t,this.links=r,this.shadow=null,this.artifacts=null,this._snapshots=[];}snapshot(){this._snapshots.push({externals:this.externals.slice(),warnings:this.warnings.slice()});}restore(){const e=this._snapshots.pop();this.externals=e.externals,this.warnings=e.warnings;}commit(){this._snapshots.pop();}},u.entry=function(e,r,s){const{tracer:n,cleanup:a}=u.tracer(r,s),i=s.debug?[]:null,o=r._ids._schemaChain?new Map:null,f=new u.Mainstay(n,i,o),m=r._ids._schemaChain?[{schema:r}]:null,h=new c([],[],{mainstay:f,schemas:m}),d=t.validate(e,r,h,s);a&&r.$_root.untrace();const p=l.process(d.errors,e,s);return {value:d.value,error:p,mainstay:f}},u.tracer=function(e,t){return e.$_root._tracer?{tracer:e.$_root._tracer._register(e)}:t.debug?(s(e.$_root.trace,"Debug mode not supported"),{tracer:e.$_root.trace()._register(e),cleanup:!0}):{tracer:u.ignore}},t.validate=function(e,t,r,s,n={}){if(t.$_terms.whens&&(t=t._generate(e,r,s).schema),t._preferences&&(s=u.prefs(t,s)),t._cache&&s.cache){const s=t._cache.get(e);if(r.mainstay.tracer.debug(r,"validate","cached",!!s),s)return s}const a=(n,a,i)=>t.$_createError(n,e,a,i||r,s),i={original:e,prefs:s,schema:t,state:r,error:a,errorsArray:u.errorsArray,warn:(e,t,s)=>r.mainstay.warnings.push(a(e,t,s)),message:(n,a)=>t.$_createError("custom",e,a,r,s,{messages:n})};r.mainstay.tracer.entry(t,r);const l=t._definition;if(l.prepare&&void 0!==e&&s.convert){const t=l.prepare(e,i);if(t){if(r.mainstay.tracer.value(r,"prepare",e,t.value),t.errors)return u.finalize(t.value,[].concat(t.errors),i);e=t.value;}}if(l.coerce&&void 0!==e&&s.convert&&(!l.coerce.from||l.coerce.from.includes(typeof e))){const t=l.coerce.method(e,i);if(t){if(r.mainstay.tracer.value(r,"coerced",e,t.value),t.errors)return u.finalize(t.value,[].concat(t.errors),i);e=t.value;}}const c=t._flags.empty;c&&c.$_match(u.trim(e,t),r.nest(c),o.defaults)&&(r.mainstay.tracer.value(r,"empty",e,void 0),e=void 0);const f=n.presence||t._flags.presence||(t._flags._endedSwitch?null:s.presence);if(void 0===e){if("forbidden"===f)return u.finalize(e,null,i);if("required"===f)return u.finalize(e,[t.$_createError("any.required",e,null,r,s)],i);if("optional"===f){if(t._flags.default!==o.symbols.deepDefault)return u.finalize(e,null,i);r.mainstay.tracer.value(r,"default",e,{}),e={};}}else if("forbidden"===f)return u.finalize(e,[t.$_createError("any.unknown",e,null,r,s)],i);const m=[];if(t._valids){const n=t._valids.get(e,r,s,t._flags.insensitive);if(n)return s.convert&&(r.mainstay.tracer.value(r,"valids",e,n.value),e=n.value),r.mainstay.tracer.filter(t,r,"valid",n),u.finalize(e,null,i);if(t._flags.only){const n=t.$_createError("any.only",e,{valids:t._valids.values({display:!0})},r,s);if(s.abortEarly)return u.finalize(e,[n],i);m.push(n);}}if(t._invalids){const n=t._invalids.get(e,r,s,t._flags.insensitive);if(n){r.mainstay.tracer.filter(t,r,"invalid",n);const a=t.$_createError("any.invalid",e,{invalids:t._invalids.values({display:!0})},r,s);if(s.abortEarly)return u.finalize(e,[a],i);m.push(a);}}if(l.validate){const t=l.validate(e,i);if(t&&(r.mainstay.tracer.value(r,"base",e,t.value),e=t.value,t.errors)){if(!Array.isArray(t.errors))return m.push(t.errors),u.finalize(e,m,i);if(t.errors.length)return m.push(...t.errors),u.finalize(e,m,i)}}return t._rules.length?u.rules(e,m,i):u.finalize(e,m,i)},u.rules=function(e,t,r){const{schema:s,state:n,prefs:a}=r;for(const i of s._rules){const l=s._definition.rules[i.method];if(l.convert&&a.convert){n.mainstay.tracer.log(s,n,"rule",i.name,"full");continue}let c,f=i.args;if(i._resolve.length){f=Object.assign({},f);for(const t of i._resolve){const r=l.argsByName.get(t),i=f[t].resolve(e,n,a),u=r.normalize?r.normalize(i):i,m=o.validateArg(u,null,r);if(m){c=s.$_createError("any.ref",i,{arg:t,ref:f[t],reason:m},n,a);break}f[t]=u;}}c=c||l.validate(e,r,f,i);const m=u.rule(c,i);if(m.errors){if(n.mainstay.tracer.log(s,n,"rule",i.name,"error"),i.warn){n.mainstay.warnings.push(...m.errors);continue}if(a.abortEarly)return u.finalize(e,m.errors,r);t.push(...m.errors);}else n.mainstay.tracer.log(s,n,"rule",i.name,"pass"),n.mainstay.tracer.value(n,"rule",e,m.value,i.name),e=m.value;}return u.finalize(e,t,r)},u.rule=function(e,t){return e instanceof l.Report?(u.error(e,t),{errors:[e],value:null}):Array.isArray(e)&&e[o.symbols.errors]?(e.forEach((e=>u.error(e,t))),{errors:e,value:null}):{errors:null,value:e}},u.error=function(e,t){return t.message&&e._setTemplate(t.message),e},u.finalize=function(e,t,r){t=t||[];const{schema:n,state:a,prefs:i}=r;if(t.length){const s=u.default("failover",void 0,t,r);void 0!==s&&(a.mainstay.tracer.value(a,"failover",e,s),e=s,t=[]);}if(t.length&&n._flags.error)if("function"==typeof n._flags.error){t=n._flags.error(t),Array.isArray(t)||(t=[t]);for(const e of t)s(e instanceof Error||e instanceof l.Report,"error() must return an Error object");}else t=[n._flags.error];if(void 0===e){const s=u.default("default",e,t,r);a.mainstay.tracer.value(a,"default",e,s),e=s;}if(n._flags.cast&&void 0!==e){const t=n._definition.cast[n._flags.cast];if(t.from(e)){const s=t.to(e,r);a.mainstay.tracer.value(a,"cast",e,s,n._flags.cast),e=s;}}if(n.$_terms.externals&&i.externals&&!1!==i._externals)for(const{method:e}of n.$_terms.externals)a.mainstay.externals.push({method:e,schema:n,state:a,label:l.label(n._flags,a,i)});const o={value:e,errors:t.length?t:null};return n._flags.result&&(o.value="strip"===n._flags.result?void 0:r.original,a.mainstay.tracer.value(a,n._flags.result,e,o.value),a.shadow(e,n._flags.result)),n._cache&&!1!==i.cache&&!n._refs.length&&n._cache.set(r.original,o),void 0===e||o.errors||void 0===n._flags.artifact||(a.mainstay.artifacts=a.mainstay.artifacts||new Map,a.mainstay.artifacts.has(n._flags.artifact)||a.mainstay.artifacts.set(n._flags.artifact,[]),a.mainstay.artifacts.get(n._flags.artifact).push(a.path)),o},u.prefs=function(e,t){const r=t===o.defaults;return r&&e._preferences[o.symbols.prefs]?e._preferences[o.symbols.prefs]:(t=o.preferences(t,e._preferences),r&&(e._preferences[o.symbols.prefs]=t),t)},u.default=function(e,t,r,s){const{schema:a,state:i,prefs:l}=s,c=a._flags[e];if(l.noDefaults||void 0===c)return t;if(i.mainstay.tracer.log(a,i,"rule",e,"full"),!c)return c;if("function"==typeof c){const t=c.length?[n(i.ancestors[0]),s]:[];try{return c(...t)}catch(t){return void r.push(a.$_createError(`any.${e}`,null,{error:t},i,l))}}return "object"!=typeof c?c:c[o.symbols.literal]?c.literal:o.isResolvable(c)?c.resolve(t,i,l):n(c)},u.trim=function(e,t){if("string"!=typeof e)return e;const r=t.$_getRule("trim");return r&&r.args.enabled?e.trim():e},u.ignore={active:!1,debug:a,entry:a,filter:a,log:a,resolve:a,value:a},u.errorsArray=function(){const e=[];return e[o.symbols.errors]=!0,e};},2036:(e,t,r)=>{const s=r(375),n=r(9474),a=r(8160),i={};e.exports=i.Values=class{constructor(e,t){this._values=new Set(e),this._refs=new Set(t),this._lowercase=i.lowercases(e),this._override=!1;}get length(){return this._values.size+this._refs.size}add(e,t){a.isResolvable(e)?this._refs.has(e)||(this._refs.add(e),t&&t.register(e)):this.has(e,null,null,!1)||(this._values.add(e),"string"==typeof e&&this._lowercase.set(e.toLowerCase(),e));}static merge(e,t,r){if(e=e||new i.Values,t){if(t._override)return t.clone();for(const r of [...t._values,...t._refs])e.add(r);}if(r)for(const t of [...r._values,...r._refs])e.remove(t);return e.length?e:null}remove(e){a.isResolvable(e)?this._refs.delete(e):(this._values.delete(e),"string"==typeof e&&this._lowercase.delete(e.toLowerCase()));}has(e,t,r,s){return !!this.get(e,t,r,s)}get(e,t,r,s){if(!this.length)return !1;if(this._values.has(e))return {value:e};if("string"==typeof e&&e&&s){const t=this._lowercase.get(e.toLowerCase());if(t)return {value:t}}if(!this._refs.size&&"object"!=typeof e)return !1;if("object"==typeof e)for(const t of this._values)if(n(t,e))return {value:t};if(t)for(const a of this._refs){const i=a.resolve(e,t,r,null,{in:!0});if(void 0===i)continue;const o=a.in&&"object"==typeof i?Array.isArray(i)?i:Object.keys(i):[i];for(const t of o)if(typeof t==typeof e)if(s&&e&&"string"==typeof e){if(t.toLowerCase()===e.toLowerCase())return {value:t,ref:a}}else if(n(t,e))return {value:t,ref:a}}return !1}override(){this._override=!0;}values(e){if(e&&e.display){const e=[];for(const t of [...this._values,...this._refs])void 0!==t&&e.push(t);return e}return Array.from([...this._values,...this._refs])}clone(){const e=new i.Values(this._values,this._refs);return e._override=this._override,e}concat(e){s(!e._override,"Cannot concat override set of values");const t=new i.Values([...this._values,...e._values],[...this._refs,...e._refs]);return t._override=this._override,t}describe(){const e=[];this._override&&e.push({override:!0});for(const t of this._values.values())e.push(t&&"object"==typeof t?{value:t}:t);for(const t of this._refs.values())e.push(t.describe());return e}},i.Values.prototype[a.symbols.values]=!0,i.Values.prototype.slice=i.Values.prototype.clone,i.lowercases=function(e){const t=new Map;if(e)for(const r of e)"string"==typeof r&&t.set(r.toLowerCase(),r);return t};},978:(e,t,r)=>{const s=r(375),n=r(8571),a=r(1687),i=r(9621),o={};e.exports=function(e,t,r={}){if(s(e&&"object"==typeof e,"Invalid defaults value: must be an object"),s(!t||!0===t||"object"==typeof t,"Invalid source value: must be true, falsy or an object"),s("object"==typeof r,"Invalid options: must be an object"),!t)return null;if(r.shallow)return o.applyToDefaultsWithShallow(e,t,r);const i=n(e);if(!0===t)return i;const l=void 0!==r.nullOverride&&r.nullOverride;return a(i,t,{nullOverride:l,mergeArrays:!1})},o.applyToDefaultsWithShallow=function(e,t,r){const l=r.shallow;s(Array.isArray(l),"Invalid keys");const c=new Map,u=!0===t?null:new Set;for(let r of l){r=Array.isArray(r)?r:r.split(".");const s=i(e,r);s&&"object"==typeof s?c.set(s,u&&i(t,r)||s):u&&u.add(r);}const f=n(e,{},c);if(!u)return f;for(const e of u)o.reachCopy(f,t,e);const m=void 0!==r.nullOverride&&r.nullOverride;return a(f,t,{nullOverride:m,mergeArrays:!1})},o.reachCopy=function(e,t,r){for(const e of r){if(!(e in t))return;const r=t[e];if("object"!=typeof r||null===r)return;t=r;}const s=t;let n=e;for(let e=0;e<r.length-1;++e){const t=r[e];"object"!=typeof n[t]&&(n[t]={}),n=n[t];}n[r[r.length-1]]=s;};},375:(e,t,r)=>{const s=r(7916);e.exports=function(e,...t){if(!e){if(1===t.length&&t[0]instanceof Error)throw t[0];throw new s(t)}};},8571:(e,t,r)=>{const s=r(9621),n=r(4277),a=r(7043),i={needsProtoHack:new Set([n.set,n.map,n.weakSet,n.weakMap])};e.exports=i.clone=function(e,t={},r=null){if("object"!=typeof e||null===e)return e;let s=i.clone,o=r;if(t.shallow){if(!0!==t.shallow)return i.cloneWithShallow(e,t);s=e=>e;}else if(o){const t=o.get(e);if(t)return t}else o=new Map;const l=n.getInternalProto(e);if(l===n.buffer)return !1;if(l===n.date)return new Date(e.getTime());if(l===n.regex)return new RegExp(e);const c=i.base(e,l,t);if(c===e)return e;if(o&&o.set(e,c),l===n.set)for(const r of e)c.add(s(r,t,o));else if(l===n.map)for(const[r,n]of e)c.set(r,s(n,t,o));const u=a.keys(e,t);for(const r of u){if("__proto__"===r)continue;if(l===n.array&&"length"===r){c.length=e.length;continue}const a=Object.getOwnPropertyDescriptor(e,r);a?a.get||a.set?Object.defineProperty(c,r,a):a.enumerable?c[r]=s(e[r],t,o):Object.defineProperty(c,r,{enumerable:!1,writable:!0,configurable:!0,value:s(e[r],t,o)}):Object.defineProperty(c,r,{enumerable:!0,writable:!0,configurable:!0,value:s(e[r],t,o)});}return c},i.cloneWithShallow=function(e,t){const r=t.shallow;(t=Object.assign({},t)).shallow=!1;const n=new Map;for(const t of r){const r=s(e,t);"object"!=typeof r&&"function"!=typeof r||n.set(r,r);}return i.clone(e,t,n)},i.base=function(e,t,r){if(!1===r.prototype)return i.needsProtoHack.has(t)?new t.constructor:t===n.array?[]:{};const s=Object.getPrototypeOf(e);if(s&&s.isImmutable)return e;if(t===n.array){const e=[];return s!==t&&Object.setPrototypeOf(e,s),e}if(i.needsProtoHack.has(t)){const e=new s.constructor;return s!==t&&Object.setPrototypeOf(e,s),e}return Object.create(s)};},9474:(e,t,r)=>{const s=r(4277),n={mismatched:null};e.exports=function(e,t,r){return r=Object.assign({prototype:!0},r),!!n.isDeepEqual(e,t,r,[])},n.isDeepEqual=function(e,t,r,a){if(e===t)return 0!==e||1/e==1/t;const i=typeof e;if(i!==typeof t)return !1;if(null===e||null===t)return !1;if("function"===i){if(!r.deepFunction||e.toString()!==t.toString())return !1}else if("object"!==i)return e!=e&&t!=t;const o=n.getSharedType(e,t,!!r.prototype);switch(o){case s.buffer:return !1;case s.promise:return e===t;case s.regex:return e.toString()===t.toString();case n.mismatched:return !1}for(let r=a.length-1;r>=0;--r)if(a[r].isSame(e,t))return !0;a.push(new n.SeenEntry(e,t));try{return !!n.isDeepEqualObj(o,e,t,r,a)}finally{a.pop();}},n.getSharedType=function(e,t,r){if(r)return Object.getPrototypeOf(e)!==Object.getPrototypeOf(t)?n.mismatched:s.getInternalProto(e);const a=s.getInternalProto(e);return a!==s.getInternalProto(t)?n.mismatched:a},n.valueOf=function(e){const t=e.valueOf;if(void 0===t)return e;try{return t.call(e)}catch(e){return e}},n.hasOwnEnumerableProperty=function(e,t){return Object.prototype.propertyIsEnumerable.call(e,t)},n.isSetSimpleEqual=function(e,t){for(const r of Set.prototype.values.call(e))if(!Set.prototype.has.call(t,r))return !1;return !0},n.isDeepEqualObj=function(e,t,r,a,i){const{isDeepEqual:o,valueOf:l,hasOwnEnumerableProperty:c}=n,{keys:u,getOwnPropertySymbols:f}=Object;if(e===s.array){if(!a.part){if(t.length!==r.length)return !1;for(let e=0;e<t.length;++e)if(!o(t[e],r[e],a,i))return !1;return !0}for(const e of t)for(const t of r)if(o(e,t,a,i))return !0}else if(e===s.set){if(t.size!==r.size)return !1;if(!n.isSetSimpleEqual(t,r)){const e=new Set(Set.prototype.values.call(r));for(const r of Set.prototype.values.call(t)){if(e.delete(r))continue;let t=!1;for(const s of e)if(o(r,s,a,i)){e.delete(s),t=!0;break}if(!t)return !1}}}else if(e===s.map){if(t.size!==r.size)return !1;for(const[e,s]of Map.prototype.entries.call(t)){if(void 0===s&&!Map.prototype.has.call(r,e))return !1;if(!o(s,Map.prototype.get.call(r,e),a,i))return !1}}else if(e===s.error&&(t.name!==r.name||t.message!==r.message))return !1;const m=l(t),h=l(r);if((t!==m||r!==h)&&!o(m,h,a,i))return !1;const d=u(t);if(!a.part&&d.length!==u(r).length&&!a.skip)return !1;let p=0;for(const e of d)if(a.skip&&a.skip.includes(e))void 0===r[e]&&++p;else {if(!c(r,e))return !1;if(!o(t[e],r[e],a,i))return !1}if(!a.part&&d.length-p!==u(r).length)return !1;if(!1!==a.symbols){const e=f(t),s=new Set(f(r));for(const n of e){if(!a.skip||!a.skip.includes(n))if(c(t,n)){if(!c(r,n))return !1;if(!o(t[n],r[n],a,i))return !1}else if(c(r,n))return !1;s.delete(n);}for(const e of s)if(c(r,e))return !1}return !0},n.SeenEntry=class{constructor(e,t){this.obj=e,this.ref=t;}isSame(e,t){return this.obj===e&&this.ref===t}};},7916:(e,t,r)=>{const s=r(8761);e.exports=class extends Error{constructor(e){super(e.filter((e=>""!==e)).map((e=>"string"==typeof e?e:e instanceof Error?e.message:s(e))).join(" ")||"Unknown error"),"function"==typeof Error.captureStackTrace&&Error.captureStackTrace(this,t.assert);}};},5277:e=>{const t={};e.exports=function(e){if(!e)return "";let r="";for(let s=0;s<e.length;++s){const n=e.charCodeAt(s);t.isSafe(n)?r+=e[s]:r+=t.escapeHtmlChar(n);}return r},t.escapeHtmlChar=function(e){return t.namedHtml.get(e)||(e>=256?"&#"+e+";":`&#x${e.toString(16).padStart(2,"0")};`)},t.isSafe=function(e){return t.safeCharCodes.has(e)},t.namedHtml=new Map([[38,"&"],[60,"<"],[62,">"],[34,"""],[160," "],[162,"¢"],[163,"£"],[164,"¤"],[169,"©"],[174,"®"]]),t.safeCharCodes=function(){const e=new Set;for(let t=32;t<123;++t)(t>=97||t>=65&&t<=90||t>=48&&t<=57||32===t||46===t||44===t||45===t||58===t||95===t)&&e.add(t);return e}();},6064:e=>{e.exports=function(e){return e.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g,"\\$&")};},738:e=>{e.exports=function(){};},1687:(e,t,r)=>{const s=r(375),n=r(8571),a=r(7043),i={};e.exports=i.merge=function(e,t,r){if(s(e&&"object"==typeof e,"Invalid target value: must be an object"),s(null==t||"object"==typeof t,"Invalid source value: must be null, undefined, or an object"),!t)return e;if(r=Object.assign({nullOverride:!0,mergeArrays:!0},r),Array.isArray(t)){s(Array.isArray(e),"Cannot merge array onto an object"),r.mergeArrays||(e.length=0);for(let s=0;s<t.length;++s)e.push(n(t[s],{symbols:r.symbols}));return e}const o=a.keys(t,r);for(let s=0;s<o.length;++s){const a=o[s];if("__proto__"===a||!Object.prototype.propertyIsEnumerable.call(t,a))continue;const l=t[a];if(l&&"object"==typeof l){if(e[a]===l)continue;!e[a]||"object"!=typeof e[a]||Array.isArray(e[a])!==Array.isArray(l)||l instanceof Date||l instanceof RegExp?e[a]=n(l,{symbols:r.symbols}):i.merge(e[a],l,r);}else (null!=l||r.nullOverride)&&(e[a]=l);}return e};},9621:(e,t,r)=>{const s=r(375),n={};e.exports=function(e,t,r){if(!1===t||null==t)return e;"string"==typeof(r=r||{})&&(r={separator:r});const a=Array.isArray(t);s(!a||!r.separator,"Separator option is not valid for array-based chain");const i=a?t:t.split(r.separator||".");let o=e;for(let e=0;e<i.length;++e){let a=i[e];const l=r.iterables&&n.iterables(o);if(Array.isArray(o)||"set"===l){const e=Number(a);Number.isInteger(e)&&(a=e<0?o.length+e:e);}if(!o||"function"==typeof o&&!1===r.functions||!l&&void 0===o[a]){s(!r.strict||e+1===i.length,"Missing segment",a,"in reach path ",t),s("object"==typeof o||!0===r.functions||"function"!=typeof o,"Invalid segment",a,"in reach path ",t),o=r.default;break}o=l?"set"===l?[...o][a]:o.get(a):o[a];}return o},n.iterables=function(e){return e instanceof Set?"set":e instanceof Map?"map":void 0};},8761:e=>{e.exports=function(...e){try{return JSON.stringify(...e)}catch(e){return "[Cannot display object: "+e.message+"]"}};},4277:(e,t)=>{const r={};t=e.exports={array:Array.prototype,buffer:!1,date:Date.prototype,error:Error.prototype,generic:Object.prototype,map:Map.prototype,promise:Promise.prototype,regex:RegExp.prototype,set:Set.prototype,weakMap:WeakMap.prototype,weakSet:WeakSet.prototype},r.typeMap=new Map([["[object Error]",t.error],["[object Map]",t.map],["[object Promise]",t.promise],["[object Set]",t.set],["[object WeakMap]",t.weakMap],["[object WeakSet]",t.weakSet]]),t.getInternalProto=function(e){if(Array.isArray(e))return t.array;if(e instanceof Date)return t.date;if(e instanceof RegExp)return t.regex;if(e instanceof Error)return t.error;const s=Object.prototype.toString.call(e);return r.typeMap.get(s)||t.generic};},7043:(e,t)=>{t.keys=function(e,t={}){return !1!==t.symbols?Reflect.ownKeys(e):Object.getOwnPropertyNames(e)};},3652:(e,t,r)=>{const s=r(375),n={};t.Sorter=class{constructor(){this._items=[],this.nodes=[];}add(e,t){const r=[].concat((t=t||{}).before||[]),n=[].concat(t.after||[]),a=t.group||"?",i=t.sort||0;s(!r.includes(a),`Item cannot come before itself: ${a}`),s(!r.includes("?"),"Item cannot come before unassociated items"),s(!n.includes(a),`Item cannot come after itself: ${a}`),s(!n.includes("?"),"Item cannot come after unassociated items"),Array.isArray(e)||(e=[e]);for(const t of e){const e={seq:this._items.length,sort:i,before:r,after:n,group:a,node:t};this._items.push(e);}if(!t.manual){const e=this._sort();s(e,"item","?"!==a?`added into group ${a}`:"","created a dependencies error");}return this.nodes}merge(e){Array.isArray(e)||(e=[e]);for(const t of e)if(t)for(const e of t._items)this._items.push(Object.assign({},e));this._items.sort(n.mergeSort);for(let e=0;e<this._items.length;++e)this._items[e].seq=e;const t=this._sort();return s(t,"merge created a dependencies error"),this.nodes}sort(){const e=this._sort();return s(e,"sort created a dependencies error"),this.nodes}_sort(){const e={},t=Object.create(null),r=Object.create(null);for(const s of this._items){const n=s.seq,a=s.group;r[a]=r[a]||[],r[a].push(n),e[n]=s.before;for(const e of s.after)t[e]=t[e]||[],t[e].push(n);}for(const t in e){const s=[];for(const n in e[t]){const a=e[t][n];r[a]=r[a]||[],s.push(...r[a]);}e[t]=s;}for(const s in t)if(r[s])for(const n of r[s])e[n].push(...t[s]);const s={};for(const t in e){const r=e[t];for(const e of r)s[e]=s[e]||[],s[e].push(t);}const n={},a=[];for(let e=0;e<this._items.length;++e){let t=e;if(s[e]){t=null;for(let e=0;e<this._items.length;++e){if(!0===n[e])continue;s[e]||(s[e]=[]);const r=s[e].length;let a=0;for(let t=0;t<r;++t)n[s[e][t]]&&++a;if(a===r){t=e;break}}}null!==t&&(n[t]=!0,a.push(t));}if(a.length!==this._items.length)return !1;const i={};for(const e of this._items)i[e.seq]=e;this._items=[],this.nodes=[];for(const e of a){const t=i[e];this.nodes.push(t.node),this._items.push(t);}return !0}},n.mergeSort=(e,t)=>e.sort===t.sort?0:e.sort<t.sort?-1:1;},5380:(e,t,r)=>{const s=r(443),n=r(2178),a={minDomainSegments:2,nonAsciiRx:/[^\x00-\x7f]/,domainControlRx:/[\x00-\x20@\:\/\\#!\$&\'\(\)\*\+,;=\?]/,tldSegmentRx:/^[a-zA-Z](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/,domainSegmentRx:/^[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/,URL:s.URL||URL};t.analyze=function(e,t={}){if(!e)return n.code("DOMAIN_NON_EMPTY_STRING");if("string"!=typeof e)throw new Error("Invalid input: domain must be a string");if(e.length>256)return n.code("DOMAIN_TOO_LONG");if(a.nonAsciiRx.test(e)){if(!1===t.allowUnicode)return n.code("DOMAIN_INVALID_UNICODE_CHARS");e=e.normalize("NFC");}if(a.domainControlRx.test(e))return n.code("DOMAIN_INVALID_CHARS");e=a.punycode(e),t.allowFullyQualified&&"."===e[e.length-1]&&(e=e.slice(0,-1));const r=t.minDomainSegments||a.minDomainSegments,s=e.split(".");if(s.length<r)return n.code("DOMAIN_SEGMENTS_COUNT");if(t.maxDomainSegments&&s.length>t.maxDomainSegments)return n.code("DOMAIN_SEGMENTS_COUNT_MAX");const i=t.tlds;if(i){const e=s[s.length-1].toLowerCase();if(i.deny&&i.deny.has(e)||i.allow&&!i.allow.has(e))return n.code("DOMAIN_FORBIDDEN_TLDS")}for(let e=0;e<s.length;++e){const t=s[e];if(!t.length)return n.code("DOMAIN_EMPTY_SEGMENT");if(t.length>63)return n.code("DOMAIN_LONG_SEGMENT");if(e<s.length-1){if(!a.domainSegmentRx.test(t))return n.code("DOMAIN_INVALID_CHARS")}else if(!a.tldSegmentRx.test(t))return n.code("DOMAIN_INVALID_TLDS_CHARS")}return null},t.isValid=function(e,r){return !t.analyze(e,r)},a.punycode=function(e){e.includes("%")&&(e=e.replace(/%/g,"%25"));try{return new a.URL(`http://${e}`).host}catch(t){return e}};},1745:(e,t,r)=>{const s=r(9848),n=r(5380),a=r(2178),i={nonAsciiRx:/[^\x00-\x7f]/,encoder:new(s.TextEncoder||TextEncoder)};t.analyze=function(e,t){return i.email(e,t)},t.isValid=function(e,t){return !i.email(e,t)},i.email=function(e,t={}){if("string"!=typeof e)throw new Error("Invalid input: email must be a string");if(!e)return a.code("EMPTY_STRING");const r=!i.nonAsciiRx.test(e);if(!r){if(!1===t.allowUnicode)return a.code("FORBIDDEN_UNICODE");e=e.normalize("NFC");}const s=e.split("@");if(2!==s.length)return s.length>2?a.code("MULTIPLE_AT_CHAR"):a.code("MISSING_AT_CHAR");const[o,l]=s;if(!o)return a.code("EMPTY_LOCAL");if(!t.ignoreLength){if(e.length>254)return a.code("ADDRESS_TOO_LONG");if(i.encoder.encode(o).length>64)return a.code("LOCAL_TOO_LONG")}return i.local(o,r)||n.analyze(l,t)},i.local=function(e,t){const r=e.split(".");for(const e of r){if(!e.length)return a.code("EMPTY_LOCAL_SEGMENT");if(t){if(!i.atextRx.test(e))return a.code("INVALID_LOCAL_CHARS")}else for(const t of e){if(i.atextRx.test(t))continue;const e=i.binary(t);if(!i.atomRx.test(e))return a.code("INVALID_LOCAL_CHARS")}}},i.binary=function(e){return Array.from(i.encoder.encode(e)).map((e=>String.fromCharCode(e))).join("")},i.atextRx=/^[\w!#\$%&'\*\+\-/=\?\^`\{\|\}~]+$/,i.atomRx=new RegExp(["(?:[\\xc2-\\xdf][\\x80-\\xbf])","(?:\\xe0[\\xa0-\\xbf][\\x80-\\xbf])|(?:[\\xe1-\\xec][\\x80-\\xbf]{2})|(?:\\xed[\\x80-\\x9f][\\x80-\\xbf])|(?:[\\xee-\\xef][\\x80-\\xbf]{2})","(?:\\xf0[\\x90-\\xbf][\\x80-\\xbf]{2})|(?:[\\xf1-\\xf3][\\x80-\\xbf]{3})|(?:\\xf4[\\x80-\\x8f][\\x80-\\xbf]{2})"].join("|"));},2178:(e,t)=>{t.codes={EMPTY_STRING:"Address must be a non-empty string",FORBIDDEN_UNICODE:"Address contains forbidden Unicode characters",MULTIPLE_AT_CHAR:"Address cannot contain more than one @ character",MISSING_AT_CHAR:"Address must contain one @ character",EMPTY_LOCAL:"Address local part cannot be empty",ADDRESS_TOO_LONG:"Address too long",LOCAL_TOO_LONG:"Address local part too long",EMPTY_LOCAL_SEGMENT:"Address local part contains empty dot-separated segment",INVALID_LOCAL_CHARS:"Address local part contains invalid character",DOMAIN_NON_EMPTY_STRING:"Domain must be a non-empty string",DOMAIN_TOO_LONG:"Domain too long",DOMAIN_INVALID_UNICODE_CHARS:"Domain contains forbidden Unicode characters",DOMAIN_INVALID_CHARS:"Domain contains invalid character",DOMAIN_INVALID_TLDS_CHARS:"Domain contains invalid tld character",DOMAIN_SEGMENTS_COUNT:"Domain lacks the minimum required number of segments",DOMAIN_SEGMENTS_COUNT_MAX:"Domain contains too many segments",DOMAIN_FORBIDDEN_TLDS:"Domain uses forbidden TLD",DOMAIN_EMPTY_SEGMENT:"Domain contains empty dot-separated segment",DOMAIN_LONG_SEGMENT:"Domain contains dot-separated segment that is too long"},t.code=function(e){return {code:e,error:t.codes[e]}};},9959:(e,t,r)=>{const s=r(375),n=r(5752);t.regex=function(e={}){s(void 0===e.cidr||"string"==typeof e.cidr,"options.cidr must be a string");const t=e.cidr?e.cidr.toLowerCase():"optional";s(["required","optional","forbidden"].includes(t),"options.cidr must be one of required, optional, forbidden"),s(void 0===e.version||"string"==typeof e.version||Array.isArray(e.version),"options.version must be a string or an array of string");let r=e.version||["ipv4","ipv6","ipvfuture"];Array.isArray(r)||(r=[r]),s(r.length>=1,"options.version must have at least 1 version specified");for(let e=0;e<r.length;++e)s("string"==typeof r[e],"options.version must only contain strings"),r[e]=r[e].toLowerCase(),s(["ipv4","ipv6","ipvfuture"].includes(r[e]),"options.version contains unknown version "+r[e]+" - must be one of ipv4, ipv6, ipvfuture");r=Array.from(new Set(r));const a=`(?:${r.map((e=>{if("forbidden"===t)return n.ip[e];const r=`\\/${"ipv4"===e?n.ip.v4Cidr:n.ip.v6Cidr}`;return "required"===t?`${n.ip[e]}${r}`:`${n.ip[e]}(?:${r})?`})).join("|")})`,i=new RegExp(`^${a}$`);return {cidr:t,versions:r,regex:i,raw:a}};},5752:(e,t,r)=>{const s=r(375),n=r(6064),a={generate:function(){const e={},t="\\dA-Fa-f",r="["+t+"]",s="\\w-\\.~",n="!\\$&'\\(\\)\\*\\+,;=",a="%"+t,i=s+a+n+":@",o="["+i+"]",l="(?:0{0,2}\\d|0?[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])";e.ipv4address="(?:"+l+"\\.){3}"+l;const c=r+"{1,4}",u="(?:"+c+":"+c+"|"+e.ipv4address+")",f="(?:"+c+":){6}"+u,m="::(?:"+c+":){5}"+u,h="(?:"+c+")?::(?:"+c+":){4}"+u,d="(?:(?:"+c+":){0,1}"+c+")?::(?:"+c+":){3}"+u,p="(?:(?:"+c+":){0,2}"+c+")?::(?:"+c+":){2}"+u,g="(?:(?:"+c+":){0,3}"+c+")?::"+c+":"+u,y="(?:(?:"+c+":){0,4}"+c+")?::"+u,b="(?:(?:"+c+":){0,5}"+c+")?::"+c,v="(?:(?:"+c+":){0,6}"+c+")?::";e.ipv4Cidr="(?:\\d|[1-2]\\d|3[0-2])",e.ipv6Cidr="(?:0{0,2}\\d|0?[1-9]\\d|1[01]\\d|12[0-8])",e.ipv6address="(?:"+f+"|"+m+"|"+h+"|"+d+"|"+p+"|"+g+"|"+y+"|"+b+"|"+v+")",e.ipvFuture="v"+r+"+\\.["+s+n+":]+",e.scheme="[a-zA-Z][a-zA-Z\\d+-\\.]*",e.schemeRegex=new RegExp(e.scheme);const _="["+s+a+n+":]*",w="["+s+a+n+"]{1,255}",$="(?:\\[(?:"+e.ipv6address+"|"+e.ipvFuture+")\\]|"+e.ipv4address+"|"+w+")",x="(?:"+_+"@)?"+$+"(?::\\d*)?",j="(?:"+_+"@)?("+$+")(?::\\d*)?",k=o+"*",R=o+"+",S="(?:\\/"+k+")*",A="\\/(?:"+R+S+")?",O=R+S,E="["+s+a+n+"@]+"+S,D="(?:\\/\\/\\/"+k+S+")";return e.hierPart="(?:(?:\\/\\/"+x+S+")|"+A+"|"+O+"|"+D+")",e.hierPartCapture="(?:(?:\\/\\/"+j+S+")|"+A+"|"+O+")",e.relativeRef="(?:(?:\\/\\/"+x+S+")|"+A+"|"+E+"|)",e.relativeRefCapture="(?:(?:\\/\\/"+j+S+")|"+A+"|"+E+"|)",e.query="["+i+"\\/\\?]*(?=#|$)",e.queryWithSquareBrackets="["+i+"\\[\\]\\/\\?]*(?=#|$)",e.fragment="["+i+"\\/\\?]*",e}};a.rfc3986=a.generate(),t.ip={v4Cidr:a.rfc3986.ipv4Cidr,v6Cidr:a.rfc3986.ipv6Cidr,ipv4:a.rfc3986.ipv4address,ipv6:a.rfc3986.ipv6address,ipvfuture:a.rfc3986.ipvFuture},a.createRegex=function(e){const t=a.rfc3986,r="(?:\\?"+(e.allowQuerySquareBrackets?t.queryWithSquareBrackets:t.query)+")?(?:#"+t.fragment+")?",i=e.domain?t.relativeRefCapture:t.relativeRef;if(e.relativeOnly)return a.wrap(i+r);let o="";if(e.scheme){s(e.scheme instanceof RegExp||"string"==typeof e.scheme||Array.isArray(e.scheme),"scheme must be a RegExp, String, or Array");const r=[].concat(e.scheme);s(r.length>=1,"scheme must have at least 1 scheme specified");const a=[];for(let e=0;e<r.length;++e){const i=r[e];s(i instanceof RegExp||"string"==typeof i,"scheme at position "+e+" must be a RegExp or String"),i instanceof RegExp?a.push(i.source.toString()):(s(t.schemeRegex.test(i),"scheme at position "+e+" must be a valid scheme"),a.push(n(i)));}o=a.join("|");}const l="(?:"+(o?"(?:"+o+")":t.scheme)+":"+(e.domain?t.hierPartCapture:t.hierPart)+")",c=e.allowRelative?"(?:"+l+"|"+i+")":l;return a.wrap(c+r,o)},a.wrap=function(e,t){return {raw:e=`(?=.)(?!https?:/(?:$|[^/]))(?!https?:///)(?!https?:[^/])${e}`,regex:new RegExp(`^${e}$`),scheme:t}},a.uriRegex=a.createRegex({}),t.regex=function(e={}){return e.scheme||e.allowRelative||e.relativeOnly||e.allowQuerySquareBrackets||e.domain?a.createRegex(e):a.uriRegex};},1447:(e,t)=>{const r={operators:["!","^","*","/","%","+","-","<","<=",">",">=","==","!=","&&","||","??"],operatorCharacters:["!","^","*","/","%","+","-","<","=",">","&","|","?"],operatorsOrder:[["^"],["*","/","%"],["+","-"],["<","<=",">",">="],["==","!="],["&&"],["||","??"]],operatorsPrefix:["!","n"],literals:{'"':'"',"`":"`","'":"'","[":"]"},numberRx:/^(?:[0-9]*(\.[0-9]*)?){1}$/,tokenRx:/^[\w\$\#\.\@\:\{\}]+$/,symbol:Symbol("formula"),settings:Symbol("settings")};t.Parser=class{constructor(e,t={}){if(!t[r.settings]&&t.constants)for(const e in t.constants){const r=t.constants[e];if(null!==r&&!["boolean","number","string"].includes(typeof r))throw new Error(`Formula constant ${e} contains invalid ${typeof r} value type`)}this.settings=t[r.settings]?t:Object.assign({[r.settings]:!0,constants:{},functions:{}},t),this.single=null,this._parts=null,this._parse(e);}_parse(e){let s=[],n="",a=0,i=!1;const o=e=>{if(a)throw new Error("Formula missing closing parenthesis");const o=s.length?s[s.length-1]:null;if(i||n||e){if(o&&"reference"===o.type&&")"===e)return o.type="function",o.value=this._subFormula(n,o.value),void(n="");if(")"===e){const e=new t.Parser(n,this.settings);s.push({type:"segment",value:e});}else if(i){if("]"===i)return s.push({type:"reference",value:n}),void(n="");s.push({type:"literal",value:n});}else if(r.operatorCharacters.includes(n))o&&"operator"===o.type&&r.operators.includes(o.value+n)?o.value+=n:s.push({type:"operator",value:n});else if(n.match(r.numberRx))s.push({type:"constant",value:parseFloat(n)});else if(void 0!==this.settings.constants[n])s.push({type:"constant",value:this.settings.constants[n]});else {if(!n.match(r.tokenRx))throw new Error(`Formula contains invalid token: ${n}`);s.push({type:"reference",value:n});}n="";}};for(const t of e)i?t===i?(o(),i=!1):n+=t:a?"("===t?(n+=t,++a):")"===t?(--a,a?n+=t:o(t)):n+=t:t in r.literals?i=r.literals[t]:"("===t?(o(),++a):r.operatorCharacters.includes(t)?(o(),n=t,o()):" "!==t?n+=t:o();o(),s=s.map(((e,t)=>"operator"!==e.type||"-"!==e.value||t&&"operator"!==s[t-1].type?e:{type:"operator",value:"n"}));let l=!1;for(const e of s){if("operator"===e.type){if(r.operatorsPrefix.includes(e.value))continue;if(!l)throw new Error("Formula contains an operator in invalid position");if(!r.operators.includes(e.value))throw new Error(`Formula contains an unknown operator ${e.value}`)}else if(l)throw new Error("Formula missing expected operator");l=!l;}if(!l)throw new Error("Formula contains invalid trailing operator");1===s.length&&["reference","literal","constant"].includes(s[0].type)&&(this.single={type:"reference"===s[0].type?"reference":"value",value:s[0].value}),this._parts=s.map((e=>{if("operator"===e.type)return r.operatorsPrefix.includes(e.value)?e:e.value;if("reference"!==e.type)return e.value;if(this.settings.tokenRx&&!this.settings.tokenRx.test(e.value))throw new Error(`Formula contains invalid reference ${e.value}`);return this.settings.reference?this.settings.reference(e.value):r.reference(e.value)}));}_subFormula(e,s){const n=this.settings.functions[s];if("function"!=typeof n)throw new Error(`Formula contains unknown function ${s}`);let a=[];if(e){let t="",n=0,i=!1;const o=()=>{if(!t)throw new Error(`Formula contains function ${s} with invalid arguments ${e}`);a.push(t),t="";};for(let s=0;s<e.length;++s){const a=e[s];i?(t+=a,a===i&&(i=!1)):a in r.literals&&!n?(t+=a,i=r.literals[a]):","!==a||n?(t+=a,"("===a?++n:")"===a&&--n):o();}o();}return a=a.map((e=>new t.Parser(e,this.settings))),function(e){const t=[];for(const r of a)t.push(r.evaluate(e));return n.call(e,...t)}}evaluate(e){const t=this._parts.slice();for(let s=t.length-2;s>=0;--s){const n=t[s];if(n&&"operator"===n.type){const a=t[s+1];t.splice(s+1,1);const i=r.evaluate(a,e);t[s]=r.single(n.value,i);}}return r.operatorsOrder.forEach((s=>{for(let n=1;n<t.length-1;)if(s.includes(t[n])){const s=t[n],a=r.evaluate(t[n-1],e),i=r.evaluate(t[n+1],e);t.splice(n,2);const o=r.calculate(s,a,i);t[n-1]=0===o?0:o;}else n+=2;})),r.evaluate(t[0],e)}},t.Parser.prototype[r.symbol]=!0,r.reference=function(e){return function(t){return t&&void 0!==t[e]?t[e]:null}},r.evaluate=function(e,t){return null===e?null:"function"==typeof e?e(t):e[r.symbol]?e.evaluate(t):e},r.single=function(e,t){if("!"===e)return !t;const r=-t;return 0===r?0:r},r.calculate=function(e,t,s){if("??"===e)return r.exists(t)?t:s;if("string"==typeof t||"string"==typeof s){if("+"===e)return (t=r.exists(t)?t:"")+(r.exists(s)?s:"")}else switch(e){case"^":return Math.pow(t,s);case"*":return t*s;case"/":return t/s;case"%":return t%s;case"+":return t+s;case"-":return t-s}switch(e){case"<":return t<s;case"<=":return t<=s;case">":return t>s;case">=":return t>=s;case"==":return t===s;case"!=":return t!==s;case"&&":return t&&s;case"||":return t||s}return null},r.exists=function(e){return null!=e};},9926:()=>{},5688:()=>{},9708:()=>{},1152:()=>{},443:()=>{},9848:()=>{},5934:e=>{e.exports=JSON.parse('{"version":"17.13.3"}');}},t={},function r(s){var n=t[s];if(void 0!==n)return n.exports;var a=t[s]={exports:{}};return e[s](a,a.exports,r),a.exports}(5107);var e,t;}));
} (joiBrowser_min));
var joiBrowser_minExports = joiBrowser_min.exports;
/**
* Расширенные типы валидации
*/
var ValidationType;
(function (ValidationType) {
ValidationType["REQUIRED"] = "required";
ValidationType["TYPE"] = "type";
ValidationType["RANGE"] = "range";
ValidationType["CUSTOM"] = "custom";
ValidationType["SCHEMA"] = "schema";
ValidationType["ASYNC"] = "async";
ValidationType["PATTERN"] = "pattern";
ValidationType["ENUM"] = "enum";
ValidationType["ARRAY"] = "array";
ValidationType["OBJECT"] = "object";
})(ValidationType || (ValidationType = {}));
/**
* Кэш схем валидации
*/
class ValidationSchemaCache {
constructor() {
this.cache = new Map();
this.maxSize = 100;
}
get(key) {
return this.cache.get(key);
}
set(key, schema) {
if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
if (firstKey) {
this.cache.delete(firstKey);
}
}
this.cache.set(key, schema);
}
clear() {
this.cache.clear();
}
}
/**
* Система валидации для CSElement
*/
class ElementValidator {
constructor() {
this.rules = new Map();
this.schemaCache = new ValidationSchemaCache();
this.globalSchemas = new Map();
}
/**
* Добавляет правило валидации
*/
addRule(elementType, rule) {
if (!this.rules.has(elementType)) {
this.rules.set(elementType, []);
}
this.rules.get(elementType).push(rule);
}
/**
* Добавляет схему Joi для типа элемента
*/
addSchema(elementType, schema) {
this.globalSchemas.set(elementType, schema);
}
/**
* Удаляет все правила для типа элемента
*/
clearRules(elementType) {
this.rules.delete(elementType);
this.globalSchemas.delete(elementType);
}
/**
* Валидирует элемент (синхронно)
*/
validate(element, elementType = 'default') {
const errors = [];
const warnings = [];
const rules = this.rules.get(elementType) || [];
// Сначала проверяем глобальную схему
const globalSchema = this.globalSchemas.get(elementType);
if (globalSchema) {
const schemaResult = this.validateWithJoi(element, globalSchema);
errors.push(...schemaResult.errors);
warnings.push(...(schemaResult.warnings || []));
}
// Затем проверяем индивидуальные правила
for (const rule of rules) {
// Пропускаем асинхронные правила в синхронной валидации
if (rule.async)
continue;
// Проверяем условие выполнения правила
if (rule.condition && !rule.condition(element))
continue;
const error = this.validateRule(element, rule);
if (error) {
if (rule.severity === 'warning') {
warnings.push(error);
}
else {
errors.push(error);
}
}
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Асинхронная валидация элемента
*/
async validateAsync(element, elementType = 'default') {
const startTime = Date.now();
const errors = [];
const warnings = [];
const rules = this.rules.get(elementType) || [];
let rulesExecuted = 0;
// Сначала выполняем синхронную валидацию
const syncResult = this.validate(element, elementType);
errors.push(...syncResult.errors);
warnings.push(...(syncResult.warnings || []));
rulesExecuted += rules.filter(r => !r.async).length;
// Затем выполняем асинхронные правила
const asyncRules = rules.filter(r => r.async);
for (const rule of asyncRules) {
if (rule.condition && !rule.condition(element))
continue;
try {
const error = await this.validateRuleAsync(element, rule);
if (error) {
if (rule.severity === 'warning') {
warnings.push(error);
}
else {
errors.push(error);
}
}
rulesExecuted++;
}
catch (validationError) {
errors.push({
field: rule.field,
message: `Ошибка валидации: ${validationError instanceof Error ? validationError.message : String(validationError)}`,
value: this.getFieldValue(element, rule.field)
});
}
}
return {
isValid: errors.length === 0,
errors,
warnings,
performance: {
executionTime: Date.now() - startTime,
rulesExecuted
}
};
}
/**
* Валидация с использованием Joi схемы
*/
validateWithJoi(element, schema) {
try {
// Используем кэш для оптимизации
const cacheKey = `${element.id}_${element.name}`;
const cachedSchema = this.schemaCache.get(cacheKey);
const validationSchema = cachedSchema || schema;
// Кэшируем схему для будущих использований
if (!cachedSchema) {
this.schemaCache.set(cacheKey, schema);
}
const elementData = this.serializeElementForValidation(element);
const result = validationSchema.validate(elementData, { abortEarly: false, allowUnknown: true });
if (result.error) {
const errors = result.error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message,
value: detail.context?.value
}));
return { errors };
}
return { errors: [] };
}
catch (error) {
return {
errors: [{
field: 'schema',
message: `Ошибка схемы валидации: ${error instanceof Error ? error.message : String(error)}`,
value: undefined
}]
};
}
}
/**
* Сериализует элемент для валидации
*/
serializeElementForValidation(element) {
return {
id: element.id,
name: element.name,
index: element.index,
data: element.getData ? element.getData('') : {},
children: [] // Упрощаем - дочерние элементы не обязательны для валидации
};
}
/**
* Валидирует одно правило (синхронно)
*/
validateRule(element, rule) {
const value = this.getFieldValue(element, rule.field);
switch (rule.type) {
case ValidationType.REQUIRED:
if (value === undefined || value === null || value === '') {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} обязательно для заполнения`,
value
};
}
break;
case ValidationType.TYPE:
if (value !== undefined && typeof value !== rule.value) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} должно быть типа ${rule.value}`,
value
};
}
break;
case ValidationType.RANGE:
if (typeof value === 'number' && rule.value) {
const { min, max } = rule.value;
if ((min !== undefined && value < min) || (max !== undefined && value > max)) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} должно быть в диапазоне [${min}, ${max}]`,
value
};
}
}
break;
case ValidationType.PATTERN:
if (typeof value === 'string' && rule.value instanceof RegExp) {
if (!rule.value.test(value)) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} не соответствует шаблону`,
value
};
}
}
break;
case ValidationType.ENUM:
if (Array.isArray(rule.value) && !rule.value.includes(value)) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} должно быть одним из: ${rule.value.join(', ')}`,
value
};
}
break;
case ValidationType.ARRAY:
if (!Array.isArray(value)) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} должно быть массивом`,
value
};
}
break;
case ValidationType.OBJECT:
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} должно быть объектом`,
value
};
}
break;
case ValidationType.SCHEMA:
if (rule.schema) {
const schemaResult = this.validateWithJoi({ [rule.field]: value }, joiBrowser_minExports.object({ [rule.field]: rule.schema }));
if (schemaResult.errors.length > 0) {
return schemaResult.errors[0];
}
}
break;
case ValidationType.CUSTOM:
if (rule.validator) {
const result = rule.validator(value, element);
if (typeof result === 'string') {
return {
field: rule.field,
message: result,
value
};
}
else if (!result) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} не прошло валидацию`,
value
};
}
}
break;
}
return null;
}
/**
* Валидирует одно правило (асинхронно)
*/
async validateRuleAsync(element, rule) {
if (!rule.async || !rule.validator) {
return null;
}
const value = this.getFieldValue(element, rule.field);
try {
const result = await rule.validator(value, element);
if (typeof result === 'string') {
return {
field: rule.field,
message: result,
value
};
}
else if (!result) {
return {
field: rule.field,
message: rule.message || `Поле ${rule.field} не прошло асинхронную валидацию`,
value
};
}
}
catch (error) {
return {
field: rule.field,
message: `Ошибка асинхронной валидации: ${error instanceof Error ? error.message : String(error)}`,
value
};
}
return null;
}
/**
* Получает значение поля элемента
*/
getFieldValue(element, field) {
switch (field) {
case 'name':
return element.name;
case 'index':
return element.index;
case 'id':
return element.id;
default:
// Проверяем в данных элемента
return element.getData ? element.getData(field) : undefined;
}
}
/**
* Создает стандартные правила валидации
*/
static createStandardRules() {
const validator = new ElementValidator();
// Правила для базового элемента
validator.addRule('default', {
field: 'name',
type: ValidationType.TYPE,
value: 'string',
message: 'Имя элемента должно быть строкой'
});
validator.addRule('default', {
field: 'index',
type: ValidationType.TYPE,
value: 'number',
message: 'Индекс элемента должен быть числом'
});
validator.addRule('default', {
field: 'index',
type: ValidationType.RANGE,
value: { min: 0 },
message: 'Индекс элемента должен быть неотрицательным'
});
validator.addRule('default', {
field: 'id',
type: ValidationType.REQUIRED,
message: 'ID элемента обязательно'
});
validator.addRule('default', {
field: 'id',
type: ValidationType.PATTERN,
value: /^[a-zA-Z0-9_-]+$/,
message: 'ID элемента может содержать только буквы, цифры, дефисы и подчеркивания'
});
// Схема для базового элемента
const baseElementSchema = joiBrowser_minExports.object({
id: joiBrowser_minExports.string().required().pattern(/^[a-zA-Z0-9_-]+$/),
name: joiBrowser_minExports.string().optional(),
index: joiBrowser_minExports.number().integer().min(0).optional(),
data: joiBrowser_minExports.object().optional(),
children: joiBrowser_minExports.array().optional()
});
validator.addSchema('default', baseElementSchema);
return validator;
}
/**
* Создает расширенные правила валидации для типизированных элементов
*/
static createTypedElementRules() {
const validator = ElementValidator.createStandardRules();
// Правила для типизированных элементов
validator.addRule('typed', {
field: 'schema',
type: ValidationType.REQUIRED,
message: 'Типизированный элемент должен иметь схему'
});
validator.addRule('typed', {
field: 'version',
type: ValidationType.TYPE,
value: 'string',
message: 'Версия схемы должна быть строкой'
});
// Асинхронная валидация схемы
validator.addRule('typed', {
field: 'data',
type: ValidationType.CUSTOM,
async: true,
validator: async (_value, element) => {
// Имитация асинхронной проверки схемы
return new Promise((resolve) => {
setTimeout(() => {
if (element.getData && typeof element.getData('schema') === 'string') {
resolve(true);
}
else {
resolve('Схема типизированного элемента недействительна');
}
}, 10);
});
},
message: 'Данные не соответствуют схеме типизированного элемента'
});
return validator;
}
}
/**
* Глобальный валидатор по умолчанию
*/
const defaultValidator = ElementValidator.createStandardRules();
/**
* Валидатор для типизированных элементов
*/
ElementValidator.createTypedElementRules();
/**
* Интерфейсы для системы плагинов CSElement
*/
/**
* Приоритет middleware
*/
var MiddlewarePriority;
(function (MiddlewarePriority) {
MiddlewarePriority[MiddlewarePriority["HIGHEST"] = 1000] = "HIGHEST";
MiddlewarePriority[MiddlewarePriority["HIGH"] = 750] = "HIGH";
MiddlewarePriority[MiddlewarePriority["NORMAL"] = 500] = "NORMAL";
MiddlewarePriority[MiddlewarePriority["LOW"] = 250] = "LOW";
MiddlewarePriority[MiddlewarePriority["LOWEST"] = 1] = "LOWEST";
})(MiddlewarePriority || (MiddlewarePriority = {}));
/**
* Расширенный менеджер middleware для CSElement
* Поддерживает приоритеты, модификацию операций, статистику и таймауты
*/
class AdvancedMiddlewareManagerImpl extends EventEmitter {
constructor() {
super();
this.middleware = new Map();
this.globalTimeout = 5000; // 5 секунд по умолчанию
this.operationCounter = 0;
}
/**
* Добавить middleware с конфигурацией
*/
addMiddleware(operation, config) {
const middlewares = this.middleware.get(operation) || [];
const internalConfig = {
...config,
id: generateId(),
priority: config.priority ?? MiddlewarePriority.NORMAL,
name: config.name || `middleware_${middlewares.length}`,
enabled: true,
stats: {
totalExecutions: 0,
averageExecutionTime: 0,
errorCount: 0,
abortCount: 0,
lastExecuted: 0
}
};
middlewares.push(internalConfig);
// Сортируем по приоритету (больше = раньше)
middlewares.sort((a, b) => (b.priority || 0) - (a.priority || 0));
this.middleware.set(operation, middlewares);
this.emit('middleware:added', {
operation,
name: internalConfig.name,
priority: internalConfig.priority
});
}
/**
* Удалить middleware по имени
*/
removeMiddleware(operation, name) {
const middlewares = this.middleware.get(operation);
if (!middlewares)
return false;
const index = middlewares.findIndex(m => m.name === name);
if (index === -1)
return false;
middlewares.splice(index, 1);
this.middleware.set(operation, middlewares);
this.emit('middleware:removed', { operation, name });
return true;
}
/**
* Получить все middleware для операции
*/
getMiddleware(operation) {
const middlewares = this.middleware.get(operation) || [];
return middlewares.map(({ id, enabled, stats, ...config }) => config);
}
/**
* Очистить все middleware для операции
*/
clearMiddleware(operation) {
this.middleware.delete(operation);
this.emit('middleware:cleared', { operation });
}
/**
* Очистить все middleware для всех операций
*/
clearAllMiddleware() {
this.middleware.clear();
this.operationCounter = 0;
this.emit('middleware:all-cleared');
}
/**
* Выполнить middleware цепочку
*/
async executeMiddleware(context, operation) {
const middlewares = this.middleware.get(context.operation) || [];
const enabledMiddlewares = middlewares.filter(m => m.enabled && (!m.condition || m.condition(context)));
if (enabledMiddlewares.length === 0) {
return await operation();
}
// Дополняем контекст
const enhancedContext = {
...context,
operationId: context.operationId || `op_${++this.operationCounter}`,
middlewareStack: [],
flags: {
...context.flags,
aborted: context.flags?.aborted ?? false,
modified: context.flags?.modified ?? false,
inTransaction: context.flags?.inTransaction ?? false
}
};
let index = 0;
let currentArgs = [...context.args]; // Создаем копию аргументов
const next = async (modifiedArgs) => {
if (modifiedArgs && modifiedArgs !== currentArgs) {
currentArgs = [...modifiedArgs]; // Создаем копию модифицированных аргументов
enhancedContext.flags.modified = true;
enhancedContext.args = currentArgs; // Обновляем аргументы в контексте
}
if (index >= enabledMiddlewares.length) {
// Создаем новую операцию с модифицированными аргументами
if (enhancedContext.flags.modified) {
// Если аргументы были модифицированы, создаем новую операцию
const originalOperation = operation;
return await (async () => {
// Подменяем аргументы в контексте для операции
const oldArgs = context.args;
context.args = currentArgs;
try {
return await originalOperation();
}
finally {
context.args = oldArgs; // Восстанавливаем оригинальные аргументы
}
})();
}
return await operation();
}
const middlewareConfig = enabledMiddlewares[index++];
const startTime = Date.now();
// Обновляем контекст с текущими аргументами
enhancedContext.args = currentArgs;
enhancedContext.middlewareStack.push(middlewareConfig.name || 'unknown');
try {
const timeout = middlewareConfig.timeout || this.globalTimeout;
const result = await this.executeWithTimeout(middlewareConfig.middleware(enhancedContext, next), timeout, `Middleware ${middlewareConfig.name} timed out`);
// Обновляем статистику
this.updateStats(middlewareConfig, Date.now() - startTime, false, false);
// Обрабатываем результат
if (this.isMiddlewareResult(result)) {
if (result.abort) {
enhancedContext.flags.aborted = true;
this.updateStats(middlewareConfig, Date.now() - startTime, false, true);
return result.result;
}
if (result.modifiedArgs) {
currentArgs = [...result.modifiedArgs];
enhancedContext.flags.modified = true;
enhancedContext.args = currentArgs;
}
if (result.metadata) {
Object.assign(enhancedContext.metadata, result.metadata);
}
return result.result !== undefined ? result.result : await next(currentArgs);
}
return result;
}
catch (error) {
this.updateStats(middlewareConfig, Date.now() - startTime, true, false);
this.emit('middleware:error', {
operation: context.operation,
middleware: middlewareConfig.name,
error,
context: enhancedContext
});
throw error;
}
};
return await next();
}
/**
* Получить статистику middleware
*/
getStats(operation, name) {
if (operation && name) {
const middlewares = this.middleware.get(operation) || [];
const middleware = middlewares.find(m => m.name === name);
return middleware ? { ...middleware.stats } : {
totalExecutions: 0,
averageExecutionTime: 0,
errorCount: 0,
abortCount: 0,
lastExecuted: 0
};
}
if (operation) {
const middlewares = this.middleware.get(operation) || [];
const stats = {};
middlewares.forEach(m => {
stats[m.name || 'unknown'] = { ...m.stats };
});
return stats;
}
// Возвращаем статистику для всех операций
const allStats = {};
for (const [op, middlewares] of this.middleware) {
middlewares.forEach(m => {
const key = `${op}.${m.name}`;
allStats[key] = { ...m.stats };
});
}
return allStats;
}
/**
* Включить/выключить middleware
*/
toggleMiddleware(operation, name, enabled) {
const middlewares = this.middleware.get(operation);
if (!middlewares)
return;
const middleware = middlewares.find(m => m.name === name);
if (middleware) {
middleware.enabled = enabled;
this.emit('middleware:toggled', { operation, name, enabled });
}
}
/**
* Установить глобальный таймаут для middleware
*/
setGlobalTimeout(timeout) {
this.globalTimeout = timeout;
this.emit('middleware:timeout-changed', { timeout });
}
/**
* Получить все операции с middleware
*/
getOperations() {
return Array.from(this.middleware.keys());
}
/**
* Получить общую статистику
*/
getGlobalStats() {
let totalMiddleware = 0;
let totalExecutions = 0;
let totalExecutionTime = 0;
for (const middlewares of this.middleware.values()) {
totalMiddleware += middlewares.length;
for (const middleware of middlewares) {
totalExecutions += middleware.stats.totalExecutions;
totalExecutionTime += middleware.stats.averageExecutionTime * middleware.stats.totalExecutions;
}
}
return {
totalOperations: this.middleware.size,
totalMiddleware,
totalExecutions,
averageExecutionTime: totalExecutions > 0 ? totalExecutionTime / totalExecutions : 0
};
}
/**
* Очистить всю статистику
*/
clearStats() {
for (const middlewares of this.middleware.values()) {
for (const middleware of middlewares) {
middleware.stats = {
totalExecutions: 0,
averageExecutionTime: 0,
errorCount: 0,
abortCount: 0,
lastExecuted: 0
};
}
}
this.emit('middleware:stats-cleared');
}
/**
* Приватные методы
*/
async executeWithTimeout(promise, timeout, errorMessage) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(errorMessage));
}, timeout);
promise
.then(resolve)
.catch(reject)
.finally(() => clearTimeout(timer));
});
}
isMiddlewareResult(result) {
return result && typeof result === 'object' && ('result' in result ||
'abort' in result ||
'modifiedArgs' in result ||
'metadata' in result);
}
updateStats(middleware, executionTime, error, abort) {
const stats = middleware.stats;
stats.totalExecutions++;
stats.lastExecuted = Date.now();
if (error) {
stats.errorCount++;
}
if (abort) {
stats.abortCount++;
}
// Обновляем среднее время выполнения
stats.averageExecutionTime =
(stats.averageExecutionTime * (stats.totalExecutions - 1) + executionTime) / stats.totalExecutions;
}
}
/**
* Менеджер плагинов для CSElement
* Обеспечивает загрузку, управление и выполнение плагинов
*/
class PluginManager extends EventEmitter {
constructor() {
super();
this.plugins = new Map();
this.pluginOptions = new Map();
this.hooks = new Map();
this.advancedMiddleware = new AdvancedMiddlewareManagerImpl();
this.CSElementClass = null;
this.initializeHooks();
}
/**
* Инициализация хуков
*/
initializeHooks() {
const hookNames = [
'beforeCreate', 'afterCreate',
'beforeAddElement', 'afterAddElement',
'beforeRemoveElement', 'afterRemoveElement',
'beforeSetData', 'afterSetData',
'beforeDeleteData', 'afterDeleteData',
'beforeDestroy', 'afterDestroy'
];
hookNames.forEach(hookName => {
this.hooks.set(hookName, []);
});
}
/**
* Установить ссылку на класс CSElement
*/
setCSElementClass(CSElementClass) {
this.CSElementClass = CSElementClass;
}
/**
* Установить плагин
*/
use(plugin, options) {
try {
// Проверка зависимостей
if (plugin.dependencies) {
for (const dependency of plugin.dependencies) {
if (!this.hasPlugin(dependency)) {
throw new Error(`Плагин "${plugin.name}" требует зависимость "${dependency}", которая не установлена`);
}
}
}
// Проверка на дублирование
if (this.hasPlugin(plugin.name)) {
throw new Error(`Плагин "${plugin.name}" уже установлен`);
}
// Установка плагина
if (!this.CSElementClass) {
throw new Error('CSElement класс не установлен в менеджере плагинов');
}
plugin.install(this.CSElementClass, options);
// Сохранение плагина
this.plugins.set(plugin.name, plugin);
this.pluginOptions.set(plugin.name, options || {});
// Событие установки
const event = {
type: 'install',
plugin: plugin.name,
data: { version: plugin.version, options },
timestamp: Date.now()
};
this.emit('plugin:installed', event);
console.log(`✅ Плагин "${plugin.name}" v${plugin.version} успешно установлен`);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
const event = {
type: 'error',
plugin: plugin.name,
data: { error: errorMessage },
timestamp: Date.now()
};
this.emit('plugin:error', event);
throw error;
}
}
/**
* Удалить плагин
*/
uninstall(pluginName) {
try {
const plugin = this.plugins.get(pluginName);
if (!plugin) {
return false;
}
// Проверка зависимостей других плагинов
for (const [name, installedPlugin] of this.plugins) {
if (name !== pluginName && installedPlugin.dependencies?.includes(pluginName)) {
throw new Error(`Нельзя удалить плагин "${pluginName}": от него зависит плагин "${name}"`);
}
}
// Удаление плагина
if (plugin.uninstall) {
plugin.uninstall();
}
this.plugins.delete(pluginName);
this.pluginOptions.delete(pluginName);
// Событие удаления
const event = {
type: 'uninstall',
plugin: pluginName,
timestamp: Date.now()
};
this.emit('plugin:uninstalled', event);
console.log(`✅ Плагин "${pluginName}" успешно удален`);
return true;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
const event = {
type: 'error',
plugin: pluginName,
data: { error: errorMessage },
timestamp: Date.now()
};
this.emit('plugin:error', event);
throw error;
}
}
/**
* Получить установленный плагин
*/
getPlugin(name) {
return this.plugins.get(name) || null;
}
/**
* Получить все установленные плагины
*/
getPlugins() {
return Array.from(this.plugins.values());
}
/**
* Проверить, установлен ли плагин
*/
hasPlugin(name) {
return this.plugins.has(name);
}
/**
* Получить информацию о всех плагинах
*/
getPluginInfo() {
return Array.from(this.plugins.entries()).map(([name, plugin]) => ({
name: plugin.name,
version: plugin.version,
description: plugin.description,
dependencies: plugin.dependencies || [],
installed: true,
options: this.pluginOptions.get(name)
}));
}
/**
* Добавить хук жизненного цикла
*/
addHook(hookName, callback) {
const hooks = this.hooks.get(hookName) || [];
hooks.push(callback);
this.hooks.set(hookName, hooks);
}
/**
* Удалить хук жизненного цикла
*/
removeHook(hookName, callback) {
const hooks = this.hooks.get(hookName) || [];
const index = hooks.indexOf(callback);
if (index > -1) {
hooks.splice(index, 1);
this.hooks.set(hookName, hooks);
}
}
/**
* Добавить middleware с конфигурацией
*/
addAdvancedMiddleware(operation, config) {
this.advancedMiddleware.addMiddleware(operation, config);
}
/**
* Выполнить хуки жизненного цикла
*/
async executeHooks(hookName, ...args) {
const hooks = this.hooks.get(hookName) || [];
for (const hook of hooks) {
try {
const result = hook(...args);
if (result instanceof Promise) {
await result;
}
}
catch (error) {
console.error(`Ошибка в хуке ${hookName}:`, error);
this.emit('hook:error', { hookName, error, args });
}
}
}
/**
* Выполнить middleware цепочку
*/
async executeMiddleware(context, operation) {
return this.advancedMiddleware.executeMiddleware(context, operation);
}
/**
* Очистить все плагины (для тестирования)
*/
clear() {
// Удаляем все плагины
for (const [name] of this.plugins) {
try {
this.uninstall(name);
}
catch (error) {
console.error(`Ошибка при удалении плагина ${name}:`, error);
}
}
// Очищаем хуки и middleware
this.initializeHooks();
// Очищаем advanced middleware
this.advancedMiddleware.clearAllMiddleware();
}
/**
* Получить статистику плагинов
*/
getStats() {
const totalHooks = Array.from(this.hooks.values())
.reduce((sum, hooks) => sum + hooks.length, 0);
const advancedMiddlewareStats = this.advancedMiddleware.getGlobalStats();
return {
totalPlugins: this.plugins.size,
totalHooks,
totalMiddleware: advancedMiddlewareStats.totalMiddleware,
pluginsByType: {} // Можно расширить для категоризации плагинов
};
}
}
/**
* Интерфейсы для системы персистентности CSElement
* Поддержка различных типов хранилищ и адаптеров
*/
/**
* Типы хранилищ
*/
var StorageType;
(function (StorageType) {
StorageType["MEMORY"] = "memory";
StorageType["LOCAL_STORAGE"] = "localStorage";
StorageType["SESSION_STORAGE"] = "sessionStorage";
StorageType["INDEXED_DB"] = "indexedDB";
StorageType["FILE_SYSTEM"] = "fileSystem";
StorageType["CUSTOM"] = "custom";
})(StorageType || (StorageType = {}));
/**
* События системы персистентности
*/
var PersistenceEventType;
(function (PersistenceEventType) {
PersistenceEventType["ADAPTER_REGISTERED"] = "adapter:registered";
PersistenceEventType["ADAPTER_REMOVED"] = "adapter:removed";
PersistenceEventType["ADAPTER_ERROR"] = "adapter:error";
PersistenceEventType["SAVE_STARTED"] = "save:started";
PersistenceEventType["SAVE_COMPLETED"] = "save:completed";
PersistenceEventType["SAVE_FAILED"] = "save:failed";
PersistenceEventType["LOAD_STARTED"] = "load:started";
PersistenceEventType["LOAD_COMPLETED"] = "load:completed";
PersistenceEventType["LOAD_FAILED"] = "load:failed";
PersistenceEventType["DELETE_STARTED"] = "delete:started";
PersistenceEventType["DELETE_COMPLETED"] = "delete:completed";
PersistenceEventType["DELETE_FAILED"] = "delete:failed";
PersistenceEventType["SYNC_STARTED"] = "sync:started";
PersistenceEventType["SYNC_COMPLETED"] = "sync:completed";
PersistenceEventType["SYNC_FAILED"] = "sync:failed";
})(PersistenceEventType || (PersistenceEventType = {}));
/**
* Менеджер персистентности CSElement
* Управляет адаптерами хранилища и предоставляет единый API
*/
/**
* Реализация менеджера персистентности
*/
class PersistenceManagerImpl extends EventEmitter {
constructor() {
super();
this.adapters = new Map();
this.defaultAdapterName = null;
}
/**
* Регистрация адаптера хранилища
*/
registerAdapter(adapter) {
if (this.adapters.has(adapter.name)) {
throw new Error(`Адаптер с именем "${adapter.name}" уже зарегистрирован`);
}
this.adapters.set(adapter.name, adapter);
// Подписываемся на события адаптера
this.subscribeToAdapterEvents(adapter);
// Если это первый адаптер, делаем его адаптером по умолчанию
if (!this.defaultAdapterName) {
this.defaultAdapterName = adapter.name;
}
this.emitEvent({
type: PersistenceEventType.ADAPTER_REGISTERED,
timestamp: Date.now(),
adapterName: adapter.name
});
}
/**
* Получение адаптера по имени
*/
getAdapter(name) {
return this.adapters.get(name) || null;
}
/**
* Получение всех адаптеров
*/
getAllAdapters() {
return Array.from(this.adapters.values());
}
/**
* Удаление адаптера
*/
removeAdapter(name) {
const adapter = this.adapters.get(name);
if (!adapter) {
return false;
}
// Отписываемся от событий адаптера
this.unsubscribeFromAdapterEvents(adapter);
this.adapters.delete(name);
// Если удаляем адаптер по умолчанию, выбираем новый
if (this.defaultAdapterName === name) {
const remainingAdapters = Array.from(this.adapters.keys());
this.defaultAdapterName = remainingAdapters.length > 0 ? remainingAdapters[0] : null;
}
this.emitEvent({
type: PersistenceEventType.ADAPTER_REMOVED,
timestamp: Date.now(),
adapterName: name
});
return true;
}
/**
* Установка адаптера по умолчанию
*/
setDefaultAdapter(name) {
if (!this.adapters.has(name)) {
throw new Error(`Адаптер "${name}" не найден`);
}
this.defaultAdapterName = name;
}
/**
* Получение адаптера по умолчанию
*/
getDefaultAdapter() {
return this.defaultAdapterName ? this.adapters.get(this.defaultAdapterName) || null : null;
}
/**
* Сохранение элемента в указанное хранилище
*/
async save(elementId, adapterName, options) {
const adapter = this.getAdapterForOperation(adapterName);
if (!adapter) {
return {
success: false,
error: 'Адаптер хранилища не найден'
};
}
// Получаем элемент из глобального реестра
const element = CSElement.getElementById(elementId);
if (!element) {
return {
success: false,
error: `Элемент с ID "${elementId}" не найден`
};
}
this.emitEvent({
type: PersistenceEventType.SAVE_STARTED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name
});
try {
// Сериализуем элемент
const serializedData = element.serialize({
includeChildren: true,
includeData: true,
includeMetadata: true
});
// Сохраняем в адаптере
const result = await adapter.save(elementId, serializedData, options);
if (result.success) {
this.emitEvent({
type: PersistenceEventType.SAVE_COMPLETED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
metadata: result.metadata
});
}
else {
this.emitEvent({
type: PersistenceEventType.SAVE_FAILED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
error: result.error
});
}
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Неизвестная ошибка';
this.emitEvent({
type: PersistenceEventType.SAVE_FAILED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
error: errorMessage
});
return {
success: false,
error: errorMessage
};
}
}
/**
* Загрузка элемента из хранилища
*/
async load(elementId, adapterName, options) {
const adapter = this.getAdapterForOperation(adapterName);
if (!adapter) {
return {
success: false,
error: 'Адаптер хранилища не найден'
};
}
this.emitEvent({
type: PersistenceEventType.LOAD_STARTED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name
});
try {
const result = await adapter.load(elementId, options);
if (result.success) {
this.emitEvent({
type: PersistenceEventType.LOAD_COMPLETED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
metadata: result.metadata
});
}
else {
this.emitEvent({
type: PersistenceEventType.LOAD_FAILED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
error: result.error
});
}
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Неизвестная ошибка';
this.emitEvent({
type: PersistenceEventType.LOAD_FAILED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
error: errorMessage
});
return {
success: false,
error: errorMessage
};
}
}
/**
* Удаление элемента из хранилища
*/
async delete(elementId, adapterName, options) {
const adapter = this.getAdapterForOperation(adapterName);
if (!adapter) {
return {
success: false,
error: 'Адаптер хранилища не найден'
};
}
this.emitEvent({
type: PersistenceEventType.DELETE_STARTED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name
});
try {
const result = await adapter.delete(elementId, options);
if (result.success) {
this.emitEvent({
type: PersistenceEventType.DELETE_COMPLETED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name
});
}
else {
this.emitEvent({
type: PersistenceEventType.DELETE_FAILED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
error: result.error
});
}
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Неизвестная ошибка';
this.emitEvent({
type: PersistenceEventType.DELETE_FAILED,
timestamp: Date.now(),
elementId,
adapterName: adapter.name,
error: errorMessage
});
return {
success: false,
error: errorMessage
};
}
}
/**
* Синхронизация между хранилищами
*/
async sync(sourceAdapter, targetAdapter, options = {}) {
const source = this.adapters.get(sourceAdapter);
const target = this.adapters.get(targetAdapter);
if (!source || !target) {
throw new Error('Один из адаптеров не найден');
}
this.emitEvent({
type: PersistenceEventType.SYNC_STARTED,
timestamp: Date.now(),
metadata: { sourceAdapter, targetAdapter, options }
});
try {
let synced = 0;
let conflicts = 0;
const errors = [];
// Получаем все записи из источника
const sourceRecords = await source.search(options.filter || {}, { includeData: true });
for (const record of sourceRecords.records) {
try {
// Проверяем, существует ли запись в целевом хранилище
const targetExists = await target.exists(record.id);
if (targetExists) {
// Есть конфликт - применяем стратегию разрешения
const targetRecord = await target.load(record.id);
if (targetRecord.success && targetRecord.metadata) {
const shouldUpdate = this.resolveConflict(record.metadata, targetRecord.metadata, options.conflictResolution || 'latest');
if (shouldUpdate) {
await target.save(record.id, record.data);
synced++;
}
else {
conflicts++;
}
}
}
else {
// Записи нет в целевом хранилище - просто копируем
await target.save(record.id, record.data);
synced++;
}
}
catch (error) {
errors.push(`Ошибка синхронизации записи ${record.id}: ${error instanceof Error ? error.message : 'Неизвестная ошибка'}`);
}
}
// Если синхронизация двунаправленная, повторяем в обратную сторону
if (options.bidirectional) {
const targetRecords = await target.search(options.filter || {}, { includeData: true });
for (const record of targetRecords.records) {
try {
const sourceExists = await source.exists(record.id);
if (!sourceExists) {
await source.save(record.id, record.data);
synced++;
}
}
catch (error) {
errors.push(`Ошибка обратной синхронизации записи ${record.id}: ${error instanceof Error ? error.message : 'Неизвестная ошибка'}`);
}
}
}
const result = { synced, conflicts, errors };
this.emitEvent({
type: PersistenceEventType.SYNC_COMPLETED,
timestamp: Date.now(),
metadata: { sourceAdapter, targetAdapter, result }
});
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Неизвестная ошибка';
this.emitEvent({
type: PersistenceEventType.SYNC_FAILED,
timestamp: Date.now(),
error: errorMessage,
metadata: { sourceAdapter, targetAdapter }
});
throw error;
}
}
/**
* Резервное копирование
*/
async backup(adapterName, options = {}) {
const adapter = this.adapters.get(adapterName);
if (!adapter) {
throw new Error(`Адаптер "${adapterName}" не найден`);
}
// Получаем все записи
const records = await adapter.search({}, { includeData: true });
const backupData = {
version: '1.0',
timestamp: Date.now(),
adapter: {
name: adapter.name,
type: adapter.type,
config: adapter.config
},
records: records.records.map(record => ({
id: record.id,
data: record.data,
metadata: options.includeMetadata ? record.metadata : undefined
}))
};
// TODO: Добавить сжатие и шифрование если требуется
const serializedData = JSON.stringify(backupData);
const size = new TextEncoder().encode(serializedData).length;
return {
data: backupData,
metadata: {
timestamp: Date.now(),
recordCount: records.records.length,
size
}
};
}
/**
* Восстановление из резервной копии
*/
async restore(backupData, adapterName, options = {}) {
const adapter = this.adapters.get(adapterName);
if (!adapter) {
throw new Error(`Адаптер "${adapterName}" не найден`);
}
let restored = 0;
let skipped = 0;
const errors = [];
if (!backupData.records || !Array.isArray(backupData.records)) {
throw new Error('Неверный формат резервной копии');
}
for (const record of backupData.records) {
try {
const exists = await adapter.exists(record.id);
if (exists && !options.overwrite) {
skipped++;
continue;
}
// TODO: Добавить валидацию данных если требуется
await adapter.save(record.id, record.data);
restored++;
}
catch (error) {
errors.push(`Ошибка восстановления записи ${record.id}: ${error instanceof Error ? error.message : 'Неизвестная ошибка'}`);
}
}
return { restored, skipped, errors };
}
/**
* Получение адаптера для операции
*/
getAdapterForOperation(adapterName) {
if (adapterName) {
return this.adapters.get(adapterName) || null;
}
return this.getDefaultAdapter();
}
/**
* Разрешение конфликтов при синхронизации
*/
resolveConflict(sourceMetadata, targetMetadata, strategy) {
switch (strategy) {
case 'source':
return true; // Всегда используем источник
case 'target':
return false; // Всегда используем цель
case 'latest':
return sourceMetadata.updatedAt > targetMetadata.updatedAt;
case 'merge':
// Простая стратегия слияния - используем более новую версию
return sourceMetadata.version > targetMetadata.version;
default:
return false;
}
}
/**
* Подписка на события адаптера
*/
subscribeToAdapterEvents(adapter) {
// Проверяем, является ли адаптер EventEmitter
if ('on' in adapter && typeof adapter.on === 'function') {
adapter.on('error', (error) => {
this.emitEvent({
type: PersistenceEventType.ADAPTER_ERROR,
timestamp: Date.now(),
adapterName: adapter.name,
error: error.message || 'Неизвестная ошибка адаптера'
});
});
}
}
/**
* Отписка от событий адаптера
*/
unsubscribeFromAdapterEvents(adapter) {
// Проверяем, является ли адаптер EventEmitter
if ('removeAllListeners' in adapter && typeof adapter.removeAllListeners === 'function') {
adapter.removeAllListeners();
}
}
/**
* Генерация события персистентности
*/
emitEvent(eventData) {
this.emit('persistence:event', eventData);
this.emit(eventData.type, eventData);
}
}
/**
* Реализация менеджера истории изменений
*/
class HistoryManagerImpl {
constructor(config = {}) {
this.operations = [];
this.snapshots = [];
this.currentIndex = -1;
this.eventListeners = new Map();
this.stats = {
undoCount: 0,
redoCount: 0,
snapshotCount: 0,
lastCleanup: Date.now()
};
this.config = {
maxOperations: config.maxOperations ?? 100,
maxSize: config.maxSize ?? 10 * 1024 * 1024, // 10MB
snapshotInterval: config.snapshotInterval ?? 10,
autoCleanup: config.autoCleanup ?? true,
compression: config.compression ?? false,
trackOperations: config.trackOperations ?? [],
ignoreOperations: config.ignoreOperations ?? []
};
// Инициализация карты событий
const events = [
'operation-added', 'snapshot-created', 'undo-performed',
'redo-performed', 'history-cleared', 'cleanup-performed'
];
events.forEach(event => {
this.eventListeners.set(event, new Set());
});
}
/**
* Добавить операцию в историю
*/
addOperation(operation) {
// Проверяем, нужно ли игнорировать операцию
if (this.config.ignoreOperations.includes(operation.type)) {
return;
}
// Проверяем, нужно ли отслеживать операцию
if (this.config.trackOperations.length > 0 &&
!this.config.trackOperations.includes(operation.type)) {
return;
}
const newOperation = {
...operation,
id: this.generateId(),
timestamp: Date.now()
};
// Удаляем операции после текущей позиции (для новой ветки истории)
if (this.currentIndex < this.operations.length - 1) {
this.operations = this.operations.slice(0, this.currentIndex + 1);
}
this.operations.push(newOperation);
this.currentIndex = this.operations.length - 1;
// Создаем снимок если нужно
if (this.shouldCreateSnapshot()) {
this.createSnapshot(operation.after, `Snapshot after ${operation.description}`);
}
// Автоочистка если включена
if (this.config.autoCleanup) {
this.performCleanupIfNeeded();
}
this.emitEvent('operation-added', {
operation: newOperation,
state: this.getState(),
timestamp: Date.now()
});
}
/**
* Создать снимок состояния
*/
createSnapshot(data, description) {
const snapshot = {
id: this.generateId(),
timestamp: Date.now(),
data: this.config.compression ? this.compressData(data) : this.deepClone(data),
metadata: {
operation: description,
source: 'system'
},
size: this.calculateSize(data)
};
this.snapshots.push(snapshot);
this.stats.snapshotCount++;
this.emitEvent('snapshot-created', {
snapshot,
state: this.getState(),
timestamp: Date.now()
});
return snapshot;
}
/**
* Отменить последнюю операцию
*/
async undo() {
if (!this.canPerformUndo()) {
throw new Error('Невозможно выполнить отмену');
}
const operation = this.operations[this.currentIndex];
this.currentIndex--;
this.stats.undoCount++;
const result = operation.before;
this.emitEvent('undo-performed', {
operation,
state: this.getState(),
timestamp: Date.now()
});
return result;
}
/**
* Повторить отмененную операцию
*/
async redo() {
if (!this.canPerformRedo()) {
throw new Error('Невозможно выполнить повтор');
}
this.currentIndex++;
const operation = this.operations[this.currentIndex];
this.stats.redoCount++;
const result = operation.after;
this.emitEvent('redo-performed', {
operation,
state: this.getState(),
timestamp: Date.now()
});
return result;
}
/**
* Отменить до определенной операции
*/
async undoTo(operationId) {
const targetIndex = this.operations.findIndex(op => op.id === operationId);
if (targetIndex === -1) {
throw new Error(`Операция с ID ${operationId} не найдена`);
}
if (targetIndex >= this.currentIndex) {
throw new Error('Невозможно отменить до указанной операции');
}
let result;
while (this.currentIndex > targetIndex) {
result = await this.undo();
}
return result;
}
/**
* Повторить до определенной операции
*/
async redoTo(operationId) {
const targetIndex = this.operations.findIndex(op => op.id === operationId);
if (targetIndex === -1) {
throw new Error(`Операция с ID ${operationId} не найдена`);
}
if (targetIndex <= this.currentIndex) {
throw new Error('Невозможно повторить до указанной операции');
}
let result;
while (this.currentIndex < targetIndex) {
result = await this.redo();
}
return result;
}
/**
* Получить текущее состояние истории
*/
getState() {
const totalSize = this.calculateTotalSize();
return {
currentIndex: this.currentIndex,
maxSize: this.config.maxOperations,
totalOperations: this.operations.length,
totalSize,
canUndo: this.canPerformUndo(),
canRedo: this.canPerformRedo(),
stats: { ...this.stats }
};
}
/**
* Получить список операций
*/
getOperations(limit) {
const ops = [...this.operations];
return limit ? ops.slice(-limit) : ops;
}
/**
* Получить список снимков
*/
getSnapshots(limit) {
const snapshots = [...this.snapshots];
return limit ? snapshots.slice(-limit) : snapshots;
}
/**
* Очистить историю
*/
clear() {
this.operations = [];
this.snapshots = [];
this.currentIndex = -1;
this.stats = {
undoCount: 0,
redoCount: 0,
snapshotCount: 0,
lastCleanup: Date.now()
};
this.emitEvent('history-cleared', {
state: this.getState(),
timestamp: Date.now()
});
}
/**
* Очистить старые записи
*/
cleanup() {
const beforeCount = this.operations.length;
// Удаляем старые операции если превышен лимит
if (this.operations.length > this.config.maxOperations) {
const toRemove = this.operations.length - this.config.maxOperations;
this.operations = this.operations.slice(toRemove);
this.currentIndex = Math.max(-1, this.currentIndex - toRemove);
}
// Удаляем старые снимки если превышен размер
const totalSize = this.calculateTotalSize();
if (totalSize > this.config.maxSize) {
this.cleanupBySize();
}
this.stats.lastCleanup = Date.now();
if (beforeCount !== this.operations.length) {
this.emitEvent('cleanup-performed', {
state: this.getState(),
timestamp: Date.now()
});
}
}
/**
* Получить операцию по ID
*/
getOperation(id) {
return this.operations.find(op => op.id === id) || null;
}
/**
* Получить снимок по ID
*/
getSnapshot(id) {
return this.snapshots.find(snapshot => snapshot.id === id) || null;
}
/**
* Экспорт истории
*/
export() {
return {
version: '1.0.0',
timestamp: Date.now(),
operations: [...this.operations],
snapshots: [...this.snapshots],
state: this.getState(),
config: { ...this.config }
};
}
/**
* Импорт истории
*/
import(data) {
this.operations = [...data.operations];
this.snapshots = [...data.snapshots];
this.currentIndex = data.state.currentIndex;
this.stats = { ...data.state.stats };
this.emitEvent('history-cleared', {
state: this.getState(),
timestamp: Date.now()
});
}
/**
* Подписка на события истории
*/
on(event, callback) {
const listeners = this.eventListeners.get(event);
if (listeners) {
listeners.add(callback);
}
}
/**
* Отписка от событий
*/
off(event, callback) {
const listeners = this.eventListeners.get(event);
if (listeners) {
listeners.delete(callback);
}
}
/**
* Создать diff между двумя состояниями
*/
createDiff(before, after, path = []) {
const diffs = [];
if (before === after) {
return diffs;
}
if (typeof before !== typeof after || before === null || after === null) {
diffs.push({
type: 'modified',
path: [...path],
oldValue: before,
newValue: after
});
return diffs;
}
if (Array.isArray(before) && Array.isArray(after)) {
const maxLength = Math.max(before.length, after.length);
for (let i = 0; i < maxLength; i++) {
if (i >= before.length) {
diffs.push({
type: 'added',
path: [...path, i.toString()],
newValue: after[i],
index: i
});
}
else if (i >= after.length) {
diffs.push({
type: 'removed',
path: [...path, i.toString()],
oldValue: before[i],
index: i
});
}
else {
diffs.push(...this.createDiff(before[i], after[i], [...path, i.toString()]));
}
}
return diffs;
}
if (typeof before === 'object' && typeof after === 'object') {
const allKeys = new Set([...Object.keys(before), ...Object.keys(after)]);
for (const key of allKeys) {
if (!(key in before)) {
diffs.push({
type: 'added',
path: [...path, key],
newValue: after[key]
});
}
else if (!(key in after)) {
diffs.push({
type: 'removed',
path: [...path, key],
oldValue: before[key]
});
}
else {
diffs.push(...this.createDiff(before[key], after[key], [...path, key]));
}
}
return diffs;
}
diffs.push({
type: 'modified',
path: [...path],
oldValue: before,
newValue: after
});
return diffs;
}
// Приватные методы
generateId() {
return `hist_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
canPerformUndo() {
return this.currentIndex >= 0 &&
this.operations[this.currentIndex]?.canUndo !== false;
}
canPerformRedo() {
return this.currentIndex < this.operations.length - 1 &&
this.operations[this.currentIndex + 1]?.canRedo !== false;
}
shouldCreateSnapshot() {
return this.operations.length % this.config.snapshotInterval === 0;
}
performCleanupIfNeeded() {
if (this.operations.length > this.config.maxOperations ||
this.calculateTotalSize() > this.config.maxSize) {
this.cleanup();
}
}
calculateSize(data) {
return new Blob([JSON.stringify(data)]).size;
}
calculateTotalSize() {
const operationsSize = this.operations.reduce((sum, op) => sum + this.calculateSize(op), 0);
const snapshotsSize = this.snapshots.reduce((sum, snapshot) => sum + snapshot.size, 0);
return operationsSize + snapshotsSize;
}
cleanupBySize() {
let currentSize = this.calculateTotalSize();
// Сначала удаляем старые снимки
while (currentSize > this.config.maxSize && this.snapshots.length > 1) {
const removed = this.snapshots.shift();
if (removed) {
currentSize -= removed.size;
}
}
// Затем удаляем старые операции
while (currentSize > this.config.maxSize && this.operations.length > 10) {
const removed = this.operations.shift();
if (removed) {
currentSize -= this.calculateSize(removed);
this.currentIndex--;
}
}
}
deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (Array.isArray(obj)) {
return obj.map(item => this.deepClone(item));
}
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = this.deepClone(obj[key]);
}
}
return cloned;
}
compressData(data) {
// Простое сжатие - удаление undefined значений и дублирование
return JSON.parse(JSON.stringify(data));
}
emitEvent(event, data) {
const listeners = this.eventListeners.get(event);
if (listeners) {
listeners.forEach(callback => {
try {
callback(data);
}
catch (error) {
console.error(`Ошибка в обработчике события ${event}:`, error);
}
});
}
}
}
/**
* Реализация менеджера реактивности и computed свойств
*/
class ReactivityManagerImpl extends EventEmitter {
constructor(config = {}) {
super();
this.computedProperties = new Map();
this.watchers = new Map();
this.reactiveProperties = new Map();
this.refs = new Map();
this.scopes = new Map();
this.currentlyComputing = new Set();
this.notificationQueue = [];
this.batchTimeout = null;
this.statsInterval = null;
this.memoryLeakCheckInterval = null;
// Контекст выполнения для отслеживания иерархии
this.context = {
computedStack: [],
watcherStack: [],
cleanupFns: []
};
this.stats = {
computedCount: 0,
watcherCount: 0,
refCount: 0,
scopeCount: 0,
disposedCount: 0,
notificationsPerSecond: 0,
averageComputeTime: 0,
memoryUsage: 0,
totalComputeTime: 0,
computeCallCount: 0
};
this.notificationHistory = [];
this.currentDependencyTracker = null;
this.config = {
maxComputed: 1000,
maxWatchers: 10000,
defaultTTL: 60000, // 1 минута
maxDepth: 10,
batchNotifications: true,
batchSize: 100,
debug: false,
autoDispose: true,
maxDisposeDepth: 50,
warnMemoryLeaks: true,
...config
};
// Обновляем статистику каждую секунду
this.statsInterval = setInterval(() => this.updateStats(), 1000);
// Проверяем утечки памяти каждые 30 секунд
if (this.config.warnMemoryLeaks) {
this.memoryLeakCheckInterval = setInterval(() => this.checkMemoryLeaks(), 30000);
}
}
computed(compute, config = {}) {
if (this.computedProperties.size >= this.config.maxComputed) {
throw new Error(`Превышен лимит computed свойств: ${this.config.maxComputed}`);
}
const id = this.generateId('computed');
const computedConfig = {
lazy: true,
cache: true,
ttl: this.config.defaultTTL,
maxDepth: this.config.maxDepth,
debug: this.config.debug,
autoDispose: this.config.autoDispose,
...config
};
// Определяем родителя из текущего контекста
const parent = config.parent || this.context.currentComputed;
const computedProperty = {
id,
compute,
dependencies: new Set(),
cachedValue: undefined,
isValid: false,
lastUpdated: 0,
config: computedConfig,
parent,
children: new Set(),
isDisposed: false
};
// Устанавливаем функцию очистки
computedProperty.dispose = () => {
this.disposeComputed(id);
};
this.computedProperties.set(id, computedProperty);
this.stats.computedCount++;
// Устанавливаем связь родитель-ребенок
if (parent) {
this.setParent(id, parent, 'computed');
}
// Если не ленивое, сразу вычисляем
if (!computedConfig.lazy) {
this.getComputedValue(id);
}
this.emitEvent('computed-created', {
computedId: id,
dependencies: Array.from(computedProperty.dependencies),
parent
});
return computedProperty;
}
watch(path, callback, config = {}) {
if (this.watchers.size >= this.config.maxWatchers) {
throw new Error(`Превышен лимит наблюдателей: ${this.config.maxWatchers}`);
}
const id = this.generateId('watcher');
const pathArray = Array.isArray(path) ? path : [path];
const pathKey = pathArray.join('.');
const watcherConfig = {
immediate: false,
deep: false,
flush: 'async',
debug: this.config.debug,
autoDispose: this.config.autoDispose,
...config
};
// Определяем родителя из текущего контекста
const parent = config.parent || this.context.currentComputed || this.context.currentWatcher;
const watcher = {
id,
path: pathArray,
callback,
config: watcherConfig,
active: true,
parent,
children: new Set(),
isDisposed: false
};
// Устанавливаем функцию очистки
watcher.dispose = () => {
this.disposeWatcher(id);
};
this.watchers.set(id, watcher);
this.stats.watcherCount++;
// Регистрируем наблюдатель для свойства
if (!this.reactiveProperties.has(pathKey)) {
this.reactiveProperties.set(pathKey, {
path: pathArray,
watchers: new Set(),
computedDependents: new Set()
});
}
this.reactiveProperties.get(pathKey).watchers.add(watcher);
// Устанавливаем связь родитель-ребенок
if (parent) {
this.setParent(id, parent, 'watcher');
}
this.emitEvent('watcher-created', {
watcherId: id,
path: pathArray,
parent
});
// Немедленный вызов если настроен
if (watcherConfig.immediate) {
try {
// Устанавливаем контекст для отслеживания дочерних элементов
const previousWatcher = this.context.currentWatcher;
this.context.currentWatcher = id;
this.context.watcherStack.push(id);
callback(undefined, undefined, pathArray);
this.context.watcherStack.pop();
this.context.currentWatcher = previousWatcher;
}
catch (error) {
console.error(`Ошибка в немедленном вызове наблюдателя ${id}:`, error);
}
}
return watcher;
}
unwatch(watcherId) {
const watcher = this.watchers.get(watcherId);
if (!watcher || watcher.isDisposed) {
return false;
}
this.disposeWatcher(watcherId);
return true;
}
getComputedValue(computedId) {
const computed = this.computedProperties.get(computedId);
if (!computed || computed.isDisposed) {
return undefined;
}
// Проверяем валидность кэша
if (computed.isValid && computed.config.cache) {
if (computed.config.ttl && Date.now() - computed.lastUpdated > computed.config.ttl) {
computed.isValid = false;
}
else {
return computed.cachedValue;
}
}
// Проверяем циклические зависимости
if (this.currentlyComputing.has(computedId)) {
throw new Error(`Обнаружена циклическая зависимость в computed ${computedId}`);
}
this.currentlyComputing.add(computedId);
// Устанавливаем контекст для отслеживания дочерних элементов
const previousComputed = this.context.currentComputed;
this.context.currentComputed = computedId;
this.context.computedStack.push(computedId);
try {
const startTime = performance.now();
// Очищаем старые зависимости только если не кэшируется
if (!computed.config.cache || !computed.isValid) {
this.clearComputedDependencies(computedId);
}
// Если computed пересоздается и включен auto-dispose, очищаем детей
if (computed.config.autoDispose && computed.children.size > 0) {
this.disposeChildren(computedId);
}
// Устанавливаем отслеживание зависимостей
this.currentDependencyTracker = computedId;
// Вычисляем новое значение
const value = computed.compute();
computed.cachedValue = value;
computed.isValid = true;
computed.lastUpdated = Date.now();
const endTime = performance.now();
const computeTime = endTime - startTime;
this.stats.totalComputeTime += computeTime;
this.stats.computeCallCount++;
this.emitEvent('computed-updated', {
computedId,
dependencies: Array.from(computed.dependencies)
});
return value;
}
catch (error) {
computed.isValid = false;
throw error;
}
finally {
this.currentDependencyTracker = null;
this.currentlyComputing.delete(computedId);
this.context.computedStack.pop();
this.context.currentComputed = previousComputed;
}
}
// === AUTO-DISPOSE МЕТОДЫ ===
createScope(parentId) {
const id = this.generateId('scope');
const scope = {
id,
computedProperties: new Set(),
watchers: new Set(),
parent: parentId,
children: new Set(),
isDisposed: false,
dispose: () => this.disposeScope(id)
};
this.scopes.set(id, scope);
this.stats.scopeCount++;
// Устанавливаем связь с родительской областью
if (parentId) {
const parentScope = this.scopes.get(parentId);
if (parentScope && !parentScope.isDisposed) {
parentScope.children.add(id);
}
}
this.emitEvent('scope-created', {
scopeId: id,
parent: parentId
});
return scope;
}
runInScope(scopeId, fn) {
const scope = this.scopes.get(scopeId);
if (!scope || scope.isDisposed) {
throw new Error(`Область видимости ${scopeId} не найдена или была disposed`);
}
// Сохраняем текущий контекст
const previousContext = { ...this.context };
try {
// Выполняем функцию в контексте области
return fn();
}
finally {
// Восстанавливаем контекст
this.context = previousContext;
}
}
disposeScope(scopeId) {
const scope = this.scopes.get(scopeId);
if (!scope || scope.isDisposed) {
return;
}
// Сначала очищаем все дочерние области
for (const childId of scope.children) {
this.disposeScope(childId);
}
// Очищаем все computed в этой области
for (const computedId of scope.computedProperties) {
this.disposeComputed(computedId);
}
// Очищаем все watchers в этой области
for (const watcherId of scope.watchers) {
this.disposeWatcher(watcherId);
}
// Удаляем связь с родительской областью
if (scope.parent) {
const parentScope = this.scopes.get(scope.parent);
if (parentScope) {
parentScope.children.delete(scopeId);
}
}
scope.isDisposed = true;
this.scopes.delete(scopeId);
this.stats.scopeCount--;
this.stats.disposedCount++;
this.emitEvent('scope-disposed', {
scopeId,
parent: scope.parent
});
}
disposeComputed(computedId) {
const computed = this.computedProperties.get(computedId);
if (!computed || computed.isDisposed) {
return;
}
// Сначала очищаем всех детей
this.disposeChildren(computedId);
// Удаляем зависимости
this.clearComputedDependencies(computedId);
// Удаляем связь с родителем
if (computed.parent) {
this.removeFromParent(computedId, computed.parent);
}
computed.isDisposed = true;
this.computedProperties.delete(computedId);
this.stats.computedCount--;
this.stats.disposedCount++;
this.emitEvent('computed-disposed', {
computedId,
parent: computed.parent,
children: Array.from(computed.children)
});
}
disposeWatcher(watcherId) {
const watcher = this.watchers.get(watcherId);
if (!watcher || watcher.isDisposed) {
return;
}
// Сначала очищаем всех детей
this.disposeChildren(watcherId);
// Удаляем из реактивных свойств
const pathKey = watcher.path.join('.');
const property = this.reactiveProperties.get(pathKey);
if (property) {
property.watchers.delete(watcher);
if (property.watchers.size === 0 && property.computedDependents.size === 0) {
this.reactiveProperties.delete(pathKey);
}
}
// Удаляем связь с родителем
if (watcher.parent) {
this.removeFromParent(watcherId, watcher.parent);
}
watcher.isDisposed = true;
this.watchers.delete(watcherId);
this.stats.watcherCount--;
this.stats.disposedCount++;
this.emitEvent('watcher-disposed', {
watcherId,
parent: watcher.parent,
children: Array.from(watcher.children)
});
}
getCurrentContext() {
return { ...this.context };
}
setParent(childId, parentId, type) {
if (type === 'computed') {
const child = this.computedProperties.get(childId);
const parent = this.computedProperties.get(parentId);
if (child && parent && !child.isDisposed && !parent.isDisposed) {
child.parent = parentId;
parent.children.add(childId);
}
}
else {
const child = this.watchers.get(childId);
// Родитель может быть как computed, так и watcher
const parentComputed = this.computedProperties.get(parentId);
const parentWatcher = this.watchers.get(parentId);
if (child && !child.isDisposed) {
if (parentComputed && !parentComputed.isDisposed) {
child.parent = parentId;
parentComputed.children.add(childId);
}
else if (parentWatcher && !parentWatcher.isDisposed) {
child.parent = parentId;
parentWatcher.children.add(childId);
}
}
}
}
getChildren(parentId, type) {
const computed = this.computedProperties.get(parentId);
const watcher = this.watchers.get(parentId);
let children = new Set();
if (computed && !computed.isDisposed) {
children = new Set([...children, ...computed.children]);
}
if (watcher && !watcher.isDisposed) {
children = new Set([...children, ...watcher.children]);
}
if (type) {
return Array.from(children).filter(childId => {
if (type === 'computed') {
return this.computedProperties.has(childId);
}
else {
return this.watchers.has(childId);
}
});
}
return Array.from(children);
}
isDisposed(id, type) {
if (type === 'computed') {
const computed = this.computedProperties.get(id);
return !computed || computed.isDisposed;
}
else {
const watcher = this.watchers.get(id);
return !watcher || watcher.isDisposed;
}
}
// === ПРИВАТНЫЕ МЕТОДЫ ===
disposeChildren(parentId) {
const computed = this.computedProperties.get(parentId);
const watcher = this.watchers.get(parentId);
const children = new Set();
if (computed) {
for (const childId of computed.children) {
children.add(childId);
}
computed.children.clear();
}
if (watcher) {
for (const childId of watcher.children) {
children.add(childId);
}
watcher.children.clear();
}
// Рекурсивно очищаем всех детей
for (const childId of children) {
if (this.computedProperties.has(childId)) {
this.disposeComputed(childId);
}
else if (this.watchers.has(childId)) {
this.disposeWatcher(childId);
}
}
}
removeFromParent(childId, parentId) {
const parentComputed = this.computedProperties.get(parentId);
const parentWatcher = this.watchers.get(parentId);
if (parentComputed) {
parentComputed.children.delete(childId);
}
if (parentWatcher) {
parentWatcher.children.delete(childId);
}
}
checkMemoryLeaks() {
const now = Date.now();
const leakThreshold = 300000; // 5 минут
// Проверяем computed свойства
for (const [id, computed] of this.computedProperties) {
if (!computed.isDisposed && (now - computed.lastUpdated) > leakThreshold) {
if (computed.dependencies.size === 0 && computed.children.size === 0) {
this.emitEvent('memory-leak-detected', {
leakInfo: {
type: 'computed',
id,
age: now - computed.lastUpdated,
dependencies: computed.dependencies.size
}
});
}
}
}
// Проверяем watchers
for (const [id, watcher] of this.watchers) {
if (!watcher.isDisposed && watcher.children.size === 0) {
// Если watcher давно не использовался и у него нет детей
this.emitEvent('memory-leak-detected', {
leakInfo: {
type: 'watcher',
id,
age: now,
dependencies: 0
}
});
}
}
}
invalidateComputed(computedId) {
const computed = this.computedProperties.get(computedId);
if (computed && !computed.isDisposed) {
computed.isValid = false;
this.emitEvent('computed-invalidated', {
computedId
});
}
}
invalidateByPath(path) {
const pathKey = path.join('.');
const property = this.reactiveProperties.get(pathKey);
if (property) {
for (const computedId of property.computedDependents) {
this.invalidateComputed(computedId);
}
}
}
notify(path, newValue, oldValue) {
if (this.config.batchNotifications) {
this.notificationQueue.push({ path, newValue, oldValue });
this.scheduleBatchNotification();
}
else {
this.processNotification(path, newValue, oldValue);
}
}
scheduleBatchNotification() {
if (this.batchTimeout)
return;
this.batchTimeout = setTimeout(() => {
this.processBatchNotifications();
this.batchTimeout = null;
}, 0);
}
processBatchNotifications() {
const notifications = this.notificationQueue.splice(0, this.config.batchSize);
for (const notification of notifications) {
this.processNotification(notification.path, notification.newValue, notification.oldValue);
}
if (this.notificationQueue.length > 0) {
this.scheduleBatchNotification();
}
}
processNotification(path, newValue, oldValue) {
const pathKey = path.join('.');
const property = this.reactiveProperties.get(pathKey);
if (!property)
return;
this.notificationHistory.push(Date.now());
// Уведомляем watchers
for (const watcher of property.watchers) {
if (watcher.active && !watcher.isDisposed) {
try {
// Устанавливаем контекст для отслеживания дочерних элементов
const previousWatcher = this.context.currentWatcher;
this.context.currentWatcher = watcher.id;
this.context.watcherStack.push(watcher.id);
if (watcher.config.flush === 'sync') {
watcher.callback(newValue, oldValue, path);
}
else {
setTimeout(() => {
if (!watcher.isDisposed) {
watcher.callback(newValue, oldValue, path);
}
}, 0);
}
this.context.watcherStack.pop();
this.context.currentWatcher = previousWatcher;
this.emitEvent('watcher-triggered', {
watcherId: watcher.id,
path,
newValue,
oldValue
});
}
catch (error) {
console.error(`Ошибка в наблюдателе ${watcher.id}:`, error);
}
}
}
// Инвалидируем computed свойства
for (const computedId of property.computedDependents) {
this.invalidateComputed(computedId);
}
this.emitEvent('property-changed', {
path,
newValue,
oldValue
});
}
ref(value) {
const id = this.generateId('ref');
const manager = this;
const ref = {
id,
watchers: new Set(),
isDisposed: false,
dispose: () => {
ref.isDisposed = true;
this.refs.delete(id);
this.stats.refCount--;
this.stats.disposedCount++;
},
get value() {
// Отслеживаем зависимость
if (manager.currentDependencyTracker) {
manager.trackDependency(manager.currentDependencyTracker, [id]);
}
return value;
},
set value(newValue) {
const oldValue = value;
value = newValue;
manager.notify([id], newValue, oldValue);
}
};
this.refs.set(id, ref);
this.stats.refCount++;
return ref;
}
reactive(obj) {
return this.createReactiveProxy(obj, []);
}
createReactiveProxy(obj, basePath) {
return new Proxy(obj, {
get: (target, prop) => {
if (typeof prop === 'symbol')
return target[prop];
const path = [...basePath, prop];
// Отслеживаем зависимость
if (this.currentDependencyTracker) {
this.trackDependency(this.currentDependencyTracker, path);
}
const value = target[prop];
// Если значение - объект, создаем для него реактивный прокси
if (typeof value === 'object' && value !== null) {
return this.createReactiveProxy(value, path);
}
return value;
},
set: (target, prop, value) => {
if (typeof prop === 'symbol') {
target[prop] = value;
return true;
}
const path = [...basePath, prop];
const oldValue = target[prop];
target[prop] = value;
this.notify(path, value, oldValue);
return true;
}
});
}
trackDependency(computedId, path) {
const computed = this.computedProperties.get(computedId);
if (!computed || computed.isDisposed)
return;
const pathKey = path.join('.');
computed.dependencies.add(pathKey);
// Регистрируем computed как зависимый от этого свойства
if (!this.reactiveProperties.has(pathKey)) {
this.reactiveProperties.set(pathKey, {
path,
watchers: new Set(),
computedDependents: new Set()
});
}
this.reactiveProperties.get(pathKey).computedDependents.add(computedId);
this.emitEvent('dependency-added', {
computedId,
path
});
}
clearComputedDependencies(computedId) {
const computed = this.computedProperties.get(computedId);
if (!computed)
return;
for (const pathKey of computed.dependencies) {
const property = this.reactiveProperties.get(pathKey);
if (property) {
property.computedDependents.delete(computedId);
if (property.watchers.size === 0 && property.computedDependents.size === 0) {
this.reactiveProperties.delete(pathKey);
}
this.emitEvent('dependency-removed', {
computedId,
path: property.path
});
}
}
computed.dependencies.clear();
}
getDependencies(computedId) {
const computed = this.computedProperties.get(computedId);
return computed ? Array.from(computed.dependencies) : [];
}
getDependencyGraph() {
const nodes = new Map();
const edges = new Map();
// Добавляем узлы для всех свойств
for (const [pathKey, property] of this.reactiveProperties) {
nodes.set(pathKey, {
path: property.path,
type: 'property',
dependentCount: property.computedDependents.size + property.watchers.size,
dependencyCount: 0
});
}
// Добавляем узлы для computed свойств
for (const [computedId, computed] of this.computedProperties) {
if (!computed.isDisposed) {
nodes.set(computedId, {
path: [computedId],
type: 'computed',
dependentCount: computed.children.size,
dependencyCount: computed.dependencies.size
});
// Добавляем рёбра зависимостей
edges.set(computedId, new Set(computed.dependencies));
}
}
// Добавляем узлы для refs
for (const [refId, ref] of this.refs) {
if (!ref.isDisposed) {
nodes.set(refId, {
path: [refId],
type: 'ref',
dependentCount: ref.watchers.size,
dependencyCount: 0
});
}
}
return { nodes, edges };
}
clear() {
// Очищаем все области видимости
for (const scopeId of this.scopes.keys()) {
this.disposeScope(scopeId);
}
this.computedProperties.clear();
this.watchers.clear();
this.reactiveProperties.clear();
this.refs.clear();
this.scopes.clear();
this.currentlyComputing.clear();
this.notificationQueue = [];
// Сбрасываем контекст
this.context = {
computedStack: [],
watcherStack: [],
cleanupFns: []
};
if (this.batchTimeout) {
clearTimeout(this.batchTimeout);
this.batchTimeout = null;
}
if (this.statsInterval) {
clearInterval(this.statsInterval);
this.statsInterval = null;
}
if (this.memoryLeakCheckInterval) {
clearInterval(this.memoryLeakCheckInterval);
this.memoryLeakCheckInterval = null;
}
this.stats = {
computedCount: 0,
watcherCount: 0,
refCount: 0,
scopeCount: 0,
disposedCount: 0,
notificationsPerSecond: 0,
averageComputeTime: 0,
memoryUsage: 0,
totalComputeTime: 0,
computeCallCount: 0
};
}
getStats() {
return {
computedCount: this.stats.computedCount,
watcherCount: this.stats.watcherCount,
refCount: this.stats.refCount,
scopeCount: this.stats.scopeCount,
disposedCount: this.stats.disposedCount,
notificationsPerSecond: this.stats.notificationsPerSecond,
averageComputeTime: this.stats.averageComputeTime,
memoryUsage: this.estimateMemoryUsage()
};
}
updateStats() {
// Подсчитываем уведомления за последнюю секунду
const now = Date.now();
this.notificationHistory = this.notificationHistory.filter(time => now - time < 1000);
this.stats.notificationsPerSecond = this.notificationHistory.length;
// Обновляем среднее время вычисления
if (this.stats.computeCallCount > 0) {
this.stats.averageComputeTime = this.stats.totalComputeTime / this.stats.computeCallCount;
}
}
estimateMemoryUsage() {
let memoryUsage = 0;
// Примерная оценка использования памяти
memoryUsage += this.computedProperties.size * 500; // ~500 байт на computed
memoryUsage += this.watchers.size * 300; // ~300 байт на watcher
memoryUsage += this.reactiveProperties.size * 200; // ~200 байт на свойство
memoryUsage += this.refs.size * 100; // ~100 байт на ref
memoryUsage += this.scopes.size * 400; // ~400 байт на scope
return memoryUsage;
}
generateId(prefix) {
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
emitEvent(event, data) {
this.emit(event, {
event,
timestamp: Date.now(),
...data
});
}
onReactivityEvent(event, callback) {
this.on(event, callback);
}
offReactivityEvent(event, callback) {
this.off(event, callback);
}
}
/**
* Реализация менеджера алгоритмов работы с графами
*/
class GraphAlgorithmsManagerImpl extends EventEmitter {
constructor(config = {}) {
super();
this.config = {
defaultTimeout: 30000,
defaultMaxDepth: 1000,
enableCaching: true,
cacheSize: 1000,
enableEvents: true,
enableDebug: false,
...config
};
this.cache = {
paths: new Map(),
cycles: new Map(),
components: new Map(),
statistics: new Map(),
ttl: 5 * 60 * 1000 // 5 минут
};
this.metrics = {
totalExecutions: 0,
averageExecutionTime: new Map(),
cacheHits: 0,
cacheMisses: 0,
memoryUsage: 0,
lastExecutionTime: 0
};
}
// ==================== Поиск путей ====================
async findPath(from, to, options = {}) {
const startTime = Date.now();
const algorithm = options.algorithm || 'bfs';
this.emitEvent({
type: 'algorithm-start',
algorithm: `findPath-${algorithm}`,
timestamp: startTime
});
try {
const cacheKey = this.getCacheKey('path', from.id, to.id, options);
// Проверяем кэш
if (this.config.enableCaching && this.cache.paths.has(cacheKey)) {
this.metrics.cacheHits++;
return this.cache.paths.get(cacheKey);
}
let result = null;
switch (algorithm) {
case 'bfs':
result = await this.findPathBFS(from, to, options);
break;
case 'dfs':
result = await this.findPathDFS(from, to, options);
break;
case 'dijkstra':
result = await this.findPathDijkstra(from, to, options);
break;
case 'astar':
result = await this.findPathAStar(from, to, options);
break;
default:
throw new Error(`Неизвестный алгоритм поиска пути: ${algorithm}`);
}
// Кэшируем результат
if (result && this.config.enableCaching) {
this.cache.paths.set(cacheKey, result);
this.metrics.cacheMisses++;
}
this.updateMetrics(algorithm, Date.now() - startTime);
this.emitEvent({
type: 'algorithm-complete',
algorithm: `findPath-${algorithm}`,
result,
timestamp: Date.now()
});
return result;
}
catch (error) {
this.emitEvent({
type: 'algorithm-error',
algorithm: `findPath-${algorithm}`,
error: error,
timestamp: Date.now()
});
throw error;
}
}
async findShortestPath(from, to, options = {}) {
// Для кратчайшего пути используем Dijkstra или BFS для невзвешенного графа
const algorithm = options.weighted ? 'dijkstra' : 'bfs';
return this.findPath(from, to, { ...options, algorithm });
}
async findAllPaths(from, to, options = {}) {
const startTime = Date.now();
const paths = [];
const visited = new Set();
const currentPath = [];
const maxPaths = options.maxPathLength || 100;
const dfs = async (current, target, depth = 0) => {
if (depth > (options.maxDepth || this.config.defaultMaxDepth))
return;
if (paths.length >= maxPaths)
return;
currentPath.push(current.id);
if (current.id === target.id && currentPath.length > 1) {
paths.push({
path: [...currentPath],
cost: this.calculatePathCost(currentPath, options),
steps: currentPath.length - 1,
executionTime: Date.now() - startTime,
algorithm: 'dfs',
metadata: {
visitedNodes: visited.size,
exploredEdges: 0,
isOptimal: false
}
});
currentPath.pop();
return;
}
visited.add(current.id);
const neighbors = this.getNeighbors(current, options);
for (const neighbor of neighbors) {
if (!visited.has(neighbor.id)) {
await dfs(neighbor, target, depth + 1);
}
}
visited.delete(current.id);
currentPath.pop();
};
await dfs(from, to);
return paths;
}
// ==================== Обнаружение циклов ====================
async detectCycles(root, options = {}) {
const startTime = Date.now();
const algorithm = options.algorithm || 'dfs';
this.emitEvent({
type: 'algorithm-start',
algorithm: `detectCycles-${algorithm}`,
timestamp: startTime
});
try {
const cacheKey = this.getCacheKey('cycles', root.id, '', options);
if (this.config.enableCaching && this.cache.cycles.has(cacheKey)) {
this.metrics.cacheHits++;
return this.cache.cycles.get(cacheKey);
}
let result;
switch (algorithm) {
case 'dfs':
result = await this.detectCyclesDFS(root, options);
break;
case 'tarjan':
result = await this.detectCyclesTarjan(root, options);
break;
default:
throw new Error(`Неизвестный алгоритм обнаружения циклов: ${algorithm}`);
}
if (this.config.enableCaching) {
this.cache.cycles.set(cacheKey, result);
this.metrics.cacheMisses++;
}
this.updateMetrics(`detectCycles-${algorithm}`, Date.now() - startTime);
this.emitEvent({
type: 'algorithm-complete',
algorithm: `detectCycles-${algorithm}`,
result,
timestamp: Date.now()
});
return result;
}
catch (error) {
this.emitEvent({
type: 'algorithm-error',
algorithm: `detectCycles-${algorithm}`,
error: error,
timestamp: Date.now()
});
throw error;
}
}
async hasCycle(root, options = {}) {
const result = await this.detectCycles(root, { ...options, findAll: false });
return result.totalCycles > 0;
}
async findCycle(root, options = {}) {
const result = await this.detectCycles(root, { ...options, findAll: false });
return result.cycles.length > 0 ? result.cycles[0] : null;
}
// ==================== Анализ компонент связности ====================
async findConnectedComponents(root, options = {}) {
const startTime = Date.now();
const algorithm = options.algorithm || 'dfs';
this.emitEvent({
type: 'algorithm-start',
algorithm: `findComponents-${algorithm}`,
timestamp: startTime
});
try {
const cacheKey = this.getCacheKey('components', root.id, '', options);
if (this.config.enableCaching && this.cache.components.has(cacheKey)) {
this.metrics.cacheHits++;
return this.cache.components.get(cacheKey);
}
let result;
switch (algorithm) {
case 'dfs':
result = await this.findComponentsDFS(root, options);
break;
case 'bfs':
result = await this.findComponentsBFS(root, options);
break;
case 'tarjan':
result = await this.findComponentsTarjan(root, options);
break;
case 'kosaraju':
result = await this.findComponentsKosaraju(root, options);
break;
default:
throw new Error(`Неизвестный алгоритм анализа компонент: ${algorithm}`);
}
if (this.config.enableCaching) {
this.cache.components.set(cacheKey, result);
this.metrics.cacheMisses++;
}
this.updateMetrics(`findComponents-${algorithm}`, Date.now() - startTime);
this.emitEvent({
type: 'algorithm-complete',
algorithm: `findComponents-${algorithm}`,
result,
timestamp: Date.now()
});
return result;
}
catch (error) {
this.emitEvent({
type: 'algorithm-error',
algorithm: `findComponents-${algorithm}`,
error: error,
timestamp: Date.now()
});
throw error;
}
}
async findStronglyConnectedComponents(root, options = {}) {
return this.findConnectedComponents(root, { ...options, stronglyConnected: true, algorithm: 'tarjan' });
}
async isConnected(root, options = {}) {
const components = await this.findConnectedComponents(root, options);
return components.totalComponents === 1;
}
// ==================== Построение представления графа ====================
async buildGraph(root, options = {}) {
const adjacencyList = new Map();
const nodes = new Map();
const edgeList = [];
const visited = new Set();
const traverse = (element, depth = 0) => {
if (depth > (options.maxDepth || this.config.defaultMaxDepth))
return;
if (visited.has(element.id))
return;
if (options.nodeFilter && !options.nodeFilter(element))
return;
visited.add(element.id);
nodes.set(element.id, element);
adjacencyList.set(element.id, new Set());
const children = element.getAllElements();
for (const child of children) {
if (child instanceof CSElement) {
// Применяем фильтр узла к дочернему элементу
if (options.nodeFilter && !options.nodeFilter(child))
continue;
// Применяем фильтр рёбер
if (options.edgeFilter && !options.edgeFilter(element, child))
continue;
adjacencyList.get(element.id).add(child.id);
const weight = options.getEdgeWeight ? options.getEdgeWeight(element, child) : 1;
edgeList.push({
from: element.id,
to: child.id,
weight: options.weighted ? weight : undefined
});
// Для неориентированного графа добавляем обратное ребро
if (!options.directed) {
if (!adjacencyList.has(child.id)) {
adjacencyList.set(child.id, new Set());
}
adjacencyList.get(child.id).add(element.id);
edgeList.push({
from: child.id,
to: element.id,
weight: options.weighted ? weight : undefined
});
}
traverse(child, depth + 1);
}
}
};
traverse(root);
return {
adjacencyList,
edgeList,
nodes,
directed: options.directed || false,
weighted: options.weighted || false
};
}
// ==================== Анализ графа ====================
async analyzeGraph(root, options = {}) {
const graphStats = await this.getGraphStatistics(root, options);
return {
graphStats
};
}
async getGraphStatistics(root, options = {}) {
const startTime = Date.now();
const cacheKey = this.getCacheKey('stats', root.id, '', options);
if (this.config.enableCaching && this.cache.statistics.has(cacheKey)) {
this.metrics.cacheHits++;
return this.cache.statistics.get(cacheKey);
}
const graph = await this.buildGraph(root, options);
const nodeCount = graph.nodes.size;
const edgeCount = graph.edgeList.length;
// Вычисляем статистики
const degrees = Array.from(graph.adjacencyList.values()).map(neighbors => neighbors.size);
const averageDegree = degrees.reduce((sum, degree) => sum + degree, 0) / nodeCount;
const maxPossibleEdges = graph.directed ? nodeCount * (nodeCount - 1) : nodeCount * (nodeCount - 1) / 2;
const density = maxPossibleEdges > 0 ? edgeCount / maxPossibleEdges : 0;
// Проверяем связность
const components = await this.findConnectedComponents(root, options);
const isConnected = components.totalComponents === 1;
// Проверяем ацикличность
const cycles = await this.detectCycles(root, options);
const isAcyclic = cycles.totalCycles === 0;
// Проверяем является ли деревом
// Для направленного графа (дерево) количество рёбер должно быть nodeCount - 1
// Для неориентированного графа также nodeCount - 1
const expectedEdges = nodeCount > 0 ? nodeCount - 1 : 0;
const actualEdges = graph.directed ? edgeCount : edgeCount / 2; // Для неориентированного делим на 2
const isTree = isConnected && isAcyclic && actualEdges === expectedEdges;
// Вычисляем диаметр (максимальное расстояние между узлами)
let diameter = 0;
const nodeIds = Array.from(graph.nodes.keys());
for (let i = 0; i < nodeIds.length && i < 50; i++) { // Ограничиваем для производительности
for (let j = i + 1; j < nodeIds.length && j < 50; j++) {
const from = graph.nodes.get(nodeIds[i]);
const to = graph.nodes.get(nodeIds[j]);
const distance = await this.getDistance(from, to, options);
if (distance > diameter && distance < Infinity) {
diameter = distance;
}
}
}
// Вычисляем максимальную глубину
let maxDepth = 0;
const visited = new Set();
const calculateDepth = (nodeId, depth = 0) => {
if (visited.has(nodeId))
return;
visited.add(nodeId);
maxDepth = Math.max(maxDepth, depth);
const neighbors = graph.adjacencyList.get(nodeId) || new Set();
for (const neighborId of neighbors) {
calculateDepth(neighborId, depth + 1);
}
};
calculateDepth(root.id);
const stats = {
nodeCount,
edgeCount,
maxDepth,
averageDegree,
density,
isConnected,
isAcyclic,
isTree,
diameter
};
if (this.config.enableCaching) {
this.cache.statistics.set(cacheKey, stats);
this.metrics.cacheMisses++;
}
this.updateMetrics('getGraphStatistics', Date.now() - startTime);
return stats;
}
// ==================== Утилиты ====================
async getDistance(from, to, options = {}) {
const path = await this.findShortestPath(from, to, options);
return path ? path.steps : Infinity;
}
getNeighbors(node, options = {}) {
const neighbors = [];
// Добавляем дочерние элементы (исходящие рёбра)
node.getAllElements().forEach(child => {
if (child instanceof CSElement) {
if (!options.nodeFilter || options.nodeFilter(child)) {
if (!options.edgeFilter || options.edgeFilter(node, child)) {
neighbors.push(child);
}
}
}
});
// Для неориентированного графа добавляем владельцев
if (!options.directed) {
node.getAllOwners().forEach(owner => {
if (owner instanceof CSElement) {
if (!options.nodeFilter || options.nodeFilter(owner)) {
if (!options.edgeFilter || options.edgeFilter(owner, node)) {
neighbors.push(owner);
}
}
}
});
}
return neighbors;
}
getDegree(node, options = {}) {
return this.getNeighbors(node, options).length;
}
async topologicalSort(root, options = {}) {
const graph = await this.buildGraph(root, { ...options, directed: true });
const inDegree = new Map();
const result = [];
const queue = [];
// Инициализируем входящие степени
for (const nodeId of graph.nodes.keys()) {
inDegree.set(nodeId, 0);
}
// Вычисляем входящие степени
for (const edge of graph.edgeList) {
inDegree.set(edge.to, (inDegree.get(edge.to) || 0) + 1);
}
// Добавляем узлы с нулевой входящей степенью
for (const [nodeId, degree] of inDegree) {
if (degree === 0) {
queue.push(nodeId);
}
}
// Алгоритм Кана
while (queue.length > 0) {
const current = queue.shift();
result.push(current);
const neighbors = graph.adjacencyList.get(current) || new Set();
for (const neighbor of neighbors) {
const newDegree = inDegree.get(neighbor) - 1;
inDegree.set(neighbor, newDegree);
if (newDegree === 0) {
queue.push(neighbor);
}
}
}
// Проверяем на циклы
if (result.length !== graph.nodes.size) {
throw new Error('Граф содержит циклы - топологическая сортировка невозможна');
}
return result;
}
async minimumSpanningTree(root, options = {}) {
const graph = await this.buildGraph(root, { ...options, weighted: true });
// Алгоритм Краскала
const edges = graph.edgeList
.filter(edge => edge.weight !== undefined)
.sort((a, b) => (a.weight || 0) - (b.weight || 0));
const parent = new Map();
const rank = new Map();
// Инициализация Union-Find
for (const nodeId of graph.nodes.keys()) {
parent.set(nodeId, nodeId);
rank.set(nodeId, 0);
}
const find = (x) => {
if (parent.get(x) !== x) {
parent.set(x, find(parent.get(x)));
}
return parent.get(x);
};
const union = (x, y) => {
const rootX = find(x);
const rootY = find(y);
if (rootX === rootY)
return false;
const rankX = rank.get(rootX);
const rankY = rank.get(rootY);
if (rankX < rankY) {
parent.set(rootX, rootY);
}
else if (rankX > rankY) {
parent.set(rootY, rootX);
}
else {
parent.set(rootY, rootX);
rank.set(rootX, rankX + 1);
}
return true;
};
const mstEdges = [];
const mstAdjacencyList = new Map();
// Инициализируем список смежности
for (const nodeId of graph.nodes.keys()) {
mstAdjacencyList.set(nodeId, new Set());
}
// Строим MST
for (const edge of edges) {
if (union(edge.from, edge.to)) {
mstEdges.push(edge);
mstAdjacencyList.get(edge.from).add(edge.to);
mstAdjacencyList.get(edge.to).add(edge.from);
if (mstEdges.length === graph.nodes.size - 1) {
break;
}
}
}
return {
adjacencyList: mstAdjacencyList,
edgeList: mstEdges,
nodes: graph.nodes,
directed: false,
weighted: true
};
}
async calculateCentrality(root, options = {}) {
const graph = await this.buildGraph(root, options);
const centrality = new Map();
// Вычисляем центральность по степени (degree centrality)
for (const [nodeId, neighbors] of graph.adjacencyList) {
const degree = neighbors.size;
const normalizedCentrality = graph.nodes.size > 1 ? degree / (graph.nodes.size - 1) : 0;
centrality.set(nodeId, normalizedCentrality);
}
return centrality;
}
// ==================== Приватные методы алгоритмов ====================
async findPathBFS(from, to, options) {
const startTime = Date.now();
const queue = [{ node: from, path: [from.id] }];
const visited = new Set();
let visitedNodes = 0;
let exploredEdges = 0;
while (queue.length > 0) {
const { node, path } = queue.shift();
if (visited.has(node.id))
continue;
visited.add(node.id);
visitedNodes++;
if (node.id === to.id) {
return {
path,
cost: this.calculatePathCost(path, options),
steps: path.length - 1,
executionTime: Date.now() - startTime,
algorithm: 'bfs',
metadata: {
visitedNodes,
exploredEdges,
isOptimal: true
}
};
}
if (path.length > (options.maxPathLength || this.config.defaultMaxDepth))
continue;
const neighbors = this.getNeighbors(node, options);
for (const neighbor of neighbors) {
exploredEdges++;
if (!visited.has(neighbor.id) && !path.includes(neighbor.id)) {
queue.push({
node: neighbor,
path: [...path, neighbor.id]
});
}
}
}
return null;
}
async findPathDFS(from, to, options) {
const startTime = Date.now();
const visited = new Set();
let visitedNodes = 0;
let exploredEdges = 0;
const dfs = (current, target, path, depth) => {
if (depth > (options.maxPathLength || this.config.defaultMaxDepth))
return null;
if (visited.has(current.id))
return null;
visited.add(current.id);
visitedNodes++;
path.push(current.id);
if (current.id === target.id) {
return [...path];
}
const neighbors = this.getNeighbors(current, options);
for (const neighbor of neighbors) {
exploredEdges++;
if (!visited.has(neighbor.id)) {
const result = dfs(neighbor, target, path, depth + 1);
if (result)
return result;
}
}
path.pop();
visited.delete(current.id);
return null;
};
const path = dfs(from, to, [], 0);
if (path) {
return {
path,
cost: this.calculatePathCost(path, options),
steps: path.length - 1,
executionTime: Date.now() - startTime,
algorithm: 'dfs',
metadata: {
visitedNodes,
exploredEdges,
isOptimal: false
}
};
}
return null;
}
async findPathDijkstra(from, to, options) {
const startTime = Date.now();
const distances = new Map();
const previous = new Map();
const unvisited = new Set();
let visitedNodes = 0;
let exploredEdges = 0;
// Инициализация
const graph = await this.buildGraph(from, options);
for (const nodeId of graph.nodes.keys()) {
distances.set(nodeId, Infinity);
previous.set(nodeId, null);
unvisited.add(nodeId);
}
distances.set(from.id, 0);
while (unvisited.size > 0) {
// Находим узел с минимальным расстоянием
let current = null;
let minDistance = Infinity;
for (const nodeId of unvisited) {
const distance = distances.get(nodeId);
if (distance < minDistance) {
minDistance = distance;
current = nodeId;
}
}
if (!current || minDistance === Infinity)
break;
unvisited.delete(current);
visitedNodes++;
if (current === to.id)
break;
const currentNode = graph.nodes.get(current);
const neighbors = this.getNeighbors(currentNode, options);
for (const neighbor of neighbors) {
exploredEdges++;
if (unvisited.has(neighbor.id)) {
const weight = options.getEdgeWeight ? options.getEdgeWeight(currentNode, neighbor) : 1;
const alt = distances.get(current) + weight;
if (alt < distances.get(neighbor.id)) {
distances.set(neighbor.id, alt);
previous.set(neighbor.id, current);
}
}
}
}
// Восстанавливаем путь
if (distances.get(to.id) === Infinity)
return null;
const path = [];
let current = to.id;
while (current !== null) {
path.unshift(current);
current = previous.get(current);
}
return {
path,
cost: distances.get(to.id),
steps: path.length - 1,
executionTime: Date.now() - startTime,
algorithm: 'dijkstra',
metadata: {
visitedNodes,
exploredEdges,
isOptimal: true
}
};
}
async findPathAStar(from, to, options) {
if (!options.heuristic) {
// Fallback на Dijkstra если нет эвристики
return this.findPathDijkstra(from, to, options);
}
const startTime = Date.now();
const openSet = new Set([from.id]);
const gScore = new Map();
const fScore = new Map();
const cameFrom = new Map();
let visitedNodes = 0;
let exploredEdges = 0;
const graph = await this.buildGraph(from, options);
// Инициализация
for (const nodeId of graph.nodes.keys()) {
gScore.set(nodeId, Infinity);
fScore.set(nodeId, Infinity);
}
gScore.set(from.id, 0);
fScore.set(from.id, options.heuristic(from, to));
while (openSet.size > 0) {
// Находим узел с минимальным fScore
let current = null;
let minFScore = Infinity;
for (const nodeId of openSet) {
const score = fScore.get(nodeId);
if (score < minFScore) {
minFScore = score;
current = nodeId;
}
}
if (!current)
break;
visitedNodes++;
if (current === to.id) {
// Восстанавливаем путь
const path = [];
let node = current;
while (node) {
path.unshift(node);
node = cameFrom.get(node);
}
return {
path,
cost: gScore.get(to.id),
steps: path.length - 1,
executionTime: Date.now() - startTime,
algorithm: 'astar',
metadata: {
visitedNodes,
exploredEdges,
isOptimal: true
}
};
}
openSet.delete(current);
const currentNode = graph.nodes.get(current);
const neighbors = this.getNeighbors(currentNode, options);
for (const neighbor of neighbors) {
exploredEdges++;
const weight = options.getEdgeWeight ? options.getEdgeWeight(currentNode, neighbor) : 1;
const tentativeGScore = gScore.get(current) + weight;
if (tentativeGScore < gScore.get(neighbor.id)) {
cameFrom.set(neighbor.id, current);
gScore.set(neighbor.id, tentativeGScore);
fScore.set(neighbor.id, tentativeGScore + options.heuristic(currentNode, neighbor));
if (!openSet.has(neighbor.id)) {
openSet.add(neighbor.id);
}
}
}
}
return null;
}
async detectCyclesDFS(root, options) {
const startTime = Date.now();
const cycles = [];
const visited = new Set();
let visitedNodes = 0;
let maxCycleLength = 0;
let minCycleLength = Infinity;
let hasSelfLoops = false;
// Специальный подход: ищем только "настоящие" циклы
// Цикл возникает когда элемент добавляется как ребенок своего потомка
const findActualCycles = (node, ancestors = new Set()) => {
if (visited.has(node.id))
return;
visited.add(node.id);
visitedNodes++;
if (this.config.enableDebug) {
console.log(`Visiting node: ${node.name || node.id}, ancestors: [${Array.from(ancestors).map(id => {
const elem = CSElement.getElementById(id);
return elem?.name || id;
}).join(', ')}]`);
}
// Получаем дочерние элементы
const children = node.getAllElements().filter(child => child instanceof CSElement);
if (this.config.enableDebug) {
console.log(`Children of ${node.name || node.id}: [${children.map(c => c.name || c.id).join(', ')}]`);
}
for (const child of children) {
// Проверяем на петли (узел ссылается сам на себя)
if (child.id === node.id) {
hasSelfLoops = true;
cycles.push([node.id]);
if (this.config.enableDebug) {
console.log(`Found self-loop: ${node.name || node.id}`);
}
continue;
}
// Проверяем, не является ли ребенок предком текущего узла
if (ancestors.has(child.id)) {
// Найден цикл! Ребенок является предком
const ancestorsList = Array.from(ancestors);
const cycleStart = ancestorsList.indexOf(child.id);
if (cycleStart !== -1) {
const cycle = ancestorsList.slice(cycleStart).concat([node.id, child.id]);
const cycleLength = cycle.length - 1;
if (this.config.enableDebug) {
console.log(`Found cycle: [${cycle.map(id => {
const elem = CSElement.getElementById(id);
return elem?.name || id;
}).join(' -> ')}]`);
}
if (!options.minCycleLength || cycleLength >= options.minCycleLength) {
cycles.push(cycle);
maxCycleLength = Math.max(maxCycleLength, cycleLength);
minCycleLength = Math.min(minCycleLength, cycleLength);
}
}
}
else {
// Рекурсивно обходим детей, добавляя текущий узел в предки
const newAncestors = new Set([...ancestors, node.id]);
findActualCycles(child, newAncestors);
}
}
};
// Используем новый алгоритм поиска циклов
visited.clear(); // Сбрасываем visited для нового алгоритма
findActualCycles(root);
return {
cycles,
totalCycles: cycles.length,
executionTime: Date.now() - startTime,
algorithm: 'dfs',
metadata: {
visitedNodes,
maxCycleLength: maxCycleLength === 0 ? 0 : maxCycleLength,
minCycleLength: minCycleLength === Infinity ? 0 : minCycleLength,
hasSelfLoops
}
};
}
async detectCyclesTarjan(root, options) {
const startTime = Date.now();
const componentOptions = {
algorithm: 'tarjan',
stronglyConnected: true,
maxDepth: options.maxDepth,
nodeFilter: options.nodeFilter,
edgeFilter: options.edgeFilter,
getEdgeWeight: options.getEdgeWeight,
timeout: options.timeout,
weighted: options.weighted
};
const sccResult = await this.findComponentsTarjan(root, componentOptions);
const cycles = [];
let hasSelfLoops = false;
for (const component of sccResult.components) {
if (component.length > 1) {
// Любая компонента размером > 1 является циклом
cycles.push(component);
}
else if (component.length === 1) {
// Проверяем на петлю
const nodeId = component[0];
const node = CSElement.getElementById(nodeId);
if (node) {
const neighbors = this.getNeighbors(node, options);
if (neighbors.some(n => n.id === nodeId)) {
cycles.push(component);
hasSelfLoops = true;
}
}
}
}
const filteredCycles = cycles.filter(c => !options.minCycleLength || c.length >= options.minCycleLength);
const cycleLengths = filteredCycles.map(c => c.length);
return {
cycles: filteredCycles,
totalCycles: filteredCycles.length,
executionTime: Date.now() - startTime,
algorithm: 'tarjan',
metadata: {
visitedNodes: sccResult.metadata.largestComponentSize, // Приблизительно
maxCycleLength: Math.max(...cycleLengths, 0),
minCycleLength: Math.min(...cycleLengths, Infinity),
hasSelfLoops
}
};
}
async findComponentsDFS(root, options) {
const startTime = Date.now();
const components = [];
const visited = new Set();
const graph = await this.buildGraph(root, options);
const dfs = (nodeId, component) => {
if (visited.has(nodeId))
return;
visited.add(nodeId);
component.push(nodeId);
const neighbors = graph.adjacencyList.get(nodeId) || new Set();
for (const neighborId of neighbors) {
if (!visited.has(neighborId)) {
dfs(neighborId, component);
}
}
};
for (const nodeId of graph.nodes.keys()) {
if (!visited.has(nodeId)) {
const component = [];
dfs(nodeId, component);
if (!options.minComponentSize || component.length >= options.minComponentSize) {
components.push(component);
}
}
}
const componentSizes = components.map(c => c.length);
const largestComponentSize = Math.max(...componentSizes, 0);
const smallestComponentSize = Math.min(...componentSizes, 0);
const averageComponentSize = componentSizes.length > 0
? componentSizes.reduce((sum, size) => sum + size, 0) / componentSizes.length
: 0;
const isolatedNodes = components.filter(c => c.length === 1).length;
return {
components,
totalComponents: components.length,
executionTime: Date.now() - startTime,
algorithm: 'dfs',
metadata: {
largestComponentSize,
smallestComponentSize,
averageComponentSize,
isolatedNodes
}
};
}
async findComponentsBFS(root, options) {
const startTime = Date.now();
const components = [];
const visited = new Set();
const graph = await this.buildGraph(root, options);
const bfs = (startNodeId) => {
const component = [];
const queue = [startNodeId];
while (queue.length > 0) {
const nodeId = queue.shift();
if (visited.has(nodeId))
continue;
visited.add(nodeId);
component.push(nodeId);
const neighbors = graph.adjacencyList.get(nodeId) || new Set();
for (const neighborId of neighbors) {
if (!visited.has(neighborId)) {
queue.push(neighborId);
}
}
}
return component;
};
for (const nodeId of graph.nodes.keys()) {
if (!visited.has(nodeId)) {
const component = bfs(nodeId);
if (!options.minComponentSize || component.length >= options.minComponentSize) {
components.push(component);
}
}
}
const componentSizes = components.map(c => c.length);
const largestComponentSize = Math.max(...componentSizes, 0);
const smallestComponentSize = Math.min(...componentSizes, 0);
const averageComponentSize = componentSizes.length > 0
? componentSizes.reduce((sum, size) => sum + size, 0) / componentSizes.length
: 0;
const isolatedNodes = components.filter(c => c.length === 1).length;
return {
components,
totalComponents: components.length,
executionTime: Date.now() - startTime,
algorithm: 'bfs',
metadata: {
largestComponentSize,
smallestComponentSize,
averageComponentSize,
isolatedNodes
}
};
}
async findComponentsTarjan(root, options) {
const startTime = Date.now();
const components = [];
const graph = await this.buildGraph(root, { ...options, directed: true });
let index = 0;
const indices = new Map();
const lowlinks = new Map();
const onStack = new Set();
const stack = [];
const strongConnect = (nodeId) => {
indices.set(nodeId, index);
lowlinks.set(nodeId, index);
index++;
stack.push(nodeId);
onStack.add(nodeId);
const neighbors = graph.adjacencyList.get(nodeId) || new Set();
for (const neighborId of neighbors) {
if (!indices.has(neighborId)) {
strongConnect(neighborId);
lowlinks.set(nodeId, Math.min(lowlinks.get(nodeId), lowlinks.get(neighborId)));
}
else if (onStack.has(neighborId)) {
lowlinks.set(nodeId, Math.min(lowlinks.get(nodeId), indices.get(neighborId)));
}
}
if (lowlinks.get(nodeId) === indices.get(nodeId)) {
const component = [];
let w;
do {
w = stack.pop();
onStack.delete(w);
component.push(w);
} while (w !== nodeId);
components.push(component);
}
};
for (const nodeId of graph.nodes.keys()) {
if (!indices.has(nodeId)) {
strongConnect(nodeId);
}
}
return {
components,
totalComponents: components.length,
executionTime: Date.now() - startTime,
algorithm: 'tarjan',
metadata: {
largestComponentSize: Math.max(...components.map(c => c.length), 0),
smallestComponentSize: Math.min(...components.map(c => c.length), Infinity),
averageComponentSize: components.length > 0 ? components.reduce((sum, c) => sum + c.length, 0) / components.length : 0,
isolatedNodes: components.filter(c => c.length === 1).length,
}
};
}
async findComponentsKosaraju(root, options) {
const startTime = Date.now();
const graph = await this.buildGraph(root, { ...options, directed: true });
// 1. Первый проход DFS для получения порядка обхода
const visited = new Set();
const finishOrder = [];
const dfs1 = (nodeId) => {
if (visited.has(nodeId))
return;
visited.add(nodeId);
const neighbors = graph.adjacencyList.get(nodeId) || new Set();
for (const neighborId of neighbors) {
dfs1(neighborId);
}
finishOrder.push(nodeId);
};
for (const nodeId of graph.nodes.keys()) {
if (!visited.has(nodeId)) {
dfs1(nodeId);
}
}
// 2. Транспонируем граф
const reversedGraph = new Map();
for (const nodeId of graph.nodes.keys()) {
reversedGraph.set(nodeId, new Set());
}
for (const [nodeId, neighbors] of graph.adjacencyList.entries()) {
for (const neighborId of neighbors) {
reversedGraph.get(neighborId).add(nodeId);
}
}
// 3. Второй проход DFS на транспонированном графе
const components = [];
visited.clear();
const dfs2 = (nodeId, component) => {
if (visited.has(nodeId))
return;
visited.add(nodeId);
component.push(nodeId);
const neighbors = reversedGraph.get(nodeId) || new Set();
for (const neighborId of neighbors) {
dfs2(neighborId, component);
}
};
for (let i = finishOrder.length - 1; i >= 0; i--) {
const nodeId = finishOrder[i];
if (!visited.has(nodeId)) {
const component = [];
dfs2(nodeId, component);
components.push(component);
}
}
return {
components,
totalComponents: components.length,
executionTime: Date.now() - startTime,
algorithm: 'kosaraju',
metadata: {
largestComponentSize: Math.max(...components.map(c => c.length), 0),
smallestComponentSize: Math.min(...components.map(c => c.length), Infinity),
averageComponentSize: components.length > 0 ? components.reduce((sum, c) => sum + c.length, 0) / components.length : 0,
isolatedNodes: components.filter(c => c.length === 1).length,
}
};
}
// ==================== Вспомогательные методы ====================
calculatePathCost(path, options) {
if (path.length < 2)
return 0;
let totalCost = 0;
for (let i = 0; i < path.length - 1; i++) {
const fromId = path[i];
const toId = path[i + 1];
if (options.getEdgeWeight) {
const fromNode = CSElement.getElementById(fromId);
const toNode = CSElement.getElementById(toId);
if (fromNode && toNode) {
totalCost += options.getEdgeWeight(fromNode, toNode);
}
else {
totalCost += 1;
}
}
else {
totalCost += 1;
}
}
return totalCost;
}
getCacheKey(type, nodeId1, nodeId2, options) {
const optionsStr = JSON.stringify(options);
return `${type}-${nodeId1}-${nodeId2}-${optionsStr}`;
}
emitEvent(event) {
if (this.config.enableEvents) {
this.emit('algorithm-event', event);
}
}
updateMetrics(algorithm, executionTime) {
this.metrics.totalExecutions++;
// Убеждаемся, что время выполнения больше 0 для тестов
this.metrics.lastExecutionTime = Math.max(executionTime, 1);
const currentAvg = this.metrics.averageExecutionTime.get(algorithm) || 0;
const newAvg = (currentAvg + this.metrics.lastExecutionTime) / 2;
this.metrics.averageExecutionTime.set(algorithm, newAvg);
// Обновляем использование памяти (приблизительно)
this.metrics.memoryUsage = process.memoryUsage().heapUsed;
}
// ==================== Публичные методы управления ====================
getMetrics() {
return { ...this.metrics };
}
clearCache() {
this.cache.paths.clear();
this.cache.cycles.clear();
this.cache.components.clear();
this.cache.statistics.clear();
}
configure(config) {
this.config = { ...this.config, ...config };
}
}
class ElementRegistry {
constructor() {
this.registry = new Map();
}
get totalElementsCount() {
return this.registry.size;
}
register(element) {
this.registry.set(element.id, element);
}
unregister(element) {
this.registry.delete(element.id);
}
getElementById(id) {
return this.registry.get(id) || null;
}
getElementsByName(name) {
const result = [];
this.registry.forEach(element => {
if (element.name === name) {
result.push(element);
}
});
return result;
}
getAllElements() {
return Array.from(this.registry.values());
}
clear() {
this.registry.clear();
}
}
class ServiceRegistry {
constructor() {
this._pluginManager = new PluginManager();
this._persistenceManager = new PersistenceManagerImpl();
this._historyManager = new HistoryManagerImpl();
this._reactivityManager = new ReactivityManagerImpl();
this._liveQueryManager = new LiveQueryManagerImpl();
this._graphAlgorithmsManager = new GraphAlgorithmsManagerImpl();
this._registry = new ElementRegistry();
}
init(cselementClass) {
// Решение проблемы циклической зависимости
this._pluginManager.setCSElementClass(cselementClass);
this._liveQueryManager.setCSElementClass(cselementClass);
// GraphAlgorithmsManagerImpl не имеет прямой зависимости от статического класса CSElement
}
get plugins() {
return this._pluginManager;
}
get persistence() {
return this._persistenceManager;
}
get history() {
return this._historyManager;
}
get reactivity() {
return this._reactivityManager;
}
get liveQueries() {
return this._liveQueryManager;
}
get graphAlgorithms() {
return this._graphAlgorithmsManager;
}
get registry() {
return this._registry;
}
configureHistory(config) {
this._historyManager = new HistoryManagerImpl(config);
}
configureReactivity(config) {
this._reactivityManager = new ReactivityManagerImpl(config);
}
}
const services = new ServiceRegistry();
class ElementNavigation {
/**
* Обход всех элементов в глубину
*/
static async traverseDepthFirst(root, callback, includeThis = true) {
const visited = new Set();
const traverse = async (element) => {
if (visited.has(element.id))
return;
visited.add(element.id);
if (includeThis || element.id !== root.id) {
await callback(element);
}
for (const child of element.getAllElements()) {
if (child instanceof CSElement) {
await traverse(child);
}
}
};
await traverse(root);
}
/**
* Обход всех элементов в ширину
*/
static async traverseBreadthFirst(root, callback, includeThis = true) {
const visited = new Set();
const queue = [root];
while (queue.length > 0) {
const element = queue.shift();
if (visited.has(element.id))
continue;
visited.add(element.id);
if (includeThis || element.id !== root.id) {
await callback(element);
}
element.getAllElements().forEach(child => {
if (child instanceof CSElement && !visited.has(child.id)) {
queue.push(child);
}
});
}
}
/**
* Получить путь до элемента
*/
static getPath(element) {
const path = [];
let current = element;
while (current) {
path.unshift(current.name);
current = current.mainOwner;
}
return path;
}
/**
* Получить глубину элемента
*/
static getDepth(element) {
let depth = 0;
let current = element.mainOwner;
while (current) {
depth++;
current = current.mainOwner;
}
return depth;
}
/**
* Найти все элементы по условию
*/
static async findElements(root, predicate) {
const results = [];
await this.traverseDepthFirst(root, (element) => {
if (predicate(element)) {
results.push(element);
}
});
return results;
}
/**
* Найти первый элемент по условию
*/
static async findElement(root, predicate) {
let found = null;
await this.traverseDepthFirst(root, (element) => {
if (!found && predicate(element)) {
found = element;
}
});
return found;
}
}
class ElementSerializer {
/**
* Экспорт структуры в JSON
*/
static toJSON(element, includeData = true) {
const result = {
id: element.id,
name: element.name,
index: element.index
};
if (includeData && element.data.size > 0) {
result.data = Object.fromEntries(element.data);
}
if (element.elementsCount() > 0) {
result.elements = element.getAllElements().map(child => {
if (child instanceof CSElement) {
return this.toJSON(child, includeData);
}
return null;
}).filter(Boolean);
}
return result;
}
/**
* Улучшенная сериализация с опциями
*/
static serialize(element, options = {}) {
const { includeChildren = true, includeData = true, includeOwners = false, includeMetadata = true, maxDepth = Infinity, excludeFields = [] } = options;
const result = {
id: element.id
};
if (!excludeFields.includes('name')) {
result.name = element.name;
}
if (!excludeFields.includes('index')) {
result.index = element.index;
}
if (includeData && !excludeFields.includes('data') && element.data.size > 0) {
result.data = Object.fromEntries(element.data);
}
if (includeOwners && !excludeFields.includes('owners')) {
result.owners = element.getAllOwners().map(o => o.id);
}
if (includeMetadata && !excludeFields.includes('metadata')) {
result.metadata = {
createdAt: Date.now(), // This should be stored on creation
updatedAt: Date.now(),
depth: ElementNavigation.getDepth(element),
path: ElementNavigation.getPath(element).join('/')
};
}
if (includeChildren && !excludeFields.includes('children') && maxDepth > 0) {
const children = [];
for (const child of element.getAllElements()) {
if (child instanceof CSElement) {
children.push(this.serialize(child, {
...options,
maxDepth: maxDepth - 1
}));
}
}
if (children.length > 0) {
result.children = children;
}
}
return result;
}
}
/**
* Основная реализация CSElement
* Потокобезопасный класс для работы с графовыми структурами
*/
class CSElement extends EventEmitter {
/**
* Получить количество всех элементов в системе
*/
static get totalElementsCount() {
return services.registry.totalElementsCount;
}
/**
* Получить все элементы из глобального реестра
*/
static getAllElements() {
return services.registry.getAllElements();
}
constructor(name = '', options = {}) {
super();
this._id = generateId();
this._name = name;
this._index = options.index ?? 0;
// Правильно обрабатываем данные
if (options.data instanceof Map) {
this._data = options.data;
}
else if (options.data && typeof options.data === 'object') {
this._data = new Map(Object.entries(options.data));
}
else {
this._data = new Map();
}
this._elements = new Map();
this._elementsByIndex = [];
this._owners = new Map();
this._mainOwner = null;
this._lock = new AsyncLock();
this._lastElement = null;
// Регистрируем элемент в глобальном реестре
services.registry.register(this);
// Выполняем хук afterCreate синхронно
services.plugins?.executeHooks('afterCreate', this);
}
// Геттеры для readonly свойств
get id() {
return this._id;
}
get name() {
return this._name;
}
get index() {
return this._index;
}
get data() {
return new Map(this._data);
}
get mainOwner() {
return this._mainOwner;
}
get lastElement() {
return this._lastElement;
}
/**
* Получить владельца по имени или индексу
*/
getOwner(nameOrIndex) {
if (typeof nameOrIndex === 'string') {
return this._owners.get(nameOrIndex) || null;
}
else {
const owners = Array.from(this._owners.values());
return owners[nameOrIndex] || null;
}
}
/**
* Асинхронно получить владельца
*/
async getOwnerAsync(nameOrIndex) {
return this._lock.withLock(async () => {
return this.getOwner(nameOrIndex);
});
}
/**
* Получить элемент по имени или индексу
*/
getElement(nameOrIndex) {
if (typeof nameOrIndex === 'string') {
return this._elements.get(nameOrIndex) || null;
}
else {
return this._elementsByIndex[nameOrIndex] || null;
}
}
/**
* Асинхронно получить элемент
*/
async getElementAsync(nameOrIndex) {
return this._lock.withLock(async () => {
return this.getElement(nameOrIndex);
});
}
/**
* Получить относительный индекс для владельца
*/
getRelativeIndex(owner) {
if (!(owner instanceof CSElement)) {
return -1;
}
const ownerElement = owner;
const elements = Array.from(ownerElement._elements.values());
return elements.findIndex(el => el._id === this._id);
}
/**
* Количество дочерних элементов
*/
elementsCount() {
return this._elementsByIndex.length;
}
/**
* Количество владельцев
*/
ownersCount() {
return this._owners.size;
}
/**
* Получить все дочерние элементы
*/
getAllElements() {
return Array.from(this._elementsByIndex);
}
/**
* Получить всех владельцев
*/
getAllOwners() {
return Array.from(this._owners.values());
}
/**
* Проверяет наличие дочернего элемента по имени или индексу
*/
hasElement(nameOrIndex) {
if (typeof nameOrIndex === 'string') {
return this._elements.has(nameOrIndex);
}
else {
return nameOrIndex >= 0 && nameOrIndex < this._elementsByIndex.length;
}
}
/**
* Добавить новый элемент
*/
async addElement(value, options = {}) {
const context = {
element: this,
operation: 'addElement',
args: [value, options],
metadata: {},
startTime: Date.now(),
operationId: generateId(),
middlewareStack: [],
flags: {
aborted: false,
modified: false,
inTransaction: false
}
};
const operation = async () => {
return this._lock.withLock(async () => {
const element = value instanceof CSElement ? value : new CSElement(value, options);
// Выполняем хук beforeAddElement
await services.plugins?.executeHooks('beforeAddElement', this, element);
// Устанавливаем имя если не задано
if (!element._name && options.name) {
element._name = options.name;
}
// Добавляем в коллекции
if (element._name) {
this._elements.set(element._name, element);
}
// Устанавливаем индекс
element._index = options.index ?? this._elementsByIndex.length;
// Добавляем в массив по индексу
if (element._index >= this._elementsByIndex.length) {
this._elementsByIndex.push(element);
}
else {
this._elementsByIndex.splice(element._index, 0, element);
// Обновляем индексы последующих элементов
for (let i = element._index + 1; i < this._elementsByIndex.length; i++) {
this._elementsByIndex[i]._index = i;
}
}
// Устанавливаем владельца
element._owners.set(this._id, this);
if (!element._mainOwner) {
element._mainOwner = this;
}
// Обновляем последний элемент
this._lastElement = element;
// Записываем операцию в историю
this.recordOperation('create', `Add element ${element.name || element.id}`, null, {
id: element.id,
name: element.name,
index: element.index
});
// Генерируем событие
this.emit(exports.ElementEventType.ElementAdded, element);
// Уведомляем реактивность об изменении дочерних элементов
this.notifyChildrenChange('add', element);
// Выполняем хук afterAddElement
await services.plugins?.executeHooks('afterAddElement', this, element);
// Уведомляем Live queries о создании элемента
element.notifyElementChange('create');
return element;
});
};
// Выполняем через middleware если есть менеджер плагинов
if (services.plugins) {
return services.plugins.executeMiddleware(context, operation);
}
else {
return operation();
}
}
/**
* Удалить элемент
*/
async removeElement(nameOrIndex) {
const context = {
element: this,
operation: 'removeElement',
args: [nameOrIndex],
metadata: {},
startTime: Date.now(),
operationId: generateId(),
middlewareStack: [],
flags: {
aborted: false,
modified: false,
inTransaction: false
}
};
const operation = async () => {
return this._lock.withLock(async () => {
let element = null;
if (nameOrIndex instanceof CSElement) {
element = nameOrIndex;
}
else if (typeof nameOrIndex === 'string' || typeof nameOrIndex === 'number') {
element = this.getElement(nameOrIndex);
}
else {
// nameOrIndex is ICSElement but not CSElement
element = nameOrIndex;
}
if (!element) {
return false;
}
// Выполняем хук beforeRemoveElement
await services.plugins?.executeHooks('beforeRemoveElement', this, element);
// Удаляем из коллекций
if (element._name) {
this._elements.delete(element._name);
}
// Удаляем из массива по индексу
const index = this._elementsByIndex.indexOf(element);
if (index !== -1) {
this._elementsByIndex.splice(index, 1);
// Обновляем индексы последующих элементов
for (let i = index; i < this._elementsByIndex.length; i++) {
this._elementsByIndex[i]._index = i;
}
}
// Удаляем владельца у элемента
element._owners.delete(this._id);
if (element._mainOwner === this) {
// Если это был главный владелец, назначаем нового
const newOwner = element._owners.values().next().value;
element._mainOwner = newOwner || null;
}
// Обновляем последний элемент
if (this._lastElement === element) {
this._lastElement = this._elementsByIndex[this._elementsByIndex.length - 1] || null;
}
// Записываем операцию в историю
this.recordOperation('delete', `Remove element ${element.name || element.id}`, {
id: element.id,
name: element.name,
index: element.index
}, null);
// Генерируем событие
this.emit(exports.ElementEventType.ElementRemoved, element);
// Уведомляем реактивность об изменении дочерних элементов
this.notifyChildrenChange('remove', element);
// Выполняем хук afterRemoveElement
await services.plugins?.executeHooks('afterRemoveElement', this, element);
// Уведомляем Live queries об удалении элемента
element.notifyElementChange('delete');
return true;
});
};
// Выполняем через middleware если есть менеджер плагинов
if (services.plugins) {
return services.plugins.executeMiddleware(context, operation);
}
else {
return operation();
}
}
/**
* Установить данные
*/
async setData(key, value) {
const context = {
element: this,
operation: 'setData',
args: [key, value],
metadata: {},
startTime: Date.now(),
operationId: generateId(),
middlewareStack: [],
flags: {
aborted: false,
modified: false,
inTransaction: false
}
};
const operation = async () => {
return this._lock.withLock(async () => {
// Используем аргументы из контекста (могут быть модифицированы middleware)
const [finalKey, finalValue] = context.args;
// Выполняем хук beforeSetData
await services.plugins?.executeHooks('beforeSetData', this, finalKey, finalValue);
const oldValue = this._data.get(finalKey);
this._data.set(finalKey, finalValue);
// Записываем операцию в историю
this.recordOperation('update', `Set data ${finalKey}`, oldValue, finalValue, [finalKey]);
// Уведомляем систему реактивности
this.notifyDataChange(finalKey, finalValue, oldValue);
// Очищаем кэш QueryEngine, так как данные элемента изменились
QueryEngine.clearCache();
this.emit(exports.ElementEventType.DataChanged, { key: finalKey, oldValue, newValue: finalValue });
// Выполняем хук afterSetData
await services.plugins?.executeHooks('afterSetData', this, finalKey, finalValue);
});
};
// Выполняем через middleware если есть менеджер плагинов
if (services.plugins) {
return services.plugins.executeMiddleware(context, operation);
}
else {
return operation();
}
}
/**
* Получить данные
*/
getData(key) {
return this._data.get(key);
}
/**
* Удалить данные
*/
async deleteData(key) {
const context = {
element: this,
operation: 'deleteData',
args: [key],
metadata: {},
startTime: Date.now(),
operationId: generateId(),
middlewareStack: [],
flags: {
aborted: false,
modified: false,
inTransaction: false
}
};
const operation = async () => {
return this._lock.withLock(async () => {
// Выполняем хук beforeDeleteData
await services.plugins?.executeHooks('beforeDeleteData', this, key);
const hadKey = this._data.has(key);
if (hadKey) {
const oldValue = this._data.get(key);
this._data.delete(key);
// Записываем операцию в историю
this.recordOperation('delete', `Delete data ${key}`, oldValue, undefined, [key]);
// Уведомляем систему реактивности
this.notifyDataChange(key, undefined, oldValue);
this.emit(exports.ElementEventType.DataChanged, { key, oldValue, newValue: undefined });
}
// Выполняем хук afterDeleteData
await services.plugins?.executeHooks('afterDeleteData', this, key);
return hadKey;
});
};
// Выполняем через middleware если есть менеджер плагинов
if (services.plugins) {
return services.plugins.executeMiddleware(context, operation);
}
else {
return operation();
}
}
/**
* Войти в блокировку
*/
async enterLock() {
await this._lock.acquire();
this.emit(exports.ElementEventType.Locked);
}
/**
* Выйти из блокировки
*/
async leaveLock() {
this._lock.release();
this.emit(exports.ElementEventType.Unlocked);
}
/**
* Проверить заблокирован ли элемент
*/
isLocked() {
return this._lock.isLocked();
}
/**
* Добавить себя как дочерний элемент к другому элементу
*/
async addTo(owner, options = {}) {
return owner.addElement(this, options);
}
/**
* Удалить себя из владельца
*/
async removeFrom(owner) {
return owner.removeElement(this);
}
// ===== Builder API =====
/**
* Установить имя элемента (fluent API)
*/
setName(name) {
this._name = name;
return this;
}
/**
* Установить индекс элемента (fluent API)
*/
setIndex(index) {
this._index = index;
return this;
}
/**
* Добавить данные (fluent API)
*/
withData(key, value) {
this._data.set(key, value);
return this;
}
/**
* Добавить несколько данных сразу (fluent API)
*/
withDataObject(data) {
Object.entries(data).forEach(([key, value]) => {
this._data.set(key, value);
});
return this;
}
/**
* Добавить дочерний элемент (fluent API)
*/
withChild(nameOrElement, options) {
// Используем синхронную версию для fluent API
const element = nameOrElement instanceof CSElement ? nameOrElement : new CSElement(nameOrElement, options);
// Устанавливаем имя если не задано
if (!element._name && options?.name) {
element._name = options.name;
}
// Добавляем в коллекции
if (element._name) {
this._elements.set(element._name, element);
}
// Устанавливаем индекс
element._index = options?.index ?? this._elementsByIndex.length;
// Добавляем в массив по индексу
if (element._index >= this._elementsByIndex.length) {
this._elementsByIndex.push(element);
}
else {
this._elementsByIndex.splice(element._index, 0, element);
// Обновляем индексы последующих элементов
for (let i = element._index + 1; i < this._elementsByIndex.length; i++) {
this._elementsByIndex[i]._index = i;
}
}
// Устанавливаем владельца
element._owners.set(this._id, this);
if (!element._mainOwner) {
element._mainOwner = this;
}
// Обновляем последний элемент
this._lastElement = element;
// Генерируем событие
this.emit(exports.ElementEventType.ElementAdded, element);
return this;
}
/**
* Добавить несколько дочерних элементов (fluent API)
*/
withChildren(...names) {
names.forEach(name => {
this.addElement(name).catch(err => {
console.error('Failed to add child:', err);
});
});
return this;
}
// ===== Статические методы для создания =====
/**
* Создать новый элемент с fluent API
*/
static create(name, options) {
return new CSElement(name || '', options);
}
// ===== Глобальный поиск и навигация =====
/**
* Найти элемент по ID глобально
*/
static getElementById(id) {
return services.registry.getElementById(id);
}
/**
* Найти все элементы по имени глобально
*/
static getElementsByName(name) {
return services.registry.getElementsByName(name);
}
// ===== Система плагинов =====
/**
* Установить плагин
*/
static use(plugin, options) {
services.plugins.use(plugin, options || undefined);
}
/**
* Удалить плагин
*/
static uninstall(pluginName) {
return services.plugins?.uninstall(pluginName) || false;
}
/**
* Получить менеджер плагинов
*/
static get plugins() {
return services.plugins;
}
/**
* Проверить, установлен ли плагин
*/
static hasPlugin(name) {
return services.plugins?.hasPlugin(name) || false;
}
/**
* Получить информацию о плагинах
*/
static getPluginInfo() {
return services.plugins?.getPluginInfo() || [];
}
// ===== Система персистентности =====
/**
* Получить менеджер персистентности
*/
static get persistence() {
return services.persistence;
}
/**
* Сохранить элемент в хранилище
*/
async save(adapterName, options) {
return services.persistence.save(this.id, adapterName, options);
}
/**
* Загрузить элемент из хранилища и восстановить его состояние
*/
static async load(elementId, adapterName, options) {
const result = await services.persistence.load(elementId, adapterName, options);
if (result.success && result.data) {
// Десериализуем элемент
const element = CSElement.deserialize(result.data);
return element;
}
return null;
}
/**
* Удалить элемент из хранилища
*/
async deleteFromStorage(adapterName, options) {
return services.persistence.delete(this.id, adapterName, options);
}
/**
* Найти все элементы по условию
*/
async findElements(predicate) {
return ElementNavigation.findElements(this, predicate);
}
/**
* Найти первый элемент по условию
*/
async findElement(predicate) {
return ElementNavigation.findElement(this, predicate);
}
/**
* Обход всех элементов в глубину
*/
async traverseDepthFirst(callback, includeThis = true) {
return ElementNavigation.traverseDepthFirst(this, callback, includeThis);
}
/**
* Обход всех элементов в ширину
*/
async traverseBreadthFirst(callback, includeThis = true) {
return ElementNavigation.traverseBreadthFirst(this, callback, includeThis);
}
/**
* Получить путь до элемента
*/
getPath() {
return ElementNavigation.getPath(this);
}
/**
* Получить глубину элемента
*/
getDepth() {
return ElementNavigation.getDepth(this);
}
/**
* Экспорт структуры в JSON
*/
toJSON(includeData = true) {
return ElementSerializer.toJSON(this, includeData);
}
/**
* Клонировать элемент со всей структурой
*/
async clone(deep = true) {
const cloned = new CSElement(this.name, {
data: new Map(this._data),
index: this.index
});
if (deep) {
for (const child of this.getAllElements()) {
if (child instanceof CSElement) {
const clonedChild = await child.clone(deep);
await cloned.addElement(clonedChild);
}
}
}
return cloned;
}
/**
* Удалить элемент из глобального реестра (вызывается при уничтожении)
*/
destroy() {
// Выполняем хук beforeDestroy синхронно
services.plugins?.executeHooks('beforeDestroy', this);
// Удаляем из всех владельцев
this.getAllOwners().forEach(owner => {
if (owner instanceof CSElement) {
owner.removeElement(this);
}
});
// Рекурсивно удаляем все дочерние элементы
this.getAllElements().forEach(child => {
if (child instanceof CSElement) {
child.destroy();
}
});
// Удаляем из глобального реестра
services.registry.unregister(this);
// Удаляем все слушатели событий
this.removeAllListeners();
// Выполняем хук afterDestroy синхронно
services.plugins?.executeHooks('afterDestroy', this);
}
/**
* Получить статистику элемента
*/
getStats() {
let totalElements = 0;
let maxDepth = 0;
let totalData = 0;
const visited = new Set();
const traverse = (element, currentDepth = 0) => {
if (visited.has(element.id))
return;
visited.add(element.id);
totalElements++;
maxDepth = Math.max(maxDepth, currentDepth);
totalData += element.data.size;
element.getAllElements().forEach(child => {
if (child instanceof CSElement) {
traverse(child, currentDepth + 1);
}
});
};
traverse(this, 0);
return { totalElements, maxDepth, totalData };
}
/**
* Проверить является ли элемент предком
*/
isAncestorOf(element) {
let current = element;
while (current) {
if (current === this)
return true;
current = current.mainOwner;
}
return false;
}
/**
* Проверить является ли элемент потомком
*/
isDescendantOf(element) {
return element.isAncestorOf(this);
}
// ===== Расширенная сериализация =====
/**
* Улучшенная сериализация с опциями
*/
serialize(options = {}) {
return ElementSerializer.serialize(this, options);
}
/**
* Десериализация из объекта
*/
static deserialize(data) {
const element = new CSElement(data.name || '', {
index: data.index
});
// Восстанавливаем оригинальный ID если он есть
if (data.id) {
// Удаляем из глобального реестра текущий ID
services.registry.unregister(element);
// Устанавливаем новый ID
element._id = data.id;
// Регистрируем с новым ID
services.registry.register(element);
}
// Восстанавливаем данные
if (data.data) {
Object.entries(data.data).forEach(([key, value]) => {
element._data.set(key, value);
});
}
// Рекурсивно восстанавливаем дочерние элементы
if (data.children) {
data.children.forEach(childData => {
const child = CSElement.deserialize(childData);
element.withChild(child);
});
}
return element;
}
// ===== Валидация =====
/**
* Валидирует элемент используя глобальный валидатор
*/
validate(elementType = 'default') {
return defaultValidator.validate(this, elementType);
}
/**
* Валидирует элемент используя кастомный валидатор
*/
validateWith(validator, elementType = 'default') {
return validator.validate(this, elementType);
}
// ===== Система запросов =====
/**
* Поиск элементов по селектору
*/
query(selector) {
if (typeof selector === 'string') {
return QueryEngine.query(this, selector);
}
const objectSelector = { type: SelectorType.OBJECT, selector: selector };
return QueryEngine.query(this, objectSelector);
}
/**
* Поиск первого элемента по селектору
*/
queryOne(selector) {
if (typeof selector === 'string') {
return QueryEngine.queryOne(this, selector);
}
const objectSelector = { type: SelectorType.OBJECT, selector: selector };
return QueryEngine.queryOne(this, objectSelector);
}
/**
* Проверяет, соответствует ли элемент селектору
*/
matches(selector) {
if (typeof selector === 'string') {
return QueryEngine.query(this, selector).includes(this);
}
const objectSelector = { type: SelectorType.OBJECT, selector: selector };
const results = QueryEngine.query(this, objectSelector);
return results.includes(this);
}
// ===== Статические методы для работы с селекторами =====
/**
* Создает селектор для поиска
*/
static createSelector() {
return QueryEngine.createSelector();
}
/**
* Получает предопределенные селекторы
*/
static get selectors() {
return CommonSelectors;
}
// ===== Утилиты для работы с данными =====
/**
* Массовое обновление данных с валидацией
*/
async updateData(updates, validate = false) {
return this._lock.withLock(async () => {
// Сначала валидируем если требуется
if (validate) {
// Создаем временную копию для валидации
const tempElement = await this.clone(false);
Object.entries(updates).forEach(([key, value]) => {
tempElement._data.set(key, value);
});
const validationResult = tempElement.validate();
if (!validationResult.isValid) {
return validationResult;
}
}
// Применяем изменения
const oldValues = {};
Object.entries(updates).forEach(([key, value]) => {
oldValues[key] = this._data.get(key);
this._data.set(key, value);
});
// Генерируем событие
this.emit(exports.ElementEventType.DataChanged, {
updates,
oldValues,
newValues: updates
});
return null; // Валидация прошла успешно
});
}
/**
* Получает все данные как обычный объект
*/
getDataAsObject() {
return Object.fromEntries(this._data);
}
/**
* Проверяет наличие данных по ключу
*/
hasData(key) {
return this._data.has(key);
}
/**
* Получает размер данных
*/
getDataSize() {
return this._data.size;
}
/**
* Очищает все данные
*/
async clearData() {
return this._lock.withLock(async () => {
const oldData = this.getDataAsObject();
this._data.clear();
this.emit(exports.ElementEventType.DataChanged, {
cleared: true,
oldData
});
});
}
// === МЕТОДЫ РАБОТЫ С ИСТОРИЕЙ ===
/**
* Получить менеджер истории
*/
static get history() {
return services.history;
}
/**
* Настроить систему истории
*/
static configureHistory(config) {
services.configureHistory(config);
}
/**
* Отменить последнюю операцию
*/
static async undo() {
return services.history.undo();
}
/**
* Повторить отмененную операцию
*/
static async redo() {
return services.history.redo();
}
/**
* Отменить до определенной операции
*/
static async undoTo(operationId) {
return services.history.undoTo(operationId);
}
/**
* Повторить до определенной операции
*/
static async redoTo(operationId) {
return services.history.redoTo(operationId);
}
/**
* Получить состояние истории
*/
static getHistoryState() {
return services.history.getState();
}
/**
* Получить операции истории
*/
static getHistoryOperations(limit) {
return services.history.getOperations(limit);
}
/**
* Получить снимки истории
*/
static getHistorySnapshots(limit) {
return services.history.getSnapshots(limit);
}
/**
* Создать снимок текущего состояния элемента
*/
createSnapshot(description = 'Manual snapshot') {
const data = {
id: this._id,
name: this._name,
index: this._index,
data: Object.fromEntries(this._data),
elements: this._elementsByIndex.map(el => el.id),
owners: Array.from(this._owners.keys())
};
return services.history.createSnapshot(data, description);
}
/**
* Очистить историю
*/
static clearHistory() {
services.history.clear();
}
/**
* Экспорт истории
*/
static exportHistory() {
return services.history.export();
}
/**
* Импорт истории
*/
static importHistory(data) {
services.history.import(data);
}
/**
* Подписка на события истории
*/
static onHistoryEvent(event, callback) {
services.history.on(event, callback);
}
/**
* Отписка от событий истории
*/
static offHistoryEvent(event, callback) {
services.history.off(event, callback);
}
// Приватные методы для записи операций в историю
recordOperation(type, description, before, after, path) {
try {
services.history.addOperation({
type: type,
before,
after,
path,
description,
canUndo: true,
canRedo: true,
metadata: {
elementId: this._id,
elementName: this._name,
timestamp: Date.now()
}
});
}
catch (error) {
console.warn('Не удалось записать операцию в историю:', error);
}
}
// ==================== СИСТЕМА РЕАКТИВНОСТИ ====================
/**
* Получить глобальный менеджер реактивности
*/
static get reactivity() {
return services.reactivity;
}
/**
* Настроить систему реактивности
*/
static configureReactivity(config) {
services.configureReactivity(config);
}
/**
* Создать computed свойство
*/
static computed(compute, config) {
return services.reactivity.computed(compute, config);
}
/**
* Создать наблюдатель за изменениями
*/
static watch(path, callback, config) {
return services.reactivity.watch(path, callback, config);
}
/**
* Удалить наблюдатель
*/
static unwatch(watcherId) {
return services.reactivity.unwatch(watcherId);
}
/**
* Получить значение computed свойства
*/
static getComputedValue(computedId) {
return services.reactivity.getComputedValue(computedId);
}
/**
* Создать реактивную ссылку
*/
static ref(value) {
return services.reactivity.ref(value);
}
/**
* Создать реактивный объект
*/
static reactive(obj) {
return services.reactivity.reactive(obj);
}
/**
* Получить граф зависимостей
*/
static getDependencyGraph() {
return services.reactivity.getDependencyGraph();
}
/**
* Получить статистику реактивности
*/
static getReactivityStats() {
return services.reactivity.getStats();
}
/**
* Очистить все реактивные данные
*/
static clearReactivity() {
services.reactivity.clear();
}
/**
* Подписаться на события реактивности
*/
static onReactivityEvent(event, callback) {
services.reactivity.on(event, callback);
}
/**
* Отписаться от событий реактивности
*/
static offReactivityEvent(event, callback) {
services.reactivity.off(event, callback);
}
// === AUTO-DISPOSE МЕТОДЫ ===
/**
* Создать новую область видимости
*/
static createScope(parentId) {
return services.reactivity.createScope(parentId);
}
/**
* Выполнить функцию в контексте области видимости
*/
static runInScope(scopeId, fn) {
return services.reactivity.runInScope(scopeId, fn);
}
/**
* Очистить область видимости и все её дочерние элементы
*/
static disposeScope(scopeId) {
services.reactivity.disposeScope(scopeId);
}
/**
* Очистить computed и все его дочерние элементы
*/
static disposeComputed(computedId) {
services.reactivity.disposeComputed(computedId);
}
/**
* Очистить watcher и все его дочерние элементы
*/
static disposeWatcher(watcherId) {
services.reactivity.disposeWatcher(watcherId);
}
/**
* Получить текущий контекст выполнения
*/
static getCurrentContext() {
return services.reactivity.getCurrentContext();
}
/**
* Установить родителя для auto-dispose
*/
static setParent(childId, parentId, type) {
services.reactivity.setParent(childId, parentId, type);
}
/**
* Получить всех детей элемента
*/
static getChildren(parentId, type) {
return services.reactivity.getChildren(parentId, type);
}
/**
* Проверить, был ли элемент disposed
*/
static isDisposed(id, type) {
return services.reactivity.isDisposed(id, type);
}
// Методы экземпляра для работы с реактивностью
/**
* Создать computed свойство для этого элемента
*/
computed(compute, config) {
return services.reactivity.computed(compute, config);
}
/**
* Создать наблюдатель за свойством этого элемента
*/
watch(property, callback, config) {
const path = [this._id, 'data', property];
return services.reactivity.watch(path, callback, config);
}
/**
* Наблюдать за дочерними элементами
*/
watchChildren(callback, config) {
const path = [this.id, 'children'];
return services.reactivity.watch(path, callback, config);
}
/**
* Создать реактивную версию данных элемента
*/
makeReactive() {
// Создаем реактивную версию данных
const reactiveData = services.reactivity.reactive(Object.fromEntries(this._data));
// Заменяем Map на реактивный объект
this._data.clear();
for (const [key, value] of Object.entries(reactiveData)) {
this._data.set(key, value);
}
return this;
}
// Приватные методы для уведомления системы реактивности
notifyDataChange(key, newValue, oldValue) {
// Уведомляем глобальный менеджер
services.reactivity.notify([this.id, 'data', key], newValue, oldValue);
services.liveQueries.notifyDataChange(this, key, newValue, oldValue);
}
notifyChildrenChange(action, element) {
// Уведомляем об изменении коллекции дочерних элементов
const changeInfo = { action, element: element.id };
services.reactivity.notify([this.id, 'children'], changeInfo, null);
}
// ==================== LIVE QUERIES API ====================
/**
* Получить менеджер Live queries
*/
static get liveQueries() {
return services.liveQueries;
}
/**
* Получить менеджер алгоритмов работы с графами
*/
static get graphAlgorithms() {
return services.graphAlgorithms;
}
/**
* Создать live query
*/
static createLiveQuery(selector, options, config) {
return services.liveQueries.createLiveQuery(selector, options, config);
}
/**
* Получить builder для live query
*/
static queryBuilder() {
return new LiveQueryBuilderImpl(services.liveQueries);
}
/**
* Подписаться на live query
*/
static subscribeLiveQuery(queryId, callback) {
return services.liveQueries.subscribe(queryId, callback);
}
/**
* Отписаться от live query
*/
static unsubscribeLiveQuery(queryId, subscriptionId) {
return services.liveQueries.unsubscribe(queryId, subscriptionId);
}
/**
* Запустить live query
*/
static startLiveQuery(queryId) {
services.liveQueries.start(queryId);
}
/**
* Остановить live query
*/
static stopLiveQuery(queryId) {
services.liveQueries.stop(queryId);
}
/**
* Получить live query
*/
static getLiveQuery(queryId) {
return services.liveQueries.getLiveQuery(queryId);
}
/**
* Получить все live queries
*/
static getAllLiveQueries() {
return services.liveQueries.getAllLiveQueries();
}
/**
* Удалить live query
*/
static removeLiveQuery(queryId) {
return services.liveQueries.removeLiveQuery(queryId);
}
/**
* Получить статистику live query
*/
static getLiveQueryStats() {
return services.liveQueries.getStats();
}
/**
* Очистить все live query
*/
static clearLiveQueries() {
services.liveQueries.clear();
}
/**
* Обновить все live query
*/
static updateAllLiveQueries() {
services.liveQueries.updateAllQueries();
}
/**
* Подписаться на события live query
*/
static onLiveQueryEvent(callback) {
services.liveQueries.on('event', callback);
}
/**
* Отписаться от событий live query
*/
static offLiveQueryEvent(callback) {
services.liveQueries.off('event', callback);
}
// Методы экземпляра для live query
/**
* Создать live query для дочерних элементов
*/
createChildrenLiveQuery(selector = '*', options, config) {
// Модифицируем селектор для поиска только среди дочерних элементов
const childSelector = `#${this._id} > ${selector}`;
return services.liveQueries.createLiveQuery(childSelector, options || {}, config);
}
/**
* Создать live query для всех потомков
*/
createDescendantsLiveQuery(selector = '*', options, config) {
// Модифицируем селектор для поиска среди всех потомков
const descendantSelector = `#${this._id} ${selector}`;
return services.liveQueries.createLiveQuery(descendantSelector, options || {}, config);
}
/**
* Наблюдать за дочерними элементами
*/
watchChildrenLive(callback, config) {
const query = services.liveQueries.createLiveQuery('> *', { root: this }, config);
services.liveQueries.subscribe(query.id, callback);
return query;
}
/**
* Наблюдать за элементами с определенными данными
*/
watchElementsWithData(dataKey, callback, config) {
const query = services.liveQueries.createLiveQuery(`[${dataKey}]`, { root: this, deep: true }, config);
services.liveQueries.subscribe(query.id, callback);
return query;
}
/**
* Уведомить live query об изменении элемента
*/
notifyElementChange(changeType) {
// Уведомляем глобальный менеджер
services.liveQueries.notifyElementChange(this, changeType);
}
// ==================== АЛГОРИТМЫ ГРАФОВ ====================
/**
* Найти путь между узлами
*/
static async findPath(from, to, options) {
return services.graphAlgorithms.findPath(from, to, options);
}
/**
* Найти кратчайший путь
*/
static async findShortestPath(from, to, options) {
return services.graphAlgorithms.findShortestPath(from, to, options);
}
/**
* Найти все пути
*/
static async findAllPaths(from, to, options) {
return services.graphAlgorithms.findAllPaths(from, to, options);
}
/**
* Обнаружить циклы
*/
static async detectCycles(root, options) {
return services.graphAlgorithms.detectCycles(root, options);
}
/**
* Проверить наличие циклов
*/
static async hasCycle(root, options) {
return services.graphAlgorithms.hasCycle(root, options);
}
/**
* Найти цикл
*/
static async findCycle(root, options) {
return services.graphAlgorithms.findCycle(root, options);
}
/**
* Найти компоненты связности
*/
static async findConnectedComponents(root, options) {
return services.graphAlgorithms.findConnectedComponents(root, options);
}
/**
* Найти сильно связанные компоненты
*/
static async findStronglyConnectedComponents(root, options) {
return services.graphAlgorithms.findStronglyConnectedComponents(root, options);
}
/**
* Проверить связность
*/
static async isConnected(root, options) {
return services.graphAlgorithms.isConnected(root, options);
}
/**
* Построить представление графа
*/
static async buildGraph(root, options) {
return services.graphAlgorithms.buildGraph(root, options);
}
/**
* Проанализировать граф
*/
static async analyzeGraph(root, options) {
return services.graphAlgorithms.analyzeGraph(root, options);
}
/**
* Получить статистику графа
*/
static async getGraphStatistics(root, options) {
return services.graphAlgorithms.getGraphStatistics(root, options);
}
/**
* Получить расстояние между узлами
*/
static async getDistance(from, to, options) {
return services.graphAlgorithms.getDistance(from, to, options);
}
/**
* Получить соседей
*/
static getNeighbors(node, options) {
return services.graphAlgorithms.getNeighbors(node, options);
}
/**
* Получить степень вершины
*/
static getDegree(node, options) {
return services.graphAlgorithms.getDegree(node, options);
}
/**
* Топологическая сортировка
*/
static async topologicalSort(root, options) {
return services.graphAlgorithms.topologicalSort(root, options);
}
/**
* Минимальное остовное дерево
*/
static async minimumSpanningTree(root, options) {
return services.graphAlgorithms.minimumSpanningTree(root, options);
}
/**
* Рассчитать центральность
*/
static async calculateCentrality(root, options) {
return services.graphAlgorithms.calculateCentrality(root, options);
}
/**
* Получить метрики производительности
*/
static getGraphAlgorithmMetrics() {
return services.graphAlgorithms.getMetrics();
}
/**
* Очистить кэш алгоритмов
*/
static clearGraphAlgorithmCache() {
services.graphAlgorithms.clearCache();
}
/**
* Настроить алгоритмы
*/
static configureGraphAlgorithms(config) {
services.graphAlgorithms.configure(config);
}
/**
* Подписаться на события
*/
static onGraphAlgorithmEvent(callback) {
services.graphAlgorithms.on('algorithm-event', callback);
}
/**
* Отписаться от событий
*/
static offGraphAlgorithmEvent(callback) {
services.graphAlgorithms.off('algorithm-event', callback);
}
// Методы экземпляра для работы с алгоритмами
/**
* Найти путь до другого элемента
*/
async findPathTo(target, options) {
return services.graphAlgorithms.findPath(this, target, options);
}
/**
* Найти кратчайший путь до другого элемента
*/
async findShortestPathTo(target, options) {
return services.graphAlgorithms.findShortestPath(this, target, options);
}
/**
* Найти все пути до другого элемента
*/
async findAllPathsTo(target, options) {
return services.graphAlgorithms.findAllPaths(this, target, options);
}
/**
* Обнаружить циклы начиная отсюда
*/
async detectCyclesFromHere(options) {
return services.graphAlgorithms.detectCycles(this, options);
}
/**
* Проверить наличие циклов отсюда
*/
async hasCycleFromHere(options) {
return services.graphAlgorithms.hasCycle(this, options);
}
/**
* Найти компоненты связности отсюда
*/
async findConnectedComponentsFromHere(options) {
return services.graphAlgorithms.findConnectedComponents(this, options);
}
/**
* Проверить связность отсюда
*/
async isConnectedFromHere(options) {
return services.graphAlgorithms.isConnected(this, options);
}
/**
* Построить граф отсюда
*/
async buildGraphFromHere(options) {
return services.graphAlgorithms.buildGraph(this, options);
}
/**
* Проанализировать граф отсюда
*/
async analyzeGraphFromHere(options) {
return services.graphAlgorithms.analyzeGraph(this, options);
}
/**
* Получить статистику графа отсюда
*/
async getGraphStatisticsFromHere(options) {
return services.graphAlgorithms.getGraphStatistics(this, options);
}
/**
* Получить расстояние до другого элемента
*/
async getDistanceTo(target, options) {
return services.graphAlgorithms.getDistance(this, target, options);
}
/**
* Получить соседей отсюда
*/
getNeighborsFromHere(options) {
return services.graphAlgorithms.getNeighbors(this, options);
}
/**
* Получить степень отсюда
*/
getDegreeFromHere(options) {
return services.graphAlgorithms.getDegree(this, options);
}
/**
* Топологическая сортировка отсюда
*/
async topologicalSortFromHere(options) {
return services.graphAlgorithms.topologicalSort(this, options);
}
/**
* Минимальное остовное дерево отсюда
*/
async minimumSpanningTreeFromHere(options) {
return services.graphAlgorithms.minimumSpanningTree(this, options);
}
/**
* Рассчитать центральность для этого узла
*/
async calculateCentralityFromHere(options) {
return services.graphAlgorithms.calculateCentrality(this, options);
}
/**
* Преобразует элемент и его потомков в простой объект для JSPath
*/
toJSPathObject() {
const obj = {
___id: this.id, // Используем нестандартное имя, чтобы избежать конфликтов
name: this.name,
index: this.index,
data: Object.fromEntries(this.data)
};
const children = this.getAllElements();
if (children.length > 0) {
obj.children = children.map(child => child.toJSPathObject());
}
return obj;
}
}
/**
* Минимальная версия CSElement для тестирования
*/
exports.CSElement = CSElement;
exports.EventEmitter = EventEmitter;
exports.default = CSElement;
exports.generateId = generateId;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=index.minimal.umd.js.map