diff --git a/package.json b/package.json index ccb00cd9..c6efa72e 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,11 @@ "clean_dist_cjs": "rm -rf ./dist/cjs", "clean_dist_es": "rm -rf ./dist/es", "check_circular_dependencies": "madge ./dist/cjs --circular", - "compile_cjs": " tsc dist/cjs/src/index.ts dist/cjs/src/proxy/index.ts -m commonjs --outDir dist/cjs --sourcemap --target ES5 -d --diagnostics --pretty --strict --noImplicitReturns --noUnusedLocals --noUnusedParameters --suppressImplicitAnyIndexErrors --moduleResolution node --noEmitHelpers --importHelpers --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom", - "compile_module_es": "tsc dist/es/src/index.ts dist/es/src/proxy/index.ts -m ES2015 --outDir dist/es --sourcemap --target ES5 -d --diagnostics --pretty --strict --noImplicitReturns --noUnusedLocals --noUnusedParameters --suppressImplicitAnyIndexErrors --moduleResolution node --noEmitHelpers --importHelpers --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom", + "compile_cjs": " tsc dist/cjs/src/index.ts dist/cjs/src/proxy/index.ts -m commonjs --outDir dist/cjs --sourcemap --target ES5 -d --diagnostics --pretty --strict --noImplicitReturns --noUnusedLocals --noUnusedParameters --suppressImplicitAnyIndexErrors --moduleResolution node --noEmitHelpers --importHelpers --downlevelIteration --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom", + "compile_module_es": "tsc dist/es/src/index.ts dist/es/src/proxy/index.ts -m ES2015 --outDir dist/es --sourcemap --target ES5 -d --diagnostics --pretty --strict --noImplicitReturns --noUnusedLocals --noUnusedParameters --suppressImplicitAnyIndexErrors --moduleResolution node --noEmitHelpers --importHelpers --downlevelIteration --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom", "copy_src_cjs": "shx mkdir -p ./dist/cjs/src && shx cp -r ./src/* ./dist/cjs/src", "copy_src_es": "shx mkdir -p ./dist/es/src && shx cp -r ./src/* ./dist/es/src", - "cover": "rm -rf ./.nyc_output ./coverage && NODE_ENV=test nyc --reporter=html --reporter=lcov --exclude=node_modules --exclude=spec-js/test --exclude=spec-js/src/storage/lovefield.js --exclude=spec-js/src/shared/Logger.js --exclude=spec-js/src/utils/option.js --exclude=spec-js/src/utils/valid.js tman --mocha spec-js/test/run.js", + "cover": "rm -rf ./.nyc_output ./coverage && NODE_ENV=test nyc --reporter=html --reporter=lcov --exclude=node_modules --exclude=spec-js/test --exclude=spec-js/src/storage/lovefield.js --exclude=spec-js/src/shared/Logger.js --exclude=spec-js/src/utils/option.js --exclude=spec-js/src/utils/valid.js tman --mocha spec-js/test/run.js && nyc report", "lint": "tslint -c tslint.json src/*.ts --project ./tsconfig.json --type-check src/**/*.ts ./test/**/*.ts -e ./test/e2e/*.ts", "publish_all": "ts-node ./tools/publish.ts", "start": "webpack-dev-server --inline --colors --progress --port 3000", @@ -59,42 +59,44 @@ }, "license": "MIT", "devDependencies": { - "@types/chai": "^4.0.1", - "@types/node": "^8.0.14", + "@types/chai": "^4.0.3", + "@types/lodash": "^4.14.73", + "@types/node": "^8.0.23", "@types/shelljs": "^0.7.4", "@types/sinon": "^2.3.3", "@types/sinon-chai": "^2.7.28", - "awesome-typescript-loader": "^3.2.1", - "chai": "^4.1.0", + "awesome-typescript-loader": "^3.2.3", + "chai": "^4.1.1", "coveralls": "^2.13.1", "css-loader": "^0.28.4", "extract-text-webpack-plugin": "^3.0.0", - "happypack": "^3.0.3", - "html-webpack-plugin": "^2.29.0", + "happypack": "^4.0.0-beta.2", + "html-webpack-plugin": "^2.30.1", + "lodash": "^4.17.4", "madge": "^2.0.0", "moment": "^2.18.1", "node-watch": "^0.5.5", "npm-run-all": "^4.0.2", - "nyc": "^11.0.3", + "nyc": "^11.1.0", "raw-loader": "^0.5.1", - "rxjs": "^5.4.2", + "rxjs": "^5.4.3", "shelljs": "^0.7.8", "shx": "^0.2.2", - "sinon": "^3.0.0", - "sinon-chai": "^2.11.0", + "sinon": "^3.2.0", + "sinon-chai": "^2.13.0", "source-map-loader": "^0.2.1", "style-loader": "^0.18.2", "tman": "^1.7.1", - "ts-node": "^3.2.0", - "tslint": "^5.5.0", + "ts-node": "^3.3.0", + "tslint": "^5.6.0", "tslint-eslint-rules": "^4.1.1", "tslint-loader": "^3.5.3", "typescript": "^2.4.2", - "webpack": "^3.3.0", - "webpack-dev-server": "^2.5.1" + "webpack": "^3.5.5", + "webpack-dev-server": "^2.7.1" }, "dependencies": { - "@types/lovefield": "^2.0.32", + "@types/lovefield": "^2.1.0", "lovefield": "2.1.12", "nesthydrationjs": "^1.0.2" }, diff --git a/src/exception/index.ts b/src/exception/index.ts index b0694c3f..29d1faf8 100644 --- a/src/exception/index.ts +++ b/src/exception/index.ts @@ -1,2 +1,3 @@ export * from './database' export * from './token' +export * from './revert' diff --git a/src/exception/revert.ts b/src/exception/revert.ts new file mode 100644 index 00000000..6920aa1d --- /dev/null +++ b/src/exception/revert.ts @@ -0,0 +1,10 @@ +import { ReactiveDBException } from './Exception' + +export const tokenMustBeSymbol = (token: any) => + new ReactiveDBException(`Symbol type expected, but got ${ typeof token }: ${ token }`) + +export const clauseMissingError = () => + new ReactiveDBException('Clause must be specified when when reverControoler is passed to delete method') + +export const revertBeforeOperationSuccessError = () => + new ReactiveDBException('You can only revert after the operation that you passed RevertController success') diff --git a/src/interface/index.ts b/src/interface/index.ts index 50ea2f4f..475dbc55 100644 --- a/src/interface/index.ts +++ b/src/interface/index.ts @@ -174,3 +174,10 @@ export type Predicate = { } export { StatementType, JoinMode, LeafType, Relationship, DataStoreType, RDBType } + +export interface TablesStruct { + [index: string]: { + table: lf.schema.Table + contextName?: string + } +} diff --git a/src/shared/Traversable.ts b/src/shared/Traversable.ts index 24953a6a..93305dc3 100644 --- a/src/shared/Traversable.ts +++ b/src/shared/Traversable.ts @@ -1,4 +1,4 @@ -import { forEach, getType } from '../utils' +import { forEach, getType, keys } from '../utils' import { TraverseContext } from '../interface' export class Traversable { @@ -29,7 +29,7 @@ export class Traversable { } else if (target && typeof target.keys === 'function') { return Array.from(target.keys()) } else { - return (typeof target === 'object' && target !== null) || Array.isArray(target) ? Object.keys(target) : [] + return (typeof target === 'object' && target !== null) || Array.isArray(target) ? keys(target) : [] } } } diff --git a/src/storage/Database.ts b/src/storage/Database.ts index beb347a7..b8b599e7 100644 --- a/src/storage/Database.ts +++ b/src/storage/Database.ts @@ -3,18 +3,19 @@ import { ErrorObservable } from 'rxjs/observable/ErrorObservable' import { Subscription } from 'rxjs/Subscription' import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable' import * as lf from 'lovefield' +import { __assign as assign } from 'tslib' import * as Exception from '../exception' import * as typeDefinition from './helper/definition' import Version from '../version' import { Traversable } from '../shared' -import { Mutation, Selector, QueryToken, PredicateProvider } from './modules' +import { Mutation, Selector, QueryToken, PredicateProvider, checkPredicate, predicateOperatorNames, RevertController } from './modules' import { dispose, contextTableName, fieldIdentifier, hiddenColName } from './symbols' -import { forEach, clone, contains, tryCatch, hasOwn, getType, assert, identity, warn } from '../utils' -import { createPredicate, createPkClause, mergeTransactionResult, predicatableQuery, lfFactory } from './helper' +import { forEach, clone, contains, tryCatch, hasOwn, getType, assert, identity, warn, keys as objKeys } from '../utils' +import { createPredicate, createPkClause, predicatableQuery, lfFactory, executor } from './helper' import { Relationship, RDBType, DataStoreType, LeafType, StatementType, JoinMode } from '../interface/enum' -import { Record, Fields, JoinInfo, Query, Clause, Predicate } from '../interface' +import { Record, Fields, JoinInfo, Query, Predicate } from '../interface' import { SchemaDef, ColumnDef, ParsedSchema, Association, ScopedHandler } from '../interface' -import { ColumnLeaf, NavigatorLeaf, ExecutorResult, UpsertContext, SelectContext } from '../interface' +import { ColumnLeaf, NavigatorLeaf, ExecutorResult, UpsertContext, SelectContext, TablesStruct } from '../interface' export class Database { @@ -52,7 +53,7 @@ export class Database { const advanced = !this.schemaDefs.has(tableName) && !this.connected assert(advanced, Exception.UnmodifiableTable()) - const hasPK = Object.keys(schema) + const hasPK = objKeys(schema) .some((key: string) => schema[key].primaryKey === true) assert(hasPK, Exception.PrimaryKeyNotProvided()) @@ -109,49 +110,43 @@ export class Database { }) } - insert(tableName: string, raw: T[]): Observable + insert(tableName: string, raw: T[], revertController?: RevertController): Observable - insert(tableName: string, raw: T): Observable + insert(tableName: string, raw: T, revertController?: RevertController): Observable - insert(tableName: string, raw: T | T[]): Observable + insert(tableName: string, raw: T | T[], revertController?: RevertController): Observable - insert(tableName: string, raw: T | T[]): Observable { - return this.database$ - .concatMap(db => { - const schema = this.findSchema(tableName) - const pk = schema.pk - const columnMapper = schema.mapper - const [ table ] = Database.getTables(db, tableName) - const muts: Mutation[] = [] - const entities = clone(raw) - - const iterator = Array.isArray(entities) ? entities : [entities] - - iterator.forEach((entity: any) => { - const mut = new Mutation(db, table) - const hiddenPayload = Object.create(null) - - columnMapper.forEach((mapper, key) => { - // cannot create a hidden column for primary key - if (!hasOwn(entity, key) || key === pk) { - return + insert(tableName: string, raw: T | T[], revertController?: RevertController): Observable { + return this.database$.concatMap(db => { + const { queries, contextIds } = this.buildInsertQuery(db, tableName, raw) + const schema = this.findSchema(tableName) + const pk = schema.pk + const [ table ] = Database.getTables(db, tableName) + return Observable.fromPromise(executor(db, queries)) + .do({ + next: () => { + if (revertController) { + const equalClause = (Array.isArray(raw) ? raw : [raw]) + .map(data => ({ + [pk]: data[pk] + })) + const clause = { $or: equalClause } + const tablesStruct: TablesStruct = { + [tableName]: { + table, contextName: tableName + } + } + const provider = new PredicateProvider(tablesStruct, tableName, clause) + const deleteQuery = + predicatableQuery(db, table, provider.getPredicate(), StatementType.Delete) + revertController.inject( + db, [ deleteQuery ] + ) } - - const val = entity[key] - hiddenPayload[key] = mapper(val) - hiddenPayload[hiddenColName(key)] = val - }) - - mut.patch({ ...entity, ...hiddenPayload }) - mut.withId(pk, entity[pk]) - muts.push(mut) + }, + error: () => contextIds.forEach(id => this.storedIds.delete(id)) }) - - const { contextIds, queries } = Mutation.aggregate(db, muts, []) - contextIds.forEach(id => this.storedIds.add(id)) - return this.executor(db, queries) - .do({ error: () => contextIds.forEach(id => this.storedIds.delete(id)) }) - }) + }) } get(tableName: string, query: Query = {}, mode: JoinMode = JoinMode.imlicit): QueryToken { @@ -159,7 +154,7 @@ export class Database { return new QueryToken(selector$) } - update(tableName: string, clause: Predicate, raw: Partial): Observable { + update(tableName: string, clause: Predicate, raw: Partial, revertController?: RevertController): Observable { const type = getType(raw) if (type !== 'Object') { return Observable.throw(Exception.InvalidType(['Object', type])) @@ -171,7 +166,7 @@ export class Database { } return this.database$ - .concatMap(db => { + .concatMap(db => { const entity = clone(raw) const [ table ] = Database.getTables(db, tableName) const columnMapper = schema!.mapper @@ -189,7 +184,13 @@ export class Database { }) const mut = { ...(entity as any), ...hiddenPayload } - const predicate = createPredicate(table, clause) + const tables = this.buildTablesStructure(table) + const predicate = createPredicate(tables, tableName, clause) + + if (!predicate) { + warn(`The result of parsed Predicate is null, you are deleting all ${ tableName } Table!`) + } + const query = predicatableQuery(db, table, predicate!, StatementType.Update) forEach(mut, (val, key) => { @@ -203,37 +204,80 @@ export class Database { } }) - return this.executor(db, [query]) + if (revertController) { + const columns = Object.keys(raw).map(key => table[key]) + predicatableQuery(db, table, predicate!, StatementType.Select, ...columns) + .exec() + .then(([ value ]) => { + const revertQuery = predicatableQuery(db, table, predicate!, StatementType.Update) + forEach(value, (val, key) => { + const column = table[key] + revertQuery.set(column, val) + }) + revertController.inject( + db, [ revertQuery ] + ) + }) + .catch(e => { + revertController.giveup() + warn(e) + }) + } + + return Observable.fromPromise(executor(db, [ query ])) + .do({ + error: () => { + if (revertController) { + revertController.giveup() + } + } + }) }) } - delete(tableName: string, clause: Predicate = {}): Observable { + delete(tableName: string, clause: Predicate = {}, revertController?: RevertController): Observable { const [pk, err] = tryCatch(this.findPrimaryKey)(tableName) if (err) { return Observable.throw(err) } - return this.database$ - .concatMap(db => { - const [ table ] = Database.getTables(db, tableName) - const column = table[pk!] - const provider = new PredicateProvider(table, clause) - const prefetch = - predicatableQuery(db, table, provider.getPredicate(), StatementType.Select, column) - - return Observable.fromPromise(prefetch.exec()) - .concatMap((scopedIds) => { - const query = predicatableQuery(db, table, provider.getPredicate(), StatementType.Delete) - - scopedIds.forEach((entity: any) => - this.storedIds.delete(fieldIdentifier(tableName, entity[pk!]))) - - return this.executor(db, [query]).do({ error: () => { - scopedIds.forEach((entity: any) => - this.storedIds.add(fieldIdentifier(tableName, entity[pk!]))) - }}) - }) - }) + if (revertController) { + if (!clause) { + throw Exception.clauseMissingError() + } + } + + return this.database$.concatMap(db => { + const [ table ] = Database.getTables(db, tableName) + const tablesStruct = this.buildTablesStructure(table) + const provider = new PredicateProvider(tablesStruct, tableName, clause) + const predicate = provider.getPredicate() + if (!predicate) { + warn(`The result of parsed Predicate is null, you are deleting all ${ tableName } Tables!`) + } + const query = predicatableQuery(db, table, predicate, StatementType.Delete) + const columns = revertController ? [] as any : [ pk ] + return Observable.fromPromise( + this.deletePrefetch(db, table, provider, columns) + ) + .concatMap(scopedIds => + Observable.fromPromise(executor(db, [query])) + .do({ + next: () => { + if (revertController) { + const { queries } = this.buildInsertQuery(db, tableName, scopedIds) + revertController.inject(db, queries) + } + scopedIds.forEach((entity: any) => + this.storedIds.delete(fieldIdentifier(tableName, entity[pk!]))) + }, + error: () => { + scopedIds.forEach((entity: any) => + this.storedIds.add(fieldIdentifier(tableName, entity[pk!]))) + } + }) + ) + }) } upsert(tableName: string, raw: T): Observable @@ -252,7 +296,7 @@ export class Database { const { contextIds, queries } = Mutation.aggregate(db, insert, update) if (queries.length > 0) { contextIds.forEach(id => this.storedIds.add(id)) - return this.executor(db, queries) + return Observable.fromPromise(executor(db, queries)) .do({ error: () => contextIds.forEach(id => this.storedIds.delete(id)) }) } else { return Observable.of({ result: false, insert: 0, update: 0, delete: 0, select: 0 }) @@ -260,7 +304,7 @@ export class Database { }) } - remove(tableName: string, clause: Clause = {}): Observable { + remove(tableName: string, clause: Predicate = {}): Observable { const [schema, err] = tryCatch(this.findSchema)(tableName) if (err) { return Observable.throw(err) @@ -269,10 +313,15 @@ export class Database { return this.database$.concatMap((db) => { const [ table ] = Database.getTables(db, tableName) - const predicate = createPredicate(table, clause.where) + const tables = this.buildTablesStructure(table) + const predicate = createPredicate(tables, tableName, clause) + + if (!predicate) { + warn(`The result of parsed Predicate is null, you are removing all ${ tableName } Tables!`) + } const queries: lf.query.Builder[] = [] - const removedIds: any = [] + const removedIds: string[] = [] queries.push(predicatableQuery(db, table, predicate!, StatementType.Delete)) const prefetch = predicatableQuery(db, table, predicate!, StatementType.Select) @@ -286,10 +335,10 @@ export class Database { const scope = this.createScopedHandler(db, queries, removedIds) return disposeHandler(rootEntities, scope) .do(() => removedIds.forEach((id: string) => this.storedIds.delete(id))) - .concatMap(() => this.executor(db, queries)) + .concatMap(() => executor(db, queries)) } else { removedIds.forEach((id: string) => this.storedIds.delete(id)) - return this.executor(db, queries) + return executor(db, queries) } }) .do({ error: () => @@ -312,7 +361,7 @@ export class Database { db.getSchema().tables().map(t => db.delete().from(t))) return this.database$.concatMap(db => { - return cleanup.concatMap(queries => this.executor(db, queries)) + return cleanup.concatMap(queries => executor(db, queries)) .do(() => { db.close() this.schemas.clear() @@ -420,14 +469,26 @@ export class Database { const containFields = !!clause.fields const containKey = containFields ? contains(pk, clause.fields!) : true - const fields: Set = containFields ? new Set(clause.fields) : new Set(schema.columns.keys()) - const { table, columns, joinInfo, definition } = - this.traverseQueryFields(db, tableName, fields, containKey, !containFields, [], {}, mode) + const [ additionJoinInfo ] = tryCatch(this.buildJoinFieldsFromPredicate.bind(this))(clause.where!, tableName) + + const fields = containFields ? clause.fields : Array.from(schema.columns.keys()) + const newFields = containFields && additionJoinInfo ? this.mergeFields(fields!, [ additionJoinInfo as Fields ]) : fields + const fieldsSet: Set = new Set(newFields) + const tablesStruct: TablesStruct = Object.create(null) + + const { table, columns, joinInfo, definition, contextName } = + this.traverseQueryFields(db, tableName, fieldsSet, containKey, !containFields, [], {}, tablesStruct, mode) const query = predicatableQuery(db, table!, null, StatementType.Select, ...columns) - joinInfo.forEach((info: JoinInfo) => - query.leftOuterJoin(info.table, info.predicate)) + joinInfo.forEach((info: JoinInfo) => { + const predicate = info.predicate + if (predicate) { + query.leftOuterJoin(info.table, predicate) + } + }) + + this.paddingTablesStruct(db, this.getAllRelatedTables(tableName, contextName), tablesStruct) const orderDesc = (clause.orderBy || []).map(desc => { return { @@ -445,7 +506,7 @@ export class Database { mainTable: table! } const { limit, skip } = clause - const provider = new PredicateProvider(table!, clause.where) + const provider = new PredicateProvider(tablesStruct, contextName, clause.where) return new Selector(db, query, matcher, provider, limit, skip, orderDesc) }) @@ -490,6 +551,67 @@ export class Database { } } + private buildInsertQuery(db: lf.Database, tableName: string, raw: T | T[]) { + const schema = this.findSchema(tableName) + const pk = schema.pk + const columnMapper = schema.mapper + const [ table ] = Database.getTables(db, tableName) + const muts: Mutation[] = [] + const entities = clone(raw) + + const iterator = Array.isArray(entities) ? entities : [entities] + + iterator.forEach((entity: any) => { + const mut = new Mutation(db, table) + const hiddenPayload = Object.create(null) + + columnMapper.forEach((mapper, key) => { + // cannot create a hidden column for primary key + if (!hasOwn(entity, key) || key === pk) { + return + } + + const val = entity[key] + hiddenPayload[key] = mapper(val) + hiddenPayload[hiddenColName(key)] = val + }) + + mut.patch({ ...entity, ...hiddenPayload }) + mut.withId(pk, entity[pk]) + muts.push(mut) + }) + + const { contextIds, queries } = Mutation.aggregate(db, muts, []) + contextIds.forEach(id => this.storedIds.add(id)) + + return { queries, contextIds } + } + + private deletePrefetch( + db: lf.Database, + table: lf.schema.Table, + provider: PredicateProvider, + columnNames: string[] + ) { + let columns: lf.schema.Column[] + // build revert query + if (!columnNames.length) { + const tableName = table.getName() + columns = Array.from(this.findSchema(tableName).columns.keys()) + .map(columnName => { + const column = table[columnName] + const hiddenName = hiddenColName(columnName) + const hiddenCol = table[hiddenName] + return hiddenCol || column + }) + } else { + columns = columnNames.map(columnName => table[columnName]) + } + const prefetch = + predicatableQuery(db, table, provider.getPredicate(), StatementType.Select, ...columns) + return prefetch.exec() + } + // context 用来标记DFS路径中的所有出现过的表,用于解决self-join时的二义性 // path 用来标记每个查询路径上出现的表,用于解决circular reference private traverseQueryFields( @@ -500,6 +622,7 @@ export class Database { glob: boolean, path: string[] = [], context: Record = {}, + tablesStruct: TablesStruct = Object.create(null), mode: JoinMode ) { const schema = this.findSchema(tableName) @@ -510,7 +633,7 @@ export class Database { const joinInfo: JoinInfo[] = [] if (mode === JoinMode.imlicit && contains(tableName, path)) { // thinking mode: implicit & explicit - return { columns, joinInfo, advanced: false, table: null, definition: null } + return { columns, joinInfo, advanced: false, table: null, definition: null, contextName: tableName } } else { path.push(tableName) } @@ -533,7 +656,10 @@ export class Database { const suffix = (context[tableName] || 0) + 1 context[tableName] = suffix const contextName = contextTableName(tableName, suffix) - const currentTable = Database.getTables(db, tableName)[0].as(contextName) + const [ originTable ] = Database.getTables(db, tableName) + const currentTable = originTable.as(contextName) + + assign(tablesStruct, this.buildTablesStructure(currentTable, contextName)) const handleAdvanced = (ret: any, key: string, defs: Association | ColumnDef) => { if (!ret.advanced) { @@ -542,17 +668,20 @@ export class Database { columns.push(...ret.columns) assert(!rootDefinition[key], Exception.AliasConflict(key, tableName)) - if ((defs as ColumnDef).column) { rootDefinition[key] = defs } else { const { where, type } = defs as Association rootDefinition[key] = typeDefinition.revise(type!, ret.definition) - const [ predicate, err ] = tryCatch(createPredicate)(currentTable, where(ret.table)) - if (err) { + tablesStruct[`${ contextName }@${ key }`] = { + table: ret.table, + contextName: ret.contextName + } + const [ predicate, e ] = tryCatch(createPredicate)(tablesStruct, contextName, where(ret.table)) + if (e) { warn( - `Failed to build predicate, since ${err.message}` + - `, on table: ${ret.table.getName()}` + `Failed to build predicate, since ${e.message}` + + `, on table: ${ ret.table.getName() }` ) } const joinLink = predicate @@ -603,14 +732,13 @@ export class Database { case LeafType.navigator: const { containKey, fields, assocaiation } = ctx.leaf as NavigatorLeaf const ret = - this.traverseQueryFields(db, assocaiation.name, new Set(fields), containKey, glob, path.slice(0), context, mode) + this.traverseQueryFields(db, assocaiation.name, new Set(fields), containKey, glob, path.slice(0), context, tablesStruct, mode) handleAdvanced(ret, ctx.key, assocaiation) ctx.skip() break } }) - - return { columns, joinInfo, advanced: true, table: currentTable, definition: rootDefinition } + return { columns, joinInfo, advanced: true, table: currentTable, definition: rootDefinition, contextName } } private traverseCompound( @@ -724,7 +852,8 @@ export class Database { entities.forEach(entity => { const pkVal = entity[pk] const clause = createPkClause(pk, pkVal) - const predicate = createPredicate(table, clause) + const tables = this.buildTablesStructure(table) + const predicate = createPredicate(tables, tableName, clause.where) const query = predicatableQuery(db, table, predicate!, StatementType.Delete) queryCollection.push(query) @@ -734,7 +863,8 @@ export class Database { const get = (where: Predicate | null = null) => { const [ table ] = Database.getTables(db, tableName) - const [ predicate, err ] = tryCatch(createPredicate)(table, where) + const tables = this.buildTablesStructure(table) + const [ predicate, err ] = tryCatch(createPredicate)(tables, tableName, where) if (err) { return Observable.throw(err) } @@ -747,20 +877,171 @@ export class Database { } } - private executor(db: lf.Database, queries: lf.query.Builder[]) { - const tx = db.createTransaction() - const handler = { - error: () => warn(`Execute failed, transaction is already marked for rollback.`) + private buildTablesStructure(defaultTable: lf.schema.Table, aliasName?: string, tablesStruct: TablesStruct = Object.create(null)) { + if (aliasName) { + tablesStruct[aliasName] = { + table: defaultTable, + contextName: aliasName + } + } else { + const tableName = defaultTable.getName() + tablesStruct[tableName] = { + table: defaultTable, + contextName: tableName + } } + return tablesStruct + } - return Observable.fromPromise(tx.exec(queries)) - .do(handler) - .map((ret) => { - return { - result: true, - ...mergeTransactionResult(queries, ret) + private getAllRelatedTables(tableName: string, contextName: string) { + const tablesStructure = new Map() + const schemas = [ + { + schema: this.findSchema(tableName), + relatedTo: contextName + } + ] + while (schemas.length) { + const { schema, relatedTo } = schemas.pop()! + for (const [ key, val ] of schema.associations) { + const relatedName = val.name + if (!tablesStructure.has(relatedName)) { + const path = `${ relatedTo }@${ key }` + tablesStructure.set(relatedName, path) + schemas.push({ + schema: this.findSchema(relatedName), + relatedTo: relatedName + }) + } + } + } + return tablesStructure + } + + private paddingTablesStruct(db: lf.Database, tables: Map, tablesStruct: TablesStruct): TablesStruct { + forEach(tablesStruct, structure => { + const tableName = structure.table.getName() + tables.delete(tableName) + }) + tables.forEach((key, tableName) => { + const [ table ] = Database.getTables(db, tableName) + tablesStruct[key] = { table, contextName: tableName } + }) + return tablesStruct + } + + private buildJoinFieldsFromPredicate(predicate: Predicate, tableName: string) { + let result = Object.create(null) + let schema = this.findSchema(tableName) + + const buildJoinInfo = (keys: string[]) => { + return keys.reduce((acc, k, currentIndex) => { + if (currentIndex < keys.length - 1) { + const _newTableName = schema.associations.get(k)!.name + schema = this.findSchema(_newTableName) + const pk = schema.pk + const f = currentIndex !== keys.length - 2 ? Object.create(null) : keys[currentIndex + 1] + acc[k] = [pk] + if (f !== pk) { + acc[k].push(f) + } + return f + } + }, result) + } + + forEach(predicate, (val, key) => { + if (!predicateOperatorNames.has(key)) { + if (checkPredicate(val)) { + const keys = key.split('.') + const newTableName = this.getTablenameFromNestedPredicate(key, tableName) + if (keys.length > 1) { + buildJoinInfo(keys) + } else { + result[key] = [schema.pk, this.buildJoinFieldsFromPredicate(val, newTableName)] + } + } else { + const keys = key.split('.') + if (keys.length > 1) { + buildJoinInfo(keys) + } else { + result = key + } + } + } + }) + + return typeof result === 'object' && objKeys(result).length ? result : null + } + + private mergeFields(fields: Fields[], predicateFields: Fields[]) { + const newFields = this.buildFieldsTree(fields) + predicateFields = this.buildFieldsTree(predicateFields) + + const nestedFields = newFields[newFields.length - 1] + const nestedPredicateFields = predicateFields[predicateFields.length - 1] + forEach(predicateFields, val => { + if (typeof val === 'string') { + if (newFields.indexOf(val) === -1) { + newFields.push(val) + } + } + }) + + if (typeof nestedPredicateFields === 'object') { + if (typeof nestedFields === 'object') { + forEach(nestedPredicateFields, (val, key) => { + const existedVal = nestedFields[key] + if (existedVal) { + this.mergeFields(existedVal, val) + } else { + nestedFields[key] = val + } + }) + } else { + newFields.push(nestedPredicateFields) + } + } + return newFields + } + + private buildFieldsTree(fields: Fields[]) { + const dist: Fields[] = [] + const nestedFields: { [index: string]: Fields[] }[] = [] + forEach(fields, field => { + if (typeof field === 'string') { + dist.push(field) + } else { + nestedFields.push(field) + } + }) + + const nestedField = nestedFields.reduce((acc, field) => { + forEach(field, (val, key) => { + const existedVal = acc[key] + if (existedVal) { + acc[key] = this.mergeFields(existedVal, val) + } else { + acc[key] = val } }) + return acc + }, {}) + if (objKeys(nestedField).length) { + dist.push(nestedField) + } + return dist + } + + private getTablenameFromNestedPredicate(def: string, tableName: string) { + const defs = def.split('.') + let newTableName: string + let schema = this.findSchema(tableName) + forEach(defs, de => { + newTableName = schema.associations.get(de)!.name + schema = this.findSchema(newTableName) + }) + return newTableName! } } diff --git a/src/storage/helper/create-predicate.ts b/src/storage/helper/create-predicate.ts index 78c12503..27d435e3 100644 --- a/src/storage/helper/create-predicate.ts +++ b/src/storage/helper/create-predicate.ts @@ -1,7 +1,6 @@ -import * as lf from 'lovefield' import { PredicateProvider } from '../modules/PredicateProvider' -import { Predicate } from '../../interface' +import { Predicate, TablesStruct } from '../../interface' -export function createPredicate(table: lf.schema.Table, clause: Predicate | null = null) { - return clause ? new PredicateProvider(table, clause).getPredicate() : null +export function createPredicate(tables: TablesStruct, tableName: string, clause: Predicate) { + return new PredicateProvider(tables, tableName, clause).getPredicate() } diff --git a/src/storage/helper/executor.ts b/src/storage/helper/executor.ts new file mode 100644 index 00000000..fe76cb3d --- /dev/null +++ b/src/storage/helper/executor.ts @@ -0,0 +1,17 @@ +import { mergeTransactionResult } from './merge-transaction-result' +import { ExecutorResult } from '../../interface' +import { warn } from '../../utils' + +export function executor(db: lf.Database, queries: lf.query.Builder[]): Promise { + const tx = db.createTransaction() + + return tx.exec(queries) + .then(ret => ({ + result: true, + ...mergeTransactionResult(queries, ret) + })) + .catch((e: Error) => { + warn(`Execute failed, transaction is already marked for rollback.`) + throw e + }) +} diff --git a/src/storage/helper/index.ts b/src/storage/helper/index.ts index 8d78b62b..33f389aa 100644 --- a/src/storage/helper/index.ts +++ b/src/storage/helper/index.ts @@ -6,4 +6,5 @@ export * from './predicatable-query' export * from './merge-transaction-result' export * from './db-factory' export * from './graph' +export * from './executor' export { definition } diff --git a/src/storage/helper/predicatable-query.ts b/src/storage/helper/predicatable-query.ts index 64973a2d..0f96a1a8 100644 --- a/src/storage/helper/predicatable-query.ts +++ b/src/storage/helper/predicatable-query.ts @@ -13,16 +13,14 @@ export function predicatableQuery( db: lf.Database, table: lf.schema.Table, predicate: lf.Predicate | null, - type: StatementType.Delete, - ...columns: lf.schema.Column[] + type: StatementType.Delete ): lf.query.Delete export function predicatableQuery( db: lf.Database, table: lf.schema.Table, predicate: lf.Predicate | null, - type: StatementType.Update, - ...columns: lf.schema.Column[] + type: StatementType.Update ): lf.query.Update export function predicatableQuery( diff --git a/src/storage/modules/Mutation.ts b/src/storage/modules/Mutation.ts index 8a3a1d21..d1487563 100644 --- a/src/storage/modules/Mutation.ts +++ b/src/storage/modules/Mutation.ts @@ -1,4 +1,4 @@ -import { forEach, assert, warn } from '../../utils' +import { forEach, assert, warn, keys } from '../../utils' import { fieldIdentifier } from '../symbols' import * as Exception from '../../exception' @@ -21,7 +21,7 @@ export class Mutation { } static aggregate(db: lf.Database, insert: Mutation[], update: Mutation[]) { - const keys: any[] = [] + const ks: any[] = [] const insertQueries: lf.query.Insert[] = [] const map = new Map() @@ -31,7 +31,7 @@ export class Mutation { const tableName = table.getName() const acc = map.get(tableName) - keys.push(fieldIdentifier(tableName, curr.refId())) + ks.push(fieldIdentifier(tableName, curr.refId())) if (acc) { acc.push(row) @@ -50,13 +50,13 @@ export class Mutation { const updateQueries: lf.query.Update[] = [] for (let i = 0; i < update.length; i++) { - if (Object.keys(update[i].params).length > 0) { + if (keys(update[i].params).length > 0) { updateQueries.push(update[i].toUpdater()) } } return { - contextIds: keys, + contextIds: ks, queries: insertQueries.concat(updateQueries as any[]) } } diff --git a/src/storage/modules/PredicateProvider.ts b/src/storage/modules/PredicateProvider.ts index c6684262..bd8c5b20 100644 --- a/src/storage/modules/PredicateProvider.ts +++ b/src/storage/modules/PredicateProvider.ts @@ -1,6 +1,6 @@ import * as lf from 'lovefield' -import { forEach, warn } from '../../utils' -import { ValueLiteral, VaildEqType, Predicate, PredicateMeta } from '../../interface' +import { forEach, warn, concat, keys } from '../../utils' +import { ValueLiteral, VaildEqType, Predicate, PredicateMeta, TablesStruct } from '../../interface' const predicateFactory = { @@ -67,14 +67,26 @@ const compoundPredicateFactory = { }, } +export const predicateOperatorNames = + new Set(concat(keys(predicateFactory), keys(compoundPredicateFactory))) + export class PredicateProvider { + private table: lf.schema.Table | null + constructor( - private table: lf.schema.Table, + private tables: TablesStruct, + private tableName: string, private meta?: Predicate - ) { } + ) { + const tableDef = this.tables[this.tableName] + this.table = tableDef ? tableDef.table : null + } getPredicate(): lf.Predicate | null { + if (!this.table) { + return null + } const predicates = this.meta ? this.normalizeMeta(this.meta) : [] if (predicates.length) { if (predicates.length === 1) { @@ -92,7 +104,8 @@ export class PredicateProvider { return pred ? JSON.stringify(this.meta) : '' } - private normalizeMeta(meta: Predicate, column?: lf.schema.Column): lf.Predicate[] { + private normalizeMeta(meta: Predicate, column?: lf.schema.Column, parentKey?: string): lf.Predicate[] { + const table = this.table! const buildSinglePred = (col: lf.schema.Column, val: any, key: string): lf.Predicate => this.checkMethod(key) ? predicateFactory[key](col, val) : col.eq(val as ValueLiteral) @@ -105,15 +118,30 @@ export class PredicateProvider { if (this.checkCompound(key)) { nestedPreds = this.normalizeMeta(val as Predicate, column) resultPred = compoundPredicateFactory[key](nestedPreds) - } else if (this.checkPredicate(val)) { - nestedPreds = this.normalizeMeta(val as any, this.table[key]) + } else if (checkPredicate(val)) { + nestedPreds = this.normalizeMeta(val as any, table[key], key) resultPred = compoundPredicateFactory['$and'](nestedPreds) } else { - const _column = column || this.table[key] + if (parentKey && !this.checkMethod(key)) { + key = `${ parentKey }.${ key }` + } + const ks: string[] = key.split('.') + let _column: lf.schema.Column + if (!column) { + if (ks.length === 1) { + _column = table[key] + } else { + const columnKey = ks.pop()! + const tableName = this.getAliasTableName(ks) + _column = this.tables[tableName].table[columnKey] + } + } else { + _column = column + } if (_column) { resultPred = buildSinglePred(_column, val, key) } else { - warn(`Failed to build predicate, since column: ${key} is not exist, on table: ${this.table.getName()}`) + warn(`Failed to build predicate, since column: ${key} is not exist, on table: ${table.getName()}`) return } } @@ -124,6 +152,19 @@ export class PredicateProvider { return predicates } + private getAliasTableName(ks: string[]) { + let { length } = ks + let ctxName = this.tableName + let resultKey = this.tableName + while (length > 0) { + const localKey = ks.shift()! + resultKey = `${ ctxName }@${ localKey }` + ctxName = this.tables[resultKey].contextName! + length = ks.length + } + return resultKey + } + private checkMethod(methodName: string) { return typeof predicateFactory[methodName] === 'function' } @@ -132,11 +173,11 @@ export class PredicateProvider { return typeof compoundPredicateFactory[methodName] === 'function' } - private checkPredicate(val: Partial> | ValueLiteral) { - return val && typeof val === 'object' && - !(val instanceof Array) && - !(val instanceof RegExp) && - !(val instanceof (lf.schema as any).BaseColumn) - } +} +export function checkPredicate(val: Partial> | ValueLiteral) { + return val && typeof val === 'object' && + !(val instanceof Array) && + !(val instanceof RegExp) && + !(val instanceof (lf.schema as any).BaseColumn) } diff --git a/src/storage/modules/RevertController.ts b/src/storage/modules/RevertController.ts new file mode 100644 index 00000000..95193589 --- /dev/null +++ b/src/storage/modules/RevertController.ts @@ -0,0 +1,67 @@ +import * as lf from 'lovefield' +import { ExecutorResult } from '../../interface' +import { executor } from '../helper' +import { tokenMustBeSymbol, revertBeforeOperationSuccessError } from '../../exception/revert' + +export interface RevertToken extends String { + kind?: 'revert-token' +} + +export interface RevertControllerInteface { + revert(): Promise +} + +export class RevertController implements RevertControllerInteface { + static create() { + return new RevertController + } + + static getToken(token: Symbol) { + if (typeof token !== typeof RevertController.symbolTemplate) { + throw tokenMustBeSymbol(token) + } + return RevertController.controllers.get(token) || null + } + + private static controllers = new Map() + + private static symbolTemplate = Symbol('template-for-revert') + + private readonly symbol: Symbol + private queries: lf.query.Builder[] + private db: lf.Database + + revert() { + if (!this.db || !this.queries) { + throw revertBeforeOperationSuccessError() + } + return executor(this.db, this.queries) + } + + giveup() { + RevertController.controllers.delete(this.symbol) + } + + toToken() { + return this.symbol + } + + inject(db: lf.Database, queries: lf.query.Builder[]) { + this.db = db + this.queries = queries + return this + } + + merge(...revertCtrls: RevertController[]) { + const dist = RevertController.create() + const queries = revertCtrls.reduce((acc, cur) => { + return acc.concat(cur.queries) + }, this.queries) + + return dist.inject(this.db, queries) + } + + private constructor () { + this.symbol = Symbol(`revert-token-${ Date.now() }`) + } +} diff --git a/src/storage/modules/index.ts b/src/storage/modules/index.ts index b4c88805..cfd41ee7 100644 --- a/src/storage/modules/index.ts +++ b/src/storage/modules/index.ts @@ -3,3 +3,4 @@ export * from './Selector' export * from './ProxySelector' export * from './PredicateProvider' export * from './QueryToken' +export * from './RevertController' diff --git a/src/utils/concat.ts b/src/utils/concat.ts new file mode 100644 index 00000000..f3d25d1c --- /dev/null +++ b/src/utils/concat.ts @@ -0,0 +1,9 @@ +import { forEach } from './for-each' + +// side effect +export function concat(target: T[], patch: T[]) { + forEach(patch, p => { + target.push(p) + }) + return target +} diff --git a/src/utils/for-each.ts b/src/utils/for-each.ts index 6774f9f9..6cc77ce4 100644 --- a/src/utils/for-each.ts +++ b/src/utils/for-each.ts @@ -1,4 +1,5 @@ import { noop } from './noop' +import { keys } from './keys' export function forEach (target: Array, eachFunc: (val: T, key: number) => void, inverse?: boolean): void @@ -40,12 +41,12 @@ export function forEach (target: any, eachFunc: (val: any, key: any) => any, inv } } else if (typeof target === 'object') { - const keys = Object.keys(target) + const ks = keys(target) let key: string - length = keys.length + length = ks.length let i = -1 while (++i < length) { - key = keys[i] + key = ks[i] if (eachFunc(target[key], key) === false) { break } diff --git a/src/utils/index.ts b/src/utils/index.ts index 60cfed79..e6cf222d 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -9,4 +9,6 @@ export * from './for-each' export * from './identity' export * from './get-type' export * from './try-catch' +export * from './concat' +export * from './keys' export { warn } from './warn' diff --git a/src/utils/keys.ts b/src/utils/keys.ts new file mode 100644 index 00000000..43f018b3 --- /dev/null +++ b/src/utils/keys.ts @@ -0,0 +1 @@ +export const keys = (target: any) => Object.keys(target) diff --git a/test/schemas/Organization.ts b/test/schemas/Organization.ts new file mode 100644 index 00000000..f04a0e06 --- /dev/null +++ b/test/schemas/Organization.ts @@ -0,0 +1,20 @@ +import { TeambitionTypes, Database, RDBType } from '../index' + +export interface OrganizationSchema { + _id: TeambitionTypes.OrganizationId + name: string + isArchived: boolean +} + +export default (db: Database) => db.defineSchema('Organization', { + _id: { + type: RDBType.STRING, + primaryKey: true + }, + name: { + type: RDBType.STRING + }, + isArchived: { + type: RDBType.BOOLEAN + } +}) diff --git a/test/schemas/Project.ts b/test/schemas/Project.ts index 282c74f4..406b25a5 100644 --- a/test/schemas/Project.ts +++ b/test/schemas/Project.ts @@ -1,16 +1,22 @@ import { TeambitionTypes, Database, RDBType, Relationship } from '../index' - +import { OrganizationSchema } from './Organization' export interface ProjectSchema { _id: TeambitionTypes.ProjectId + _organizationId: string name: string isArchived: boolean posts: any[] + organization: OrganizationSchema } + export default (db: Database) => db.defineSchema('Project', { _id: { type: RDBType.STRING, primaryKey: true }, + _organizationId: { + type: RDBType.STRING + }, name: { type: RDBType.STRING }, @@ -25,5 +31,14 @@ export default (db: Database) => db.defineSchema('Project', { _id: ref.belongTo }) } + }, + organization: { + type: Relationship.oneToOne, + virtual: { + name: 'Organization', + where: (organizationTable) => ({ + _organizationId: organizationTable._id + }) + } } }) diff --git a/test/schemas/Task.ts b/test/schemas/Task.ts index a28919ed..20681079 100644 --- a/test/schemas/Task.ts +++ b/test/schemas/Task.ts @@ -1,5 +1,5 @@ import { RDBType, Relationship } from '../index' -import { TeambitionTypes, Database, SubtaskSchema } from '../index' +import { TeambitionTypes, Database, SubtaskSchema, ProjectSchema } from '../index' export interface TaskSchema { _id: TeambitionTypes.TaskId @@ -12,12 +12,7 @@ export interface TaskSchema { _stageId: TeambitionTypes.StageId _tasklistId: TeambitionTypes.TasklistId accomplished: string - project?: { - _id: TeambitionTypes.ProjectId - name: string, - isArchived: boolean, - posts?: any[] - } + project?: ProjectSchema subtasks: SubtaskSchema[] subtasksCount: number created: string, diff --git a/test/schemas/index.ts b/test/schemas/index.ts index e422c22b..b0ac5613 100644 --- a/test/schemas/index.ts +++ b/test/schemas/index.ts @@ -4,6 +4,7 @@ import ProjectSelectMetadata from './Project' import SubtaskSelectMetadata from './Subtask' import TaskSelectMetadata from './Task' import PostSelectMetadata from './Post' +import OrganizationSelectMetadata from './Organization' import EngineerSelectMetadata from './Engineer' import ModuleSelectMetadata from './Module' import ProgramSelectMetadata from './Program' @@ -16,10 +17,12 @@ export default (db: Database) => { EngineerSelectMetadata(db) ModuleSelectMetadata(db) ProgramSelectMetadata(db) + OrganizationSelectMetadata(db) } export { ProjectSchema } from './Project' export { PostSchema } from './Post' +export { OrganizationSchema } from './Organization' export { SubtaskSchema } from './Subtask' export { TaskSchema } from './Task' export { ProgramSchema } from './Program' diff --git a/test/specs/storage/Database.public.spec.ts b/test/specs/storage/Database.public.spec.ts index 25bd2981..ddffb0d9 100644 --- a/test/specs/storage/Database.public.spec.ts +++ b/test/specs/storage/Database.public.spec.ts @@ -4,12 +4,14 @@ import { describe, it, beforeEach, afterEach } from 'tman' import { expect, assert, use } from 'chai' import * as sinon from 'sinon' import * as SinonChai from 'sinon-chai' +import { chain } from 'lodash' + import { uuid, checkExecutorResult } from '../../utils' import schemaFactory from '../../schemas' import { TestFixture2 } from '../../schemas/Test' import { scenarioGen, programGen, postGen, taskGen, subtaskGen } from '../../utils/generators' -import { RDBType, DataStoreType, Database, clone, forEach, JoinMode } from '../../index' -import { TaskSchema, ProjectSchema, PostSchema, ModuleSchema, ProgramSchema, SubtaskSchema } from '../../index' +import { RDBType, DataStoreType, Database, clone, forEach, JoinMode, Logger, RevertController } from '../../index' +import { TaskSchema, ProjectSchema, PostSchema, ModuleSchema, ProgramSchema, SubtaskSchema, OrganizationSchema } from '../../index' import { InvalidQuery, NonExistentTable, InvalidType, PrimaryKeyNotProvided, NotConnected, Selector } from '../../index' use(SinonChai) @@ -22,9 +24,10 @@ export default describe('Database Testcase: ', () => { let version = 0 const taskCleanup = (tasks: TaskSchema[]) => - tasks.forEach(t => { + tasks.map(t => { delete t.project delete t.subtasks + return t }) const refreshDB = () => { @@ -463,6 +466,7 @@ export default describe('Database Testcase: ', () => { const projects: ProjectSchema[] = [] const posts: PostSchema[] = [] const tasks: TaskSchema[] = [] + const organizations: OrganizationSchema[] = [] associationFixture.forEach(f => { forEach(f, (value, key) => { @@ -472,6 +476,9 @@ export default describe('Database Testcase: ', () => { if (value.posts) { posts.push(...value.posts) } + if (value.organization) { + organizations.push(value.organization) + } projects.push(value) } }) @@ -482,7 +489,8 @@ export default describe('Database Testcase: ', () => { database.insert('Task', tasks), database.insert('Subtask', subtasks), database.insert('Project', projects), - database.insert('Post', posts) + database.insert('Post', posts), + database.insert('Organization', organizations) ] Observable.forkJoin(...queries).subscribe(() => { @@ -505,6 +513,88 @@ export default describe('Database Testcase: ', () => { expect(keys(result.project)).to.deep.equal(refFields) }) + it('should get association by nested Association query', function* () { + const queryToken = database.get('Task', { + where: { 'project._id': innerTarget.project._id } + }) + + const results = yield queryToken.values() + const [ result ] = results + expect(results.length).to.equal(1) + expect(result).to.deep.equal(innerTarget) + }) + + it('should get association by deep nested Association query', function* () { + const queryToken = database.get('Task', { + where: { 'project.organization._id': innerTarget.project._organizationId } + }) + + const results = yield queryToken.values() + const [ result ] = results + expect(results.length).to.equal(1) + expect(result).to.deep.equal(innerTarget) + }) + + it('should get value by deep nested Association query without association fields', function* () { + const fields = ['_id', 'content'] + const queryToken = database.get('Task', { + fields, + where: { 'project.organization._id': innerTarget.project._organizationId } + }) + + const results = yield queryToken.values() + + expect(results.length).to.equal(1) + }) + + it('should get value by deep nested Association query without association fields', function* () { + const fields = ['_id', 'content'] + const queryToken = database.get('Task', { + fields, + where: { + project: { + 'organization._id': innerTarget.project._organizationId + } + } + }) + + const results = yield queryToken.values() + + expect(results.length).to.equal(1) + }) + + it('should merge fields when get value by deep nested Association query with nested association fields', function* () { + const fields = ['_id', 'content', { project: ['_id'] }, { project: [ { organization: [ '_id' ] } ] }] + const queryToken = database.get('Task', { + fields, + where: { + 'project.organization': { + _id: innerTarget.project._organizationId + } + } + }) + + const results = yield queryToken.values() + + expect(results.length).to.equal(1) + }) + + it('should merge fields when get value by deep nested Association query without nested association fields', function* () { + const fields = ['_id', 'content', { subtasks: ['_id'] }] + const queryToken = database.get('Task', { + fields, + where: { + 'project.organization': { + _id: innerTarget.project._organizationId + } + } + }) + + const results = yield queryToken.values() + + expect(results.length).to.equal(1) + }) + it('should apply `skip` clause on multi joined query', function* () { const result = yield database.get('Task', { skip: 20 }).values() @@ -757,7 +847,7 @@ export default describe('Database Testcase: ', () => { } }) - const errSpy = sinon.spy((): void => void 0) + const errSpy = sinon.spy() tmpDB.connect() tmpDB.update(T, { id: 1 }, { @@ -843,6 +933,19 @@ export default describe('Database Testcase: ', () => { } }) + it('should warn if predicate is null', function* () { + const spy = sinon.spy(Logger, 'warn') + + yield database.delete('Task') + + const result = yield database.get('Task').values() + + expect(result).deep.equal([]) + expect(spy.callCount).to.equal(1) + + spy.restore() + }) + }) describe('Method: upsert', () => { @@ -1049,9 +1152,7 @@ export default describe('Database Testcase: ', () => { const execRet1 = yield database.upsert('Program', program) yield database.delete('Program', { - where: { - _id: program._id - } + _id: program._id }) const execRet2 = yield database.upsert('Program', program) @@ -1210,6 +1311,24 @@ export default describe('Database Testcase: ', () => { } }) + it('should warn if predicate is null', function* () { + const spy = sinon.spy(Logger, 'warn') + const [ program ] = programGen(1, 1) + + yield database.upsert('Engineer', program.owner) + const ret1 = yield database.get('Engineer').values() + const execRet = yield database.remove('Engineer') + const ret2 = yield database.get('Engineer').values() + + expect(spy.callCount).to.equal(1) + + checkExecutorResult(execRet, 0, 1, 0) + expect(ret1).have.lengthOf(1) + expect(ret2).to.deep.equal([]) + + spy.restore() + }) + }) describe('case: Relational Scenario', () => { @@ -1289,4 +1408,146 @@ export default describe('Database Testcase: ', () => { }) + describe('case: Revert', () => { + let fixture: TaskSchema[] + let target: TaskSchema + let revertController: RevertController + const maxCount = 20 + + beforeEach(function* () { + database.connect() + fixture = taskGen(maxCount) + target = fixture[0] + taskCleanup(fixture) + yield database.insert('Task', fixture) + revertController = RevertController.create() + }) + + afterEach(function* () { + yield database.dispose() + }) + + it('should revert insert', function* () { + const [ taskFixture ] = taskCleanup(taskGen(1)) + + yield database.insert('Task', taskFixture, revertController) + + const signal$ = database.get('Task', { + where: { + _id: taskFixture._id + } + }) + .values() + + yield signal$.do(result => { + expect(taskCleanup(result)).to.deep.equal([taskFixture]) + }) + + yield revertController.revert() + + yield signal$.do(result => { + expect(result.length).to.equal(0) + }) + }) + + it('should revert update', function* () { + const note = 'foo' + + const signal$ = database.get('Task', { + where: { _id: target._id } + }) + .values() + + const [ original ] = yield signal$ + + yield database.update('Task', target._id as string, { note }, revertController) + + yield signal$ + .do(([ result ]) => { + expect(result.note).to.equal(note) + }) + + yield revertController.revert() + + yield signal$ + .do(([ result ]) => { + expect(result).to.deep.equal(original) + }) + }) + + it('should revert delete', function* () { + const fields = chain(target) + .omit('subtasks', 'project') + .keys() + .value() + + yield database.delete('Task', { _id: target._id }, revertController) + + yield database.get('Task', { + where: { _id: target._id } + }) + .values() + .do((result) => { + expect(result).to.deep.equal([]) + }) + + yield revertController.revert() + + yield database.get('Task', { + where: { _id: target._id }, fields + }) + .values() + .do(([ result ]) => { + expect(result).to.deep.equal(target) + }) + }) + + it('should revert mutiple actions', function* () { + const fields = chain(target) + .omit('subtasks', 'project') + .keys() + .value() + + const buildFixture = async function (fixt: TaskSchema, revertCtrl: RevertController) { + await database.delete('Task', { _id: fixt._id }, revertCtrl) + .toPromise() + + await database.get('Task', { + where: { _id: fixt._id } + }) + .values() + .do((result) => { + expect(result).to.deep.equal([]) + }) + .toPromise() + + return revertCtrl + } + + const ctrls: RevertController[] = yield fixture.map(fixt => { + const ctrl = RevertController.create() + return buildFixture(fixt, ctrl) + }) + + const firstCtrl = ctrls.pop() + const distCtrl = firstCtrl.merge(...ctrls) + + yield distCtrl.revert() + + const assterEqual = async function (fixt: TaskSchema) { + await database.get('Task', { + where: { _id: fixt._id }, fields + }) + .values() + .do(([ result ]) => { + expect(result).to.deep.equal(fixt) + }) + .toPromise() + } + + fixture.forEach(assterEqual) + + }) + }) + }) diff --git a/test/specs/storage/modules/PredicateProvider.spec.ts b/test/specs/storage/modules/PredicateProvider.spec.ts index 0da574b4..0ff236e3 100644 --- a/test/specs/storage/modules/PredicateProvider.spec.ts +++ b/test/specs/storage/modules/PredicateProvider.spec.ts @@ -1,7 +1,7 @@ import * as lf from 'lovefield' import { describe, it, beforeEach } from 'tman' import { expect } from 'chai' -import { PredicateProvider, lfFactory, DataStoreType } from '../../../index' +import { PredicateProvider, lfFactory, DataStoreType, Predicate } from '../../../index' export default describe('PredicateProvider test', () => { const dataLength = 1000 @@ -14,8 +14,13 @@ export default describe('PredicateProvider test', () => { let db: lf.Database let table: lf.schema.Table + let tableDef: { TestPredicateProvider: { table: lf.schema.Table } } let version = 1 + function predicateFactory(desc: Predicate) { + return new PredicateProvider(tableDef, 'TestPredicateProvider', desc) + } + beforeEach(function* () { const schemaBuilder = lf.schema.create('PredicateProviderDatabase', version++) const db$ = lfFactory(schemaBuilder, { @@ -47,12 +52,15 @@ export default describe('PredicateProvider test', () => { nullable: i >= 300 ? null : false })) } + + tableDef = { TestPredicateProvider: { table } } + yield db.insert().into(table).values(rows).exec() }) describe('PredicateProvider#getPredicate', () => { it('invalid key should be ignored', function* () { - const fn = () => new PredicateProvider(table, { + const fn = () => predicateFactory({ nonExist: 'whatever' }).getPredicate() @@ -62,15 +70,21 @@ export default describe('PredicateProvider test', () => { expect(result).deep.equal(expectResult) }) + it('should get null when tablesStructure is not contain tableName', () => { + const predicateProvider = new PredicateProvider({}, 'whatever', { _id: 1 }) + const result = predicateProvider.getPredicate() + expect(result).to.be.null + }) + it('empty meta should ok', function* () { - const predicate = new PredicateProvider(table, {}).getPredicate() + const predicate = predicateFactory({}).getPredicate() expect(predicate).to.be.null const result = yield execQuery(db, table, predicate) expect(result).to.have.lengthOf(1000) }) it('literal value should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: 20 }).getPredicate() @@ -81,7 +95,7 @@ export default describe('PredicateProvider test', () => { }) it('$ne should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $ne: 20 } @@ -94,7 +108,7 @@ export default describe('PredicateProvider test', () => { }) it('$lt should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $lt: 20 } @@ -107,7 +121,7 @@ export default describe('PredicateProvider test', () => { }) it('$lte should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $lte: 19 } @@ -120,7 +134,7 @@ export default describe('PredicateProvider test', () => { }) it('$gt should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time2: { $gt: 20 } @@ -133,7 +147,7 @@ export default describe('PredicateProvider test', () => { }) it('$gte should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time2: { $gte: 21 } @@ -147,7 +161,7 @@ export default describe('PredicateProvider test', () => { it('$match should ok', function* () { const regExp = /\:(\d{0,1}1$)/ - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ name: { $match: regExp } @@ -161,7 +175,7 @@ export default describe('PredicateProvider test', () => { it('$notMatch should ok', function* () { const regExp = /\:(\d{0,1}1$)/ - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ name: { $notMatch: regExp } @@ -175,7 +189,7 @@ export default describe('PredicateProvider test', () => { }) it('$between should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $between: [1, 20] } @@ -188,7 +202,7 @@ export default describe('PredicateProvider test', () => { }) it('$has should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ times: { $has: 'times: 10' } @@ -204,7 +218,7 @@ export default describe('PredicateProvider test', () => { it('$in should ok', function* () { const seed = [10, 20, 30, 10000] - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $in: seed } @@ -217,7 +231,7 @@ export default describe('PredicateProvider test', () => { }) it('$isNull should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ nullable: { $isNull: true } @@ -230,7 +244,7 @@ export default describe('PredicateProvider test', () => { }) it('$isNotNull should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ nullable: { $isNotNull: true } @@ -243,7 +257,7 @@ export default describe('PredicateProvider test', () => { }) it('$not should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ $not: { time1: 0 } @@ -255,7 +269,7 @@ export default describe('PredicateProvider test', () => { }) it('$and should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $and: { $lt: 200, @@ -271,7 +285,7 @@ export default describe('PredicateProvider test', () => { }) it('$or should ok', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $or: { $gte: dataLength - 50, @@ -287,7 +301,7 @@ export default describe('PredicateProvider test', () => { }) it('non-compound predicates should be combined with $and', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ $or: { time1: { $gte: 0, $lt: 50 }, time2: { $gt: 0, $lte: 50 } @@ -306,7 +320,7 @@ export default describe('PredicateProvider test', () => { }) it('compoundPredicate should skip null/undefined property', function* () { - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $or: { $gte: dataLength - 50, @@ -323,7 +337,7 @@ export default describe('PredicateProvider test', () => { it('complex PredicateDescription should ok', function* () { const reg = /\:(\d{0,1}1$)/ - const predicate = new PredicateProvider(table, { + const predicate = predicateFactory({ time1: { $or: { $gte: dataLength - 50, @@ -357,15 +371,15 @@ export default describe('PredicateProvider test', () => { describe('PredicateProvider#toString', () => { it('convert empty PredicateProvider to empty string', () => { - expect(new PredicateProvider(table, {}).toString()).to.equal('') + expect(predicateFactory({}).toString()).to.equal('') }) it('convert to string representation of the predicate', () => { - expect(new PredicateProvider(table, { notExist: 20 }).toString()).to.equal('') + expect(predicateFactory({ notExist: 20 }).toString()).to.equal('') - expect(new PredicateProvider(table, { time1: 20 }).toString()).to.equal('{"time1":20}') + expect(predicateFactory({ time1: 20 }).toString()).to.equal('{"time1":20}') - expect(new PredicateProvider(table, { + expect(predicateFactory({ $or: { time1: { $gte: 0, $lt: 50 }, time2: { $gt: 0, $lte: 50 } diff --git a/test/specs/storage/modules/Selector.spec.ts b/test/specs/storage/modules/Selector.spec.ts index 24bfe533..fd7fa653 100644 --- a/test/specs/storage/modules/Selector.spec.ts +++ b/test/specs/storage/modules/Selector.spec.ts @@ -10,6 +10,7 @@ import { lfFactory, ShapeMatcher, PredicateProvider, + Predicate, TokenConcatFailed } from '../../../index' @@ -25,12 +26,17 @@ interface Fixture { export default describe('Selector test', () => { let db: lf.Database let table: lf.schema.Table + let tableDef: { TestSelectMetadata: { table: lf.schema.Table } } let version = 1 let tableShape: ShapeMatcher let storeData: any[] let subscription: Subscription + function predicateFactory(desc: Predicate) { + return new PredicateProvider(tableDef, 'TestSelectMetadata', desc) + } + beforeEach(function * () { const schemaBuilder = lf.schema.create('SelectorTest', version ++) const db$ = lfFactory(schemaBuilder, { @@ -93,6 +99,8 @@ export default describe('Selector test', () => { } } } + + tableDef = { TestSelectMetadata: { table } } }) afterEach(() => { @@ -116,7 +124,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }) + predicateFactory({ time: { $gte: 50 } }) ) const results = yield selector.values() @@ -131,7 +139,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) const result = yield selector.values() @@ -147,7 +155,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) @@ -160,7 +168,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), null, null, [ { column: table['priority'], orderBy: lf.Order.ASC }, @@ -187,7 +195,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }) + predicateFactory({ time: { $gte: 50 } }) ) const newName = 'test name change' @@ -211,7 +219,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, {}), + predicateFactory({}), ) const row = { _id: '_id:939.5', name: 'name:939.5', time: 939.5, priority: 10 } @@ -240,7 +248,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }) + predicateFactory({ time: { $gte: 50 } }) ) const spy = sinon.spy((): void => void 0) @@ -268,7 +276,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }) + predicateFactory({ time: { $gte: 50 } }) ) const newName = 'test name change' @@ -307,7 +315,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }) + predicateFactory({ time: { $gte: 50 } }) ) const changes = selector.changes() @@ -345,7 +353,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $eq: impossibleTime } }), + predicateFactory({ time: { $eq: impossibleTime } }), 1, 0 // 添加 limit, skip 以模仿实际使用场景 ) @@ -386,7 +394,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) @@ -415,7 +423,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20, [ { column: table['priority'], orderBy: lf.Order.DESC }, @@ -473,7 +481,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) @@ -514,7 +522,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) @@ -544,7 +552,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 960 } }), + predicateFactory({ time: { $gt: 960 } }), 20, 20 ) @@ -574,7 +582,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $lt: 0 } }), + predicateFactory({ time: { $lt: 0 } }), 20 ) @@ -616,7 +624,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }) + predicateFactory({ time: { $gt: 50 } }) ) const spy = sinon.spy() @@ -634,7 +642,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 10000 } }) + predicateFactory({ time: { $gt: 10000 } }) ) const spy = sinon.spy() @@ -652,7 +660,7 @@ export default describe('Selector test', () => { const selector = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) @@ -679,12 +687,12 @@ export default describe('Selector test', () => { selector1 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $lt: 50 } }) + predicateFactory({ time: { $lt: 50 } }) ) selector2 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { + predicateFactory({ time: { $gte: 50, $lt: 100 @@ -694,14 +702,14 @@ export default describe('Selector test', () => { const select1And2 = selector1.combine(selector2) - selector3 = new Selector(db, db.select().from(table), tableShape, new PredicateProvider(table, { + selector3 = new Selector(db, db.select().from(table), tableShape, predicateFactory({ time: { $gte: 100, $lt: 150 } })) - selector4 = new Selector(db, db.select().from(table), tableShape, new PredicateProvider(table, { + selector4 = new Selector(db, db.select().from(table), tableShape, predicateFactory({ time: { $gte: 150, $lt: 200 @@ -775,13 +783,13 @@ export default describe('Selector test', () => { const selector5 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) const selector6 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 100 } }), + predicateFactory({ time: { $gt: 100 } }), 20, 20 ) @@ -853,13 +861,13 @@ export default describe('Selector test', () => { const _selector1 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }) + predicateFactory({ time: { $gte: 50 } }) ) const _selector2 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $lte: 250 } }) + predicateFactory({ time: { $lte: 250 } }) ) const selector = _selector1.combine(_selector2) @@ -885,34 +893,34 @@ export default describe('Selector test', () => { selector1 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 0 ) selector2 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 20 ) selector3 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 40 ) selector4 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 10, 60 ) selector5 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 70 ) @@ -1001,7 +1009,7 @@ export default describe('Selector test', () => { const selector6 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $lt: 930 } }), + predicateFactory({ time: { $lt: 930 } }), 20, 0, [ { column: table['priority'], orderBy: lf.Order.DESC }, @@ -1011,7 +1019,7 @@ export default describe('Selector test', () => { const selector7 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $lt: 930 } }), + predicateFactory({ time: { $lt: 930 } }), 20, 20, [ { column: table['priority'], orderBy: lf.Order.DESC }, @@ -1052,13 +1060,13 @@ export default describe('Selector test', () => { const selector6 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 10, 0 ) const selector7 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 11 ) @@ -1071,13 +1079,13 @@ export default describe('Selector test', () => { const selector6 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 0 ) const selector7 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 20 ) @@ -1097,14 +1105,14 @@ export default describe('Selector test', () => { const selector7 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 20 ) const selector6_1 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 0 ) const selector7_1 = new Selector(db, @@ -1127,13 +1135,13 @@ export default describe('Selector test', () => { const selector6 = new Selector(db, db.select(table['name']).from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 0 ) const selector7 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 20 ) @@ -1147,13 +1155,13 @@ export default describe('Selector test', () => { const selector6 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 0 ) const selector7 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gt: 50 } }), + predicateFactory({ time: { $gt: 50 } }), 20, 40 ) @@ -1180,14 +1188,14 @@ export default describe('Selector test', () => { selector1 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 0 ) .map(mapFn) selector2 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 20 ) .map(mapFn) @@ -1197,7 +1205,7 @@ export default describe('Selector test', () => { selector3 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 40 ) .map(mapFn2) @@ -1205,7 +1213,7 @@ export default describe('Selector test', () => { selector4 = new Selector(db, db.select().from(table), tableShape, - new PredicateProvider(table, { time: { $gte: 50 } }), + predicateFactory({ time: { $gte: 50 } }), 20, 60 ) .map(mapFn3) diff --git a/test/specs/utils/utils.spec.ts b/test/specs/utils/utils.spec.ts index 079f5252..286615c3 100644 --- a/test/specs/utils/utils.spec.ts +++ b/test/specs/utils/utils.spec.ts @@ -1,4 +1,4 @@ -import { forEach, clone, getType, assert, hash } from '../../index' +import { forEach, clone, getType, assert, hash, concat, keys } from '../../index' import { describe, it } from 'tman' import { expect } from 'chai' @@ -407,4 +407,52 @@ export default describe('Utils Testcase: ', () => { }) + describe('Func: concat', () => { + it('should return target', () => { + const target: number[] = [] + const result = concat(target, [1, 2, 3]) + expect(result).to.equal(target) + }) + + it('concat result should be correct', () => { + const target = ['foo', 'bar'] + const patch = [1, 2, 3] + const expected = target.concat(patch as any) + const result = concat(target, patch as any) + expect(result).to.deep.equal(expected) + }) + + it('should have side effect', () => { + const target = ['foo', 'bar'] + const patch = [1, 2, 3] + const expected = target.concat(patch as any) + concat(target, patch as any) + expect(target).to.deep.equal(expected) + }) + }) + + describe('Func: keys', () => { + it('should return object keys', () => { + const target = { + foo: 1, + bar: 2 + } + + expect(keys(target)).to.deep.equal(Object.keys(target)) + }) + + it('should not get property keys in prototype', () => { + function a (this: any) { + this.foo = 1 + this.bar = 2 + } + + a.prototype.whatever = 3 + + const target: any = new (a as any) + expect(keys(target).length).to.equal(2) + expect(keys(target).indexOf('wahtever')).to.equal(-1) + }) + }) + }) diff --git a/test/utils/generators/task-generator.ts b/test/utils/generators/task-generator.ts index b1210df2..a6ce08c0 100644 --- a/test/utils/generators/task-generator.ts +++ b/test/utils/generators/task-generator.ts @@ -14,6 +14,7 @@ export default function (limit: number) { const _projectId = uuid() const _stageId = uuid() const _creatorId = uuid() + const _organizationId = uuid() const _executorId = random.rnd(20) ? uuid() : _creatorId const involves = [ _executorId ] if (_creatorId !== _executorId) { @@ -35,7 +36,13 @@ export default function (limit: number) { _id: _projectId, name: 'project name: ' + uuid(), isArchived: true, - posts: postGen(5, _projectId) + posts: postGen(5, _projectId), + _organizationId: _organizationId, + organization: { + _id: _organizationId, + name: 'organization name: ' + uuid(), + isArchived: false, + } }, involveMembers: involveMembersGen(15, involves), created: moment().add(6 - random.number(0, 12), 'month').add(30 - random.number(0, 30), 'day').toISOString() diff --git a/yarn.lock b/yarn.lock index 267fbb3f..17a2b3fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,32 +2,32 @@ # yarn lockfile v1 -"@types/chai@*", "@types/chai@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@types/chai/-/chai-4.0.1.tgz#37fea779617cfec3fd2b19a0247e8bbdd5133bf6" +"@types/chai@*", "@types/chai@^4.0.3": + version "4.0.3" + resolved "https://registry.npmjs.org/@types/chai/-/chai-4.0.3.tgz#6c2264b195cd2bb4c95c108487e13df0c8567c3e" "@types/glob@*": - version "5.0.30" - resolved "https://registry.npmjs.org/@types/glob/-/glob-5.0.30.tgz#1026409c5625a8689074602808d082b2867b8a51" + version "5.0.31" + resolved "https://registry.npmjs.org/@types/glob/-/glob-5.0.31.tgz#6cb8500bd170750c1948f785cc5828e9cff0c36a" dependencies: "@types/minimatch" "*" "@types/node" "*" -"@types/lovefield@^2.0.32": +"@types/lodash@^4.14.73": + version "4.14.73" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.73.tgz#9837e47db8643ba5bcef2c7921f37d90f9c24213" + +"@types/lovefield@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@types/lovefield/-/lovefield-2.1.0.tgz#513d88b343c137c033706d4d684a8003256fe485" "@types/minimatch@*": - version "2.0.29" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a" - -"@types/node@*": - version "8.0.19" - resolved "https://registry.npmjs.org/@types/node/-/node-8.0.19.tgz#e46e2b0243de7d03f15b26b45c59ebb84f657a4e" + version "3.0.0" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.0.tgz#a8b68c324817169b6004b432a598478a5d8f025a" -"@types/node@^8.0.14": - version "8.0.14" - resolved "https://registry.npmjs.org/@types/node/-/node-8.0.14.tgz#4a19dc6bb61d16c01cbadc7b30ac23518fff176b" +"@types/node@*", "@types/node@^8.0.23": + version "8.0.23" + resolved "https://registry.npmjs.org/@types/node/-/node-8.0.23.tgz#c746697004782346594a0d755c34425bbf3014d2" "@types/shelljs@^0.7.4": version "0.7.4" @@ -125,21 +125,21 @@ ansi-styles@^2.2.1: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" ansi-styles@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz#09c202d5c917ec23188caa5c9cb9179cd9547750" + version "3.2.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" dependencies: - color-convert "^1.0.0" + color-convert "^1.9.0" any-promise@^1.0.0: version "1.3.0" resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" anymatch@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + version "1.3.2" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" dependencies: - arrify "^1.0.0" micromatch "^2.1.5" + normalize-path "^2.0.0" app-module-path@~1.1.0: version "1.1.0" @@ -318,12 +318,12 @@ autoprefixer@^6.3.1: postcss "^5.2.16" postcss-value-parser "^3.2.3" -awesome-typescript-loader@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.2.1.tgz#600f5d552da3e5501e3e5c19aa3e8986059f8947" +awesome-typescript-loader@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.2.3.tgz#aa2119b7c808a031e2b28945b031450a8975367f" dependencies: colors "^1.1.2" - enhanced-resolve "^3.1.0" + enhanced-resolve "3.3.0" loader-utils "^1.1.0" lodash "^4.17.4" micromatch "^3.0.3" @@ -339,25 +339,25 @@ aws4@^1.2.1: version "1.6.0" resolved "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -babel-code-frame@^6.11.0, babel-code-frame@^6.22.0: - version "6.22.0" - resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" +babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: - chalk "^1.1.0" + chalk "^1.1.3" esutils "^2.0.2" - js-tokens "^3.0.0" + js-tokens "^3.0.2" babel-generator@^6.18.0: - version "6.25.0" - resolved "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc" + version "6.26.0" + resolved "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" dependencies: babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-types "^6.25.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" detect-indent "^4.0.0" jsesc "^1.3.0" - lodash "^4.2.0" - source-map "^0.5.0" + lodash "^4.17.4" + source-map "^0.5.6" trim-right "^1.0.1" babel-messages@^6.23.0: @@ -366,49 +366,49 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" -babel-runtime@^6.22.0: - version "6.23.0" - resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: core-js "^2.4.0" - regenerator-runtime "^0.10.0" + regenerator-runtime "^0.11.0" babel-template@^6.16.0: - version "6.25.0" - resolved "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" + version "6.26.0" + resolved "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.25.0" - babel-types "^6.25.0" - babylon "^6.17.2" - lodash "^4.2.0" + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" -babel-traverse@^6.18.0, babel-traverse@^6.25.0: - version "6.25.0" - resolved "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" +babel-traverse@^6.18.0, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" dependencies: - babel-code-frame "^6.22.0" + babel-code-frame "^6.26.0" babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-types "^6.25.0" - babylon "^6.17.2" - debug "^2.2.0" - globals "^9.0.0" - invariant "^2.2.0" - lodash "^4.2.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" -babel-types@^6.18.0, babel-types@^6.25.0: - version "6.25.0" - resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" +babel-types@^6.18.0, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" dependencies: - babel-runtime "^6.22.0" + babel-runtime "^6.26.0" esutils "^2.0.2" - lodash "^4.2.0" - to-fast-properties "^1.0.1" + lodash "^4.17.4" + to-fast-properties "^1.0.3" -babylon@^6.17.0, babylon@^6.17.2, babylon@^6.17.4: - version "6.17.4" - resolved "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" +babylon@^6.17.0, babylon@^6.17.4, babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" balanced-match@^0.4.2: version "0.4.2" @@ -451,8 +451,8 @@ big.js@^3.1.3: resolved "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" binary-extensions@^1.0.0: - version "1.8.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + version "1.10.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" block-stream@*: version "0.0.9" @@ -465,8 +465,8 @@ bluebird@^3.4.7: resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.7" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.7.tgz#ddb048e50d9482790094c13eb3fcfc833ce7ab46" + version "4.11.8" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" bonjour@^3.5.0: version "3.5.0" @@ -673,8 +673,8 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000702" - resolved "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000702.tgz#a60cd30f3ef44ae7b5ed12e9d9b70d4bff6352cb" + version "1.0.30000715" + resolved "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000715.tgz#0b9b5c795950dfbaf301a8806bafe87f126da8ca" caseless@~0.11.0: version "0.11.0" @@ -691,9 +691,9 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chai@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/chai/-/chai-4.1.0.tgz#331a0391b55c3af8740ae9c3b7458bc1c3805e6d" +chai@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/chai/-/chai-4.1.1.tgz#66e21279e6f3c6415ff8231878227900e2171b39" dependencies: assertion-error "^1.0.1" check-error "^1.0.1" @@ -702,7 +702,7 @@ chai@^4.1.0: pathval "^1.0.0" type-detect "^4.0.0" -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -712,9 +712,9 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d" +chalk@^2.0.0, chalk@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" dependencies: ansi-styles "^3.1.0" escape-string-regexp "^1.0.5" @@ -820,7 +820,7 @@ collection-visit@^0.2.1: map-visit "^0.1.5" object-visit "^0.3.4" -color-convert@^1.0.0, color-convert@^1.3.0: +color-convert@^1.3.0, color-convert@^1.9.0: version "1.9.0" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" dependencies: @@ -862,13 +862,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.9.x, commander@~2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" - dependencies: - graceful-readlink ">= 1.0.0" - -commander@^2.8.1, commander@^2.9.0, commander@~2.11.0: +commander@2.11.x, commander@^2.8.1, commander@^2.9.0, commander@~2.11.0: version "2.11.0" resolved "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -891,10 +885,10 @@ component-emitter@^1.2.1: resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" compressible@~2.0.10: - version "2.0.10" - resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.10.tgz#feda1c7f7617912732b29bf8cf26252a20b9eecd" + version "2.0.11" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.11.tgz#16718a75de283ed8e604041625a2064586797d8a" dependencies: - mime-db ">= 1.27.0 < 2" + mime-db ">= 1.29.0 < 2" compression@^1.5.2: version "1.7.0" @@ -955,10 +949,10 @@ copy-descriptor@^0.1.0: resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" core-js@^2.4.0: - version "2.4.1" - resolved "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + version "2.5.0" + resolved "https://registry.npmjs.org/core-js/-/core-js-2.5.0.tgz#569c050918be6486b3837552028ae0466b717086" -core-util-is@~1.0.0: +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -999,7 +993,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^4, cross-spawn@^4.0.0: +cross-spawn@^4: version "4.0.2" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" dependencies: @@ -1153,13 +1147,7 @@ debug-log@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" -debug@2.6.7: - version "2.6.7" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" - dependencies: - ms "2.0.0" - -debug@2.6.8, debug@^2.2.0, debug@^2.3.3, debug@^2.6.3, debug@^2.6.8: +debug@2.6.8, debug@^2.2.0, debug@^2.3.3, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8: version "2.6.8" resolved "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: @@ -1241,9 +1229,9 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depd@1.1.0, depd@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" +depd@1.1.1, depd@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" dependency-tree@5.9.1: version "5.9.1" @@ -1342,8 +1330,8 @@ dns-equal@^1.0.0: resolved "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" dns-packet@^1.0.1: - version "1.1.1" - resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-1.1.1.tgz#2369d45038af045f3898e6fa56862aed3f40296c" + version "1.2.2" + resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-1.2.2.tgz#a8a26bec7646438963fc86e06f8f8b16d6c8bf7a" dependencies: ip "^1.1.0" safe-buffer "^5.0.1" @@ -1420,8 +1408,8 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" electron-to-chromium@^1.2.7: - version "1.3.16" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.16.tgz#d0e026735754770901ae301a21664cba45d92f7d" + version "1.3.18" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.18.tgz#3dcc99da3e6b665f6abbc71c28ad51a2cd731a9c" elliptic@^6.0.0: version "6.4.0" @@ -1443,7 +1431,7 @@ encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" -enhanced-resolve@^3.1.0, enhanced-resolve@^3.3.0: +enhanced-resolve@3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.3.0.tgz#950964ecc7f0332a42321b673b38dc8ff15535b3" dependencies: @@ -1452,6 +1440,15 @@ enhanced-resolve@^3.1.0, enhanced-resolve@^3.3.0: object-assign "^4.0.1" tapable "^0.2.5" +enhanced-resolve@^3.4.0: + version "3.4.1" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.7" + enhanced-resolve@~3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.0.3.tgz#df14c06b5fc5eecade1094c9c5a12b4b3edc0b62" @@ -1478,13 +1475,14 @@ error-ex@^1.2.0: is-arrayish "^0.2.1" es-abstract@^1.4.3: - version "1.7.0" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c" + version "1.8.0" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.0.tgz#3b00385e85729932beffa9163bbea1234e932914" dependencies: es-to-primitive "^1.1.1" function-bind "^1.1.0" + has "^1.0.1" is-callable "^1.1.3" - is-regex "^1.0.3" + is-regex "^1.0.4" es-to-primitive@^1.1.1: version "1.1.1" @@ -1495,8 +1493,8 @@ es-to-primitive@^1.1.1: is-symbol "^1.0.1" es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.24" - resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.24.tgz#a55877c9924bc0c8d9bd3c2cbe17495ac1709b14" + version "0.10.27" + resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.27.tgz#bf926b058c62b1cb5de1a887930673b6aa6d9a66" dependencies: es6-iterator "2" es6-symbol "~3.1" @@ -1652,12 +1650,12 @@ evp_bytestokey@^1.0.0: dependencies: create-hash "^1.1.1" -execa@^0.5.0: - version "0.5.1" - resolved "https://registry.npmjs.org/execa/-/execa-0.5.1.tgz#de3fb85cb8d6e91c85bcbceb164581785cb57b36" +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" dependencies: - cross-spawn "^4.0.0" - get-stream "^2.2.0" + cross-spawn "^5.0.1" + get-stream "^3.0.0" is-stream "^1.1.0" npm-run-path "^2.0.0" p-finally "^1.0.0" @@ -1689,8 +1687,8 @@ expand-range@^1.8.1: fill-range "^2.1.0" express@^4.13.3: - version "4.15.3" - resolved "https://registry.npmjs.org/express/-/express-4.15.3.tgz#bab65d0f03aa80c358408972fc700f916944b662" + version "4.15.4" + resolved "https://registry.npmjs.org/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1" dependencies: accepts "~1.3.3" array-flatten "1.1.1" @@ -1698,23 +1696,23 @@ express@^4.13.3: content-type "~1.0.2" cookie "0.3.1" cookie-signature "1.0.6" - debug "2.6.7" - depd "~1.1.0" + debug "2.6.8" + depd "~1.1.1" encodeurl "~1.0.1" escape-html "~1.0.3" etag "~1.8.0" - finalhandler "~1.0.3" + finalhandler "~1.0.4" fresh "0.5.0" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" parseurl "~1.3.1" path-to-regexp "0.1.7" - proxy-addr "~1.1.4" - qs "6.4.0" + proxy-addr "~1.1.5" + qs "6.5.0" range-parser "~1.2.0" - send "0.15.3" - serve-static "1.12.3" + send "0.15.4" + serve-static "1.12.4" setprototypeof "1.0.3" statuses "~1.3.1" type-is "~1.6.15" @@ -1759,9 +1757,9 @@ extract-text-webpack-plugin@^3.0.0: schema-utils "^0.3.0" webpack-sources "^1.0.1" -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" +extsprintf@1.3.0, extsprintf@^1.2.0: + version "1.3.0" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" fast-deep-equal@^1.0.0: version "1.0.0" @@ -1831,11 +1829,11 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" -finalhandler@~1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89" +finalhandler@~1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7" dependencies: - debug "2.6.7" + debug "2.6.8" encodeurl "~1.0.1" escape-html "~1.0.3" on-finished "~2.3.0" @@ -2014,12 +2012,9 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" get-value@^2.0.3, get-value@^2.0.5, get-value@^2.0.6: version "2.0.6" @@ -2055,7 +2050,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@~7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^9.0.0: +globals@^9.18.0: version "9.18.0" resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -2103,14 +2098,13 @@ handlebars@^4.0.3: optionalDependencies: uglify-js "^2.6" -happypack@^3.0.3: - version "3.1.0" - resolved "https://registry.npmjs.org/happypack/-/happypack-3.1.0.tgz#8bc55e3701bacff718d3889cb88b5021641cad59" +happypack@^4.0.0-beta.2: + version "4.0.0-beta.2" + resolved "https://registry.npmjs.org/happypack/-/happypack-4.0.0-beta.2.tgz#552e75d17292d5142c84be32f9b255d91e40e020" dependencies: async "1.5.0" json-stringify-safe "5.0.1" - loader-utils "0.2.16" - mkdirp "0.5.1" + loader-utils "1.1.0" serialize-error "^2.1.0" har-schema@^1.0.5: @@ -2229,21 +2223,21 @@ html-entities@^1.2.0: resolved "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" html-minifier@^3.2.3: - version "3.5.2" - resolved "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.2.tgz#d73bc3ff448942408818ce609bf3fb0ea7ef4eb7" + version "3.5.3" + resolved "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.3.tgz#4a275e3b1a16639abb79b4c11191ff0d0fcf1ab9" dependencies: camel-case "3.0.x" clean-css "4.1.x" - commander "2.9.x" + commander "2.11.x" he "1.1.x" ncname "1.0.x" param-case "2.1.x" relateurl "0.2.x" uglify-js "3.0.x" -html-webpack-plugin@^2.29.0: - version "2.29.0" - resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.29.0.tgz#e987f421853d3b6938c8c4c8171842e5fd17af23" +html-webpack-plugin@^2.30.1: + version "2.30.1" + resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz#7f9c421b7ea91ec460f56527d78df484ee7537d5" dependencies: bluebird "^3.4.7" html-minifier "^3.2.3" @@ -2265,11 +2259,11 @@ http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" -http-errors@~1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257" +http-errors@~1.6.1, http-errors@~1.6.2: + version "1.6.2" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" dependencies: - depd "1.1.0" + depd "1.1.1" inherits "2.0.3" setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" @@ -2363,7 +2357,7 @@ interpret@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" -invariant@^2.2.0: +invariant@^2.2.2: version "2.2.2" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: @@ -2373,13 +2367,13 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -ip@^1.1.0: +ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" -ipaddr.js@1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec" +ipaddr.js@1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" is-absolute-url@^2.0.0: version "2.1.0" @@ -2426,22 +2420,20 @@ is-date-object@^1.0.1: resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" is-descriptor@^0.1.0: - version "0.1.5" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.5.tgz#e3fb8b4ab65f3a37373388e18b401d78c58cbea7" + version "0.1.6" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" - kind-of "^3.0.2" - lazy-cache "^2.0.2" + kind-of "^5.0.0" is-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.0.tgz#d6ec686f238f6b02f23757abe12cf6b2ea2790f9" + version "1.0.1" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.1.tgz#2c6023599bde2de9d5d2c8b9a9d94082036b6ef2" dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" - kind-of "^3.0.2" - lazy-cache "^2.0.2" + kind-of "^5.0.0" is-dotfile@^1.0.0: version "1.0.3" @@ -2558,15 +2550,15 @@ is-property@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" -is-regex@^1.0.3: +is-regex@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" dependencies: has "^1.0.1" is-relative-path@^1.0.1, is-relative-path@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.1.tgz#ac72793a2d60c049e50676e04a24a8d8f263dc26" + version "1.0.2" + resolved "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" is-stream@^1.1.0: version "1.1.0" @@ -2667,7 +2659,7 @@ js-base64@^2.1.9: version "2.1.9" resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce" -js-tokens@^3.0.0: +js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -2705,8 +2697,8 @@ jsesc@~0.5.0: resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" json-loader@^0.5.4: - version "0.5.4" - resolved "https://registry.npmjs.org/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" + version "0.5.7" + resolved "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" json-schema-traverse@^0.3.0: version "0.3.1" @@ -2747,13 +2739,13 @@ jsonpointer@^4.0.0: resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" jsprim@^1.2.2: - version "1.4.0" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + version "1.4.1" + resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" dependencies: assert-plus "1.0.0" - extsprintf "1.0.2" + extsprintf "1.3.0" json-schema "0.2.3" - verror "1.3.6" + verror "1.10.0" just-extend@^1.1.22: version "1.1.22" @@ -2771,6 +2763,10 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" +kind-of@^5.0.0: + version "5.0.2" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.0.2.tgz#f57bec933d9a2209ffa96c5c08343607b7035fda" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -2821,22 +2817,22 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@0.2.16, loader-utils@^0.2.16, loader-utils@~0.2.2: - version "0.2.16" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" +loader-utils@1.1.0, loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" - object-assign "^4.0.1" -loader-utils@^1.0.2, loader-utils@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" +loader-utils@^0.2.16, loader-utils@~0.2.2: + version "0.2.17" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" + object-assign "^4.0.1" locate-path@^2.0.0: version "2.0.0" @@ -2861,7 +2857,7 @@ lodash@4.13.1: version "4.13.1" resolved "https://registry.npmjs.org/lodash/-/lodash-4.13.1.tgz#83e4b10913f48496d4d16fec4a560af2ee744b68" -lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0: +lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4: version "4.17.4" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -2875,6 +2871,10 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" +loglevel@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.4.1.tgz#95b383f91a3c2756fd4ab093667e4309161f2bcd" + lolex@^1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6" @@ -3065,15 +3065,15 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.27.0 < 2", mime-db@~1.27.0: - version "1.27.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" +"mime-db@>= 1.29.0 < 2", mime-db@~1.29.0: + version "1.29.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878" mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: - version "2.1.15" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" + version "2.1.16" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz#2b858a52e5ecd516db897ac2be87487830698e23" dependencies: - mime-db "~1.27.0" + mime-db "~1.29.0" mime@1.3.4: version "1.3.4" @@ -3101,7 +3101,7 @@ minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@0.0.8, minimist@~0.0.1: +minimist@0.0.8: version "0.0.8" resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -3113,6 +3113,10 @@ minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + mixin-deep@^1.1.3: version "1.2.0" resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.2.0.tgz#d02b8c6f8b6d4b8f5982d3fd009c4919851c3fe2" @@ -3124,7 +3128,7 @@ mkdirp@0.3.x: version "0.3.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" -mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -3317,7 +3321,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.1: +normalize-path@^2.0.0, normalize-path@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" dependencies: @@ -3377,7 +3381,7 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" -nyc@^11.0.3: +nyc@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/nyc/-/nyc-11.1.0.tgz#d6b3c5e16892a25af63138ba484676aa8a22eda7" dependencies: @@ -3533,10 +3537,10 @@ os-locale@^1.4.0: lcid "^1.0.0" os-locale@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/os-locale/-/os-locale-2.0.0.tgz#15918ded510522b81ee7ae5a309d54f639fc39a4" + version "2.1.0" + resolved "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" dependencies: - execa "^0.5.0" + execa "^0.7.0" lcid "^1.0.0" mem "^1.1.0" @@ -3681,8 +3685,8 @@ pause-stream@0.0.11: through "~2.3" pbkdf2@^3.0.3: - version "3.0.12" - resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2" + version "3.0.13" + resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.13.tgz#c37d295531e786b1da3e3eadc840426accb0ae25" dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" @@ -3977,12 +3981,12 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 supports-color "^3.2.3" postcss@^6.0.1: - version "6.0.7" - resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.7.tgz#6a097477c46d13d0560a817d69abc0bae549d0a0" + version "6.0.9" + resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.9.tgz#54819766784a51c65b1ec4d54c2f93765438c35a" dependencies: - chalk "^2.0.1" + chalk "^2.1.0" source-map "^0.5.6" - supports-color "^4.2.0" + supports-color "^4.2.1" precinct@^3.6.0: version "3.6.0" @@ -4035,12 +4039,12 @@ process@^0.11.0: version "0.11.10" resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" -proxy-addr@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" +proxy-addr@~1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" dependencies: forwarded "~0.1.0" - ipaddr.js "1.3.0" + ipaddr.js "1.4.0" prr@~0.0.0: version "0.0.0" @@ -4078,14 +4082,18 @@ q@^1.1.2: version "1.5.0" resolved "https://registry.npmjs.org/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" -qs@6.4.0, qs@~6.4.0: - version "6.4.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" +qs@6.5.0: + version "6.5.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" qs@~6.3.0: version "6.3.2" resolved "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + query-string@^4.1.0: version "4.3.4" resolved "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" @@ -4230,9 +4238,9 @@ regenerate@^1.2.1: version "1.3.2" resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" -regenerator-runtime@^0.10.0: - version "0.10.5" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" +regenerator-runtime@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" regex-cache@^0.4.2: version "0.4.3" @@ -4274,8 +4282,8 @@ relateurl@0.2.x: resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" remove-trailing-separator@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511" + version "1.1.0" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" renderkid@^2.0.1: version "2.0.1" @@ -4390,8 +4398,8 @@ resolve-url@^0.2.1: resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" resolve@^1.1.6, resolve@^1.3.2: - version "1.3.3" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" + version "1.4.0" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" dependencies: path-parse "^1.0.5" @@ -4429,9 +4437,9 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -rxjs@^5.4.2: - version "5.4.2" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-5.4.2.tgz#2a3236fcbf03df57bae06fd6972fd99e5c08fcf7" +rxjs@^5.4.3: + version "5.4.3" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz#0758cddee6033d68e0fd53676f0f3596ce3d483f" dependencies: symbol-observable "^1.0.1" @@ -4465,27 +4473,27 @@ select-hose@^2.0.0: resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" selfsigned@^1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-1.9.1.tgz#cdda4492d70d486570f87c65546023558e1dfa5a" + version "1.10.1" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.1.tgz#bf8cb7b83256c4551e31347c6311778db99eec52" dependencies: node-forge "0.6.33" "semver@2 || 3 || 4 || 5", semver@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + version "5.4.1" + resolved "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" -send@0.15.3: - version "0.15.3" - resolved "https://registry.npmjs.org/send/-/send-0.15.3.tgz#5013f9f99023df50d1bd9892c19e3defd1d53309" +send@0.15.4: + version "0.15.4" + resolved "https://registry.npmjs.org/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9" dependencies: - debug "2.6.7" - depd "~1.1.0" + debug "2.6.8" + depd "~1.1.1" destroy "~1.0.4" encodeurl "~1.0.1" escape-html "~1.0.3" etag "~1.8.0" fresh "0.5.0" - http-errors "~1.6.1" + http-errors "~1.6.2" mime "1.3.4" ms "2.0.0" on-finished "~2.3.0" @@ -4508,14 +4516,14 @@ serve-index@^1.7.2: mime-types "~2.1.15" parseurl "~1.3.1" -serve-static@1.12.3: - version "1.12.3" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz#9f4ba19e2f3030c547f8af99107838ec38d5b1e2" +serve-static@1.12.4: + version "1.12.4" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961" dependencies: encodeurl "~1.0.1" escape-html "~1.0.3" parseurl "~1.3.1" - send "0.15.3" + send "0.15.4" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -4593,13 +4601,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -sinon-chai@^2.11.0: - version "2.12.0" - resolved "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.12.0.tgz#da71e9642ef7b893ba3cf2af806396a00aa45927" +sinon-chai@^2.13.0: + version "2.13.0" + resolved "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.13.0.tgz#b9a42e801c20234bfc2f43b29e6f4f61b60990c4" -sinon@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/sinon/-/sinon-3.0.0.tgz#f6919755c8c705e0b4ae977e8351bbcbaf6d91de" +sinon@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/sinon/-/sinon-3.2.0.tgz#8848a66ab6e8b80b5532e3824f59f83ea2628c77" dependencies: diff "^3.1.0" formatio "1.2.0" @@ -4648,16 +4656,16 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -sockjs-client@1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz#f0212a8550e4c9468c8cceaeefd2e3493c033ad5" +sockjs-client@1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12" dependencies: - debug "^2.2.0" + debug "^2.6.6" eventsource "0.1.6" faye-websocket "~0.11.0" inherits "^2.0.1" json3 "^3.3.2" - url-parse "^1.1.1" + url-parse "^1.1.8" sockjs@0.3.18: version "0.3.18" @@ -4707,7 +4715,7 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" -source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: +source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: version "0.5.6" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" @@ -4941,15 +4949,15 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2, supports-color@^3.2.3: +supports-color@^3.1.1, supports-color@^3.1.2, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: has-flag "^1.0.0" -supports-color@^4.0.0, supports-color@^4.2.0, supports-color@~4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz#ad986dc7eb2315d009b4d77c8169c2231a684037" +supports-color@^4.0.0, supports-color@^4.2.1, supports-color@~4.2.0: + version "4.2.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-4.2.1.tgz#65a4bb2631e90e02420dba5554c375a4754bb836" dependencies: has-flag "^2.0.0" @@ -4969,9 +4977,9 @@ symbol-observable@^1.0.1: version "1.0.4" resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" -tapable@^0.2.5, tapable@~0.2.5: - version "0.2.6" - resolved "https://registry.npmjs.org/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" +tapable@^0.2.5, tapable@^0.2.7: + version "0.2.8" + resolved "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" tar-pack@^3.4.0: version "3.4.0" @@ -5036,9 +5044,13 @@ thunky@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e" +time-stamp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357" + timers-browserify@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" + version "2.0.4" + resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz#96ca53f4b794a5e7c0e1bd7cc88a372298fa01e6" dependencies: setimmediate "^1.0.4" @@ -5056,7 +5068,7 @@ to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" -to-fast-properties@^1.0.1: +to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" @@ -5111,9 +5123,9 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -ts-node@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-3.2.0.tgz#9814f0c0141784900cf12fef1197ad4b7f4d23d1" +ts-node@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz#c13c6a3024e30be1180dd53038fc209289d4bf69" dependencies: arrify "^1.0.0" chalk "^2.0.0" @@ -5123,7 +5135,7 @@ ts-node@^3.2.0: mkdirp "^0.5.1" source-map-support "^0.4.0" tsconfig "^6.0.0" - v8flags "^2.0.11" + v8flags "^3.0.0" yn "^2.0.0" tsconfig@^6.0.0: @@ -5155,9 +5167,9 @@ tslint-loader@^3.5.3: rimraf "^2.4.4" semver "^5.3.0" -tslint@^5.5.0: - version "5.5.0" - resolved "https://registry.npmjs.org/tslint/-/tslint-5.5.0.tgz#10e8dab3e3061fa61e9442e8cee3982acf20a6aa" +tslint@^5.6.0: + version "5.6.0" + resolved "https://registry.npmjs.org/tslint/-/tslint-5.6.0.tgz#088aa6c6026623338650b2900828ab3edf59f6cf" dependencies: babel-code-frame "^6.22.0" colors "^1.1.2" @@ -5168,15 +5180,15 @@ tslint@^5.5.0: resolve "^1.3.2" semver "^5.3.0" tslib "^1.7.1" - tsutils "^2.5.1" + tsutils "^2.7.1" tsutils@^1.4.0: version "1.9.1" resolved "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0" -tsutils@^2.5.1: - version "2.7.1" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-2.7.1.tgz#411a0e9466525a2b2869260a55620d7292155e24" +tsutils@^2.7.1: + version "2.8.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-2.8.0.tgz#0160173729b3bf138628dd14a1537e00851d814a" dependencies: tslib "^1.7.1" @@ -5224,10 +5236,10 @@ typescript@^2.4.2: resolved "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844" uglify-js@3.0.x: - version "3.0.25" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.25.tgz#3dc190b0ee437497e449bc6f785665b06afbe052" + version "3.0.27" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.27.tgz#a97db8c8ba6b9dba4e2f88d86aa9548fa6320034" dependencies: - commander "~2.9.0" + commander "~2.11.0" source-map "~0.5.1" uglify-js@^2.6, uglify-js@^2.8.29: @@ -5312,7 +5324,7 @@ url-parse@1.0.x: querystringify "0.0.x" requires-port "1.0.x" -url-parse@^1.1.1: +url-parse@^1.1.8: version "1.1.9" resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz#c67f1d775d51f0a18911dd7b3ffad27bb9e5bd19" dependencies: @@ -5368,9 +5380,9 @@ uuid@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" -v8flags@^2.0.11: - version "2.1.1" - resolved "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" +v8flags@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/v8flags/-/v8flags-3.0.0.tgz#4be9604488e0c4123645def705b1848d16b8e01f" dependencies: user-home "^1.1.1" @@ -5389,11 +5401,13 @@ vendors@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" -verror@1.3.6: - version "1.3.6" - resolved "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" +verror@1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" dependencies: - extsprintf "1.0.2" + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" vm-browserify@0.0.4: version "0.0.4" @@ -5420,17 +5434,18 @@ wbuf@^1.1.0, wbuf@^1.7.2: minimalistic-assert "^1.0.0" webpack-dev-middleware@^1.11.0: - version "1.11.0" - resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz#09691d0973a30ad1f82ac73a12e2087f0a4754f9" + version "1.12.0" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz#d34efefb2edda7e1d3b5dbe07289513219651709" dependencies: memory-fs "~0.4.1" mime "^1.3.4" path-is-absolute "^1.0.0" range-parser "^1.0.3" + time-stamp "^2.0.0" -webpack-dev-server@^2.5.1: - version "2.5.1" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.5.1.tgz#a02e726a87bb603db5d71abb7d6d2649bf10c769" +webpack-dev-server@^2.7.1: + version "2.7.1" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.7.1.tgz#21580f5a08cd065c71144cf6f61c345bca59a8b8" dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -5442,12 +5457,14 @@ webpack-dev-server@^2.5.1: html-entities "^1.2.0" http-proxy-middleware "~0.17.4" internal-ip "^1.2.0" + ip "^1.1.5" + loglevel "^1.4.1" opn "4.0.2" portfinder "^1.0.9" selfsigned "^1.9.1" serve-index "^1.7.2" sockjs "0.3.18" - sockjs-client "1.1.2" + sockjs-client "1.1.4" spdy "^3.4.1" strip-ansi "^3.0.0" supports-color "^3.1.1" @@ -5461,16 +5478,16 @@ webpack-sources@^1.0.1: source-list-map "^2.0.0" source-map "~0.5.3" -webpack@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-3.3.0.tgz#ce2f9e076566aba91f74887133a883fd7da187bc" +webpack@^3.5.5: + version "3.5.5" + resolved "https://registry.npmjs.org/webpack/-/webpack-3.5.5.tgz#3226f09fc8b3e435ff781e7af34f82b68b26996c" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0" ajv "^5.1.5" ajv-keywords "^2.0.0" async "^2.1.2" - enhanced-resolve "^3.3.0" + enhanced-resolve "^3.4.0" escope "^3.6.0" interpret "^1.0.0" json-loader "^0.5.4" @@ -5481,12 +5498,12 @@ webpack@^3.3.0: mkdirp "~0.5.0" node-libs-browser "^2.0.0" source-map "^0.5.3" - supports-color "^3.1.0" - tapable "~0.2.5" + supports-color "^4.2.1" + tapable "^0.2.7" uglifyjs-webpack-plugin "^0.4.6" watchpack "^1.4.0" webpack-sources "^1.0.1" - yargs "^6.0.0" + yargs "^8.0.2" websocket-driver@>=0.5.1: version "0.6.5" @@ -5511,8 +5528,8 @@ which-module@^2.0.0: resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" which@^1.2.4, which@^1.2.9: - version "1.2.14" - resolved "https://registry.npmjs.org/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + version "1.3.0" + resolved "https://registry.npmjs.org/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: isexe "^2.0.0" @@ -5609,7 +5626,7 @@ yargs@^6.0.0: y18n "^3.2.1" yargs-parser "^4.2.0" -yargs@^8.0.1: +yargs@^8.0.1, yargs@^8.0.2: version "8.0.2" resolved "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" dependencies: