UNPKG

domain-objects

Version:

A simple, convenient way to represent domain objects, leverage domain knowledge, and add runtime validation in your code base.

73 lines (70 loc) 4.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.assertDomainObjectIsSafeToManipulate = exports.DomainObjectNotSafeToManipulateError = void 0; const DomainObject_1 = require("../instantiation/DomainObject"); class DomainObjectNotSafeToManipulateError extends Error { constructor({ unsafeKeys, className, }) { const message = ` DomainObject '${className}' is not safe to manipulate. Specifically, the keys: ${JSON.stringify(unsafeKeys)}. Please make sure all nested objects are DomainObjects and are explicitly defined on the class definition, using the 'nested' static property. For example: \`\`\`ts class ${className} ... { public static nested = { ${unsafeKeys[0]}: ... }; } \`\`\` `; super(message); } } exports.DomainObjectNotSafeToManipulateError = DomainObjectNotSafeToManipulateError; /** * For internal use only. DomainObjects must have all nested domain objects defined in their `DomainObject.nested` definition. Otherwise, we could be dealing with a domain object that is not instantiated, and thus we will not appropriately serialize it. * * Therefore, if any DomainObject has nested objects that are not instantiated as DomainObjects, there is a risk that the nested object is really a nested, uninstantiated DomainObject - which would cause bugs if uncaught. */ const assertDomainObjectIsSafeToManipulate = (obj) => { var _a; // grab all the keys that have objects defined for their values const nestedObjectKeys = Object.keys(obj).filter((key) => typeof obj[key] === 'object' && obj[key] !== null); // grab all the "nested object keys" who's values are defined as "should be domain object" const nestedDomainObjectKeysDefined = Object.keys((_a = obj.constructor.nested) !== null && _a !== void 0 ? _a : {}); const nestedNonDomainObjectObjectKeys = nestedObjectKeys.filter((key) => !nestedDomainObjectKeysDefined.includes(key)); // not explicitly defined as a nested domain object key // now apply some filters to those concerning keys let concerningKeys = nestedNonDomainObjectObjectKeys; // now filter out the keys which correspond to well known objects that we known are serializable and are not potentially DomainObjects (e.g., dates) concerningKeys = concerningKeys.filter((concerningKey) => { const value = obj[concerningKey]; if (value instanceof DomainObject_1.DomainObject) return false; // if value is already as a domain object, not concerning if (value instanceof Date) return false; // if its a date, not concerning if ('Buffer' in globalThis && value instanceof Buffer) return false; // if its a buffer, not concerning return true; // if it wasn't filtered out by now, its still concerning }); // now filter out the keys which correspond to arrays of objects which are safe to manipulate; // TODO: consider how we can move this check to not be dependent on runtime contents of the domain object instance concerningKeys = concerningKeys.filter((concerningKey) => { const value = obj[concerningKey]; if (!Array.isArray(value)) return true; // if its not an array, its still concerning try { (0, exports.assertDomainObjectIsSafeToManipulate)(value); return false; // if we reached here, that means that the contents of the array _are_ safe, so not concerning } catch (error) { if (error instanceof DomainObjectNotSafeToManipulateError) return true; // if it was this error, that means that this key is definitely still concerning throw error; // otherwise its an error we couldn't handle, so we should pass it up } }); // if we have any concerningKeys still, then its not safe! if (concerningKeys.length) throw new DomainObjectNotSafeToManipulateError({ unsafeKeys: concerningKeys, className: obj.constructor.name, }); }; exports.assertDomainObjectIsSafeToManipulate = assertDomainObjectIsSafeToManipulate; //# sourceMappingURL=assertDomainObjectIsSafeToManipulate.js.map