UNPKG

fewer

Version:

A minimal ORM for Node.js.

98 lines 3.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IsModel = Symbol('isModel'); exports.Dirty = Symbol('dirty'); exports.Changed = Symbol('changed'); exports.Changes = Symbol('changes'); exports.Valid = Symbol('valid'); exports.Errors = Symbol('errors'); exports.Symbols = { isModel: exports.IsModel, dirty: exports.Dirty, changed: exports.Changed, changes: exports.Changes, valid: exports.Valid, errors: exports.Errors, }; // NOTE: Intentionally type as any[] so that we can do un-guarded includes() lookups: const SYMBOL_VALUES = Object.values(exports.Symbols); const SetErrors = Symbol('setErrors'); const HasValidationRun = Symbol('hasValidationRun'); const DynAssign = Symbol('dynAssign'); exports.InternalSymbols = { setErrors: SetErrors, hasValidationRun: HasValidationRun, dynAssign: DynAssign, }; const DEFAULT_ERRORS = Object.freeze([]); function createModel(initialObj) { // Clone to ensure we can safely mutate: const obj = Object.assign({}, initialObj); const changes = new Map(); let hasValidationRun = false; let errors = DEFAULT_ERRORS; function setErrors(newErrors) { // Errors get set when running validation, so we mark that we have run validation when we set the errors: hasValidationRun = true; errors = Object.freeze(newErrors); } function dynAssign(nextObj) { changes.clear(); Object.assign(obj, nextObj); hasValidationRun = false; errors = DEFAULT_ERRORS; } // @ts-ignore The proxy implementation here is hard for TypeScript to understand. return new Proxy(obj, { get(target, prop) { // TODO: We should probably break this out into a function to get the symbol properties: switch (prop) { case exports.Symbols.isModel: return true; case exports.Symbols.dirty: return changes.size > 0; case exports.Symbols.changes: { const map = {}; for (const prop of changes.keys()) { map[prop] = changes.get(prop); } return Object.freeze(map); } case exports.Symbols.changed: return Object.freeze([...changes.keys()]); case exports.Symbols.valid: return hasValidationRun && errors.length === 0; case exports.Symbols.errors: return errors; // Internal symbols: case exports.InternalSymbols.setErrors: return setErrors; case exports.InternalSymbols.hasValidationRun: return hasValidationRun; case exports.InternalSymbols.dynAssign: return dynAssign; default: return Reflect.get(target, prop); } }, set(target, prop, value) { if (typeof prop === 'symbol' && SYMBOL_VALUES.includes(prop)) { throw new Error('Cannot set Fewer Symbol properties'); } // Reset our validation state: hasValidationRun = false; if (changes.has(prop)) { // If the value is reset to the original value then there is no work to do: if (changes.get(prop) === value) { changes.delete(prop); } } else { changes.set(prop, Reflect.get(target, prop)); } return Reflect.set(target, prop, value); }, }); } exports.default = createModel; //# sourceMappingURL=createModel.js.map