UNPKG

@martinmilo/verve

Version:

TypeScript domain modeling library with field-level authorization, business rule validation, and context-aware access control

206 lines 7.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Field = void 0; const constants_1 = require("../../constants"); const utils_1 = require("./utils"); const generator_1 = require("../utils/generator"); const validator_1 = require("../utils/validator"); const errors_1 = require("../../errors"); const FieldType_1 = require("./FieldType"); class Field { constructor(metadata, options = {}) { this.metadata = metadata; this.options = options; } static setGlobalValidators(validators) { Field.globalValidatorsMap.set(this, validators); } static getGlobalValidators() { var _a; return (_a = Field.globalValidatorsMap.get(this)) !== null && _a !== void 0 ? _a : []; } static getGlobalGenerator() { var _a; return (_a = Field.globalGeneratorsMap.get(this)) !== null && _a !== void 0 ? _a : undefined; } static getEagerGenerator(options) { if (options.generator && (0, generator_1.isEagerFieldGenerator)(options.generator)) { return options.generator; } return undefined; } static getEagerValidators(options) { var _a, _b; return (_b = (_a = options.validators) === null || _a === void 0 ? void 0 : _a.filter(validator_1.isEagerFieldValidator)) !== null && _b !== void 0 ? _b : []; } get(model, key) { const state = model[constants_1.MODEL_STATE](); if (!this.isReadable(model, key)) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_NOT_READABLE, { field: key, model: this.metadata.model }); } const value = state[key]; if (value === undefined) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_NOT_INITIALIZED, { field: key, model: this.metadata.model }); } const errors = this.validate(model, key); if (errors.isPresent()) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_VALIDATORS_FAILED, { field: key, model: this.metadata.model, errors: errors.toErrorMessagesWithCode().join('\n'), }); } return value; } unsafeGet(model, key) { const state = model[constants_1.MODEL_STATE](); return state[key]; } set(model, key, value) { const state = model[constants_1.MODEL_STATE](); FieldType_1.FieldType.validate(this, value); if (!this.isWritable(model, key)) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_NOT_WRITABLE, { field: key, model: this.metadata.model }); } if (value === undefined) { this.unset(model, key); return; } try { model[constants_1.MODEL_CHANGE_LOG]({ field: key, currentValue: value, previousValue: state[key], timestamp: new Date(), }); state[key] = value; } catch (error) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_SET_ERROR, { field: key, model: this.metadata.model, error: error instanceof Error ? error.message : String(error), }); } } unset(model, key) { const state = model[constants_1.MODEL_STATE](); try { model[constants_1.MODEL_CHANGE_LOG]({ field: key, currentValue: undefined, previousValue: state[key], timestamp: new Date(), }); delete state[key]; delete model[key]; } catch (error) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_UNSET_ERROR, { field: key, model: this.metadata.model, error: error instanceof Error ? error.message : String(error), }); } } is(model, key, value) { const currentValue = this.unsafeGet(model, key); if (typeof currentValue !== typeof value) { return false; } if (Array.isArray(currentValue) && Array.isArray(value)) { return (0, utils_1.isArrayEqual)(currentValue, value); } if (typeof currentValue === 'object' && typeof value === 'object') { return (0, utils_1.isObjectEqual)(currentValue, value); } return currentValue === value; } isEmpty(model, key) { const value = this.unsafeGet(model, key); if (value === undefined || value === null) { return true; } if (typeof value === 'string') { return value.trim().length === 0; } if (Array.isArray(value)) { return value.length === 0; } if (typeof value === 'object') { return Object.keys(value).length === 0; } return false; } isPresent(model, key) { return !this.isEmpty(model, key); } isValid(model, key) { const value = this.unsafeGet(model, key); const validators = this.options.validators || []; return validators.every((validator) => validator(value)); } generate(model, key) { const generator = this.options.generator; if (!generator) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_NO_GENERATOR, { field: key, model: this.metadata.model }); } const value = this.unsafeGet(model, key); if (value) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_ALREADY_GENERATED, { field: key, model: this.metadata.model }); } if (model.isExisting()) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_CANNOT_GENERATE_EXISTING, { field: key, model: this.metadata.model }); } this.set(model, key, generator()); } compute(model, key) { const compute = this.options.compute; if (!compute) { throw new errors_1.VerveError(errors_1.ErrorCode.FIELD_NO_COMPUTE, { field: key, model: this.metadata.model }); } const proxy = model[constants_1.MODEL_PROXY](); return compute(proxy); } validate(model, key) { const value = this.unsafeGet(model, key); const validators = this.options.validators || []; const errors = errors_1.VerveErrorList.new(); if (value === undefined) { return errors; } for (const validator of validators) { const result = validator(value, model); if (result !== true) { errors.add(errors_1.ErrorCode.FIELD_VALIDATOR_FAILED, { field: key, validator: validator.name || 'anonymous', model: this.metadata.model, }); } } return errors; } isReadable(model, key) { var _a; const value = this.unsafeGet(model, key); const proxy = model[constants_1.MODEL_PROXY](); if (typeof this.options.readable === 'function') { return this.options.readable(model.getContext(), proxy, value); } return (_a = this.options.readable) !== null && _a !== void 0 ? _a : true; } isWritable(model, key) { var _a; const value = this.unsafeGet(model, key); const proxy = model[constants_1.MODEL_PROXY](); if (typeof this.options.writable === 'function') { return this.options.writable(model.getContext(), proxy, value); } return (_a = this.options.writable) !== null && _a !== void 0 ? _a : true; } } exports.Field = Field; Field.globalValidatorsMap = new Map(); Field.globalGeneratorsMap = new Map(); //# sourceMappingURL=Field.js.map