fewer
Version:
A minimal ORM for Node.js.
98 lines • 3.77 kB
JavaScript
;
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