diff --git a/lib/LoggerHelpers.ts b/lib/LoggerHelpers.ts new file mode 100644 index 0000000..9cc74a5 --- /dev/null +++ b/lib/LoggerHelpers.ts @@ -0,0 +1,17 @@ +export namespace LoggerHelpers { + function getLogger(persistor, logger) { + return logger || persistor.logger; + } + + export function debug(persistor, logger, logObj, message) { + getLogger(persistor, logger).debug(logObj, message); + } + + export function error(persistor, logger, logObj, message) { + getLogger(persistor, logger).error(logObj, message); + } + + export function info(persistor, logger, logObj, message) { + getLogger(persistor, logger).info(logObj, message); + } +} \ No newline at end of file diff --git a/lib/api.ts b/lib/api.ts index 0e93a4a..0a9b518 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -10,7 +10,7 @@ * */ -import { PersistorTransaction, RemoteDocConnectionOptions } from './types'; +import {PersistorTransaction, RemoteDocConnectionOptions} from './types'; module.exports = function (PersistObjectTemplate, baseClassForPersist) { @@ -56,7 +56,7 @@ module.exports = function (PersistObjectTemplate, baseClassForPersist) { } } } - } + }; /** * PUBLIC INTERFACE FOR TEMPLATES @@ -67,7 +67,8 @@ module.exports = function (PersistObjectTemplate, baseClassForPersist) { this._prepareSchema(template); this._injectTemplateFunctions(template); this._injectObjectFunctions(template); - } + }; + PersistObjectTemplate._prepareSchema = function (template) { if (!this.schemaVerified) { this._verifySchema(); @@ -305,7 +306,7 @@ module.exports = function (PersistObjectTemplate, baseClassForPersist) { let deleteQuery = dbType == PersistObjectTemplate.DB_Mongo ? PersistObjectTemplate.deleteFromPersistWithMongoQuery(template, query, options.logger) : PersistObjectTemplate.deleteFromKnexByQuery(template, query, options.transaction, options.logger); - + const name = 'persistorDeleteByQuery'; return deleteQuery .then(result => { @@ -578,7 +579,7 @@ module.exports = function (PersistObjectTemplate, baseClassForPersist) { template.createProperty(closureProp + 'Persistor', { type: Object, toClient: toClient, toServer: false, persist: false, - value: { isFetched: defineProperty.autoFetch ? false : true, isFetching: false } + value: {isFetched: !defineProperty.autoFetch, isFetching: false} }); } if (!template.prototype[closureProp + 'Fetch']) @@ -919,7 +920,7 @@ module.exports = function (PersistObjectTemplate, baseClassForPersist) { configurable: true }) - + Object.defineProperty(template.prototype, 'objectTemplateName', { get: function () { return (this.constructor && this.constructor.name) || template.__name__; diff --git a/lib/knex/commit/IdentifyChanges.ts b/lib/knex/commit/IdentifyChanges.ts new file mode 100644 index 0000000..3d0f162 --- /dev/null +++ b/lib/knex/commit/IdentifyChanges.ts @@ -0,0 +1,107 @@ +// Identifies changes for change tracking purposes + +import {ChangeTracking, ObjectChanges, PropertyChanges} from '../../types'; + +export namespace IdentifyChanges { + + // Entrance from Mappers + export function generateChanges(obj, action: string, changeTracking: ChangeTracking, notifyChanges) { + + if (notifyChanges && isChangeTrackingEnabled(obj)) { + const templateName = obj.__template__.__name__; + let objChanges: ObjectChanges = { + table: obj.__template__.__table__, + primaryKey: obj._id, + action: action, + properties: [] + }; + + changeTracking = changeTracking || {}; + changeTracking[templateName] = changeTracking[templateName] || []; + + if (action === 'update' || action === 'delete') { + const props = obj.__template__.getProperties(); + for (var prop in props) { + if (!isArrayOrPersistorShadowProp(props, prop)) { + generatePropertyChanges(props, objChanges, prop, obj); + } + } + } + + // Pushing changes into changeTracking for postSave + changeTracking[templateName].push(objChanges); + } + } + + function isChangeTrackingEnabled(obj) { + return !!(obj.__template__ && obj.__template__.__schema__ && obj.__template__.__schema__.enableChangeTracking); + } + + // This is a confusing function, however, I've decoded it. + // Prop[propName] here, is the property definition of a property within a class, and not the actual instance itself + // For example props[propName] for homeAddress field could be {toClient: true} or {getType: () => Array }, etc. + // However, we also have persistor shadow properties to hold metadata (fancy word for junk) about the original property. + // So homeAddressPersistor will have the isFetched, id fields as well. + // We don't want to then generate property changes for any of these shadow props, or even for the Array refs, + // which we handle at a later time (I suppose) + function isArrayOrPersistorShadowProp(props, propName) { + const propertyDefinition = props[propName]; + const isArrayOrObjectTemplate = propertyDefinition.type === Array && propertyDefinition.of.isObjectTemplate; + // @TODO: This may be buggy as propName.match(/Persistor$/) used to be prop.match(/Persistor$/), but prop is not in this scope + const isPersistorObject = propName.match(/Persistor$/) && typeof props[propName.replace(/Persistor$/, '')] === 'object'; + return isArrayOrObjectTemplate || isPersistorObject; + } + + function generatePropertyChanges(props, objChanges, prop, obj) { + // When the property type is not an object template, need to compare the values. + // for date and object types, need to compare the stringified values. + let newValue; + let changedProperties: PropertyChanges = {}; + const oldKey = `_ct_org_${prop}`; + const oldValue = obj[oldKey]; + const propertyDefinition = props[prop]; + + if (!propertyDefinition.type.isObjectTemplate) { + newValue = obj[prop]; + if (oldValue !== newValue || (dateOrObject(propertyDefinition.type) && !isStringifiesEqual(oldValue, newValue))) { + changedProperties = { + name: prop, + originalValue: oldValue, + newValue: newValue, + columnName: prop + }; + } + } else { + newValue = obj[`${prop}Persistor`]; + if (newValue && oldValue !== newValue.id) { + changedProperties = { + name: prop, + originalValue: oldValue, // @TODO: why is this oldValue and not oldValue.id? + newValue: newValue.id, // @TODO: Why is this newValue and not newValue.id? + columnName: getColumnName(prop, obj) + }; + } + } + + if (!(Object.entries(changedProperties).length === 0 && changedProperties.constructor === Object)) { + objChanges.properties.push(changedProperties); + } + } + + function dateOrObject(type) { + return type === Date || type === Object; + } + + function isStringifiesEqual(oldValue, newValue) { + return JSON.stringify(oldValue) === JSON.stringify(newValue) + } + + + function getColumnName(prop, obj): string { + let schema = obj.__template__.__schema__; + if (!schema || !schema.parents || !schema.parents[prop] || !schema.parents[prop].id) { + throw new Error(`${obj.__template__.__name__}.${prop} is missing a parents schema entry`); + } + return schema.parents[prop].id; + } +} \ No newline at end of file diff --git a/lib/knex/commit/Mappers.ts b/lib/knex/commit/Mappers.ts new file mode 100644 index 0000000..c52ff11 --- /dev/null +++ b/lib/knex/commit/Mappers.ts @@ -0,0 +1,46 @@ +import {DeleteQuery, PersistorTransaction} from '../../types/PersistorTransaction'; +import {IdentifyChanges} from './IdentifyChanges'; +import {ChangeTracking, Objects} from '../../types'; +// Mappers +export namespace Mappers { + + export async function saveMapper(obj, dirtyObjects: Objects, changeTracking: ChangeTracking, notifyChanges, txn: PersistorTransaction, logger?) { + delete dirtyObjects[obj.__id__]; // Once scheduled for update remove it. + + if (hasSchema(obj)) { // replace callSave; + await obj.persistSave(txn, logger); + } + + let action: string = ''; + if (obj.__version__ === 1) { // insert + action = 'insert'; + } else { + action = 'update'; + } + return IdentifyChanges.generateChanges(obj, action, changeTracking, notifyChanges); + } + + + export async function deleteMapper(obj, deletedObjects: Objects, changeTracking: ChangeTracking, notifyChanges, txn: PersistorTransaction, logger?) { + delete deletedObjects[obj.__id__]; + + if (hasSchema(obj)) { + await obj.persistDelete(txn, logger); // replace callDelete + } + + return IdentifyChanges.generateChanges(obj, 'delete', changeTracking, notifyChanges); + } + + // Persistor Def is typeof Persistor + export async function deleteQueryMapper(persistorDef, obj: DeleteQuery, deleteQueries: Objects, txn: PersistorTransaction, logger?) { + delete deleteQueries[obj.__name__]; // Once scheduled for update remove it. + + if (hasSchema(obj)) { + await persistorDef.deleteFromKnexQuery(obj.__template__, obj.queryOrChains, txn, logger); + } + } + + function hasSchema(obj): boolean { + return !!(obj.__template__ && obj.__template__.__schema__); + } +} \ No newline at end of file diff --git a/lib/knex/commit/RollbackS3.ts b/lib/knex/commit/RollbackS3.ts new file mode 100644 index 0000000..a649116 --- /dev/null +++ b/lib/knex/commit/RollbackS3.ts @@ -0,0 +1,37 @@ +import {RemoteDocService} from '../../remote-doc/RemoteDocService'; +import {LoggerHelpers} from '../../LoggerHelpers'; +import {PersistorTransaction} from '../../types'; + +// Rollback S3 Changes +export namespace RollbackS3 { + + // Persistor Def is typeof Persistor + export async function rollbackS3(persistorDef, logger, txn: PersistorTransaction) { + LoggerHelpers.info(persistorDef, logger, { + component: 'persistor', + module: 'api', + activity: 'end' + }, `Rolling back transaction of remote keys`); + const remoteDocService = RemoteDocService.new(persistorDef.environment); + const toDeletePromiseArr = []; + + // create our `delete functions` to be run later. + // also put them in one place => toDeletePromiseArr. + + for (const key of txn.remoteObjects) { // @TODO: how does this work right now? If doesn't work on sets + toDeletePromiseArr.push(rollbackS3Key(persistorDef, logger, key, remoteDocService)); + } + + // fire off our delete requests in parallel + await Promise.all(toDeletePromiseArr); + } + + async function rollbackS3Key(persistorDef, logger, key, remoteDocService: RemoteDocService) { + try { + await remoteDocService.deleteDocument(key, persistorDef.bucketName); + } catch (e) { + const newLogObj = {component: 'persistor', module: 'api', activity: 'end', error: e}; + LoggerHelpers.error(persistorDef, logger, newLogObj, `Unable to rollback remote document with key: ${key} and bucket: ${persistorDef.bucketName}`); + } + } +} \ No newline at end of file diff --git a/lib/knex/commit/Transaction.ts b/lib/knex/commit/Transaction.ts new file mode 100644 index 0000000..ed208f5 --- /dev/null +++ b/lib/knex/commit/Transaction.ts @@ -0,0 +1,147 @@ +import {Mappers} from './Mappers'; +import {LoggerHelpers} from '../../LoggerHelpers'; +import {RollbackS3} from './RollbackS3'; +import {ChangeTracking, DeleteQueries, Objects, PersistorTransaction} from '../../types'; +import _ = require("underscore"); + +// Main Transaction executor +export namespace Transaction { + + + // Wrapper for transaction function + + export async function transaction(persistorDef, notifyChanges, txn: PersistorTransaction, knexTxn, logger): Promise { + const {dirtyObjects, touchObjects, savedObjects, deletedObjects, deleteQueries} = txn; + let changeTracking = {}; + + txn.knex = knexTxn; + try { + await preSave(txn, logger); + await saves(dirtyObjects, changeTracking, notifyChanges, txn, logger); + await deletes(deletedObjects, changeTracking, notifyChanges, txn, logger); + await deletesQueries(persistorDef, deleteQueries, txn, logger); + await touches(touchObjects, savedObjects, txn, logger); + await postSave(txn, changeTracking, logger); + await commit(persistorDef, txn, knexTxn); + } catch (err) { + return await rollback(persistorDef, logger, txn, knexTxn, err); + } + } + + // Processor Handlers + + async function preSave(txn: PersistorTransaction, logger) { + if (txn.preSave) { + return txn.preSave(txn, logger); + } else { + return true; + } + } + + async function saves(dirtyObjects: Objects, changeTracking: ChangeTracking, notifyChanges, txn: PersistorTransaction, logger) { + + // Walk through the dirty objects + const dirtyObjectsArray = _.toArray(dirtyObjects); //@TODO: replace with Object.values + await Promise.all(dirtyObjectsArray.map((obj) => + Mappers.saveMapper(obj, dirtyObjects, changeTracking, notifyChanges, txn, logger))); + + const refreshedDirtyArray = _.toArray(dirtyObjects); + if (refreshedDirtyArray.length > 0) { + return saves(dirtyObjects, changeTracking, notifyChanges, txn, logger); + } + } + + async function deletes(deletedObjects: Objects, changeTracking: ChangeTracking, notifyChanges, txn: PersistorTransaction, logger) { + const deletedObjectsArray = _.toArray(deletedObjects); + await Promise.all(deletedObjectsArray.map((obj) => + Mappers.deleteMapper(obj, deletedObjects, changeTracking, notifyChanges, txn, logger))); + + const refreshedDeletedArray = _.toArray(deletedObjects); + if (refreshedDeletedArray.length > 0) { + return deletes(deletedObjects, changeTracking, notifyChanges, txn, logger); + } + } + + async function deletesQueries(persistorDef, deleteQueries: Objects, txn: PersistorTransaction, logger) { + const deleteQueriesArray: DeleteQueries = _.toArray(deleteQueries); // @TODO: Is this needed? This is already an array + + await Promise.all(deleteQueriesArray.map((obj) => + Mappers.deleteQueryMapper(persistorDef, obj, deleteQueries, txn, logger))); + + const refreshedDeleteQueriesArray = _.toArray(deleteQueries); + if (refreshedDeleteQueriesArray.length > 0) { + return deletesQueries(persistorDef, deleteQueries, txn, logger); + } + } + + // Walk through the touched objects + async function touches(touchObjects: Objects, savedObjects: Objects, txn: PersistorTransaction, logger?) { + const touchObjectsArray = _.toArray(touchObjects); + + return Promise.all(touchObjectsArray.map(async (obj: any) => { + if (obj.__template__ && obj.__template__.__schema__ && !savedObjects[obj.__id]) { + return obj.persistTouch(txn, logger); + } + })); + } + + async function postSave(txn: PersistorTransaction, changeTracking: ChangeTracking, logger) { + if (txn.postSave) { + return txn.postSave(txn, logger, changeTracking); + } + } + + // And we are done with everything. Commit or throw an update conflict if seen + async function commit(persistorDef, txn: PersistorTransaction, knexTxn) { + + // Reset global state, @TODO: remove this global state + persistorDef.dirtyObjects = {}; + persistorDef.savedObjects = {}; + + // Set through persistSaveKnex -> SaveKnexPojo -> checkUpdateResults + if (txn.updateConflict) { + throw 'Update Conflict'; + } + + // commit knex transaction + return knexTxn.commit(); + } + + // rollback if there's an error while processing any stage of this + // including just generating the SQL queries / checking the versions + // or even at the commit step + + // @returns innerError + async function rollback(persistorDef, logger, txn: PersistorTransaction, knexTxn, err: Error): Promise { + const deadlock = err.toString().match(/deadlock detected$/i); + txn.innerError = err; + let innerError; + + if (deadlock) { + innerError = new Error('Update Conflict'); + } else { + innerError = err; + } + + // @TODO: What if knex rollback fails? But S3 Works out? + if (txn.remoteObjects && txn.remoteObjects.size > 0) { + await RollbackS3.rollbackS3(persistorDef, logger, txn); + } + + await knexTxn.rollback(innerError); + const newLogObject = { + component: 'persistor', + module: 'api', + activity: 'end' + }; + + let fromDeadlock = ''; + if (deadlock) { + fromDeadlock = ' from deadlock'; + } + + LoggerHelpers.debug(persistorDef, logger, newLogObject, `Transaction rolled back ${innerError.message}${fromDeadlock}`); + + return innerError; + } +} \ No newline at end of file diff --git a/lib/knex/db.ts b/lib/knex/db.ts index c0129de..fabb4f6 100644 --- a/lib/knex/db.ts +++ b/lib/knex/db.ts @@ -1,4 +1,7 @@ -import {RemoteDocService} from "../remote-doc/RemoteDocService"; +import {DeleteQueries, PersistorTransaction} from '../types'; +import {LoggerHelpers} from '../LoggerHelpers'; +import {Transaction} from './commit/Transaction'; + module.exports = function (PersistObjectTemplate) { @@ -249,13 +252,16 @@ module.exports = function (PersistObjectTemplate) { return knex.delete(); }; - PersistObjectTemplate.deleteFromKnexByQuery = async function (template, queryOrChains, txn, _logger) { + PersistObjectTemplate.deleteFromKnexByQuery = async function (template, queryOrChains, txn: PersistorTransaction, _logger) { if (!txn) { return PersistObjectTemplate.deleteFromKnexQuery(template, queryOrChains, txn, _logger); } - var deleteQueries = txn ? txn.deleteQueries : this.deleteQueries; - var deleteQuery = {name: template.__name__, template: template, queryOrChains: queryOrChains}; - deleteQueries[template.__name__] = deleteQuery; + var deleteQueries: DeleteQueries = txn ? txn.deleteQueries : this.deleteQueries; + deleteQueries[template.__name__] = { + __name__: template.__name__, + __template__: template, + queryOrChains: queryOrChains + }; txn.deleteQueries = deleteQueries; }; @@ -1116,231 +1122,41 @@ module.exports = function (PersistObjectTemplate) { } return traverse(statement, query) - } + }; - PersistObjectTemplate._commitKnex = function _commitKnex(persistorTransaction, logger, notifyChanges) { + // Start the knex transaction + // We generate SQLs to save and to Delete and touches + // Each save / delete is done in 2 stages + // 1) We iterate through the objects and check to see if the version of any dirty / deleted objects are outdated + // if so, we set an update conflict flag to `True` on the transaction. @TODO: Perhaps should throw an error + // instead so we can stop generating random SQLS and end early + // 2) As we iterate through the objects in the first step, we generate SQL statements / attach them to the knex + // transaction + // 3) We identify which properties are changed, for change tracking purposes + // 4) We commit / finish the transaction + PersistObjectTemplate._commitKnex = async function _commitKnex(txn: PersistorTransaction, logger, notifyChanges) { logger.debug({component: 'persistor', module: 'api', activity: 'commit'}, 'end of transaction '); - var knex = _.findWhere(this._db, {type: PersistObjectTemplate.DB_Knex}).connection; - var dirtyObjects = persistorTransaction.dirtyObjects; - var touchObjects = persistorTransaction.touchObjects; - var savedObjects = persistorTransaction.savedObjects; - var deletedObjects = persistorTransaction.deletedObjects; - var deleteQueries = persistorTransaction.deleteQueries; - var innerError; - var changeTracking; - - // Start the knext transaction - return knex.transaction(function(knexTransaction) { - persistorTransaction.knex = knexTransaction; - - - Promise.resolve(true) - .then(processPreSave.bind(this)) - .then(processSaves.bind(this)) - .then(processDeletes.bind(this)) - .then(processDeleteQueries.bind(this)) - .then(processTouches.bind(this)) - .then(processPostSave.bind(this)) - .then(processCommit.bind(this)) - .catch(rollback.bind(this)); - - function processPreSave() { - return persistorTransaction.preSave - ? persistorTransaction.preSave.call(persistorTransaction, persistorTransaction, logger) - : true - } - - // Walk through the dirty objects - function processSaves() { - return Promise.map(_.toArray(dirtyObjects), function (obj) { - delete dirtyObjects[obj.__id__]; // Once scheduled for update remove it. - return callSave(obj).then(generateChanges.bind(this, obj, obj.__version__ === 1 ? 'insert' : 'update')); - }.bind(this), {concurrency: PersistObjectTemplate.concurrency}).then (function () { - if (_.toArray(dirtyObjects). length > 0) { - return processSaves.call(this); - } - }); - - function callSave(obj) { - return (obj.__template__ && obj.__template__.__schema__ - ? obj.persistSave(persistorTransaction, logger) - : Promise.resolve(true)); - } - } - - - function processDeletes() { - return Promise.map(_.toArray(deletedObjects), function (obj) { - delete deletedObjects[obj.__id__]; // Once scheduled for update remove it. - return callDelete(obj).then(generateChanges.bind(this, obj, 'delete')); - }.bind(this), {concurrency: PersistObjectTemplate.concurrency}).then (function () { - if (_.toArray(deletedObjects). length > 0) { - return processDeletes.call(this); - } - }); - - function callDelete(obj) { - return (obj.__template__ && obj.__template__.__schema__ - ? obj.persistDelete(persistorTransaction, logger) - : Promise.resolve(true)) - } - } - - function processDeleteQueries() { - return Promise.map(_.toArray(deleteQueries), function (obj) { - delete deleteQueries[obj.name]; // Once scheduled for update remove it. - return (obj.template && obj.template.__schema__ - ? PersistObjectTemplate.deleteFromKnexQuery(obj.template, obj.queryOrChains, persistorTransaction, logger) - : true) - }.bind(this), {concurrency: PersistObjectTemplate.concurrency}).then (function () { - if (_.toArray(deleteQueries). length > 0) { - return processDeleteQueries.call(this); - } - }); - } - - - function processPostSave() { - return persistorTransaction.postSave ? - persistorTransaction.postSave(persistorTransaction, logger, changeTracking) : - true; - } - - // And we are done with everything - function processCommit() { - this.dirtyObjects = {}; - this.savedObjects = {}; - if (persistorTransaction.updateConflict) { - throw 'Update Conflict'; - } - return knexTransaction.commit(); - } - - // Walk through the touched objects - function processTouches() { - return Promise.map(_.toArray(touchObjects), function (obj) { - return (obj.__template__ && obj.__template__.__schema__ && !savedObjects[obj.__id__] - ? obj.persistTouch(persistorTransaction, logger) - : true) - }.bind(this)) - } - - async function rollback (err) { - const deadlock = err.toString().match(/deadlock detected$/i); - persistorTransaction.innerError = err; - innerError = deadlock ? new Error('Update Conflict') : err; - - if (persistorTransaction.remoteObjects && persistorTransaction.remoteObjects.size > 0) { - (logger || this.logger).info({ - component: 'persistor', - module: 'api', - activity: 'end' - }, - `Rolling back transaction of remote keys`); - - let remoteDocService = RemoteDocService.new(this.environment); - - let toDeletePromiseArr = []; - - // create our `delete functions` to be run later. - // also put them in one place => toDeletePromiseArr. - for (const key of persistorTransaction.remoteObjects) { - toDeletePromiseArr.push(async () => { - try { - await remoteDocService.deleteDocument(key, this.bucketName); - } catch (e) { - (logger || this.logger).error({ - component: 'persistor', - module: 'api', - activity: 'end', - error: e}, - 'unable to rollback remote document with key:' + key + ' and bucket: ', this.bucketName); - } - }); - } - - // fire off our delete requests in parallel - await Promise.all(toDeletePromiseArr); - } - - return knexTransaction.rollback(innerError).then(() => { - (logger || this.logger).debug({ - component: 'persistor', - module: 'api', - activity: 'end'}, - 'transaction rolled back ' + innerError.message + (deadlock ? ' from deadlock' : '')); - }); - } - - function generateChanges(obj, action) { - var objChanges; - - if (notifyChanges && obj.__template__ && obj.__template__.__schema__ && obj.__template__.__schema__.enableChangeTracking) { - changeTracking = changeTracking || {}; - changeTracking[obj.__template__.__name__] = changeTracking[obj.__template__.__name__] || []; - changeTracking[obj.__template__.__name__].push(objChanges = { - table: obj.__template__.__table__, - primaryKey: obj._id, - action: action, - properties: [] - }); - if (action === 'update' || action === 'delete') { - var props = obj.__template__.getProperties(); - for (var prop in props) { - var propType = props[prop]; - if (isOnetoManyRelationsOrPersistorProps(prop, propType)) { - continue; - } - generatePropertyChanges(prop, obj); - } - } - } - - function isOnetoManyRelationsOrPersistorProps(propName, propType) { - return (propType.type === Array && propType.of.isObjectTemplate) || - (prop.match(/Persistor$/) && typeof props[propName.replace(/Persistor$/, '')] === 'object'); - } - - function generatePropertyChanges(prop, obj) { - //When the property type is not an object template, need to compare the values. - //for date and object types, need to compare the stringified values. - var oldKey = '_ct_org_' + prop; - if (!props[prop].type.isObjectTemplate && (obj[oldKey] !== obj[prop] || ((props[prop].type === Date || props[prop].type === Object) && - JSON.stringify(obj[oldKey]) !== JSON.stringify(obj[prop])))) { - addChanges(prop, obj[oldKey], obj[prop], prop); - } - //For one to one relations, we need to check the ids associated to the parent record. - else if (props[prop].type.isObjectTemplate && obj[prop + 'Persistor'] && obj['_ct_org_' + prop] !== obj[prop + 'Persistor'].id) { - addChanges(prop, obj[oldKey], obj[prop + 'Persistor'].id, getColumnName(prop, obj)); - } - } - - function getColumnName(prop, obj) { - var schema = obj.__template__.__schema__; - if (!schema || !schema.parents || !schema.parents[prop] || !schema.parents[prop].id) - throw new Error(obj.__template__.__name__ + '.' + prop + ' is missing a parents schema entry'); - return schema.parents[prop].id; - } - - function addChanges(prop, originalValue, newValue, columnName) { - objChanges.properties.push({ - name: prop, - originalValue: originalValue, - newValue: newValue, - columnName: columnName - }); - } - } - }.bind(this)).then(function () { - (logger || this.logger).debug({component: 'persistor', module: 'api'}, 'end - transaction completed'); + const knex = _.findWhere(this._db, {type: PersistObjectTemplate.DB_Knex}).connection; + let innerError; + + // Apparently it seems like Knex takes an async callback, and implicitly waits for it to trigger knex.commit. + // This is confusing behavior + try { + innerError = await knex.transaction((knexTxn) => Transaction.transaction(this, notifyChanges, txn, knexTxn, logger)); // @TODO: Is Knex.transaction async? + LoggerHelpers.debug(this, logger, {component: 'persistor', module: 'api'}, 'End - transaction completed'); return true; - }.bind(this)).catch(function (e) { - var err = e || innerError; + } catch (e) { + const err = e || innerError; if (err && err.message && err.message != 'Update Conflict') { - (logger || this.logger).error({component: 'persistor', module: 'api', activity: 'end', error: err.message + err.stack}, 'transaction ended with error'); - } //@TODO: Why throw error in all cases but log only in some cases - throw (e || innerError); - }.bind(this)) + LoggerHelpers.error(this, logger, { + component: 'persistor', + module: 'api', + activity: 'end', + error: err.message + err.stack + }, 'Transaction ended with error'); + } + throw err; + } } }; \ No newline at end of file diff --git a/lib/knex/update.ts b/lib/knex/update.ts index 629acf9..961832a 100644 --- a/lib/knex/update.ts +++ b/lib/knex/update.ts @@ -1,5 +1,5 @@ -import { RemoteDocService } from '../remote-doc/RemoteDocService'; -import { PersistorTransaction } from '../types/PersistorTransaction'; +import {RemoteDocService} from '../remote-doc/RemoteDocService'; +import {PersistorTransaction} from '../types'; import * as uuidv4 from 'uuid/v4'; module.exports = function (PersistObjectTemplate) { @@ -31,7 +31,7 @@ module.exports = function (PersistObjectTemplate) { var template = obj.__template__; var schema = template.__schema__; var templateName = template.__name__; - var isDocumentUpdate = obj.__version__ ? true : false; + var isDocumentUpdate = !!obj.__version__; var props = template.getProperties(); var promises = []; var dataSaved = {}; @@ -115,7 +115,7 @@ module.exports = function (PersistObjectTemplate) { referencedObj.setDirty(txn); } } - }) + }); if (!referencedObj._id) referencedObj._id = this.createPrimaryKey(referencedObj); }.bind(this)); @@ -140,8 +140,12 @@ module.exports = function (PersistObjectTemplate) { value.setDirty(txn); } - pojo[foreignKey] = value ? value._id : null - updatePersistorProp(obj, prop + 'Persistor', {isFetching: false, id: value ? value._id : null, isFetched: true}) + pojo[foreignKey] = value ? value._id : null; + updatePersistorProp(obj, prop + 'Persistor', { + isFetching: false, + id: value ? value._id : null, + isFetched: true + }); dataSaved[foreignKey] = pojo[foreignKey] || 'null'; @@ -163,11 +167,10 @@ module.exports = function (PersistObjectTemplate) { try { if(txn) { - if(txn.remoteObjects) { - txn.remoteObjects.add(objectKey); - } else { - txn.remoteObjects = new Set(objectKey); + if (!txn.remoteObjects) { + txn.remoteObjects = new Set(); // @TODO: Ask Nick if this works as intended } + txn.remoteObjects.add(objectKey); } // grab the document from remote store @@ -202,7 +205,7 @@ module.exports = function (PersistObjectTemplate) { pojo[prop] = obj[prop] ? obj[prop] : null; log(defineProperty, pojo, prop); } else if (defineProperty.type == Boolean) { - pojo[prop] = obj[prop] == null ? null : (obj[prop] ? true : false); + pojo[prop] = obj[prop] == null ? null : !!obj[prop]; log(defineProperty, pojo, prop); } else { pojo[prop] = obj[prop]; @@ -241,4 +244,4 @@ module.exports = function (PersistObjectTemplate) { obj[prop] = copyProps(obj[prop]); } } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/mongo/db.ts b/lib/mongo/db.ts index 5d88447..766ff33 100644 --- a/lib/mongo/db.ts +++ b/lib/mongo/db.ts @@ -1,3 +1,4 @@ + module.exports = function (PersistObjectTemplate) { /* Mongo implementation of save */ @@ -12,21 +13,7 @@ module.exports = function (PersistObjectTemplate) { collection.update(origVer ? {__version__: origVer, _id: updateID} : {_id: updateID}, pojo, {w:1}) : collection.save(pojo, {w:1}) ); - // ).then (function (error, count) { - // if (error instanceof Array) - // count = error[0]; // Don't know why things are returned this way - // if (updateID && count == 0) { - // obj.__version__ = origVer; - // if (txn && txn.onUpdateConflict) { - // txn.onUpdateCoxnflict(pojo) - // txn.updateConflict = new Error("Update Conflict"); - // } else - // throw new Error("Update Conflict"); - // } - // (logger || this.logger).debug({component: 'persistor', module: 'db', activity: 'write'}, 'saved ' + obj.__template__.__name__ + " to " + obj.__template__.__collection__); - // return true; - // }.bind(this)); - } + }; /** * Removes documents based on a query diff --git a/lib/types/Helpers.ts b/lib/types/Helpers.ts new file mode 100644 index 0000000..4f7382e --- /dev/null +++ b/lib/types/Helpers.ts @@ -0,0 +1,4 @@ +export type PropertyChanges = { name: string, originalValue: string, newValue: string, columnName: string } | {}; +export type ObjectChanges = { table: string, primaryKey: string, action: string, properties: PropertyChanges[] } | {}; +export type ChangeTracking = { [name: string]: ObjectChanges[] }; +export type Objects = { [obj: string]: any }; \ No newline at end of file diff --git a/lib/types/PersistorTransaction.ts b/lib/types/PersistorTransaction.ts index 2062763..ae94321 100644 --- a/lib/types/PersistorTransaction.ts +++ b/lib/types/PersistorTransaction.ts @@ -1,9 +1,18 @@ export type PersistorTransaction = { - id: number, - dirtyObjects: object, - savedObjects: object, - touchObjects: object, - deletedObjects: object, - remoteObjects: Set // identifiers for objects not stored directly in our db - deleteQueries: {} -}; \ No newline at end of file + updateConflict?: boolean; + id: number; + dirtyObjects: object; + savedObjects: object; + touchObjects: object; + deletedObjects: object; + remoteObjects: Set; // identifiers for objects not stored directly in our db + deleteQueries: DeleteQueries | {}; + postSave?: (txn: PersistorTransaction, logger: any, changeTracking: any) => Promise; + innerError?: Error; + knex?: any; + preSave?: (txn: PersistorTransaction, logger: any) => Promise +}; + +export type DeleteQueries = Array; + +export type DeleteQuery = { __name__: string, __template__: any, queryOrChains: any }; \ No newline at end of file diff --git a/lib/types/index.ts b/lib/types/index.ts index 20d7cf3..3c95437 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -1,2 +1,3 @@ -export { PersistorTransaction } from './PersistorTransaction'; +export {PersistorTransaction, DeleteQueries} from './PersistorTransaction'; export { RemoteDocConnectionOptions } from './RemoteDocConnectionOptions' +export {ChangeTracking, ObjectChanges, PropertyChanges, Objects} from './Helpers'; diff --git a/package-lock.json b/package-lock.json index 488278b..d1357be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2216,13 +2216,13 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "resolved": false, "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "append-transform": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "resolved": false, "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", "dev": true, "requires": { @@ -2231,25 +2231,25 @@ }, "archy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "resolved": false, "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, "arrify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "resolved": false, "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { @@ -2259,13 +2259,13 @@ }, "builtin-modules": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "resolved": false, "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "caching-transform": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-1.0.1.tgz", + "resolved": false, "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", "dev": true, "requires": { @@ -2276,7 +2276,7 @@ "dependencies": { "md5-hex": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-1.3.0.tgz", + "resolved": false, "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", "dev": true, "requires": { @@ -2287,31 +2287,31 @@ }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "commondir": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "resolved": false, "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "convert-source-map": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "resolved": false, "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", "dev": true }, "cross-spawn": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "resolved": false, "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { @@ -2321,7 +2321,7 @@ }, "debug": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "resolved": false, "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { @@ -2330,19 +2330,19 @@ }, "debug-log": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "resolved": false, "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", "dev": true }, "decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "resolved": false, "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "default-require-extensions": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "resolved": false, "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", "dev": true, "requires": { @@ -2351,7 +2351,7 @@ "dependencies": { "strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "resolved": false, "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true } @@ -2359,7 +2359,7 @@ }, "error-ex": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "resolved": false, "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { @@ -2368,7 +2368,7 @@ }, "execa": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "resolved": false, "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { @@ -2383,7 +2383,7 @@ "dependencies": { "cross-spawn": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "resolved": false, "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { @@ -2396,7 +2396,7 @@ }, "find-cache-dir": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "resolved": false, "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "dev": true, "requires": { @@ -2407,7 +2407,7 @@ }, "find-up": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "resolved": false, "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { @@ -2416,7 +2416,7 @@ }, "foreground-child": { "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "resolved": false, "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", "dev": true, "requires": { @@ -2426,25 +2426,25 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "get-caller-file": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "resolved": false, "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "dev": true }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": false, "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "resolved": false, "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { @@ -2458,31 +2458,31 @@ }, "graceful-fs": { "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "resolved": false, "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "resolved": false, "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "hosted-git-info": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "resolved": false, "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", "dev": true }, "imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "resolved": false, "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { @@ -2492,25 +2492,25 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "invert-kv": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "resolved": false, "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, "is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "resolved": false, "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": false, "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -2519,31 +2519,31 @@ }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "resolved": false, "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "resolved": false, "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "resolved": false, "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "istanbul-lib-coverage": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", + "resolved": false, "integrity": "sha512-yMSw5xLIbdaxiVXHk3amfNM2WeBxLrwH/BCyZ9HvA/fylwziAIJOG2rKqWyLqEJqwKT725vxxqidv+SyynnGAA==", "dev": true }, "istanbul-lib-hook": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.0.tgz", + "resolved": false, "integrity": "sha512-qm3dt628HKpCVtIjbdZLuQyXn0+LO8qz+YHQDfkeXuSk5D+p299SEV5DrnUUnPi2SXvdMmWapMYWiuE75o2rUQ==", "dev": true, "requires": { @@ -2552,7 +2552,7 @@ }, "istanbul-lib-report": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.0.tgz", + "resolved": false, "integrity": "sha512-RiELmy9oIRYUv36ITOAhVum9PUvuj6bjyXVEKEHNiD1me6qXtxfx7vSEJWnjOGk2QmYw/GRFjLXWJv3qHpLceQ==", "dev": true, "requires": { @@ -2563,7 +2563,7 @@ }, "istanbul-lib-source-maps": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-2.0.0.tgz", + "resolved": false, "integrity": "sha512-jenUeC0gMSSMGkvqD9xuNfs3nD7XWeXLhqaIkqHsNZ3DJBWPdlKEydE7Ya5aTgdWjrEQhrCYTv+J606cGC2vuQ==", "dev": true, "requires": { @@ -2576,7 +2576,7 @@ "dependencies": { "source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "resolved": false, "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } @@ -2584,7 +2584,7 @@ }, "istanbul-reports": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.0.tgz", + "resolved": false, "integrity": "sha512-HeZG0WHretI9FXBni5wZ9DOgNziqDCEwetxnme5k1Vv5e81uTqcsy3fMH99gXGDGKr1ea87TyGseDMa2h4HEUA==", "dev": true, "requires": { @@ -2593,13 +2593,13 @@ }, "json-parse-better-errors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "resolved": false, "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "lcid": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "resolved": false, "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { @@ -2608,7 +2608,7 @@ }, "locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "resolved": false, "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { @@ -2618,7 +2618,7 @@ "dependencies": { "path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "resolved": false, "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true } @@ -2626,7 +2626,7 @@ }, "lru-cache": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "resolved": false, "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { @@ -2636,7 +2636,7 @@ }, "make-dir": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "resolved": false, "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { @@ -2645,7 +2645,7 @@ "dependencies": { "pify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "resolved": false, "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } @@ -2653,7 +2653,7 @@ }, "md5-hex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-2.0.0.tgz", + "resolved": false, "integrity": "sha1-0FiOnxx0lUSS7NJKwKxs6ZfZLjM=", "dev": true, "requires": { @@ -2662,13 +2662,13 @@ }, "md5-o-matic": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/md5-o-matic/-/md5-o-matic-0.1.1.tgz", + "resolved": false, "integrity": "sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=", "dev": true }, "mem": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "resolved": false, "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { @@ -2677,7 +2677,7 @@ }, "merge-source-map": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "resolved": false, "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", "dev": true, "requires": { @@ -2686,7 +2686,7 @@ "dependencies": { "source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "resolved": false, "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } @@ -2694,13 +2694,13 @@ }, "mimic-fn": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "resolved": false, "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { @@ -2709,13 +2709,13 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -2724,13 +2724,13 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "normalize-package-data": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "resolved": false, "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { @@ -2742,7 +2742,7 @@ }, "npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "resolved": false, "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { @@ -2751,13 +2751,13 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { @@ -2766,13 +2766,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "resolved": false, "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { @@ -2783,13 +2783,13 @@ }, "p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "resolved": false, "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "p-limit": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "dev": true, "requires": { @@ -2798,7 +2798,7 @@ }, "p-locate": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "resolved": false, "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { @@ -2807,25 +2807,25 @@ }, "p-try": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "resolved": false, "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "resolved": false, "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "pkg-dir": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "resolved": false, "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { @@ -2834,31 +2834,31 @@ }, "pseudomap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "resolved": false, "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "resolved": false, "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "resolved": false, "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, "resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "resolved": false, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { @@ -2867,19 +2867,19 @@ }, "semver": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "resolved": false, "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "resolved": false, "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { @@ -2888,25 +2888,25 @@ }, "shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "resolved": false, "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "slide": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "resolved": false, "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, "spawn-wrap": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", + "resolved": false, "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", "dev": true, "requires": { @@ -2920,7 +2920,7 @@ }, "spdx-correct": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "resolved": false, "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { @@ -2930,13 +2930,13 @@ }, "spdx-exceptions": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "resolved": false, "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "resolved": false, "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { @@ -2946,13 +2946,13 @@ }, "spdx-license-ids": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "resolved": false, "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", "dev": true }, "string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "resolved": false, "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { @@ -2962,7 +2962,7 @@ }, "strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "resolved": false, "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { @@ -2971,13 +2971,13 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "supports-color": { "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "resolved": false, "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { @@ -2986,7 +2986,7 @@ }, "test-exclude": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.2.tgz", + "resolved": false, "integrity": "sha512-2kTGf+3tykCfrWVREgyTR0bmVO0afE6i7zVXi/m+bZZ8ujV89Aulxdcdv32yH+unVFg3Y5o6GA8IzsHnGQuFgQ==", "dev": true, "requires": { @@ -2998,7 +2998,7 @@ "dependencies": { "load-json-file": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "resolved": false, "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { @@ -3010,7 +3010,7 @@ }, "parse-json": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "resolved": false, "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { @@ -3020,7 +3020,7 @@ }, "path-type": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "resolved": false, "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { @@ -3029,13 +3029,13 @@ }, "pify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "resolved": false, "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "read-pkg": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "resolved": false, "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { @@ -3046,7 +3046,7 @@ }, "read-pkg-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "resolved": false, "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { @@ -3056,7 +3056,7 @@ }, "strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "resolved": false, "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true } @@ -3064,7 +3064,7 @@ }, "validate-npm-package-license": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "resolved": false, "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { @@ -3074,7 +3074,7 @@ }, "which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "resolved": false, "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { @@ -3083,13 +3083,13 @@ }, "which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "resolved": false, "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": false, "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -3099,13 +3099,13 @@ "dependencies": { "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { @@ -3114,7 +3114,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -3125,7 +3125,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -3136,13 +3136,13 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write-file-atomic": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "resolved": false, "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { @@ -3153,19 +3153,19 @@ }, "y18n": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "resolved": false, "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yallist": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "resolved": false, "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, "yargs": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "resolved": false, "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { @@ -3185,13 +3185,13 @@ "dependencies": { "camelcase": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "resolved": false, "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, "cliui": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "resolved": false, "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { @@ -3202,7 +3202,7 @@ }, "yargs-parser": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "resolved": false, "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { @@ -3213,7 +3213,7 @@ }, "yargs-parser": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "resolved": false, "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { @@ -3222,7 +3222,7 @@ "dependencies": { "camelcase": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "resolved": false, "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true } @@ -3302,7 +3302,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -4169,6 +4169,11 @@ } } }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, "tv4": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", diff --git a/package.json b/package.json index 3b29bbe..0fe6685 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "q": "1.x", "tv4": "^1.2.7", "underscore": "1.x", + "tslib": "^1.10.0", "uuid": "3.3.3" }, "peerDependencies": { diff --git a/test/persist_newapi_tests.js b/test/persist_newapi_tests.js index 63a2f6d..e711d5c 100644 --- a/test/persist_newapi_tests.js +++ b/test/persist_newapi_tests.js @@ -429,6 +429,7 @@ describe('persistor transaction checks', function () { expect(Object.keys(changes.Employee[0])).to.contain('primaryKey'); expect(changes.Employee[0].properties[0].name).to.equal('homeAddress'); expect(changes.Employee[0].properties[1].name).to.equal('dob'); + expect(changes.Employee[0].properties.length).to.equal(4); var empNew = new Employee(); empNew.setDirty(txn); }; diff --git a/tsconfig.json b/tsconfig.json index 34d03d3..6adbddc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,9 @@ { "compilerOptions": { "outDir": "./dist", - "rootDir": "./" + "rootDir": "./", + "downlevelIteration": true, + "importHelpers": true }, "extends": "./tsconfig.base.json", "include": [