objecture
Version:
❂ Objecture ⏣ Object Watcher, Property Manager ⊚ Capture property changes for object, array mutator methods. ⊚ Schematize and validate object, array properties. ⊚ Browser, NodeJS compatible. ⁘ Uses Core-Plex - Event Listener Manage
1,448 lines (1,414 loc) • 98.8 kB
JavaScript
import Core from 'core-plex';
import { typedObjectLiteral as typedObjectLiteral$1, assign as assign$3, variables, typeOf as typeOf$1, impandTree, regularExpressions } from 'recourse';
const Primitives = {
'string': String,
'number': Number,
'boolean': Boolean,
'bigint': BigInt,
'undefined': undefined,
'null': null,
};
Object.values(Primitives);
const Objects = {
'object': Object,
'array': Array,
};
Object.values(Objects);
const Types = Object.assign({}, Primitives, Objects);
Object.values(Types);
[
Primitives.String, Primitives.Number, Primitives.Boolean,
Objects.Object, Objects.Array
];
var typeOf = ($data) => Object
.prototype
.toString
.call($data).slice(8, -1).toLowerCase();
function typedObjectLiteral($value) {
let _typedObjectLiteral;
const typeOfValue = typeOf($value);
if(typeOfValue === 'string') {
const value = $value.toLowerCase();
if(value === 'object') { _typedObjectLiteral = {}; }
else if(value === 'array') { _typedObjectLiteral = []; }
}
else {
if(typeOfValue === 'object') { _typedObjectLiteral = {}; }
else if(typeOfValue === 'array') { _typedObjectLiteral = []; }
}
return _typedObjectLiteral
}
var isArrayLike = ($source) => {
let isArrayLike;
const typeOfSource = typeOf($source);
if(typeOfSource === 'array') { isArrayLike = true; }
else if(
typeOfSource === 'object' &&
Number.isInteger($source.length) && $source.length >= 0
) {
iterateSourceKeys:
for(const $sourceKey of Object.keys(
Object.getOwnPropertyDescriptors($source)
)) {
if($sourceKey === 'length') { continue iterateSourceKeys }
isArrayLike = !isNaN($sourceKey);
if(!isArrayLike) { break iterateSourceKeys }
}
}
else { isArrayLike = false; }
return isArrayLike
};
function assign$2($target, ...$sources) {
if(!$target) { return $target}
iterateSources:
for(const $source of $sources) {
if(!$source) continue iterateSources
for(const [
$sourcePropertyKey, $sourcePropertyValue
] of Object.entries($source)) {
const typeOfTargetPropertyValue = typeOf($target[$sourcePropertyKey]);
const typeOfSourcePropertyValue = typeOf($sourcePropertyValue);
if(
typeOfTargetPropertyValue === 'object' &&
typeOfSourcePropertyValue === 'object'
) {
$target[$sourcePropertyKey] = assign$2($target[$sourcePropertyKey], $sourcePropertyValue);
}
else {
$target[$sourcePropertyKey] = $sourcePropertyValue;
}
}
}
return $target
}
var Options$1$1 = {
ancestors: [],
delimiter: '.',
depth: 0,
frozen: false,
maxDepth: 10,
nonenumerable: true,
path: false,
sealed: false,
type: false,
};
function getOwnPropertyDescriptor($properties, $propertyKey, $options) {
const options = Object.assign({}, Options$1$1, $options, {
ancestors: Object.assign([], $options.ancestors)
});
const propertyDescriptor = Object.getOwnPropertyDescriptor($properties, $propertyKey);
if(!options.nonenumerable && !propertyDescriptor.enumerable) { return }
if(!options.ancestors.includes($properties)) { options.ancestors.unshift($properties); }
if(options.ancestors.includes(propertyDescriptor.value)) { return }
if(options.path) {
options.path = (typeOf(options.path) === 'string') ? [options.path, $propertyKey].join(options.delimiter) : $propertyKey;
propertyDescriptor.path = options.path;
}
if(options.type) { propertyDescriptor.type = typeOf(propertyDescriptor.value); }
if(options.frozen) { propertyDescriptor.frozen = Object.isFrozen(propertyDescriptor.value); }
if(options.sealed) { propertyDescriptor.sealed = Object.isSealed(propertyDescriptor.value); }
if(['array', 'object'].includes(typeOf(propertyDescriptor.value))) {
propertyDescriptor.value = getOwnPropertyDescriptors(propertyDescriptor.value, options);
}
return propertyDescriptor
}
function getOwnPropertyDescriptors($properties, $options) {
const propertyDescriptors = {};
const options = Object.assign({}, Options$1$1, $options);
if(options.depth >= options.maxDepth) { return propertyDescriptors }
else { options.depth++; }
for(const [$propertyKey, $propertyDescriptor] of Object.entries(Object.getOwnPropertyDescriptors($properties))) {
const propertyDescriptor = getOwnPropertyDescriptor($properties, $propertyKey, options);
if(propertyDescriptor !== undefined) { propertyDescriptors[$propertyKey] = propertyDescriptor; }
}
return propertyDescriptors
}
var Options$2 = {
typeCoercion: false,
};
function defineProperty$1($target, $propertyKey, $propertyDescriptor, $options) {
const propertyDescriptor = Object.assign({}, $propertyDescriptor);
const options = Object.assign({}, Options$2, $options);
const typeOfPropertyValue = typeOf(propertyDescriptor.value);
if(['array', 'object'].includes(typeOfPropertyValue)) {
const propertyValue = isArrayLike(Object.defineProperties(
typedObjectLiteral(typeOfPropertyValue), propertyDescriptor.value
)) ? [] : {};
propertyDescriptor.value = defineProperties$1(propertyValue, propertyDescriptor.value, options);
}
else if(
options.typeCoercion &&
Object.getOwnPropertyDescriptor(propertyDescriptor, 'type') !== undefined &&
!['undefined', 'null'].includes(typeOfPropertyValue)
) {
propertyDescriptor.value = Primitives[propertyDescriptor.type](propertyDescriptor.value);
}
Object.defineProperty($target, $propertyKey, propertyDescriptor);
if($propertyDescriptor.sealed) { Object.seal($target[$propertyKey]); }
if($propertyDescriptor.frozen) { Object.freeze($target[$propertyKey]); }
return $target
}
function defineProperties$1($target, $propertyDescriptors, $options) {
const options = Object.assign({}, Options$2, $options);
for(const [
$propertyKey, $propertyDescriptor
] of Object.entries($propertyDescriptors)) {
defineProperty$1($target, $propertyKey, $propertyDescriptor, options);
}
return $target
}
var Options$1$2 = ($options) => {
const options = assign$2({
propertyDescriptors: false,
defineProperties: false,
replacers: [],
revivers: [],
}, $options);
if(options.propertyDescriptors?.type) {
options.replacers.push(function BigintReplacer($key, $value) {
if(typeOf($value) === 'bigint') { return String($value) }
else { return $value }
});
}
return options
};
function JSONMiddlewares($middlewares, $key, $value) {
let value = $value;
for(const $middleware of $middlewares) {
value = $middleware($key, $value);
}
return value
}
class LocalStorageRoute extends EventTarget {
constructor($path, $options) {
super();
if(!$path) return null
const options = Options$1$2($options);
const db = localStorage;
Object.defineProperties(this, {
'path': { value: $path },
'raw': { value: function raw() { return db.getItem(this.path) } },
'get': { value: function get() {
const { path } = this;
const raw = db.getItem(this.path);
if(['undefined', undefined].includes(raw)) { return }
const propertyDescriptors = JSON.parse(raw, JSONMiddlewares.bind(null, options.revivers));
const dataTypedObjectLiteral = typedObjectLiteral(propertyDescriptors);
const data = (options.propertyDescriptors) ? defineProperties$1(
dataTypedObjectLiteral, propertyDescriptors, options.defineProperties
) : propertyDescriptors;
this.dispatchEvent(new CustomEvent('get', { detail: { path, raw, data } }));
return data
} },
'set': { value: function set($data) {
const data = $data;
const { path } = this;
let raw = (options.propertyDescriptors) ? JSON.stringify(
getOwnPropertyDescriptors(data, options.propertyDescriptors), JSONMiddlewares.bind(null, options.replacers)
) : JSON.stringify(
data, JSONMiddlewares.bind(null, options.replacers)
);
db.setItem(this.path, raw);
this.dispatchEvent(new CustomEvent('set', { detail: { path, raw, data } }));
return
} },
'remove': { value: function remove() {
const { path } = this;
const raw = this.raw();
const data = this.get();
db.removeItem(this.path);
this.dispatchEvent(new CustomEvent('remove', { detail: { path, raw, data } }));
return
} },
});
}
}
class Verification extends EventTarget {
constructor($settings) {
super();
const settings = Object.assign({}, $settings);
Object.defineProperties(this, {
'type': { value: settings.type },
'key': { value: settings.key },
'value': { value: settings.value },
'message': { configurable: true, get() {
let message;
if(this.pass !== undefined) {
message = settings.messages[String(this.pass)](this);
Object.defineProperty(this, 'message', { value: message });
}
return message
} },
'pass': { writable: true,
set pass($pass) {
Object.defineProperty(this, 'pass', { value: $pass });
},
},
});
}
}
const Messages$1 = {
'true': ($validation) => `${$validation.valid}`,
'false': ($validation) => `${$validation.valid}`,
};
function report($format = "expand", $prevalidation) {
const prevalidation = $prevalidation || this;
const schema = prevalidation.schema;
const validations = [].concat(
prevalidation.advance, prevalidation.deadvance, prevalidation.unadvance
);
if($format === "expand") {
const _report = typedObjectLiteral$1(schema.type);
for(const $validation of validations) {
const verifications = [].concat(
$validation.advance, $validation.deadvance, $validation.unadvance
);
_report[$validation.key] = {};
for(const $verification of verifications) {
_report[$validation.key][$verification.type] = {};
if($verification.validation) {
_report[$validation.key][$verification.type] = this.report($format, $verification.validation);
}
else {
_report[$validation.key][$verification.type] = $verification;
}
}
}
return _report
}
if($format === "impand") {
if(prevalidation.valid === false) { return false }
const _report = typedObjectLiteral$1(schema.type);
for(const $validation of validations) {
const verifications = [].concat(
$validation.advance, $validation.deadvance, $validation.unadvance
);
let reportValue;
iterateVerifications:
for(const $verification of verifications) {
if($verification.type === 'type') {
if($verification.validation && $validation.valid) {
reportValue = this.report($format, $verification.validation);
}
break iterateVerifications
}
}
if(!reportValue) { reportValue = $validation.valid; }
_report[$validation.key] = reportValue;
}
return _report
}
}
class Validation extends EventTarget {
constructor($settings = {}, $schema) {
super();
const settings = Object.assign({ messages: Messages$1 }, $settings);
let valid;
const advance = [];
const deadvance = [];
const unadvance = [];
Object.defineProperties(this, {
'schema': { value: $schema },
'verificationType': { value: settings.verificationType },
'required': { value: settings.required },
'definition': { value: settings.definition },
'key': { value: settings.key },
'value': { value: settings.value },
'advance': { value: advance },
'deadvance': { value: deadvance },
'unadvance': { value: unadvance },
'valid': {
writable: true,
get valid() { return valid },
set valid($valid) { Object.defineProperty(this, 'valid', { value: $valid }); }
},
'report': { configurable: true, get() {
const _report = report.bind(this);
Object.defineProperty(this, 'report', { value: _report });
return _report
} },
});
}
}
const Messages = {
'true': ($verification) => `${$verification.pass}`,
'false': ($verification) => `${$verification.pass}`,
};
class Validator extends EventTarget {
constructor($definition = {}, $schema) {
super();
const definition = Object.freeze(
Object.assign({ messages: Messages }, $definition)
);
Object.defineProperties(this, {
'definition': { value: definition },
'schema': { value: $schema },
'type': { value: definition.type },
'messages': { value: definition.messages },
'validate': { configurable: true, get() {
function validate($key, $value, $source, $target) {
const { definition, messages, type } = this;
let verification = new Verification({
type: type,
key: $key,
value: definition.value,
messages: assign$3({}, messages, definition.messages),
});
const validation = definition.validate(...arguments);
if(typeof validation === 'object') {
verification.validation = validation;
verification.pass = validation.valid;
}
else { verification.pass = validation; }
return verification
}
const boundValidate = validate.bind(this);
Object.defineProperty(this, 'validate', {
value: boundValidate
});
return boundValidate
} },
});
}
}
class RequiredValidator extends Validator {
constructor($definition, $schema) {
super(Object.assign({}, $definition, {
type: 'required',
validate: ($key, $value, $source, $target) => {
const { requiredProperties, requiredPropertiesSize, type } = $schema;
const corequiredProperties = Object.assign({}, requiredProperties);
let corequiredPropertiesSize = requiredPropertiesSize;
Object.assign(typedObjectLiteral$1(type), $source, $target);
this.definition;
let pass;
if(!requiredPropertiesSize) { pass = true; }
else {
if(Object.hasOwn(corequiredProperties, $key)) {
delete corequiredProperties[$key];
corequiredPropertiesSize--;
}
if(corequiredPropertiesSize) {
const coschema = new Schema(corequiredProperties, {
path: $schema.path,
parent: $schema.parent,
});
const comodel = Object.assign({}, $target, $source);
const covalidation = coschema.validate(comodel);
pass = covalidation.valid;
}
}
return pass
}
}), $schema);
}
}
const { ObjectKeys, TypeKeys } = variables;
class TypeValidator extends Validator {
constructor($definition = {}, $schema) {
super(Object.assign({}, $definition, {
type: 'type',
validate: ($key, $value, $source, $target) => {
let pass;
const definition = this.definition;
let typeOfDefinitionValue = typeOf$1(definition.value);
if(typeOfDefinitionValue === 'function') {
typeOfDefinitionValue = typeOf$1(definition.value());
}
else if(definition.value instanceof Schema) {
typeOfDefinitionValue = definition.value.type;
}
else {
typeOfDefinitionValue = typeOf$1(definition.value);
}
if(TypeKeys.includes(typeOfDefinitionValue)) {
const typeOfValue = typeOf$1($value);
if(typeOfValue === 'undefined') { pass = false; }
else if(typeOfDefinitionValue === 'undefined') { pass = true; }
else if(definition.value instanceof Schema) {
const validation = definition.value.validate($value, $source);
pass = validation;
}
else { pass = (typeOfDefinitionValue === typeOfValue); }
}
else { pass = false; }
return pass
},
}), $schema);
}
}
class RangeValidator extends Validator {
constructor($definition = {}, $schema) {
super(Object.assign($definition, {
type: 'range',
validate: ($key, $value) => {
const definition = this.definition;
let pass;
if(typeof $value !== 'number') { pass = false; }
else {
const { min, max } = definition;
let validMin, validMax;
if(min !== undefined) { validMin = ($value >= min.value); }
else { validMin = true; }
if(max !== undefined) { validMax = ($value <= max.value); }
else { validMax = true; }
if(validMin && validMax) { pass = true; }
else { pass = false;}
}
return pass
}
}), $schema);
}
}
class LengthValidator extends Validator {
constructor($definition = {}, $schema) {
super(Object.assign($definition, {
type: 'length',
validate: ($key, $value) => {
const definition = this.definition;
let pass;
if(typeof $value !== 'string') { pass = false; }
else {
const { min, max } = definition;
let validMin, validMax;
if(min !== undefined) {
validMin = ($value.length >= min.value);
}
else { validMin = true; }
if(max !== undefined) {
validMax = ($value.length <= max.value);
}
else { validMax = true; }
if(validMin && validMax) { pass = true; }
else { pass = false;}
}
return pass
},
}), $schema);
}
}
class EnumValidator extends Validator {
constructor($definition = {}, $schema) {
super(Object.assign($definition, {
type: 'enum',
validate: ($key, $value) => {
const definition = this.definition;
let pass;
if(![
'string', 'number', 'boolean'
].includes(typeof $value)) { pass = false;}
else {
const enumeration = definition.value;
pass = enumeration.includes($value);
}
return pass
},
}), $schema);
}
}
class MatchValidator extends Validator {
constructor($settings = {}, $schema) {
super(Object.assign($settings, {
type: 'match',
validate: ($key, $value) => {
const definition = this.settings;
let pass;
if(![
'string', 'number', 'boolean'
].includes(typeof $value)) { pass = false;}
else {
const match = definition;
(match.value.exec($value) !== null);
}
return pass ? true : false
},
}), $schema);
}
}
var Options$1 = (...$options) => Object.assign({
required: false,
verificationType: 'all',
// verificationType: 'one',
strict: false,
properties: {
type: 'type',
value: 'value',
},
}, ...$options);
class Schema extends EventTarget {
constructor($properties = {}, $options = {}) {
super();
Object.defineProperties(this, {
'options': { value: Options$1($options) },
'type': { value: typeOf$1($properties) },
'parent': { configurable: true, get() {
const { options } = this;
const parent = (options.parent) ? options.parent : null;
Object.defineProperty(this, 'parent', { value: parent });
return parent
} },
'root': { configurable: true, get() {
let root = this;
iterateParents:
while(root) {
if([undefined, null].includes(root.parent)) { break iterateParents }
root = root.parent;
}
return root
} },
'key': { configurable: true, get() {
const { path } = this;
const key = (path) ? path.split('.').pop() : null;
Object.defineProperty(this, 'key', { value: key });
return key
} },
'path': { configurable: true, get() {
const { options } = this;
const path = (options.path)
? String(options.path)
: null;
Object.defineProperty(this, 'path', { value: path });
return path
} },
'required': { configurable: true, get() {
const required = this.options.required;
Object.defineProperty(this, 'required', { value: required });
return required
} },
'requiredProperties': { configurable: true, get() {
const requiredProperties = typedObjectLiteral$1(this.type);
for(const [$propertyKey, $propertyDefinition] of Object.entries(this.target)) {
if($propertyDefinition.required?.value === true) {
requiredProperties[$propertyKey] = $propertyDefinition;
}
}
Object.defineProperty(this, 'requiredProperties', { value: Object.freeze(requiredProperties) });
return requiredProperties
} },
'requiredPropertiesSize': { configurable: true, get() {
const requiredPropertiesSize = Object.keys(this.requiredProperties).length;
Object.defineProperty(this, 'requiredPropertiesSize', { value: requiredPropertiesSize });
return requiredPropertiesSize
} },
'verificationType': { configurable: true, get() {
const verificationType = this.options.verificationType;
Object.defineProperty(this, 'verificationType', { value: verificationType });
return verificationType
} },
'target': { configurable: true, get() {
let properties;
const type = this.type;
if(type === 'array') { properties = $properties.slice(0, 1); }
else if(type === 'object') { properties = $properties; }
const target = parseProperties(properties, this);
Object.defineProperty(this, 'target', { value: target });
return target
} },
'validate': { value: function(...$arguments) {
let { $sourceName, $source, $target } = parseValidateArguments(...$arguments);
$target = $target || typedObjectLiteral$1($source);
const { target, path, required, type, verificationType } = this;
let validation = new Validation({
required, verificationType,
definition: target,
key: $sourceName,
value: $source,
}, this);
const sourceProperties = Object.entries($source);
let sourcePropertyIndex = 0;
while(sourcePropertyIndex < sourceProperties.length) {
const [$sourceKey, $sourceValue] = sourceProperties[sourcePropertyIndex];
const propertyValidation = this.validateProperty($sourceKey, $sourceValue, $source, $target);
if(propertyValidation.valid === true) { validation.advance.push(propertyValidation); }
else if(propertyValidation.valid === false) { validation.deadvance.push(propertyValidation); }
else if(propertyValidation.valid === undefined) { validation.unadvance.push(propertyValidation );}
sourcePropertyIndex++;
}
if(validation.advance.length) { validation.valid = true; }
else if(validation.deadvance.length) { validation.valid = false; }
else if(validation.unadvance.length) { validation.valid = undefined; }
else { validation.valid = true; }
return validation
} },
'validateProperty': { value: function() {
const { $key, $value, $source, $target } = parseValidatePropertyArguments(...arguments);
const { target, path, required, schema, type, verificationType } = this;
let propertyDefinition;
if(type === 'array') { propertyDefinition = target[0]; }
else if(type === 'object') { propertyDefinition = target[$key]; }
const propertyValidation = new Validation({
required,
verificationType,
definition: propertyDefinition,
key: $key,
value: $value,
}, this);
if(propertyDefinition === undefined) {
const verification = new Verification({
type: null,
definition: null,
key: $key,
value: $value,
}, this);
verification.pass = false;
propertyValidation.unadvance.push(verification);
}
else {
iteratePropertyDefinitionValidators:
for(const [$validatorIndex, $validator] of Object.entries(propertyDefinition.validators)) {
const verification = $validator.validate($key, $value, $source, $target);
if(verification.pass === true) { propertyValidation.advance.push(verification); }
else if(verification.pass === false) { propertyValidation.deadvance.push(verification); }
else if(verification.pass === undefined) { propertyValidation.unadvance.push(verification); }
if(this.verificationType === 'one' && propertyValidation.deadvance.length) {
break iteratePropertyDefinitionValidators
}
}
}
if(propertyValidation.deadvance.length) { propertyValidation.valid = false; }
else if(propertyValidation.advance.length) { propertyValidation.valid = true; }
else if(propertyValidation.unadvance.length) { propertyValidation.valid = false; }
return propertyValidation
} },
});
}
}
function parseValidateArguments(...$arguments) {
let $sourceName, $source, $target;
if($arguments.length === 1) {
$sourceName = null; $source = $arguments.shift(); $target = null;
}
else if($arguments.length === 2) {
if(['number', 'string'].includes(typeof $arguments[0])) {
$sourceName = $arguments.shift(); $source = $arguments.shift(); $target = null;
}
else if($arguments[0] && typeof $arguments[0] === 'object') {
$sourceName = null; $source = $arguments.shift(); $target = $arguments.shift();
}
}
else if($arguments.length === 3) {
if(['number', 'string'].includes(typeof $arguments[0])) {
$sourceName = $arguments.shift(); $source = $arguments.shift(); $target = $arguments.shift();
}
}
return { $sourceName, $source, $target }
}
function parseValidatePropertyArguments(...$arguments) {
let [$key, $value, $source, $target] = $arguments;
return { $key, $value, $source, $target }
}
function parseProperties($properties, $schema) {
const properties = typedObjectLiteral$1($properties);
if(_isPropertyDefinition($properties, $schema)) { return $properties }
for(const [
$propertyKey, $propertyValue
] of Object.entries($properties)) {
let propertyDefinition = {};
typeOf$1($propertyValue);
const isPropertyDefinition = _isPropertyDefinition($propertyValue, $schema);
if(variables.TypeValues.includes($propertyValue)) {
Object.assign(propertyDefinition, { type: { value: $propertyValue } });
}
else if(variables.TypeKeys.includes($propertyValue)) {
Object.assign(propertyDefinition, { type: { value: variables.Types[$propertyValue] } });
}
else if(!isPropertyDefinition) {
const subpropertyPath = ($schema.path) ? [$schema.path, $propertyKey].join('.') : $propertyKey;
Object.assign(propertyDefinition, {
type: { type: 'type', value: new Schema($propertyValue, Object.assign({}, $schema.options, {
parent: $schema,
path: subpropertyPath
})) }
});
}
else if(isPropertyDefinition) {
for(const [$propertyValidatorName, $propertyValidator] of Object.entries($propertyValue)) {
const isValidatorDefinition = _isValidatorDefinition($propertyValidator, $schema);
if(!isValidatorDefinition) {
let propertyValidator;
if($propertyValidatorName === 'type') {
if($propertyValidator && typeof $propertyValidator === 'object') {
const subpropertyPath = ($schema.path) ? [$schema.path, $propertyKey].join('.') : $propertyKey;
propertyValidator = new Schema($propertyValidator, Object.assign({}, $schema.options, {
parent: $schema,
path: subpropertyPath,
}));
}
else {
propertyValidator = $propertyValidator;
}
}
else {
propertyValidator = $propertyValidator;
}
propertyDefinition[$propertyValidatorName] = {
type: $propertyValidatorName, value: propertyValidator
};
}
else if(isValidatorDefinition) {
propertyDefinition[$propertyValidatorName] = $propertyValidator;
}
}
}
propertyDefinition.validators = [];
properties[$propertyKey] = propertyDefinition;
const validators = new Map();
validators.set('type', Object.assign({}, {
type: 'type', validator: TypeValidator, value: propertyDefinition.type.value
}));
validators.set('required', Object.assign({}, {
type: 'required', validator: RequiredValidator, value: propertyDefinition.required?.value || false
}));
if(propertyDefinition.range) { validators.set('range', Object.assign({}, propertyDefinition.range, {
type: 'range', validator: RangeValidator
})); }
else if(propertyDefinition.min || propertyDefinition.max) { validators.set('range', Object.assign({}, {
type: 'range', min: propertyDefinition.min, max: propertyDefinition.max, validator: RangeValidator
})); }
if(propertyDefinition.length) { validators.set('length', Object.assign({}, propertyDefinition.length, {
type: 'length', validator: LengthValidator
})); }
else if(propertyDefinition.minLength || propertyDefinition.maxLength) { validators.set('length', Object.assign({}, {
type: 'length', min: propertyDefinition.minLength, max: maxLength, validator: LengthValidator
})); }
if(propertyDefinition.enum) { validators.set('enum', Object.assign({}, propertyDefinition.enum, {
type: 'enum', validator: EnumValidator
})); }
if(propertyDefinition.match) { validators.set('match', Object.assign({}, propertyDefinition.match, {
type: 'match', validator: MatchValidator
})); }
delete propertyDefinition.min;
delete propertyDefinition.max;
delete propertyDefinition.minLength;
delete propertyDefinition.maxLength;
for(const [
$validatorName, $validatorSettings
] of validators.entries()) {
const ValidatorClass = $validatorSettings.validator;
propertyDefinition[$validatorName] = $validatorSettings;
propertyDefinition.validators.push(new ValidatorClass($validatorSettings, $schema));
}
}
return properties
}
function _isPropertyDefinition($object, $schema) {
if(!$object || $object instanceof Schema) { return false }
const typeKey = $schema.options.properties.type;
return Object.hasOwn($object, typeKey)
}
function _isValidatorDefinition($object, $schema) {
if(!$object) { return false }
const valueKey = $schema.options.properties.value;
return Object.hasOwn($object, valueKey)
}
var Options = ($options) => {
const Options = assign$3({
autoload: false,
autosave: false,
localStorage: false,
path: null,
parent: null,
enableEvents: false,
enableValidation: true,
validationEvents: {
'validProperty:$key': true,
'validProperty': true,
'nonvalidProperty:$key': true,
'nonvalidProperty': true,
},
pathkey: true,
subpathError: false,
assignObject: 'set',
assignArray: 'set',
methods: {
map: {
get: {
mutatorEvents: {
'get': true,
'getProperty': true,
'getProperty:$key': true,
},
},
set: {
recursive: true,
mutatorEvents: {
'set': true,
'setProperty': true,
'setProperty:$key': true,
},
},
delete: {
mutatorEvents: {
'delete': true,
'deleteProperty': true,
'deleteProperty:$key': true,
},
},
},
array: {
concat: {
mutatorEvents: {
'concatElement:$index': true,
'concatElement': true,
'concat': true,
}
},
copyWithin: {
mutatorEvents: {
'copyWithinElement:$index': true,
'copyWithinElement': true,
'copyWithin': true,
}
},
fill: {
lengthen: true,
mutatorEvents: {
'fillElement:$index': true,
'fillElement': true,
'fill': true,
}
},
pop: {
mutatorEvents: { 'pop': true },
},
push: {
mutatorEvents: {
'pushElement:$index': true,
'pushElement': true,
'push': true,
}
},
reverse: {
mutatorEvents: { 'reverse': true },
},
shift: {
mutatorEvents: { 'shift': true },
},
splice: {
mutatorEvents: {
'spliceDeleteElement:$index': true,
'spliceDeleteElement': true,
'spliceAddElement:$index': true,
'spliceAddElement': true,
'splice': true,
}
},
unshift: {
mutatorEvents: {
'unshiftElement:$index': true,
'unshiftElement': true,
'unshift': true,
}
},
},
object: {
assign: {
sourceTree: true,
mutatorEvents: {
'assignSourceProperty:$key': true,
'assignSourceProperty': true,
'assignSource': true,
'assign': true,
},
},
defineProperties: {
descriptorTree: true,
mutatorEvents: { 'defineProperties': true },
},
defineProperty: {
descriptorTree: true,
mutatorEvents: {
'defineProperty': true,
'defineProperty:$key': true,
},
},
freeze: {
recursive: true,
mutatorEvents: {
'freezeProperty': true,
'freeze': true,
},
},
seal: {
recursive: true,
mutatorEvents: {
'sealProperty': true,
'seal': true,
},
},
},
},
}, $options);
return Options
};
class ModelEvent extends CustomEvent {
constructor($type, $settings, $model) {
super($type, $settings);
Object.defineProperties(this, {
'model': { get () { return $model } },
'key': { configurable: true, get () {
const key = (this.path) ? this.path.split('.').pop() : null;
Object.defineProperty(this, 'key', { value: key });
return key
} },
'change': { configurable: true, get () {
const change = $settings.change;
Object.defineProperty(this, 'change', { value: change });
return change
} },
'value': { configurable: true, get () {
const value = $settings.value;
Object.defineProperty(this, 'value', { value: value });
return value
} },
'path': { configurable: true, get () {
const path = $settings.path;
Object.defineProperty(this, 'path', { value: path });
return path
} },
'detail': { configurable: true, get () {
const detail = $settings.detail;
Object.defineProperty(this, 'detail', { value: detail });
return detail
} },
});
}
}
class Change {
#_keyter = false
#_preter = false
#_anter = false
#_conter = false
#keyter
#preter
#anter
#conter
constructor($settings = {}) {
for(const [$key, $value] of Object.entries($settings)) { this[$key] = $value; }
}
get preter() { return this.#preter }
set preter($preter) {
if(this.#_preter === true) { return this.#preter }
this.#preter = $preter?.valueOf();
this.#_preter = true;
}
get anter() { return this.#anter }
set anter($anter) {
if(this.#_anter === true) { return this.#anter }
this.#anter = $anter?.valueOf();
this.#_anter = true;
}
get conter() {
if(
this.#_conter === true ||
[this.#_preter, this.#_anter].includes(false)
) { return this.#conter }
const preter = JSON.stringify(this.preter);
const anter = JSON.stringify(this.anter);
let conter;
if(anter !== preter) { conter = true; }
else { conter = false; }
this.#conter = conter;
this.#_conter = true;
return this.#conter
}
}
let ValidatorEvent$1 = class ValidatorEvent extends CustomEvent {
constructor($type, $settings, $model) {
super($type);
Object.defineProperties(this, {
'key': { configurable: true, get () {
const key = $settings.key;
Object.defineProperty(this, 'key', { value: key });
return key
} },
'path': { configurable: true, get () {
const path = ($model.path)
? [$model.path, $settings.key].join('.')
: $settings.key;
Object.defineProperty(this, 'path', { value: path });
return path
} },
'value': { configurable: true, get () {
const value = $settings.value;
Object.defineProperty(this, 'value', { value: value, });
return value
} },
'valid': { configurable: true, get () {
const valid = $settings.valid;
Object.defineProperty(this, 'valid', { value: valid });
return valid
} },
});
}
};
function assign$1($model, $options, ...$sources) {
const options = Object.assign({}, $options);
const assignObject = 'assign';
const assignArray = options.assignArray || 'assign';
const { path, schema, source, target } = $model;
const { enableValidation, mutatorEvents, required, sourceTree, validationEvents } = options;
const assignedSources = [];
const assignChange = new Change({ preter: $model });
for(let $source of $sources) {
let assignedSource;
const assignSourceChange = new Change({ preter: $model });
if(Array.isArray($source)) { assignedSource = []; }
else if($source && typeof $source === 'object') { assignedSource = {}; }
let validObject;
if(enableValidation && schema) {
validObject = schema.validate($source, $model.valueOf());
validObject.report();
}
iterateSourceProperties:
for(let [$sourceKey, $sourceValue] of Object.entries($source)) {
const assignSourcePropertyChange = new Change({ preter: target[$sourceKey] });
const assignSourcePropertyKeyChange = new Change({ preter: target[$sourceKey] });
if(schema && enableValidation) {
const validatorTarget = $model.valueOf();
const validatorSource = $source;
const validSourceProperty = schema.validateProperty($sourceKey, $sourceValue, validatorSource, validatorTarget);
if(validationEvents) {
let type, propertyType;
if(validSourceProperty.valid) {
type = 'validProperty';
propertyType = ['validProperty', $sourceKey].join(':');
}
else {
type = 'nonvalidProperty';
propertyType = ['nonvalidProperty', $sourceKey].join(':');
}
for(const $eventType of [type, propertyType]) {
$model.dispatchEvent(new ValidatorEvent$1($eventType, validSourceProperty, $model));
}
}
if(!validSourceProperty.valid) { continue iterateSourceProperties }
}
let sourceValue;
if($sourceValue && typeof $sourceValue === 'object') {
if($sourceValue instanceof $model.constructor) {
sourceValue = $sourceValue.valueOf();
}
let subschema;
if(schema?.type === 'array') { subschema = schema.target[0].type.value; }
else if(schema?.type === 'object') { subschema = schema.target[$sourceKey].type.value; }
else { subschema = null; }
const modelPath = (path)
? [path, $sourceKey].join('.')
: String($sourceKey);
if(sourceTree === false) {
const suboptions = assign$3({}, options, {
path: modelPath,
parent: $model,
});
sourceValue = new $model.constructor($sourceValue, subschema, suboptions);
const assignment = { [$sourceKey]: sourceValue };
Object.assign(target, assignment);
Object.assign(assignedSource, assignment);
}
else {
if(target[$sourceKey] instanceof $model.constructor) {
sourceValue = target[$sourceKey];
}
else {
const subproperties = typedObjectLiteral$1($sourceValue);
const suboptions = assign$3({}, options, {
path: modelPath,
parent: $model,
});
sourceValue = new $model.constructor(subproperties, subschema, suboptions);
}
const assignment = { [$sourceKey]: sourceValue };
Object.assign(target, assignment);
Object.assign(assignedSource, assignment);
$model.retroReenableEvents();
if(sourceValue.type === 'array') {
if(['push', 'unshift'].includes(assignArray)) { sourceValue[assignArray](...$sourceValue); }
else { sourceValue[assignArray]($sourceValue); }
}
else if(sourceValue.type === 'object') { sourceValue[assignObject]($sourceValue); }
}
}
else {
sourceValue = $sourceValue;
const assignment = { [$sourceKey]: sourceValue };
Object.assign(target, assignment);
Object.assign(assignedSource, assignment);
}
if(mutatorEvents) {
const modelEventPath = (path) ? [path, $sourceKey].join('.') : String($sourceKey);
if(mutatorEvents['assignSourceProperty:$key']) {
const type = ['assignSourceProperty', $sourceKey].join(':');
assignSourcePropertyKeyChange.anter = target[$sourceKey];
$model.dispatchEvent(
new ModelEvent(type, {
path: modelEventPath,
value: sourceValue,
change: assignSourcePropertyKeyChange,
detail: {
source: assignedSource,
}
}, $model)
);
}
if(mutatorEvents['assignSourceProperty']) {
assignSourcePropertyChange.anter = target[$sourceKey];
$model.dispatchEvent(
new ModelEvent('assignSourceProperty', {
path: modelEventPath,
value: sourceValue,
change: assignSourcePropertyChange,
detail: {
source: assignedSource,
}
}, $model)
);
}
}
}
assignedSources.push(assignedSource);
if(mutatorEvents && mutatorEvents['assignSource']) {
assignSourceChange.anter = $model;
$model.dispatchEvent(
new ModelEvent('assignSource', {
path,
change: assignSourceChange,
detail: {
source: assignedSource,
},
}, $model)
);
}
}
if(mutatorEvents && mutatorEvents['assign']) {
assignChange.anter = $model;
$model.dispatchEvent(
new ModelEvent('assign', {
path,
change: assignChange,
detail: {
sources: assignedSources,
},
}, $model)
);
}
return $model
}
function defineProperties($model, $options, $propertyDescriptors) {
const { path, schema } = $model;
let {
enableValidation, mutatorEvents, required,
validation, validationEvents, validationReport
} = $options;
const propertyDescriptorEntries = Object.entries($propertyDescriptors);
const definePropertiesChange = new Change({ preter: $model });
for(const [
$propertyKey, $propertyDescriptor
] of propertyDescriptorEntries) {
$model.defineProperty($propertyKey, $propertyDescriptor, Object.assign({}, $options, {
validation, validationReport
}));
}
if(mutatorEvents && mutatorEvents['defineProperties']) {
definePropertiesChange.anter = $model;
$model.dispatchEvent(
new ModelEvent(
'defineProperties',
{
path,
value: $model.valueOf(),
detail: {
descriptors: $propertyDescriptors,
},
},
$model
)
);
}
return $model
}
function defineProperty($model, $options, $propertyKey, $propertyDescriptor) {
const options = Object.assign({}, $options);
const assignObject = 'defineProperties';
const assignArray = options.assignArray || 'defineProperties';
const {
descriptorTree, enableValidation, mutatorEvents,
validation, validationEvents, validationReport
} = options;
const { target, path, schema } = $model;
const propertyValue = $propertyDescriptor.value;
const targetPropertyDescriptor = Object.getOwnPropertyDescriptor(target, $propertyKey) || {};
const targetPropertyValue = targetPropertyDescriptor.value;
const definePropertyChange = new Change({ preter: targetPropertyValue });
const definePropertyKeyChange = new Change({ preter: targetPropertyValue });
const targetPropertyValueIsModelInstance = targetPropertyValue instanceof $model.constructor;
if(schema && enableValidation) {
const validProperty = schema.validateProperty(
$propertyKey,
impandTree(propertyValue, 'value') || propertyValue,
{},
$model.valueOf()
);
if(validationEvents) {
let type, propertyType;
if(validProperty.valid) {
type = 'validProperty';
propertyType = ['validProperty', $propertyKey].join(':');
}
else {
type = 'nonvalidProperty';
propertyType = ['nonvalidProperty', $propertyKey].join(':');
}
for(const $eventType of [type, propertyType]) {
$model.dispatchEvent(new ValidatorEvent$1($eventType, validProperty, $model));
}
}
if(!validProperty.valid) { return $model }
}
if(propertyValue && typeof propertyValue === 'object') {
const modelPath = (path)
? [path, $propertyKey].join('.')
: String($propertyKey);
if(targetPropertyValueIsModelInstance) {
if(descriptorTree === true) {
targetPropertyValue.defineProperties($propertyDescriptor);
}
else {
Object.defineProperty(target, $propertyKey, $propertyDescriptor);
}
}
else {
let subschema;
if(schema) {
if(schema.type === 'array') { subschema = schema.target[0].type.value; }
else if(schema.type === 'object') { subschema = schema.target[$propertyKey].type.value; }
else { subschema = undefined; }
}
let subtarget = typedObjectLiteral$1(propertyValue);
const suboptions = assign$3({}, options, {
path: modelPath,
parent: $model,
});
const submodel = new $model.constructor(
subtarget, subschema, suboptions
);
if(descriptorTree === true) {
target[$propertyKey] = submodel;
$model.retroReenableEvents();
if(submodel.type === 'array') {
if(['push', 'unshift'].includes(assignArray)) { submodel[assignArray](...propertyValue); }
else { submodel[assignArray](propertyValue); }
}
else if(submodel.type === 'object') { submodel[assignObject](propertyValue); }
}
else if(descriptorTree === false) {
Object.defineProperty(target, $propertyKey, $propertyDescriptor);
}
}
}
else {
Object.defineProperty(target, $propertyKey, $propertyDescriptor);
}
if(mutatorEvents) {
const modelEventPath = (path)
? [path, $propertyKey].join('.')
: String($propertyKey);
if(mutatorEvents['defineProperty:$key']) {
definePropertyKeyChange.anter = target[$propertyKey];
const type = ['defineProperty', $propertyKey].join(':');
$model.dispatchEvent(
new ModelEvent(type, {
path: modelEventPath,
value: propertyValue,
change: definePropertyKeyChange,
detail: {
prop: $propertyKey,
descriptor: $propertyDescriptor,
},
}, $model
));
}
if(mutatorEvents['defineProperty']) {
definePropertyChange.anter = target[$propertyKey];
$model.dispatchEvent(
new ModelEvent('defineProperty', {
path: modelEventPath,
value: propertyValue,
change: definePropertyChange,
detail: {
prop: $propertyKey,
descriptor: $propertyDescriptor,
},
}, $model
));
}
}
return $model
}
function freeze($model, $options) {
const { recursive, mutatorEvents } = $options;
const { target } = $model;
if(recursive === true) {
for(const [
$propertyKey, $propertyValue
] of Object.entries(target)) {
if($propertyValue instanceof $model.constructor) {
$propertyValue.freeze();
if(mutatorEvents && mutatorEvents['freezeProperty']) {
$model.dispatchEvent(
new ModelEvent(
'freezeProperty',
{ path: $propertyValue.path },
$model
)
);
}
}
}
}
Object.freeze(target);
if(mutatorEvents && mutatorEvents['freeze']) {
$model.dispatchEvent(
new ModelEvent(
'freeze',
{ path: $model.path },
$model
)
);
}
return $model
}
function seal($model, $options) {
const { recursive, mutatorEvents } = $options;
const { target } = $model;
if(recursive === true) {
for(const [
$propertyKey, $propertyValue
] of Object.entries(target)) {
if($propertyValue instanceof $model.constructor) {
$propertyValue.seal();
if(mutatorEvents && mutatorEvents['sealProperty']) {
$model.dispatchEvent(
new ModelEvent(
'sealProperty',
{ path: $propertyValue.path },
$model
)
);
}
}
}
}
Object.seal(target);
if(mutatorEvents && mutatorEvents['seal']) {
$model.dispatchEvent(
new ModelEvent(
'seal',
{ path: $model.path },
$model
)
);
}
return $model
}
var ObjectProperty