@openhps/core
Version:
Open Hybrid Positioning System - Core component
260 lines • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConcreteTypeDescriptor = exports.TypeDescriptor = exports.DataSerializerUtils = void 0;
const typedjson_1 = require("typedjson");
const utils_1 = require("./decorators/utils");
/**
* Data serializer utilities for managing the ORM mapping
*/
class DataSerializerUtils {
static get META_FIELD() {
return '__typedJsonJsonObjectMetadataInformation__';
}
/**
* Get the own TypedJSON metadata of the prototype
* @see {@link https://gist.github.com/krizka/c83fb1966dd57997a1fc02625719387d}
* @param {any} proto Prototype of target
* @returns {ObjectMetadata} Root object metadata
*/
static getOwnMetadata(proto) {
return typedjson_1.JsonObjectMetadata.getFromConstructor(proto instanceof Function ? proto : proto.constructor);
}
/**
* Get the TypedJSON metadata
* @see {@link https://gist.github.com/krizka/c83fb1966dd57997a1fc02625719387d}
* @param {any} proto Prototype of target
* @returns {ObjectMetadata} Root object metadata
*/
static getMetadata(proto) {
var _a;
return (_a = DataSerializerUtils.getOwnMetadata(proto)) !== null && _a !== void 0 ? _a : DataSerializerUtils.getRootMetadata(proto);
}
static createMetadata(proto) {
if (Object.prototype.hasOwnProperty.call(proto, this.META_FIELD)) {
return proto[this.META_FIELD];
}
// Target has no JsonObjectMetadata associated with it yet, create it now.
const objectMetadata = new typedjson_1.JsonObjectMetadata(proto.constructor);
// Inherit json members and known types from parent @jsonObject (if any).
const parentMetadata = proto[this.META_FIELD];
if (parentMetadata !== undefined) {
parentMetadata.dataMembers.forEach((memberMetadata, propKey) => {
objectMetadata.dataMembers.set(propKey, memberMetadata);
});
parentMetadata.knownTypes.forEach((knownType) => {
// Only add if sub type
if (knownType === proto.constructor || !(knownType.prototype instanceof proto.constructor)) {
return;
}
objectMetadata.knownTypes.add(knownType);
});
// Add sub class to parent
parentMetadata.knownTypes.add(objectMetadata.classType);
objectMetadata.typeResolver = parentMetadata.typeResolver;
objectMetadata.typeHintEmitter = parentMetadata.typeHintEmitter;
}
Object.defineProperty(proto, this.META_FIELD, {
enumerable: false,
configurable: false,
writable: false,
value: objectMetadata,
});
return objectMetadata;
}
/**
* Get the root TypedJSON metadata
* @see {@link https://gist.github.com/krizka/c83fb1966dd57997a1fc02625719387d}
* @param {any} proto Prototype of target
* @returns {ObjectMetadata} Root object metadata
*/
static getRootMetadata(proto) {
const protoProto = proto instanceof Function ? proto.prototype : Object.getPrototypeOf(proto);
if (!protoProto || !protoProto[DataSerializerUtils.META_FIELD]) {
return proto[DataSerializerUtils.META_FIELD];
}
return DataSerializerUtils.getRootMetadata(protoProto);
}
static ensureTypeDescriptor(type) {
return type instanceof TypeDescriptor ? type : new ConcreteTypeDescriptor(type);
}
/**
* Get member options of a property in a data type
* @param {Constructor} dataType Data type
* @param {string} propertyKey Property key
* @returns {SerializableMemberOptions} member options
*/
static getMemberOptions(dataType, propertyKey) {
const metadata = DataSerializerUtils.getMetadata(dataType);
if (!metadata) {
return undefined;
}
const dataMember = metadata.dataMembers.get(propertyKey);
if (!dataMember) {
return undefined;
}
return dataMember.options;
}
/**
* Get member options of an identifier property in a data type
* @param {Constructor} dataType Data type
* @returns {SerializableMemberOptions} identifier member options
*/
static getIdentifierMemberOptions(dataType) {
const metadata = DataSerializerUtils.getMetadata(dataType);
if (!metadata) {
return undefined;
}
return Array.from(metadata.dataMembers.values()).filter((member) => {
return member && member.primaryKey;
})[0];
}
/**
* Deep merge member options
* @param {unknown} target Target object
* @param {string} propertyKey Property key in target
* @param {any} options Member options
* @returns {any} Merged objects
*/
static mergeMemberOptions(target, propertyKey, options) {
if (typeof options === 'function') {
return options;
}
const memberOptions = DataSerializerUtils.getMemberOptions(target.constructor, propertyKey);
if (!memberOptions) {
return options;
}
return mergeDeep(memberOptions, options);
}
static updateMemberOptions(target, propertyKey, options) {
var _a, _b;
const reflectPropCtor = Reflect.getMetadata('design:type', target, propertyKey);
// Inject additional options if available
if (options) {
const ownMeta = typedjson_1.JsonObjectMetadata.ensurePresentInPrototype(target);
const rootMeta = DataSerializerUtils.getRootMetadata(target.constructor);
const ownMemberMetadata = ownMeta.dataMembers.get(propertyKey) || ownMeta.dataMembers.get(options.name);
const rootMemberMetadata = rootMeta
? rootMeta.dataMembers.get(propertyKey) || rootMeta.dataMembers.get(options.name)
: undefined;
if (!ownMemberMetadata) {
throw new Error(`Unable to get member metadata for ${target} on property ${propertyKey}!`);
}
ownMemberMetadata.options = mergeDeep((_a = ownMemberMetadata.options) !== null && _a !== void 0 ? _a : {}, options);
if (rootMemberMetadata) {
ownMemberMetadata.options = mergeDeep((_b = rootMemberMetadata.options) !== null && _b !== void 0 ? _b : {}, ownMemberMetadata.options);
}
// Merge known sub types as well
rootMeta.knownTypes.forEach((otherType) => {
var _a, _b, _c;
if (otherType === target.constructor || !(otherType.prototype instanceof target.constructor)) {
return;
}
const otherMeta = (_a = DataSerializerUtils.getMetadata(otherType)) !== null && _a !== void 0 ? _a : typedjson_1.JsonObjectMetadata.ensurePresentInPrototype(otherType);
const otherMemberMetadata = otherMeta.dataMembers.get(propertyKey) || otherMeta.dataMembers.get(options.name);
if (otherMemberMetadata) {
otherMemberMetadata.options = mergeDeep((_b = ownMemberMetadata.options) !== null && _b !== void 0 ? _b : {}, otherMemberMetadata.options);
}
else {
otherMeta.dataMembers.set((_c = options.name) !== null && _c !== void 0 ? _c : propertyKey, (0, utils_1.cloneDeep)(ownMemberMetadata));
}
});
// TODO: Possibly need to sync super types as well
}
// Detect generic types that have no deserialization or constructor specified
const meta = typedjson_1.JsonObjectMetadata.ensurePresentInPrototype(target);
const existingOptions = meta.dataMembers.get(options ? options.name || propertyKey : propertyKey);
if (reflectPropCtor === Object &&
(!options || (!options.deserializer && !Object.keys(options).includes('constructor')))) {
// If the type is Object and no deserializer is specified, it can be any
// type of object, including serializable objects.
existingOptions.type = () => typedjson_1.AnyT;
}
else if (existingOptions &&
typeof options !== 'object' &&
existingOptions.type() instanceof ConcreteTypeDescriptor) {
existingOptions.type = () => new ConcreteTypeDescriptor(reflectPropCtor);
}
}
static updateObjectMetadata(target, options, ownMeta, rootMeta) {
var _a, _b, _c;
rootMeta.knownTypes.add(target);
if (rootMeta.initializerCallback && !ownMeta.initializerCallback) {
ownMeta.initializerCallback = rootMeta.initializerCallback;
}
// Merge options
if (options) {
ownMeta.options = mergeDeep(ownMeta === rootMeta ? (_a = ownMeta.options) !== null && _a !== void 0 ? _a : {} : (_c = (_b = ownMeta.options) !== null && _b !== void 0 ? _b : rootMeta.options) !== null && _c !== void 0 ? _c : {}, options);
// Merge known sub types as well
rootMeta.knownTypes.forEach((otherType) => {
var _a;
if (otherType === target || !(otherType.prototype instanceof target)) {
return;
}
const otherMeta = DataSerializerUtils.getMetadata(otherType);
otherMeta.options = mergeDeep((_a = ownMeta.options) !== null && _a !== void 0 ? _a : {}, otherMeta.options);
if (!otherMeta.initializerCallback && ownMeta.initializerCallback) {
otherMeta.initializerCallback = ownMeta.initializerCallback;
}
});
}
// Sync settings from super types
rootMeta.knownTypes.forEach((otherType) => {
if (otherType === target || !(target.prototype instanceof otherType)) {
return;
}
const otherMeta = DataSerializerUtils.getMetadata(otherType);
if (otherMeta && otherMeta.initializerCallback && !ownMeta.initializerCallback) {
ownMeta.initializerCallback = otherMeta.initializerCallback;
}
});
return ownMeta;
}
}
exports.DataSerializerUtils = DataSerializerUtils;
class TypeDescriptor {
constructor(ctor) {
this.ctor = ctor;
}
getTypes() {
return [this.ctor];
}
hasFriendlyName() {
return this.ctor.name !== 'Object';
}
}
exports.TypeDescriptor = TypeDescriptor;
class ConcreteTypeDescriptor extends TypeDescriptor {
constructor(ctor) {
super(ctor);
}
}
exports.ConcreteTypeDescriptor = ConcreteTypeDescriptor;
/**
* Deep merge objects
* @param {any} target Target object
* @param {any} source Source object
* @returns {any} Merged object
*/
function mergeDeep(target, source) {
const output = (0, utils_1.cloneDeep)(target);
if ((0, utils_1.isObject)(target) && (0, utils_1.isObject)(source)) {
Object.keys(source).forEach((key) => {
if (Array.isArray(source[key])) {
output[key] = source[key];
const targetProperty = target[key] !== undefined ? (Array.isArray(target[key]) ? target[key] : [target[key]]) : [];
output[key].push(...targetProperty.filter((val) => !source[key].includes(val)));
}
else if ((0, utils_1.isObject)(source[key])) {
if (!(key in target))
Object.assign(output, { [key]: source[key] });
else
output[key] = mergeDeep(target[key], source[key]);
}
else {
Object.assign(output, { [key]: source[key] });
}
});
}
return output;
}
//# sourceMappingURL=DataSerializerUtils.js.map