@decaf-ts/core
Version:
Core persistence module for the decaf framework
648 lines • 82.7 kB
JavaScript
import { Model, Validation, ValidationKeys, } from "@decaf-ts/decorator-validation";
import { Repository } from "./../repository/Repository.js";
import { findPrimaryKey, InternalError, NotFoundError, } from "@decaf-ts/db-decorators";
import { PersistenceKeys } from "./../persistence/constants.js";
import { Cascade } from "./../repository/constants.js";
/**
* @description Creates or updates a model instance
* @summary Determines whether to create a new model or update an existing one based on the presence of a primary key
* @template M - The model type extending Model
* @template F - The repository flags type
* @param {M} model - The model instance to create or update
* @param {Context<F>} context - The context for the operation
* @param {Repo<M, F, Context<F>>} [repository] - Optional repository to use for the operation
* @return {Promise<M>} A promise that resolves to the created or updated model
* @function createOrUpdate
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant createOrUpdate
* participant Repository
* participant Model
*
* Caller->>createOrUpdate: model, context, repository?
* alt repository not provided
* createOrUpdate->>Model: get(model.constructor.name)
* Model-->>createOrUpdate: constructor
* createOrUpdate->>Repository: forModel(constructor)
* Repository-->>createOrUpdate: repository
* end
*
* alt primary key undefined
* createOrUpdate->>Repository: create(model, context)
* Repository-->>createOrUpdate: created model
* else primary key defined
* createOrUpdate->>Repository: update(model, context)
* alt update successful
* Repository-->>createOrUpdate: updated model
* else NotFoundError
* createOrUpdate->>Repository: create(model, context)
* Repository-->>createOrUpdate: created model
* end
* end
*
* createOrUpdate-->>Caller: model
*/
export async function createOrUpdate(model, context, alias, repository) {
if (!repository) {
const constructor = Model.get(model.constructor.name);
if (!constructor)
throw new InternalError(`Could not find model ${model.constructor.name}`);
repository = Repository.forModel(constructor, alias);
}
if (typeof model[repository.pk] === "undefined")
return repository.create(model, context);
else {
try {
return repository.update(model, context);
}
catch (e) {
if (!(e instanceof NotFoundError))
throw e;
return repository.create(model, context);
}
}
}
/**
* @description Handles one-to-one relationship creation
* @summary Processes a one-to-one relationship when creating a model, either by referencing an existing model or creating a new one
* @template M - The model type extending Model
* @template R - The repository type extending Repo<M, F, C>
* @template V - The relations metadata type extending RelationsMetadata
* @template F - The repository flags type
* @template C - The context type extending Context<F>
* @param {R} this - The repository instance
* @param {Context<F>} context - The context for the operation
* @param {V} data - The relations metadata
* @param {string} key - The property key of the relationship
* @param {M} model - The model instance
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function oneToOneOnCreate
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant oneToOneOnCreate
* participant repositoryFromTypeMetadata
* participant Model
* participant Repository
* participant cacheModelForPopulate
*
* Caller->>oneToOneOnCreate: this, context, data, key, model
* oneToOneOnCreate->>oneToOneOnCreate: check if propertyValue exists
*
* alt propertyValue is not an object
* oneToOneOnCreate->>repositoryFromTypeMetadata: model, key
* repositoryFromTypeMetadata-->>oneToOneOnCreate: innerRepo
* oneToOneOnCreate->>innerRepo: read(propertyValue)
* innerRepo-->>oneToOneOnCreate: read
* oneToOneOnCreate->>cacheModelForPopulate: context, model, key, propertyValue, read
* oneToOneOnCreate->>oneToOneOnCreate: set model[key] = propertyValue
* else propertyValue is an object
* oneToOneOnCreate->>Model: get(data.class)
* Model-->>oneToOneOnCreate: constructor
* oneToOneOnCreate->>Repository: forModel(constructor)
* Repository-->>oneToOneOnCreate: repo
* oneToOneOnCreate->>repo: create(propertyValue)
* repo-->>oneToOneOnCreate: created
* oneToOneOnCreate->>findPrimaryKey: created
* findPrimaryKey-->>oneToOneOnCreate: pk
* oneToOneOnCreate->>cacheModelForPopulate: context, model, key, created[pk], created
* oneToOneOnCreate->>oneToOneOnCreate: set model[key] = created[pk]
* end
*
* oneToOneOnCreate-->>Caller: void
*/
export async function oneToOneOnCreate(context, data, key, model) {
const propertyValue = model[key];
if (!propertyValue)
return;
if (typeof propertyValue !== "object") {
const innerRepo = repositoryFromTypeMetadata(model, key, this.adapter.alias);
const read = await innerRepo.read(propertyValue);
await cacheModelForPopulate(context, model, key, propertyValue, read);
model[key] = propertyValue;
return;
}
const constructor = Model.get(data.class);
if (!constructor)
throw new InternalError(`Could not find model ${data.class}`);
const repo = Repository.forModel(constructor, this.adapter.alias);
const created = await repo.create(propertyValue);
const pk = findPrimaryKey(created).id;
await cacheModelForPopulate(context, model, key, created[pk], created);
model[key] = created[pk];
}
/**
* @description Handles one-to-one relationship updates
* @summary Processes a one-to-one relationship when updating a model, either by referencing an existing model or updating the related model
* @template M - The model type extending Model
* @template R - The repository type extending Repo<M, F, C>
* @template V - The relations metadata type extending RelationsMetadata
* @template F - The repository flags type
* @template C - The context type extending Context<F>
* @param {R} this - The repository instance
* @param {Context<F>} context - The context for the operation
* @param {V} data - The relations metadata
* @param key - The property key of the relationship
* @param {M} model - The model instance
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function oneToOneOnUpdate
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant oneToOneOnUpdate
* participant repositoryFromTypeMetadata
* participant createOrUpdate
* participant findPrimaryKey
* participant cacheModelForPopulate
*
* Caller->>oneToOneOnUpdate: this, context, data, key, model
* oneToOneOnUpdate->>oneToOneOnUpdate: check if propertyValue exists
* oneToOneOnUpdate->>oneToOneOnUpdate: check if cascade.update is CASCADE
*
* alt propertyValue is not an object
* oneToOneOnUpdate->>repositoryFromTypeMetadata: model, key
* repositoryFromTypeMetadata-->>oneToOneOnUpdate: innerRepo
* oneToOneOnUpdate->>innerRepo: read(propertyValue)
* innerRepo-->>oneToOneOnUpdate: read
* oneToOneOnUpdate->>cacheModelForPopulate: context, model, key, propertyValue, read
* oneToOneOnUpdate->>oneToOneOnUpdate: set model[key] = propertyValue
* else propertyValue is an object
* oneToOneOnUpdate->>createOrUpdate: model[key], context
* createOrUpdate-->>oneToOneOnUpdate: updated
* oneToOneOnUpdate->>findPrimaryKey: updated
* findPrimaryKey-->>oneToOneOnUpdate: pk
* oneToOneOnUpdate->>cacheModelForPopulate: context, model, key, updated[pk], updated
* oneToOneOnUpdate->>oneToOneOnUpdate: set model[key] = updated[pk]
* end
*
* oneToOneOnUpdate-->>Caller: void
*/
export async function oneToOneOnUpdate(context, data, key, model) {
const propertyValue = model[key];
if (!propertyValue)
return;
if (data.cascade.update !== Cascade.CASCADE)
return;
if (typeof propertyValue !== "object") {
const innerRepo = repositoryFromTypeMetadata(model, key, this.adapter.alias);
const read = await innerRepo.read(propertyValue);
await cacheModelForPopulate(context, model, key, propertyValue, read);
model[key] = propertyValue;
return;
}
const updated = await createOrUpdate(model[key], context, this.adapter.alias);
const pk = findPrimaryKey(updated).id;
await cacheModelForPopulate(context, model, key, updated[pk], updated);
model[key] = updated[pk];
}
/**
* @description Handles one-to-one relationship deletion
* @summary Processes a one-to-one relationship when deleting a model, deleting the related model if cascade is enabled
* @template M - The model type extending Model
* @template R - The repository type extending Repo<M, F, C>
* @template V - The relations metadata type extending RelationsMetadata
* @template F - The repository flags type
* @template C - The context type extending Context<F>
* @param {R} this - The repository instance
* @param {Context<F>} context - The context for the operation
* @param {V} data - The relations metadata
* @param key - The property key of the relationship
* @param {M} model - The model instance
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function oneToOneOnDelete
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant oneToOneOnDelete
* participant repositoryFromTypeMetadata
* participant cacheModelForPopulate
*
* Caller->>oneToOneOnDelete: this, context, data, key, model
* oneToOneOnDelete->>oneToOneOnDelete: check if propertyValue exists
* oneToOneOnDelete->>oneToOneOnDelete: check if cascade.update is CASCADE
*
* oneToOneOnDelete->>repositoryFromTypeMetadata: model, key
* repositoryFromTypeMetadata-->>oneToOneOnDelete: innerRepo
*
* alt propertyValue is not a Model instance
* oneToOneOnDelete->>innerRepo: delete(model[key], context)
* innerRepo-->>oneToOneOnDelete: deleted
* else propertyValue is a Model instance
* oneToOneOnDelete->>innerRepo: delete(model[key][innerRepo.pk], context)
* innerRepo-->>oneToOneOnDelete: deleted
* end
*
* oneToOneOnDelete->>cacheModelForPopulate: context, model, key, deleted[innerRepo.pk], deleted
* oneToOneOnDelete-->>Caller: void
*/
export async function oneToOneOnDelete(context, data, key, model) {
const propertyValue = model[key];
if (!propertyValue)
return;
if (data.cascade.update !== Cascade.CASCADE)
return;
const innerRepo = repositoryFromTypeMetadata(model, key, this.adapter.alias);
let deleted;
if (!(propertyValue instanceof Model))
deleted = await innerRepo.delete(model[key], context);
else
deleted = await innerRepo.delete(model[key][innerRepo.pk], context);
await cacheModelForPopulate(context, model, key, deleted[innerRepo.pk], deleted);
}
/**
* @description Handles one-to-many relationship creation
* @summary Processes a one-to-many relationship when creating a model, either by referencing existing models or creating new ones
* @template M - The model type extending Model
* @template R - The repository type extending Repo<M, F, C>
* @template V - The relations metadata type extending RelationsMetadata
* @template F - The repository flags type
* @template C - The context type extending Context<F>
* @param {R} this - The repository instance
* @param {Context<F>} context - The context for the operation
* @param {V} data - The relations metadata
* @param key - The property key of the relationship
* @param {M} model - The model instance
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function oneToManyOnCreate
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant oneToManyOnCreate
* participant repositoryFromTypeMetadata
* participant createOrUpdate
* participant findPrimaryKey
* participant cacheModelForPopulate
*
* Caller->>oneToManyOnCreate: this, context, data, key, model
* oneToManyOnCreate->>oneToManyOnCreate: check if propertyValues exists and has length
* oneToManyOnCreate->>oneToManyOnCreate: check if all elements have same type
* oneToManyOnCreate->>oneToManyOnCreate: create uniqueValues set
*
* alt arrayType is not "object"
* oneToManyOnCreate->>repositoryFromTypeMetadata: model, key
* repositoryFromTypeMetadata-->>oneToManyOnCreate: repo
* loop for each id in uniqueValues
* oneToManyOnCreate->>repo: read(id)
* repo-->>oneToManyOnCreate: read
* oneToManyOnCreate->>cacheModelForPopulate: context, model, key, id, read
* end
* oneToManyOnCreate->>oneToManyOnCreate: set model[key] = [...uniqueValues]
* else arrayType is "object"
* oneToManyOnCreate->>findPrimaryKey: propertyValues[0]
* findPrimaryKey-->>oneToManyOnCreate: pkName
* oneToManyOnCreate->>oneToManyOnCreate: create result set
* loop for each m in propertyValues
* oneToManyOnCreate->>createOrUpdate: m, context
* createOrUpdate-->>oneToManyOnCreate: record
* oneToManyOnCreate->>cacheModelForPopulate: context, model, key, record[pkName], record
* oneToManyOnCreate->>oneToManyOnCreate: add record[pkName] to result
* end
* oneToManyOnCreate->>oneToManyOnCreate: set model[key] = [...result]
* end
*
* oneToManyOnCreate-->>Caller: void
*/
export async function oneToManyOnCreate(context, data, key, model) {
const propertyValues = model[key];
if (!propertyValues || !propertyValues.length)
return;
const arrayType = typeof propertyValues[0];
if (!propertyValues.every((item) => typeof item === arrayType))
throw new InternalError(`Invalid operation. All elements of property ${key} must match the same type.`);
const uniqueValues = new Set([...propertyValues]);
if (arrayType !== "object") {
const repo = repositoryFromTypeMetadata(model, key, this.adapter.alias);
for (const id of uniqueValues) {
const read = await repo.read(id);
await cacheModelForPopulate(context, model, key, id, read);
}
model[key] = [...uniqueValues];
return;
}
const pkName = findPrimaryKey(propertyValues[0]).id;
const result = new Set();
for (const m of propertyValues) {
const record = await createOrUpdate(m, context, this.adapter.alias);
await cacheModelForPopulate(context, model, key, record[pkName], record);
result.add(record[pkName]);
}
model[key] = [...result];
}
/**
* @description Handles one-to-many relationship updates
* @summary Processes a one-to-many relationship when updating a model, delegating to oneToManyOnCreate if cascade update is enabled
* @template M - The model type extending Model
* @template R - The repository type extending Repo<M, F, C>
* @template V - The relations metadata type extending RelationsMetadata
* @template F - The repository flags type
* @template C - The context type extending Context<F>
* @param {R} this - The repository instance
* @param {Context<F>} context - The context for the operation
* @param {V} data - The relations metadata
* @param key - The property key of the relationship
* @param {M} model - The model instance
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function oneToManyOnUpdate
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant oneToManyOnUpdate
* participant oneToManyOnCreate
*
* Caller->>oneToManyOnUpdate: this, context, data, key, model
* oneToManyOnUpdate->>oneToManyOnUpdate: check if cascade.update is CASCADE
*
* alt cascade.update is CASCADE
* oneToManyOnUpdate->>oneToManyOnCreate: apply(this, [context, data, key, model])
* oneToManyOnCreate-->>oneToManyOnUpdate: void
* end
*
* oneToManyOnUpdate-->>Caller: void
*/
export async function oneToManyOnUpdate(context, data, key, model) {
const { cascade } = data;
if (cascade.update !== Cascade.CASCADE)
return;
return oneToManyOnCreate.apply(this, [
context,
data,
key,
model,
]);
}
/**
* @description Handles one-to-many relationship deletion
* @summary Processes a one-to-many relationship when deleting a model, deleting all related models if cascade delete is enabled
* @template M - The model type extending Model
* @template R - The repository type extending Repo<M, F, C>
* @template V - The relations metadata type extending RelationsMetadata
* @template F - The repository flags type
* @template C - The context type extending Context<F>
* @param {R} this - The repository instance
* @param {Context<F>} context - The context for the operation
* @param {V} data - The relations metadata
* @param key - The property key of the relationship
* @param {M} model - The model instance
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function oneToManyOnDelete
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant oneToManyOnDelete
* participant Repository
* participant repositoryFromTypeMetadata
* participant cacheModelForPopulate
*
* Caller->>oneToManyOnDelete: this, context, data, key, model
* oneToManyOnDelete->>oneToManyOnDelete: check if cascade.delete is CASCADE
* oneToManyOnDelete->>oneToManyOnDelete: check if values exists and has length
* oneToManyOnDelete->>oneToManyOnDelete: check if all elements have same type
*
* alt isInstantiated (arrayType is "object")
* oneToManyOnDelete->>Repository: forModel(values[0])
* Repository-->>oneToManyOnDelete: repo
* else not instantiated
* oneToManyOnDelete->>repositoryFromTypeMetadata: model, key
* repositoryFromTypeMetadata-->>oneToManyOnDelete: repo
* end
*
* oneToManyOnDelete->>oneToManyOnDelete: create uniqueValues set
*
* loop for each id in uniqueValues
* oneToManyOnDelete->>repo: delete(id, context)
* repo-->>oneToManyOnDelete: deleted
* oneToManyOnDelete->>cacheModelForPopulate: context, model, key, id, deleted
* end
*
* oneToManyOnDelete->>oneToManyOnDelete: set model[key] = [...uniqueValues]
* oneToManyOnDelete-->>Caller: void
*/
export async function oneToManyOnDelete(context, data, key, model) {
if (data.cascade.delete !== Cascade.CASCADE)
return;
const values = model[key];
if (!values || !values.length)
return;
const arrayType = typeof values[0];
const areAllSameType = values.every((item) => typeof item === arrayType);
if (!areAllSameType)
throw new InternalError(`Invalid operation. All elements of property ${key} must match the same type.`);
const isInstantiated = arrayType === "object";
const repo = isInstantiated
? Repository.forModel(values[0], this.adapter.alias)
: repositoryFromTypeMetadata(model, key, this.adapter.alias);
const uniqueValues = new Set([
...(isInstantiated
? values.map((v) => v[repo.pk])
: values),
]);
for (const id of uniqueValues.values()) {
const deleted = await repo.delete(id, context);
await cacheModelForPopulate(context, model, key, id, deleted);
}
model[key] = [...uniqueValues];
}
/**
* @description Generates a key for caching populated model relationships
* @summary Creates a unique key for storing and retrieving populated model relationships in the cache
* @param {string} tableName - The name of the table or model
* @param {string} fieldName - The name of the field or property
* @param {string|number} id - The identifier of the related model
* @return {string} A dot-separated string that uniquely identifies the relationship
* @function getPopulateKey
* @memberOf module:core
*/
export function getPopulateKey(tableName, fieldName, id) {
return [PersistenceKeys.POPULATE, tableName, fieldName, id].join(".");
}
/**
* @description Caches a model for later population
* @summary Stores a model in the context cache for efficient retrieval during relationship population
* @template M - The model type extending Model
* @template F - The repository flags type
* @param {Context<F>} context - The context for the operation
* @param {M} parentModel - The parent model that contains the relationship
* @param propertyKey - The property key of the relationship
* @param {string | number} pkValue - The primary key value of the related model
* @param {any} cacheValue - The model instance to cache
* @return {Promise<any>} A promise that resolves with the result of the cache operation
* @function cacheModelForPopulate
* @memberOf module:core
*/
export async function cacheModelForPopulate(context, parentModel, propertyKey, pkValue, cacheValue) {
const cacheKey = getPopulateKey(parentModel.constructor.name, propertyKey, pkValue);
return context.accumulate({ [cacheKey]: cacheValue });
}
/**
* @description Populates a model's relationship
* @summary Retrieves and attaches related models to a model's relationship property
* @template M - The model type extending Model
* @template R - The repository type extending Repo<M, F, C>
* @template V - The relations metadata type extending RelationsMetadata
* @template F - The repository flags type
* @template C - The context type extending Context<F>
* @param {R} this - The repository instance
* @param {Context<F>} context - The context for the operation
* @param {V} data - The relations metadata
* @param key - The property key of the relationship
* @param {M} model - The model instance
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function populate
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant populate
* participant fetchPopulateValues
* participant getPopulateKey
* participant Context
* participant repositoryFromTypeMetadata
*
* Caller->>populate: this, context, data, key, model
* populate->>populate: check if data.populate is true
* populate->>populate: get nested value and check if it exists
*
* populate->>fetchPopulateValues: context, model, key, isArr ? nested : [nested]
*
* fetchPopulateValues->>fetchPopulateValues: initialize variables
*
* loop for each proKeyValue in propKeyValues
* fetchPopulateValues->>getPopulateKey: model.constructor.name, propName, proKeyValue
* getPopulateKey-->>fetchPopulateValues: cacheKey
*
* alt try to get from cache
* fetchPopulateValues->>Context: get(cacheKey)
* Context-->>fetchPopulateValues: val
* else catch error
* fetchPopulateValues->>repositoryFromTypeMetadata: model, propName
* repositoryFromTypeMetadata-->>fetchPopulateValues: repo
* fetchPopulateValues->>repo: read(proKeyValue)
* repo-->>fetchPopulateValues: val
* end
*
* fetchPopulateValues->>fetchPopulateValues: add val to results
* end
*
* fetchPopulateValues-->>populate: results
* populate->>populate: set model[key] = isArr ? res : res[0]
* populate-->>Caller: void
*/
export async function populate(context, data, key, model) {
if (!data.populate)
return;
const nested = model[key];
const isArr = Array.isArray(nested);
if (typeof nested === "undefined" || (isArr && nested.length === 0))
return;
async function fetchPopulateValues(c, model, propName, propKeyValues, alias) {
let cacheKey;
let val;
const results = [];
for (const proKeyValue of propKeyValues) {
cacheKey = getPopulateKey(model.constructor.name, propName, proKeyValue);
try {
val = await c.get(cacheKey);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
const repo = repositoryFromTypeMetadata(model, propName, alias);
if (!repo)
throw new InternalError("Could not find repo");
val = await repo.read(proKeyValue);
}
results.push(val);
}
return results;
}
const res = await fetchPopulateValues(context, model, key, isArr ? nested : [nested], this.adapter.alias);
model[key] = isArr ? res : res[0];
}
/**
* @description List of common JavaScript types
* @summary An array of strings representing common JavaScript types that are not custom model types
* @const commomTypes
* @memberOf module:core
*/
const commomTypes = [
"array",
"string",
"number",
"boolean",
"symbol",
"function",
"object",
"undefined",
"null",
"bigint",
];
/**
* @description Retrieves a repository for a model property based on its type metadata
* @summary Examines a model property's type metadata to determine the appropriate repository for related models
* @template M - The model type extending Model
* @param {any} model - The model instance containing the property
* @param propertyKey - The property key to examine
* @return {Repo<M>} A repository for the model type associated with the property
* @function repositoryFromTypeMetadata
* @memberOf module:core
* @mermaid
* sequenceDiagram
* participant Caller
* participant repositoryFromTypeMetadata
* participant Reflect
* participant Validation
* participant Model
* participant Repository
*
* Caller->>repositoryFromTypeMetadata: model, propertyKey
*
* repositoryFromTypeMetadata->>Validation: key(Array.isArray(model[propertyKey]) ? ValidationKeys.LIST : ValidationKeys.TYPE)
* Validation-->>repositoryFromTypeMetadata: validationKey
*
* repositoryFromTypeMetadata->>Reflect: getMetadata(validationKey, model, propertyKey)
* Reflect-->>repositoryFromTypeMetadata: types
*
* repositoryFromTypeMetadata->>repositoryFromTypeMetadata: determine customTypes based on property type
* repositoryFromTypeMetadata->>repositoryFromTypeMetadata: check if types and customTypes exist
*
* repositoryFromTypeMetadata->>repositoryFromTypeMetadata: create allowedTypes array
* repositoryFromTypeMetadata->>repositoryFromTypeMetadata: find constructorName not in commomTypes
* repositoryFromTypeMetadata->>repositoryFromTypeMetadata: check if constructorName exists
*
* repositoryFromTypeMetadata->>Model: get(constructorName)
* Model-->>repositoryFromTypeMetadata: constructor
* repositoryFromTypeMetadata->>repositoryFromTypeMetadata: check if constructor exists
*
* repositoryFromTypeMetadata->>Repository: forModel(constructor)
* Repository-->>repositoryFromTypeMetadata: repo
*
* repositoryFromTypeMetadata-->>Caller: repo
*/
export function repositoryFromTypeMetadata(model, propertyKey, alias) {
const types = Reflect.getMetadata(Validation.key(Array.isArray(model[propertyKey])
? ValidationKeys.LIST
: ValidationKeys.TYPE), model, propertyKey);
const customTypes = Array.isArray(model[propertyKey])
? types.clazz
: types.customTypes;
if (!types || !customTypes)
throw new InternalError(`Failed to find types decorators for property ${propertyKey}`);
const allowedTypes = Array.isArray(customTypes)
? [...customTypes]
: [customTypes];
const constructorName = allowedTypes.find((t) => !commomTypes.includes(`${t}`.toLowerCase()));
if (!constructorName)
throw new InternalError(`Property key ${propertyKey} does not have a valid constructor type`);
const constructor = Model.get(constructorName);
if (!constructor)
throw new InternalError(`No registered model found for ${constructorName}`);
return Repository.forModel(constructor, alias);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RydWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21vZGVsL2NvbnN0cnVjdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsS0FBSyxFQUVMLFVBQVUsRUFDVixjQUFjLEdBQ2YsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN4QyxPQUFPLEVBQVEsVUFBVSxFQUFFLHNDQUFpQztBQUU1RCxPQUFPLEVBQ0wsY0FBYyxFQUNkLGFBQWEsRUFDYixhQUFhLEdBRWQsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsZUFBZSxFQUFFLHNDQUFpQztBQUMzRCxPQUFPLEVBQUUsT0FBTyxFQUFFLHFDQUFnQztBQUdsRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdDRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsY0FBYyxDQUlsQyxLQUFRLEVBQ1IsT0FBbUIsRUFDbkIsS0FBYyxFQUNkLFVBQW1DO0lBRW5DLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoQixNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdEQsSUFBSSxDQUFDLFdBQVc7WUFDZCxNQUFNLElBQUksYUFBYSxDQUFDLHdCQUF3QixLQUFLLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDNUUsVUFBVSxHQUFHLFVBQVUsQ0FBQyxRQUFRLENBQzlCLFdBQTZDLEVBQzdDLEtBQUssQ0FDTixDQUFDO0lBQ0osQ0FBQztJQUNELElBQUksT0FBTyxLQUFLLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxLQUFLLFdBQVc7UUFDN0MsT0FBTyxVQUFVLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztTQUN0QyxDQUFDO1FBQ0osSUFBSSxDQUFDO1lBQ0gsT0FBTyxVQUFV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V0FBVyxHQUFRLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3hELENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSztRQUNiLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDO0lBQ3RCLElBQUksQ0FBQyxLQUFLLElBQUksQ0FBQyxXQUFXO1FBQ3hCLE1BQU0sSUFBSSxhQUFhLENBQ3JCLGdEQUFnRCxXQUFxQixFQUFFLENBQ3hFLENBQUM7SUFFSixNQUFNLFlBQVksR0FBYSxLQUFLLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQztRQUN2RCxDQUFDLENBQUMsQ0FBQyxHQUFHLFdBQVcsQ0FBQztRQUNsQixDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUNsQixNQUFNLGVBQWUsR0FBRyxZQUFZLENBQUMsSUFBSSxDQUN2QyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FDbkQsQ0FBQztJQUNGLElBQUksQ0FBQyxlQUFlO1FBQ2xCLE1BQU0sSUFBSSxhQUFhLENBQ3JCLGdCQUFnQixXQUFxQix5Q0FBeUMsQ0FDL0UsQ0FBQztJQUNKLE1BQU0sV0FBVyxHQUErQixLQUFLLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzNFLElBQUksQ0FBQyxXQUFXO1FBQ2QsTUFBTSxJQUFJLGFBQWEsQ0FBQyxpQ0FBaUMsZUFBZSxFQUFFLENBQUMsQ0FBQztJQUU5RSxPQUFPLFVBQVUsQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDb25zdHJ1Y3RvcixcbiAgTW9kZWwsXG4gIE1vZGVsQ29uc3RydWN0b3IsXG4gIFZhbGlkYXRpb24sXG4gIFZhbGlkYXRpb25LZXlzLFxufSBmcm9tIFwiQGRlY2FmLXRzL2RlY29yYXRvci12YWxpZGF0aW9uXCI7XG5pbXBvcnQgeyBSZXBvLCBSZXBvc2l0b3J5IH0gZnJvbSBcIi4uL3JlcG9zaXRvcnkvUmVwb3NpdG9yeVwiO1xuaW1wb3J0IHsgUmVsYXRpb25zTWV0YWRhdGEgfSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHtcbiAgZmluZFByaW1hcnlLZXksXG4gIEludGVybmFsRXJyb3IsXG4gIE5vdEZvdW5kRXJyb3IsXG4gIFJlcG9zaXRvcnlGbGFncyxcbn0gZnJvbSBcIkBkZWNhZi10cy9kYi1kZWNvcmF0b3JzXCI7XG5pbXBvcnQgeyBQZXJzaXN0ZW5jZUtleXMgfSBmcm9tIFwiLi4vcGVyc2lzdGVuY2UvY29uc3RhbnRzXCI7XG5pbXBvcnQgeyBDYXNjYWRlIH0gZnJvbSBcIi4uL3JlcG9zaXRvcnkvY29uc3RhbnRzXCI7XG5pbXBvcnQgeyBDb250ZXh0IH0gZnJvbSBcIkBkZWNhZi10cy9kYi1kZWNvcmF0b3JzXCI7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIENyZWF0ZXMgb3IgdXBkYXRlcyBhIG1vZGVsIGluc3RhbmNlXG4gKiBAc3VtbWFyeSBEZXRlcm1pbmVzIHdoZXRoZXIgdG8gY3JlYXRlIGEgbmV3IG1vZGVsIG9yIHVwZGF0ZSBhbiBleGlzdGluZyBvbmUgYmFzZWQgb24gdGhlIHByZXNlbmNlIG9mIGEgcHJpbWFyeSBrZXlcbiAqIEB0ZW1wbGF0ZSBNIC0gVGhlIG1vZGVsIHR5cGUgZXh0ZW5kaW5nIE1vZGVsXG4gKiBAdGVtcGxhdGUgRiAtIFRoZSByZXBvc2l0b3J5IGZsYWdzIHR5cGVcbiAqIEBwYXJhbSB7TX0gbW9kZWwgLSBUaGUgbW9kZWwgaW5zdGFuY2UgdG8gY3JlYXRlIG9yIHVwZGF0ZVxuICogQHBhcmFtIHtDb250ZXh0PEY+fSBjb250ZXh0IC0gVGhlIGNvbnRleHQgZm9yIHRoZSBvcGVyYXRpb25cbiAqIEBwYXJhbSB7UmVwbzxNLCBGLCBDb250ZXh0PEY+Pn0gW3JlcG9zaXRvcnldIC0gT3B0aW9uYWwgcmVwb3NpdG9yeSB0byB1c2UgZm9yIHRoZSBvcGVyYXRpb25cbiAqIEByZXR1cm4ge1Byb21pc2U8TT59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBjcmVhdGVkIG9yIHVwZGF0ZWQgbW9kZWxcbiAqIEBmdW5jdGlvbiBjcmVhdGVPclVwZGF0ZVxuICogQG1lbWJlck9mIG1vZHVsZTpjb3JlXG4gKiBAbWVybWFpZFxuICogc2VxdWVuY2VEaWFncmFtXG4gKiAgIHBhcnRpY2lwYW50IENhbGxlclxuICogICBwYXJ0aWNpcGFudCBjcmVhdGVPclVwZGF0ZVxuICogICBwYXJ0aWNpcGFudCBSZXBvc2l0b3J5XG4gKiAgIHBhcnRpY2lwYW50IE1vZGVsXG4gKlxuICogICBDYWxsZXItPj5jcmVhdGVPclVwZGF0ZTogbW9kZWwsIGNvbnRleHQsIHJlcG9zaXRvcnk/XG4gKiAgIGFsdCByZXBvc2l0b3J5IG5vdCBwcm92aWRlZFxuICogICAgIGNyZWF0ZU9yVXBkYXRlLT4+TW9kZWw6IGdldChtb2RlbC5jb25zdHJ1Y3Rvci5uYW1lKVxuICogICAgIE1vZGVsLS0+PmNyZWF0ZU9yVXBkYXRlOiBjb25zdHJ1Y3RvclxuICogICAgIGNyZWF0ZU9yVXBkYXRlLT4+UmVwb3NpdG9yeTogZm9yTW9kZWwoY29uc3RydWN0b3IpXG4gKiAgICAgUmVwb3NpdG9yeS0tPj5jcmVhdGVPclVwZGF0ZTogcmVwb3NpdG9yeVxuICogICBlbmRcbiAqXG4gKiAgIGFsdCBwcmltYXJ5IGtleSB1bmRlZmluZWRcbiAqICAgICBjcmVhdGVPclVwZGF0ZS0+PlJlcG9zaXRvcnk6IGNyZWF0ZShtb2RlbCwgY29udGV4dClcbiAqICAgICBSZXBvc2l0b3J5LS0+PmNyZWF0ZU9yVXBkYXRlOiBjcmVhdGVkIG1vZGVsXG4gKiAgIGVsc2UgcHJpbWFyeSBrZXkgZGVmaW5lZFxuICogICAgIGNyZWF0ZU9yVXBkYXRlLT4+UmVwb3NpdG9yeTogdXBkYXRlKG1vZGVsLCBjb250ZXh0KVxuICogICAgIGFsdCB1cGRhdGUgc3VjY2Vzc2Z1bFxuICogICAgICAgUmVwb3NpdG9yeS0tPj5jcmVhdGVPclVwZGF0ZTogdXBkYXRlZCBtb2RlbFxuICogICAgIGVsc2UgTm90Rm91bmRFcnJvclxuICogICAgICAgY3JlYXRlT3JVcGRhdGUtPj5SZXBvc2l0b3J5OiBjcmVhdGUobW9kZWwsIGNvbnRleHQpXG4gKiAgICAgICBSZXBvc2l0b3J5LS0+PmNyZWF0ZU9yVXBkYXRlOiBjcmVhdGVkIG1vZGVsXG4gKiAgICAgZW5kXG4gKiAgIGVuZFxuICpcbiAqICAgY3JlYXRlT3JVcGRhdGUtLT4+Q2FsbGVyOiBtb2RlbFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlT3JVcGRhdGU8XG4gIE0gZXh0ZW5kcyBNb2RlbCxcbiAgRiBleHRlbmRzIFJlcG9zaXRvcnlGbGFncyxcbj4oXG4gIG1vZGVsOiBNLFxuICBjb250ZXh0OiBDb250ZXh0PEY+LFxuICBhbGlhcz86IHN0cmluZyxcbiAgcmVwb3NpdG9yeT86IFJlcG88TSwgRiwgQ29udGV4dDxGPj5cbik6IFByb21pc2U8TT4ge1xuICBpZiAoIXJlcG9zaXRvcnkpIHtcbiAgICBjb25zdCBjb25zdHJ1Y3RvciA9IE1vZGVsLmdldChtb2RlbC5jb25zdHJ1Y3Rvci5uYW1lKTtcbiAgICBpZiAoIWNvbnN0cnVjdG9yKVxuICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYENvdWxkIG5vdCBmaW5kIG1vZGVsICR7bW9kZWwuY29uc3RydWN0b3IubmFtZX1gKTtcbiAgICByZXBvc2l0b3J5ID0gUmVwb3NpdG9yeS5mb3JNb2RlbDxNLCBSZXBvPE0+PihcbiAgICAgIGNvbnN0cnVjdG9yIGFzIHVua25vd24gYXMgTW9kZWxDb25zdHJ1Y3RvcjxNPixcbiAgICAgIGFsaWFzXG4gICAgKTtcbiAgfVxuICBpZiAodHlwZW9mIG1vZGVsW3JlcG9zaXRvcnkucGtdID09PSBcInVuZGVmaW5lZFwiKVxuICAgIHJldHVybiByZXBvc2l0b3J5LmNyZWF0ZShtb2RlbCwgY29udGV4dCk7XG4gIGVsc2Uge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gcmVwb3NpdG9yeS51cGRhdGUobW9kZWwsIGNvbnRleHQpO1xuICAgIH0gY2F0Y2ggKGU6IGFueSkge1xuICAgICAgaWYgKCEoZSBpbnN0YW5jZW9mIE5vdEZvdW5kRXJyb3IpKSB0aHJvdyBlO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkuY3JlYXRlKG1vZGVsLCBjb250ZXh0KTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBvbmUtdG8tb25lIHJlbGF0aW9uc2hpcCBjcmVhdGlvblxuICogQHN1bW1hcnkgUHJvY2Vzc2VzIGEgb25lLXRvLW9uZSByZWxhdGlvbnNoaXAgd2hlbiBjcmVhdGluZyBhIG1vZGVsLCBlaXRoZXIgYnkgcmVmZXJlbmNpbmcgYW4gZXhpc3RpbmcgbW9kZWwgb3IgY3JlYXRpbmcgYSBuZXcgb25lXG4gKiBAdGVtcGxhdGUgTSAtIFRoZSBtb2RlbCB0eXBlIGV4dGVuZGluZyBNb2RlbFxuICogQHRlbXBsYXRlIFIgLSBUaGUgcmVwb3NpdG9yeSB0eXBlIGV4dGVuZGluZyBSZXBvPE0sIEYsIEM+XG4gKiBAdGVtcGxhdGUgViAtIFRoZSByZWxhdGlvbnMgbWV0YWRhdGEgdHlwZSBleHRlbmRpbmcgUmVsYXRpb25zTWV0YWRhdGFcbiAqIEB0ZW1wbGF0ZSBGIC0gVGhlIHJlcG9zaXRvcnkgZmxhZ3MgdHlwZVxuICogQHRlbXBsYXRlIEMgLSBUaGUgY29udGV4dCB0eXBlIGV4dGVuZGluZyBDb250ZXh0PEY+XG4gKiBAcGFyYW0ge1J9IHRoaXMgLSBUaGUgcmVwb3NpdG9yeSBpbnN0YW5jZVxuICogQHBhcmFtIHtDb250ZXh0PEY+fSBjb250ZXh0IC0gVGhlIGNvbnRleHQgZm9yIHRoZSBvcGVyYXRpb25cbiAqIEBwYXJhbSB7Vn0gZGF0YSAtIFRoZSByZWxhdGlvbnMgbWV0YWRhdGFcbiAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgLSBUaGUgcHJvcGVydHkga2V5IG9mIHRoZSByZWxhdGlvbnNoaXBcbiAqIEBwYXJhbSB7TX0gbW9kZWwgLSBUaGUgbW9kZWwgaW5zdGFuY2VcbiAqIEByZXR1cm4ge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIG9wZXJhdGlvbiBpcyBjb21wbGV0ZVxuICogQGZ1bmN0aW9uIG9uZVRvT25lT25DcmVhdGVcbiAqIEBtZW1iZXJPZiBtb2R1bGU6Y29yZVxuICogQG1lcm1haWRcbiAqIHNlcXVlbmNlRGlhZ3JhbVxuICogICBwYXJ0aWNpcGFudCBDYWxsZXJcbiAqICAgcGFydGljaXBhbnQgb25lVG9PbmVPbkNyZWF0ZVxuICogICBwYXJ0aWNpcGFudCByZXBvc2l0b3J5RnJvbVR5cGVNZXRhZGF0YVxuICogICBwYXJ0aWNpcGFudCBNb2RlbFxuICogICBwYXJ0aWNpcGFudCBSZXBvc2l0b3J5XG4gKiAgIHBhcnRpY2lwYW50IGNhY2hlTW9kZWxGb3JQb3B1bGF0ZVxuICpcbiAqICAgQ2FsbGVyLT4+b25lVG9PbmVPbkNyZWF0ZTogdGhpcywgY29udGV4dCwgZGF0YSwga2V5LCBtb2RlbFxuICogICBvbmVUb09uZU9uQ3JlYXRlLT4+b25lVG9PbmVPbkNyZWF0ZTogY2hlY2sgaWYgcHJvcGVydHlWYWx1ZSBleGlzdHNcbiAqXG4gKiAgIGFsdCBwcm9wZXJ0eVZhbHVlIGlzIG5vdCBhbiBvYmplY3RcbiAqICAgICBvbmVUb09uZU9uQ3JlYXRlLT4+cmVwb3NpdG9yeUZyb21UeXBlTWV0YWRhdGE6IG1vZGVsLCBrZXlcbiAqICAgICByZXBvc2l0b3J5RnJvbVR5cGVNZXRhZGF0YS0tPj5vbmVUb09uZU9uQ3JlYXRlOiBpbm5lclJlcG9cbiAqICAgICBvbmVUb09uZU9uQ3JlYXRlLT4+aW5uZXJSZXBvOiByZWFkKHByb3BlcnR5VmFsdWUpXG4gKiAgICAgaW5uZXJSZXBvLS0+Pm9uZVRvT25lT25DcmVhdGU6IHJlYWRcbiAqICAgICBvbmVUb09uZU9uQ3JlYXRlLT4+Y2FjaGVNb2RlbEZvclBvcHVsYXRlOiBjb250ZXh0LCBtb2RlbCwga2V5LCBwcm9wZXJ0eVZhbHVlLCByZWFkXG4gKiAgICAgb25lVG9PbmVPbkNyZWF0ZS0+Pm9uZVRvT25lT25DcmVhdGU6IHNldCBtb2RlbFtrZXldID0gcHJvcGVydHlWYWx1ZVxuICogICBlbHNlIHByb3BlcnR5VmFsdWUgaXMgYW4gb2JqZWN0XG4gKiAgICAgb25lVG9PbmVPbkNyZWF0ZS0+Pk1vZGVsOiBnZXQoZGF0YS5jbGFzcylcbiAqICAgICBNb2RlbC0tPj5vbmVUb09uZU9uQ3JlYXRlOiBjb25zdHJ1Y3RvclxuICogICAgIG9uZVRvT25lT25DcmVhdGUtPj5SZXBvc2l0b3J5OiBmb3JNb2RlbChjb25zdHJ1Y3RvcilcbiAqICAgICBSZXBvc2l0b3J5LS0+Pm9uZVRvT25lT25DcmVhdGU6IHJlcG9cbiAqICAgICBvbmVUb09uZU9uQ3JlYXRlLT4+cmVwbzogY3JlYXRlKHByb3BlcnR5VmFsdWUpXG4gKiAgICAgcmVwby0tPj5vbmVUb09uZU9uQ3JlYXRlOiBjcmVhdGVkXG4gKiAgICAgb25lVG9PbmVPbkNyZWF0ZS0+PmZpbmRQcmltYXJ5S2V5OiBjcmVhdGVkXG4gKiAgICAgZmluZFByaW1hcnlLZXktLT4+b25lVG9PbmVPbkNyZWF0ZTogcGtcbiAqICAgICBvbmVUb09uZU9uQ3JlYXRlLT4+Y2FjaGVNb2RlbEZvclBvcHVsYXRlOiBjb250ZXh0LCBtb2RlbCwga2V5LCBjcmVhdGVkW3BrXSwgY3JlYXRlZFxuICogICAgIG9uZVRvT25lT25DcmVhdGUtPj5vbmVUb09uZU9uQ3JlYXRlOiBzZXQgbW9kZWxba2V5XSA9IGNyZWF0ZWRbcGtdXG4gKiAgIGVuZFxuICpcbiAqICAgb25lVG9PbmVPbkNyZWF0ZS0tPj5DYWxsZXI6IHZvaWRcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG9uZVRvT25lT25DcmVhdGU8XG4gIE0gZXh0ZW5kcyBNb2RlbCxcbiAgUiBleHRlbmRzIFJlcG88TSwgRiwgQz4sXG4gIFYgZXh0ZW5kcyBSZWxhdGlvbnNNZXRhZGF0YSxcbiAgRiBleHRlbmRzIFJlcG9zaXRvcnlGbGFncyxcbiAgQyBleHRlbmRzIENvbnRleHQ8Rj4sXG4+KFxuICB0aGlzOiBSLFxuICBjb250ZXh0OiBDb250ZXh0PEY+LFxuICBkYXRhOiBWLFxuICBrZXk6IGtleW9mIE0sXG4gIG1vZGVsOiBNXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgcHJvcGVydHlWYWx1ZTogYW55ID0gbW9kZWxba2V5XTtcbiAgaWYgKCFwcm9wZXJ0eVZhbHVlKSByZXR1cm47XG5cbiAgaWYgKHR5cGVvZiBwcm9wZXJ0eVZhbHVlICE9PSBcIm9iamVjdFwiKSB7XG4gICAgY29uc3QgaW5uZXJSZXBvID0gcmVwb3NpdG9yeUZyb21UeXBlTWV0YWRhdGEoXG4gICAgICBtb2RlbCxcbiAgICAgIGtleSxcbiAgICAgIHRoaXMuYWRhcHRlci5hbGlhc1xuICAgICk7XG4gICAgY29uc3QgcmVhZCA9IGF3YWl0IGlubmVyUmVwby5yZWFkKHByb3BlcnR5VmFsdWUpO1xuICAgIGF3YWl0IGNhY2hlTW9kZWxGb3JQb3B1bGF0ZShjb250ZXh0LCBtb2RlbCwga2V5LCBwcm9wZXJ0eVZhbHVlLCByZWFkKTtcbiAgICAobW9kZWwgYXMgYW55KVtrZXldID0gcHJvcGVydHlWYWx1ZTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBjb25zdHJ1Y3RvciA9IE1vZGVsLmdldChkYXRhLmNsYXNzKTtcbiAgaWYgKCFjb25zdHJ1Y3RvcilcbiAgICB0aHJvdyBuZXcgSW50ZXJuYWxFcnJvcihgQ291bGQgbm90IGZpbmQgbW9kZWwgJHtkYXRhLmNsYXNzfWApO1xuICBjb25zdCByZXBvOiBSZXBvPGFueT4gPSBSZXBvc2l0b3J5LmZvck1vZGVsKGNvbnN0cnVjdG9yLCB0aGlzLmFkYXB0ZXIuYWxpYXMpO1xuICBjb25zdCBjcmVhdGVkID0gYXdhaXQgcmVwby5jcmVhdGUocHJvcGVydHlWYWx1ZSk7XG4gIGNvbnN0IHBrID0gZmluZFByaW1hcnlLZXkoY3JlYXRlZCkuaWQ7XG4gIGF3YWl0IGNhY2hlTW9kZWxGb3JQb3B1bGF0ZShjb250ZXh0LCBtb2RlbCwga2V5LCBjcmVhdGVkW3BrXSwgY3JlYXRlZCk7XG4gIChtb2RlbCBhcyBhbnkpW2tleV0gPSBjcmVhdGVkW3BrXTtcbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBvbmUtdG8tb25lIHJlbGF0aW9uc2hpcCB1cGRhdGVzXG4gKiBAc3VtbWFyeSBQcm