UNPKG

octonom

Version:

Object Document Mapper for any database

191 lines 8.05 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = require("lodash"); const errors_1 = require("../errors"); const schema_1 = require("./schema"); // populate an object and instanceMap simultaneously function populateObject(obj, instanceMap, schemaMap, populateReference) { return __awaiter(this, void 0, void 0, function* () { const newObj = {}; yield Promise.all(Object.keys(populateReference).map((key) => __awaiter(this, void 0, void 0, function* () { const schema = schemaMap[key]; if (!schema) { throw new Error(`Key ${key} not found in schema.`); } if (!schema.populate) { throw new Error(`Key ${key} is not populatable.`); } const instance = instanceMap[key]; if (instance === undefined) { return; } newObj[key] = yield schema.populate(instance, populateReference[key]); }))); Object.assign(obj, newObj); return obj; }); } exports.populateObject = populateObject; // proxify an object to sync changes to an instanceMap function proxifyObject(obj, instanceMap, parentInstance, schemaMap) { return new Proxy(obj, { set(target, key, value, receiver) { if (typeof key !== 'symbol' && schemaMap[key]) { parentInstance.beforeChange([], { [key]: value }, parentInstance); const oldInstance = instanceMap[key]; if (oldInstance) { delete oldInstance.parent; } instanceMap[key] = schemaMap[key].create(value, { parent: { instance: parentInstance, path: key } }); target[key] = instanceMap[key].value; parentInstance.afterChange([], { [key]: value }, parentInstance); } else { target[key] = value; } return true; }, deleteProperty(target, key) { if (typeof key !== 'symbol' && schemaMap[key]) { parentInstance.beforeChange([], { [key]: undefined }, parentInstance); const oldInstance = instanceMap[key]; if (oldInstance) { delete oldInstance.parent; } delete instanceMap[key]; delete target[key]; parentInstance.afterChange([], { [key]: undefined }, parentInstance); } else { delete target[key]; } return true; }, }); } exports.proxifyObject = proxifyObject; // set an object and instanceMap simultaneously function setObject(data, obj, instanceMap, parentInstance, schemaMap, sanitizeOptions) { const dataKeys = Object.keys(data); const schemaKeys = Object.keys(schemaMap); const disallowedKeys = lodash_1.difference(dataKeys, schemaKeys); if (disallowedKeys.length > 0) { throw new errors_1.SanitizationError(`Key ${disallowedKeys[0]} not found in schema.`, 'key-not-in-schema', sanitizeOptions.parent); } const newInstanceMap = {}; // sanitize all values before setting schemaKeys.forEach((key) => { if (data[key] === undefined && !sanitizeOptions.defaults) { return; } newInstanceMap[key] = schemaMap[key].create(data[key], Object.assign({}, sanitizeOptions, { parent: { instance: parentInstance, path: key } })); }); schemaKeys.forEach((key) => { if (sanitizeOptions.replace || key in data) { if (instanceMap[key]) { delete instanceMap[key].parent; } delete instanceMap[key]; delete obj[key]; } if (newInstanceMap[key]) { instanceMap[key] = newInstanceMap[key]; obj[key] = newInstanceMap[key].value; } }); } exports.setObject = setObject; function toObject(instanceMap, options) { const newObj = {}; Object.keys(instanceMap).forEach((key) => { const instance = instanceMap[key]; newObj[key] = instance.schema.toObject(instance, options); }); return newObj; } exports.toObject = toObject; function validateObject(instanceMap, parentInstance, schemaMap) { return __awaiter(this, void 0, void 0, function* () { yield Promise.all(Object.keys(schemaMap).map((key) => __awaiter(this, void 0, void 0, function* () { const schema = schemaMap[key]; const instance = instanceMap[key]; if (schema.options.required && instance === undefined) { throw new errors_1.ValidationError(`Key ${key} is required.`, 'required', { instance: parentInstance, path: key }); } if (instance) { yield schema.validate(instance); } }))); }); } exports.validateObject = validateObject; class ObjectSchema { constructor(options) { this.options = options; } create(value, sanitizeOptions = {}) { const sanitizedValue = this.sanitize(value, sanitizeOptions); if (sanitizedValue === undefined) { return undefined; } const instance = { instanceMap: {}, parent: sanitizeOptions.parent, schema: this, value: undefined, beforeChange: (path, newValue, oldInstance) => { if (this.options.callParentHooks !== false && instance.parent) { instance.parent.instance.beforeChange([instance.parent.path].concat(path), newValue, oldInstance); } }, afterChange: (path, newValue, newInstance) => { if (this.options.callParentHooks !== false && instance.parent) { instance.parent.instance.afterChange([instance.parent.path].concat(path), newValue, newInstance); } }, }; const obj = {}; setObject(sanitizedValue, obj, instance.instanceMap, instance, this.options.schemaMap, sanitizeOptions); instance.value = proxifyObject(obj, instance.instanceMap, instance, this.options.schemaMap); return instance; } populate(instance, populateReference) { return __awaiter(this, void 0, void 0, function* () { if (populateReference === true) { throw new Error('populateReference must be an object.'); } return populateObject(instance.value, instance.instanceMap, this.options.schemaMap, populateReference); }); } toObject(instance, options = {}) { return toObject(instance.instanceMap, options); } validate(instance) { return __awaiter(this, void 0, void 0, function* () { yield validateObject(instance.instanceMap, instance, this.options.schemaMap); yield schema_1.validate(this.options, instance); }); } sanitize(value, sanitizeOptions) { if (value === undefined) { if (this.options.required && sanitizeOptions.defaults) { return {}; } return undefined; } if (typeof value !== 'object') { throw new errors_1.SanitizationError('Value is not an object.', 'no-object', sanitizeOptions.parent); } return value; } } exports.ObjectSchema = ObjectSchema; //# sourceMappingURL=object.js.map