UNPKG

ts-db-helper

Version:
1,514 lines (1,488 loc) 182 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs/Subject'), require('rxjs/add/operator/share'), require('rxjs/Observable'), require('rxjs/add/observable/combineLatest'), require('rxjs/add/operator/map'), require('rxjs/add/operator/switchMap'), require('rxjs/add/Observable/from'), require('rxjs/add/Observable/empty'), require('rxjs/add/operator/catch')) : typeof define === 'function' && define.amd ? define(['exports', 'rxjs/Subject', 'rxjs/add/operator/share', 'rxjs/Observable', 'rxjs/add/observable/combineLatest', 'rxjs/add/operator/map', 'rxjs/add/operator/switchMap', 'rxjs/add/Observable/from', 'rxjs/add/Observable/empty', 'rxjs/add/operator/catch'], factory) : (factory((global.TsDbHelper = {}),global.Rx,global.Rx.Observable.prototype,global.Rx)); }(this, (function (exports,Subject,share,Observable) { 'use strict'; /** * @class DbHelperModuleConfig is a config model for the module. * * @author Olivier Margarit * @since 0.1 */ var DbHelperModuleConfig = /** @class */ (function () { function DbHelperModuleConfig() { /** * @property version, the model version */ this.version = ''; /** * @property autoIncrementVersion, flag to auto increment version with the number of * model declared, this is a trick for developpement issues due to compilator import * optimisation. Your model, even if it is define will not be imported in the project * until it is used. * To prevent misunderstanding of what is happening this option aim to automatically * call model migration on model use and mange new table creation without manually * increment the model version. */ this.autoIncrementVersion = true; } return DbHelperModuleConfig; }()); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } /** * @class QueryError is thrown when a query fails * * @see Error * @author Olivier Margarit * @since 0.1 */ var QueryError = /** @class */ (function () { /** * @public * @constructor * @param {string} message message explaining in details error * @param {string} query query text that did failed execution * @param {string} params query params of the failed query */ function QueryError(message, query, params) { var _newTarget = this.constructor; this.message = message; this.query = query; this.params = params; Object.setPrototypeOf(this, _newTarget.prototype); Error.captureStackTrace(this, this.constructor); this.message = message; this.name = 'query error'; } /** * @public * @method toString convert error to string * * @return {string} string represation of the error */ QueryError.prototype.toString = function () { return this.name + '\n' + this.message + (this.query ? '\nquery: ' + this.query : '') + (this.params ? '\nparams: ' + this.params : ''); }; return QueryError; }()); var DefaultLogger = /** @class */ (function () { function DefaultLogger() { this.levelValue = DefaultLogger.levels[3]; } Object.defineProperty(DefaultLogger.prototype, "level", { set: function (level) { if (DefaultLogger.levels.indexOf(level) < 0) { throw new Error('level \'' + level + '\' is not a valid log level'); } }, enumerable: true, configurable: true }); DefaultLogger.prototype.isLogLevelActivated = function (target) { return DefaultLogger.levels.indexOf(this.levelValue) <= DefaultLogger.levels.indexOf(target); }; DefaultLogger.prototype.log = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.isLogLevelActivated('verbose')) { console.log.apply(console, args); } }; DefaultLogger.prototype.trace = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.isLogLevelActivated('trace')) { console.trace.apply(console, args); } }; DefaultLogger.prototype.debug = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.isLogLevelActivated('debug')) { console.log.apply(console, args); } }; DefaultLogger.prototype.info = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.isLogLevelActivated('info')) { console.info.apply(console, args); } }; DefaultLogger.prototype.warn = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.isLogLevelActivated('warn')) { console.warn.apply(console, args); } }; DefaultLogger.prototype.error = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.isLogLevelActivated('error')) { console.error.apply(console, args); } }; DefaultLogger.prototype.fatal = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.isLogLevelActivated('fatal')) { console.error.apply(console, args); } }; DefaultLogger.levels = ['verbose', 'debug', 'trace', 'info', 'warn', 'error', 'fatal']; return DefaultLogger; }()); /** * @class UnsatisfiedRequirementError is thrown when a requirement is unsatisfied * * @see Error * @author Olivier Margarit * @since 0.1 */ var UnsatisfiedRequirementError = /** @class */ (function () { /** * @public * @constructor * @param {string} message message explaining in details error */ function UnsatisfiedRequirementError(message) { var _newTarget = this.constructor; this.message = message; Object.setPrototypeOf(this, _newTarget.prototype); Error.captureStackTrace(this, this.constructor); this.message = message; this.name = 'unsatisfied requirement error'; } /** * @public * @method toString convert error to string * * @return {string} string represation of the error */ UnsatisfiedRequirementError.prototype.toString = function () { return this.name + '\n' + this.message; }; return UnsatisfiedRequirementError; }()); /** * @public * @class PendingDbQuery * * @description * This class is a part of private API * it is a combination of dbQuery and linked observer to stack the query * during connector activation * * @author Olivier Margarit * @since 0.1 */ var PendingDbQuery = /** @class */ (function () { /** * @public * @constructor pending query constructor to create new stackable query * waiting for the connector to be ready * * @param {DbQuery} dbQuery db query object with all query informations * see the class documentation * @param {Observable<any>} observer the observer to notify the subscribers query result */ function PendingDbQuery(dbQuery, observer) { this.dbQuery = dbQuery; this.observer = observer; } return PendingDbQuery; }()); /** * @private * @class QueryManager * * @description * This class is a singleton manging query * This manager has not to be exposed, it is used to handle queries * with the connector. It stack it during connector is not ready and * release it when connector ca query * * @author Olivier Margarit * @since 0.1 */ var QueryManager = /** @class */ (function () { /** * @private * @constructor private constructor to preserve from other instance creation */ function QueryManager() { /** * @private * @property {Array<PendingDbQuery>} pendingDbQueries stack of queries to fire when the * connector is ready */ this.pendingDbQueries = []; /** * @private * @property {Array<PendingDbQuery>} pendingBatchDbQueries stack of queries to fire in a single transaction */ this.pendingBatchDbQueries = []; /** * @private * @property {boolean} isReady flag that should pas to true only when * query can be sent */ this.isReady = false; /** * @private * @property {boolean} isInitializationFailed flag to reject all queries * in case of connector activation failure */ this.isInitializationFailed = false; this.supportRowidValue = false; } Object.defineProperty(QueryManager.prototype, "logger", { get: function () { if (!this.loggerValue) { this.loggerValue = new DefaultLogger(); } return this.loggerValue; }, enumerable: true, configurable: true }); Object.defineProperty(QueryManager.prototype, "supportRowid", { get: function () { return this.supportRowidValue; }, enumerable: true, configurable: true }); /** * @static * @public * @method init is a part of the private API, config is submitted to * pass connector, model migration and other config. see {@link NgDbHelperModuleConfig} * for more informations * * @param {NgDbHelperModuleConfig} config the module configuration with connector instance and model migration * * @return {QueryManager} the initialized instance */ QueryManager.init = function (config) { var instance = QueryManager.getInstance(); instance.queryConnector = config.queryConnector; instance.modelMigration = config.modelMigration; instance.supportRowidValue = config.queryConnector.supportRowid; if (config.logger) { instance.loggerValue = config.logger; } ModelManager.version = config.version; if (config.autoIncrementVersion && ModelManager.version) { // compute auto upgrade version ModelManager.version += '.' + ModelManager.getInstance().getModelCount(); } instance.queryConnector.onReady().subscribe(function (ready) { if (ready) { instance.onQueryConnectorReady(); } else { instance.onInitializationFailure(null); } }, instance.onInitializationFailure); return instance; }; /** * @static * @public * @method getInstance to get the unique QueryManager instance * * @return {QueryManager} the instance */ QueryManager.getInstance = function () { return QueryManager.instance; }; /** * @private * @method onQueryConnectorReady is called when query connector is ready to * manage model migration then dequeuing each pending queries */ QueryManager.prototype.onQueryConnectorReady = function () { var _this = this; if (this.queryConnector && this.modelMigration) { var modelMigration_1 = this.modelMigration; this.queryConnector.getDbVersion().subscribe(function (version) { _this.logger.info('old version: ' + version); var dataModel = ModelManager.getInstance().getDataModel(); dataModel.version = ModelManager.version; _this.logger.info('new version: ' + dataModel.version); var isFailed = false; if (!version) { _this.logger.info('call init data model migration'); modelMigration_1.initModel(dataModel).subscribe(function () { _this.logger.info('init data model migration successed'); }, function (err) { isFailed = err; _this.logger.warn('init data model migration failed'); }, function () { if (isFailed) { _this.onInitializationFailure(isFailed); } else { _this.dequeuePendingRequest(); } }); } else if (version !== ModelManager.version) { _this.logger.info('call upgrade data model migration'); modelMigration_1.upgradeModel(dataModel, version).subscribe(function () { _this.logger.info('upgrade data model migration successed'); }, function (err) { isFailed = err; _this.logger.warn('upgrade data model migration failed'); }, function () { if (isFailed) { _this.onInitializationFailure(isFailed); } else { _this.dequeuePendingRequest(); } }); } else { _this.dequeuePendingRequest(); } }, function (err) { return _this.onInitializationFailure(err); }); } else { throw (new UnsatisfiedRequirementError('ModelMigration or QueryConnector object is missing, check and fix NgDbHelperModule configuration !')); } }; /** * @private * @method onInitializationFailure is called on initilization failure to cancel * all started queries and nexts * * @param {Error} err the error return by the initialization failure */ QueryManager.prototype.onInitializationFailure = function (err) { this.logger.error(err); this.isInitializationFailed = true; this.dequeuePendingRequest(); }; /** * @private * @method dequeuePendingRequest is called to dequeue pending request */ QueryManager.prototype.dequeuePendingRequest = function () { while (this.pendingDbQueries.length) { var pendingDbQuery = this.pendingDbQueries.shift(); if (pendingDbQuery) { this.executeQuery(pendingDbQuery.dbQuery, pendingDbQuery.observer); } } this.isReady = true; }; /** * @public * @method startBatch start transaction for multiple queries * @param {any} locker the locker that can fire the transaction if none is set */ QueryManager.prototype.startBatch = function (locker) { if (!this.batchLock) { this.batchLock = locker; } }; /** * @public * @method query private API to start queries, it check if queries can be executed * If initialization failed, error is sent back. If connector is not ready, query will * be stack until connector ready signal * * @param {DbQuery} dbQuery query information with params * * @return {Observable<QueryResult<any>>} in case of success, {@link QueryResult<any>} is passed * in case of failure, {@link QueryError} is passed */ QueryManager.prototype.query = function (dbQuery) { var subject = new Subject.Subject(); if (this.batchLock) { this.pendingBatchDbQueries.push({ dbQuery: dbQuery, observer: subject }); } else if (this.isReady) { this.executeQuery(dbQuery, subject); } else { this.pendingDbQueries.push(new PendingDbQuery(dbQuery, subject)); } return subject; }; /** * @public * @method queryBatch should only be called from {@link QueryBatch} function. * * @param locker the locker passed to startBatch that allow queries releasing * * @return {Observable<QueryResult<any>>} in case of success, {@link QueryResult<any>} is passed * in case of failure, {@link QueryError} is passed * * @since 0.2 */ QueryManager.prototype.execBatch = function (locker) { var queries = []; var observers = []; if (!this.batchSubject) { this.batchSubject = new Subject.Subject(); } if (this.batchLock === locker) { this.batchLock = null; } else { return this.batchSubject; } while (this.pendingBatchDbQueries.length) { var pendingQuery = this.pendingBatchDbQueries.shift(); queries.push(pendingQuery.dbQuery); observers.push(pendingQuery.observer); } var obs = this.queryConnector.queryBatch(queries); obs.map(function (res) { for (var _i = 0, observers_1 = observers; _i < observers_1.length; _i++) { var observer = observers_1[_i]; observer.next({ insertId: undefined, rowsAffected: 0, rows: { length: 0, item: function (index) { return null; }, toArray: function () { return []; } } }); observer.complete(); } return res; }); obs.share().subscribe(this.batchSubject); this.batchSubject = undefined; return obs; }; /** * @private * @method executeQuery should be called only when connector is ready * * @param {DbQuery} dbQuery query information with params * @param {Observer} observer observer to manage query callback * * @return {Observable<QueryResult<any>>} in case of success, {@link QueryResult<any>} is passed * in case of failure, {@link QueryError} is passed */ QueryManager.prototype.executeQuery = function (dbQuery, observer) { if (this.isInitializationFailed) { var error = new QueryError('query manager initialization did failed', dbQuery.query, dbQuery.params ? dbQuery.params.join(', ') : ''); observer.error(error); } else { if (this.queryConnector) { this.queryConnector.query(dbQuery).subscribe(observer); } else { throw (new UnsatisfiedRequirementError('QueryConnector object is missing, check and fix NgDbHelperModule configuration !')); } } }; /** * @static * @private * @property {QueryManager} instance private reference to the model manager instance */ QueryManager.instance = new QueryManager(); return QueryManager; }()); /** * @public * @class QueryCount * * @description * For design reasons this class should not be used directly. * Prefer use {@link Count} function. * * @param T @extends DbHelperModel a model declared with table and column annotations * * @example * ```typescript * // count todos * Count(Select(Todo).where({isDone: false}})).exec().subscribe((cout: number) => { * // do something with the result... * }, (err) => { * // do something with the error... * }); * ``` * * @author Olivier Margarit * @since 0.2 */ var QueryCount = /** @class */ (function () { /** * @public * @constructor create instance of QueryCount * @param querySelect the query to count the result */ function QueryCount(querySelect) { this.querySelect = querySelect; } /** * @public * @method build should be removed to be a part of the private API * * @return {DbQuery} of the query with the string part and * clauses params. */ QueryCount.prototype.build = function () { return this.querySelect.copy().projection(['count(*)']).build(); }; /** * @public * @method exec to execute the query and asynchronously retreive result. * * @return {Observable<number>} observable to subscribe and returning a count number as result */ QueryCount.prototype.exec = function () { var dbQuery = this.build(); return QueryManager.getInstance().query(dbQuery).map(function (qr) { if (qr.rows.length) { var key = 'count(*)'; return qr.rows.item(0)[key]; } else { throw new QueryError('no result error...', dbQuery.query, dbQuery.params.join(', ')); } }); }; return QueryCount; }()); /** * @public * @function Count * * @description * function helper to count element that a query could return * * @param T @extends DbHelperModel a model declared with table and column annotations * * @example * ```typescript * // count todos * Count(Select(Todo).where({isDone: false}})).exec().subscribe(count: number) => { * // do something with the result... * }, (err) => { * // do something with the error... * }); * ``` * * @return {QueryCount<T>} instance * * @author Olivier Margarit * @since 0.2 */ function Count(select) { return new QueryCount(select); } /** * @public * @class ShadowValue * * @description * This class is the data hidden model that link visible model to data store real value. * this informations are used in the shadow of the class model interface * * @author Olivier Margarit * @since 0.2 */ var ShadowValue = /** @class */ (function () { function ShadowValue() { } return ShadowValue; }()); /** * @public * @class ClauseComparators * * @description * list of managed comparators. This class should be converted to an enum in a furture version * * @author Olivier Margarit * * @since 0.2 */ var ClauseComparators = /** @class */ (function () { function ClauseComparators() { } /** * @public * @static * @method isKeyOf check if string is a valid key of ClauseComparator, this check * is not case sensitive * * @param {string} val the value to check * * @return {boolean} is true if key will return a valid value with {@link ClauseComparator.valueOf} */ ClauseComparators.isKeyOf = function (val) { return ClauseComparators.hasOwnProperty(val.toUpperCase()); }; /** * @public * @static * @method valueOf get value of ClauseComparator by using a valid key val. * * @param {string} val the value key * * @return {boolean} the comparator linked to the key */ ClauseComparators.valueOf = function (val) { return ClauseComparators[val.toUpperCase()]; }; /** * @public * @constant {string} DIFF the not equal comparator "!=" */ ClauseComparators.DIFF = '!='; /** * @public * @constant {string} LT the lower than comparator "<" */ ClauseComparators.LT = '<'; /** * @public * @constant {string} LTE the lower than or equals comparator "<=" */ ClauseComparators.LTE = '<='; /** * @public * @constant {string} GT the greater than comparator ">" */ ClauseComparators.GT = '>'; /** * @public * @constant {string} GTE the greater than equal comparator ">=" */ ClauseComparators.GTE = '>='; /** * @public * @constant {string} LIKE the like comparator "LIKE" */ ClauseComparators.LIKE = 'LIKE'; /** * @public * @constant {string} IN the in comparator "IN" */ ClauseComparators.IN = 'IN'; /** * @public * @default * @constant {string} EQ the equal comparator "=" */ ClauseComparators.EQ = '='; return ClauseComparators; }()); /** * @class UnsatisfiedRequirementError is thrown when a requirement is unsatisfied * * @see Error * @author Olivier Margarit * @since 0.1 */ var NotImplementedError = /** @class */ (function () { /** * @public * @constructor * @param {string} message message explaining in details error */ function NotImplementedError(message) { var _newTarget = this.constructor; this.message = message; Object.setPrototypeOf(this, _newTarget.prototype); Error.captureStackTrace(this, this.constructor); this.message = message; this.name = 'not implemented error'; } /** * @public * @method toString convert error to string * * @return {string} string represation of the error */ NotImplementedError.prototype.toString = function () { return this.name + '\n' + this.message; }; return NotImplementedError; }()); /** * @private * @class RelationType * * @description * the list of relations type. This class should be converted to an enum in future version * * @author Olivier Margarit * * @since 0.2 */ var RelationType = /** @class */ (function () { function RelationType() { } /** * @public * @constant {string} ONE_TO_MANY the one to many relation */ RelationType.ONE_TO_MANY = 'OneToMany'; /** * @public * @constant {string} ONE_TO_ONE the one to one relation */ RelationType.ONE_TO_ONE = 'OneToOne'; /** * @public * @constant {string} MANY_TO_MANY the many to many relation */ RelationType.MANY_TO_MANY = 'ManyToMany'; /** * @public * @constant {string} MANY_TO_ONE the many to one relation */ RelationType.MANY_TO_ONE = 'ManyToOne'; return RelationType; }()); /** * @public * @class DbColumn * * @description * This class is a column model * to help model migration to do his soup * * @author Olivier Margarit * @since 0.1 */ var DbColumn = /** @class */ (function () { /** * @public * @constructor Create column instance * * @param {string} name column name */ function DbColumn(name) { /** * @public * @property {boolean} primaryKey define the column as primary key of the table, * the default value is false */ this.primaryKey = false; /** * @public * @property {boolean} autoIncrement define if the column value is auto incremented * default value is false */ this.autoIncrement = false; /** * @public * @property {boolean} unique define if column value should be unique. Default value * is false */ this.unique = false; /** * @public * @property {boolean} indexed define if column value should be indexed. Default value * is false */ this.indexed = false; /** * @public * @property {string} type define type of the column, type must be compatible with * the field type plus the sqlite manged type */ this.type = 'string'; /** * @public * @property {string} foreignTable foreign table name */ this.foreignTable = null; /** * @public * @property {string} foreignKey foreign key linked to current key */ this.foreignKey = null; /** * @public * @property {string} foreignField field name of the foreign model */ this.foreignField = null; /** * @public * @property {any} defaultValue the default value of the column */ this.defaultValue = undefined; if (name) { this.name = name; this.field = this.name; } } /** * @public * @method configure configure the column from configurator model * * @param {ColumnConfig} config the configurator object * * @since 0.2 */ DbColumn.prototype.configure = function (config) { if (config.name) { this.name = config.name; } if (config.type) { this.type = config.type; } if (config.primaryKey !== undefined) { this.primaryKey = config.primaryKey; } if (config.unique !== undefined) { this.unique = config.unique; } if (config.indexed !== undefined) { this.indexed = config.indexed; } if (config.autoIncrement !== undefined) { this.autoIncrement = config.autoIncrement; } }; /** * @public * @method fromAlias get aliased column * * @param {string} alias the alias label * * @return {DbColumn} the aliased column * * @since 0.2 */ DbColumn.prototype.fromAlias = function (alias) { var aliasColumn = new DbColumn(alias + '.' + this.name); aliasColumn.field = alias + '.' + this.field; aliasColumn.type = this.type; aliasColumn.primaryKey = this.primaryKey; aliasColumn.unique = this.unique; aliasColumn.indexed = this.indexed; aliasColumn.autoIncrement = this.autoIncrement; return aliasColumn; }; return DbColumn; }()); /** * @private * @class ModelResult * * @description * This class is private part of the API. * A specific wrapper to convert QueryResult to typed QueryResult on demand * * @param T @extends {@link DbHelperModel}, a model declared with table and * column annotations * * @author Olivier Margarit * @since 0.1 */ var ModelResult = /** @class */ (function () { /** * @public * @constructor this is a private API and should not be available for integrators * * @param {QueryResult<any>} result the real QueryResult converted to return typed models * @param {{new(): T}} model the target model to convert * @param {Array<string>} projection the optional projection */ function ModelResult(result, model, projection) { this.result = result; this.model = model; this.projection = projection; this.cache = new Array(result.rows.length).fill(null); } Object.defineProperty(ModelResult.prototype, "rowsAffected", { /** * @public * @property {number} rowsAffected serve real ResultQuery rowsAffected; */ get: function () { return this.result.rowsAffected; }, enumerable: true, configurable: true }); Object.defineProperty(ModelResult.prototype, "insertId", { /** * @public * @property {number} insertId serve real ResultQuery insertId; */ get: function () { return this.result.insertId; }, enumerable: true, configurable: true }); Object.defineProperty(ModelResult.prototype, "rows", { /** * @public * @property {Object} rows serve customized type ResultQuery rows; */ get: function () { var _this = this; return { /** * @public * @property {number} length serve real ResultQuery rows.length; */ length: this.result.rows.length, /** * @public * @method item get typed item from ResultQuery and keep it in cache * to avoid doing the job two times * * @param {number} i the index of the item * * @return {T} the typed item */ item: function (i) { if (!_this.cache[i]) { var entity = new _this.model(); var item = _this.result.rows.item(i); for (var key in entity.$$shadow) { if (entity.$$shadow.hasOwnProperty(key)) { var shadow = entity.$$shadow[key]; if (_this.projection && _this.projection.indexOf(key) < 0) { continue; } if (item.hasOwnProperty(key)) { shadow.val = item[key]; } } } entity.$$partialWithProjection = _this.projection; entity.$$rowid = item.hasOwnProperty('rowid') ? item.rowid : null; entity.$$inserted = true; entity.$$isModified = false; _this.cache[i] = entity; } return _this.cache[i]; }, /** * @public * @method toArray convert rows to an array of instanciated models * * @return {Array<T>} the array of models */ toArray: function () { for (var i = 0; i < _this.cache.length; i++) { if (!_this.cache[i]) { _this.rows.item(i); } } return _this.cache; } }; }, enumerable: true, configurable: true }); return ModelResult; }()); /** * @public * @class BadColumnDeclarationError * * @description * thrown when a column declaration is detected * * @see Error * * @author Olivier Margarit * @since 0.1 */ var BadColumnDeclarationError = /** @class */ (function () { /** * @public * @constructor * @param {string} message message explaining in details error */ function BadColumnDeclarationError(message) { var _newTarget = this.constructor; this.message = message; Object.setPrototypeOf(this, _newTarget.prototype); Error.captureStackTrace(this, this.constructor); this.message = message; this.name = 'bad column declaration error'; } /** * @public * @method toString convert error to string * * @return {string} string represation of the error */ BadColumnDeclarationError.prototype.toString = function () { return this.name + '\n' + this.message; }; return BadColumnDeclarationError; }()); /** * @public * @class DbTable * * @description * This class is a table model to help model migration to do his soup * * @author Olivier Margarit * @since 0.1 */ var DbTable = /** @class */ (function () { function DbTable() { /** * @public * @property {number} version, the table model version, information to help migration */ this.version = 0; /** * @public * @property {Array<DbColumn>} columnList is an array of DbColumn listing each column * sql properties */ this.columnList = []; /** * @public * @property {{[index: string]: DbColumn}} column key/value column list with column name as key */ this.columns = {}; /** * @public * @property {{[index: string]: DbColumn}} fields key/value column list with field name as key */ this.fields = {}; /** * @public * @property {{[index: string]: {[index: string]: DbRelationModel}}} relations two dimensional relation access by * model name and relation key */ this.relations = {}; } /** * @public * @method configure methode to configure table from configurator object * * @param {TableConfig} config the configurator * * @since 0.2 */ DbTable.prototype.configure = function (config) { if (config.version) { this.version = config.version; } }; /** * @public * @method hasAutoIncrementedPrimaryKey check if table has auto increment column * * @return {boolean} true if table has auto increment column * * @since 0.2 */ DbTable.prototype.hasAutoIncrementedPrimaryKey = function () { for (var _i = 0, _a = this.columnList; _i < _a.length; _i++) { var column = _a[_i]; if (column.primaryKey && column.autoIncrement) { return true; } } return false; }; /** * @public * @method hasNoPrimaryKey check if table has no primary key * * @return {boolean} return true if table has no primary key * * @since 0.2 */ DbTable.prototype.hasNoPrimaryKey = function () { for (var _i = 0, _a = this.columnList; _i < _a.length; _i++) { var column = _a[_i]; if (column.primaryKey) { return false; } } return true; }; /** * @public * @method getPrimaryColumns get column playing primary role among table's columns * * @return {Array<DbColumn>} the structured primary column list * * @since 0.2 */ DbTable.prototype.getPrimaryColumns = function () { var columns = []; for (var _i = 0, _a = this.columnList; _i < _a.length; _i++) { var column = _a[_i]; if (column.primaryKey) { columns.push(column); } } return columns; }; /** * @public * @method addRelation add relation to table model * * @param {{new(): DbHelperModel}} model the target model of the relation * @param {DbRalationModel} relation the relation * @param {string} key optional key to manage multiple relations to the same model * * @since 0.2 */ DbTable.prototype.addRelation = function (model, relation, key) { if (this.relations.hasOwnProperty(model.name) && this.relations[model.name].hasOwnProperty(key || DbTable.RELATIONS_DEFAULT_KEY)) { throw new BadColumnDeclarationError('relation with key "' + (key || DbTable.RELATIONS_DEFAULT_KEY) + '" is inserted twice'); } else { if (!this.relations[model.name]) { this.relations[model.name] = {}; } this.relations[model.name][key || DbTable.RELATIONS_DEFAULT_KEY] = relation; } }; /** * @public * @method getRelation get relation from the targetted model and the optionale relation key * * @param {{new(): DbHelperModel}} model the target model of the relation * @param {string} key optional key to manage multiple relations to the same model * * @return {DbRelationModel} the relation matching to the properties, return is null if no relation matches * * @since 0.2 */ DbTable.prototype.getRelation = function (model, key) { if (this.relations.hasOwnProperty(model.name) && this.relations[model.name].hasOwnProperty(key || DbTable.RELATIONS_DEFAULT_KEY)) { return this.relations[model.name][key || DbTable.RELATIONS_DEFAULT_KEY]; } return null; }; /** * @private * @static * @constant {string} RELATIONS_DEFAULT_KEY the default relation key */ DbTable.RELATIONS_DEFAULT_KEY = 'default'; return DbTable; }()); /** * @public * @class DbQuery * * @description * This class is a model to share query informations specifically to * the query connector. * * @author Olivier Margarit * @since 0.1 */ var DbQuery = /** @class */ (function () { function DbQuery() { /** * @public * @property {number} page the page number to retrieve for select queries */ this.page = 0; /** * @public * @property {Array<any>} params list of params of the query, see the sqlite * documenetations to learn about query parameters */ this.params = []; /** * @public * @property {string} query the query string */ this.query = ''; /** * @public * @property {number} size the number of result to retrieve per page * on select statement */ this.size = 1000; } /** * @public * @method append is a part of private API. * this method is used to build query by appending part of it * * @param {QueryPart} queryPart query part to append * * @return {DbQuery} the db query instance to chain part appending */ DbQuery.prototype.append = function (queryPart) { this.query = this.query.trim() + ' ' + queryPart.content.trim(); this.params = this.params.concat(queryPart.params); return this; }; return DbQuery; }()); /** * @public * @class ColumnClauseValue * * @description * Clause that hold a column comparission with the model that can deliver an alias. * * @author Olivier Margarit * * @since 0.2 */ var ColumnClauseValue = /** @class */ (function () { /** * @public * @constructor * * @param {string} name the column name * @param {string|IQueryHelper} aliasing optaional param that is the alias or provide it */ function ColumnClauseValue(name, aliasing) { this.name = name; this.aliasing = aliasing; } Object.defineProperty(ColumnClauseValue.prototype, "alias", { /** * @public * @property {string} alias the column alias */ get: function () { if (this.aliasing instanceof String) { return this.aliasing; } return undefined; }, enumerable: true, configurable: true }); Object.defineProperty(ColumnClauseValue.prototype, "querySrc", { /** * @public * @property {IQueryHelper} querySrc the query object that hold the column and deliver the alias */ get: function () { if (!(this.aliasing instanceof String)) { return this.aliasing; } return undefined; }, enumerable: true, configurable: true }); /** * @method fqn full qulaified name * * @return the full name with the namespace alias */ ColumnClauseValue.prototype.fqn = function () { var fqn; if (this.aliasing instanceof String) { fqn = this.aliasing + '.' + this.name; } else if (this.aliasing && this.aliasing.alias) { fqn = this.aliasing.alias + '.' + this.name; } else { fqn = this.name; } return fqn; }; return ColumnClauseValue; }()); /** * @public * @class ClauseOperators * * @description * list of managed operators. This class should be converted to an enum in a furture version * * @author Olivier Margarit * * @since 0.2 */ var ClauseOperators = /** @class */ (function () { function ClauseOperators() { } /** * @public * @static * @method isKeyOf check if string is a valid key of ClauseOperator, this check * is not case sensitive * * @param {string} val the value to check * * @return {boolean} is true if key will return a valid value with {@link ClauseOperator.valueOf} */ ClauseOperators.isKeyOf = function (val) { return ClauseOperators.hasOwnProperty(val.toUpperCase()); }; /** * @public * @static * @method valueOf get value of ClauseOperator by using a valid key val. * * @param {string} val the value key * * @return {boolean} the operator linked to the key */ ClauseOperators.valueOf = function (val) { return ClauseOperators[val.toUpperCase()]; }; /** * @public * @constant {string} OR the or operator "OR" */ ClauseOperators.OR = 'OR'; /** * @public * @default * @constant {string} AND the and operator "AND" */ ClauseOperators.AND = 'AND'; return ClauseOperators; }()); /** * @private * @class QueryPart * * @description * This class is private part of the API. * It is an intermidiate query object for builded query parts * * @author Olivier Margarit * * @since 0.1 */ var QueryPart = /** @class */ (function () { function QueryPart() { /** * @public * @property {string} content string part of the query */ this.content = ''; /** * @public * @property {Array<any>} params the parameters of this part of query */ this.params = []; } /** * @public * @method append append other query part to itself and return itself * to chain appending * * @param {QueryPart} queryPart another query part to append * * @return {QueryPart} the query part itself */ QueryPart.prototype.append = function (queryPart) { this.appendContent(queryPart.content); this.params = this.params.concat(queryPart.params); return this; }; /** * @public * @method appendSub append sub query part * * @param {QueryPart} queryPart the query part to append * * @return {QueryPart} the query part instance to chain operation * * @since 0.2 */ QueryPart.prototype.appendSub = function (queryPart) { if (queryPart instanceof QueryPart) { this.content += ' ('; this.append(queryPart); this.content += ')'; } else { this.content += ' ('; this.content += queryPart.query.trim(); this.content += ')'; this.params = this.params.concat(queryPart.params); } return this; }; /** * @public * @method appendContent append query content to the query part * * @param {string} content the string part to append * * @return {QueryPart} the query part instance to chain operation * * @since 0.2 */ QueryPart.prototype.appendContent = function (content) { this.content = this.content + ' ' + content.trim(); return this; }; return QueryPart; }()); /** * @public API * @class CompositeClause * * @description * Clause that allow integrators to compare tuple of value like in this raw query: * "SELECT * FROM Animals WHERE (species, color) IN (('dog', 'white'), ('cat', 'ginger'), ('bird', 'blue'))" * * @example * ```typescript * // Create a group of clauses * const composite = new CompositeClause(['species', 'color'], [['