UNPKG

octonom

Version:

Object Document Mapper for any database

129 lines 6.15 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 errors_1 = require("./errors"); const hooks_1 = require("./hooks"); const model_1 = require("./schema/model"); const object_1 = require("./schema/object"); function Hook(name, handler) { return (constructor) => { constructor.hooks = new hooks_1.Hooks(constructor.hooks); constructor.hooks.register(name, handler); }; } exports.Hook = Hook; const shadowInstanceProperty = Symbol('shadowInstanceProperty'); exports.getShadowInstance = Symbol('getShadowInstance'); const setShadowInstance = Symbol('setShadowInstance'); // TODO: think about model instances function sanitize(value, sanitizeOptions) { if (value === undefined) { return {}; } if (typeof value !== 'object') { throw new errors_1.SanitizationError('Value is not an object.', 'no-object', sanitizeOptions.parent); } return value; } // TODO: Model can be made an abstract class but the type Constructor<Model> // doesn't work anymore (e.g., used in mixins) class Model { // TODO: ideally we'd also use Partial<this> as the type for data constructor(value, sanitizeOptions = { defaults: value === undefined }) { const constructor = this.constructor; const schemaMap = constructor.schemaMap; // The shadow property holds data that is required to allow methods to operate on both // the model instance and the instanceMap that is hidden from the user. const shadowInstance = { instanceMap: {}, parent: sanitizeOptions.parent, schema: new model_1.ModelSchema({ model: constructor }), value: undefined, rawObject: this, beforeChange: (path, newValue, oldInstance) => { constructor.hooks.run('beforeChange', { path, value: newValue, modelInstance: this, schemaInstance: oldInstance }); if (shadowInstance.schema.options.callParentHooks !== false && shadowInstance.parent) { shadowInstance.parent.instance.beforeChange([shadowInstance.parent.path].concat(path), newValue, oldInstance); } }, afterChange: (path, newValue, newInstance) => { constructor.hooks.run('afterChange', { path, value: newValue, modelInstance: this, schemaInstance: newInstance }); if (shadowInstance.schema.options.callParentHooks !== false && shadowInstance.parent) { shadowInstance.parent.instance.afterChange([shadowInstance.parent.path].concat(path), newValue, newInstance); } }, }; shadowInstance.value = object_1.proxifyObject(this, shadowInstance.instanceMap, shadowInstance, schemaMap); this[setShadowInstance](shadowInstance); const sanitizedValue = sanitize(value, sanitizeOptions); shadowInstance.beforeChange([], sanitizedValue, shadowInstance); object_1.setObject(sanitizedValue, this, shadowInstance.instanceMap, shadowInstance, schemaMap, sanitizeOptions); shadowInstance.afterChange([], sanitizedValue, shadowInstance); return shadowInstance.value; } static setSchema(key, schema) { if (this.schemaMap[key]) { throw new Error(`Key ${key} is already set.`); } this.schemaMap = Object.assign({}, this.schemaMap); this.schemaMap[key] = schema; } inspect() { return this.toObject(); } populate(populateMap) { return __awaiter(this, void 0, void 0, function* () { const constructor = this.constructor; const schemaMap = constructor.schemaMap; const shadowInstance = this[exports.getShadowInstance](); yield object_1.populateObject(shadowInstance.rawObject, shadowInstance.instanceMap, schemaMap, populateMap); return this; }); } // note: set runs with `this` bound to the unproxied instance, therefore // the hooks will be called with the unproxied instance as well. // This prevents recursion when handlers set properties. // The set hooks are called by the proxy. set(data, options = {}, runHooks = true) { const constructor = this.constructor; const schemaMap = constructor.schemaMap; const shadowInstance = this[exports.getShadowInstance](); if (runHooks) { shadowInstance.beforeChange([], data, shadowInstance); } object_1.setObject(data, shadowInstance.rawObject, shadowInstance.instanceMap, shadowInstance, schemaMap, options); if (runHooks) { shadowInstance.afterChange([], data, shadowInstance); } } toObject(options) { const shadowInstance = this[exports.getShadowInstance](); return object_1.toObject(shadowInstance.instanceMap, options); } toJSON() { return this.toObject(); } validate() { return __awaiter(this, void 0, void 0, function* () { const shadowInstance = this[exports.getShadowInstance](); yield object_1.validateObject(shadowInstance.instanceMap, shadowInstance, shadowInstance.schema.options.model.schemaMap); }); } [setShadowInstance](instance) { this[shadowInstanceProperty] = instance; } [exports.getShadowInstance]() { return this[shadowInstanceProperty]; } } Model.schemaMap = {}; Model.hooks = new hooks_1.Hooks(); exports.Model = Model; //# sourceMappingURL=model.js.map