domain-objects
Version:
A simple, convenient way to represent domain objects, leverage domain knowledge, and add runtime validation in your code base.
100 lines (99 loc) • 4.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.deserialize = exports.DeserializationMissingDomainObjectClassError = void 0;
const helpful_errors_1 = require("helpful-errors");
// .why = a class absent from the deserialize context is a developer setup bug (the fix is to register
// the class via `with`), not a caller's bad input — so it is a MalfunctionError (exit 1, http 500)
class DeserializationMissingDomainObjectClassError extends helpful_errors_1.MalfunctionError {
constructor({ className }) {
const message = `
DomainObject '${className}' was referenced in the string being deserialized but was missing from the context given to the deserialize method.
Please make sure all DomainObjects serialized in the string have their classes defined in the context given to the deserialize method, using the 'with' property.
`.trim();
super(message);
}
}
exports.DeserializationMissingDomainObjectClassError = DeserializationMissingDomainObjectClassError;
/**
* Revives the domain objects stored in a string produced by the `serialize` method
*
* Use Cases:
* - persistance
* - e.g., deserialize domain objects from a string that was saved to a persistant store (i.e., a string produced by the `serialize` method)
*/
const deserialize = (serialized, context = {}) => {
// parse the string
const parsed = JSON.parse(serialized);
// recursively traverse the parsed value to hydrate all domain objects
return toHydrated(parsed, { with: context.with ?? [], skip: context.skip });
};
exports.deserialize = deserialize;
// todo: restore inmem cache once we have a universal hash lib (currently, fails on web and in react-native)
// withSimpleCaching(
// {
// bypass: {
// get: (args) => args[1]?.cache === false, // if set to false, then skip
// set: (args) => args[1]?.cache === false, // if set to false, then skip
// },
// cache: ({ fromInput }) => fromInput[1]?.cache || cacheDefault, // deserialization is deterministic
// serialize: {
// key: ({ forInput }) =>
// toHashSha256Sync(
// JSON.stringify({
// serialized: forInput[0],
// with: (forInput[1]?.with ?? []).map(
// (dobj) => (dobj as typeof DomainObject).name,
// ),
// }),
// ),
// },
// },
// );
/**
* helper method for deserialize
*
* hydrates any domain-objects in the previously serialized value, if present
*/
const toHydrated = (value, context) => {
// if this value is not an array and is not an object, then it's a literal, and there's no more hydration to be done
if (!Array.isArray(value) && typeof value !== 'object')
return value;
// if its null, return it too (null is typeof 'object' in js :shrug:)
if (value === null)
return null;
// if its an array, then `toHydrated` each element
if (Array.isArray(value))
return value.map((el) => toHydrated(el, context));
// otherwise, hydrate the object
return toHydratedObject(value, context);
};
/**
* helper method for `toSerializable`
*
* converts all objects to deterministically serializable objects (e.g., sort keys, handle domain objects, etc)
*/
const toHydratedObject = (obj, context) => {
// if this object is a domain object, lookup its constructor and hydrate it
if (obj._dobj) {
// pull its name from the key that the `serialize` method sticks the name onto
const domainObjectClassName = obj._dobj;
// lookup the domain object constructor from context
const DomainObjectConstructor = context.with.find((thisConstructor) => thisConstructor.name === domainObjectClassName);
if (!DomainObjectConstructor)
throw new DeserializationMissingDomainObjectClassError({
className: domainObjectClassName,
});
// use .build() which applies withImmute
// nested domain objects also get .clone() via hydrateNestedDomainObjects with .build()
return DomainObjectConstructor.build(obj, { skip: context.skip });
}
// since this was not a domain object, recursively traverse each key
const hydratedObj = {};
// for each key, recursively hydrate it
Object.keys(obj).forEach((key) => {
hydratedObj[key] = toHydrated(obj[key], context);
});
// return it
return hydratedObj;
};
//# sourceMappingURL=deserialize.js.map