UNPKG

@decaf-ts/core

Version:

Core persistence module for the decaf framework

770 lines 120 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Repository = void 0; const db_decorators_1 = require("@decaf-ts/db-decorators"); const Adapter_1 = require("./../persistence/Adapter.cjs"); const decorator_validation_1 = require("@decaf-ts/decorator-validation"); const constants_1 = require("./../persistence/constants.cjs"); const constants_2 = require("./constants.cjs"); const reflection_1 = require("@decaf-ts/reflection"); const Sequence_1 = require("./../persistence/Sequence.cjs"); const utils_1 = require("./../identity/utils.cjs"); const decorators_1 = require("./../persistence/decorators.cjs"); const logging_1 = require("@decaf-ts/logging"); const ObserverHandler_1 = require("./../persistence/ObserverHandler.cjs"); const utils_2 = require("./../utils/index.cjs"); /** * @description Core repository implementation for database operations on models on a table by table way. * @summary Provides CRUD operations, querying capabilities, and observer pattern implementation for model persistence. * @template M - The model type that extends Model. * @template Q - The query type used by the adapter. * @template A - The adapter type for database operations. * @template F - The repository flags type. * @template C - The context type for operations. * @param {A} [adapter] - Optional adapter instance for database operations. * @param {Constructor<M>} [clazz] - Optional constructor for the model class. * @param {...any[]} [args] - Additional arguments for repository initialization. * @class Repository * @example * // Creating a repository for User model * const userRepo = Repository.forModel(User); * * // Using the repository for CRUD operations * const user = await userRepo.create(new User({ name: 'John' })); * const retrievedUser = await userRepo.read(user.id); * user.name = 'Jane'; * await userRepo.update(user); * await userRepo.delete(user.id); * * // Querying with conditions * const users = await userRepo * .select() * .where({ name: 'Jane' }) * .orderBy('createdAt', OrderDirection.DSC) * .limit(10) * .execute(); * @mermaid * sequenceDiagram * participant C as Client Code * participant R as Repository * participant A as Adapter * participant DB as Database * participant O as Observers * * C->>+R: create(model) * R->>R: createPrefix(model) * R->>+A: prepare(model) * A-->>-R: prepared data * R->>+A: create(table, id, record) * A->>+DB: Insert Operation * DB-->>-A: Result * A-->>-R: record * R->>+A: revert(record) * A-->>-R: model instance * R->>R: createSuffix(model) * R->>+O: updateObservers(table, CREATE, id) * O-->>-R: Notification complete * R-->>-C: created model */ class Repository extends db_decorators_1.Repository { static { this._cache = {}; } /** * @description Logger instance for this repository. * @summary Provides access to the logger for this repository instance. * @return {Logger} The logger instance. */ get log() { if (!this.logger) this.logger = logging_1.Logging.for(this); return this.logger; } /** * @description Adapter for database operations. * @summary Provides access to the adapter instance for this repository. * @template A - The adapter type. * @return {A} The adapter instance. * @throws {InternalError} If no adapter is found. */ get adapter() { if (!this._adapter) throw new db_decorators_1.InternalError(`No adapter found for this repository. did you use the @uses decorator or pass it in the constructor?`); return this._adapter; } /** * @description Table name for this repository's model. * @summary Gets the database table name associated with this repository's model. * @return {string} The table name. */ get tableName() { if (!this._tableName) this._tableName = Repository.table(this.class); return this._tableName; } /** * @description Primary key properties for this repository's model. * @summary Gets the sequence options containing primary key information. * @return {SequenceOptions} The primary key properties. */ get pkProps() { return super.pkProps; } // eslint-disable-next-line @typescript-eslint/no-unused-vars constructor(adapter, clazz, ...args) { super(clazz); this.observers = []; if (adapter) this._adapter = adapter; if (clazz) { Repository.register(clazz, this, this.adapter.alias); if (adapter) { const flavour = Reflect.getMetadata(Adapter_1.Adapter.key(constants_1.PersistenceKeys.ADAPTER), clazz); if (flavour && flavour !== adapter.flavour) throw new db_decorators_1.InternalError("Incompatible flavours"); (0, decorators_1.uses)(adapter.flavour)(clazz); } } [this.createAll, this.readAll, this.updateAll, this.deleteAll].forEach((m) => { const name = m.name; (0, db_decorators_1.wrapMethodWithContext)(this, this[name + "Prefix"], m, this[name + "Suffix"]); }); } /** * @description Creates a proxy with overridden repository flags. * @summary Returns a proxy of this repository with the specified flags overridden. * @param {Partial<F>} flags - The flags to override. * @return {Repository} A proxy of this repository with overridden flags. */ override(flags) { this.log .for(this.override) .debug(`Overriding repository flags with ${JSON.stringify(flags)}`); return new Proxy(this, { get: (target, p, receiver) => { const result = Reflect.get(target, p, receiver); if (p !== "_overrides") return result; return Object.assign({}, result, flags); }, }); } /** * @description Creates a new observer handler. * @summary Factory method for creating an observer handler instance. * @return {ObserverHandler} A new observer handler instance. */ ObserverHandler() { return new ObserverHandler_1.ObserverHandler(); } /** * @description Prepares a model for creation. * @summary Validates the model and prepares it for creation in the database. * @template M - The model type. * @param {M} model - The model to create. * @param {...any[]} args - Additional arguments. * @return The prepared model and context arguments. * @throws {ValidationError} If the model fails validation. */ async createPrefix(model, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.CREATE, this.class, args, this.adapter, this._overrides || {}); model = new this.class(model); await (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, model, db_decorators_1.OperationKeys.CREATE, db_decorators_1.OperationKeys.ON); const errors = await Promise.resolve(model.hasErrors(...(contextArgs.context.get("ignoredValidationProperties") || []))); if (errors) throw new db_decorators_1.ValidationError(errors.toString()); return [model, ...contextArgs.args]; } /** * @description Creates a model in the database. * @summary Persists a model instance to the database. * @param {M} model - The model to create. * @param {...any[]} args - Additional arguments. * @return {Promise<M>} The created model with updated properties. */ async create(model, ...args) { // eslint-disable-next-line prefer-const let { record, id, transient } = this.adapter.prepare(model, this.pk); record = await this.adapter.create(this.tableName, id, record, ...args); let c = undefined; if (args.length) c = args[args.length - 1]; return this.adapter.revert(record, this.class, this.pk, id, c && c.get("rebuildWithTransient") ? transient : undefined); } /** * @description Post-creation hook. * @summary Executes after a model is created to perform additional operations. * @param {M} model - The created model. * @param {C} context - The operation context. * @return {Promise<M>} The processed model. */ async createSuffix(model, context) { return super.createSuffix(model, context); } /** * @description Creates multiple models in the database. * @summary Persists multiple model instances to the database in a batch operation. * @param {M[]} models - The models to create. * @param {...any[]} args - Additional arguments. * @return {Promise<M[]>} The created models with updated properties. */ async createAll(models, ...args) { if (!models.length) return models; const prepared = models.map((m) => this.adapter.prepare(m, this.pk)); const ids = prepared.map((p) => p.id); let records = prepared.map((p) => p.record); records = await this.adapter.createAll(this.tableName, ids, records, ...args); return records.map((r, i) => this.adapter.revert(r, this.class, this.pk, ids[i])); } /** * @description Prepares multiple models for creation. * @summary Validates multiple models and prepares them for creation in the database. * @param {M[]} models - The models to create. * @param {...any[]} args - Additional arguments. * @return The prepared models and context arguments. * @throws {ValidationError} If any model fails validation. */ async createAllPrefix(models, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.CREATE, this.class, args, this.adapter, this._overrides || {}); if (!models.length) return [models, ...contextArgs.args]; const opts = Repository.getSequenceOptions(models[0]); let ids = []; if (opts.type) { if (!opts.name) opts.name = Sequence_1.Sequence.pk(models[0]); ids = await (await this.adapter.Sequence(opts)).range(models.length); } models = await Promise.all(models.map(async (m, i) => { m = new this.class(m); m[this.pk] = ids[i]; await (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, m, db_decorators_1.OperationKeys.CREATE, db_decorators_1.OperationKeys.ON); return m; })); const ignoredProps = contextArgs.context.get("ignoredValidationProperties") || []; const errors = await Promise.all(models.map((m) => Promise.resolve(m.hasErrors(...ignoredProps)))); const errorMessages = errors.reduce((accum, e, i) => { if (e) accum = typeof accum === "string" ? accum + `\n - ${i}: ${e.toString()}` : ` - ${i}: ${e.toString()}`; return accum; }, undefined); if (errorMessages) throw new db_decorators_1.ValidationError(errorMessages); return [models, ...contextArgs.args]; } /** * @description Prepares for reading a model by ID. * @summary Prepares the context and enforces decorators before reading a model. * @param {string} key - The primary key of the model to read. * @param {...any[]} args - Additional arguments. * @return The key and context arguments. */ async readPrefix(key, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.READ, this.class, args, this.adapter, this._overrides || {}); const model = new this.class(); model[this.pk] = key; await (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, model, db_decorators_1.OperationKeys.READ, db_decorators_1.OperationKeys.ON); return [key, ...contextArgs.args]; } /** * @description Reads a model from the database by ID. * @summary Retrieves a model instance from the database using its primary key. * @param {string|number|bigint} id - The primary key of the model to read. * @param {...any[]} args - Additional arguments. * @return {Promise<M>} The retrieved model instance. */ async read(id, ...args) { const m = await this.adapter.read(this.tableName, id, ...args); return this.adapter.revert(m, this.class, this.pk, id); } /** * @description Prepares for reading multiple models by IDs. * @summary Prepares the context and enforces decorators before reading multiple models. * @param {string[]|number[]} keys - The primary keys of the models to read. * @param {...any[]} args - Additional arguments. * @return The keys and context arguments. */ async readAllPrefix(keys, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.READ, this.class, args, this.adapter, this._overrides || {}); await Promise.all(keys.map(async (k) => { const m = new this.class(); m[this.pk] = k; return (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, m, db_decorators_1.OperationKeys.READ, db_decorators_1.OperationKeys.ON); })); return [keys, ...contextArgs.args]; } /** * @description Reads multiple models from the database by IDs. * @summary Retrieves multiple model instances from the database using their primary keys. * @param {string[]|number[]} keys - The primary keys of the models to read. * @param {...any[]} args - Additional arguments. * @return {Promise<M[]>} The retrieved model instances. */ async readAll(keys, ...args) { const records = await this.adapter.readAll(this.tableName, keys, ...args); return records.map((r, i) => this.adapter.revert(r, this.class, this.pk, keys[i])); } /** * @description Updates a model in the database. * @summary Persists changes to an existing model instance in the database. * @param {M} model - The model to update. * @param {...any[]} args - Additional arguments. * @return {Promise<M>} The updated model with refreshed properties. */ async update(model, ...args) { // eslint-disable-next-line prefer-const let { record, id, transient } = this.adapter.prepare(model, this.pk); record = await this.adapter.update(this.tableName, id, record, ...args); return this.adapter.revert(record, this.class, this.pk, id, transient); } /** * @description Prepares a model for update. * @summary Validates the model and prepares it for update in the database. * @param {M} model - The model to update. * @param {...any[]} args - Additional arguments. * @return The prepared model and context arguments. * @throws {InternalError} If the model has no primary key value. * @throws {ValidationError} If the model fails validation. */ async updatePrefix(model, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.UPDATE, this.class, args, this.adapter, this._overrides || {}); const pk = model[this.pk]; if (!pk) throw new db_decorators_1.InternalError(`No value for the Id is defined under the property ${this.pk}`); const oldModel = await this.read(pk, ...contextArgs.args); model = this.merge(oldModel, model); await (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, model, db_decorators_1.OperationKeys.UPDATE, db_decorators_1.OperationKeys.ON, oldModel); const errors = await Promise.resolve(model.hasErrors(oldModel, ...Repository.relations(this.class), ...(contextArgs.context.get("ignoredValidationProperties") || []))); if (errors) throw new db_decorators_1.ValidationError(errors.toString()); if (Repository.getMetadata(oldModel)) { if (!Repository.getMetadata(model)) Repository.setMetadata(model, Repository.getMetadata(oldModel)); } return [model, ...contextArgs.args]; } /** * @description Updates multiple models in the database. * @summary Persists changes to multiple existing model instances in the database in a batch operation. * @param {M[]} models - The models to update. * @param {...any[]} args - Additional arguments. * @return {Promise<M[]>} The updated models with refreshed properties. */ async updateAll(models, ...args) { const records = models.map((m) => this.adapter.prepare(m, this.pk)); const updated = await this.adapter.updateAll(this.tableName, records.map((r) => r.id), records.map((r) => r.record), ...args); return updated.map((u, i) => this.adapter.revert(u, this.class, this.pk, records[i].id)); } /** * @description Prepares multiple models for update. * @summary Validates multiple models and prepares them for update in the database. * @param {M[]} models - The models to update. * @param {...any[]} args - Additional arguments. * @return {Promise<any[]>} The prepared models and context arguments. * @throws {InternalError} If any model has no primary key value. * @throws {ValidationError} If any model fails validation. */ async updateAllPrefix(models, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.UPDATE, this.class, args, this.adapter, this._overrides || {}); const ids = models.map((m) => { const id = m[this.pk]; if (!id) throw new db_decorators_1.InternalError("missing id on update operation"); return id; }); const oldModels = await this.readAll(ids, ...contextArgs.args); models = models.map((m, i) => { m = this.merge(oldModels[i], m); if (Repository.getMetadata(oldModels[i])) { if (!Repository.getMetadata(m)) Repository.setMetadata(m, Repository.getMetadata(oldModels[i])); } return m; }); await Promise.all(models.map((m, i) => (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, m, db_decorators_1.OperationKeys.UPDATE, db_decorators_1.OperationKeys.ON, oldModels[i]))); const ignoredProps = contextArgs.context.get("ignoredValidationProperties") || []; const errors = await Promise.all(models.map((m, i) => Promise.resolve(m.hasErrors(oldModels[i], m, ...ignoredProps)))); const errorMessages = errors.reduce((accum, e, i) => { if (e) accum = typeof accum === "string" ? accum + `\n - ${i}: ${e.toString()}` : ` - ${i}: ${e.toString()}`; return accum; }, undefined); if (errorMessages) throw new db_decorators_1.ValidationError(errorMessages); models.forEach((m, i) => { if (Repository.getMetadata(oldModels[i])) { if (!Repository.getMetadata(m)) Repository.setMetadata(m, Repository.getMetadata(oldModels[i])); } }); return [models, ...contextArgs.args]; } /** * @description Prepares for deleting a model by ID. * @summary Prepares the context and enforces decorators before deleting a model. * @param {any} key - The primary key of the model to delete. * @param {...any[]} args - Additional arguments. * @return The key and context arguments. */ async deletePrefix(key, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.DELETE, this.class, args, this.adapter, this._overrides || {}); const model = await this.read(key, ...contextArgs.args); await (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, model, db_decorators_1.OperationKeys.DELETE, db_decorators_1.OperationKeys.ON); return [key, ...contextArgs.args]; } /** * @description Deletes a model from the database by ID. * @summary Removes a model instance from the database using its primary key. * @param {string|number|bigint} id - The primary key of the model to delete. * @param {...any[]} args - Additional arguments. * @return {Promise<M>} The deleted model instance. */ async delete(id, ...args) { const m = await this.adapter.delete(this.tableName, id, ...args); return this.adapter.revert(m, this.class, this.pk, id); } /** * @description Prepares for deleting multiple models by IDs. * @summary Prepares the context and enforces decorators before deleting multiple models. * @param {string[]|number[]} keys - The primary keys of the models to delete. * @param {...any[]} args - Additional arguments. * @return The keys and context arguments. */ async deleteAllPrefix(keys, ...args) { const contextArgs = await db_decorators_1.Context.args(db_decorators_1.OperationKeys.DELETE, this.class, args, this.adapter, this._overrides || {}); const models = await this.readAll(keys, ...contextArgs.args); await Promise.all(models.map(async (m) => { return (0, db_decorators_1.enforceDBDecorators)(this, contextArgs.context, m, db_decorators_1.OperationKeys.DELETE, db_decorators_1.OperationKeys.ON); })); return [keys, ...contextArgs.args]; } /** * @description Deletes multiple models from the database by IDs. * @summary Removes multiple model instances from the database using their primary keys. * @param {string[]|number[]} keys - The primary keys of the models to delete. * @param {...any[]} args - Additional arguments. * @return {Promise<M[]>} The deleted model instances. */ async deleteAll(keys, ...args) { const results = await this.adapter.deleteAll(this.tableName, keys, ...args); return results.map((r, i) => this.adapter.revert(r, this.class, this.pk, keys[i])); } /** * @description Implementation of the select method. * @summary Creates a query builder for the model with optional field selection. * @template S - The array type of select selectors. * @param [selector] - Optional fields to select. * @return A query builder. */ select(selector) { return this.adapter .Statement() .select(selector) .from(this.class); } /** * @description Executes a query with the specified conditions and options. * @summary Provides a simplified way to query the database with common query parameters. * @param {Condition<M>} condition - The condition to filter records. * @param orderBy - The field to order results by. * @param {OrderDirection} [order=OrderDirection.ASC] - The sort direction. * @param {number} [limit] - Optional maximum number of results to return. * @param {number} [skip] - Optional number of results to skip. * @return {Promise<M[]>} The query results as model instances. */ async query(condition, orderBy, order = constants_2.OrderDirection.ASC, limit, skip) { const sort = [orderBy, order]; const query = this.select().where(condition).orderBy(sort); if (limit) query.limit(limit); if (skip) query.offset(skip); return query.execute(); } /** * @description Registers an observer for this repository. * @summary Adds an observer that will be notified of changes to models in this repository. * @param {Observer} observer - The observer to register. * @param {ObserverFilter} [filter] - Optional filter to limit which events the observer receives. * @return {void} * @see {Observable#observe} */ observe(observer, filter) { if (!this.observerHandler) Object.defineProperty(this, "observerHandler", { value: this.ObserverHandler(), writable: false, }); const log = this.log.for(this.observe); const tableName = Repository.table(this.class); this.adapter.observe(this, (table) => tableName === table); log.verbose(`now observing ${this.adapter} filtering on table === ${tableName}`); this.observerHandler.observe(observer, filter); log.verbose(`Registered new observer ${observer.toString()}`); } /** * @description Unregisters an observer from this repository. * @summary Removes an observer so it will no longer receive notifications of changes. * @param {Observer} observer - The observer to unregister. * @return {void} * @throws {InternalError} If the observer handler is not initialized. * @see {Observable#unObserve} */ unObserve(observer) { if (!this.observerHandler) throw new db_decorators_1.InternalError("ObserverHandler not initialized. Did you register any observables?"); this.observerHandler.unObserve(observer); this.log .for(this.unObserve) .verbose(`Observer ${observer.toString()} removed`); if (!this.observerHandler.count()) { this.log.verbose(`No more observers registered for ${this.adapter}, unsubscribing`); this.adapter.unObserve(this); this.log.verbose(`No longer observing adapter ${this.adapter.flavour}`); } } /** * @description Notifies all observers of an event. * @summary Updates all registered observers with information about a database event. * @param {string} table - The table name where the event occurred. * @param {OperationKeys|BulkCrudOperationKeys|string} event - The type of event that occurred. * @param {EventIds} id - The ID or IDs of the affected records. * @param {...any[]} args - Additional arguments. * @return {Promise<void>} A promise that resolves when all observers have been notified. * @throws {InternalError} If the observer handler is not initialized. */ async updateObservers(table, event, id, ...args) { if (!this.observerHandler) throw new db_decorators_1.InternalError("ObserverHandler not initialized. Did you register any observables?"); this.log .for(this.updateObservers) .verbose(`Updating ${this.observerHandler.count()} observers for ${this}`); await this.observerHandler.updateObservers(this.log, table, event, Array.isArray(id) ? id.map((i) => Sequence_1.Sequence.parseValue(this.pkProps.type, i)) : Sequence_1.Sequence.parseValue(this.pkProps.type, id), ...args); } /** * @description Alias for updateObservers. * @summary Notifies all observers of an event (alias for updateObservers). * @param {string} table - The table name where the event occurred. * @param {OperationKeys|BulkCrudOperationKeys|string} event - The type of event that occurred. * @param {EventIds} id - The ID or IDs of the affected records. * @param {...any[]} args - Additional arguments. * @return {Promise<void>} A promise that resolves when all observers have been notified. */ async refresh(table, event, id, ...args) { return this.updateObservers(table, event, id, ...args); } /** * @description Creates or retrieves a repository for a model. * @summary Factory method that returns a repository instance for the specified model. * @template M - The model type that extends Model. * @template R - The repository type that extends Repo<M>. * @param {Constructor<M>} model - The model constructor. * @param {string} [defaultFlavour] - Optional default adapter flavour if not specified on the model. * @param {...any[]} [args] - Additional arguments to pass to the repository constructor. * @return {R} A repository instance for the model. * @throws {InternalError} If no adapter is registered for the flavour. */ static forModel(model, alias, ...args) { let repo; const _alias = alias || Reflect.getMetadata(Adapter_1.Adapter.key(constants_1.PersistenceKeys.ADAPTER), model); try { repo = this.get(model, _alias); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { repo = undefined; } if (repo instanceof Repository) return repo; const flavour = alias || Reflect.getMetadata(Adapter_1.Adapter.key(constants_1.PersistenceKeys.ADAPTER), model) || (repo && Reflect.getMetadata(Adapter_1.Adapter.key(constants_1.PersistenceKeys.ADAPTER), repo)); const adapter = flavour ? Adapter_1.Adapter.get(flavour) : undefined; if (!adapter) throw new db_decorators_1.InternalError(`No registered persistence adapter found flavour ${flavour}`); repo = repo || adapter.repository(); return new repo(adapter, model, ...args); } /** * @description Retrieves a repository for a model from the cache. * @summary Gets a repository constructor or instance for the specified model from the internal cache. * @template M - The model type that extends Model. * @param {Constructor<M>} model - The model constructor. * @return {Constructor<Repo<M>> | Repo<M>} The repository constructor or instance. * @throws {InternalError} If no repository is registered for the model. */ static get(model, alias) { let name = Repository.table(model); if (alias) { name = [name, alias].join(db_decorators_1.DefaultSeparator); } if (name in this._cache) return this._cache[name]; throw new db_decorators_1.InternalError(`Could not find repository registered under ${name}`); } /** * @description Registers a repository for a model. * @summary Associates a repository constructor or instance with a model in the internal cache. * @template M - The model type that extends Model. * @param {Constructor<M>} model - The model constructor. * @param {Constructor<Repo<M>> | Repo<M>} repo - The repository constructor or instance. * @throws {InternalError} If a repository is already registered for the model. */ static register(model, repo, alias) { let name = Repository.table(model); if (alias) { name = [name, alias].join(db_decorators_1.DefaultSeparator); } if (name in this._cache) throw new db_decorators_1.InternalError(`${name} already registered as a repository`); this._cache[name] = repo; } /** * @description Sets metadata on a model instance. * @summary Attaches metadata to a model instance using a non-enumerable property. * @template M - The model type that extends Model. * @param {M} model - The model instance. * @param {any} metadata - The metadata to attach to the model. */ static setMetadata(model, metadata) { Object.defineProperty(model, constants_1.PersistenceKeys.METADATA, { enumerable: false, configurable: true, writable: false, value: metadata, }); } /** * @description Gets metadata from a model instance. * @summary Retrieves previously attached metadata from a model instance. * @template M - The model type that extends Model. * @param {M} model - The model instance. * @return {any} The metadata or undefined if not found. */ static getMetadata(model) { const descriptor = Object.getOwnPropertyDescriptor(model, constants_1.PersistenceKeys.METADATA); return descriptor ? descriptor.value : undefined; } /** * @description Removes metadata from a model instance. * @summary Deletes the metadata property from a model instance. * @template M - The model type that extends Model. * @param {M} model - The model instance. */ static removeMetadata(model) { const descriptor = Object.getOwnPropertyDescriptor(model, constants_1.PersistenceKeys.METADATA); if (descriptor) delete model[constants_1.PersistenceKeys.METADATA]; } /** * @description Gets sequence options for a model's primary key. * @summary Retrieves the sequence configuration for a model's primary key from metadata. * @template M - The model type that extends Model. * @param {M} model - The model instance. * @return {SequenceOptions} The sequence options for the model's primary key. * @throws {InternalError} If no sequence options are defined for the model. */ static getSequenceOptions(model) { const pk = (0, db_decorators_1.findPrimaryKey)(model).id; const metadata = Reflect.getMetadata(Repository.key(db_decorators_1.DBKeys.ID), model, pk); if (!metadata) throw new db_decorators_1.InternalError("No sequence options defined for model. did you use the @pk decorator?"); return metadata; } /** * @description Gets all indexes defined on a model. * @summary Retrieves all index metadata from a model's property decorators. * @template M - The model type that extends Model. * @param {M | Constructor<M>} model - The model instance or constructor. * @return {Record<string, Record<string, IndexMetadata>>} A nested record of property names to index metadata. */ static indexes(model) { const indexDecorators = reflection_1.Reflection.getAllPropertyDecorators(model instanceof decorator_validation_1.Model ? model : new model(), db_decorators_1.DBKeys.REFLECT); return Object.entries(indexDecorators || {}).reduce((accum, [k, val]) => { const decs = val.filter((v) => v.key.startsWith(constants_1.PersistenceKeys.INDEX)); if (decs && decs.length) { for (const dec of decs) { const { key, props } = dec; accum[k] = accum[k] || {}; accum[k][key] = props; } } return accum; }, {}); } /** * @description Gets all relation properties defined on a model. * @summary Retrieves the names of all properties marked as relations in the model hierarchy. * @template M - The model type that extends Model. * @param {M | Constructor<M>} model - The model instance or constructor. * @return {string[]} An array of property names that are relations. */ static relations(model) { const result = []; let prototype = model instanceof decorator_validation_1.Model ? Object.getPrototypeOf(model) : model.prototype; while (prototype != null) { const props = prototype[constants_1.PersistenceKeys.RELATIONS]; if (props) { result.push(...props); } prototype = Object.getPrototypeOf(prototype); } return result; } /** * @description Gets the table name for a model. * @summary Retrieves the database table name associated with a model. * @template M - The model type that extends Model. * @param {M | Constructor<M>} model - The model instance or constructor. * @return {string} The table name for the model. */ static table(model) { return (0, utils_1.getTableName)(model); } /** * @description Gets the column name for a model attribute. * @summary Retrieves the database column name for a model property. * @template M - The model type that extends Model. * @param {M} model - The model instance. * @param {string} attribute - The attribute/property name. * @return {string} The column name for the attribute. */ static column(model, attribute) { const metadata = Reflect.getMetadata(Adapter_1.Adapter.key(constants_1.PersistenceKeys.COLUMN), model, attribute); return metadata ? metadata : attribute; } } exports.Repository = Repository; __decorate([ (0, utils_2.final)(), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Function]), __metadata("design:returntype", void 0) ], Repository.prototype, "observe", null); __decorate([ (0, utils_2.final)(), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], Repository.prototype, "unObserve", null); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVwb3NpdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZXBvc2l0b3J5L1JlcG9zaXRvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQUEsMkRBY2lDO0FBR2pDLDBEQUFpRDtBQUNqRCx5RUFBb0U7QUFDcEUsOERBQTJEO0FBQzNELCtDQUE2QztBQUc3QyxxREFBa0Q7QUFFbEQsNERBQW1EO0FBSW5ELG1EQUFpRDtBQUNqRCxnRUFBaUQ7QUFDakQsK0NBQW9EO0FBQ3BELDBFQUFpRTtBQUNqRSxnREFBaUM7QUFzQmpDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0RHO0FBQ0gsTUFBYSxVQU9YLFNBQVEsMEJBQVk7YUFHTCxXQUFNLEdBR2pCLEVBQUUsQUFIZSxDQUdkO0lBWVA7Ozs7T0FJRztJQUNILElBQUksR0FBRztRQUNMLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUFFLElBQUksQ0FBQyxNQUFNLEdBQUcsaUJBQU8sQ0FBQyxHQUFHLENBQUMsSUFBVyxDQUFDLENBQUM7UUFDekQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxJQUFjLE9BQU87UUFDbkIsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRO1lBQ2hCLE1BQU0sSUFBSSw2QkFBYSxDQUNyQixzR0FBc0csQ0FDdkcsQ0FBQztRQUNKLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILElBQWMsU0FBUztRQUNyQixJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVU7WUFBRSxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3JFLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILElBQXVCLE9BQU87UUFDNUIsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCw2REFBNkQ7SUFDN0QsWUFBWSxPQUFXLEVBQUUsS0FBc0IsRUFBRSxHQUFHLElBQVc7UUFDN0QsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBeERMLGNBQVMsR0FBZSxFQUFFLENBQUM7UUF5RG5DLElBQUksT0FBTztZQUFFLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO1FBQ3JDLElBQUksS0FBSyxFQUFFLENBQUM7WUFDVixVQUFVLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNyRCxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQ2pDLGlCQUFPLENBQUMsR0FBRyxDQUFDLDJCQUFlLENBQUMsT0FBTyxDQUFDLEVBQ3BDLEtBQUssQ0FDTixDQUFDO2dCQUNGLElBQUksT0FBTyxJQUFJLE9BQU8sS0FBSyxPQUFPLENBQUMsT0FBTztvQkFDeEMsTUFBTSxJQUFJLDZCQUFhLENBQUMsdUJBQXVCLENBQUMsQ0FBQztnQkFDbkQsSUFBQSxpQkFBSSxFQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUMvQixDQUFDO1FBQ0gsQ0FBQztRQUNELENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLE9BQU8sQ0FDcEUsQ0FBQyxDQUFDLEVBQUUsRUFBRTtZQUNKLE1BQU0sSUFBSSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDcEIsSUFBQSxxQ0FBcUIsRUFDbkIsSUFBSSxFQUNILElBQVksQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLEVBQzlCLENBQUMsRUFDQSxJQUFZLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxDQUMvQixDQUFDO1FBQ0osQ0FBQyxDQUNGLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxRQUFRLENBQUMsS0FBaUI7UUFDeEIsSUFBSSxDQUFDLEdBQUc7YUFDTCxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQzthQUNsQixLQUFLLENBQUMsb0NBQW9DLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3RFLE9BQU8sSUFBSSxLQUFLLENBQUMsSUFBSSxFQUFFO1lBQ3JCLEdBQUcsRUFBRSxDQUFDLE1BQW1CLEVBQUUsQ0FBa0IsRUFBRSxRQUFhLEVBQUUsRUFBRTtnQkFDOUQsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDO2dCQUNoRCxJQUFJLENBQUMsS0FBSyxZQUFZO29CQUFFLE9BQU8sTUFBTSxDQUFDO2dCQUN0QyxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMxQyxDQUFDO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7O09BSUc7SUFDTyxlQUFlO1FBQ3ZCLE9BQU8sSUFBSSxpQ0FBZSxFQUFFLENBQUM7SUFDL0IsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ2dCLEtBQUssQ0FBQyxZQUFZLENBQ25DLEtBQVEsRUFDUixHQUFHLElBQVc7UUFFZCxNQUFNLFdBQVcsR0FBRyxNQUFNLHVCQUFPLENBQUMsSUFBSSxDQUNwQyw2QkFBYSxDQUFDLE1BQU0sRUFDcEIsSUFBSSxDQUFDLEtBQUssRUFDVixJQUFJLEVBQ0osSUFBSSxDQUFDLE9BQU8sRUFDWixJQUFJLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FDdEIsQ0FBQztRQUNGLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDOUIsTUFBTSxJQUFBLG1DQUFtQixFQUN2QixJQUFJLEVBQ0osV0FBVyxDQUFDLE9BQU8sRUFDbkIsS0FBSyxFQUNMLDZCQUFhLENBQUMsTUFBTSxFQUNwQiw2QkFBYSxDQUFDLEVBQUUsQ0FDakIsQ0FBQztRQUVGLE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FDbEMsS0FBSyxDQUFDLFNBQVMsQ0FDYixHQUFHLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsNkJBQTZCLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FDbEUsQ0FDRixDQUFDO1FBQ0YsSUFBSSxNQUFNO1lBQUUsTUFBTSxJQUFJLCtCQUFlLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFFekQsT0FBTyxDQUFDLEtBQUssRUFBRSxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFRLEVBQUUsR0FBRyxJQUFXO1FBQ25DLHdDQUF3QztRQUN4QyxJQUFJLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3JFLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQ3hFLElBQUksQ0FBQyxHQUFrQixTQUFTLENBQUM7UUFDakMsSUFBSSxJQUFJLENBQUMsTUFBTTtZQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQU0sQ0FBQztRQUNoRCxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUN4QixNQUFNLEVBQ04sSUFBSSxDQUFDLEtBQUssRUFDVixJQUFJLENBQUMsRUFBRSxFQUNQLEVBQUUsRUFDRixDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FDM0QsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDTSxLQUFLLENBQUMsWUFBWSxDQUFDLEtBQVEsRUFBRSxPQUFVO1FBQzlDLE9BQU8sS0FBSyxDQUFDLFlBQVksQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNNLEtBQUssQ0FBQyxTQUFTLENBQUMsTUFBVyxFQUFFLEdBQUcsSUFBVztRQUNsRCxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU07WUFBRSxPQUFPLE1BQU0sQ0FBQztRQUNsQyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDckUsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3RDLElBQUksT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM1QyxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FDcEMsSUFBSSxDQUFDLFNBQVMsRUFDZCxHQUEwQixFQUMxQixPQUFPLEVBQ1AsR0FBRyxJQUFJLENBQ1IsQ0FBQztRQUNGLE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUMxQixJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQW9CLENBQUMsQ0FDdkUsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ2dCLEtBQUssQ0FBQyxlQUFlLENBQUMsTUFBVyxFQUFFLEdBQUcsSUFBVztRQUNsRSxNQUFNLFdBQVcsR0FBRyxNQUFNLHVCQUFPLENBQUMsSUFBSSxDQUNwQyw2QkFBYSxDQUFDLE1BQU0sRUFDcEIsSUFBSSxDQUFDLEtBQUssRUFDVixJQUFJLEVBQ0osSUFBSSxDQUFDLE9BQU8sRUFDWixJQUFJLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FDdEIsQ0FBQztRQUNGLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTTtZQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekQsTUFBTSxJQUFJLEdBQUcsVUFBVSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3RELElBQUksR0FBRyxHQUE2QyxFQUFFLENBQUM7UUFDdkQsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDZCxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUk7Z0JBQUUsSUFBSSxDQUFDLElBQUksR0FBRyxtQkFBUSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNuRCxHQUFHLEdBQUcsTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3ZFLENBQUM7UUFFRCxNQUFNLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUN4QixNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDeEIsQ0FBQyxHQUFHLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN0QixDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQWUsQ0FBQztZQUNsQyxNQUFNLElBQUEsbUNBQW1CLEVBQ3ZCLElBQUksRUFDSixXQUFXLENBQUMsT0FBTyxFQUNuQixDQUFDLEVBQ0QsNkJBQWEsQ0FBQyxNQUFNLEVBQ3BCLDZCQUFhLENBQUMsRUFBRSxDQUNqQixDQUFDO1lBQ0YsT0FBTyxDQUFDLENBQUM7UUFDWCxDQUFDLENBQUMsQ0FDSCxDQUFDO1FBRUYsTUFBTSxZQUFZLEdBQ2hCLFdBQVcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLDZCQUE2QixDQUFDLElBQUksRUFBRSxDQUFDO1FBRS9ELE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FDOUIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLEdBQUcsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUNqRSxDQUFDO1FBRUYsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQXlCLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ3RFLElBQUksQ0FBQztnQkFDSCxLQUFLO29CQUNILE9BQU8sS0FBSyxLQUFLLFFBQVE7d0JBQ3ZCLENBQUMsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFO3dCQUN0QyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUM7WUFDbkMsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFFZCxJQUFJLGFBQWE7WUFBRSxNQUFNLElBQUksK0JBQWUsQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUM1RCxPQUFPLENBQUMsTUFBTSxFQUFFLEdBQUcsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDZ0IsS0FBSyxDQUFDLFVBQVUsQ0FBQyxHQUFXLEVBQUUsR0FBRyxJQUFXO1FBQzdELE1BQU0sV0FBVyxHQUFHLE1BQU0sdUJBQU8sQ0FBQyxJQUFJLENBQ3BDLDZCQUFhLENBQUMsSUFBSSxFQUNsQixJQUFJLENBQUMsS0FBSyxFQUNWLElBQUksRUFDSixJQUFJLENBQUMsT0FBTyxFQUNaLElBQUksQ0FBQyxVQUFVLElBQUksRUFBRSxDQUN0QixDQUFDO1FBQ0YsTUFBTSxLQUFLLEdBQU0sSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDbEMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsR0FBRyxHQUFpQixDQUFDO1FBQ25DLE1BQU0sSUFBQSxtQ0FBbUIsRUFDdkIsSUFBSSxFQUNKLFdBQVcsQ0FBQyxPQUFPLEVBQ25CLEtBQUssRUFDTCw2QkFBYSxDQUFDLElBQUksRUFDbEIsNkJBQWEsQ0FBQyxFQUFFLENBQ2pCLENBQUM7UUFDRixPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQTRCLEVBQUUsR0FBRyxJQUFXO1FBQ3JELE1BQU0sQ0FBQyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUMvRCxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFJLENBQUMsRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNnQixLQUFLLENBQUMsYUFBYSxDQUNwQyxJQUF5QixFQUN6QixHQUFHLElBQVc7UUFFZCxNQUFNLFdBQVcsR0FBRyxNQUFNLHVCQUFPLENBQUMsSUFBSSxDQUNwQyw2QkFBYSxDQUFDLElBQUksRUFDbEIsSUFBSSxDQUFDLEtBQUssRUFDVixJQUFJLEVBQ0osSUFBSSxDQUFDLE9BQU8sRUFDWixJQUFJLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FDdEIsQ0FBQztRQUNGLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FDZixJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNuQixNQUFNLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUMzQixDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQWUsQ0FBQztZQUM3QixPQUFPLElBQUEsbUNBQW1CLEVBQ3hCLElBQUksRUFDSixXQUFXLENBQUMsT0FBTyxFQUNuQixDQUFDLEVBQ0QsNkJBQWEsQ0FBQyxJQUFJLEVBQ2xCLDZCQUFhLENBQUMsRUFBRSxDQUNqQixDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQ0gsQ0FBQztRQUNGLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNNLEtBQUssQ0FBQyxPQUFPLENBQ3BCLElBQXlCLEVBQ3pCLEdBQUcsSUFBVztRQUVkLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUMxRSxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FDckQsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQVEsRUFBRSxHQUFHLElBQVc7UUFDbkMsd0NBQXdDO1FBQ3hDLElBQUksRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDckUsTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7UUFDeEUsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBSSxNQUFNLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDZ0IsS0FBSyxDQUFDLFlBQVksQ0FDbkMsS0FBUSxFQUNSLEdBQUcsSUFBVztRQUVkLE1BQU0sV0FBVyxHQUFHLE1BQU0sdUJBQU8sQ0FBQyxJQUFJLENBQ3BDLDZCQUFhLENBQUMsTUFBTSxFQUNwQixJQUFJLENBQUMsS0FBSyxFQUNWLElBQUksRUFDSixJQUFJLENBQUMsT0FBTyxFQUNaLElBQUksQ0FBQyxVQUFVLElBQUksRUFBRSxDQUN0QixDQUFDO1FBQ0YsTUFBTSxFQUFFLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQVcsQ0FBQztRQUNwQyxJQUFJLENBQUMsRUFBRTtZQUNMLE1BQU0sSUFBSSw2QkFBYSxDQUNyQixxREFBcUQsSUFBSSxDQUFDLEVBQVksRUFBRSxDQUN6RSxDQUFDO1FBQ0osTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMxRCxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDcEMsTUFBTSxJQUFBLG1DQUFtQixFQUN2QixJQUFJLEVBQ0osV0FBVyxDQUFDLE9BQU8sRUFDbkIsS0FBSyxFQUNMLDZCQUFhLENBQUMsTUFBTSxFQUNwQiw2QkFBYSxDQUFDLEVBQUUsRUFDaEIsUUFBUSxDQUNULENBQUM7UUFFRixNQUFNLE1BQU0sR0FBRyxNQUFNLE9BQU8sQ0FBQyxPQUFPLENBQ2xDLEtBQUssQ0FBQyxTQUFTLENBQ2IsUUFBUSxFQUNSLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUF