diff --git a/modules/common/big-number-util/condition.spec.ts b/modules/common/big-number-util/condition.spec.ts new file mode 100644 index 0000000..9fd2857 --- /dev/null +++ b/modules/common/big-number-util/condition.spec.ts @@ -0,0 +1,193 @@ +import { + equal, + equalStrictly, + gt, + gtStrictly, + gte, + gteStrictly, + isFinite, + isFiniteStrictly, + isNaN, + isNegative, + isNegativeStrictly, + isPositive, + isPositiveStrictly, + isZero, + isZeroStrictly, + lt, + ltStrictly, + lte, + lteStrictly, +} from './condition'; + +describe('BigNumber Util Condition Functions', () => { + describe('equal', () => { + it('should handle value as expected', () => { + expect(equal(1, 1)).toBe(1 === 1); + }); + it('should return false whenever non numeric value', () => { + expect(equal(NaN, NaN)).toBe(false); + expect(equal(null, 1)).toBe(false); + }); + }); + + describe('gt', () => { + it('should handle value as expected', () => { + expect(gt(2, 1)).toBe(2 > 1); + }); + it('should return false whenever non numeric value', () => { + expect(gt(null, 1)).toBe(false); + }); + }); + + describe('lt', () => { + it('should handle value as expected', () => { + expect(lt(1, 2)).toBe(1 < 2); + }); + it('should return false whenever non numeric value', () => { + expect(lt(null, 1)).toBe(false); + }); + }); + + describe('gte', () => { + it('should handle value as expected', () => { + expect(gte(2, 1)).toBe(2 > 1); + }); + it('should return false whenever non numeric value', () => { + expect(gte(null, 1)).toBe(false); + }); + }); + + describe('lte', () => { + it('should handle value as expected', () => { + expect(lte(1, 2)).toBe(1 < 2); + }); + it('should return false whenever non numeric value', () => { + expect(lte(null, 1)).toBe(false); + }); + }); + + describe('isPositive', () => { + it('should handle value as expected', () => { + expect(isPositive(1)).toBe(0 < 1); + }); + it('should return false whenever non numeric value', () => { + expect(isPositive(null)).toBe(false); + }); + }); + + describe('isNegative', () => { + it('should handle value as expected', () => { + expect(isNegative(-1)).toBe(-1 < 0); + }); + it('should return false whenever non numeric value', () => { + expect(isNegative(null)).toBe(false); + }); + }); + + describe('isZero', () => { + it('should handle value as expected', () => { + expect(isZero(0)).toBe(0 === 0); + }); + it('should return false whenever non numeric value', () => { + expect(isZero(null)).toBe(false); + }); + }); + + describe('isFinite', () => { + it('should handle value as expected', () => { + expect(isFinite(1)).toBe(Number.isFinite(1)); + }); + it('should return false whenever non numeric value', () => { + expect(isFinite(null)).toBe(false); + }); + }); + + describe('isNaN', () => { + it('should handle value as expected', () => { + expect(isNaN(NaN)).toBe(Number.isNaN(NaN)); + }); + }); + + describe('equalStrictly', () => { + it('should handle value as expected', () => { + expect(equalStrictly(1, 1)).toBe(1 === 1); + }); + it('should throw error whenever non numeric value', () => { + expect(() => equalStrictly(null, 1)).toThrow(); + expect(() => equalStrictly(NaN, 1)).toThrow(); + }); + }); + + describe('gtStrictly', () => { + it('should handle value as expected', () => { + expect(gtStrictly(2, 1)).toBe(2 > 1); + }); + it('should throw error whenever non numeric value', () => { + expect(() => gtStrictly(null, 1)).toThrow(); + }); + }); + + describe('ltStrictly', () => { + it('should handle value as expected', () => { + expect(ltStrictly(1, 2)).toBe(1 < 2); + }); + it('should throw error whenever non numeric value', () => { + expect(() => ltStrictly(null, 1)).toThrow(); + }); + }); + + describe('gteStrictly', () => { + it('should handle value as expected', () => { + expect(gteStrictly(2, 1)).toBe(2 > 1); + }); + it('should throw error whenever non numeric value', () => { + expect(() => gteStrictly(null, 1)).toThrow(); + }); + }); + + describe('lteStrictly', () => { + it('should handle value as expected', () => { + expect(lteStrictly(1, 2)).toBe(1 < 2); + }); + it('should throw error whenever non numeric value', () => { + expect(() => lteStrictly(null, 1)).toThrow(); + }); + }); + + describe('isPositiveStrictly', () => { + it('should handle value as expected', () => { + expect(isPositiveStrictly(1)).toBe(0 < 1); + }); + it('should throw error whenever non numeric value', () => { + expect(() => isPositiveStrictly(null)).toThrow(); + }); + }); + + describe('isNegativeStrictly', () => { + it('should handle value as expected', () => { + expect(isNegativeStrictly(-1)).toBe(-1 < 0); + }); + it('should throw error whenever non numeric value', () => { + expect(() => isNegativeStrictly(null)).toThrow(); + }); + }); + + describe('isZeroStrictly', () => { + it('should handle value as expected', () => { + expect(isZeroStrictly(0)).toBe(0 === 0); + }); + it('should throw error whenever non numeric value', () => { + expect(() => isZeroStrictly(null)).toThrow(); + }); + }); + + describe('isFiniteStrictly', () => { + it('should handle value as expected', () => { + expect(isFiniteStrictly(1)).toBe(Number.isFinite(1)); + }); + it('should throw error whenever non numeric value', () => { + expect(() => isFiniteStrictly(null)).toThrow(); + }); + }); +}); diff --git a/modules/common/big-number-util/condition.ts b/modules/common/big-number-util/condition.ts new file mode 100644 index 0000000..21e830e --- /dev/null +++ b/modules/common/big-number-util/condition.ts @@ -0,0 +1,117 @@ +import { BigNumber } from 'bignumber.js'; + +import { getBigNumber, getBigNumberStrictly } from './get-big-number'; + +export function equal(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumber(v1); + const b = getBigNumber(v2); + + return a.isEqualTo(b); +} + +export function gt(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumber(v1); + const b = getBigNumber(v2); + + return a.gt(b); +} + +export function lt(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumber(v1); + const b = getBigNumber(v2); + + return a.lt(b); +} + +export function gte(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumber(v1); + const b = getBigNumber(v2); + + return a.gte(b); +} + +export function lte(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumber(v1); + const b = getBigNumber(v2); + + return a.lte(b); +} + +export function isPositive(v: BigNumber.Value): boolean { + const bn = getBigNumber(v); + + return bn.isPositive() && bn.isGreaterThan(0); +} + +export function isNegative(v: BigNumber.Value): boolean { + const bn = getBigNumber(v); + + return bn.isNegative() && bn.isLessThan(0); +} + +export function isZero(v: BigNumber.Value): boolean { + return getBigNumber(v).isZero(); +} + +export function isFinite(v: BigNumber.Value): boolean { + return getBigNumber(v).isFinite(); +} + +export function isNaN(v: BigNumber.Value): boolean { + return getBigNumber(v).isNaN(); +} + +export function equalStrictly(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumberStrictly(v1); + const b = getBigNumberStrictly(v2); + + return a.isEqualTo(b); +} + +export function gtStrictly(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumberStrictly(v1); + const b = getBigNumberStrictly(v2); + + return a.gt(b); +} + +export function ltStrictly(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumberStrictly(v1); + const b = getBigNumberStrictly(v2); + + return a.lt(b); +} + +export function gteStrictly(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumberStrictly(v1); + const b = getBigNumberStrictly(v2); + + return a.gte(b); +} + +export function lteStrictly(v1: BigNumber.Value, v2: BigNumber.Value): boolean { + const a = getBigNumberStrictly(v1); + const b = getBigNumberStrictly(v2); + + return a.lte(b); +} + +export function isPositiveStrictly(v: BigNumber.Value): boolean { + const bn = getBigNumberStrictly(v); + + return bn.isPositive() && bn.isGreaterThan(0); +} + +export function isNegativeStrictly(v: BigNumber.Value): boolean { + const bn = getBigNumberStrictly(v); + + return bn.isNegative() && bn.isLessThan(0); +} + +export function isZeroStrictly(v: BigNumber.Value): boolean { + return getBigNumberStrictly(v).isZero(); +} + +export function isFiniteStrictly(v: BigNumber.Value): boolean { + return getBigNumberStrictly(v).isFinite(); +} diff --git a/modules/common/big-number-util/get-big-number.spec.ts b/modules/common/big-number-util/get-big-number.spec.ts new file mode 100644 index 0000000..b36ea94 --- /dev/null +++ b/modules/common/big-number-util/get-big-number.spec.ts @@ -0,0 +1,97 @@ +import { BigNumber } from 'bignumber.js'; + +import { getBigNumber, getBigNumberStrictly } from './get-big-number'; + +describe('BigNumber Util Functions', () => { + describe('getBigNumber', () => { + it('should return instance for acceptable values', () => { + expect(getBigNumber(0).toNumber()).toBe(0); + expect(getBigNumber(-0).toNumber()).toBe(-0); + expect(getBigNumber(1).toNumber()).toBe(1); + expect(getBigNumber(-1).toNumber()).toBe(-1); + expect(getBigNumber(10000000000000000000000).toNumber()).toBe(10000000000000000000000); + expect(getBigNumber(-10000000000000000000000).toNumber()).toBe(-10000000000000000000000); + expect(getBigNumber(0.000000000000000000001).toNumber()).toBe(0.000000000000000000001); + expect(getBigNumber(-0.000000000000000000001).toNumber()).toBe(-0.000000000000000000001); + expect(getBigNumber(Infinity).toNumber()).toBe(Infinity); + expect(getBigNumber(-Infinity).toNumber()).toBe(-Infinity); + + expect(getBigNumber('0').toString()).toBe('0'); + expect(getBigNumber('-0').toString()).toBe('0'); + expect(getBigNumber('1').toString()).toBe('1'); + expect(getBigNumber('-1').toString()).toBe('-1'); + expect(getBigNumber('10000000000000000000000').toString()).toBe('1e+22'); + expect(getBigNumber('-10000000000000000000000').toString()).toBe('-1e+22'); + expect(getBigNumber('0.000000000000000000001').toString()).toBe('1e-21'); + expect(getBigNumber('-0.000000000000000000001').toString()).toBe('-1e-21'); + expect(getBigNumber('Infinity').toString()).toBe('Infinity'); + expect(getBigNumber('-Infinity').toString()).toBe('-Infinity'); + + expect(getBigNumber(new BigNumber('0')).toString()).toBe('0'); + expect(getBigNumber(new BigNumber(getBigNumber('-0'))).toString()).toBe('0'); + expect(getBigNumber(new BigNumber(getBigNumber('1'))).toString()).toBe('1'); + expect(getBigNumber(new BigNumber(getBigNumber('-1'))).toString()).toBe('-1'); + expect(getBigNumber(new BigNumber(getBigNumber('10000000000000000000000'))).toString()).toBe('1e+22'); + expect(getBigNumber(new BigNumber(getBigNumber('-10000000000000000000000'))).toString()).toBe('-1e+22'); + expect(getBigNumber(new BigNumber(getBigNumber('0.000000000000000000001'))).toString()).toBe('1e-21'); + expect(getBigNumber(new BigNumber(getBigNumber('-0.000000000000000000001'))).toString()).toBe('-1e-21'); + expect(getBigNumber(new BigNumber(getBigNumber('Infinity'))).toString()).toBe('Infinity'); + expect(getBigNumber(new BigNumber(getBigNumber('-Infinity'))).toString()).toBe('-Infinity'); + }); + + it('should return instance for unacceptable values.', () => { + expect(getBigNumber('').toNumber()).toEqual(NaN); + expect(getBigNumber('string').toNumber()).toEqual(NaN); + expect(getBigNumber(null).toNumber()).toEqual(NaN); + expect(getBigNumber(undefined).toNumber()).toEqual(NaN); + expect(getBigNumber([]).toNumber()).toEqual(NaN); + expect(getBigNumber({}).toNumber()).toEqual(NaN); + }); + }); + + describe('getBigNumberSafely', () => { + it('should not throw error when acceptable value', () => { + expect(() => getBigNumberStrictly(0)).not.toThrow(); + expect(() => getBigNumberStrictly(-0)).not.toThrow(); + expect(() => getBigNumberStrictly(1)).not.toThrow(); + expect(() => getBigNumberStrictly(-1)).not.toThrow(); + expect(() => getBigNumberStrictly(10000000000000000000000)).not.toThrow(); + expect(() => getBigNumberStrictly(-10000000000000000000000)).not.toThrow(); + expect(() => getBigNumberStrictly(0.000000000000000000001)).not.toThrow(); + expect(() => getBigNumberStrictly(-0.000000000000000000001)).not.toThrow(); + expect(() => getBigNumberStrictly(Infinity)).not.toThrow(); + expect(() => getBigNumberStrictly(-Infinity)).not.toThrow(); + + expect(() => getBigNumberStrictly('0')).not.toThrow(); + expect(() => getBigNumberStrictly('-0')).not.toThrow(); + expect(() => getBigNumberStrictly('1')).not.toThrow(); + expect(() => getBigNumberStrictly('-1')).not.toThrow(); + expect(() => getBigNumberStrictly('10000000000000000000000')).not.toThrow(); + expect(() => getBigNumberStrictly('-10000000000000000000000')).not.toThrow(); + expect(() => getBigNumberStrictly('0.000000000000000000001')).not.toThrow(); + expect(() => getBigNumberStrictly('-0.000000000000000000001')).not.toThrow(); + expect(() => getBigNumberStrictly('Infinity')).not.toThrow(); + expect(() => getBigNumberStrictly('-Infinity')).not.toThrow(); + + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('0')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('-0')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('1')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('-1')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('10000000000000000000000')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('-10000000000000000000000')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('0.000000000000000000001')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('-0.000000000000000000001')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('Infinity')))).not.toThrow(); + expect(() => getBigNumberStrictly(new BigNumber(getBigNumberStrictly('-Infinity')))).not.toThrow(); + }); + + it('should throw error when unacceptable value', () => { + expect(() => getBigNumberStrictly('')).toThrow(); + expect(() => getBigNumberStrictly('string')).toThrow(); + expect(() => getBigNumberStrictly(null)).toThrow(); + expect(() => getBigNumberStrictly(undefined)).toThrow(); + expect(() => getBigNumberStrictly([])).toThrow(); + expect(() => getBigNumberStrictly({})).toThrow(); + }); + }); +}); diff --git a/modules/common/big-number-util/get-big-number.ts b/modules/common/big-number-util/get-big-number.ts new file mode 100644 index 0000000..ceb5539 --- /dev/null +++ b/modules/common/big-number-util/get-big-number.ts @@ -0,0 +1,23 @@ +import { BigNumber } from 'bignumber.js'; + +export function getBigNumber(value: BigNumber.Value): BigNumber { + let bn: BigNumber; + + try { + bn = new BigNumber(value); + } catch (e) { + bn = new BigNumber(NaN); + } + + return bn; +} + +export function getBigNumberStrictly(value: BigNumber.Value): BigNumber { + const bn = getBigNumber(value); + + if (bn.isNaN()) { + throw new Error('value is not numeric.'); + } + + return bn; +} diff --git a/modules/common/big-number-util/index.ts b/modules/common/big-number-util/index.ts new file mode 100644 index 0000000..198acb7 --- /dev/null +++ b/modules/common/big-number-util/index.ts @@ -0,0 +1,2 @@ +export * from './condition'; +export * from './get-big-number'; diff --git a/modules/common/index.ts b/modules/common/index.ts index 98111c5..79e1824 100644 --- a/modules/common/index.ts +++ b/modules/common/index.ts @@ -1 +1,2 @@ export * from './websocket'; +export * from './big-number-util'; diff --git a/modules/models/common/constants.ts b/modules/models/common/constants.ts new file mode 100644 index 0000000..00bc9ee --- /dev/null +++ b/modules/models/common/constants.ts @@ -0,0 +1 @@ +export const dripjsDbName = 'drip'; diff --git a/modules/models/common/index.ts b/modules/models/common/index.ts index 845cc62..53b1e83 100644 --- a/modules/models/common/index.ts +++ b/modules/models/common/index.ts @@ -1 +1,3 @@ export * from './transformers'; +export * from './constants'; +export * from './testing'; diff --git a/modules/models/common/testing/entity-test-bed/entity-test-bed/constants.ts b/modules/models/common/testing/entity-test-bed/entity-test-bed/constants.ts new file mode 100644 index 0000000..3dec25e --- /dev/null +++ b/modules/models/common/testing/entity-test-bed/entity-test-bed/constants.ts @@ -0,0 +1,6 @@ +import { MasterPairEntity, MasterSpotPairRepository } from '@dripjs/models'; +import { ObjectType } from 'typeorm'; + +export const allEntityTypes: ObjectType[] = [MasterPairEntity]; + +export const allRepositoryTypes: ObjectType[] = [MasterSpotPairRepository]; diff --git a/modules/models/common/testing/entity-test-bed/entity-test-bed/entity-test-bed.spec.ts b/modules/models/common/testing/entity-test-bed/entity-test-bed/entity-test-bed.spec.ts new file mode 100644 index 0000000..7e68028 --- /dev/null +++ b/modules/models/common/testing/entity-test-bed/entity-test-bed/entity-test-bed.spec.ts @@ -0,0 +1,485 @@ +import { overrideTimestampColumns } from '../../test-helpers'; +import { MasterPairEntity, MasterSpotPairRepository } from '@dripjs/models'; + +import { DatabaseSnapshot, EntityTestBed, getLatestUpdatedTime } from './entity-test-bed'; + +const createBase = { + ask: '10', + bid: '20', + last: '30', + spotEventId: '1', +}; + +const expectedBase = { + ask: '10', + bid: '20', + createdAt: 'overridden', + last: '30', + spotEventId: '1', + updatedAt: 'overridden', +}; + +describe('EntityTestBed', () => { + beforeAll(async () => { + await EntityTestBed.setup(); + }); + + afterAll(async () => { + await EntityTestBed.cleanup(); + }); + + beforeEach(async () => { + await EntityTestBed.reset(); + }); + + describe('getManager', () => { + it('should get manager of core and public database', async () => { + const manager = EntityTestBed.getManager(); + expect(manager).toBeTruthy(); + }); + }); + + describe('getRepository', () => { + it('should get custom repository of database', async () => { + const repository = EntityTestBed.getRepository(MasterPairEntity); + expect(repository instanceof MasterSpotPairRepository).toBe(true); + }); + }); +/* + describe('createEntity', () => { + describe('When one argument (use class)', () => { + it('should create single instance', async () => { + const created = await EntityTestBed.createEntity(ExchangeInformationEntity, { + ...createBase, + pair: 'btc_jpy', + }); + + expect(overrideTimestampColumns(created)).toEqual({ + ...expectedBase, + id: '1', + pair: 'btc_jpy', + }); + }); + }); + + describe('When one argument (use entity name string)', () => { + it('should create single instance', async () => { + const created = await EntityTestBed.createEntity(ExchangeInformationEntity.name, { + ...createBase, + pair: 'btc_jpy', + }); + + expect(overrideTimestampColumns(created)).toEqual({ + ...expectedBase, + id: '1', + pair: 'btc_jpy', + }); + }); + }); + + describe('When multiple arguments', () => { + it('should create multiple instances', async () => { + const created = await EntityTestBed.createEntity(ExchangeInformationEntity, [ + { + ...createBase, + pair: 'btc_jpy', + }, + { + ...createBase, + pair: 'xrp_jpy', + }, + ]); + + expect(overrideTimestampColumns(created)).toEqual([ + { + ...expectedBase, + id: '1', + pair: 'btc_jpy', + }, + { + ...expectedBase, + id: '2', + pair: 'xrp_jpy', + }, + ]); + }); + }); + + describe('When lacking params', () => { + it('should throw error', async () => { + await expect( + EntityTestBed.createEntity(ExchangeInformationEntity, { + // pair is lacking + ...createBase, + }), + ).rejects.toBeTruthy(); + }); + }); + + describe('When argument has function', () => { + it('should create single instance with function executed result', async () => { + const created = await EntityTestBed.createEntity(ExchangeInformationEntity, { + ...createBase, + pair: () => 'this is result of function', + }); + + expect(overrideTimestampColumns(created)).toEqual({ + ...expectedBase, + id: '1', + pair: 'this is result of function', + }); + }); + }); + }); + + describe('assertEntity', () => { + it('should not throw error', async () => { + const created = await EntityTestBed.createEntity(ExchangeInformationEntity, { + ...createBase, + pair: 'btc_jpy', + }); + const expectation = { + ...expectedBase, + id: '1', + pair: 'btc_jpy', + createdAt: (value: any) => Number.isSafeInteger(value), + updatedAt: (value: any) => Number.isSafeInteger(value), + }; + + expect(() => EntityTestBed.assertEntity(created, expectation)).not.toThrow(); + }); + + it('should throw error', async () => { + const created = await EntityTestBed.createEntity(ExchangeInformationEntity, { + ...createBase, + pair: 'bad name', + }); + const expectation = { + id: '2', + }; + + expect(() => EntityTestBed.assertEntity(created, expectation)).toThrow(); + }); + }); + + describe('assertDatabase', () => { + // EntityTestBed.assertDatabase() returns > + const valueWhenAssertIsOK = undefined; + let e1: ExchangeInformationEntity; + let e2: ExchangeInformationEntity; + let snapshot: DatabaseSnapshot; + + beforeEach(async () => { + await EntityTestBed.clear(); + [e1, e2] = await EntityTestBed.createEntity(ExchangeInformationEntity, [ + { + ...createBase, + pair: 'btc_jpy', + }, + { + ...createBase, + pair: 'xrp_jpy', + }, + ]); + snapshot = await EntityTestBed.getCoreDatabaseSnapshot(); + }); + + describe('When create assertion', () => { + beforeEach(async () => { + await EntityTestBed.createEntity(ExchangeInformationEntity, [ + { + ...createBase, + pair: 'ltc_jpy', + }, + { + ...createBase, + pair: 'eth_jpy', + }, + ]); + }); + + describe('When count is used', () => { + describe('And when count matches', () => { + it('should not throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + created: { + count: 2, + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + created: { + count: (n) => 2 <= n, + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + }); + }); + + describe('And when count mismatches', () => { + it('should throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + created: { + count: 1, + }, + }, + }), + ).rejects.toBeTruthy(); + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + created: { + count: (n) => 3 <= n, + }, + }, + }), + ).rejects.toBeTruthy(); + }); + }); + }); + + describe('When all expectations satisfy condition', () => { + it('should not throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + created: { + assertion: [ + { id: (v: any) => Number.isInteger(+v), pair: 'ltc_jpy' }, + { id: (v: any) => Number.isInteger(+v), pair: 'eth_jpy' }, + ], + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + }); + }); + + describe('When any expectations does not satisfy condition', () => { + it('should throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + created: { + assertion: [{ pair: 'ltc_jpy' }, { pair: 'bad name' }], + }, + }, + }), + ).rejects.toBeTruthy(); + }); + }); + + describe('When update assertion', () => { + beforeEach(async () => { + (e1).pair = 'updated name 1'; + (e2).pair = 'updated name 2'; + await EntityTestBed.getManager(ExchangeInformationEntity).save(ExchangeInformationEntity, [e1, e2]); + }); + + describe('When count is used', () => { + describe('And when count matches', () => { + it('should not throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + updated: { + count: 2, + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + updated: { + count: (n) => 2 <= n, + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + }); + }); + + describe('And when count mismatches', () => { + it('should throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + updated: { + count: 1, + }, + }, + }), + ).rejects.toBeTruthy(); + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + updated: { + count: (n) => 3 <= n, + }, + }, + }), + ).rejects.toBeTruthy(); + }); + }); + }); + + describe('When all expectations satisfy condition', () => { + it('should not throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + updated: { + assertion: [ + [{ id: '1', pair: 'btc_jpy' }, { id: '1', pair: 'updated name 1' }], + [{ id: '2', pair: 'xrp_jpy' }, { id: '2', pair: 'updated name 2' }], + ], + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + }); + }); + + describe('When any expectation does not satisfy condition', () => { + it('should throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + updated: { + assertion: [ + [{ id: '1', pair: 'btc_jpy' }, { id: '1', name: 'updated name 1' }], + [{ id: '2', pair: 'xrp_jpy' }, { id: '2', name: 'bad name' }], + ], + }, + }, + }), + ).rejects.toBeTruthy(); + }); + }); + }); + + describe('When delete assertion', () => { + beforeEach(async () => { + await EntityTestBed.getManager(ExchangeInformationEntity).remove(ExchangeInformationEntity, [{ id: '1' }, { id: '2' }]); + }); + + describe('When count is used', () => { + describe('And when count matches', () => { + it('should not throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + deleted: { + count: 2, + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + deleted: { + count: (n) => 2 <= n, + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + }); + }); + + describe('And when count mismatches', () => { + it('should throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + deleted: { + count: 3, + }, + }, + }), + ).rejects.toBeTruthy(); + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + deleted: { + count: (n) => 3 <= n, + }, + }, + }), + ).rejects.toBeTruthy(); + }); + }); + }); + + describe('When all expectations satisfy condition', () => { + it('should not throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + deleted: { + assertion: [{ id: '1', pair: 'btc_jpy' }, { id: '2', pair: 'xrp_jpy' }], + }, + }, + }), + ).resolves.toBe(valueWhenAssertIsOK); + }); + }); + + describe('When any expectations does not satisfy condition', () => { + it('should throw error', async () => { + await expect( + EntityTestBed.assertDatabase(snapshot, { + [ExchangeInformationEntity.name]: { + deleted: { + assertion: [{ id: '1', name: 'name 1', uuid: 'uuid 1' }, { id: 'bad id' }], + }, + }, + }), + ).rejects.toBeTruthy(); + }); + }); + }); + }); + }); +}); + +describe('getLatestUpdatedTime', () => { + describe('When no records', () => { + it('should return 0', () => { + expect(getLatestUpdatedTime([])).toBe(0); + }); + }); + + describe('When max is value of createdAt', () => { + it('should get max value', () => { + expect( + getLatestUpdatedTime([ + { id: '1', createdAt: 1, updatedAt: 1 }, + { id: '2', createdAt: 2, updatedAt: 1 }, + { id: '3', createdAt: 3, updatedAt: 1 }, + ]), + ).toBe(3); + }); + }); + + describe('When max is value of updatedAt', () => { + it('should get max value', () => { + expect( + getLatestUpdatedTime([ + { id: '1', createdAt: 1, updatedAt: 3 }, + { id: '2', createdAt: 1, updatedAt: 2 }, + { id: '3', createdAt: 1, updatedAt: 1 }, + ]), + ).toBe(3); + }); + });*/ +}); diff --git a/modules/models/common/testing/entity-test-bed/entity-test-bed/entity-test-bed.ts b/modules/models/common/testing/entity-test-bed/entity-test-bed/entity-test-bed.ts new file mode 100644 index 0000000..45ee3df --- /dev/null +++ b/modules/models/common/testing/entity-test-bed/entity-test-bed/entity-test-bed.ts @@ -0,0 +1,749 @@ +import * as assert from 'assert'; + +import { gt, gtStrictly, lte } from '@dripjs/common'; +import { dripjsDbName } from '@dripjs/models'; +import { isFunction, isObject } from 'lodash'; +import { Connection, EntityManager, ObjectType, createConnection, getConnectionManager } from 'typeorm'; + +import { allEntityTypes } from './constants'; + +/** + * `class` of entity. + */ +export type EntityClass = ObjectType; + +/** + * `class` of repository. + */ +export type RepositoryClass = ObjectType; + +/** + * The value to create entity. + * + * This is object that have partial members of entity and its value can use function to generate value. + * + * ``` + * EntityTestBed.createEntity(Entity, { + * // can use static value. + * field1: 'value of field 1', + * // can use dynamic value. + * field2: () => Math.random(), + * }); + * ``` + */ +export type CreateEntityValue = Partial>; + +/** + * Fields all entities in database have. + */ +interface BaseEntity { + id: string; + createdAt: number; + updatedAt: number; +} + +/** + * The value to create entity. + * + * This is object that have partial members of entity and its value can use function to generate value. + * + * ``` + * EntityTestBed.createEntity(Entity, { + * // can use static value. + * field1: 'value of field 1', + * // can use comparer function. + * field2: (value) => 1 < value && value < 10, + * }); + * ``` + */ +export type EntityAssertion = Partial>; + +/** + * Snapshot of database. + * + * ``` + * const snapshot = await EntityTestBed.getCoreDatabaseSnapshot(); + * ``` + */ +export interface DatabaseSnapshot { + [entityClassName: string]: BaseEntity[]; +} + +interface TableAssertion { + created?: { + count?: number | ((v: number) => boolean); + assertion?: EntityAssertion[]; + }; + updated?: { + count?: number | ((v: number) => boolean); + assertion?: [EntityAssertion, EntityAssertion][]; + }; + deleted?: { + count?: number | ((v: number) => boolean); + assertion?: EntityAssertion[]; + }; +} + +/** + * Used in `assertDatabase` + */ +interface DatabaseAssertion { + [tableName: string]: TableAssertion; +} + +/** + * How to set each test + * + * 1. beforeAllでsetup + * 2. afterAllでcleanup + * 3. beforeEachでreset + * + * ```typescript + * import { EntityTestBed } from '@dripjs/models/testing'; + * + * describe('some test', () => { + * beforeAll(async () => { + * // Configure Database connection. + * await EntityTestBed.setup(); + * }); + * + * afterAll(async () => { + * // Close Database connection. Internally EntityTestBed.clear is also executed. + * await EntityTestBed.cleanup(); + * }); + * + * beforeEach(async () => { + * // Delete all records of the table operated by this TestBed + * await EntityTestBed.reset(); + * }); + * }); + * ``` + */ +export class EntityTestBed { + /** + * setup database connections. + */ + static async setup(): Promise { + return getEntityTestBed()._setup(); + } + + /** + * Close database connections and clean up tables of entity created by this test bed. + */ + static async cleanup(): Promise { + return getEntityTestBed()._cleanup(); + } + + /** + * Clear all entity in database and restore records that created in migrations. + */ + static async reset(): Promise { + return getEntityTestBed()._reset(); + } + + /** + * Delete all records in database. + */ + static async clear(): Promise { + return getEntityTestBed()._clear(); + } + + /** + * Restore all records in database from snapshot. + */ + static async restoreFromSnapshot(snapshot: DatabaseSnapshot): Promise { + return getEntityTestBed()._restoreFromSnapshot(snapshot); + } + + /** + * Get manager of either in database. + * + */ + static getManager(): EntityManager { + return getEntityTestBed()._getManager(); + } + + /** + * Get custom repository of either core or public database. + * + * @param repository + */ + static getRepository(repository: ObjectType): T { + return getEntityTestBed()._getRepository(repository); + } + + /** + * Create an Entity and save it in Database. + * + * @param entity + * @param value + * + * How to use + * + * ```typescript + * import { EntityTestBed } from '@dripjs/testing'; + + * it('some test', async () => { + * // Create with specified value + * const entity = await EntityTestBed.createEntity(Entity, { + * property1: 'value 1', + * property2: 'value 2', + * }); + * + * // Dynamic value setting with function + * const entity = await EntityTestBed.createEntity(Entity, { + * property1: () => Math.random(), + * property2: 'value 2', + * }); + * + * // It is also possible to create multiple records + * const entities = await EntityTestBed.createEntity(Entity, [ + * { + * property1: 'value 1', + * property2: 'value 2', + * }, + * { + * property1: 'value 3', + * property2: 'value 4', + * }, + * ]); + * }); + * ``` + */ + static async createEntity(entity: ObjectType | string, value: CreateEntityValue): Promise; + static async createEntity(entity: ObjectType | string, value: CreateEntityValue[]): Promise; + static async createEntity(entity: ObjectType | string, value: any): Promise { + return getEntityTestBed()._createEntity(entity, value); + } + + /** + * Assert Entity with specified expectation notation. + * + * @param entity + * @param expectation + * + * ```typescript + * import { EntityTestBed } from '@dripjs/testing'; + * + * it('some test', async () => { + * const entity = await EntityTestBed.createEntity(Entity, { + * property1: 'value 1', + * property2: 'value 2', + * }); + * + * // Assert with the specified value + * EntityTestBed.assertEntity(entity, { + * property1: 'value 1', + * property2: 'value 2', + * }); + * + * // Use the comparer function to assert + * EntityTestBed.assertEntity(entity, { + * property1: (value) => typeof value === 'string', + * property2: 'value 2', + * }); + * + * // It is also possible to assert multiple records + * EntityTestBed.assertEntity( + * [entityA, entityB], + * [ + * { + * property1: 'value 1', + * property2: 'value 2', + * }, + * { + * property1: 'value 3', + * property2: 'value 4', + * }, + * ], + * ); + * }); + * ``` + */ + static assertEntity(entity: T, expectation: EntityAssertion): void { + return assertEntity(entity, expectation); + } + + /** + * Assert the change amount of the database under specified conditions + * + * @param previous + * @param difference + * + * eg: Assertion of newly added record in User table (when record with id 1 is added) + * + * ```typescript + * EntityTestBed.assertDatabase(previous, { + * UserTable: { + * created: [ + * { id: 1 } + * ] + * } + * }) + * ``` + * + * eg: At assertion of update record in user table (when name of record with id = 1 is updated) + * + * ```typescript + * EntityTestBed.assertDatabase(previous, { + * UserTable: { + * updated: [ + * [{ id: 1 }, { name: 'new name' }] + * ] + * } + * }) + * ``` + * + * eg: When deletion records are asserted (when records with id 1 are deleted) in the User table + * + * ```typescript + * EntityTestBed.assertDatabase(previous, { + * UserTable: { + * deleted: [ + * { id: 1 } + * ] + * } + * }) + * ``` + */ + static async assertDatabase(previous: DatabaseSnapshot, difference: DatabaseAssertion): Promise { + return assertDatabase(previous, difference); + } + + /** + * Get all entities in database. + */ + static async getDatabaseSnapshot(): Promise { + return getEntityTestBed()._getDatabaseSnapshot(); + } + + private databaseConnection: Connection | null = null; + + /** + * Setup database connections. + */ + async _setup(): Promise { + const connectionManager = getConnectionManager(); + this.databaseConnection = connectionManager.has(dripjsDbName) + ? connectionManager.get(dripjsDbName) + : await createConnection(dripjsDbName); + + await this._reset(); + } + + /** + * Close database connections and clean up entities created by this test bed. + */ + async _cleanup(): Promise { + if (this.databaseConnection === null) { + throw new Error('connection is not created yet, use setup method before'); + } + + // Reset records in databases. + await this._reset(); + + try { + await this.databaseConnection.close(); + } catch (e) { + console.error('[EntityTestBed] Error occurred when closing core database connection.', e); + throw e; + } + } + + /** + * Delete all records in database. + */ + async _clear(): Promise { + if (this.databaseConnection === null) { + throw new Error('connection is not created yet, use setup method before'); + } + + try { + await Promise.all( + allEntityTypes.map(async (entity) => + this._getManager() + .getRepository(entity) + .clear(), + ), + ); + } catch (e) { + console.error('[EntityTestBed] Error occurred when repository clear.', e); + throw e; + } + } + + /** + * Create entities with provided values and store it into database. + * + * @param entity + * @param value + */ + async _createEntity(entity: ObjectType | string, value: CreateEntityValue): Promise; + async _createEntity(entity: ObjectType | string, value: CreateEntityValue[]): Promise; + async _createEntity(entity: ObjectType | string, value: any): Promise { + const useArray = Array.isArray(value); + const values: Partial[] = Array.isArray(value) ? [...value] : [value]; + const activatedValues: Partial[] = []; + + for (const v of values) { + const activated: Partial = {}; + for (const member of Object.entries(v)) { + // if value is function, use its executed result. + (activated)[member[0]] = typeof member[1] === 'function' ? member[1]() : member[1]; + } + activatedValues.push(activated); + } + + const manager = this._getManager(); + const repository = manager.getRepository(entity); + + const created = await repository.save(activatedValues as any); + + return useArray ? created : created[0]; + } + + /** + * Get entity manager of database. + */ + _getManager(): EntityManager { + const connection = this.getDatabaseConnection(); + + return connection.manager; + } + + /** + * Get all entities in core database. + * + * @private + * + * ``` + * result = { + * SourceEventEntity: [ + * { id: '1', userUuid: '...' }, + * { id: '2', userUuid: '...' }, + * ], + * UserAssetEntity: [ + * { id: '1', uuid: '...' }, + * { id: '2', uuid: '...' }, + * ], + * }; + * ``` + */ + async _getDatabaseSnapshot(): Promise { + const resultAsArray: BaseEntity[][] = await Promise.all(allEntityTypes.map(async (e) => this._getManager().find(e))); + + const resultAsObject: DatabaseSnapshot = {}; + resultAsArray.forEach((entities, i) => (resultAsObject[allEntityTypes[i].name] = entities)); + + return resultAsObject; + } + + /** + * Restore all records in database from snapshot. + */ + async _restoreFromSnapshot(snapshot: DatabaseSnapshot): Promise { + await this._clear(); + + const entries: [EntityClass, BaseEntity[]][] = Object.entries(snapshot) + .filter(([_, records]) => 0 < records.length) + .map(([name, records]) => [getEntityClassByName(name), records] as [EntityClass, BaseEntity[]]); + const createEntities = entries + .filter(([className]) => allEntityTypes.includes(className)) + .map(async ([className, records]) => this._createEntity(className, records)); + + await Promise.all(createEntities); + } + + private _getRepository(repository: ObjectType): T { + return this._getManager().getCustomRepository(repository); + } + + /** + * Clear all entity in database and restore records that created in migrations. + * + * @private + */ + private async _reset(): Promise { + if (this.databaseConnection === null) { + throw new Error('connection is not created yet, use setup method before'); + } + + /** + * TypeORM can be frozen by using same connection for a long time so we manually handle reconnect. + */ + await this.databaseConnection.close(); + await this.databaseConnection.connect(); + + await this._clear(); + await this.restoreMigrationRecords(); + } + + /** + * Restore initial records that should be created with migrations. + */ + private async restoreMigrationRecords(): Promise { + if (this.databaseConnection === null) { + throw new Error('connection is not created yet, use setup method before'); + } + + const manager = this.databaseConnection.createQueryRunner(); + for (const migration of this.databaseConnection.migrations) { + if ((migration)['shouldRunOnResetInTest']) { + await migration.up(manager); + } + } + } + + /** + * Get connection to core database. + */ + private getDatabaseConnection(): Connection { + if (this.databaseConnection === null) { + throw new Error('connection is not created yet, use setup method before'); + } + + return this.databaseConnection; + } +} + +function getEntityClassByName(name: string): EntityClass { + for (const e of allEntityTypes) { + if (name === e.name) { + return e; + } + } + + throw new Error('Unregistered name passed.'); +} + +class AssertionError extends Error {} + +/** + * Internal function for EntityTestBed. + * + * @internal + * @param entity + * @param assertion + * @param parentKey + * @private + */ +function assertEntity(entity: T, assertion: EntityAssertion, parentKey: string = ''): void { + for (const [key, assertionValue] of Object.entries(assertion)) { + const actualValue = (entity)[key]; + + try { + // error can be thrown here. + if (isFunction(assertionValue)) { + assert.strictEqual(assertionValue(actualValue), true); + } else if (isObject(assertionValue)) { + assertEntity(actualValue, assertionValue, `${parentKey + key}.`); + } else { + assert.strictEqual(assertionValue, actualValue); + } + } catch (e) { + // throw the most bottom error in recursion. + if (e instanceof AssertionError) { + throw e; + } + + // add more readability. + let toLog = ''; + if (typeof assertionValue === 'function') { + toLog = assertionValue.toString(); + } else if (isObject(assertionValue)) { + toLog = JSON.stringify(assertionValue); + } else { + toLog = `${assertionValue}`; + } + + throw new AssertionError(`"${parentKey + key}" is incompatible. expected: ${toLog}, actual: ${actualValue}`); + } + } +} + +// tslint:disable cyclomatic-complexity +/** + * @internal + * @param previous + * @param databaseAssertion + */ +async function assertDatabase(previous: DatabaseSnapshot, databaseAssertion: DatabaseAssertion): Promise { + for (const data of Object.entries(databaseAssertion)) { + const tableName = data[0]; + const tableAssertion = data[1]; + + const previousEntities = previous[tableName]; + if (!Array.isArray(previousEntities)) { + throw new Error(`provided snapshot does not have records for expectation, ${tableName}`); + } + const currentEntities = await EntityTestBed.getManager().find(tableName); + const latestUpdatedTime = getLatestUpdatedTime(previousEntities); + + /** + * Assert created expressions + */ + if (tableAssertion.created) { + const newlyCreatedEntities = currentEntities.filter((record) => gtStrictly(record.createdAt, latestUpdatedTime)); + const createAssertions = tableAssertion.created.assertion || []; + + if (typeof tableAssertion.created.count === 'number' || typeof tableAssertion.created.count === 'function') { + const valid = + typeof tableAssertion.created.count === 'function' + ? tableAssertion.created.count(newlyCreatedEntities.length) + : tableAssertion.created.count === newlyCreatedEntities.length; + + if (!valid) { + throw new Error( + `count does not match.\n\n expected: ${tableAssertion.created.count},\n actual: ${newlyCreatedEntities.length}`, + ); + } + } + + for (const assertion of createAssertions) { + const errorMessages = getFailedAssertionMessages(newlyCreatedEntities, assertion); + const allAssertionFailed = errorMessages.length === newlyCreatedEntities.length; + if (allAssertionFailed) { + throw new Error( + `Any record does not match to assertion.\n assertion: ${JSON.stringify(assertion)},\n created: ${JSON.stringify( + newlyCreatedEntities, + )}, \n\n -- ${errorMessages.join(',\n -- ')}`, + ); + } + } + } + + /** + * Assert updated expressions + */ + if (tableAssertion.updated) { + const newlyUpdatedEntities = currentEntities.filter((e) => gt(e.updatedAt, latestUpdatedTime) && lte(e.createdAt, latestUpdatedTime)); + const updateAssertions = tableAssertion.updated.assertion || []; + + if (typeof tableAssertion.updated.count === 'number' || typeof tableAssertion.updated.count === 'function') { + const valid = + typeof tableAssertion.updated.count === 'function' + ? tableAssertion.updated.count(newlyUpdatedEntities.length) + : tableAssertion.updated.count === newlyUpdatedEntities.length; + + if (!valid) { + throw new Error( + `updated record count does not match.\n\n expected: ${tableAssertion.updated.count},\n actual: ${ + newlyUpdatedEntities.length + }`, + ); + } + } + + for (const [findOption, assertion] of updateAssertions) { + const target = previousEntities.find((e) => canSatisfy(e, findOption)); + if (target === undefined) { + throw new Error(`no record found for previous. \n findOption: ${JSON.stringify(findOption)}`); + } + const targetId = target.id; + const updated = newlyUpdatedEntities.find((e) => e.id === targetId); + if (updated === undefined) { + throw new Error(`no record found for updated. \n id: ${targetId}`); + } + + try { + assertEntity(updated, assertion); + } catch (e) { + throw new Error(`assertion does not match. \n id: ${targetId}, \n error: ${e.message}`); + } + } + } + + /** + * Deleted record assertion rule. + * + * 1. all expectations should match to anyone in previous records. + * 2. all expectations should not match to anyone in current records. + */ + if (tableAssertion.deleted) { + const newlyDeletedEntities = previousEntities.filter((e1) => !currentEntities.some((e2) => e1.id === e2.id)); + const deleteAssertions = tableAssertion.deleted.assertion || []; + + if (typeof tableAssertion.deleted.count === 'number' || typeof tableAssertion.deleted.count === 'function') { + const valid = + typeof tableAssertion.deleted.count === 'function' + ? tableAssertion.deleted.count(newlyDeletedEntities.length) + : tableAssertion.deleted.count === newlyDeletedEntities.length; + + if (!valid) { + throw new Error( + `deleted record count does not match (for delete assertion).\n\n expected: ${tableAssertion.deleted.count},\n actual: ${ + newlyDeletedEntities.length + }`, + ); + } + } + + for (const assertion of deleteAssertions) { + const errorMessages = getFailedAssertionMessages(newlyDeletedEntities, assertion); + const allAssertionFailed = errorMessages.length === newlyDeletedEntities.length; + if (allAssertionFailed) { + throw new Error( + `provided assertion does not match to any. \n\n assertion: ${JSON.stringify(assertion)}, \n deleted: ${JSON.stringify( + newlyDeletedEntities, + )}, \n\n -- ${errorMessages.join(',\n -- ')}`, + ); + } + } + } + } +} + +/** + * @internal + * @param entities + * @param assertion + */ +function getFailedAssertionMessages(entities: T[], assertion: EntityAssertion): string[] { + const errors: string[] = []; + for (const entity of entities) { + try { + assertEntity(entity, assertion); + } catch (e) { + errors.push(`id "${entity.id}": ${e.message}`); + } + } + + return errors; +} + +/** + * @private + * @internal + */ +function canSatisfy(entity: BaseEntity, assertion: EntityAssertion): boolean { + let result = false; + + try { + assertEntity(entity, assertion); + result = true; + } catch {} + + return result; +} + +// to management singleton +let testBed: EntityTestBed | null = null; +function getEntityTestBed(): EntityTestBed { + return testBed ? testBed : (testBed = new EntityTestBed()); +} + +/** + * @internal used in EntityTestBed + * @param entities + */ +export function getLatestUpdatedTime(entities: BaseEntity[]): number { + // min value as timestamp. + let max = 0; + + for (const e of entities) { + max = Math.max(max, e.createdAt); + max = Math.max(max, e.updatedAt); + } + + return max; +} diff --git a/modules/models/common/testing/entity-test-bed/entity-test-bed/index.ts b/modules/models/common/testing/entity-test-bed/entity-test-bed/index.ts new file mode 100644 index 0000000..f2e6d21 --- /dev/null +++ b/modules/models/common/testing/entity-test-bed/entity-test-bed/index.ts @@ -0,0 +1 @@ +export { EntityTestBed, DatabaseSnapshot } from './entity-test-bed'; diff --git a/modules/models/common/testing/entity-test-bed/index.ts b/modules/models/common/testing/entity-test-bed/index.ts new file mode 100644 index 0000000..80ea7d4 --- /dev/null +++ b/modules/models/common/testing/entity-test-bed/index.ts @@ -0,0 +1 @@ +export * from './entity-test-bed'; diff --git a/modules/models/common/testing/index.ts b/modules/models/common/testing/index.ts new file mode 100644 index 0000000..3deb075 --- /dev/null +++ b/modules/models/common/testing/index.ts @@ -0,0 +1,2 @@ +export * from './entity-test-bed'; +export * from './test-helpers'; diff --git a/modules/models/common/testing/test-helpers/export-as-json-file.ts b/modules/models/common/testing/test-helpers/export-as-json-file.ts new file mode 100644 index 0000000..3262f2c --- /dev/null +++ b/modules/models/common/testing/test-helpers/export-as-json-file.ts @@ -0,0 +1,10 @@ +import { writeFile } from 'fs'; +import { promisify } from 'util'; + +const writeFileAsync = promisify(writeFile); + +export async function exportAsJsonFile(path: string, data: any): Promise { + const contents = JSON.stringify(data, null, 2); + + await writeFileAsync(path, contents); +} diff --git a/modules/models/common/testing/test-helpers/index.ts b/modules/models/common/testing/test-helpers/index.ts new file mode 100644 index 0000000..0ec4fff --- /dev/null +++ b/modules/models/common/testing/test-helpers/index.ts @@ -0,0 +1,2 @@ +export * from './override-timestamp-columns'; +export { exportAsJsonFile } from './export-as-json-file'; diff --git a/modules/models/common/testing/test-helpers/override-timestamp-columns.spec.ts b/modules/models/common/testing/test-helpers/override-timestamp-columns.spec.ts new file mode 100644 index 0000000..4bfb770 --- /dev/null +++ b/modules/models/common/testing/test-helpers/override-timestamp-columns.spec.ts @@ -0,0 +1,31 @@ +import { overrideTimestampColumns } from '@dripjs/models'; + +describe('overrideTimestampColumns', () => { + describe('When shallow object', () => { + it('should override target key', () => { + expect( + overrideTimestampColumns({ + timestamp: 1, + }), + ).toEqual({ + timestamp: 'overridden', + }); + }); + }); + + describe('When deep object', () => { + it('should override target key', () => { + expect( + overrideTimestampColumns({ + deep: { + timestamp: 1, + }, + }), + ).toEqual({ + deep: { + timestamp: 'overridden', + }, + }); + }); + }); +}); diff --git a/modules/models/common/testing/test-helpers/override-timestamp-columns.ts b/modules/models/common/testing/test-helpers/override-timestamp-columns.ts new file mode 100644 index 0000000..0406def --- /dev/null +++ b/modules/models/common/testing/test-helpers/override-timestamp-columns.ts @@ -0,0 +1,81 @@ +import { difference, isObject, pick } from 'lodash'; + +/** + * Override autogenerated timestamp columns with provided values. + * + * @param target + * @param valueToOverride + */ +export function overrideTimestampColumns(target: T, valueToOverride: any = 'overridden'): T { + if (target === null || target === undefined || typeof target === 'string' || typeof target === 'number' || typeof target === 'boolean') { + return target; + } + + if (Array.isArray(target)) { + target.forEach((v: any) => overrideTimestampColumns(v, valueToOverride)); + } else { + overrideProperties(target, valueToOverride); + } + + return target; +} + +function overrideProperties(object: { [key: string]: any }, valueToOverride: any): void { + Object.keys(object).forEach((key) => { + if (isObject(object[key])) { + overrideProperties(object[key], valueToOverride); + } else { + // all column names that exists in @dripjs/core-models. + switch (key) { + case 'createdAt': + case 'updatedAt': + case 'executedAt': + case 'canceledAt': + case 'orderedAt': + case 'tickAt': + case 'timestamp': + case 'lastExecutedAt': { + object[key] = valueToOverride; + + break; + } + default: { + // noop + } + } + } + }); +} + +/** + * Override autogenerated columns with provided values. + * + * @param target + * @param attrs + */ +export function unpickAutoGenColumns(target: T, attrs?: string[]): T { + if (!isIterable(target)) { + return target; + } + + const attrsToUnpick = attrs ? attrs : ['createdAt', 'updatedAt', 'id']; + + const pickImpl = (ob: any): object => { + return pick(ob, difference(Object.keys(ob), attrsToUnpick)); + }; + + if (Array.isArray(target)) { + return ((target as any[]).map(pickImpl) as unknown) as T; + } else { + return ([target].map(pickImpl)[0] as unknown) as T; + } +} + +function isIterable(obj: any): boolean { + // checks for null and undefined + if (obj == null) { + return false; + } + + return typeof obj[Symbol.iterator] === 'function'; +} diff --git a/modules/models/entity/index.ts b/modules/models/entity/index.ts new file mode 100644 index 0000000..8739983 --- /dev/null +++ b/modules/models/entity/index.ts @@ -0,0 +1 @@ +export * from './master-pair'; diff --git a/modules/models/entity/master-pair/index.ts b/modules/models/entity/master-pair/index.ts index 70e0491..04ea3a0 100644 --- a/modules/models/entity/master-pair/index.ts +++ b/modules/models/entity/master-pair/index.ts @@ -1 +1,2 @@ export * from './master-pair.entity'; +export * from './master-pair.repository'; diff --git a/modules/models/entity/master-pair/master-pair.entity.ts b/modules/models/entity/master-pair/master-pair.entity.ts index 4970fd2..8a59cd2 100644 --- a/modules/models/entity/master-pair/master-pair.entity.ts +++ b/modules/models/entity/master-pair/master-pair.entity.ts @@ -28,7 +28,7 @@ export class MasterPairEntity { type: 'varchar', name: 'exchange', length: 10, - comment: 'exchange of special pair name', + comment: 'exchange name of special pair', }) readonly exchange!: Pair['exchange']; @@ -94,7 +94,7 @@ export class MasterPairEntity { default: () => '0.0', transformer: getFloorByDigitsTransformer(9), }) - readonly minOrderPrice!: Pair['minOrderAmount']; + readonly minOrderPrice!: Pair['minOrderPrice']; @Column({ type: 'int', @@ -102,7 +102,7 @@ export class MasterPairEntity { unsigned: true, comment: 'max Order price', }) - readonly maxOrderPrice!: Pair['maxOrderAmount']; + readonly maxOrderPrice!: Pair['maxOrderPrice']; @Column({ type: 'tinyint', diff --git a/modules/models/entity/master-pair/master-pair.repository.ts b/modules/models/entity/master-pair/master-pair.repository.ts new file mode 100644 index 0000000..4f53f49 --- /dev/null +++ b/modules/models/entity/master-pair/master-pair.repository.ts @@ -0,0 +1,58 @@ +import { EntityRepository, Repository } from 'typeorm'; + +import { MasterPairEntity } from './master-pair.entity'; + +export interface MasterPairEntityCreateParams { + name: MasterPairEntity['name']; + baseAsset: MasterPairEntity['baseAsset']; + quoteAsset: MasterPairEntity['quoteAsset']; + amountPrecision: MasterPairEntity['amountPrecision']; + pricePrecision: MasterPairEntity['pricePrecision']; +} + +export interface PairPrecision { + amountPrecision: MasterPairEntity['amountPrecision']; + pricePrecision: MasterPairEntity['pricePrecision']; +} + +@EntityRepository(MasterPairEntity) +export class MasterSpotPairRepository extends Repository { + async findByPair(pair: string): Promise { + return this.findOne({ where: { name: pair } }); + } + + async insertNewPair(params: MasterPairEntityCreateParams): Promise { + return this.save(params as MasterPairEntity, { reload: false }); + } + + async insertNewPairs(params: MasterPairEntityCreateParams[]): Promise { + return this.save(params as MasterPairEntity[], { reload: false }); + } + + async getPrecisionByPair(pair: string): Promise { + const rs = await this.createQueryBuilder() + .select('price_precision', 'pricePrecision') + .addSelect('amount_precision', 'amountPrecision') + .where({ name: pair }) + .limit(1) + .execute(); + + return rs && rs.length > 0 ? rs[0] : { amountPrecision: 0, pricePrecision: 0 }; + } + + async getPair(): Promise { + return this.find({ where: { isEnabled: true } }); + } + + async getVisiblePairs(): Promise { + return this.find({ where: { isEnabled: true } }); + } + + async getPairNames(): Promise { + const rs: { name: string }[] = await this.createQueryBuilder() + .select('name') + .execute(); + + return rs.map((record) => record.name); + } +} diff --git a/modules/models/index.ts b/modules/models/index.ts new file mode 100644 index 0000000..c13a43d --- /dev/null +++ b/modules/models/index.ts @@ -0,0 +1,2 @@ +export * from './entity'; +export * from './common'; diff --git a/package.json b/package.json index 6064539..44123d6 100644 --- a/package.json +++ b/package.json @@ -101,10 +101,13 @@ }, "homepage": "https://github.com/zlq4863947/dripjs#readme", "devDependencies": { + "@nestjs/common": "5.7.2", + "@nestjs/core": "5.7.2", "@nestjs/typeorm": "5.3.0", "@types/jest": "24.0.9", "@types/qs": "6.5.1", "@types/ws": "6.0.1", + "@types/lodash": "4.14.120", "@zerollup/ts-transform-paths": "1.7.0", "conventional-commits-detector": "1.0.2", "coveralls": "3.0.3", @@ -131,14 +134,16 @@ }, "dependencies": { "axios": "^0.18.0", - "bignumber.js": "^8.1.0", + "bignumber.js": "^8.1.1", "dotenv": "^6.2.0", "dripjs-common": "^0.1.0", "dripjs-types": "^0.1.2", + "lodash": "^4.17.11", "moment": "^2.24.0", "qs": "^6.6.0", "rxjs": "^6.4.0", - "typeorm": "^0.2.14", + "typeorm": "0.2.13", + "reflect-metadata": "0.1.12", "ws": "^6.1.4" } } diff --git a/yarn.lock b/yarn.lock index 8850c6d..26b8d92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -139,6 +139,33 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@nestjs/common@5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-5.7.2.tgz#2cf0317d5bdf852ad3018ec4fdb093c11e8be8c1" + integrity sha512-T5jjyfqskRM1v19N1hTs4DkpZhSr8uud36I5EfSYcsx8w140kf710to02hEPUh3iL+7LTRgo4WCjC7RzXijgcg== + dependencies: + axios "0.18.0" + cli-color "1.2.0" + deprecate "1.0.0" + multer "1.3.0" + uuid "3.3.2" + +"@nestjs/core@5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-5.7.2.tgz#680930dad64fd315bd5cbc874929f71ada30f4d5" + integrity sha512-mpCJENUDk9gHjyJ7tcBjIupQeMRgW0/hjB8n43raT31vcsMKHDuoC+l4ZqbMX5kM73c3olnwqUlQ4PE8iiUvGQ== + dependencies: + "@nuxtjs/opencollective" "0.1.0" + body-parser "1.18.3" + cors "2.8.4" + express "4.16.3" + fast-safe-stringify "1.2.0" + iterare "0.0.8" + object-hash "1.3.0" + optional "0.1.4" + path-to-regexp "2.2.1" + uuid "3.3.2" + "@nestjs/typeorm@5.3.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@nestjs/typeorm/-/typeorm-5.3.0.tgz#448d6d08d73ceb3c1f8a00a07e26a86c8a62b9f8" @@ -151,6 +178,16 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@nuxtjs/opencollective@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nuxtjs/opencollective/-/opencollective-0.1.0.tgz#5dfb10b2148ce77e9590bca9b9ed6e71d2a500eb" + integrity sha512-e09TxGpTxMOfVwvVWPKNttKslnCtbvp5ofc0EwlKdA4IA8AUIyeteGraGZGs+JO4zw4y2+YxRlNN2xQ+c6KFjw== + dependencies: + chalk "^2.4.1" + consola "^1.4.3" + esm "^3.0.79" + node-fetch "^2.2.0" + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -199,6 +236,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash@4.14.120": + version "4.14.120" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.120.tgz#cf265d06f6c7a710db087ed07523ab8c1a24047b" + integrity sha512-jQ21kQ120mo+IrDs1nFNVm/AsdFxIx2+vZ347DbogHJPd/JzKNMOqU6HCYin1W6v8l5R9XSO2/e9cxmn7HAnVw== + "@types/lodash@^4.14.110": version "4.14.121" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.121.tgz#9327e20d49b95fc2bf983fc2f045b2c6effc80b9" @@ -272,6 +314,14 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + acorn-globals@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.0.tgz#e3b6f8da3c1552a95ae627571f7dd6923bb54103" @@ -315,7 +365,7 @@ ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-regex@^2.0.0: +ansi-regex@^2.0.0, ansi-regex@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= @@ -360,6 +410,11 @@ app-root-path@^2.0.1: resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a" integrity sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo= +append-field@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-0.1.0.tgz#6ddc58fa083c7bc545d3c5995b2830cc2366d44a" + integrity sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo= + append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -417,6 +472,11 @@ array-find-index@^1.0.1: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -498,7 +558,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== -axios@^0.18.0: +axios@0.18.0, axios@^0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" integrity sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI= @@ -582,10 +642,42 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bignumber.js@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.0.tgz#39c7588a080f1d91c6c20f481c274b767f975826" - integrity sha512-hRv8jfqOBRnK1oGd0zDbfZv5blY3gy4QIsZZQ35mLCEMp/YdZmnrphBhX02SRKyLlohl+Sx6Ol7RlQhVLj/TAg== +bignumber.js@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885" + integrity sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ== + +body-parser@1.18.2: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + integrity sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ= + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + +body-parser@1.18.3: + version "1.18.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" + integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "~1.6.3" + iconv-lite "0.4.23" + on-finished "~2.3.0" + qs "6.5.2" + raw-body "2.3.3" + type-is "~1.6.16" brace-expansion@^1.1.7: version "1.1.11" @@ -660,6 +752,19 @@ builtin-modules@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.0.0.tgz#1e587d44b006620d90286cc7a9238bbc6129cab1" integrity sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg== +busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM= + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -778,6 +883,11 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== +ci-info@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -793,6 +903,18 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +cli-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.2.0.tgz#3a5ae74fd76b6267af666e69e2afbbd01def34d1" + integrity sha1-OlrnT9drYmevZm5p4q+70B3vNNE= + dependencies: + ansi-regex "^2.1.1" + d "1" + es5-ext "^0.10.12" + es6-iterator "2" + memoizee "^0.4.3" + timers-ext "0.1" + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -902,7 +1024,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.0: +concat-stream@^1.5.0, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -912,11 +1034,31 @@ concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" +consola@^1.4.3: + version "1.4.5" + resolved "https://registry.yarnpkg.com/consola/-/consola-1.4.5.tgz#09732d07cb50af07332e54e0f42fafb92b962c4a" + integrity sha512-movqq3MbyXbSf7cG/x+EbO3VjKQVZPB/zeB5+lN1TuBYh9BWDemLQca9P+a4xpO4lXva9rz+Bd8XyqlH136Lww== + dependencies: + chalk "^2.3.2" + figures "^2.0.0" + lodash "^4.17.5" + std-env "^1.1.0" + console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + conventional-changelog-angular@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz#299fdd43df5a1f095283ac16aeedfb0a682ecab0" @@ -1083,6 +1225,16 @@ convert-source-map@^1.1.0, convert-source-map@^1.4.0: dependencies: safe-buffer "~5.1.1" +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1093,6 +1245,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cors@2.8.4: + version "2.8.4" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.4.tgz#2bd381f2eb201020105cd50ea59da63090694686" + integrity sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY= + dependencies: + object-assign "^4" + vary "^1" + cosmiconfig@^5.0.5: version "5.0.7" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04" @@ -1154,6 +1314,13 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + integrity sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8= + dependencies: + es5-ext "^0.10.9" + dargs@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" @@ -1182,7 +1349,7 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1287,6 +1454,26 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +depd@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k= + +depd@~1.1.1, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +deprecate@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/deprecate/-/deprecate-1.0.0.tgz#661490ed2428916a6c8883d8834e5646f4e4a4a8" + integrity sha1-ZhSQ7SQokWpsiIPYg05WRvTkpKg= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" @@ -1302,6 +1489,14 @@ detect-newline@^2.1.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8= + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + diff-sequences@^24.0.0: version "24.0.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.0.0.tgz#cdf8e27ed20d8b8d3caccb4e0c0d8fe31a173013" @@ -1392,6 +1587,11 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -1402,6 +1602,11 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" @@ -1458,6 +1663,47 @@ es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.12, es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.48" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.48.tgz#9a0b31eeded39e64453bcedf6f9d50bbbfb43850" + integrity sha512-CdRvPlX/24Mj5L4NVxTs4804sxiS2CjVprgCmrgoDkdmjdY4D+ySHa7K3jJf8R40dFg0tIm3z/dk326LrnuSGw== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.1" + next-tick "1" + +es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-weak-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + integrity sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8= + dependencies: + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -1535,6 +1781,11 @@ eslint@^5.0.0: table "^5.2.3" text-table "^0.2.0" +esm@^3.0.79: + version "3.2.9" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.9.tgz#9dca653e3b39f89c3c65c5e84cebfa4af345c10d" + integrity sha512-mATFs9dpCjnEyNv27z29UNDmJmBBX8zMdcFip7aIOrBRTpLs8SA+6Ek1QtsWfvecAJVeZy+X5D3Z6xZVtUvYdg== + espree@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" @@ -1583,6 +1834,19 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + exec-sh@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36" @@ -1645,6 +1909,42 @@ expect@^24.1.0: jest-message-util "^24.0.0" jest-regex-util "^24.0.0" +express@4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" + integrity sha1-avilAjUNsyRuzEvs9rWjTSL37VM= + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.2" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.3" + qs "6.5.1" + range-parser "~1.2.0" + safe-buffer "5.1.1" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -1725,6 +2025,11 @@ fast-levenshtein@~2.0.4: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-safe-stringify@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-1.2.0.tgz#ebd42666fd18fe4f2ba4f0d295065f3f85cade96" + integrity sha1-69QmZv0Y/k8rpPDSlQZfP4XK3pY= + fb-watchman@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" @@ -1774,6 +2079,19 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + find-line-column@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/find-line-column/-/find-line-column-0.5.2.tgz#db00238ff868551a182e74a103416d295a98c8ca" @@ -1846,6 +2164,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -1853,6 +2176,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + fs-access@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a" @@ -2166,6 +2494,26 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" +http-errors@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + integrity sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY= + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -2175,6 +2523,18 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== + +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2327,7 +2687,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -2378,6 +2738,11 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ipaddr.js@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" + integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -2414,6 +2779,13 @@ is-callable@^1.1.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-ci@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== + dependencies: + ci-info "^1.5.0" + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -2542,7 +2914,7 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-promise@^2.1.0: +is-promise@^2.1, is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= @@ -2701,6 +3073,11 @@ istanbul-reports@^2.1.1: dependencies: handlebars "^4.1.0" +iterare@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/iterare/-/iterare-0.0.8.tgz#a969a80a1fbff6b78f28776594d7bc2bdfab6aad" + integrity sha1-qWmoCh+/9rePKHdllNe8K9+raq0= + jest-changed-files@^24.0.0: version "24.0.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.0.0.tgz#c02c09a8cc9ca93f513166bc773741bd39898ff7" @@ -3313,7 +3690,7 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~3.0.0" -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.2.1: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5, lodash@^4.2.1: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -3346,6 +3723,13 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-queue@0.1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + make-dir@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -3438,6 +3822,11 @@ mdurl@^1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + mem@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" @@ -3454,6 +3843,20 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^2.0.0" +memoizee@^0.4.3: + version "0.4.14" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" + integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg== + dependencies: + d "1" + es5-ext "^0.10.45" + es6-weak-map "^2.0.2" + event-emitter "^0.3.5" + is-promise "^2.1" + lru-queue "0.1" + next-tick "1" + timers-ext "^0.1.5" + memory-fs@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -3508,6 +3911,11 @@ meow@^5.0.0: trim-newlines "^2.0.0" yargs-parser "^10.0.0" +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + merge-stream@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" @@ -3525,6 +3933,11 @@ merge@^1.2.0: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -3549,13 +3962,18 @@ mime-db@~1.38.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== -mime-types@^2.1.12, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.19: version "2.1.22" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== dependencies: mime-db "~1.38.0" +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -3641,6 +4059,20 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +multer@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.3.0.tgz#092b2670f6846fa4914965efc8cf94c20fec6cd2" + integrity sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI= + dependencies: + append-field "^0.1.0" + busboy "^0.2.11" + concat-stream "^1.5.0" + mkdirp "^0.5.1" + object-assign "^3.0.0" + on-finished "^2.3.0" + type-is "^1.6.4" + xtend "^4.0.0" + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -3691,11 +4123,26 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= + +next-tick@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-fetch@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" + integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -3808,7 +4255,12 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= + +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -3822,6 +4274,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.0.tgz#76d9ba6ff113cf8efc0d996102851fe6723963e2" + integrity sha512-05KzQ70lSeGSrZJQXE5wNDiTkBJDlUT/myi6RX9dVIvz7a7Qh4oH93BQdiPMn27nldYvVQCKMUaM83AfizZlsQ== + object-keys@^1.0.12: version "1.1.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" @@ -3849,6 +4306,13 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +on-finished@^2.3.0, on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3871,6 +4335,11 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +optional@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/optional/-/optional-0.1.4.tgz#cdb1a9bedc737d2025f690ceeb50e049444fd5b3" + integrity sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw== + optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" @@ -4028,6 +4497,11 @@ parse5@^3.0.3: dependencies: "@types/node" "*" +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -4070,6 +4544,16 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -4178,6 +4662,14 @@ prompts@^2.0.1: kleur "^3.0.2" sisteransi "^1.0.0" +proxy-addr@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" + integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.8.0" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -4216,21 +4708,51 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@^6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2" - integrity sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA== +qs@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== -qs@~6.5.2: +qs@6.5.2, qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +qs@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2" + integrity sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA== + quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + integrity sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k= + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + +raw-body@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" + integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== + dependencies: + bytes "3.0.0" + http-errors "1.6.3" + iconv-lite "0.4.23" + unpipe "1.0.0" + rc@^1.2.7, rc@~1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -4283,6 +4805,16 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -4326,6 +4858,11 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +reflect-metadata@0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2" + integrity sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A== + reflect-metadata@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -4491,6 +5028,11 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== + safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -4535,6 +5077,35 @@ sax@>=0.6.0, sax@^1.2.4: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -4560,6 +5131,16 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + integrity sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -4772,11 +5353,33 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== + +std-env@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-1.3.1.tgz#4e1758412439e9ece1d437b1b098551911aa44ee" + integrity sha512-KI2F2pPJpd3lHjng+QLezu0eq+QDtXcv1um016mhOPAJFHKL+09ykK5PUBWta2pZDC8BVV0VPya08A15bUXSLQ== + dependencies: + is-ci "^1.1.0" + stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" @@ -4811,6 +5414,11 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.0.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -4989,6 +5597,14 @@ through@2, "through@>=2.2.7 <3", through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +timers-ext@0.1, timers-ext@^0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -5215,6 +5831,14 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-is@^1.6.4, type-is@~1.6.15, type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -5248,10 +5872,10 @@ typedoc@0.14.2: typedoc-default-themes "^0.5.0" typescript "3.2.x" -typeorm@^0.2.14: - version "0.2.14" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.14.tgz#5bd39de1306977e7ccdb31c3915018374415e9ff" - integrity sha512-kPV8nX7g7hvFpuzdLKGxqkVRHEJe9AK6csgWJJPaCFU3NrLmUG6a2Q0FxY/Z0Qy/2gqt5VG3t3FyFpw360XSvg== +typeorm@0.2.13: + version "0.2.13" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.13.tgz#d6d49cd83aba5709134b99cdaca0b0f1b5002543" + integrity sha512-SfRHZt6un1rCsTtHpBSgG5kh2jWznzrBex1Dfxbnji0nlrrNQ+ET4uxIhq3herk0TE0ETx3R+UjU0MGv8DB/3g== dependencies: app-root-path "^2.0.1" buffer "^5.1.0" @@ -5311,6 +5935,11 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -5349,6 +5978,11 @@ util.promisify@^1.0.0: define-properties "^1.1.2" object.getownpropertydescriptors "^2.0.3" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + uuid@3.3.2, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" @@ -5362,6 +5996,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -5530,7 +6169,7 @@ xmlbuilder@~9.0.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= -xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=