@pebula/metap
Version:
meta-programming tools
1,162 lines (1,143 loc) • 35.5 kB
JavaScript
import { MetaClass, PropMetadata, ExcludeMetadata, RelationMetadata, TypeMetadata, targetStore, ModelMetadata, array, LazyInit, BaseSerializer, BaseDeserializer, DualKeyMap } from '@pebula/metap/internal';
export { BaseDeserializer, BaseSerializer, Errors, ModelClassBase, ModelClassCollection, PlainObjectMapper, errors } from '@pebula/metap/internal';
import { getProtoChain, isFunction, isPrimitive } from '@pebula/utils';
import { __decorate, __metadata } from 'tslib';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/*
We need to do some funky stuff for angular compiler...
If we wont, the output d.ts files will be messed up
For example:
export const Prop = MetaClass.decorator(PropMetadata, true);
WILL BECOME IN "d.ts"
export declare const Prop: (def?: import("../../../../../dist/@pebula/metap/internal/pebula-metap-internal").PropMetadataArgs) => (target: any, propertyKey?: string | number | symbol, descOrIndex?: number | PropertyDescriptor) => any;
This will happen because ngc doesn't know how properly get the symbol declaration so it fallbacks to the import style....
*/
/**
* \@propertyDecorator instance
* \@param def
* @type {?}
*/
const Prop = MetaClass.decorator(PropMetadata, true);
/**
* \@propertyDecorator instance
* \@param def
* @type {?}
*/
const Exclude = MetaClass.decorator(ExcludeMetadata, true);
/**
* \@propertyDecorator instance
* \@param def
* @type {?}
*/
const Relation = MetaClass.decorator(RelationMetadata, true);
/**
* \@propertyDecorator instance
* \@param def
* @type {?}
*/
const Type = MetaClass.decorator(TypeMetadata);
/**
* \@propertyDecorator instance
* @return {?}
*/
function Identity() {
return (/**
* @param {?} target
* @param {?} key
* @return {?}
*/
(target, key) => {
targetStore.getTargetMeta((/** @type {?} */ (target.constructor))).model().identity = key;
});
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* \@propertyDecorator static
* @param {?=} metaArgs
* @return {?}
*/
function Model(metaArgs) {
return (/**
* @param {?} target
* @return {?}
*/
(target) => {
/** @type {?} */
const metaClass = MetaClass.create(ModelMetadata, metaArgs, target);
processModel(target, metaClass, metaClass.skip !== true);
});
}
/**
* Takes a model and process it.
* The first step is to extend the target, if it inherits.
* The second step is calling the build() method on the metadata class which will
* start the event life-cycle.
* @param {?} target
* @param {?} metaClass
* @param {?=} build
* @return {?}
*/
function processModel(target, metaClass, build) {
for (let proto of getProtoChain(target)) {
if (target !== proto && targetStore.hasTarget(proto)) {
targetStore.extend(proto, target);
}
}
if (build) {
metaClass.build();
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @record
*/
function CompiledTransformation() { }
if (false) {
/**
* Excluded members that doesn't have a \@Prop decorator.
* All \@Exclude instructions on members with \@Prop instructions are add to the PoClassPropertyMap
* @type {?}
*/
CompiledTransformation.prototype.excluded;
/** @type {?} */
CompiledTransformation.prototype.instructions;
}
/**
* @param {?} value
* @param {?} prop
* @return {?}
*/
function transformValueOut(value, prop) {
if (prop && prop.transform && prop.transform.outgoing) {
return prop.transform.outgoing(value);
}
return value;
}
/**
* @param {?} value
* @param {?} prop
* @return {?}
*/
function transformValueIn(value, prop) {
if (prop && prop.transform && prop.transform.incoming) {
return prop.transform.incoming(value);
}
return value;
}
/**
* @param {?} e
* @return {?}
*/
function excludedPredicate(e) {
return e.name === this;
}
class InclusivePropertyContainer {
/**
* @param {?} target
* @param {?} compiled
* @param {?} predicate
* @param {?=} renamer
*/
constructor(target, compiled, predicate, renamer) {
this.target = target;
this.compiled = compiled;
this.predicate = predicate;
this.renamer = renamer;
}
/**
* @param {?} keys
* @param {?} cb
* @return {?}
*/
forEach(keys, cb) {
/** @type {?} */
let len = keys.length;
/** @type {?} */
const instructions = this.compiled.instructions.slice();
/** @type {?} */
const excluded = this.compiled.excluded.slice();
for (let i = 0; i < len; i++) {
/** @type {?} */
let prop = array.findRemove(instructions, this.predicate, keys[i]) || {
cls: keys[i],
obj: keys[i],
exclude: array.findRemove(excluded, excludedPredicate, keys[i])
};
if (!prop.exclude) {
// we only transform names for ad-hoc properties. registered @Prop's are transformed
// when the prop is compiled.
if (!prop.prop && this.renamer) {
this.renamer(prop);
}
cb(prop);
}
}
}
/**
* A forEach loop on all instructions including excluded instructions and properties not in "keys" but in metadata.
* It is recommended to use "forEach" unless the mapper implementation has different transformation strategies.
* @param {?} keys
* @param {?} cb
* @return {?}
*/
forEachRaw(keys, cb) {
/** @type {?} */
let len = keys.length;
/** @type {?} */
const instructions = this.compiled.instructions.slice();
/** @type {?} */
const excluded = this.compiled.excluded.slice();
for (let i = 0; i < len; i++) {
/** @type {?} */
let prop = array.findRemove(instructions, this.predicate, keys[i]) || {
cls: keys[i],
obj: keys[i],
exclude: array.findRemove(excluded, excludedPredicate, keys[i])
};
// we only transform names for ad-hoc properties. registered @Prop's are transformed
// when the prop is compiled.
if (!prop.prop && this.renamer) {
this.renamer(prop);
}
cb(prop);
}
len = instructions.length;
for (let i = 0; i < len; i++) {
/** @type {?} */
let prop = instructions[i];
// we only transform names for ad-hoc properties. registered @Prop's are transformed
// when the prop is compiled.
if (!prop.prop && this.renamer) {
this.renamer(prop);
}
cb(prop);
}
}
}
if (false) {
/** @type {?} */
InclusivePropertyContainer.prototype.target;
/**
* @type {?}
* @private
*/
InclusivePropertyContainer.prototype.compiled;
/**
* @type {?}
* @private
*/
InclusivePropertyContainer.prototype.predicate;
/**
* @type {?}
* @private
*/
InclusivePropertyContainer.prototype.renamer;
}
class ExclusivePropertyContainer {
/**
* @param {?} target
* @param {?} compiled
*/
constructor(target, compiled) {
this.target = target;
this.compiled = compiled;
}
/**
* @param {?} keys
* @param {?} cb
* @return {?}
*/
forEach(keys, cb) {
/** @type {?} */
const instructions = this.compiled.instructions;
// No need to apply transformNameStrategy, it is cached in the instructions.
for (let i = 0, len = instructions.length; i < len; i++) {
!instructions[i].exclude && cb(instructions[i]);
}
}
/**
* A forEach loop on all instructions including excluded instructions and properties not in "keys" but in metadata.
* It is recommended to use "forEach" unless the mapper implementation has different transformation strategies.
* @param {?} keys
* @param {?} cb
* @return {?}
*/
forEachRaw(keys, cb) {
/** @type {?} */
const instructions = this.compiled.instructions;
for (let i = 0, len = instructions.length; i < len; i++) {
cb(instructions[i]);
}
}
}
if (false) {
/** @type {?} */
ExclusivePropertyContainer.prototype.target;
/**
* @type {?}
* @private
*/
ExclusivePropertyContainer.prototype.compiled;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class TransformationError extends Error {
/**
* @param {?=} message
*/
constructor(message) {
super(message);
}
/**
* @param {?} expectedCol
* @return {?}
*/
static coll_obj(expectedCol) {
return new TransformationError(expectedCol
? `Expected a collection but got an object`
: `Expected an object but got a collection`);
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Returns an array of 2 property names, first is the name of the transformed output
* second is the name of the property name to transform.
* Used for applying NamingStrategyConfig based on the TransformDir
* @param {?} dir
* @param {?} transformNameStrategy
* @return {?}
*/
function namingStrategyMap(dir, transformNameStrategy) {
return transformNameStrategy && isFunction(transformNameStrategy[dir]);
}
/**
* \@internal
* @template T, Z
* @param {?} meta
* @param {?} dir
* @return {?}
*/
function getInstructions(meta, dir) {
// all excluded instructions for this type
// this array will be filtered to hold only @Exclude without @Prop
/** @type {?} */
const excluded = meta
.getValues(ExcludeMetadata)
.filter((/**
* @param {?} e
* @return {?}
*/
e => !e.from || e.from === dir));
/** @type {?} */
const model = meta.model();
// in exclusive mode there is no point in have 2 transformation strategies.
// incoming is never there since incoming keys are not calculated, only defined Props.
if (model.transformStrategy === 'exclusive') {
dir = 'outgoing';
}
// only apply naming strategy on outgoing, incoming has no effect here
/** @type {?} */
const naming = namingStrategyMap(dir, model.transformNameStrategy);
/** @type {?} */
const fkMap = new Map();
// TODO: move to for loop
/** @type {?} */
const instructions = meta.getValues(PropMetadata).map((/**
* @param {?} prop
* @return {?}
*/
prop => {
/** @type {?} */
const obj = {
cls: prop.name,
obj: prop.alias[dir],
exclude: array.findRemove(excluded, (/**
* @param {?} e
* @return {?}
*/
e => e.name === prop.name)),
prop
};
// apply naming strategy when DONT HAVE ALIAS!
if (!obj.exclude && naming && obj.cls === obj.obj) {
obj.obj = model.transformNameStrategy[dir](obj.cls);
}
// store the PoClassPropertyMap of a belongsTo PropMetadata relation
// and the PoClassPropertyMap of all foreign key PropMetadata.
// These arr actually matching pairs of a belongTo relation and it's fk
// (not all belongsTo has fk, only different property name is a fk)
//
// At the end, go through the stored PropMetadata and see if matching pairs found (2 values in array)
// for all of them, swap the prop names so:
// belongsTo PoClassPropertyMap will output (deserialize) to the original fk property name
// foreignKey PoClassPropertyMap wil input (serialize) to the belongsTo property name
// this swap make the deserialize/serialize process transparent to fk mismatch defined on the model.
// De/Serialize implementations are only responsible to return the right object
// (e.g. detect when a key is incoming, return obj instead)
if (prop.relation) {
/** @type {?} */
const arr = fkMap.get(prop) || [];
arr[0] = obj;
fkMap.set(prop, arr);
}
else if (prop.foreignKeyOf) {
/** @type {?} */
const arr = fkMap.get(prop.foreignKeyOf) || [];
arr[1] = obj;
fkMap.set(prop.foreignKeyOf, arr);
}
return obj;
}));
Array.from(fkMap.entries()).forEach((/**
* @param {?} __0
* @return {?}
*/
([k, v]) => {
if (v.length === 2) {
// this is a swap
v[0].obj = (/** @type {?} */ (v[1].cls));
v[1].cls = (/** @type {?} */ (k.name)); // v[0].cls === k.name
}
}));
return { excluded, instructions };
}
/**
* @param {?} p
* @return {?}
*/
function serializePredicate(p) {
return p.cls === this;
}
/**
* @param {?} p
* @return {?}
*/
function deserializePredicate(p) {
return p.obj === this;
}
const ɵ0 = /**
* @this {?}
* @return {?}
*/
function () {
/** @type {?} */
const idKey = this.meta.getIdentityKey();
if (idKey) {
return (this.hasOwnProperty('incoming')
? this.incoming
: this.outgoing).instructions.find((/**
* @param {?} p
* @return {?}
*/
p => p.prop.name === idKey));
}
}, ɵ1 = /**
* @this {?}
* @return {?}
*/
function () {
return getInstructions(this.meta, 'incoming');
}, ɵ2 = /**
* @this {?}
* @return {?}
*/
function () {
return getInstructions(this.meta, 'outgoing');
}, ɵ3 = /**
* @this {?}
* @return {?}
*/
function () {
/** @type {?} */
const model = this.meta.model();
if (model.transformStrategy === 'exclusive') {
return new ExclusivePropertyContainer(this.meta.target, this.incoming);
}
else {
/** @type {?} */
const rename = namingStrategyMap('incoming', model.transformNameStrategy)
? (/**
* @param {?} prop
* @return {?}
*/
prop => (prop.cls = model.transformNameStrategy.incoming(prop.obj)))
: undefined;
return new InclusivePropertyContainer(this.meta.target, this.incoming, deserializePredicate, rename);
}
}, ɵ4 = /**
* @this {?}
* @return {?}
*/
function () {
/** @type {?} */
const model = this.meta.model();
if (model.transformStrategy === 'exclusive') {
return new ExclusivePropertyContainer(this.meta.target, this.outgoing);
}
else {
/** @type {?} */
const rename = namingStrategyMap('outgoing', model.transformNameStrategy)
? (/**
* @param {?} prop
* @return {?}
*/
prop => (prop.obj = model.transformNameStrategy.outgoing(prop.cls)))
: undefined;
return new InclusivePropertyContainer(this.meta.target, this.outgoing, serializePredicate, rename);
}
};
// @dynamic
/**
* A TargetSerializationContext is the running context of a mapper for a specific target class that
* can serialize and deserialize instances of the target class.
* It will run the mapper, provide input and parse results
* @template T, Z
*/
class TargetSerializationContext {
/**
* @param {?} meta
*/
constructor(meta) {
this.meta = meta;
}
/**
* @param {?} mapper
* @return {?}
*/
serialize(mapper) {
return mapper.serialize(this.outgoingContainer);
}
/**
* Deserialize a single target.
* Does not support collection deserialization, if mapper is a collection will throw.
* @param {?} mapper
* @param {?} target
* @return {?}
*/
deserialize(mapper, target) {
/** @type {?} */
const cb = (/**
* @param {?} prop
* @return {?}
*/
(prop) => {
/** @type {?} */
const propMeta = (prop.prop && prop.prop.foreignKeyOf) || prop.prop;
target[prop.cls] = transformValueIn(mapper.getValue(prop.obj, propMeta), propMeta);
});
if (isFunction(mapper.setRef)) {
mapper.setRef(target);
}
if (mapper.raw === true) {
this.incomingContainer.forEachRaw(mapper.getKeys(), cb);
}
else {
this.incomingContainer.forEach(mapper.getKeys(), cb);
}
if (isFunction(mapper.getIdentity)) {
if (this.identity) {
/** @type {?} */
const ident = transformValueIn(mapper.getIdentity(), this.identity.prop);
if (ident) {
target[this.identity.cls] = ident;
}
}
}
}
}
__decorate([
LazyInit((ɵ0)),
__metadata("design:type", Object)
], TargetSerializationContext.prototype, "identity", void 0);
__decorate([
LazyInit((ɵ1)),
__metadata("design:type", Object)
], TargetSerializationContext.prototype, "incoming", void 0);
__decorate([
LazyInit((ɵ2)),
__metadata("design:type", Object)
], TargetSerializationContext.prototype, "outgoing", void 0);
__decorate([
LazyInit((ɵ3)),
__metadata("design:type", Object)
], TargetSerializationContext.prototype, "incomingContainer", void 0);
__decorate([
LazyInit((ɵ4)),
__metadata("design:type", Object)
], TargetSerializationContext.prototype, "outgoingContainer", void 0);
if (false) {
/**
* @type {?}
* @protected
*/
TargetSerializationContext.prototype.identity;
/**
* @type {?}
* @protected
*/
TargetSerializationContext.prototype.incoming;
/**
* @type {?}
* @protected
*/
TargetSerializationContext.prototype.outgoing;
/**
* @type {?}
* @protected
*/
TargetSerializationContext.prototype.incomingContainer;
/**
* @type {?}
* @protected
*/
TargetSerializationContext.prototype.outgoingContainer;
/**
* @type {?}
* @protected
*/
TargetSerializationContext.prototype.meta;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const targetSerializationContextStore = new Map();
/**
* @param {?} targetMeta
* @return {?}
*/
function getTargetSerializationContext(targetMeta) {
/** @type {?} */
let ctx = targetSerializationContextStore.get(targetMeta);
if (!ctx) {
ctx = new TargetSerializationContext(targetMeta);
targetSerializationContextStore.set(targetMeta, ctx);
}
return ctx;
}
/**
* @template TMeta
* @param {?} targetMeta
* @param {?} mapper
* @return {?}
*/
function serializeTargetMeta(targetMeta, mapper) {
return getTargetSerializationContext(targetMeta).serialize(mapper);
}
/**
* @template TMeta
* @param {?} targetMeta
* @param {?} mapper
* @param {?} target
* @param {?=} plain
* @return {?}
*/
function deserializeTargetMeta(targetMeta, mapper, target, plain = false) {
if (mapper.isCollection) {
if (!Array.isArray(target)) {
throw TransformationError.coll_obj(true);
}
/** @type {?} */
const refItems = target.splice(0, target.length);
/** @type {?} */
const identKey = targetStore.getIdentityKey(targetMeta.target, 'incoming');
while (mapper.next()) {
/** @type {?} */
let t;
// compare current item to map with a list of items that if we, if we got.
// if match use that instance.
// TODO: Move compare to the global store, so logic can change without bugs.
if (refItems.length > 0 && isFunction(mapper.getIdentity)) {
/** @type {?} */
const incomingIdent = mapper.getIdentity();
t = array.findRemove(refItems, (/**
* @param {?} item
* @return {?}
*/
item => item[identKey] === incomingIdent));
}
if (!t) {
t = plain ? {} : targetMeta.model().factory(false);
}
getTargetSerializationContext(targetMeta).deserialize(mapper, t);
target.push(t);
}
}
else {
if (Array.isArray(target)) {
throw TransformationError.coll_obj(false);
}
getTargetSerializationContext(targetMeta).deserialize(mapper, target);
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
let PlainObject = class PlainObject {
};
PlainObject = __decorate([
Model({ resName: 'InternalPlainObject' })
], PlainObject);
/**
* @param {?} mapper
* @param {?} instanceOrTarget
* @param {?=} target
* @return {?}
*/
function serialize(mapper, instanceOrTarget, target) {
if (mapper instanceof BaseSerializer) {
/** @type {?} */
const meta = targetStore.getTargetMeta(instanceOrTarget);
if (meta) {
return serializeTargetMeta(meta, mapper);
}
}
else {
/** @type {?} */
const meta = targetStore.getTargetMeta(target || instanceOrTarget.constructor);
if (meta) {
return serializeTargetMeta(meta, mapper.serializer(instanceOrTarget));
}
}
}
/**
* Automatically serialize an instance.
* This method will serialize an instance by first trying to locate the target using the `constructor` function.
* If a target is found and if it's a model target (i.e. ModelMetadata) it will try to get the mapper assign for that
* model.
*
* If no target, model or mapper was found it will use the fallbackMapper mapper provided, or `directMapper`
* if no fallback is provided provided.
*
* Note that when provided a fallback mapper, make sure it is able to serialize unknown targets. (plain objects)
* @param {?} instance
* @param {?=} fallbackMapper
* @return {?}
*/
function autoSerialize(instance, fallbackMapper) {
/** @type {?} */
const tMeta = targetStore.getTargetMeta((/** @type {?} */ (instance.constructor)));
/** @type {?} */
const mapper = (tMeta && tMeta.hasModel && tMeta.model().mapper) || fallbackMapper || directMapper;
return serialize(mapper, instance);
}
/**
* @param {?} mapper
* @param {?=} plainObject
* @param {?=} type
* @param {?=} instance
* @return {?}
*/
function deserialize(mapper, plainObject, type, instance) {
/** @type {?} */
let deserializer;
if (mapper instanceof BaseDeserializer) {
instance = plainObject;
deserializer = mapper;
}
else {
deserializer = mapper.deserializer(plainObject, type);
}
if (targetStore.hasTarget(deserializer.sourceType)) {
/** @type {?} */
const meta = targetStore.getTargetMeta(deserializer.sourceType);
/** @type {?} */
const result = instance || meta.model().factory(deserializer.isCollection);
deserializeTargetMeta(meta, deserializer, result);
return result;
}
else {
/** @type {?} */
const meta = targetStore.getTargetMeta(PlainObject);
/** @type {?} */
const result = instance || deserializer.isCollection ? [] : {};
deserializeTargetMeta(meta, deserializer, result, true);
return result;
}
}
/**
* Automatically de-serialize an object to/into an instance.
* This method will de-serialize an object by first trying to locate a model (i.e. ModelMetadata) for the target.
* If a model is found it will try to get the mapper assign for that model.
*
* If no model or mapper was found it will use the fallbackMapper mapper provided, or `directMapper`
* if no fallback is provided provided.
*
* @template T, Z
* @param {?} plainObject
* @param {?} type
* @param {?=} instance
* @param {?=} fallbackMapper
* @return {?}
*/
function autoDeserialize(plainObject, type, instance = null, fallbackMapper) {
/** @type {?} */
const tMeta = targetStore.getTargetMeta(type);
/** @type {?} */
const mapper = (tMeta && tMeta.hasModel && tMeta.model().mapper) || fallbackMapper || directMapper;
return deserialize(mapper, plainObject, type, instance);
}
/**
* Performs a deep clone to the resource using serialization and deserialization, which means that all rules apply (i.e \@Exclude)
*
* @template T
* @param {?} resource the resource (instance) to clone
* @param {?=} serializationFactory Optional, The [[SerializationFactory]] to use, defaults to [[directMapper]].
* @return {?}
*/
function clone(resource, serializationFactory) {
return autoDeserialize(autoSerialize(resource, serializationFactory), (/** @type {?} */ (resource.constructor)), null, serializationFactory);
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* A mapper that has no mapping effect.
* Maps every property on the source to the same property on the target.
* This mapper does not support non primitive id's
* @template T, Z
*/
class DirectDeserializeMapper extends BaseDeserializer {
/**
* @param {?} source
* @param {?} sourceType
* @param {?=} plainMapper
*/
constructor(source, sourceType, plainMapper) {
super(source, sourceType, plainMapper);
this.idx = -1;
if (!(this instanceof DirectChildDeserializeMapper)) {
this.existing = new DualKeyMap();
}
this.identity = targetStore.getIdentityKey(this.sourceType, 'outgoing');
this.isCollection = Array.isArray(source);
if (!this.isCollection) {
this.current = this.source;
}
}
/**
* @param {?} value
* @return {?}
*/
setRef(value) {
if (this.current) {
this.existing.set(this.sourceType, this.getIdentity(), value);
}
}
/**
* @protected
* @return {?}
*/
get ref() {
if (this.current) {
return this.existing.get(this.sourceType, this.getIdentity());
}
}
/**
* @return {?}
*/
getIdentity() {
// TODO: Move to the global store, so logic can change without bugs.
return this.current[this.identity];
}
/**
* @return {?}
*/
next() {
if (this.isCollection) {
this.current = this.source[++this.idx];
return !!this.current;
}
else {
return false;
}
}
/**
* @return {?}
*/
getKeys() {
return Object.keys(this.current);
}
/**
* @param {?} key
* @param {?=} prop
* @return {?}
*/
getValue(key, prop) {
/** @type {?} */
let value = this.current[key];
if (prop) {
// The adapter has the responsibility to manage relationships.
// It doesn't care about key matching (e.g. key in property customer_id but property is customer)
// it get's a value and the property to assign to, the adapter should check if the value it got
// was an id or an object.
// this relationship handling logic makes this whole adapter support only primitive ID properties.
// if we have primitives we treat them as id's and create an object.
// later we wil check if this value is in cache, if not create it.
// if its not a primitive, it will process as a full object included in the payload.
/** @type {?} */
const rel = this.getRelationQuery(prop, value);
if (rel) {
value = rel;
}
if (targetStore.hasTarget(prop.type.ref)) {
return (this.getCache(prop.type.ref, value) || this.deserialize(value, prop));
}
}
return typeof value === 'object'
? this.plainMapper.deserialize(value)
: value;
}
/**
* @protected
* @param {?} value
* @param {?} prop
* @return {?}
*/
deserialize(value, prop) {
/** @type {?} */
const deserializer = this.ref
? new DirectChildDeserializeMapper(value, prop.type.ref, this.existing, this.plainMapper)
: directMapper.deserializer(value, prop.type.ref, this.plainMapper);
return deserialize(deserializer);
}
/**
* Returns a relationship object with the identity property set.
* This object can then be used by the cache to identify if a value is cached or not (using the type & identity comb)
* @protected
* @param {?} prop
* @param {?} value
* @return {?}
*/
getRelationQuery(prop, value) {
if (prop.relation && isPrimitive(value)) {
return {
[targetStore.getIdentityKey((/** @type {?} */ (prop.type.ref)), 'outgoing')]: value
};
}
}
/**
* @protected
* @param {?} type
* @param {?} value
* @return {?}
*/
getCache(type, value) {
/** @type {?} */
const idKey = targetStore.getIdentityKey(type, 'outgoing');
/** @type {?} */
const idVal = idKey && value[idKey];
if (idVal) {
return this.existing.get(type, idVal);
}
}
}
if (false) {
/** @type {?} */
DirectDeserializeMapper.prototype.isCollection;
/**
* @type {?}
* @protected
*/
DirectDeserializeMapper.prototype.existing;
/**
* @type {?}
* @protected
*/
DirectDeserializeMapper.prototype.current;
/**
* @type {?}
* @protected
*/
DirectDeserializeMapper.prototype.identity;
/**
* @type {?}
* @private
*/
DirectDeserializeMapper.prototype.idx;
}
// tslint:disable-next-line
class DirectChildDeserializeMapper extends DirectDeserializeMapper {
/**
* @param {?} source
* @param {?} sourceType
* @param {?} existing
* @param {?} plainMapper
*/
constructor(source, sourceType, existing, plainMapper) {
super(source, sourceType, plainMapper);
this.existing = existing;
}
}
if (false) {
/**
* @type {?}
* @protected
*/
DirectChildDeserializeMapper.prototype.existing;
}
// tslint:disable-next-line
class DirectSerializeMapper extends BaseSerializer {
/**
* @param {?} container
* @return {?}
*/
serialize(container) {
if (!this.cache) {
this.cache = new Map();
}
if (Array.isArray(this.source)) {
return this.serializeCollection(this.source, container);
}
else {
return this.serializeObject(this.source, container);
}
}
/**
* @private
* @param {?} obj
* @param {?} container
* @return {?}
*/
serializeObject(obj, container) {
/** @type {?} */
const data = {};
/** @type {?} */
const cb = (/**
* @param {?} pMap
* @return {?}
*/
(pMap) => {
/** @type {?} */
const p = pMap.prop;
if (p && targetStore.hasTarget(p.type.ref)) {
/** @type {?} */
const type = p.type.ref;
if (p.relation && !p.type.container) {
/** @type {?} */
const idKey = targetStore.getIdentityKey(type);
// if the rel points to a different fk property name, @tdm will make sure prop.obj is that fk.
data[pMap.obj] = obj[pMap.cls][idKey];
}
else {
data[pMap.obj] = serialize(new DirectChildSerializeMapper(obj[pMap.cls], this.cache, this.plainMapper), type);
}
}
else {
/** @type {?} */
const newVal = this.plainMapper.serialize(transformValueOut(obj[pMap.cls], p));
data[pMap.obj] = newVal;
}
});
container.forEach(Object.keys(obj), cb);
/** @type {?} */
const idKey = targetStore.getIdentityKey(container.target);
if (idKey !== targetStore.getIdentityKey(container.target, 'outgoing')) {
delete data[idKey];
}
return data;
}
/**
* @private
* @param {?} arr
* @param {?} container
* @return {?}
*/
serializeCollection(arr, container) {
return arr.map((/**
* @param {?} s
* @return {?}
*/
s => this.serializeObject(s, container)));
}
}
if (false) {
/**
* @type {?}
* @protected
*/
DirectSerializeMapper.prototype.cache;
}
// tslint:disable-next-line
class DirectChildSerializeMapper extends DirectSerializeMapper {
/**
* @param {?} source
* @param {?} cache
* @param {?} plainMapper
*/
constructor(source, cache, plainMapper) {
super(source, plainMapper);
this.cache = cache;
}
}
if (false) {
/**
* @type {?}
* @protected
*/
DirectChildSerializeMapper.prototype.cache;
}
/** @type {?} */
const directMapper = {
/**
* @param {?} source
* @param {?=} plainMapper
* @return {?}
*/
serializer(source, plainMapper) {
return new DirectSerializeMapper(source, plainMapper);
},
/**
* @template T, Z
* @param {?} source
* @param {?} sourceType
* @param {?=} plainMapper
* @return {?}
*/
deserializer(source, sourceType, plainMapper) {
return new DirectDeserializeMapper(source, sourceType, plainMapper);
}
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { DirectDeserializeMapper, DirectSerializeMapper, Exclude, Identity, Model, Prop, Relation, Type, autoDeserialize, autoSerialize, clone, deserialize, deserializeTargetMeta, directMapper, serialize, serializeTargetMeta };
//# sourceMappingURL=pebula-metap.js.map