diff --git a/docs/effects/testing.md b/docs/effects/testing.md index b3f9f4cb7d..80a8ee3d2b 100644 --- a/docs/effects/testing.md +++ b/docs/effects/testing.md @@ -1,180 +1,181 @@ ---- -# The documentation below is for NgRx versions 6.x and older. -# Visit https://ngrx.io for the latest documentation. ---- - -# Testing - -## @ngrx/effects/testing - -### provideMockActions - -Provides a mock test provider of the `Actions` Observable for testing effects. This works well with writing -marble tests and tests using the `subscribe` method on an Observable. The mock Actions will deliver a new Observable to subscribe to for each test. - -Details on marble tests and their syntax, as shown in the `hot` and `cold` methods, can be found in [Writing Marble Tests](https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md). - -Usage: - -```ts -import { TestBed } from '@angular/core/testing'; -import { provideMockActions } from '@ngrx/effects/testing'; -import { cold, hot } from 'jasmine-marbles'; -import { Observable } from 'rxjs'; - -import { MyEffects } from './my-effects'; -import * as MyActions from '../actions/my-actions'; - -describe('My Effects', () => { - let effects: MyEffects; - let actions: Observable; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - // any modules needed - ], - providers: [ - MyEffects, - provideMockActions(() => actions), - // other providers - ], - }); - - effects = TestBed.get(MyEffects); - }); - - it('should work', () => { - const action = new MyActions.ExampleAction(); - const completion = new MyActions.ExampleActionSuccess(); - - // Refer to 'Writing Marble Tests' for details on '--a-' syntax - actions = hot('--a-', { a: action }); - const expected = cold('--b', { b: completion }); - - expect(effects.someSource$).toBeObservable(expected); - }); -}); -``` - -It is also possible to use `ReplaySubject` as an alternative for marble tests: - -```ts -import { TestBed } from '@angular/core/testing'; -import { provideMockActions } from '@ngrx/effects/testing'; -import { ReplaySubject } from 'rxjs'; - -import { MyEffects } from './my-effects'; -import * as MyActions from '../actions/my-actions'; - -describe('My Effects', () => { - let effects: MyEffects; - let actions: ReplaySubject<any>; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - // any modules needed - ], - providers: [ - MyEffects, - provideMockActions(() => actions), - // other providers - ], - }); - - effects = TestBed.get(MyEffects); - }); - - it('should work', () => { - actions = new ReplaySubject(1); - actions.next(new MyActions.ExampleAction()); - - effects.someSource$.subscribe(result => { - expect(result).toEqual(new MyActions.ExampleActionSuccess()); - }); - }); -}); -``` - -### getEffectsMetadata - -Returns decorator configuration for all effects in a class instance. -Use this function to ensure that effects have been properly decorated. - -Usage: - -```ts -import { TestBed } from '@angular/core/testing'; -import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; -import { MyEffects } from './my-effects'; - -describe('My Effects', () => { - let effects: MyEffects; - let metadata: EffectsMetadata; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - MyEffects, - // other providers - ], - }); - - effects = TestBed.get(MyEffects); - metadata = getEffectsMetadata(effects); - }); - - it('should register someSource$ that dispatches an action', () => { - expect(metadata.someSource$).toEqual({ dispatch: true }); - }); - - it('should register someOtherSource$ that does not dispatch an action', () => { - expect(metadata.someOtherSource$).toEqual({ dispatch: false }); - }); - - it('should not register undecoratedSource$', () => { - expect(metadata.undecoratedSource$).toBeUndefined(); - }); -}); -``` - -### Effects as functions - -Effects can be defined as functions as well as variables. Defining an effect as a function allows you to define default values while having the option to override these variables during tests. This without breaking the functionality in the application. - -The following example effect debounces the user input into from a search action. - -```ts -@Effect() -search$ = this.actions$.pipe( - ofType(BookActionTypes.Search), - debounceTime(300, asyncScheduler), - switchMap(...) -``` - -The same effect but now defined as a function, would look as follows: - -```ts -@Effect() -// refactor as input properties and provide default values -search$ = ({ - debounce = 300, - scheduler = asyncScheduler -} = {}) => this.actions$.pipe( - ofType(BookActionTypes.Search), - debounceTime(debounce, scheduler), - switchMap(...) -``` - -Within our tests we can now override the default properties: - -```ts -const actual = effects.search$({ - debounce: 30, - scheduler: getTestScheduler(), -}); -expect(actual).toBeObservable(expected); -``` - -Doing this has the extra benefit of hiding implementation details, making your tests less prone to break due to implementation details changes. Meaning that if you would change the `debounceTime` inside the effect your tests wouldn't have to be changed,these tests would still pass. +--- +# The documentation below is for NgRx versions 6.x and older. +# Visit https://ngrx.io for the latest documentation. +--- + +# Testing + +## @ngrx/effects/testing + +### provideMockActions + +Provides a mock test provider of the `Actions` Observable for testing effects. This works well with writing +marble tests and tests using the `subscribe` method on an Observable. The mock Actions will deliver a new Observable to subscribe to for each test. + +Details on marble tests and their syntax, as shown in the `hot` and `cold` methods, can be found in [Writing Marble Tests](https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md). + +Usage: + +```ts +import { TestBed } from '@angular/core/testing'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { cold, hot } from 'jasmine-marbles'; +import { Observable } from 'rxjs'; + +import { MyEffects } from './my-effects'; +import * as MyActions from '../actions/my-actions'; + +describe('My Effects', () => { + let effects: MyEffects; + let actions: Observable; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + // any modules needed + ], + providers: [ + MyEffects, + provideMockActions(() => actions), + // other providers + ], + }); + + effects = TestBed.get(MyEffects); + }); + + it('should work', () => { + const action = new MyActions.ExampleAction(); + const completion = new MyActions.ExampleActionSuccess(); + + // Refer to 'Writing Marble Tests' for details on '--a-' syntax + actions = hot('--a-', { a: action }); + const expected = cold('--b', { b: completion }); + + expect(effects.someSource$).toBeObservable(expected); + }); +}); +``` + +It is also possible to use `ReplaySubject` as an alternative for marble tests: + +```ts +import { TestBed } from '@angular/core/testing'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { ReplaySubject } from 'rxjs'; + +import { MyEffects } from './my-effects'; +import * as MyActions from '../actions/my-actions'; + +describe('My Effects', () => { + let effects: MyEffects; + let actions: ReplaySubject & lt; + any & gt; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + // any modules needed + ], + providers: [ + MyEffects, + provideMockActions(() => actions), + // other providers + ], + }); + + effects = TestBed.get(MyEffects); + }); + + it('should work', () => { + actions = new ReplaySubject(1); + actions.next(new MyActions.ExampleAction()); + + effects.someSource$.subscribe(result => { + expect(result).toEqual(new MyActions.ExampleActionSuccess()); + }); + }); +}); +``` + +### getEffectsMetadata + +Returns decorator configuration for all effects in a class instance. +Use this function to ensure that effects have been properly decorated. + +Usage: + +```ts +import { TestBed } from '@angular/core/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { MyEffects } from './my-effects'; + +describe('My Effects', () => { + let effects: MyEffects; + let metadata: EffectsMetadata; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + MyEffects, + // other providers + ], + }); + + effects = TestBed.get(MyEffects); + metadata = getEffectsMetadata(effects); + }); + + it('should register someSource$ that dispatches an action', () => { + expect(metadata.someSource$).toEqual({ dispatch: true }); + }); + + it('should register someOtherSource$ that does not dispatch an action', () => { + expect(metadata.someOtherSource$).toEqual({ dispatch: false }); + }); + + it('should not register undecoratedSource$', () => { + expect(metadata.undecoratedSource$).toBeUndefined(); + }); +}); +``` + +### Effects as functions + +Effects can be defined as functions as well as variables. Defining an effect as a function allows you to define default values while having the option to override these variables during tests. This without breaking the functionality in the application. + +The following example effect debounces the user input into from a search action. + +```ts +@Effect() +search$ = this.actions$.pipe( + ofType(BookActionTypes.Search), + debounceTime(300, asyncScheduler), + switchMap(...) +``` + +The same effect but now defined as a function, would look as follows: + +```ts +@Effect() +// refactor as input properties and provide default values +search$ = ({ + debounce = 300, + scheduler = asyncScheduler +} = {}) => this.actions$.pipe( + ofType(BookActionTypes.Search), + debounceTime(debounce, scheduler), + switchMap(...) +``` + +Within our tests we can now override the default properties: + +```ts +const actual = effects.search$({ + debounce: 30, + scheduler: getTestScheduler(), +}); +expect(actual).toBeObservable(expected); +``` + +Doing this has the extra benefit of hiding implementation details, making your tests less prone to break due to implementation details changes. Meaning that if you would change the `debounceTime` inside the effect your tests wouldn't have to be changed,these tests would still pass. diff --git a/docs/store/testing.md b/docs/store/testing.md index 10668b983d..50dd745f7d 100644 --- a/docs/store/testing.md +++ b/docs/store/testing.md @@ -1,157 +1,157 @@ ---- -# The documentation below is for NgRx versions 6.x and older. -# Visit https://ngrx.io for the latest documentation. ---- - -# Testing - -### Providing Store for testing - -Use the `StoreModule.forRoot` in your `TestBed` configuration when testing components or services that inject `Store`. - -- Reducing state is synchronous, so mocking out the `Store` isn't required. -- Use the `combineReducers` method with the map of feature reducers to compose the `State` for the test. -- Dispatch actions to load data into the `Store`. - -my-component.ts - -```ts -import { Component, OnInit } from '@angular/core'; -import { Store, select } from '@ngrx/store'; -import * as fromFeature from '../reducers'; -import * as DataActions from '../actions/data'; - -@Component({ - selector: 'my-component', - template: ` -
{{ item }}
- - - `, -}) -export class MyComponent implements OnInit { - items$ = this.store.pipe(select(fromFeature.selectFeatureItems)); - - constructor(private store: Store) {} - - ngOnInit() { - this.store.dispatch(new DataActions.LoadData()); - } - - onRefresh() { - this.store.dispatch(new DataActions.RefreshItems()); - } -} -``` - -my-component.spec.ts - -```ts -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { StoreModule, Store, combineReducers } from '@ngrx/store'; -import { MyComponent } from './my.component'; -import * as fromRoot from '../reducers'; -import * as fromFeature from './reducers'; -import * as DataActions from '../actions/data'; - -describe('My Component', () => { - let component: MyComponent; - let fixture: ComponentFixture; - let store: Store; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({ - ...fromRoot.reducers, - feature: combineReducers(fromFeature.reducers), - }), - // other imports - ], - declarations: [ - MyComponent, - // other declarations - ], - providers: [ - // other providers - ], - }); - - store = TestBed.get(Store); - - spyOn(store, 'dispatch').and.callThrough(); - - fixture = TestBed.createComponent(MyComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); - - it('should dispatch an action to load data when created', () => { - const action = new DataActions.LoadData(); - - expect(store.dispatch).toHaveBeenCalledWith(action); - }); - - it('should dispatch an action to refreshing data', () => { - const action = new DataActions.RefreshData(); - - component.onRefresh(); - - expect(store.dispatch).toHaveBeenCalledWith(action); - }); - - it('should display a list of items after the data is loaded', () => { - const items = [1, 2, 3]; - const action = new DataActions.LoadDataSuccess({ items }); - - store.dispatch(action); - - component.items$.subscribe(data => { - expect(data.length).toBe(items.length); - }); - }); -}); -``` - -### Testing selectors - -You can use the projector function used by the selector by accessing the `.projector` property. - -my-reducer.ts - -```ts -export interface State { - evenNums: number[]; - oddNums: number[]; -} - -export const selectSumEvenNums = createSelector( - (state: State) => state.evenNums, - evenNums => evenNums.reduce((prev, curr) => prev + curr) -); -export const selectSumOddNums = createSelector( - (state: State) => state.oddNums, - oddNums => oddNums.reduce((prev, curr) => prev + curr) -); -export const selectTotal = createSelector( - selectSumEvenNums, - selectSumOddNums, - (evenSum, oddSum) => evenSum + oddSum -); -``` - -my-reducer.spec.ts - -```ts -import * as fromMyReducers from './my-reducers'; - -describe('My Selectors', () => { - it('should calc selectTotal', () => { - expect(fromMyReducers.selectTotal.projector(2, 3)).toBe(5); - }); -}); -``` +--- +# The documentation below is for NgRx versions 6.x and older. +# Visit https://ngrx.io for the latest documentation. +--- + +# Testing + +### Providing Store for testing + +Use the `StoreModule.forRoot` in your `TestBed` configuration when testing components or services that inject `Store`. + +- Reducing state is synchronous, so mocking out the `Store` isn't required. +- Use the `combineReducers` method with the map of feature reducers to compose the `State` for the test. +- Dispatch actions to load data into the `Store`. + +my-component.ts + +```ts +import { Component, OnInit } from '@angular/core'; +import { Store, select } from '@ngrx/store'; +import * as fromFeature from '../reducers'; +import * as DataActions from '../actions/data'; + +@Component({ + selector: 'my-component', + template: ` +
{{ item }}
+ + + `, +}) +export class MyComponent implements OnInit { + items$ = this.store.pipe(select(fromFeature.selectFeatureItems)); + + constructor(private store: Store) {} + + ngOnInit() { + this.store.dispatch(new DataActions.LoadData()); + } + + onRefresh() { + this.store.dispatch(new DataActions.RefreshItems()); + } +} +``` + +my-component.spec.ts + +```ts +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { StoreModule, Store, combineReducers } from '@ngrx/store'; +import { MyComponent } from './my.component'; +import * as fromRoot from '../reducers'; +import * as fromFeature from './reducers'; +import * as DataActions from '../actions/data'; + +describe('My Component', () => { + let component: MyComponent; + let fixture: ComponentFixture; + let store: Store; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({ + ...fromRoot.reducers, + feature: combineReducers(fromFeature.reducers), + }), + // other imports + ], + declarations: [ + MyComponent, + // other declarations + ], + providers: [ + // other providers + ], + }); + + store = TestBed.get(Store); + + spyOn(store, 'dispatch').and.callThrough(); + + fixture = TestBed.createComponent(MyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); + + it('should dispatch an action to load data when created', () => { + const action = new DataActions.LoadData(); + + expect(store.dispatch).toHaveBeenCalledWith(action); + }); + + it('should dispatch an action to refreshing data', () => { + const action = new DataActions.RefreshData(); + + component.onRefresh(); + + expect(store.dispatch).toHaveBeenCalledWith(action); + }); + + it('should display a list of items after the data is loaded', () => { + const items = [1, 2, 3]; + const action = new DataActions.LoadDataSuccess({ items }); + + store.dispatch(action); + + component.items$.subscribe(data => { + expect(data.length).toBe(items.length); + }); + }); +}); +``` + +### Testing selectors + +You can use the projector function used by the selector by accessing the `.projector` property. + +my-reducer.ts + +```ts +export interface State { + evenNums: number[]; + oddNums: number[]; +} + +export const selectSumEvenNums = createSelector( + (state: State) => state.evenNums, + evenNums => evenNums.reduce((prev, curr) => prev + curr) +); +export const selectSumOddNums = createSelector( + (state: State) => state.oddNums, + oddNums => oddNums.reduce((prev, curr) => prev + curr) +); +export const selectTotal = createSelector( + selectSumEvenNums, + selectSumOddNums, + (evenSum, oddSum) => evenSum + oddSum +); +``` + +my-reducer.spec.ts + +```ts +import * as fromMyReducers from './my-reducers'; + +describe('My Selectors', () => { + it('should calc selectTotal', () => { + expect(fromMyReducers.selectTotal.projector(2, 3)).toBe(5); + }); +}); +``` diff --git a/modules/data/spec/dataservices/default-data.service.spec.ts b/modules/data/spec/dataservices/default-data.service.spec.ts index 8cca8ec1d5..3f084ee4af 100644 --- a/modules/data/spec/dataservices/default-data.service.spec.ts +++ b/modules/data/spec/dataservices/default-data.service.spec.ts @@ -1,596 +1,602 @@ -import { TestBed } from '@angular/core/testing'; - -import { HttpClient } from '@angular/common/http'; -import { - HttpClientTestingModule, - HttpTestingController, -} from '@angular/common/http/testing'; - -import { of } from 'rxjs'; - -import { Update } from '@ngrx/entity'; - -import { - DefaultDataService, - DefaultDataServiceFactory, - DefaultHttpUrlGenerator, - HttpUrlGenerator, - DefaultDataServiceConfig, - DataServiceError, -} from '../../'; - -class Hero { - id!: number; - name!: string; - version?: number; -} - -//////// Tests ///////////// -describe('DefaultDataService', () => { - let httpClient: HttpClient; - let httpTestingController: HttpTestingController; - const heroUrl = 'api/hero/'; - const heroesUrl = 'api/heroes/'; - let httpUrlGenerator: HttpUrlGenerator; - let service: DefaultDataService; - - //// HttpClient testing boilerplate - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - }); - httpClient = TestBed.get(HttpClient); - httpTestingController = TestBed.get(HttpTestingController); - - httpUrlGenerator = new DefaultHttpUrlGenerator(null as any); - httpUrlGenerator.registerHttpResourceUrls({ - Hero: { - entityResourceUrl: heroUrl, - collectionResourceUrl: heroesUrl, - }, - }); - - service = new DefaultDataService('Hero', httpClient, httpUrlGenerator); - }); - - afterEach(() => { - // After every test, assert that there are no pending requests. - httpTestingController.verify(); - }); - /////////////////// - - describe('property inspection', () => { - // Test wrapper exposes protected properties - class TestService extends DefaultDataService { - properties = { - entityUrl: this.entityUrl, - entitiesUrl: this.entitiesUrl, - getDelay: this.getDelay, - saveDelay: this.saveDelay, - timeout: this.timeout, - }; - } - - // tslint:disable-next-line:no-shadowed-variable - let service: TestService; - - beforeEach(() => { - // use test wrapper class to get to protected properties - service = new TestService('Hero', httpClient, httpUrlGenerator); - }); - - it('has expected name', () => { - expect(service.name).toBe('Hero DefaultDataService'); - }); - - it('has expected single-entity url', () => { - expect(service.properties.entityUrl).toBe(heroUrl); - }); - - it('has expected multiple-entities url', () => { - expect(service.properties.entitiesUrl).toBe(heroesUrl); - }); - }); - - describe('#getAll', () => { - let expectedHeroes: Hero[]; - - beforeEach(() => { - expectedHeroes = [{ id: 1, name: 'A' }, { id: 2, name: 'B' }] as Hero[]; - }); - - it('should return expected heroes (called once)', () => { - service - .getAll() - .subscribe( - heroes => - expect(heroes).toEqual( - expectedHeroes, - 'should return expected heroes' - ), - fail - ); - - // HeroService should have made one request to GET heroes from expected URL - const req = httpTestingController.expectOne(heroesUrl); - expect(req.request.method).toEqual('GET'); - - expect(req.request.body).toBeNull('should not send data'); - - // Respond with the mock heroes - req.flush(expectedHeroes); - }); - - it('should be OK returning no heroes', () => { - service - .getAll() - .subscribe( - heroes => - expect(heroes.length).toEqual(0, 'should have empty heroes array'), - fail - ); - - const req = httpTestingController.expectOne(heroesUrl); - req.flush([]); // Respond with no heroes - }); - - it('should return expected heroes (called multiple times)', () => { - service.getAll().subscribe(); - service.getAll().subscribe(); - service - .getAll() - .subscribe( - heroes => - expect(heroes).toEqual( - expectedHeroes, - 'should return expected heroes' - ), - fail - ); - - const requests = httpTestingController.match(heroesUrl); - expect(requests.length).toEqual(3, 'calls to getAll()'); - - // Respond to each request with different mock hero results - requests[0].flush([]); - requests[1].flush([{ id: 1, name: 'bob' }]); - requests[2].flush(expectedHeroes); - }); - - it('should turn 404 into Observable', () => { - const msg = 'deliberate 404 error'; - - service.getAll().subscribe( - heroes => fail('getAll succeeded when expected it to fail with a 404'), - err => { - expect(err).toBeDefined('request should have failed'); - expect(err instanceof DataServiceError).toBe( - true, - 'is DataServiceError' - ); - expect(err.error.status).toEqual(404, 'has 404 status'); - expect(err.message).toEqual(msg, 'has expected error message'); - } - ); - - const req = httpTestingController.expectOne(heroesUrl); - - const errorEvent = { - // Source of the service's not-so-friendly user-facing message - message: msg, - - // The rest of this is optional and not used. Just showing that you could. - filename: 'DefaultDataService.ts', - lineno: 42, - colno: 21, - } as ErrorEvent; - - req.error(errorEvent, { status: 404, statusText: 'Not Found' }); - }); - }); - - describe('#getById', () => { - let expectedHero: Hero; - const heroUrlId1 = heroUrl + '1'; - - it('should return expected hero when id is found', () => { - expectedHero = { id: 1, name: 'A' }; - - service - .getById(1) - .subscribe( - hero => - expect(hero).toEqual(expectedHero, 'should return expected hero'), - fail - ); - - // One request to GET hero from expected URL - const req = httpTestingController.expectOne(heroUrlId1); - - expect(req.request.body).toBeNull('should not send data'); - - // Respond with the expected hero - req.flush(expectedHero); - }); - - it('should turn 404 when id not found', () => { - service.getById(1).subscribe( - heroes => fail('getById succeeded when expected it to fail with a 404'), - err => { - expect(err instanceof DataServiceError).toBe(true); - } - ); - - const req = httpTestingController.expectOne(heroUrlId1); - const errorEvent = { message: 'boom!' } as ErrorEvent; - req.error(errorEvent, { status: 404, statusText: 'Not Found' }); - }); - - it('should throw when no id given', () => { - service.getById(undefined as any).subscribe( - heroes => fail('getById succeeded when expected it to fail'), - err => { - expect(err.error).toMatch(/No "Hero" key/); - } - ); - }); - }); - - describe('#getWithQuery', () => { - let expectedHeroes: Hero[]; - - beforeEach(() => { - expectedHeroes = [{ id: 1, name: 'BA' }, { id: 2, name: 'BB' }] as Hero[]; - }); - - it('should return expected selected heroes w/ object params', () => { - service - .getWithQuery({ name: 'B' }) - .subscribe( - heroes => - expect(heroes).toEqual( - expectedHeroes, - 'should return expected heroes' - ), - fail - ); - - // HeroService should have made one request to GET heroes - // from expected URL with query params - const req = httpTestingController.expectOne(heroesUrl + '?name=B'); - expect(req.request.method).toEqual('GET'); - - expect(req.request.body).toBeNull('should not send data'); - - // Respond with the mock heroes - req.flush(expectedHeroes); - }); - - it('should return expected selected heroes w/ string params', () => { - service - .getWithQuery('name=B') - .subscribe( - heroes => - expect(heroes).toEqual( - expectedHeroes, - 'should return expected heroes' - ), - fail - ); - - // HeroService should have made one request to GET heroes - // from expected URL with query params - const req = httpTestingController.expectOne(heroesUrl + '?name=B'); - expect(req.request.method).toEqual('GET'); - - // Respond with the mock heroes - req.flush(expectedHeroes); - }); - - it('should be OK returning no heroes', () => { - service - .getWithQuery({ name: 'B' }) - .subscribe( - heroes => - expect(heroes.length).toEqual(0, 'should have empty heroes array'), - fail - ); - - const req = httpTestingController.expectOne(heroesUrl + '?name=B'); - req.flush([]); // Respond with no heroes - }); - - it('should turn 404 into Observable', () => { - const msg = 'deliberate 404 error'; - - service.getWithQuery({ name: 'B' }).subscribe( - heroes => - fail('getWithQuery succeeded when expected it to fail with a 404'), - err => { - expect(err).toBeDefined('request should have failed'); - expect(err instanceof DataServiceError).toBe( - true, - 'is DataServiceError' - ); - expect(err.error.status).toEqual(404, 'has 404 status'); - expect(err.message).toEqual(msg, 'has expected error message'); - } - ); - - const req = httpTestingController.expectOne(heroesUrl + '?name=B'); - - const errorEvent = { message: msg } as ErrorEvent; - - req.error(errorEvent, { status: 404, statusText: 'Not Found' }); - }); - }); - - describe('#add', () => { - let expectedHero: Hero; - - it('should return expected hero with id', () => { - expectedHero = { id: 42, name: 'A' }; - const heroData: Hero = { id: undefined, name: 'A' } as any; - - service - .add(heroData) - .subscribe( - hero => - expect(hero).toEqual(expectedHero, 'should return expected hero'), - fail - ); - - // One request to POST hero from expected URL - const req = httpTestingController.expectOne( - r => r.method === 'POST' && r.url === heroUrl - ); - - expect(req.request.body).toEqual(heroData, 'should send entity data'); - - // Respond with the expected hero - req.flush(expectedHero); - }); - - it('should throw when no entity given', () => { - service.add(undefined as any).subscribe( - heroes => fail('add succeeded when expected it to fail'), - err => { - expect(err.error).toMatch(/No "Hero" entity/); - } - ); - }); - }); - - describe('#delete', () => { - const heroUrlId1 = heroUrl + '1'; - - it('should delete by hero id', () => { - service - .delete(1) - .subscribe( - result => - expect(result).toEqual(1, 'should return the deleted entity id'), - fail - ); - - // One request to DELETE hero from expected URL - const req = httpTestingController.expectOne( - r => r.method === 'DELETE' && r.url === heroUrlId1 - ); - - expect(req.request.body).toBeNull('should not send data'); - - // Respond with empty nonsense object - req.flush({}); - }); - - it('should return successfully when id not found and delete404OK is true (default)', () => { - service - .delete(1) - .subscribe( - result => - expect(result).toEqual(1, 'should return the deleted entity id'), - fail - ); - - // One request to DELETE hero from expected URL - const req = httpTestingController.expectOne( - r => r.method === 'DELETE' && r.url === heroUrlId1 - ); - - // Respond with empty nonsense object - req.flush({}); - }); - - it('should return 404 when id not found and delete404OK is false', () => { - service = new DefaultDataService('Hero', httpClient, httpUrlGenerator, { - delete404OK: false, - }); - service.delete(1).subscribe( - heroes => fail('delete succeeded when expected it to fail with a 404'), - err => { - expect(err instanceof DataServiceError).toBe(true); - } - ); - - const req = httpTestingController.expectOne(heroUrlId1); - const errorEvent = { message: 'boom!' } as ErrorEvent; - req.error(errorEvent, { status: 404, statusText: 'Not Found' }); - }); - - it('should throw when no id given', () => { - service.delete(undefined as any).subscribe( - heroes => fail('delete succeeded when expected it to fail'), - err => { - expect(err.error).toMatch(/No "Hero" key/); - } - ); - }); - }); - - describe('#update', () => { - const heroUrlId1 = heroUrl + '1'; - - it('should return expected hero with id', () => { - // Call service.update with an Update arg - const updateArg: Update = { - id: 1, - changes: { id: 1, name: 'B' }, - }; - - // The server makes the update AND updates the version concurrency property. - const expectedHero: Hero = { id: 1, name: 'B', version: 2 }; - - service - .update(updateArg) - .subscribe( - updated => - expect(updated).toEqual( - expectedHero, - 'should return the expected hero' - ), - fail - ); - - // One request to PUT hero from expected URL - const req = httpTestingController.expectOne( - r => r.method === 'PUT' && r.url === heroUrlId1 - ); - - expect(req.request.body).toEqual( - updateArg.changes, - 'should send update entity data' - ); - - // Respond with the expected hero - req.flush(expectedHero); - }); - - it('should return 404 when id not found', () => { - service.update({ id: 1, changes: { id: 1, name: 'B' } }).subscribe( - update => fail('update succeeded when expected it to fail with a 404'), - err => { - expect(err instanceof DataServiceError).toBe(true); - } - ); - - const req = httpTestingController.expectOne(heroUrlId1); - const errorEvent = { message: 'boom!' } as ErrorEvent; - req.error(errorEvent, { status: 404, statusText: 'Not Found' }); - }); - - it('should throw when no update given', () => { - service.update(undefined as any).subscribe( - heroes => fail('update succeeded when expected it to fail'), - err => { - expect(err.error).toMatch(/No "Hero" update data/); - } - ); - }); - }); - - describe('#upsert', () => { - let expectedHero: Hero; - - it('should return expected hero with id', () => { - expectedHero = { id: 42, name: 'A' }; - const heroData: Hero = { id: undefined, name: 'A' } as any; - - service - .upsert(heroData) - .subscribe( - hero => - expect(hero).toEqual(expectedHero, 'should return expected hero'), - fail - ); - - // One request to POST hero from expected URL - const req = httpTestingController.expectOne( - r => r.method === 'POST' && r.url === heroUrl - ); - - expect(req.request.body).toEqual(heroData, 'should send entity data'); - - // Respond with the expected hero - req.flush(expectedHero); - }); - - it('should throw when no entity given', () => { - service.upsert(undefined as any).subscribe( - heroes => fail('add succeeded when expected it to fail'), - err => { - expect(err.error).toMatch(/No "Hero" entity/); - } - ); - }); - }); -}); - -describe('DefaultDataServiceFactory', () => { - const heroUrl = 'api/hero'; - const heroesUrl = 'api/heroes'; - - let http: any; - let httpUrlGenerator: HttpUrlGenerator; - - beforeEach(() => { - httpUrlGenerator = new DefaultHttpUrlGenerator(null as any); - httpUrlGenerator.registerHttpResourceUrls({ - Hero: { - entityResourceUrl: heroUrl, - collectionResourceUrl: heroesUrl, - }, - }); - http = jasmine.createSpyObj('HttpClient', ['get', 'delete', 'post', 'put']); - http.get.and.returnValue(of([])); - }); - - describe('(no config)', () => { - it('can create factory', () => { - const factory = new DefaultDataServiceFactory(http, httpUrlGenerator); - const heroDS = factory.create('Hero'); - expect(heroDS.name).toBe('Hero DefaultDataService'); - }); - - it('should produce hero data service that gets all heroes with expected URL', () => { - const factory = new DefaultDataServiceFactory(http, httpUrlGenerator); - const heroDS = factory.create('Hero'); - heroDS.getAll(); - expect(http.get).toHaveBeenCalledWith('api/heroes', undefined); - }); - }); - - describe('(with config)', () => { - it('can create factory', () => { - const config: DefaultDataServiceConfig = { root: 'api' }; - const factory = new DefaultDataServiceFactory( - http, - httpUrlGenerator, - config - ); - const heroDS = factory.create('Hero'); - expect(heroDS.name).toBe('Hero DefaultDataService'); - }); - - it('should produce hero data service that gets heroes via hero HttpResourceUrls', () => { - const newHeroesUrl = 'some/other/api/heroes'; - const config: DefaultDataServiceConfig = { - root: 'api', - entityHttpResourceUrls: { - Hero: { - entityResourceUrl: heroUrl, - collectionResourceUrl: newHeroesUrl, - }, - }, - }; - const factory = new DefaultDataServiceFactory( - http, - httpUrlGenerator, - config - ); - const heroDS = factory.create('Hero'); - heroDS.getAll(); - expect(http.get).toHaveBeenCalledWith(newHeroesUrl, undefined); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; + +import { HttpClient } from '@angular/common/http'; +import { + HttpClientTestingModule, + HttpTestingController, +} from '@angular/common/http/testing'; + +import { of } from 'rxjs'; + +import { Update } from '@ngrx/entity'; + +import { + DefaultDataService, + DefaultDataServiceFactory, + DefaultHttpUrlGenerator, + HttpUrlGenerator, + DefaultDataServiceConfig, + DataServiceError, +} from '../../'; + +class Hero { + id!: number; + name!: string; + version?: number; +} + +//////// Tests ///////////// +describe('DefaultDataService', () => { + let httpClient: HttpClient; + let httpTestingController: HttpTestingController; + const heroUrl = 'api/hero/'; + const heroesUrl = 'api/heroes/'; + let httpUrlGenerator: HttpUrlGenerator; + let service: DefaultDataService; + + //// HttpClient testing boilerplate + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + httpClient = TestBed.get(HttpClient); + httpTestingController = TestBed.get(HttpTestingController); + + httpUrlGenerator = new DefaultHttpUrlGenerator(null as any); + httpUrlGenerator.registerHttpResourceUrls({ + Hero: { + entityResourceUrl: heroUrl, + collectionResourceUrl: heroesUrl, + }, + }); + + service = new DefaultDataService('Hero', httpClient, httpUrlGenerator); + }); + + afterEach(() => { + // After every test, assert that there are no pending requests. + httpTestingController.verify(); + }); + /////////////////// + + describe('property inspection', () => { + // Test wrapper exposes protected properties + class TestService extends DefaultDataService { + properties = { + entityUrl: this.entityUrl, + entitiesUrl: this.entitiesUrl, + getDelay: this.getDelay, + saveDelay: this.saveDelay, + timeout: this.timeout, + }; + } + + // tslint:disable-next-line:no-shadowed-variable + let service: TestService; + + beforeEach(() => { + // use test wrapper class to get to protected properties + service = new TestService('Hero', httpClient, httpUrlGenerator); + }); + + it('has expected name', () => { + expect(service.name).toBe('Hero DefaultDataService'); + }); + + it('has expected single-entity url', () => { + expect(service.properties.entityUrl).toBe(heroUrl); + }); + + it('has expected multiple-entities url', () => { + expect(service.properties.entitiesUrl).toBe(heroesUrl); + }); + }); + + describe('#getAll', () => { + let expectedHeroes: Hero[]; + + beforeEach(() => { + expectedHeroes = [ + { id: 1, name: 'A' }, + { id: 2, name: 'B' }, + ] as Hero[]; + }); + + it('should return expected heroes (called once)', () => { + service + .getAll() + .subscribe( + heroes => + expect(heroes).toEqual( + expectedHeroes, + 'should return expected heroes' + ), + fail + ); + + // HeroService should have made one request to GET heroes from expected URL + const req = httpTestingController.expectOne(heroesUrl); + expect(req.request.method).toEqual('GET'); + + expect(req.request.body).toBeNull('should not send data'); + + // Respond with the mock heroes + req.flush(expectedHeroes); + }); + + it('should be OK returning no heroes', () => { + service + .getAll() + .subscribe( + heroes => + expect(heroes.length).toEqual(0, 'should have empty heroes array'), + fail + ); + + const req = httpTestingController.expectOne(heroesUrl); + req.flush([]); // Respond with no heroes + }); + + it('should return expected heroes (called multiple times)', () => { + service.getAll().subscribe(); + service.getAll().subscribe(); + service + .getAll() + .subscribe( + heroes => + expect(heroes).toEqual( + expectedHeroes, + 'should return expected heroes' + ), + fail + ); + + const requests = httpTestingController.match(heroesUrl); + expect(requests.length).toEqual(3, 'calls to getAll()'); + + // Respond to each request with different mock hero results + requests[0].flush([]); + requests[1].flush([{ id: 1, name: 'bob' }]); + requests[2].flush(expectedHeroes); + }); + + it('should turn 404 into Observable', () => { + const msg = 'deliberate 404 error'; + + service.getAll().subscribe( + heroes => fail('getAll succeeded when expected it to fail with a 404'), + err => { + expect(err).toBeDefined('request should have failed'); + expect(err instanceof DataServiceError).toBe( + true, + 'is DataServiceError' + ); + expect(err.error.status).toEqual(404, 'has 404 status'); + expect(err.message).toEqual(msg, 'has expected error message'); + } + ); + + const req = httpTestingController.expectOne(heroesUrl); + + const errorEvent = { + // Source of the service's not-so-friendly user-facing message + message: msg, + + // The rest of this is optional and not used. Just showing that you could. + filename: 'DefaultDataService.ts', + lineno: 42, + colno: 21, + } as ErrorEvent; + + req.error(errorEvent, { status: 404, statusText: 'Not Found' }); + }); + }); + + describe('#getById', () => { + let expectedHero: Hero; + const heroUrlId1 = heroUrl + '1'; + + it('should return expected hero when id is found', () => { + expectedHero = { id: 1, name: 'A' }; + + service + .getById(1) + .subscribe( + hero => + expect(hero).toEqual(expectedHero, 'should return expected hero'), + fail + ); + + // One request to GET hero from expected URL + const req = httpTestingController.expectOne(heroUrlId1); + + expect(req.request.body).toBeNull('should not send data'); + + // Respond with the expected hero + req.flush(expectedHero); + }); + + it('should turn 404 when id not found', () => { + service.getById(1).subscribe( + heroes => fail('getById succeeded when expected it to fail with a 404'), + err => { + expect(err instanceof DataServiceError).toBe(true); + } + ); + + const req = httpTestingController.expectOne(heroUrlId1); + const errorEvent = { message: 'boom!' } as ErrorEvent; + req.error(errorEvent, { status: 404, statusText: 'Not Found' }); + }); + + it('should throw when no id given', () => { + service.getById(undefined as any).subscribe( + heroes => fail('getById succeeded when expected it to fail'), + err => { + expect(err.error).toMatch(/No "Hero" key/); + } + ); + }); + }); + + describe('#getWithQuery', () => { + let expectedHeroes: Hero[]; + + beforeEach(() => { + expectedHeroes = [ + { id: 1, name: 'BA' }, + { id: 2, name: 'BB' }, + ] as Hero[]; + }); + + it('should return expected selected heroes w/ object params', () => { + service + .getWithQuery({ name: 'B' }) + .subscribe( + heroes => + expect(heroes).toEqual( + expectedHeroes, + 'should return expected heroes' + ), + fail + ); + + // HeroService should have made one request to GET heroes + // from expected URL with query params + const req = httpTestingController.expectOne(heroesUrl + '?name=B'); + expect(req.request.method).toEqual('GET'); + + expect(req.request.body).toBeNull('should not send data'); + + // Respond with the mock heroes + req.flush(expectedHeroes); + }); + + it('should return expected selected heroes w/ string params', () => { + service + .getWithQuery('name=B') + .subscribe( + heroes => + expect(heroes).toEqual( + expectedHeroes, + 'should return expected heroes' + ), + fail + ); + + // HeroService should have made one request to GET heroes + // from expected URL with query params + const req = httpTestingController.expectOne(heroesUrl + '?name=B'); + expect(req.request.method).toEqual('GET'); + + // Respond with the mock heroes + req.flush(expectedHeroes); + }); + + it('should be OK returning no heroes', () => { + service + .getWithQuery({ name: 'B' }) + .subscribe( + heroes => + expect(heroes.length).toEqual(0, 'should have empty heroes array'), + fail + ); + + const req = httpTestingController.expectOne(heroesUrl + '?name=B'); + req.flush([]); // Respond with no heroes + }); + + it('should turn 404 into Observable', () => { + const msg = 'deliberate 404 error'; + + service.getWithQuery({ name: 'B' }).subscribe( + heroes => + fail('getWithQuery succeeded when expected it to fail with a 404'), + err => { + expect(err).toBeDefined('request should have failed'); + expect(err instanceof DataServiceError).toBe( + true, + 'is DataServiceError' + ); + expect(err.error.status).toEqual(404, 'has 404 status'); + expect(err.message).toEqual(msg, 'has expected error message'); + } + ); + + const req = httpTestingController.expectOne(heroesUrl + '?name=B'); + + const errorEvent = { message: msg } as ErrorEvent; + + req.error(errorEvent, { status: 404, statusText: 'Not Found' }); + }); + }); + + describe('#add', () => { + let expectedHero: Hero; + + it('should return expected hero with id', () => { + expectedHero = { id: 42, name: 'A' }; + const heroData: Hero = { id: undefined, name: 'A' } as any; + + service + .add(heroData) + .subscribe( + hero => + expect(hero).toEqual(expectedHero, 'should return expected hero'), + fail + ); + + // One request to POST hero from expected URL + const req = httpTestingController.expectOne( + r => r.method === 'POST' && r.url === heroUrl + ); + + expect(req.request.body).toEqual(heroData, 'should send entity data'); + + // Respond with the expected hero + req.flush(expectedHero); + }); + + it('should throw when no entity given', () => { + service.add(undefined as any).subscribe( + heroes => fail('add succeeded when expected it to fail'), + err => { + expect(err.error).toMatch(/No "Hero" entity/); + } + ); + }); + }); + + describe('#delete', () => { + const heroUrlId1 = heroUrl + '1'; + + it('should delete by hero id', () => { + service + .delete(1) + .subscribe( + result => + expect(result).toEqual(1, 'should return the deleted entity id'), + fail + ); + + // One request to DELETE hero from expected URL + const req = httpTestingController.expectOne( + r => r.method === 'DELETE' && r.url === heroUrlId1 + ); + + expect(req.request.body).toBeNull('should not send data'); + + // Respond with empty nonsense object + req.flush({}); + }); + + it('should return successfully when id not found and delete404OK is true (default)', () => { + service + .delete(1) + .subscribe( + result => + expect(result).toEqual(1, 'should return the deleted entity id'), + fail + ); + + // One request to DELETE hero from expected URL + const req = httpTestingController.expectOne( + r => r.method === 'DELETE' && r.url === heroUrlId1 + ); + + // Respond with empty nonsense object + req.flush({}); + }); + + it('should return 404 when id not found and delete404OK is false', () => { + service = new DefaultDataService('Hero', httpClient, httpUrlGenerator, { + delete404OK: false, + }); + service.delete(1).subscribe( + heroes => fail('delete succeeded when expected it to fail with a 404'), + err => { + expect(err instanceof DataServiceError).toBe(true); + } + ); + + const req = httpTestingController.expectOne(heroUrlId1); + const errorEvent = { message: 'boom!' } as ErrorEvent; + req.error(errorEvent, { status: 404, statusText: 'Not Found' }); + }); + + it('should throw when no id given', () => { + service.delete(undefined as any).subscribe( + heroes => fail('delete succeeded when expected it to fail'), + err => { + expect(err.error).toMatch(/No "Hero" key/); + } + ); + }); + }); + + describe('#update', () => { + const heroUrlId1 = heroUrl + '1'; + + it('should return expected hero with id', () => { + // Call service.update with an Update arg + const updateArg: Update = { + id: 1, + changes: { id: 1, name: 'B' }, + }; + + // The server makes the update AND updates the version concurrency property. + const expectedHero: Hero = { id: 1, name: 'B', version: 2 }; + + service + .update(updateArg) + .subscribe( + updated => + expect(updated).toEqual( + expectedHero, + 'should return the expected hero' + ), + fail + ); + + // One request to PUT hero from expected URL + const req = httpTestingController.expectOne( + r => r.method === 'PUT' && r.url === heroUrlId1 + ); + + expect(req.request.body).toEqual( + updateArg.changes, + 'should send update entity data' + ); + + // Respond with the expected hero + req.flush(expectedHero); + }); + + it('should return 404 when id not found', () => { + service.update({ id: 1, changes: { id: 1, name: 'B' } }).subscribe( + update => fail('update succeeded when expected it to fail with a 404'), + err => { + expect(err instanceof DataServiceError).toBe(true); + } + ); + + const req = httpTestingController.expectOne(heroUrlId1); + const errorEvent = { message: 'boom!' } as ErrorEvent; + req.error(errorEvent, { status: 404, statusText: 'Not Found' }); + }); + + it('should throw when no update given', () => { + service.update(undefined as any).subscribe( + heroes => fail('update succeeded when expected it to fail'), + err => { + expect(err.error).toMatch(/No "Hero" update data/); + } + ); + }); + }); + + describe('#upsert', () => { + let expectedHero: Hero; + + it('should return expected hero with id', () => { + expectedHero = { id: 42, name: 'A' }; + const heroData: Hero = { id: undefined, name: 'A' } as any; + + service + .upsert(heroData) + .subscribe( + hero => + expect(hero).toEqual(expectedHero, 'should return expected hero'), + fail + ); + + // One request to POST hero from expected URL + const req = httpTestingController.expectOne( + r => r.method === 'POST' && r.url === heroUrl + ); + + expect(req.request.body).toEqual(heroData, 'should send entity data'); + + // Respond with the expected hero + req.flush(expectedHero); + }); + + it('should throw when no entity given', () => { + service.upsert(undefined as any).subscribe( + heroes => fail('add succeeded when expected it to fail'), + err => { + expect(err.error).toMatch(/No "Hero" entity/); + } + ); + }); + }); +}); + +describe('DefaultDataServiceFactory', () => { + const heroUrl = 'api/hero'; + const heroesUrl = 'api/heroes'; + + let http: any; + let httpUrlGenerator: HttpUrlGenerator; + + beforeEach(() => { + httpUrlGenerator = new DefaultHttpUrlGenerator(null as any); + httpUrlGenerator.registerHttpResourceUrls({ + Hero: { + entityResourceUrl: heroUrl, + collectionResourceUrl: heroesUrl, + }, + }); + http = jasmine.createSpyObj('HttpClient', ['get', 'delete', 'post', 'put']); + http.get.and.returnValue(of([])); + }); + + describe('(no config)', () => { + it('can create factory', () => { + const factory = new DefaultDataServiceFactory(http, httpUrlGenerator); + const heroDS = factory.create('Hero'); + expect(heroDS.name).toBe('Hero DefaultDataService'); + }); + + it('should produce hero data service that gets all heroes with expected URL', () => { + const factory = new DefaultDataServiceFactory(http, httpUrlGenerator); + const heroDS = factory.create('Hero'); + heroDS.getAll(); + expect(http.get).toHaveBeenCalledWith('api/heroes', undefined); + }); + }); + + describe('(with config)', () => { + it('can create factory', () => { + const config: DefaultDataServiceConfig = { root: 'api' }; + const factory = new DefaultDataServiceFactory( + http, + httpUrlGenerator, + config + ); + const heroDS = factory.create('Hero'); + expect(heroDS.name).toBe('Hero DefaultDataService'); + }); + + it('should produce hero data service that gets heroes via hero HttpResourceUrls', () => { + const newHeroesUrl = 'some/other/api/heroes'; + const config: DefaultDataServiceConfig = { + root: 'api', + entityHttpResourceUrls: { + Hero: { + entityResourceUrl: heroUrl, + collectionResourceUrl: newHeroesUrl, + }, + }, + }; + const factory = new DefaultDataServiceFactory( + http, + httpUrlGenerator, + config + ); + const heroDS = factory.create('Hero'); + heroDS.getAll(); + expect(http.get).toHaveBeenCalledWith(newHeroesUrl, undefined); + }); + }); +}); diff --git a/modules/data/spec/effects/entity-cache-effects.spec.ts b/modules/data/spec/effects/entity-cache-effects.spec.ts index 14210a0fe9..d6498d3167 100644 --- a/modules/data/spec/effects/entity-cache-effects.spec.ts +++ b/modules/data/spec/effects/entity-cache-effects.spec.ts @@ -1,211 +1,211 @@ -// Not using marble testing -import { TestBed } from '@angular/core/testing'; -import { Action } from '@ngrx/store'; -import { Actions } from '@ngrx/effects'; -import { observeOn } from 'rxjs/operators'; -import { asapScheduler, ReplaySubject, Subject } from 'rxjs'; - -import { - EntityCacheEffects, - EntityActionFactory, - EntityCacheDataService, - SaveEntities, - SaveEntitiesSuccess, - SaveEntitiesCancel, - SaveEntitiesCanceled, - SaveEntitiesError, - HttpMethods, - DataServiceError, - ChangeSet, - ChangeSetItem, - ChangeSetOperation, - Logger, - MergeStrategy, -} from '../..'; - -describe('EntityCacheEffects (normal testing)', () => { - let actions$: ReplaySubject; - let correlationId: string; - let dataService: TestEntityCacheDataService; - let effects: EntityCacheEffects; - let logger: Logger; - let mergeStrategy: MergeStrategy | undefined; - let options: { - correlationId: typeof correlationId; - mergeStrategy: typeof mergeStrategy; - }; - - function expectCompletion(completion: any, done: DoneFn) { - effects.saveEntities$.subscribe(result => { - expect(result).toEqual(completion); - done(); - }, fail); - } - - beforeEach(() => { - actions$ = new ReplaySubject(1); - correlationId = 'CORID42'; - logger = jasmine.createSpyObj('Logger', ['error', 'log', 'warn']); - mergeStrategy = undefined; - options = { correlationId, mergeStrategy }; - - const eaFactory = new EntityActionFactory(); // doesn't change. - - TestBed.configureTestingModule({ - providers: [ - EntityCacheEffects, - { provide: EntityActionFactory, useValue: eaFactory }, - { provide: Actions, useValue: actions$ }, - /* tslint:disable-next-line:no-use-before-declare */ - { - provide: EntityCacheDataService, - useClass: TestEntityCacheDataService, - }, - { provide: Logger, useValue: logger }, - ], - }); - - actions$ = TestBed.get(Actions); - effects = TestBed.get(EntityCacheEffects); - dataService = TestBed.get(EntityCacheDataService); - }); - - it('should return a SAVE_ENTITIES_SUCCESS with the expected ChangeSet on success', (done: DoneFn) => { - const cs = createChangeSet(); - const action = new SaveEntities(cs, 'test/save', options); - const completion = new SaveEntitiesSuccess(cs, 'test/save', options); - - expectCompletion(completion, done); - - actions$.next(action); - dataService.setResponse(cs); - }); - - it('should not emit SAVE_ENTITIES_SUCCESS if cancel arrives in time', (done: DoneFn) => { - const cs = createChangeSet(); - const action = new SaveEntities(cs, 'test/save', options); - const cancel = new SaveEntitiesCancel(correlationId, 'Test Cancel'); - - effects.saveEntities$.subscribe(result => { - expect(result instanceof SaveEntitiesSuccess).toBe(false); - expect(result instanceof SaveEntitiesCanceled).toBe(true); // instead - done(); - }, done.fail); - - actions$.next(action); - actions$.next(cancel); - dataService.setResponse(cs); - }); - - it('should emit SAVE_ENTITIES_SUCCESS if cancel arrives too late', (done: DoneFn) => { - const cs = createChangeSet(); - const action = new SaveEntities(cs, 'test/save', options); - const cancel = new SaveEntitiesCancel(correlationId, 'Test Cancel'); - - effects.saveEntities$.subscribe(result => { - expect(result instanceof SaveEntitiesSuccess).toBe(true); - done(); - }, done.fail); - - actions$.next(action); - dataService.setResponse(cs); - setTimeout(() => actions$.next(cancel), 1); - }); - - it('should emit SAVE_ENTITIES_SUCCESS immediately if no changes to save', (done: DoneFn) => { - const action = new SaveEntities({ changes: [] }, 'test/save', options); - effects.saveEntities$.subscribe(result => { - expect(result instanceof SaveEntitiesSuccess).toBe(true); - expect(dataService.saveEntities).not.toHaveBeenCalled(); - done(); - }, done.fail); - actions$.next(action); - }); - - xit('should return a SAVE_ENTITIES_ERROR when data service fails', (done: DoneFn) => { - const cs = createChangeSet(); - const action = new SaveEntities(cs, 'test/save', options); - const httpError = { error: new Error('Test Failure'), status: 501 }; - const error = makeDataServiceError('POST', httpError); - const completion = new SaveEntitiesError(error, action); - - expectCompletion(completion, done); - - actions$.next(action); - dataService.setErrorResponse(error); - }); -}); - -// #region test helpers -export class TestEntityCacheDataService { - response$ = new Subject(); - - saveEntities = jasmine - .createSpy('saveEntities') - .and.returnValue(this.response$.pipe(observeOn(asapScheduler))); - - setResponse(data: any) { - this.response$.next(data); - } - - setErrorResponse(error: any) { - this.response$.error(error); - } -} - -/** make error produced by the EntityDataService */ -function makeDataServiceError( - /** Http method for that action */ - method: HttpMethods, - /** Http error from the web api */ - httpError?: any, - /** Options sent with the request */ - options?: any -) { - let url = 'test/save'; - if (httpError) { - url = httpError.url || url; - } else { - httpError = { error: new Error('Test error'), status: 500, url }; - } - return new DataServiceError(httpError, { method, url, options }); -} - -function createChangeSet(): ChangeSet { - const changes: ChangeSetItem[] = [ - { - op: ChangeSetOperation.Add, - entityName: 'Hero', - entities: [{ id: 1, name: 'A1 Add' }], - }, - { - op: ChangeSetOperation.Delete, - entityName: 'Hero', - entities: [2, 3], - }, - { - op: ChangeSetOperation.Update, - entityName: 'Villain', - entities: [ - { id: 4, changes: { id: 4, name: 'V4 Update' } }, - { id: 5, changes: { id: 5, name: 'V5 Update' } }, - { id: 6, changes: { id: 6, name: 'V6 Update' } }, - ], - }, - { - op: ChangeSetOperation.Upsert, - entityName: 'Villain', - entities: [ - { id: 7, name: 'V7 Upsert new' }, - { id: 4, name: 'V4 Upsert existing' }, - ], - }, - ]; - - return { - changes, - extras: { foo: 'anything' }, - tag: 'Test', - }; -} -// #endregion test helpers +// Not using marble testing +import { TestBed } from '@angular/core/testing'; +import { Action } from '@ngrx/store'; +import { Actions } from '@ngrx/effects'; +import { observeOn } from 'rxjs/operators'; +import { asapScheduler, ReplaySubject, Subject } from 'rxjs'; + +import { + EntityCacheEffects, + EntityActionFactory, + EntityCacheDataService, + SaveEntities, + SaveEntitiesSuccess, + SaveEntitiesCancel, + SaveEntitiesCanceled, + SaveEntitiesError, + HttpMethods, + DataServiceError, + ChangeSet, + ChangeSetItem, + ChangeSetOperation, + Logger, + MergeStrategy, +} from '../..'; + +describe('EntityCacheEffects (normal testing)', () => { + let actions$: ReplaySubject; + let correlationId: string; + let dataService: TestEntityCacheDataService; + let effects: EntityCacheEffects; + let logger: Logger; + let mergeStrategy: MergeStrategy | undefined; + let options: { + correlationId: typeof correlationId; + mergeStrategy: typeof mergeStrategy; + }; + + function expectCompletion(completion: any, done: DoneFn) { + effects.saveEntities$.subscribe(result => { + expect(result).toEqual(completion); + done(); + }, fail); + } + + beforeEach(() => { + actions$ = new ReplaySubject(1); + correlationId = 'CORID42'; + logger = jasmine.createSpyObj('Logger', ['error', 'log', 'warn']); + mergeStrategy = undefined; + options = { correlationId, mergeStrategy }; + + const eaFactory = new EntityActionFactory(); // doesn't change. + + TestBed.configureTestingModule({ + providers: [ + EntityCacheEffects, + { provide: EntityActionFactory, useValue: eaFactory }, + { provide: Actions, useValue: actions$ }, + /* tslint:disable-next-line:no-use-before-declare */ + { + provide: EntityCacheDataService, + useClass: TestEntityCacheDataService, + }, + { provide: Logger, useValue: logger }, + ], + }); + + actions$ = TestBed.get(Actions); + effects = TestBed.get(EntityCacheEffects); + dataService = TestBed.get(EntityCacheDataService); + }); + + it('should return a SAVE_ENTITIES_SUCCESS with the expected ChangeSet on success', (done: DoneFn) => { + const cs = createChangeSet(); + const action = new SaveEntities(cs, 'test/save', options); + const completion = new SaveEntitiesSuccess(cs, 'test/save', options); + + expectCompletion(completion, done); + + actions$.next(action); + dataService.setResponse(cs); + }); + + it('should not emit SAVE_ENTITIES_SUCCESS if cancel arrives in time', (done: DoneFn) => { + const cs = createChangeSet(); + const action = new SaveEntities(cs, 'test/save', options); + const cancel = new SaveEntitiesCancel(correlationId, 'Test Cancel'); + + effects.saveEntities$.subscribe(result => { + expect(result instanceof SaveEntitiesSuccess).toBe(false); + expect(result instanceof SaveEntitiesCanceled).toBe(true); // instead + done(); + }, done.fail); + + actions$.next(action); + actions$.next(cancel); + dataService.setResponse(cs); + }); + + it('should emit SAVE_ENTITIES_SUCCESS if cancel arrives too late', (done: DoneFn) => { + const cs = createChangeSet(); + const action = new SaveEntities(cs, 'test/save', options); + const cancel = new SaveEntitiesCancel(correlationId, 'Test Cancel'); + + effects.saveEntities$.subscribe(result => { + expect(result instanceof SaveEntitiesSuccess).toBe(true); + done(); + }, done.fail); + + actions$.next(action); + dataService.setResponse(cs); + setTimeout(() => actions$.next(cancel), 1); + }); + + it('should emit SAVE_ENTITIES_SUCCESS immediately if no changes to save', (done: DoneFn) => { + const action = new SaveEntities({ changes: [] }, 'test/save', options); + effects.saveEntities$.subscribe(result => { + expect(result instanceof SaveEntitiesSuccess).toBe(true); + expect(dataService.saveEntities).not.toHaveBeenCalled(); + done(); + }, done.fail); + actions$.next(action); + }); + + xit('should return a SAVE_ENTITIES_ERROR when data service fails', (done: DoneFn) => { + const cs = createChangeSet(); + const action = new SaveEntities(cs, 'test/save', options); + const httpError = { error: new Error('Test Failure'), status: 501 }; + const error = makeDataServiceError('POST', httpError); + const completion = new SaveEntitiesError(error, action); + + expectCompletion(completion, done); + + actions$.next(action); + dataService.setErrorResponse(error); + }); +}); + +// #region test helpers +export class TestEntityCacheDataService { + response$ = new Subject(); + + saveEntities = jasmine + .createSpy('saveEntities') + .and.returnValue(this.response$.pipe(observeOn(asapScheduler))); + + setResponse(data: any) { + this.response$.next(data); + } + + setErrorResponse(error: any) { + this.response$.error(error); + } +} + +/** make error produced by the EntityDataService */ +function makeDataServiceError( + /** Http method for that action */ + method: HttpMethods, + /** Http error from the web api */ + httpError?: any, + /** Options sent with the request */ + options?: any +) { + let url = 'test/save'; + if (httpError) { + url = httpError.url || url; + } else { + httpError = { error: new Error('Test error'), status: 500, url }; + } + return new DataServiceError(httpError, { method, url, options }); +} + +function createChangeSet(): ChangeSet { + const changes: ChangeSetItem[] = [ + { + op: ChangeSetOperation.Add, + entityName: 'Hero', + entities: [{ id: 1, name: 'A1 Add' }], + }, + { + op: ChangeSetOperation.Delete, + entityName: 'Hero', + entities: [2, 3], + }, + { + op: ChangeSetOperation.Update, + entityName: 'Villain', + entities: [ + { id: 4, changes: { id: 4, name: 'V4 Update' } }, + { id: 5, changes: { id: 5, name: 'V5 Update' } }, + { id: 6, changes: { id: 6, name: 'V6 Update' } }, + ], + }, + { + op: ChangeSetOperation.Upsert, + entityName: 'Villain', + entities: [ + { id: 7, name: 'V7 Upsert new' }, + { id: 4, name: 'V4 Upsert existing' }, + ], + }, + ]; + + return { + changes, + extras: { foo: 'anything' }, + tag: 'Test', + }; +} +// #endregion test helpers diff --git a/modules/data/spec/entity-metadata/entity-definition.service.spec.ts b/modules/data/spec/entity-metadata/entity-definition.service.spec.ts index cfc90b7698..923f7623dd 100644 --- a/modules/data/spec/entity-metadata/entity-definition.service.spec.ts +++ b/modules/data/spec/entity-metadata/entity-definition.service.spec.ts @@ -1,144 +1,144 @@ -import { NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; - -import { - createEntityDefinition, - EntityDefinitionService, - EntityMetadataMap, - ENTITY_METADATA_TOKEN, -} from '../..'; - -@NgModule({}) -class LazyModule { - lazyMetadataMap = { - Sidekick: {}, - }; - - constructor(entityDefinitionService: EntityDefinitionService) { - entityDefinitionService.registerMetadataMap(this.lazyMetadataMap); - } -} - -describe('EntityDefinitionService', () => { - let service: EntityDefinitionService; - let metadataMap: EntityMetadataMap; - - beforeEach(() => { - metadataMap = { - Hero: {}, - Villain: {}, - }; - - TestBed.configureTestingModule({ - // Not actually lazy but demonstrates a module that registers metadata - imports: [LazyModule], - providers: [ - EntityDefinitionService, - { provide: ENTITY_METADATA_TOKEN, multi: true, useValue: metadataMap }, - ], - }); - service = TestBed.get(EntityDefinitionService); - }); - - describe('#getDefinition', () => { - it('returns definition for known entity', () => { - const def = service.getDefinition('Hero'); - expect(def).toBeDefined(); - }); - - it('throws if request definition for unknown entity', () => { - expect(() => service.getDefinition('Foo')).toThrowError(/no entity/i); - }); - - it('returns undefined if request definition for unknown entity and `shouldThrow` is false', () => { - const def = service.getDefinition('foo', /* shouldThrow */ false); - expect(def).not.toBeDefined(); - }); - }); - - describe('#registerMetadata(Map)', () => { - it('can register a new definition by metadata', () => { - service.registerMetadata({ entityName: 'Foo' }); - - let def = service.getDefinition('Foo'); - expect(def).toBeDefined(); - // Hero is still defined after registering Foo - def = service.getDefinition('Hero'); - expect(def).toBeDefined('Hero still defined'); - }); - - it('can register new definitions by metadata map', () => { - service.registerMetadataMap({ - Foo: {}, - Bar: {}, - }); - - let def = service.getDefinition('Foo'); - expect(def).toBeDefined('Foo'); - def = service.getDefinition('Bar'); - expect(def).toBeDefined('Bar'); - def = service.getDefinition('Hero'); - expect(def).toBeDefined('Hero still defined'); - }); - - it('entityName property should trump map key', () => { - service.registerMetadataMap({ - 1: { entityName: 'Foo' }, // key and entityName differ - }); - - let def = service.getDefinition('Foo'); - expect(def).toBeDefined('Foo'); - def = service.getDefinition('Hero'); - expect(def).toBeDefined('Hero still defined'); - }); - - it('a (lazy-loaded) module can register metadata with its constructor', () => { - // The `Sidekick` metadata are registered by LazyModule's ctor - // Although LazyModule is actually eagerly-loaded in this test, - // the registration technique is the important thing. - const def = service.getDefinition('Sidekick'); - expect(def).toBeDefined('Sidekick'); - }); - }); - - describe('#registerDefinition(s)', () => { - it('can register a new definition', () => { - const newDef = createEntityDefinition({ entityName: 'Foo' }); - service.registerDefinition(newDef); - - let def = service.getDefinition('Foo'); - expect(def).toBeDefined(); - // Hero is still defined after registering Foo - def = service.getDefinition('Hero'); - expect(def).toBeDefined('Hero still defined'); - }); - - it('can register a map of several definitions', () => { - const newDefMap = { - Foo: createEntityDefinition({ entityName: 'Foo' }), - Bar: createEntityDefinition({ entityName: 'Bar' }), - }; - service.registerDefinitions(newDefMap); - - let def = service.getDefinition('Foo'); - expect(def).toBeDefined('Foo'); - def = service.getDefinition('Bar'); - expect(def).toBeDefined('Bar'); - def = service.getDefinition('Hero'); - expect(def).toBeDefined('Hero still defined'); - }); - - it('can re-register an existing definition', () => { - const testSelectId = (entity: any) => 'test-id'; - const newDef = createEntityDefinition({ - entityName: 'Hero', - selectId: testSelectId, - }); - service.registerDefinition(newDef); - - const def = service.getDefinition('Hero'); - expect(def).toBeDefined('Hero still defined'); - expect(def.selectId).toBe(testSelectId, 'updated w/ new selectId'); - }); - }); -}); +import { NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; + +import { + createEntityDefinition, + EntityDefinitionService, + EntityMetadataMap, + ENTITY_METADATA_TOKEN, +} from '../..'; + +@NgModule({}) +class LazyModule { + lazyMetadataMap = { + Sidekick: {}, + }; + + constructor(entityDefinitionService: EntityDefinitionService) { + entityDefinitionService.registerMetadataMap(this.lazyMetadataMap); + } +} + +describe('EntityDefinitionService', () => { + let service: EntityDefinitionService; + let metadataMap: EntityMetadataMap; + + beforeEach(() => { + metadataMap = { + Hero: {}, + Villain: {}, + }; + + TestBed.configureTestingModule({ + // Not actually lazy but demonstrates a module that registers metadata + imports: [LazyModule], + providers: [ + EntityDefinitionService, + { provide: ENTITY_METADATA_TOKEN, multi: true, useValue: metadataMap }, + ], + }); + service = TestBed.get(EntityDefinitionService); + }); + + describe('#getDefinition', () => { + it('returns definition for known entity', () => { + const def = service.getDefinition('Hero'); + expect(def).toBeDefined(); + }); + + it('throws if request definition for unknown entity', () => { + expect(() => service.getDefinition('Foo')).toThrowError(/no entity/i); + }); + + it('returns undefined if request definition for unknown entity and `shouldThrow` is false', () => { + const def = service.getDefinition('foo', /* shouldThrow */ false); + expect(def).not.toBeDefined(); + }); + }); + + describe('#registerMetadata(Map)', () => { + it('can register a new definition by metadata', () => { + service.registerMetadata({ entityName: 'Foo' }); + + let def = service.getDefinition('Foo'); + expect(def).toBeDefined(); + // Hero is still defined after registering Foo + def = service.getDefinition('Hero'); + expect(def).toBeDefined('Hero still defined'); + }); + + it('can register new definitions by metadata map', () => { + service.registerMetadataMap({ + Foo: {}, + Bar: {}, + }); + + let def = service.getDefinition('Foo'); + expect(def).toBeDefined('Foo'); + def = service.getDefinition('Bar'); + expect(def).toBeDefined('Bar'); + def = service.getDefinition('Hero'); + expect(def).toBeDefined('Hero still defined'); + }); + + it('entityName property should trump map key', () => { + service.registerMetadataMap({ + 1: { entityName: 'Foo' }, // key and entityName differ + }); + + let def = service.getDefinition('Foo'); + expect(def).toBeDefined('Foo'); + def = service.getDefinition('Hero'); + expect(def).toBeDefined('Hero still defined'); + }); + + it('a (lazy-loaded) module can register metadata with its constructor', () => { + // The `Sidekick` metadata are registered by LazyModule's ctor + // Although LazyModule is actually eagerly-loaded in this test, + // the registration technique is the important thing. + const def = service.getDefinition('Sidekick'); + expect(def).toBeDefined('Sidekick'); + }); + }); + + describe('#registerDefinition(s)', () => { + it('can register a new definition', () => { + const newDef = createEntityDefinition({ entityName: 'Foo' }); + service.registerDefinition(newDef); + + let def = service.getDefinition('Foo'); + expect(def).toBeDefined(); + // Hero is still defined after registering Foo + def = service.getDefinition('Hero'); + expect(def).toBeDefined('Hero still defined'); + }); + + it('can register a map of several definitions', () => { + const newDefMap = { + Foo: createEntityDefinition({ entityName: 'Foo' }), + Bar: createEntityDefinition({ entityName: 'Bar' }), + }; + service.registerDefinitions(newDefMap); + + let def = service.getDefinition('Foo'); + expect(def).toBeDefined('Foo'); + def = service.getDefinition('Bar'); + expect(def).toBeDefined('Bar'); + def = service.getDefinition('Hero'); + expect(def).toBeDefined('Hero still defined'); + }); + + it('can re-register an existing definition', () => { + const testSelectId = (entity: any) => 'test-id'; + const newDef = createEntityDefinition({ + entityName: 'Hero', + selectId: testSelectId, + }); + service.registerDefinition(newDef); + + const def = service.getDefinition('Hero'); + expect(def).toBeDefined('Hero still defined'); + expect(def.selectId).toBe(testSelectId, 'updated w/ new selectId'); + }); + }); +}); diff --git a/modules/data/spec/selectors/related-entity-selectors.spec.ts b/modules/data/spec/selectors/related-entity-selectors.spec.ts index 612b1dbb24..9d9175d9dd 100644 --- a/modules/data/spec/selectors/related-entity-selectors.spec.ts +++ b/modules/data/spec/selectors/related-entity-selectors.spec.ts @@ -1,487 +1,481 @@ -import { TestBed } from '@angular/core/testing'; -import { createSelector, StoreModule, Store } from '@ngrx/store'; -import { Actions } from '@ngrx/effects'; -import { Update } from '@ngrx/entity'; - -import { Observable } from 'rxjs'; -import { skip } from 'rxjs/operators'; - -import { - EntityMetadataMap, - EntityActionFactory, - EntitySelectorsFactory, - EntityCache, - EntityDataModuleWithoutEffects, - ENTITY_METADATA_TOKEN, - EntityOp, - EntityAction, -} from '../../'; - -const entityMetadataMap: EntityMetadataMap = { - Battle: {}, - Hero: {}, - HeroPowerMap: {}, - Power: { - sortComparer: sortByName, - }, - Sidekick: {}, -}; - -describe('Related-entity Selectors', () => { - // #region setup - let eaFactory: EntityActionFactory; - let entitySelectorsFactory: EntitySelectorsFactory; - let store: Store; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [StoreModule.forRoot({}), EntityDataModuleWithoutEffects], - providers: [ - // required by EntityData but not used in these tests - { provide: Actions, useValue: null }, - { - provide: ENTITY_METADATA_TOKEN, - multi: true, - useValue: entityMetadataMap, - }, - ], - }); - - store = TestBed.get(Store); - eaFactory = TestBed.get(EntityActionFactory); - entitySelectorsFactory = TestBed.get(EntitySelectorsFactory); - initializeCache(eaFactory, store); - }); - - // #endregion setup - - describe('hero -> sidekick (1-1)', () => { - function setCollectionSelectors() { - const heroSelectors = entitySelectorsFactory.create('Hero'); - const selectHeroMap = heroSelectors.selectEntityMap; - - const sidekickSelectors = entitySelectorsFactory.create( - 'Sidekick' - ); - const selectSidekickMap = sidekickSelectors.selectEntityMap; - - return { - selectHeroMap, - selectSidekickMap, - }; - } - - function createHeroSidekickSelector$(heroId: number): Observable { - const { selectHeroMap, selectSidekickMap } = setCollectionSelectors(); - const selectHero = createSelector( - selectHeroMap, - heroes => heroes[heroId] - ); - const selectSideKick = createSelector( - selectHero, - selectSidekickMap, - (hero, sidekicks) => { - const sidekickId = hero && hero.sidekickFk!; - return sidekickId && sidekicks[sidekickId]; - } - ); - return store.select(selectSideKick) as Observable; - } - - // Note: async done() callback ensures test passes only if subscribe(successCallback()) called. - - it('should get Alpha Hero sidekick', (done: DoneFn) => { - createHeroSidekickSelector$(1).subscribe(sk => { - expect(sk.name).toBe('Bob'); - done(); - }); - }); - - it('should get Alpha Hero updated sidekick', (done: DoneFn) => { - // Skip the initial sidekick and check the one after update - createHeroSidekickSelector$(1) - .pipe(skip(1)) - .subscribe(sk => { - expect(sk.name).toBe('Robert'); - done(); - }); - - // update the related sidekick - const action = eaFactory.create>( - 'Sidekick', - EntityOp.UPDATE_ONE, - { id: 1, changes: { id: 1, name: 'Robert' } } - ); - store.dispatch(action); - }); - - it('should get Alpha Hero changed sidekick', (done: DoneFn) => { - // Skip the initial sidekick and check the one after update - createHeroSidekickSelector$(1) - .pipe(skip(1)) - .subscribe(sk => { - expect(sk.name).toBe('Sally'); - done(); - }); - - // update the hero's sidekick from fk=1 to fk=2 - const action = eaFactory.create>( - 'Hero', - EntityOp.UPDATE_ONE, - { id: 1, changes: { id: 1, sidekickFk: 2 } } // Sally - ); - store.dispatch(action); - }); - - it('changing a different hero should NOT trigger first hero selector', (done: DoneFn) => { - let alphaCount = 0; - - createHeroSidekickSelector$(1).subscribe(sk => { - alphaCount += 1; - }); - - // update a different hero's sidekick from fk=2 (Sally) to fk=1 (Bob) - createHeroSidekickSelector$(2) - .pipe(skip(1)) - .subscribe(sk => { - expect(sk.name).toBe('Bob'); - expect(alphaCount).toEqual( - 1, - 'should only callback for Hero #1 once' - ); - done(); - }); - - const action = eaFactory.create>( - 'Hero', - EntityOp.UPDATE_ONE, - { id: 2, changes: { id: 2, sidekickFk: 1 } } // Bob - ); - store.dispatch(action); - }); - - it('should get undefined sidekick if hero not found', (done: DoneFn) => { - createHeroSidekickSelector$(1234).subscribe(sk => { - expect(sk).toBeUndefined(); - done(); - }); - }); - - it('should get undefined sidekick from Gamma because it has no sidekickFk', (done: DoneFn) => { - createHeroSidekickSelector$(3).subscribe(sk => { - expect(sk).toBeUndefined(); - done(); - }); - }); - - it('should get Gamma sidekick after creating and assigning one', (done: DoneFn) => { - // Skip(1), the initial state in which Gamma has no sidekick - // Note that BOTH dispatches complete synchronously, before the selector updates - // so we only have to skip one. - createHeroSidekickSelector$(3) - .pipe(skip(1)) - .subscribe(sk => { - expect(sk.name).toBe('Robin'); - done(); - }); - - // create a new sidekick - let action: EntityAction = eaFactory.create( - 'Sidekick', - EntityOp.ADD_ONE, - { - id: 42, - name: 'Robin', - } - ); - store.dispatch(action); - - // assign new sidekick to Gamma - action = eaFactory.create>('Hero', EntityOp.UPDATE_ONE, { - id: 3, - changes: { id: 3, sidekickFk: 42 }, - }); - store.dispatch(action); - }); - }); - - describe('hero -> battles (1-m)', () => { - function setCollectionSelectors() { - const heroSelectors = entitySelectorsFactory.create('Hero'); - const selectHeroMap = heroSelectors.selectEntityMap; - - const battleSelectors = entitySelectorsFactory.create('Battle'); - const selectBattleEntities = battleSelectors.selectEntities; - - const selectHeroBattleMap = createSelector( - selectBattleEntities, - battles => - battles.reduce( - (acc, battle) => { - const hid = battle.heroFk; - if (hid) { - const hbs = acc[hid]; - if (hbs) { - hbs.push(battle); - } else { - acc[hid] = [battle]; - } - } - return acc; - }, - {} as { [heroId: number]: Battle[] } - ) - ); - - return { - selectHeroMap, - selectHeroBattleMap, - }; - } - - function createHeroBattlesSelector$(heroId: number): Observable { - const { selectHeroMap, selectHeroBattleMap } = setCollectionSelectors(); - - const selectHero = createSelector( - selectHeroMap, - heroes => heroes[heroId] - ); - - const selectHeroBattles = createSelector( - selectHero, - selectHeroBattleMap, - (hero, heroBattleMap) => { - const hid = hero && hero.id; - return (hid && heroBattleMap[hid]) || []; - } - ); - return store.select(selectHeroBattles); - } - - // TODO: more tests - // Note: async done() callback ensures test passes only if subscribe(successCallback()) called. - - it('should get Alpha Hero battles', (done: DoneFn) => { - createHeroBattlesSelector$(1).subscribe(battles => { - expect(battles.length).toBe(3, 'Alpha should have 3 battles'); - done(); - }); - }); - - it('should get Alpha Hero battles again after updating one of its battles', (done: DoneFn) => { - // Skip the initial sidekick and check the one after update - createHeroBattlesSelector$(1) - .pipe(skip(1)) - .subscribe(battles => { - expect(battles[0].name).toBe('Scalliwag'); - done(); - }); - - // update the first of the related battles - const action = eaFactory.create>( - 'Battle', - EntityOp.UPDATE_ONE, - { id: 100, changes: { id: 100, name: 'Scalliwag' } } - ); - store.dispatch(action); - }); - - it('Gamma Hero should have no battles', (done: DoneFn) => { - createHeroBattlesSelector$(3).subscribe(battles => { - expect(battles.length).toBe(0, 'Gamma should have no battles'); - done(); - }); - }); - }); - - describe('hero -> heropower <- power (m-m)', () => { - function setCollectionSelectors() { - const heroSelectors = entitySelectorsFactory.create('Hero'); - const selectHeroMap = heroSelectors.selectEntityMap; - - const powerSelectors = entitySelectorsFactory.create('Power'); - const selectPowerMap = powerSelectors.selectEntityMap; - - const heroPowerMapSelectors = entitySelectorsFactory.create( - 'HeroPowerMap' - ); - const selectHeroPowerMapEntities = heroPowerMapSelectors.selectEntities; - - const selectHeroPowerIds = createSelector( - selectHeroPowerMapEntities, - hpMaps => - hpMaps.reduce( - (acc, hpMap) => { - const hid = hpMap.heroFk; - if (hid) { - const hpIds = acc[hid]; - if (hpIds) { - hpIds.push(hpMap.powerFk); - } else { - acc[hid] = [hpMap.powerFk]; - } - } - return acc; - }, - {} as { [heroId: number]: number[] } - ) - ); - - return { - selectHeroMap, - selectHeroPowerIds, - selectPowerMap, - }; - } - - function createHeroPowersSelector$(heroId: number): Observable { - const { - selectHeroMap, - selectHeroPowerIds, - selectPowerMap, - } = setCollectionSelectors(); - - const selectHero = createSelector( - selectHeroMap, - heroes => heroes[heroId] - ); - - const selectHeroPowers = createSelector( - selectHero, - selectHeroPowerIds, - selectPowerMap, - (hero, heroPowerIds, powerMap) => { - const hid = hero && hero.id; - const pids = (hid && heroPowerIds[hid]) || []; - const powers = pids.map(id => powerMap[id]).filter(power => power); - return powers; - } - ); - return store.select(selectHeroPowers) as Observable; - } - - // TODO: more tests - // Note: async done() callback ensures test passes only if subscribe(successCallback()) called. - - it('should get Alpha Hero powers', (done: DoneFn) => { - createHeroPowersSelector$(1).subscribe(powers => { - expect(powers.length).toBe(3, 'Alpha should have 3 powers'); - done(); - }); - }); - - it('should get Beta Hero power', (done: DoneFn) => { - createHeroPowersSelector$(2).subscribe(powers => { - expect(powers.length).toBe(1, 'Beta should have 1 power'); - expect(powers[0].name).toBe('Invisibility'); - done(); - }); - }); - - it('Beta Hero should have no powers after delete', (done: DoneFn) => { - createHeroPowersSelector$(2) - .pipe(skip(1)) - .subscribe(powers => { - expect(powers.length).toBe(0, 'Beta should have no powers'); - done(); - }); - - // delete Beta's one power via the HeroPowerMap - const action: EntityAction = eaFactory.create( - 'HeroPowerMap', - EntityOp.REMOVE_ONE, - 96 - ); - store.dispatch(action); - }); - - it('Gamma Hero should have no powers', (done: DoneFn) => { - createHeroPowersSelector$(3).subscribe(powers => { - expect(powers.length).toBe(0, 'Gamma should have no powers'); - done(); - }); - }); - }); -}); - -// #region Test support - -interface Hero { - id: number; - name: string; - saying?: string; - sidekickFk?: number; -} - -interface Battle { - id: number; - name: string; - heroFk: number; - won: boolean; -} - -interface HeroPowerMap { - id: number; - heroFk: number; - powerFk: number; -} - -interface Power { - id: number; - name: string; -} - -interface Sidekick { - id: number; - name: string; -} - -/** Sort Comparer to sort the entity collection by its name property */ -export function sortByName(a: { name: string }, b: { name: string }): number { - return a.name.localeCompare(b.name); -} - -function initializeCache( - eaFactory: EntityActionFactory, - store: Store -) { - let action: EntityAction; - - action = eaFactory.create('Sidekick', EntityOp.ADD_ALL, [ - { id: 1, name: 'Bob' }, - { id: 2, name: 'Sally' }, - ]); - store.dispatch(action); - - action = eaFactory.create('Hero', EntityOp.ADD_ALL, [ - { id: 1, name: 'Alpha', sidekickFk: 1 }, - { id: 2, name: 'Beta', sidekickFk: 2 }, - { id: 3, name: 'Gamma' }, // no sidekick - ]); - store.dispatch(action); - - action = eaFactory.create('Battle', EntityOp.ADD_ALL, [ - { id: 100, heroFk: 1, name: 'Plains of Yon', won: true }, - { id: 200, heroFk: 1, name: 'Yippee-kai-eh', won: false }, - { id: 300, heroFk: 1, name: 'Yada Yada', won: true }, - { id: 400, heroFk: 2, name: 'Tally-hoo', won: true }, - ]); - store.dispatch(action); - - action = eaFactory.create('Power', EntityOp.ADD_ALL, [ - { id: 10, name: 'Speed' }, - { id: 20, name: 'Strength' }, - { id: 30, name: 'Invisibility' }, - ]); - store.dispatch(action); - - action = eaFactory.create('HeroPowerMap', EntityOp.ADD_ALL, [ - { id: 99, heroFk: 1, powerFk: 10 }, - { id: 98, heroFk: 1, powerFk: 20 }, - { id: 97, heroFk: 1, powerFk: 30 }, - { id: 96, heroFk: 2, powerFk: 30 }, - // Gamma has no powers - ]); - store.dispatch(action); -} -// #endregion Test support +import { TestBed } from '@angular/core/testing'; +import { createSelector, StoreModule, Store } from '@ngrx/store'; +import { Actions } from '@ngrx/effects'; +import { Update } from '@ngrx/entity'; + +import { Observable } from 'rxjs'; +import { skip } from 'rxjs/operators'; + +import { + EntityMetadataMap, + EntityActionFactory, + EntitySelectorsFactory, + EntityCache, + EntityDataModuleWithoutEffects, + ENTITY_METADATA_TOKEN, + EntityOp, + EntityAction, +} from '../../'; + +const entityMetadataMap: EntityMetadataMap = { + Battle: {}, + Hero: {}, + HeroPowerMap: {}, + Power: { + sortComparer: sortByName, + }, + Sidekick: {}, +}; + +describe('Related-entity Selectors', () => { + // #region setup + let eaFactory: EntityActionFactory; + let entitySelectorsFactory: EntitySelectorsFactory; + let store: Store; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [StoreModule.forRoot({}), EntityDataModuleWithoutEffects], + providers: [ + // required by EntityData but not used in these tests + { provide: Actions, useValue: null }, + { + provide: ENTITY_METADATA_TOKEN, + multi: true, + useValue: entityMetadataMap, + }, + ], + }); + + store = TestBed.get(Store); + eaFactory = TestBed.get(EntityActionFactory); + entitySelectorsFactory = TestBed.get(EntitySelectorsFactory); + initializeCache(eaFactory, store); + }); + + // #endregion setup + + describe('hero -> sidekick (1-1)', () => { + function setCollectionSelectors() { + const heroSelectors = entitySelectorsFactory.create('Hero'); + const selectHeroMap = heroSelectors.selectEntityMap; + + const sidekickSelectors = entitySelectorsFactory.create( + 'Sidekick' + ); + const selectSidekickMap = sidekickSelectors.selectEntityMap; + + return { + selectHeroMap, + selectSidekickMap, + }; + } + + function createHeroSidekickSelector$(heroId: number): Observable { + const { selectHeroMap, selectSidekickMap } = setCollectionSelectors(); + const selectHero = createSelector( + selectHeroMap, + heroes => heroes[heroId] + ); + const selectSideKick = createSelector( + selectHero, + selectSidekickMap, + (hero, sidekicks) => { + const sidekickId = hero && hero.sidekickFk!; + return sidekickId && sidekicks[sidekickId]; + } + ); + return store.select(selectSideKick) as Observable; + } + + // Note: async done() callback ensures test passes only if subscribe(successCallback()) called. + + it('should get Alpha Hero sidekick', (done: DoneFn) => { + createHeroSidekickSelector$(1).subscribe(sk => { + expect(sk.name).toBe('Bob'); + done(); + }); + }); + + it('should get Alpha Hero updated sidekick', (done: DoneFn) => { + // Skip the initial sidekick and check the one after update + createHeroSidekickSelector$(1) + .pipe(skip(1)) + .subscribe(sk => { + expect(sk.name).toBe('Robert'); + done(); + }); + + // update the related sidekick + const action = eaFactory.create>( + 'Sidekick', + EntityOp.UPDATE_ONE, + { id: 1, changes: { id: 1, name: 'Robert' } } + ); + store.dispatch(action); + }); + + it('should get Alpha Hero changed sidekick', (done: DoneFn) => { + // Skip the initial sidekick and check the one after update + createHeroSidekickSelector$(1) + .pipe(skip(1)) + .subscribe(sk => { + expect(sk.name).toBe('Sally'); + done(); + }); + + // update the hero's sidekick from fk=1 to fk=2 + const action = eaFactory.create>( + 'Hero', + EntityOp.UPDATE_ONE, + { id: 1, changes: { id: 1, sidekickFk: 2 } } // Sally + ); + store.dispatch(action); + }); + + it('changing a different hero should NOT trigger first hero selector', (done: DoneFn) => { + let alphaCount = 0; + + createHeroSidekickSelector$(1).subscribe(sk => { + alphaCount += 1; + }); + + // update a different hero's sidekick from fk=2 (Sally) to fk=1 (Bob) + createHeroSidekickSelector$(2) + .pipe(skip(1)) + .subscribe(sk => { + expect(sk.name).toBe('Bob'); + expect(alphaCount).toEqual( + 1, + 'should only callback for Hero #1 once' + ); + done(); + }); + + const action = eaFactory.create>( + 'Hero', + EntityOp.UPDATE_ONE, + { id: 2, changes: { id: 2, sidekickFk: 1 } } // Bob + ); + store.dispatch(action); + }); + + it('should get undefined sidekick if hero not found', (done: DoneFn) => { + createHeroSidekickSelector$(1234).subscribe(sk => { + expect(sk).toBeUndefined(); + done(); + }); + }); + + it('should get undefined sidekick from Gamma because it has no sidekickFk', (done: DoneFn) => { + createHeroSidekickSelector$(3).subscribe(sk => { + expect(sk).toBeUndefined(); + done(); + }); + }); + + it('should get Gamma sidekick after creating and assigning one', (done: DoneFn) => { + // Skip(1), the initial state in which Gamma has no sidekick + // Note that BOTH dispatches complete synchronously, before the selector updates + // so we only have to skip one. + createHeroSidekickSelector$(3) + .pipe(skip(1)) + .subscribe(sk => { + expect(sk.name).toBe('Robin'); + done(); + }); + + // create a new sidekick + let action: EntityAction = eaFactory.create( + 'Sidekick', + EntityOp.ADD_ONE, + { + id: 42, + name: 'Robin', + } + ); + store.dispatch(action); + + // assign new sidekick to Gamma + action = eaFactory.create>('Hero', EntityOp.UPDATE_ONE, { + id: 3, + changes: { id: 3, sidekickFk: 42 }, + }); + store.dispatch(action); + }); + }); + + describe('hero -> battles (1-m)', () => { + function setCollectionSelectors() { + const heroSelectors = entitySelectorsFactory.create('Hero'); + const selectHeroMap = heroSelectors.selectEntityMap; + + const battleSelectors = entitySelectorsFactory.create('Battle'); + const selectBattleEntities = battleSelectors.selectEntities; + + const selectHeroBattleMap = createSelector( + selectBattleEntities, + battles => + battles.reduce((acc, battle) => { + const hid = battle.heroFk; + if (hid) { + const hbs = acc[hid]; + if (hbs) { + hbs.push(battle); + } else { + acc[hid] = [battle]; + } + } + return acc; + }, {} as { [heroId: number]: Battle[] }) + ); + + return { + selectHeroMap, + selectHeroBattleMap, + }; + } + + function createHeroBattlesSelector$(heroId: number): Observable { + const { selectHeroMap, selectHeroBattleMap } = setCollectionSelectors(); + + const selectHero = createSelector( + selectHeroMap, + heroes => heroes[heroId] + ); + + const selectHeroBattles = createSelector( + selectHero, + selectHeroBattleMap, + (hero, heroBattleMap) => { + const hid = hero && hero.id; + return (hid && heroBattleMap[hid]) || []; + } + ); + return store.select(selectHeroBattles); + } + + // TODO: more tests + // Note: async done() callback ensures test passes only if subscribe(successCallback()) called. + + it('should get Alpha Hero battles', (done: DoneFn) => { + createHeroBattlesSelector$(1).subscribe(battles => { + expect(battles.length).toBe(3, 'Alpha should have 3 battles'); + done(); + }); + }); + + it('should get Alpha Hero battles again after updating one of its battles', (done: DoneFn) => { + // Skip the initial sidekick and check the one after update + createHeroBattlesSelector$(1) + .pipe(skip(1)) + .subscribe(battles => { + expect(battles[0].name).toBe('Scalliwag'); + done(); + }); + + // update the first of the related battles + const action = eaFactory.create>( + 'Battle', + EntityOp.UPDATE_ONE, + { id: 100, changes: { id: 100, name: 'Scalliwag' } } + ); + store.dispatch(action); + }); + + it('Gamma Hero should have no battles', (done: DoneFn) => { + createHeroBattlesSelector$(3).subscribe(battles => { + expect(battles.length).toBe(0, 'Gamma should have no battles'); + done(); + }); + }); + }); + + describe('hero -> heropower <- power (m-m)', () => { + function setCollectionSelectors() { + const heroSelectors = entitySelectorsFactory.create('Hero'); + const selectHeroMap = heroSelectors.selectEntityMap; + + const powerSelectors = entitySelectorsFactory.create('Power'); + const selectPowerMap = powerSelectors.selectEntityMap; + + const heroPowerMapSelectors = entitySelectorsFactory.create( + 'HeroPowerMap' + ); + const selectHeroPowerMapEntities = heroPowerMapSelectors.selectEntities; + + const selectHeroPowerIds = createSelector( + selectHeroPowerMapEntities, + hpMaps => + hpMaps.reduce((acc, hpMap) => { + const hid = hpMap.heroFk; + if (hid) { + const hpIds = acc[hid]; + if (hpIds) { + hpIds.push(hpMap.powerFk); + } else { + acc[hid] = [hpMap.powerFk]; + } + } + return acc; + }, {} as { [heroId: number]: number[] }) + ); + + return { + selectHeroMap, + selectHeroPowerIds, + selectPowerMap, + }; + } + + function createHeroPowersSelector$(heroId: number): Observable { + const { + selectHeroMap, + selectHeroPowerIds, + selectPowerMap, + } = setCollectionSelectors(); + + const selectHero = createSelector( + selectHeroMap, + heroes => heroes[heroId] + ); + + const selectHeroPowers = createSelector( + selectHero, + selectHeroPowerIds, + selectPowerMap, + (hero, heroPowerIds, powerMap) => { + const hid = hero && hero.id; + const pids = (hid && heroPowerIds[hid]) || []; + const powers = pids.map(id => powerMap[id]).filter(power => power); + return powers; + } + ); + return store.select(selectHeroPowers) as Observable; + } + + // TODO: more tests + // Note: async done() callback ensures test passes only if subscribe(successCallback()) called. + + it('should get Alpha Hero powers', (done: DoneFn) => { + createHeroPowersSelector$(1).subscribe(powers => { + expect(powers.length).toBe(3, 'Alpha should have 3 powers'); + done(); + }); + }); + + it('should get Beta Hero power', (done: DoneFn) => { + createHeroPowersSelector$(2).subscribe(powers => { + expect(powers.length).toBe(1, 'Beta should have 1 power'); + expect(powers[0].name).toBe('Invisibility'); + done(); + }); + }); + + it('Beta Hero should have no powers after delete', (done: DoneFn) => { + createHeroPowersSelector$(2) + .pipe(skip(1)) + .subscribe(powers => { + expect(powers.length).toBe(0, 'Beta should have no powers'); + done(); + }); + + // delete Beta's one power via the HeroPowerMap + const action: EntityAction = eaFactory.create( + 'HeroPowerMap', + EntityOp.REMOVE_ONE, + 96 + ); + store.dispatch(action); + }); + + it('Gamma Hero should have no powers', (done: DoneFn) => { + createHeroPowersSelector$(3).subscribe(powers => { + expect(powers.length).toBe(0, 'Gamma should have no powers'); + done(); + }); + }); + }); +}); + +// #region Test support + +interface Hero { + id: number; + name: string; + saying?: string; + sidekickFk?: number; +} + +interface Battle { + id: number; + name: string; + heroFk: number; + won: boolean; +} + +interface HeroPowerMap { + id: number; + heroFk: number; + powerFk: number; +} + +interface Power { + id: number; + name: string; +} + +interface Sidekick { + id: number; + name: string; +} + +/** Sort Comparer to sort the entity collection by its name property */ +export function sortByName(a: { name: string }, b: { name: string }): number { + return a.name.localeCompare(b.name); +} + +function initializeCache( + eaFactory: EntityActionFactory, + store: Store +) { + let action: EntityAction; + + action = eaFactory.create('Sidekick', EntityOp.ADD_ALL, [ + { id: 1, name: 'Bob' }, + { id: 2, name: 'Sally' }, + ]); + store.dispatch(action); + + action = eaFactory.create('Hero', EntityOp.ADD_ALL, [ + { id: 1, name: 'Alpha', sidekickFk: 1 }, + { id: 2, name: 'Beta', sidekickFk: 2 }, + { id: 3, name: 'Gamma' }, // no sidekick + ]); + store.dispatch(action); + + action = eaFactory.create('Battle', EntityOp.ADD_ALL, [ + { id: 100, heroFk: 1, name: 'Plains of Yon', won: true }, + { id: 200, heroFk: 1, name: 'Yippee-kai-eh', won: false }, + { id: 300, heroFk: 1, name: 'Yada Yada', won: true }, + { id: 400, heroFk: 2, name: 'Tally-hoo', won: true }, + ]); + store.dispatch(action); + + action = eaFactory.create('Power', EntityOp.ADD_ALL, [ + { id: 10, name: 'Speed' }, + { id: 20, name: 'Strength' }, + { id: 30, name: 'Invisibility' }, + ]); + store.dispatch(action); + + action = eaFactory.create('HeroPowerMap', EntityOp.ADD_ALL, [ + { id: 99, heroFk: 1, powerFk: 10 }, + { id: 98, heroFk: 1, powerFk: 20 }, + { id: 97, heroFk: 1, powerFk: 30 }, + { id: 96, heroFk: 2, powerFk: 30 }, + // Gamma has no powers + ]); + store.dispatch(action); +} +// #endregion Test support diff --git a/modules/data/spec/utils/default-pluralizer.spec.ts b/modules/data/spec/utils/default-pluralizer.spec.ts index 79fb1699e9..134e70e5bb 100644 --- a/modules/data/spec/utils/default-pluralizer.spec.ts +++ b/modules/data/spec/utils/default-pluralizer.spec.ts @@ -1,98 +1,98 @@ -import { TestBed } from '@angular/core/testing'; - -import { DefaultPluralizer, Pluralizer, PLURAL_NAMES_TOKEN } from '../../'; - -describe('DefaultPluralizer', () => { - describe('without plural names', () => { - let pluralizer: Pluralizer; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{ provide: Pluralizer, useClass: DefaultPluralizer }], - }); - - pluralizer = TestBed.get(Pluralizer); - }); - it('should turn "Hero" to "Heros" because no plural names map', () => { - // No map so 'Hero' gets default pluralization - expect(pluralizer.pluralize('Hero')).toBe('Heros'); - }); - - it('should pluralize "Villain" which is not in plural names', () => { - // default pluralization with 's' - expect(pluralizer.pluralize('Villain')).toBe('Villains'); - }); - - it('should pluralize "consonant + y" with "-ies"', () => { - expect(pluralizer.pluralize('Company')).toBe('Companies'); - }); - - it('should pluralize "vowel + y" with "-es"', () => { - expect(pluralizer.pluralize('Cowboy')).toBe('Cowboys'); - }); - - it('should pluralize "Information" as "Information ', () => { - // known "uncoumtables" - expect(pluralizer.pluralize('Information')).toBe('Information'); - }); - - it('should pluralize "SkyBox" which is not in plural names', () => { - // default pluralization of word ending in 'x' - expect(pluralizer.pluralize('SkyBox')).toBe('SkyBoxes'); - }); - }); - - describe('with injected plural names', () => { - let pluralizer: Pluralizer; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - // Demonstrate multi-provider - // Default would turn "Hero" into "Heros". Fix it. - { - provide: PLURAL_NAMES_TOKEN, - multi: true, - useValue: { Hero: 'Heroes' }, - }, - // "Foots" is deliberately wrong. Count on override in next provider - { - provide: PLURAL_NAMES_TOKEN, - multi: true, - useValue: { Foot: 'Foots' }, - }, - // Demonstrate overwrite of 'Foot' while setting multiple names - { - provide: PLURAL_NAMES_TOKEN, - multi: true, - useValue: { Foot: 'Feet', Person: 'People' }, - }, - { provide: Pluralizer, useClass: DefaultPluralizer }, - ], - }); - - pluralizer = TestBed.get(Pluralizer); - }); - - it('should pluralize "Villain" which is not in plural names', () => { - // default pluralization with 's' - expect(pluralizer.pluralize('Villain')).toBe('Villains'); - }); - - it('should pluralize "Hero" using plural names', () => { - expect(pluralizer.pluralize('Hero')).toBe('Heroes'); - }); - - it('should be case sensitive when using map', () => { - // uses default pluralization rule, not the names map - expect(pluralizer.pluralize('hero')).toBe('heros'); - }); - - it('should pluralize "Person" using a plural name added to the map later', () => { - expect(pluralizer.pluralize('Person')).toBe('People'); - }); - - it('later plural name map replaces earlier one', () => { - expect(pluralizer.pluralize('Foot')).toBe('Feet'); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; + +import { DefaultPluralizer, Pluralizer, PLURAL_NAMES_TOKEN } from '../../'; + +describe('DefaultPluralizer', () => { + describe('without plural names', () => { + let pluralizer: Pluralizer; + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ provide: Pluralizer, useClass: DefaultPluralizer }], + }); + + pluralizer = TestBed.get(Pluralizer); + }); + it('should turn "Hero" to "Heros" because no plural names map', () => { + // No map so 'Hero' gets default pluralization + expect(pluralizer.pluralize('Hero')).toBe('Heros'); + }); + + it('should pluralize "Villain" which is not in plural names', () => { + // default pluralization with 's' + expect(pluralizer.pluralize('Villain')).toBe('Villains'); + }); + + it('should pluralize "consonant + y" with "-ies"', () => { + expect(pluralizer.pluralize('Company')).toBe('Companies'); + }); + + it('should pluralize "vowel + y" with "-es"', () => { + expect(pluralizer.pluralize('Cowboy')).toBe('Cowboys'); + }); + + it('should pluralize "Information" as "Information ', () => { + // known "uncoumtables" + expect(pluralizer.pluralize('Information')).toBe('Information'); + }); + + it('should pluralize "SkyBox" which is not in plural names', () => { + // default pluralization of word ending in 'x' + expect(pluralizer.pluralize('SkyBox')).toBe('SkyBoxes'); + }); + }); + + describe('with injected plural names', () => { + let pluralizer: Pluralizer; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + // Demonstrate multi-provider + // Default would turn "Hero" into "Heros". Fix it. + { + provide: PLURAL_NAMES_TOKEN, + multi: true, + useValue: { Hero: 'Heroes' }, + }, + // "Foots" is deliberately wrong. Count on override in next provider + { + provide: PLURAL_NAMES_TOKEN, + multi: true, + useValue: { Foot: 'Foots' }, + }, + // Demonstrate overwrite of 'Foot' while setting multiple names + { + provide: PLURAL_NAMES_TOKEN, + multi: true, + useValue: { Foot: 'Feet', Person: 'People' }, + }, + { provide: Pluralizer, useClass: DefaultPluralizer }, + ], + }); + + pluralizer = TestBed.get(Pluralizer); + }); + + it('should pluralize "Villain" which is not in plural names', () => { + // default pluralization with 's' + expect(pluralizer.pluralize('Villain')).toBe('Villains'); + }); + + it('should pluralize "Hero" using plural names', () => { + expect(pluralizer.pluralize('Hero')).toBe('Heroes'); + }); + + it('should be case sensitive when using map', () => { + // uses default pluralization rule, not the names map + expect(pluralizer.pluralize('hero')).toBe('heros'); + }); + + it('should pluralize "Person" using a plural name added to the map later', () => { + expect(pluralizer.pluralize('Person')).toBe('People'); + }); + + it('later plural name map replaces earlier one', () => { + expect(pluralizer.pluralize('Foot')).toBe('Feet'); + }); + }); +}); diff --git a/modules/effects/schematics/ng-add/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template b/modules/effects/schematics/ng-add/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template index 9235777793..4a7089c41a 100644 --- a/modules/effects/schematics/ng-add/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template +++ b/modules/effects/schematics/ng-add/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template @@ -1,25 +1,25 @@ -import { TestBed, inject } from '@angular/core/testing'; -import { provideMockActions } from '@ngrx/effects/testing'; -import { Observable } from 'rxjs'; - -import { <%= classify(name) %>Effects } from './<%= dasherize(name) %>.effects'; - -describe('<%= classify(name) %>Effects', () => { - let actions$: Observable; - let effects: <%= classify(name) %>Effects; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - <%= classify(name) %>Effects, - provideMockActions(() => actions$) - ] - }); - - effects = TestBed.get<<%= classify(name) %>Effects>(<%= classify(name) %>Effects); - }); - - it('should be created', () => { - expect(effects).toBeTruthy(); - }); -}); +import { TestBed, inject } from '@angular/core/testing'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { Observable } from 'rxjs'; + +import { <%= classify(name) %>Effects } from './<%= dasherize(name) %>.effects'; + +describe('<%= classify(name) %>Effects', () => { + let actions$: Observable; + let effects: <%= classify(name) %>Effects; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + <%= classify(name) %>Effects, + provideMockActions(() => actions$) + ] + }); + + effects = TestBed.get<<%= classify(name) %>Effects>(<%= classify(name) %>Effects); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); +}); diff --git a/modules/effects/schematics/ng-add/index.spec.ts b/modules/effects/schematics/ng-add/index.spec.ts index cfe419b935..038c78ac9f 100644 --- a/modules/effects/schematics/ng-add/index.spec.ts +++ b/modules/effects/schematics/ng-add/index.spec.ts @@ -1,225 +1,225 @@ -import { - SchematicTestRunner, - UnitTestTree, -} from '@angular-devkit/schematics/testing'; -import { getFileContent } from '@schematics/angular/utility/test'; -import * as path from 'path'; -import { Schema as RootEffectOptions } from './schema'; -import { - getTestProjectPath, - createWorkspace, - createAppModuleWithEffects, -} from '../../../schematics-core/testing'; - -describe('Effects ng-add Schematic', () => { - const schematicRunner = new SchematicTestRunner( - '@ngrx/effects', - path.join(__dirname, '../collection.json') - ); - - const defaultOptions: RootEffectOptions = { - name: 'foo', - skipPackageJson: false, - project: 'bar', - spec: true, - module: 'app', - flat: false, - group: false, - }; - - const projectPath = getTestProjectPath(); - - let appTree: UnitTestTree; - - beforeEach(async () => { - appTree = await createWorkspace(schematicRunner, appTree); - }); - - it('should update package.json', () => { - const options = { ...defaultOptions }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const packageJson = JSON.parse(tree.readContent('/package.json')); - - expect(packageJson.dependencies['@ngrx/effects']).toBeDefined(); - }); - - it('should skip package.json update', () => { - const options = { ...defaultOptions, skipPackageJson: true }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const packageJson = JSON.parse(tree.readContent('/package.json')); - - expect(packageJson.dependencies['@ngrx/effects']).toBeUndefined(); - }); - - it('should create an effect', () => { - const options = { ...defaultOptions }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const files = tree.files; - expect( - files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`) - ).toBeGreaterThanOrEqual(0); - expect( - files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`) - ).toBeGreaterThanOrEqual(0); - }); - - it('should not create an effect if the minimal flag is provided', () => { - const options = { ...defaultOptions, minimal: true }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const files = tree.files; - const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); - - expect(content).toMatch(/EffectsModule\.forRoot\(\[\]\)/); - expect( - files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`) - ).toBe(-1); - expect(files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`)).toBe(-1); - }); - - it('should not import an effect into a specified module in the minimal flag is provided', () => { - const options = { - ...defaultOptions, - minimal: true, - module: 'app.module.ts', - }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); - expect(content).not.toMatch( - /import { FooEffects } from '.\/foo\/foo.effects'/ - ); - }); - - it('should be provided by default', () => { - const options = { ...defaultOptions }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); - expect(content).toMatch(/import { FooEffects } from '.\/foo\/foo.effects'/); - }); - - it('should import into a specified module', () => { - const options = { ...defaultOptions, module: 'app.module.ts' }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); - expect(content).toMatch(/import { FooEffects } from '.\/foo\/foo.effects'/); - }); - - it('should fail if specified module does not exist', () => { - const options = { - ...defaultOptions, - module: `${projectPath}/src/app/app.moduleXXX.ts`, - }; - let thrownError: Error | null = null; - try { - schematicRunner.runSchematic('effects', options, appTree); - } catch (err) { - thrownError = err; - } - expect(thrownError).toBeDefined(); - }); - - it('should respect the spec flag', () => { - const options = { ...defaultOptions, spec: false }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const files = tree.files; - expect( - files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`) - ).toBeGreaterThanOrEqual(0); - expect( - files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`) - ).toEqual(-1); - }); - - it('should register the root effect in the provided module', () => { - const options = { ...defaultOptions }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); - - expect(content).toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/); - }); - - it('should add an effect to the empty array of registered effects', () => { - const storeModule = `${projectPath}/src/app/store.module.ts`; - const options = { - ...defaultOptions, - module: 'store.module.ts', - }; - appTree = createAppModuleWithEffects( - appTree, - storeModule, - 'EffectsModule.forRoot([])' - ); - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const content = tree.readContent(storeModule); - - expect(content).toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/); - }); - - it('should add an effect to the existing registered root effects', () => { - const storeModule = `${projectPath}/src/app/store.module.ts`; - const options = { - ...defaultOptions, - module: 'store.module.ts', - }; - appTree = createAppModuleWithEffects( - appTree, - storeModule, - 'EffectsModule.forRoot([UserEffects])' - ); - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const content = tree.readContent(storeModule); - - expect(content).toMatch( - /EffectsModule\.forRoot\(\[UserEffects, FooEffects\]\)/ - ); - }); - - it('should not add an effect to registered effects defined with a variable', () => { - const storeModule = `${projectPath}/src/app/store.module.ts`; - const options = { ...defaultOptions, module: 'store.module.ts' }; - appTree = createAppModuleWithEffects( - appTree, - storeModule, - 'EffectsModule.forRoot(effects)' - ); - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const content = tree.readContent(storeModule); - - expect(content).not.toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/); - }); - - it('should group within an "effects" folder if group is set', () => { - const options = { ...defaultOptions, flat: true, spec: false, group: true }; - - const tree = schematicRunner.runSchematic('ng-add', options, appTree); - const files = tree.files; - expect( - files.indexOf(`${projectPath}/src/app/effects/foo.effects.ts`) - ).toBeGreaterThanOrEqual(0); - }); - - it('should inject the effect service correctly', async () => { - const options = { ...defaultOptions, spec: true }; - const tree = await schematicRunner - .runSchematicAsync('ng-add', options, appTree) - .toPromise(); - const content = tree.readContent( - `${projectPath}/src/app/foo/foo.effects.spec.ts` - ); - - expect(content).toMatch( - /effects = TestBed\.get\(FooEffects\);/ - ); - }); -}); +import { + SchematicTestRunner, + UnitTestTree, +} from '@angular-devkit/schematics/testing'; +import { getFileContent } from '@schematics/angular/utility/test'; +import * as path from 'path'; +import { Schema as RootEffectOptions } from './schema'; +import { + getTestProjectPath, + createWorkspace, + createAppModuleWithEffects, +} from '../../../schematics-core/testing'; + +describe('Effects ng-add Schematic', () => { + const schematicRunner = new SchematicTestRunner( + '@ngrx/effects', + path.join(__dirname, '../collection.json') + ); + + const defaultOptions: RootEffectOptions = { + name: 'foo', + skipPackageJson: false, + project: 'bar', + spec: true, + module: 'app', + flat: false, + group: false, + }; + + const projectPath = getTestProjectPath(); + + let appTree: UnitTestTree; + + beforeEach(async () => { + appTree = await createWorkspace(schematicRunner, appTree); + }); + + it('should update package.json', () => { + const options = { ...defaultOptions }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const packageJson = JSON.parse(tree.readContent('/package.json')); + + expect(packageJson.dependencies['@ngrx/effects']).toBeDefined(); + }); + + it('should skip package.json update', () => { + const options = { ...defaultOptions, skipPackageJson: true }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const packageJson = JSON.parse(tree.readContent('/package.json')); + + expect(packageJson.dependencies['@ngrx/effects']).toBeUndefined(); + }); + + it('should create an effect', () => { + const options = { ...defaultOptions }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const files = tree.files; + expect( + files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`) + ).toBeGreaterThanOrEqual(0); + expect( + files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`) + ).toBeGreaterThanOrEqual(0); + }); + + it('should not create an effect if the minimal flag is provided', () => { + const options = { ...defaultOptions, minimal: true }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const files = tree.files; + const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); + + expect(content).toMatch(/EffectsModule\.forRoot\(\[\]\)/); + expect( + files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`) + ).toBe(-1); + expect(files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`)).toBe(-1); + }); + + it('should not import an effect into a specified module in the minimal flag is provided', () => { + const options = { + ...defaultOptions, + minimal: true, + module: 'app.module.ts', + }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); + expect(content).not.toMatch( + /import { FooEffects } from '.\/foo\/foo.effects'/ + ); + }); + + it('should be provided by default', () => { + const options = { ...defaultOptions }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); + expect(content).toMatch(/import { FooEffects } from '.\/foo\/foo.effects'/); + }); + + it('should import into a specified module', () => { + const options = { ...defaultOptions, module: 'app.module.ts' }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); + expect(content).toMatch(/import { FooEffects } from '.\/foo\/foo.effects'/); + }); + + it('should fail if specified module does not exist', () => { + const options = { + ...defaultOptions, + module: `${projectPath}/src/app/app.moduleXXX.ts`, + }; + let thrownError: Error | null = null; + try { + schematicRunner.runSchematic('effects', options, appTree); + } catch (err) { + thrownError = err; + } + expect(thrownError).toBeDefined(); + }); + + it('should respect the spec flag', () => { + const options = { ...defaultOptions, spec: false }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const files = tree.files; + expect( + files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`) + ).toBeGreaterThanOrEqual(0); + expect( + files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`) + ).toEqual(-1); + }); + + it('should register the root effect in the provided module', () => { + const options = { ...defaultOptions }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const content = tree.readContent(`${projectPath}/src/app/app.module.ts`); + + expect(content).toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/); + }); + + it('should add an effect to the empty array of registered effects', () => { + const storeModule = `${projectPath}/src/app/store.module.ts`; + const options = { + ...defaultOptions, + module: 'store.module.ts', + }; + appTree = createAppModuleWithEffects( + appTree, + storeModule, + 'EffectsModule.forRoot([])' + ); + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const content = tree.readContent(storeModule); + + expect(content).toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/); + }); + + it('should add an effect to the existing registered root effects', () => { + const storeModule = `${projectPath}/src/app/store.module.ts`; + const options = { + ...defaultOptions, + module: 'store.module.ts', + }; + appTree = createAppModuleWithEffects( + appTree, + storeModule, + 'EffectsModule.forRoot([UserEffects])' + ); + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const content = tree.readContent(storeModule); + + expect(content).toMatch( + /EffectsModule\.forRoot\(\[UserEffects, FooEffects\]\)/ + ); + }); + + it('should not add an effect to registered effects defined with a variable', () => { + const storeModule = `${projectPath}/src/app/store.module.ts`; + const options = { ...defaultOptions, module: 'store.module.ts' }; + appTree = createAppModuleWithEffects( + appTree, + storeModule, + 'EffectsModule.forRoot(effects)' + ); + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const content = tree.readContent(storeModule); + + expect(content).not.toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/); + }); + + it('should group within an "effects" folder if group is set', () => { + const options = { ...defaultOptions, flat: true, spec: false, group: true }; + + const tree = schematicRunner.runSchematic('ng-add', options, appTree); + const files = tree.files; + expect( + files.indexOf(`${projectPath}/src/app/effects/foo.effects.ts`) + ).toBeGreaterThanOrEqual(0); + }); + + it('should inject the effect service correctly', async () => { + const options = { ...defaultOptions, spec: true }; + const tree = await schematicRunner + .runSchematicAsync('ng-add', options, appTree) + .toPromise(); + const content = tree.readContent( + `${projectPath}/src/app/foo/foo.effects.spec.ts` + ); + + expect(content).toMatch( + /effects = TestBed\.get\(FooEffects\);/ + ); + }); +}); diff --git a/modules/effects/spec/effect_sources.spec.ts b/modules/effects/spec/effect_sources.spec.ts index f50797b1f0..b4117355be 100644 --- a/modules/effects/spec/effect_sources.spec.ts +++ b/modules/effects/spec/effect_sources.spec.ts @@ -1,609 +1,609 @@ -import { ErrorHandler } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { cold, hot, getTestScheduler } from 'jasmine-marbles'; -import { concat, NEVER, Observable, of, throwError, timer } from 'rxjs'; -import { concatMap, map } from 'rxjs/operators'; - -import { - Effect, - EffectSources, - OnIdentifyEffects, - OnInitEffects, - createEffect, -} from '../'; -import { Store } from '@ngrx/store'; - -describe('EffectSources', () => { - let mockErrorReporter: ErrorHandler; - let effectSources: EffectSources; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - EffectSources, - { - provide: Store, - useValue: { - dispatch: jasmine.createSpy('dispatch'), - }, - }, - ], - }); - - mockErrorReporter = TestBed.get(ErrorHandler); - effectSources = TestBed.get(EffectSources); - - spyOn(mockErrorReporter, 'handleError'); - }); - - it('should have an "addEffects" method to push new source instances', () => { - const effectSource = {}; - spyOn(effectSources, 'next'); - - effectSources.addEffects(effectSource); - - expect(effectSources.next).toHaveBeenCalledWith(effectSource); - }); - - it('should dispatch an action on ngrxOnInitEffects after being registered', () => { - class EffectWithInitAction implements OnInitEffects { - ngrxOnInitEffects() { - return { type: '[EffectWithInitAction] Init' }; - } - } - - effectSources.addEffects(new EffectWithInitAction()); - - const store = TestBed.get(Store); - expect(store.dispatch).toHaveBeenCalledWith({ - type: '[EffectWithInitAction] Init', - }); - }); - - describe('toActions() Operator', () => { - describe('with @Effect()', () => { - const a = { type: 'From Source A' }; - const b = { type: 'From Source B' }; - const c = { type: 'From Source C that completes' }; - const d = { not: 'a valid action' }; - const e = undefined; - const f = null; - const i = { type: 'From Source Identifier' }; - const i2 = { type: 'From Source Identifier 2' }; - - let circularRef = {} as any; - circularRef.circularRef = circularRef; - const g = { circularRef }; - - const error = new Error('An Error'); - - class SourceA { - @Effect() a$ = alwaysOf(a); - } - - class SourceB { - @Effect() b$ = alwaysOf(b); - } - - class SourceC { - @Effect() c$ = of(c); - } - - class SourceD { - @Effect() d$ = alwaysOf(d); - } - - class SourceE { - @Effect() e$ = alwaysOf(e); - } - - class SourceF { - @Effect() f$ = alwaysOf(f); - } - - class SourceG { - @Effect() g$ = alwaysOf(g); - } - - class SourceError { - @Effect() e$ = throwError(error); - } - - class SourceH { - @Effect() empty = of('value'); - @Effect() - never = timer(50, getTestScheduler() as any).pipe(map(() => 'update')); - } - - class SourceWithIdentifier implements OnIdentifyEffects { - effectIdentifier: string; - @Effect() i$ = alwaysOf(i); - - ngrxOnIdentifyEffects() { - return this.effectIdentifier; - } - - constructor(identifier: string) { - this.effectIdentifier = identifier; - } - } - - class SourceWithIdentifier2 implements OnIdentifyEffects { - effectIdentifier: string; - @Effect() i2$ = alwaysOf(i2); - - ngrxOnIdentifyEffects() { - return this.effectIdentifier; - } - - constructor(identifier: string) { - this.effectIdentifier = identifier; - } - } - - it('should resolve effects from instances', () => { - const sources$ = cold('--a--', { a: new SourceA() }); - const expected = cold('--a--', { a }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should ignore duplicate sources', () => { - const sources$ = cold('--a--a--a--', { - a: new SourceA(), - }); - const expected = cold('--a--------', { a }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should resolve effects with different identifiers', () => { - const sources$ = cold('--a--b--c--', { - a: new SourceWithIdentifier('a'), - b: new SourceWithIdentifier('b'), - c: new SourceWithIdentifier('c'), - }); - const expected = cold('--i--i--i--', { i }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should ignore effects with the same identifier', () => { - const sources$ = cold('--a--b--c--', { - a: new SourceWithIdentifier('a'), - b: new SourceWithIdentifier('a'), - c: new SourceWithIdentifier('a'), - }); - const expected = cold('--i--------', { i }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should resolve effects with same identifiers but different classes', () => { - const sources$ = cold('--a--b--c--d--', { - a: new SourceWithIdentifier('a'), - b: new SourceWithIdentifier2('a'), - c: new SourceWithIdentifier('b'), - d: new SourceWithIdentifier2('b'), - }); - const expected = cold('--a--b--a--b--', { a: i, b: i2 }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should report an error if an effect dispatches an invalid action', () => { - const sources$ = of(new SourceD()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error( - 'Effect "SourceD.d$" dispatched an invalid action: {"not":"a valid action"}' - ) - ); - }); - - it('should report an error if an effect dispatches an `undefined`', () => { - const sources$ = of(new SourceE()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error( - 'Effect "SourceE.e$" dispatched an invalid action: undefined' - ) - ); - }); - - it('should report an error if an effect dispatches a `null`', () => { - const sources$ = of(new SourceF()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error('Effect "SourceF.f$" dispatched an invalid action: null') - ); - }); - - it('should report an error if an effect throws one', () => { - const sources$ = of(new SourceError()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error('An Error') - ); - }); - - it('should resubscribe on error by default', () => { - class Eff { - @Effect() - b$ = hot('a--e--b--e--c--e--d').pipe( - map(v => { - if (v == 'e') throw new Error('An Error'); - return v; - }) - ); - } - - const sources$ = of(new Eff()); - - // 👇 'e' is ignored. - const expected = cold('a-----b-----c-----d'); - expect(toActions(sources$)).toBeObservable(expected); - }); - - it('should not resubscribe on error when resubscribeOnError is false', () => { - class Eff { - @Effect({ resubscribeOnError: false }) - b$ = hot('a--b--c--d').pipe( - map(v => { - if (v == 'b') throw new Error('An Error'); - return v; - }) - ); - } - - const sources$ = of(new Eff()); - - // 👇 completes. - const expected = cold('a--|'); - - expect(toActions(sources$)).toBeObservable(expected); - }); - - it(`should not break when the action in the error message can't be stringified`, () => { - const sources$ = of(new SourceG()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error( - 'Effect "SourceG.g$" dispatched an invalid action: [object Object]' - ) - ); - }); - - it('should not complete the group if just one effect completes', () => { - const sources$ = cold('g', { - g: new SourceH(), - }); - const expected = cold('a----b-----', { a: 'value', b: 'update' }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - function toActions(source: any): Observable { - source['errorHandler'] = mockErrorReporter; - return (effectSources as any)['toActions'].call(source); - } - }); - - describe('with createEffect()', () => { - const a = { type: 'From Source A' }; - const b = { type: 'From Source B' }; - const c = { type: 'From Source C that completes' }; - const d = { not: 'a valid action' }; - const e = undefined; - const f = null; - const i = { type: 'From Source Identifier' }; - const i2 = { type: 'From Source Identifier 2' }; - - let circularRef = {} as any; - circularRef.circularRef = circularRef; - const g = { circularRef }; - - const error = new Error('An Error'); - - class SourceA { - a$ = createEffect(() => alwaysOf(a)); - } - - class SourceB { - b$ = createEffect(() => alwaysOf(b)); - } - - class SourceC { - c$ = createEffect(() => of(c)); - } - - class SourceD { - // typed as `any` because otherwise there would be compile errors - // createEffect is typed that it always has to return an action - d$ = createEffect(() => alwaysOf(d) as any); - } - - class SourceE { - // typed as `any` because otherwise there would be compile errors - // createEffect is typed that it always has to return an action - e$ = createEffect(() => alwaysOf(e) as any); - } - - class SourceF { - // typed as `any` because otherwise there would be compile errors - // createEffect is typed that it always has to return an action - f$ = createEffect(() => alwaysOf(f) as any); - } - - class SourceG { - // typed as `any` because otherwise there would be compile errors - // createEffect is typed that it always has to return an action - g$ = createEffect(() => alwaysOf(g) as any); - } - - class SourceError { - e$ = createEffect(() => throwError(error)); - } - - class SourceH { - // typed as `any` because otherwise there would be compile errors - // createEffect is typed that it always has to return an action - empty = createEffect(() => of('value') as any); - never = createEffect( - () => - // typed as `any` because otherwise there would be compile errors - // createEffect is typed that it always has to return an action - timer(50, getTestScheduler() as any).pipe( - map(() => 'update') - ) as any - ); - } - - class SourceWithIdentifier implements OnIdentifyEffects { - effectIdentifier: string; - i$ = createEffect(() => alwaysOf(i)); - - ngrxOnIdentifyEffects() { - return this.effectIdentifier; - } - - constructor(identifier: string) { - this.effectIdentifier = identifier; - } - } - - class SourceWithIdentifier2 implements OnIdentifyEffects { - effectIdentifier: string; - i2$ = createEffect(() => alwaysOf(i2)); - - ngrxOnIdentifyEffects() { - return this.effectIdentifier; - } - - constructor(identifier: string) { - this.effectIdentifier = identifier; - } - } - - it('should resolve effects from instances', () => { - const sources$ = cold('--a--', { a: new SourceA() }); - const expected = cold('--a--', { a }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should ignore duplicate sources', () => { - const sources$ = cold('--a--a--a--', { - a: new SourceA(), - }); - const expected = cold('--a--------', { a }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should resolve effects with different identifiers', () => { - const sources$ = cold('--a--b--c--', { - a: new SourceWithIdentifier('a'), - b: new SourceWithIdentifier('b'), - c: new SourceWithIdentifier('c'), - }); - const expected = cold('--i--i--i--', { i }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should ignore effects with the same identifier', () => { - const sources$ = cold('--a--b--c--', { - a: new SourceWithIdentifier('a'), - b: new SourceWithIdentifier('a'), - c: new SourceWithIdentifier('a'), - }); - const expected = cold('--i--------', { i }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should resolve effects with same identifiers but different classes', () => { - const sources$ = cold('--a--b--c--d--', { - a: new SourceWithIdentifier('a'), - b: new SourceWithIdentifier2('a'), - c: new SourceWithIdentifier('b'), - d: new SourceWithIdentifier2('b'), - }); - const expected = cold('--a--b--a--b--', { a: i, b: i2 }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - it('should report an error if an effect dispatches an invalid action', () => { - const sources$ = of(new SourceD()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error( - 'Effect "SourceD.d$" dispatched an invalid action: {"not":"a valid action"}' - ) - ); - }); - - it('should report an error if an effect dispatches an `undefined`', () => { - const sources$ = of(new SourceE()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error( - 'Effect "SourceE.e$" dispatched an invalid action: undefined' - ) - ); - }); - - it('should report an error if an effect dispatches a `null`', () => { - const sources$ = of(new SourceF()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error('Effect "SourceF.f$" dispatched an invalid action: null') - ); - }); - - it('should report an error if an effect throws one', () => { - const sources$ = of(new SourceError()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error('An Error') - ); - }); - - it('should resubscribe on error by default', () => { - const sources$ = of( - new class { - b$ = createEffect(() => - hot('a--e--b--e--c--e--d').pipe( - map(v => { - if (v == 'e') throw new Error('An Error'); - return v; - }) - ) - ); - }() - ); - - // 👇 'e' is ignored. - const expected = cold('a-----b-----c-----d'); - - expect(toActions(sources$)).toBeObservable(expected); - }); - - it('should resubscribe on error by default when dispatch is false', () => { - const sources$ = of( - new class { - b$ = createEffect( - () => - hot('a--b--c--d').pipe( - map(v => { - if (v == 'b') throw new Error('An Error'); - return v; - }) - ), - { dispatch: false } - ); - }() - ); - // 👇 doesn't complete and doesn't dispatch - const expected = cold('----------'); - - expect(toActions(sources$)).toBeObservable(expected); - }); - - it('should not resubscribe on error when resubscribeOnError is false', () => { - const sources$ = of( - new class { - b$ = createEffect( - () => - hot('a--b--c--d').pipe( - map(v => { - if (v == 'b') throw new Error('An Error'); - return v; - }) - ), - { dispatch: false, resubscribeOnError: false } - ); - }() - ); - // 👇 errors with dispatch false - const expected = cold('---#', undefined, new Error('An Error')); - - expect(toActions(sources$)).toBeObservable(expected); - }); - - it(`should not break when the action in the error message can't be stringified`, () => { - const sources$ = of(new SourceG()); - - toActions(sources$).subscribe(); - - expect(mockErrorReporter.handleError).toHaveBeenCalledWith( - new Error( - 'Effect "SourceG.g$" dispatched an invalid action: [object Object]' - ) - ); - }); - - it('should not complete the group if just one effect completes', () => { - const sources$ = cold('g', { - g: new SourceH(), - }); - const expected = cold('a----b-----', { a: 'value', b: 'update' }); - - const output = toActions(sources$); - - expect(output).toBeObservable(expected); - }); - - function toActions(source: any): Observable { - source['errorHandler'] = mockErrorReporter; - return (effectSources as any)['toActions'].call(source); - } - }); - }); - - function alwaysOf(value: T) { - return concat(of(value), NEVER); - } -}); +import { ErrorHandler } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { cold, hot, getTestScheduler } from 'jasmine-marbles'; +import { concat, NEVER, Observable, of, throwError, timer } from 'rxjs'; +import { concatMap, map } from 'rxjs/operators'; + +import { + Effect, + EffectSources, + OnIdentifyEffects, + OnInitEffects, + createEffect, +} from '../'; +import { Store } from '@ngrx/store'; + +describe('EffectSources', () => { + let mockErrorReporter: ErrorHandler; + let effectSources: EffectSources; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + EffectSources, + { + provide: Store, + useValue: { + dispatch: jasmine.createSpy('dispatch'), + }, + }, + ], + }); + + mockErrorReporter = TestBed.get(ErrorHandler); + effectSources = TestBed.get(EffectSources); + + spyOn(mockErrorReporter, 'handleError'); + }); + + it('should have an "addEffects" method to push new source instances', () => { + const effectSource = {}; + spyOn(effectSources, 'next'); + + effectSources.addEffects(effectSource); + + expect(effectSources.next).toHaveBeenCalledWith(effectSource); + }); + + it('should dispatch an action on ngrxOnInitEffects after being registered', () => { + class EffectWithInitAction implements OnInitEffects { + ngrxOnInitEffects() { + return { type: '[EffectWithInitAction] Init' }; + } + } + + effectSources.addEffects(new EffectWithInitAction()); + + const store = TestBed.get(Store); + expect(store.dispatch).toHaveBeenCalledWith({ + type: '[EffectWithInitAction] Init', + }); + }); + + describe('toActions() Operator', () => { + describe('with @Effect()', () => { + const a = { type: 'From Source A' }; + const b = { type: 'From Source B' }; + const c = { type: 'From Source C that completes' }; + const d = { not: 'a valid action' }; + const e = undefined; + const f = null; + const i = { type: 'From Source Identifier' }; + const i2 = { type: 'From Source Identifier 2' }; + + let circularRef = {} as any; + circularRef.circularRef = circularRef; + const g = { circularRef }; + + const error = new Error('An Error'); + + class SourceA { + @Effect() a$ = alwaysOf(a); + } + + class SourceB { + @Effect() b$ = alwaysOf(b); + } + + class SourceC { + @Effect() c$ = of(c); + } + + class SourceD { + @Effect() d$ = alwaysOf(d); + } + + class SourceE { + @Effect() e$ = alwaysOf(e); + } + + class SourceF { + @Effect() f$ = alwaysOf(f); + } + + class SourceG { + @Effect() g$ = alwaysOf(g); + } + + class SourceError { + @Effect() e$ = throwError(error); + } + + class SourceH { + @Effect() empty = of('value'); + @Effect() + never = timer(50, getTestScheduler() as any).pipe(map(() => 'update')); + } + + class SourceWithIdentifier implements OnIdentifyEffects { + effectIdentifier: string; + @Effect() i$ = alwaysOf(i); + + ngrxOnIdentifyEffects() { + return this.effectIdentifier; + } + + constructor(identifier: string) { + this.effectIdentifier = identifier; + } + } + + class SourceWithIdentifier2 implements OnIdentifyEffects { + effectIdentifier: string; + @Effect() i2$ = alwaysOf(i2); + + ngrxOnIdentifyEffects() { + return this.effectIdentifier; + } + + constructor(identifier: string) { + this.effectIdentifier = identifier; + } + } + + it('should resolve effects from instances', () => { + const sources$ = cold('--a--', { a: new SourceA() }); + const expected = cold('--a--', { a }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should ignore duplicate sources', () => { + const sources$ = cold('--a--a--a--', { + a: new SourceA(), + }); + const expected = cold('--a--------', { a }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should resolve effects with different identifiers', () => { + const sources$ = cold('--a--b--c--', { + a: new SourceWithIdentifier('a'), + b: new SourceWithIdentifier('b'), + c: new SourceWithIdentifier('c'), + }); + const expected = cold('--i--i--i--', { i }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should ignore effects with the same identifier', () => { + const sources$ = cold('--a--b--c--', { + a: new SourceWithIdentifier('a'), + b: new SourceWithIdentifier('a'), + c: new SourceWithIdentifier('a'), + }); + const expected = cold('--i--------', { i }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should resolve effects with same identifiers but different classes', () => { + const sources$ = cold('--a--b--c--d--', { + a: new SourceWithIdentifier('a'), + b: new SourceWithIdentifier2('a'), + c: new SourceWithIdentifier('b'), + d: new SourceWithIdentifier2('b'), + }); + const expected = cold('--a--b--a--b--', { a: i, b: i2 }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should report an error if an effect dispatches an invalid action', () => { + const sources$ = of(new SourceD()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error( + 'Effect "SourceD.d$" dispatched an invalid action: {"not":"a valid action"}' + ) + ); + }); + + it('should report an error if an effect dispatches an `undefined`', () => { + const sources$ = of(new SourceE()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error( + 'Effect "SourceE.e$" dispatched an invalid action: undefined' + ) + ); + }); + + it('should report an error if an effect dispatches a `null`', () => { + const sources$ = of(new SourceF()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error('Effect "SourceF.f$" dispatched an invalid action: null') + ); + }); + + it('should report an error if an effect throws one', () => { + const sources$ = of(new SourceError()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error('An Error') + ); + }); + + it('should resubscribe on error by default', () => { + class Eff { + @Effect() + b$ = hot('a--e--b--e--c--e--d').pipe( + map(v => { + if (v == 'e') throw new Error('An Error'); + return v; + }) + ); + } + + const sources$ = of(new Eff()); + + // 👇 'e' is ignored. + const expected = cold('a-----b-----c-----d'); + expect(toActions(sources$)).toBeObservable(expected); + }); + + it('should not resubscribe on error when resubscribeOnError is false', () => { + class Eff { + @Effect({ resubscribeOnError: false }) + b$ = hot('a--b--c--d').pipe( + map(v => { + if (v == 'b') throw new Error('An Error'); + return v; + }) + ); + } + + const sources$ = of(new Eff()); + + // 👇 completes. + const expected = cold('a--|'); + + expect(toActions(sources$)).toBeObservable(expected); + }); + + it(`should not break when the action in the error message can't be stringified`, () => { + const sources$ = of(new SourceG()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error( + 'Effect "SourceG.g$" dispatched an invalid action: [object Object]' + ) + ); + }); + + it('should not complete the group if just one effect completes', () => { + const sources$ = cold('g', { + g: new SourceH(), + }); + const expected = cold('a----b-----', { a: 'value', b: 'update' }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + function toActions(source: any): Observable { + source['errorHandler'] = mockErrorReporter; + return (effectSources as any)['toActions'].call(source); + } + }); + + describe('with createEffect()', () => { + const a = { type: 'From Source A' }; + const b = { type: 'From Source B' }; + const c = { type: 'From Source C that completes' }; + const d = { not: 'a valid action' }; + const e = undefined; + const f = null; + const i = { type: 'From Source Identifier' }; + const i2 = { type: 'From Source Identifier 2' }; + + let circularRef = {} as any; + circularRef.circularRef = circularRef; + const g = { circularRef }; + + const error = new Error('An Error'); + + class SourceA { + a$ = createEffect(() => alwaysOf(a)); + } + + class SourceB { + b$ = createEffect(() => alwaysOf(b)); + } + + class SourceC { + c$ = createEffect(() => of(c)); + } + + class SourceD { + // typed as `any` because otherwise there would be compile errors + // createEffect is typed that it always has to return an action + d$ = createEffect(() => alwaysOf(d) as any); + } + + class SourceE { + // typed as `any` because otherwise there would be compile errors + // createEffect is typed that it always has to return an action + e$ = createEffect(() => alwaysOf(e) as any); + } + + class SourceF { + // typed as `any` because otherwise there would be compile errors + // createEffect is typed that it always has to return an action + f$ = createEffect(() => alwaysOf(f) as any); + } + + class SourceG { + // typed as `any` because otherwise there would be compile errors + // createEffect is typed that it always has to return an action + g$ = createEffect(() => alwaysOf(g) as any); + } + + class SourceError { + e$ = createEffect(() => throwError(error)); + } + + class SourceH { + // typed as `any` because otherwise there would be compile errors + // createEffect is typed that it always has to return an action + empty = createEffect(() => of('value') as any); + never = createEffect( + () => + // typed as `any` because otherwise there would be compile errors + // createEffect is typed that it always has to return an action + timer(50, getTestScheduler() as any).pipe( + map(() => 'update') + ) as any + ); + } + + class SourceWithIdentifier implements OnIdentifyEffects { + effectIdentifier: string; + i$ = createEffect(() => alwaysOf(i)); + + ngrxOnIdentifyEffects() { + return this.effectIdentifier; + } + + constructor(identifier: string) { + this.effectIdentifier = identifier; + } + } + + class SourceWithIdentifier2 implements OnIdentifyEffects { + effectIdentifier: string; + i2$ = createEffect(() => alwaysOf(i2)); + + ngrxOnIdentifyEffects() { + return this.effectIdentifier; + } + + constructor(identifier: string) { + this.effectIdentifier = identifier; + } + } + + it('should resolve effects from instances', () => { + const sources$ = cold('--a--', { a: new SourceA() }); + const expected = cold('--a--', { a }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should ignore duplicate sources', () => { + const sources$ = cold('--a--a--a--', { + a: new SourceA(), + }); + const expected = cold('--a--------', { a }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should resolve effects with different identifiers', () => { + const sources$ = cold('--a--b--c--', { + a: new SourceWithIdentifier('a'), + b: new SourceWithIdentifier('b'), + c: new SourceWithIdentifier('c'), + }); + const expected = cold('--i--i--i--', { i }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should ignore effects with the same identifier', () => { + const sources$ = cold('--a--b--c--', { + a: new SourceWithIdentifier('a'), + b: new SourceWithIdentifier('a'), + c: new SourceWithIdentifier('a'), + }); + const expected = cold('--i--------', { i }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should resolve effects with same identifiers but different classes', () => { + const sources$ = cold('--a--b--c--d--', { + a: new SourceWithIdentifier('a'), + b: new SourceWithIdentifier2('a'), + c: new SourceWithIdentifier('b'), + d: new SourceWithIdentifier2('b'), + }); + const expected = cold('--a--b--a--b--', { a: i, b: i2 }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + it('should report an error if an effect dispatches an invalid action', () => { + const sources$ = of(new SourceD()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error( + 'Effect "SourceD.d$" dispatched an invalid action: {"not":"a valid action"}' + ) + ); + }); + + it('should report an error if an effect dispatches an `undefined`', () => { + const sources$ = of(new SourceE()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error( + 'Effect "SourceE.e$" dispatched an invalid action: undefined' + ) + ); + }); + + it('should report an error if an effect dispatches a `null`', () => { + const sources$ = of(new SourceF()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error('Effect "SourceF.f$" dispatched an invalid action: null') + ); + }); + + it('should report an error if an effect throws one', () => { + const sources$ = of(new SourceError()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error('An Error') + ); + }); + + it('should resubscribe on error by default', () => { + const sources$ = of( + new (class { + b$ = createEffect(() => + hot('a--e--b--e--c--e--d').pipe( + map(v => { + if (v == 'e') throw new Error('An Error'); + return v; + }) + ) + ); + })() + ); + + // 👇 'e' is ignored. + const expected = cold('a-----b-----c-----d'); + + expect(toActions(sources$)).toBeObservable(expected); + }); + + it('should resubscribe on error by default when dispatch is false', () => { + const sources$ = of( + new (class { + b$ = createEffect( + () => + hot('a--b--c--d').pipe( + map(v => { + if (v == 'b') throw new Error('An Error'); + return v; + }) + ), + { dispatch: false } + ); + })() + ); + // 👇 doesn't complete and doesn't dispatch + const expected = cold('----------'); + + expect(toActions(sources$)).toBeObservable(expected); + }); + + it('should not resubscribe on error when resubscribeOnError is false', () => { + const sources$ = of( + new (class { + b$ = createEffect( + () => + hot('a--b--c--d').pipe( + map(v => { + if (v == 'b') throw new Error('An Error'); + return v; + }) + ), + { dispatch: false, resubscribeOnError: false } + ); + })() + ); + // 👇 errors with dispatch false + const expected = cold('---#', undefined, new Error('An Error')); + + expect(toActions(sources$)).toBeObservable(expected); + }); + + it(`should not break when the action in the error message can't be stringified`, () => { + const sources$ = of(new SourceG()); + + toActions(sources$).subscribe(); + + expect(mockErrorReporter.handleError).toHaveBeenCalledWith( + new Error( + 'Effect "SourceG.g$" dispatched an invalid action: [object Object]' + ) + ); + }); + + it('should not complete the group if just one effect completes', () => { + const sources$ = cold('g', { + g: new SourceH(), + }); + const expected = cold('a----b-----', { a: 'value', b: 'update' }); + + const output = toActions(sources$); + + expect(output).toBeObservable(expected); + }); + + function toActions(source: any): Observable { + source['errorHandler'] = mockErrorReporter; + return (effectSources as any)['toActions'].call(source); + } + }); + }); + + function alwaysOf(value: T) { + return concat(of(value), NEVER); + } +}); diff --git a/modules/effects/spec/effects_feature_module.spec.ts b/modules/effects/spec/effects_feature_module.spec.ts index 69b8936fed..411937db80 100644 --- a/modules/effects/spec/effects_feature_module.spec.ts +++ b/modules/effects/spec/effects_feature_module.spec.ts @@ -1,175 +1,175 @@ -import { Injectable, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { - Action, - createFeatureSelector, - createSelector, - select, - Store, - StoreModule, -} from '@ngrx/store'; -import { map, withLatestFrom } from 'rxjs/operators'; -import { Actions, Effect, EffectsModule, ofType, createEffect } from '../'; -import { EffectsFeatureModule } from '../src/effects_feature_module'; -import { EffectsRootModule } from '../src/effects_root_module'; -import { FEATURE_EFFECTS } from '../src/tokens'; - -describe('Effects Feature Module', () => { - describe('when registered', () => { - const sourceA = 'sourceA'; - const sourceB = 'sourceB'; - const sourceC = 'sourceC'; - const effectSourceGroups = [[sourceA], [sourceB], [sourceC]]; - - let mockEffectSources: { addEffects: jasmine.Spy }; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - { - provide: EffectsRootModule, - useValue: { - addEffects: jasmine.createSpy('addEffects'), - }, - }, - { - provide: FEATURE_EFFECTS, - useValue: effectSourceGroups, - }, - EffectsFeatureModule, - ], - }); - - mockEffectSources = TestBed.get(EffectsRootModule); - }); - - it('should add all effects when instantiated', () => { - TestBed.get(EffectsFeatureModule); - - expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceA); - expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceB); - expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceC); - }); - }); - - describe('when registered in a different NgModule from the feature state', () => { - let effects: FeatureEffects; - let store: Store; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [AppModule], - }); - - effects = TestBed.get(FeatureEffects); - store = TestBed.get(Store); - }); - - it('should have the feature state defined to select from the effect', (done: any) => { - const action = { type: 'INCREMENT' }; - const result = { type: 'INCREASE' }; - - effects.effectWithStore.subscribe(res => { - expect(res).toEqual(result); - }); - - store.dispatch(action); - - store.pipe(select(getDataState)).subscribe(data => { - expect(data).toBe(110); - done(); - }); - }); - - it('should have the feature state defined to select from the createEffect', (done: any) => { - const action = { type: 'CREATE_INCREMENT' }; - const result = { type: 'CREATE_INCREASE' }; - - effects.createEffectWithStore.subscribe(res => { - expect(res).toEqual(result); - }); - - store.dispatch(action); - - store.pipe(select(getCreateDataState)).subscribe(data => { - expect(data).toBe(220); - done(); - }); - }); - }); -}); - -const FEATURE_KEY = 'feature'; - -interface State { - FEATURE_KEY: DataState; -} - -interface DataState { - data: number; - createData: number; -} - -const initialState: DataState = { - data: 100, - createData: 200, -}; - -function reducer(state: DataState = initialState, action: Action) { - switch (action.type) { - case 'INCREASE': - return { - ...state, - data: state.data + 10, - }; - case 'CREATE_INCREASE': - return { - ...state, - createData: state.createData + 20, - }; - } - return state; -} - -const getFeatureState = createFeatureSelector(FEATURE_KEY); - -const getDataState = createSelector(getFeatureState, state => state.data); -const getCreateDataState = createSelector( - getFeatureState, - state => state.createData -); - -@Injectable() -class FeatureEffects { - constructor(private actions: Actions, private store: Store) {} - - @Effect() - effectWithStore = this.actions.pipe( - ofType('INCREMENT'), - withLatestFrom(this.store.select(getDataState)), - map(([action, state]) => ({ type: 'INCREASE' })) - ); - - createEffectWithStore = createEffect(() => - this.actions.pipe( - ofType('CREATE_INCREMENT'), - withLatestFrom(this.store.select(getDataState)), - map(([action, state]) => ({ type: 'CREATE_INCREASE' })) - ) - ); -} - -@NgModule({ - imports: [EffectsModule.forFeature([FeatureEffects])], -}) -class FeatureEffectsModule {} - -@NgModule({ - imports: [FeatureEffectsModule, StoreModule.forFeature(FEATURE_KEY, reducer)], -}) -class FeatureModule {} - -@NgModule({ - imports: [StoreModule.forRoot({}), EffectsModule.forRoot([]), FeatureModule], -}) -class AppModule {} +import { Injectable, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { + Action, + createFeatureSelector, + createSelector, + select, + Store, + StoreModule, +} from '@ngrx/store'; +import { map, withLatestFrom } from 'rxjs/operators'; +import { Actions, Effect, EffectsModule, ofType, createEffect } from '../'; +import { EffectsFeatureModule } from '../src/effects_feature_module'; +import { EffectsRootModule } from '../src/effects_root_module'; +import { FEATURE_EFFECTS } from '../src/tokens'; + +describe('Effects Feature Module', () => { + describe('when registered', () => { + const sourceA = 'sourceA'; + const sourceB = 'sourceB'; + const sourceC = 'sourceC'; + const effectSourceGroups = [[sourceA], [sourceB], [sourceC]]; + + let mockEffectSources: { addEffects: jasmine.Spy }; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + { + provide: EffectsRootModule, + useValue: { + addEffects: jasmine.createSpy('addEffects'), + }, + }, + { + provide: FEATURE_EFFECTS, + useValue: effectSourceGroups, + }, + EffectsFeatureModule, + ], + }); + + mockEffectSources = TestBed.get(EffectsRootModule); + }); + + it('should add all effects when instantiated', () => { + TestBed.get(EffectsFeatureModule); + + expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceA); + expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceB); + expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceC); + }); + }); + + describe('when registered in a different NgModule from the feature state', () => { + let effects: FeatureEffects; + let store: Store; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppModule], + }); + + effects = TestBed.get(FeatureEffects); + store = TestBed.get(Store); + }); + + it('should have the feature state defined to select from the effect', (done: any) => { + const action = { type: 'INCREMENT' }; + const result = { type: 'INCREASE' }; + + effects.effectWithStore.subscribe(res => { + expect(res).toEqual(result); + }); + + store.dispatch(action); + + store.pipe(select(getDataState)).subscribe(data => { + expect(data).toBe(110); + done(); + }); + }); + + it('should have the feature state defined to select from the createEffect', (done: any) => { + const action = { type: 'CREATE_INCREMENT' }; + const result = { type: 'CREATE_INCREASE' }; + + effects.createEffectWithStore.subscribe(res => { + expect(res).toEqual(result); + }); + + store.dispatch(action); + + store.pipe(select(getCreateDataState)).subscribe(data => { + expect(data).toBe(220); + done(); + }); + }); + }); +}); + +const FEATURE_KEY = 'feature'; + +interface State { + FEATURE_KEY: DataState; +} + +interface DataState { + data: number; + createData: number; +} + +const initialState: DataState = { + data: 100, + createData: 200, +}; + +function reducer(state: DataState = initialState, action: Action) { + switch (action.type) { + case 'INCREASE': + return { + ...state, + data: state.data + 10, + }; + case 'CREATE_INCREASE': + return { + ...state, + createData: state.createData + 20, + }; + } + return state; +} + +const getFeatureState = createFeatureSelector(FEATURE_KEY); + +const getDataState = createSelector(getFeatureState, state => state.data); +const getCreateDataState = createSelector( + getFeatureState, + state => state.createData +); + +@Injectable() +class FeatureEffects { + constructor(private actions: Actions, private store: Store) {} + + @Effect() + effectWithStore = this.actions.pipe( + ofType('INCREMENT'), + withLatestFrom(this.store.select(getDataState)), + map(([action, state]) => ({ type: 'INCREASE' })) + ); + + createEffectWithStore = createEffect(() => + this.actions.pipe( + ofType('CREATE_INCREMENT'), + withLatestFrom(this.store.select(getDataState)), + map(([action, state]) => ({ type: 'CREATE_INCREASE' })) + ) + ); +} + +@NgModule({ + imports: [EffectsModule.forFeature([FeatureEffects])], +}) +class FeatureEffectsModule {} + +@NgModule({ + imports: [FeatureEffectsModule, StoreModule.forFeature(FEATURE_KEY, reducer)], +}) +class FeatureModule {} + +@NgModule({ + imports: [StoreModule.forRoot({}), EffectsModule.forRoot([]), FeatureModule], +}) +class AppModule {} diff --git a/modules/effects/spec/effects_root_module.spec.ts b/modules/effects/spec/effects_root_module.spec.ts index 5b043d6e60..19a49600cc 100644 --- a/modules/effects/spec/effects_root_module.spec.ts +++ b/modules/effects/spec/effects_root_module.spec.ts @@ -1,49 +1,49 @@ -import { TestBed } from '@angular/core/testing'; -import { INIT, Store, StoreModule } from '@ngrx/store'; - -import { EffectsModule } from '../src/effects_module'; -import { ROOT_EFFECTS_INIT } from '../src/effects_root_module'; - -describe('Effects Root Module', () => { - const foo = 'foo'; - const reducer = jasmine.createSpy('reducer').and.returnValue(foo); - - beforeEach(() => { - reducer.calls.reset(); - }); - - it('dispatches the root effects init action', () => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({ reducer }, { initialState: { reducer: foo } }), - EffectsModule.forRoot([]), - ], - }); - - const store = TestBed.get(Store); - - expect(reducer).toHaveBeenCalledWith(foo, { - type: INIT, - }); - expect(reducer).toHaveBeenCalledWith(foo, { - type: ROOT_EFFECTS_INIT, - }); - }); - - it(`doesn't dispatch the root effects init action when EffectsModule isn't used`, () => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({ reducer }, { initialState: { reducer: foo } }), - ], - }); - - const store = TestBed.get(Store); - - expect(reducer).toHaveBeenCalledWith(foo, { - type: INIT, - }); - expect(reducer).not.toHaveBeenCalledWith(foo, { - type: ROOT_EFFECTS_INIT, - }); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { INIT, Store, StoreModule } from '@ngrx/store'; + +import { EffectsModule } from '../src/effects_module'; +import { ROOT_EFFECTS_INIT } from '../src/effects_root_module'; + +describe('Effects Root Module', () => { + const foo = 'foo'; + const reducer = jasmine.createSpy('reducer').and.returnValue(foo); + + beforeEach(() => { + reducer.calls.reset(); + }); + + it('dispatches the root effects init action', () => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({ reducer }, { initialState: { reducer: foo } }), + EffectsModule.forRoot([]), + ], + }); + + const store = TestBed.get(Store); + + expect(reducer).toHaveBeenCalledWith(foo, { + type: INIT, + }); + expect(reducer).toHaveBeenCalledWith(foo, { + type: ROOT_EFFECTS_INIT, + }); + }); + + it(`doesn't dispatch the root effects init action when EffectsModule isn't used`, () => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({ reducer }, { initialState: { reducer: foo } }), + ], + }); + + const store = TestBed.get(Store); + + expect(reducer).toHaveBeenCalledWith(foo, { + type: INIT, + }); + expect(reducer).not.toHaveBeenCalledWith(foo, { + type: ROOT_EFFECTS_INIT, + }); + }); +}); diff --git a/modules/effects/spec/integration.spec.ts b/modules/effects/spec/integration.spec.ts index fe58788a17..e865452e79 100644 --- a/modules/effects/spec/integration.spec.ts +++ b/modules/effects/spec/integration.spec.ts @@ -1,140 +1,140 @@ -import { NgModuleFactoryLoader, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { - RouterTestingModule, - SpyNgModuleFactoryLoader, -} from '@angular/router/testing'; -import { Router } from '@angular/router'; -import { Store, Action } from '@ngrx/store'; -import { - EffectsModule, - OnInitEffects, - ROOT_EFFECTS_INIT, - OnIdentifyEffects, - EffectSources, -} from '..'; - -describe('NgRx Effects Integration spec', () => { - let dispatch: jasmine.Spy; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - EffectsModule.forRoot([ - RootEffectWithInitAction, - RootEffectWithoutLifecycle, - RootEffectWithInitActionWithPayload, - ]), - EffectsModule.forFeature([FeatEffectWithInitAction]), - RouterTestingModule.withRoutes([]), - ], - providers: [ - { - provide: Store, - useValue: { - dispatch: jasmine.createSpy('dispatch'), - }, - }, - ], - }); - - const store = TestBed.get(Store) as Store; - - const effectSources = TestBed.get(EffectSources) as EffectSources; - effectSources.addEffects(new FeatEffectWithIdentifierAndInitAction('one')); - effectSources.addEffects(new FeatEffectWithIdentifierAndInitAction('two')); - effectSources.addEffects(new FeatEffectWithIdentifierAndInitAction('one')); - - dispatch = store.dispatch as jasmine.Spy; - }); - - it('should dispatch init actions in the correct order', () => { - expect(dispatch.calls.count()).toBe(7); - - // All of the root effects init actions are dispatched first - expect(dispatch.calls.argsFor(0)).toEqual([ - { type: '[RootEffectWithInitAction]: INIT' }, - ]); - - expect(dispatch.calls.argsFor(1)).toEqual([new ActionWithPayload()]); - - // After all of the root effects are registered, the ROOT_EFFECTS_INIT action is dispatched - expect(dispatch.calls.argsFor(2)).toEqual([{ type: ROOT_EFFECTS_INIT }]); - - // After the root effects init, the feature effects are dispatched - expect(dispatch.calls.argsFor(3)).toEqual([ - { type: '[FeatEffectWithInitAction]: INIT' }, - ]); - - expect(dispatch.calls.argsFor(4)).toEqual([ - { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }, - ]); - - expect(dispatch.calls.argsFor(5)).toEqual([ - { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }, - ]); - - // While the effect has the same identifier the init effect action is still being dispatched - expect(dispatch.calls.argsFor(6)).toEqual([ - { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }, - ]); - }); - - it('throws if forRoot() is used more than once', (done: DoneFn) => { - let router: Router = TestBed.get(Router); - const loader: SpyNgModuleFactoryLoader = TestBed.get(NgModuleFactoryLoader); - - loader.stubbedModules = { feature: FeatModuleWithForRoot }; - router.resetConfig([{ path: 'feature-path', loadChildren: 'feature' }]); - - router.navigateByUrl('/feature-path').catch((err: TypeError) => { - expect(err.message).toBe( - 'EffectsModule.forRoot() called twice. Feature modules should use EffectsModule.forFeature() instead.' - ); - done(); - }); - }); - - class RootEffectWithInitAction implements OnInitEffects { - ngrxOnInitEffects(): Action { - return { type: '[RootEffectWithInitAction]: INIT' }; - } - } - - class ActionWithPayload implements Action { - readonly type = '[RootEffectWithInitActionWithPayload]: INIT'; - readonly payload = 47; - } - - class RootEffectWithInitActionWithPayload implements OnInitEffects { - ngrxOnInitEffects(): Action { - return new ActionWithPayload(); - } - } - - class RootEffectWithoutLifecycle {} - - class FeatEffectWithInitAction implements OnInitEffects { - ngrxOnInitEffects(): Action { - return { type: '[FeatEffectWithInitAction]: INIT' }; - } - } - - class FeatEffectWithIdentifierAndInitAction - implements OnInitEffects, OnIdentifyEffects { - ngrxOnIdentifyEffects(): string { - return this.effectIdentifier; - } - - ngrxOnInitEffects(): Action { - return { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }; - } - - constructor(private effectIdentifier: string) {} - } - - @NgModule({ - imports: [EffectsModule.forRoot([])], - }) - class FeatModuleWithForRoot {} -}); +import { NgModuleFactoryLoader, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { + RouterTestingModule, + SpyNgModuleFactoryLoader, +} from '@angular/router/testing'; +import { Router } from '@angular/router'; +import { Store, Action } from '@ngrx/store'; +import { + EffectsModule, + OnInitEffects, + ROOT_EFFECTS_INIT, + OnIdentifyEffects, + EffectSources, +} from '..'; + +describe('NgRx Effects Integration spec', () => { + let dispatch: jasmine.Spy; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + EffectsModule.forRoot([ + RootEffectWithInitAction, + RootEffectWithoutLifecycle, + RootEffectWithInitActionWithPayload, + ]), + EffectsModule.forFeature([FeatEffectWithInitAction]), + RouterTestingModule.withRoutes([]), + ], + providers: [ + { + provide: Store, + useValue: { + dispatch: jasmine.createSpy('dispatch'), + }, + }, + ], + }); + + const store = TestBed.get(Store) as Store; + + const effectSources = TestBed.get(EffectSources) as EffectSources; + effectSources.addEffects(new FeatEffectWithIdentifierAndInitAction('one')); + effectSources.addEffects(new FeatEffectWithIdentifierAndInitAction('two')); + effectSources.addEffects(new FeatEffectWithIdentifierAndInitAction('one')); + + dispatch = store.dispatch as jasmine.Spy; + }); + + it('should dispatch init actions in the correct order', () => { + expect(dispatch.calls.count()).toBe(7); + + // All of the root effects init actions are dispatched first + expect(dispatch.calls.argsFor(0)).toEqual([ + { type: '[RootEffectWithInitAction]: INIT' }, + ]); + + expect(dispatch.calls.argsFor(1)).toEqual([new ActionWithPayload()]); + + // After all of the root effects are registered, the ROOT_EFFECTS_INIT action is dispatched + expect(dispatch.calls.argsFor(2)).toEqual([{ type: ROOT_EFFECTS_INIT }]); + + // After the root effects init, the feature effects are dispatched + expect(dispatch.calls.argsFor(3)).toEqual([ + { type: '[FeatEffectWithInitAction]: INIT' }, + ]); + + expect(dispatch.calls.argsFor(4)).toEqual([ + { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }, + ]); + + expect(dispatch.calls.argsFor(5)).toEqual([ + { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }, + ]); + + // While the effect has the same identifier the init effect action is still being dispatched + expect(dispatch.calls.argsFor(6)).toEqual([ + { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }, + ]); + }); + + it('throws if forRoot() is used more than once', (done: DoneFn) => { + let router: Router = TestBed.get(Router); + const loader: SpyNgModuleFactoryLoader = TestBed.get(NgModuleFactoryLoader); + + loader.stubbedModules = { feature: FeatModuleWithForRoot }; + router.resetConfig([{ path: 'feature-path', loadChildren: 'feature' }]); + + router.navigateByUrl('/feature-path').catch((err: TypeError) => { + expect(err.message).toBe( + 'EffectsModule.forRoot() called twice. Feature modules should use EffectsModule.forFeature() instead.' + ); + done(); + }); + }); + + class RootEffectWithInitAction implements OnInitEffects { + ngrxOnInitEffects(): Action { + return { type: '[RootEffectWithInitAction]: INIT' }; + } + } + + class ActionWithPayload implements Action { + readonly type = '[RootEffectWithInitActionWithPayload]: INIT'; + readonly payload = 47; + } + + class RootEffectWithInitActionWithPayload implements OnInitEffects { + ngrxOnInitEffects(): Action { + return new ActionWithPayload(); + } + } + + class RootEffectWithoutLifecycle {} + + class FeatEffectWithInitAction implements OnInitEffects { + ngrxOnInitEffects(): Action { + return { type: '[FeatEffectWithInitAction]: INIT' }; + } + } + + class FeatEffectWithIdentifierAndInitAction + implements OnInitEffects, OnIdentifyEffects { + ngrxOnIdentifyEffects(): string { + return this.effectIdentifier; + } + + ngrxOnInitEffects(): Action { + return { type: '[FeatEffectWithIdentifierAndInitAction]: INIT' }; + } + + constructor(private effectIdentifier: string) {} + } + + @NgModule({ + imports: [EffectsModule.forRoot([])], + }) + class FeatModuleWithForRoot {} +}); diff --git a/modules/router-store/spec/integration.spec.ts b/modules/router-store/spec/integration.spec.ts index 822b907346..61a64350ff 100644 --- a/modules/router-store/spec/integration.spec.ts +++ b/modules/router-store/spec/integration.spec.ts @@ -1,1033 +1,1030 @@ -import { Injectable, ErrorHandler } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { - NavigationEnd, - Router, - RouterStateSnapshot, - NavigationCancel, - NavigationError, - ActivatedRouteSnapshot, -} from '@angular/router'; -import { Store, ScannedActionsSubject } from '@ngrx/store'; -import { filter, first, mapTo, take } from 'rxjs/operators'; - -import { - NavigationActionTiming, - ROUTER_CANCEL, - ROUTER_ERROR, - ROUTER_NAVIGATED, - ROUTER_NAVIGATION, - ROUTER_REQUEST, - RouterAction, - routerReducer, - RouterReducerState, - RouterStateSerializer, - StateKeyOrSelector, -} from '../src'; -import { createTestModule } from './utils'; - -describe('integration spec', () => { - it('should work', (done: any) => { - const reducer = (state: string = '', action: RouterAction) => { - if (action.type === ROUTER_NAVIGATION) { - return action.payload.routerState.url.toString(); - } else { - return state; - } - }; - - createTestModule({ reducers: { reducer } }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - expect(log).toEqual([ - { type: 'store', state: '' }, // init event. has nothing to do with the router - { type: 'store', state: '' }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/' }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/' }, - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/' }, - { type: 'router', event: 'ResolveStart', url: '/' }, - { type: 'router', event: 'ResolveEnd', url: '/' }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/' }, - ]); - }) - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .then(() => { - expect(log).toEqual([ - { type: 'store', state: '/' }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'store', state: '/next' }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'store', state: '/next' }, // ROUTER_NAVIGATED event in the store - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/next' }, - ]); - - done(); - }); - }); - - it('should have the routerState in the payload', (done: any) => { - const actionLog: RouterAction[] = []; - const reducer = (state: string = '', action: RouterAction) => { - switch (action.type) { - case ROUTER_CANCEL: - case ROUTER_ERROR: - case ROUTER_NAVIGATED: - case ROUTER_NAVIGATION: - case ROUTER_REQUEST: - actionLog.push(action); - return state; - default: - return state; - } - }; - - createTestModule({ - reducers: { reducer }, - canActivate: ( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot - ) => state.url !== 'next', - }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - const hasRouterState = (action: RouterAction) => - !!action.payload.routerState; - - router - .navigateByUrl('/') - .then(() => { - expect(actionLog.filter(hasRouterState).length).toBe(actionLog.length); - }) - .then(() => { - actionLog.splice(0); - return router.navigateByUrl('next'); - }) - .then(() => { - expect(actionLog.filter(hasRouterState).length).toBe(actionLog.length); - done(); - }); - }); - - xit('should support preventing navigation', (done: any) => { - const reducer = (state: string = '', action: RouterAction) => { - if ( - action.type === ROUTER_NAVIGATION && - action.payload.routerState.url.toString() === '/next' - ) { - throw new Error('You shall not pass!'); - } else { - return state; - } - }; - - createTestModule({ reducers: { reducer } }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .catch(e => { - expect(e.message).toEqual('You shall not pass!'); - expect(log).toEqual([ - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - { type: 'router', event: 'NavigationError', url: '/next' }, - ]); - - done(); - }); - }); - - it('should support rolling back if navigation gets canceled (navigation initialized through router)', (done: any) => { - const reducer = (state: string = '', action: RouterAction): any => { - if (action.type === ROUTER_NAVIGATION) { - return { - url: action.payload.routerState.url.toString(), - lastAction: ROUTER_NAVIGATION, - }; - } else if (action.type === ROUTER_CANCEL) { - return { - url: action.payload.routerState.url.toString(), - storeState: action.payload.storeState.reducer, - lastAction: ROUTER_CANCEL, - }; - } else { - return state; - } - }; - - createTestModule({ - reducers: { reducer, routerReducer }, - canActivate: () => false, - }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .then(r => { - expect(r).toEqual(false); - - expect(log).toEqual([ - { - type: 'store', - state: { url: '/', lastAction: ROUTER_NAVIGATION }, - }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { - type: 'store', - state: { url: '/next', lastAction: ROUTER_NAVIGATION }, - }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - - /* new Router Lifecycle in Angular 4.3 - m */ - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - // { type: 'router', event: 'ResolveStart', url: '/next' }, - // { type: 'router', event: 'ResolveEnd', url: '/next' }, - { - type: 'store', - state: { - url: '/', - lastAction: ROUTER_CANCEL, - storeState: { url: '/', lastAction: ROUTER_NAVIGATION }, - }, - }, - { type: 'action', action: ROUTER_CANCEL }, - { type: 'router', event: 'NavigationCancel', url: '/next' }, - ]); - - done(); - }); - }); - - it('should support rolling back if navigation gets canceled (navigation initialized through store)', (done: any) => { - const CHANGE_ROUTE = 'CHANGE_ROUTE'; - const reducer = ( - state: RouterReducerState, - action: any - ): RouterReducerState => { - if (action.type === CHANGE_ROUTE) { - return { - state: { url: '/next', root: {} }, - navigationId: 123, - }; - } else { - const nextState = routerReducer(state, action); - if (nextState && nextState.state) { - return { - ...nextState, - state: { - ...nextState.state, - root: {} as any, - }, - }; - } - return nextState; - } - }; - - createTestModule({ - reducers: { reducer }, - canActivate: () => false, - config: { stateKey: 'reducer' }, - }); - - const router: Router = TestBed.get(Router); - const store: Store = TestBed.get(Store); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - store.dispatch({ type: CHANGE_ROUTE }); - return waitForNavigation(router, NavigationCancel); - }) - .then(() => { - expect(log).toEqual([ - { type: 'router', event: 'NavigationStart', url: '/next' }, - { - type: 'store', - state: { state: { url: '/next', root: {} }, navigationId: 123 }, - }, - { type: 'action', action: CHANGE_ROUTE }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { - type: 'store', - state: { state: { url: '/', root: {} }, navigationId: 2 }, - }, - { type: 'action', action: ROUTER_CANCEL }, - { type: 'router', event: 'NavigationCancel', url: '/next' }, - ]); - - done(); - }); - }); - - it('should support rolling back if navigation errors (navigation initialized through router)', (done: any) => { - const reducer = (state: string = '', action: RouterAction): any => { - if (action.type === ROUTER_NAVIGATION) { - return { - url: action.payload.routerState.url.toString(), - lastAction: ROUTER_NAVIGATION, - }; - } else if (action.type === ROUTER_ERROR) { - return { - url: action.payload.routerState.url.toString(), - storeState: action.payload.storeState.reducer, - lastAction: ROUTER_ERROR, - }; - } else { - return state; - } - }; - - createTestModule({ - reducers: { reducer, routerReducer }, - canActivate: () => { - throw new Error('BOOM!'); - }, - }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .catch(e => { - expect(e.message).toEqual('BOOM!'); - - expect(log).toEqual([ - { - type: 'store', - state: { url: '/', lastAction: ROUTER_NAVIGATION }, - }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { - type: 'store', - state: { url: '/next', lastAction: ROUTER_NAVIGATION }, - }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - - { - type: 'store', - state: { - url: '/', - lastAction: ROUTER_ERROR, - storeState: { url: '/', lastAction: ROUTER_NAVIGATION }, - }, - }, - { type: 'action', action: ROUTER_ERROR }, - { type: 'router', event: 'NavigationError', url: '/next' }, - ]); - - done(); - }); - }); - - it('should support rolling back if navigation errors and hand error to error handler (navigation initialized through store)', (done: any) => { - const CHANGE_ROUTE = 'CHANGE_ROUTE'; - const reducer = ( - state: RouterReducerState, - action: any - ): RouterReducerState => { - if (action.type === CHANGE_ROUTE) { - return { - state: { url: '/next', root: {} }, - navigationId: 123, - }; - } else { - const nextState = routerReducer(state, action); - if (nextState && nextState.state) { - return { - ...nextState, - state: { - ...nextState.state, - root: {} as any, - }, - }; - } - return nextState; - } - }; - - const routerError = new Error('BOOM!'); - class SilentErrorHandler implements ErrorHandler { - handleError(error: any) { - expect(error).toBe(routerError); - } - } - - createTestModule({ - reducers: { reducer }, - canActivate: () => { - throw routerError; - }, - providers: [{ provide: ErrorHandler, useClass: SilentErrorHandler }], - config: { stateKey: 'reducer' }, - }); - - const router: Router = TestBed.get(Router); - const store: Store = TestBed.get(Store); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - store.dispatch({ type: CHANGE_ROUTE }); - return waitForNavigation(router, NavigationError); - }) - .then(() => { - expect(log).toEqual([ - { type: 'router', event: 'NavigationStart', url: '/next' }, - { - type: 'store', - state: { state: { url: '/next', root: {} }, navigationId: 123 }, - }, - { type: 'action', action: CHANGE_ROUTE }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { - type: 'store', - state: { state: { url: '/', root: {} }, navigationId: 2 }, - }, - { type: 'action', action: ROUTER_ERROR }, - { type: 'router', event: 'NavigationError', url: '/next' }, - ]); - - done(); - }); - }); - - it('should call navigateByUrl when resetting state of the routerReducer', (done: any) => { - const reducer = (state: any, action: RouterAction) => { - const r = routerReducer(state, action); - return r && r.state - ? { url: r.state.url, navigationId: r.navigationId } - : null; - }; - - createTestModule({ reducers: { router: routerReducer, reducer } }); - - const router = TestBed.get(Router); - const store = TestBed.get(Store); - const log = logOfRouterAndActionsAndStore(); - - const routerReducerStates: any[] = []; - store.subscribe((state: any) => { - if (state.router) { - routerReducerStates.push(state.router); - } - }); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .then(() => { - expect(log).toEqual([ - { type: 'store', state: null }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'store', state: { url: '/next', navigationId: 2 } }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'store', state: null }, // ROUTER_NAVIGATED event in the store - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/next' }, - ]); - log.splice(0); - - store.dispatch({ - type: ROUTER_NAVIGATION, - payload: { - routerState: routerReducerStates[0].state, - event: { id: routerReducerStates[0].navigationId }, - }, - }); - return waitForNavigation(router); - }) - .then(() => { - expect(log).toEqual([ - { type: 'router', event: 'NavigationStart', url: '/' }, - { type: 'store', state: { url: '/', navigationId: 1 } }, // restored - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/' }, - - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/' }, - { type: 'router', event: 'ResolveStart', url: '/' }, - { type: 'router', event: 'ResolveEnd', url: '/' }, - - { type: 'router', event: 'NavigationEnd', url: '/' }, - ]); - log.splice(0); - }) - .then(() => { - store.dispatch({ - type: ROUTER_NAVIGATION, - payload: { - routerState: routerReducerStates[3].state, - event: { id: routerReducerStates[3].navigationId }, - }, - }); - return waitForNavigation(router); - }) - .then(() => { - expect(log).toEqual([ - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'store', state: { url: '/next', navigationId: 2 } }, // restored - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - - { type: 'router', event: 'NavigationEnd', url: '/next' }, - ]); - done(); - }); - }); - - it('should support cancellation of initial navigation using canLoad guard', (done: any) => { - const reducer = (state: any, action: RouterAction) => { - const r = routerReducer(state, action); - return r && r.state - ? { url: r.state.url, navigationId: r.navigationId } - : null; - }; - - createTestModule({ - reducers: { routerReducer, reducer }, - canLoad: () => false, - }); - - const router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router.navigateByUrl('/load').then((r: boolean) => { - expect(r).toBe(false); - - expect(log).toEqual([ - { type: 'store', state: null }, // initial state - { type: 'store', state: null }, // ROUTER_REQEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/load' }, - { type: 'store', state: { url: '', navigationId: 1 } }, - { type: 'action', action: ROUTER_CANCEL }, - { type: 'router', event: 'NavigationCancel', url: '/load' }, - ]); - done(); - }); - }); - - it('should support cancellation of initial navigation when canLoad guard rejects', (done: any) => { - const reducer = (state: any, action: RouterAction) => { - const r = routerReducer(state, action); - return r && r.state - ? { url: r.state.url, navigationId: r.navigationId } - : null; - }; - - createTestModule({ - reducers: { routerReducer, reducer }, - canLoad: () => Promise.reject('boom'), - }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/load') - .then(() => { - fail(`Shouldn't be called`); - }) - .catch(err => { - expect(err).toBe('boom'); - - expect(log).toEqual([ - { type: 'store', state: null }, // initial state - { type: 'store', state: null }, // ROUTER_REQEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/load' }, - { type: 'store', state: { url: '', navigationId: 1 } }, - { type: 'action', action: ROUTER_ERROR }, - { type: 'router', event: 'NavigationError', url: '/load' }, - ]); - - done(); - }); - }); - - function shouldSupportCustomSerializer( - serializerThroughConfig: boolean, - done: Function - ) { - interface SerializedState { - url: string; - params: any; - } - - const reducer = ( - state: any, - action: RouterAction - ) => { - const r = routerReducer(state, action); - return r && r.state - ? { - url: r.state.url, - navigationId: r.navigationId, - params: r.state.params, - } - : null; - }; - - @Injectable() - class CustomSerializer implements RouterStateSerializer { - constructor(store: Store) { - // Requiring store to test Serializer with injected arguments works. - } - serialize(routerState: RouterStateSnapshot): SerializedState { - const url = `${routerState.url}-custom`; - const params = { test: 1 }; - - return { url, params }; - } - } - - if (serializerThroughConfig) { - createTestModule({ - reducers: { routerReducer, reducer }, - config: { serializer: CustomSerializer }, - }); - } else { - const providers = [ - { provide: RouterStateSerializer, useClass: CustomSerializer }, - ]; - createTestModule({ reducers: { routerReducer, reducer }, providers }); - } - - const router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .then(() => { - expect(log).toEqual([ - { type: 'store', state: null }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { - type: 'store', - state: { - url: '/next-custom', - navigationId: 2, - params: { test: 1 }, - }, - }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'store', state: null }, // ROUTER_NAVIGATED event in the store - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/next' }, - ]); - log.splice(0); - done(); - }); - } - - it('should support a custom RouterStateSnapshot serializer via provider', (done: any) => { - shouldSupportCustomSerializer(false, done); - }); - - it('should support a custom RouterStateSnapshot serializer via config', (done: any) => { - shouldSupportCustomSerializer(true, done); - }); - - it('should support event during an async canActivate guard', (done: any) => { - createTestModule({ - reducers: { routerReducer }, - canActivate: () => { - store.dispatch({ type: 'USER_EVENT' }); - return store.pipe( - take(1), - mapTo(true) - ); - }, - }); - - const router: Router = TestBed.get(Router); - const store: Store = TestBed.get(Store); - const log = logOfRouterAndActionsAndStore(); - - router - .navigateByUrl('/') - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .then(() => { - expect(log).toEqual([ - { type: 'store', state: undefined }, // after ROUTER_REQUEST - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'store', state: undefined }, // after ROUTER_NAVIGATION - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'store', state: undefined }, // after USER_EVENT - { type: 'action', action: 'USER_EVENT' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'store', state: undefined }, // after ROUTER_NAVIGATED - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/next' }, - ]); - - done(); - }); - }); - - it('should work when defining state key', (done: any) => { - const reducer = (state: string = '', action: RouterAction) => { - if (action.type === ROUTER_NAVIGATION) { - return action.payload.routerState.url.toString(); - } else { - return state; - } - }; - - createTestModule({ - reducers: { 'router-reducer': reducer }, - config: { stateKey: 'router-reducer' }, - }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore({ stateKey: 'router-reducer' }); - - router - .navigateByUrl('/') - .then(() => { - expect(log).toEqual([ - { type: 'store', state: '' }, // init event. has nothing to do with the router - { type: 'store', state: '' }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/' }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/' }, - { type: 'router', event: 'GuardsCheckStart', url: '/' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/' }, - { type: 'router', event: 'ResolveStart', url: '/' }, - { type: 'router', event: 'ResolveEnd', url: '/' }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/' }, - ]); - }) - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .then(() => { - expect(log).toEqual([ - { type: 'store', state: '/' }, - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'store', state: '/next' }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'store', state: '/next' }, - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/next' }, - ]); - - done(); - }); - }); - - it('should work when defining state selector', (done: any) => { - const reducer = (state: string = '', action: RouterAction) => { - if (action.type === ROUTER_NAVIGATION) { - return action.payload.routerState.url.toString(); - } else { - return state; - } - }; - - createTestModule({ - reducers: { routerReducer: reducer }, - config: { stateKey: (state: any) => state.routerReducer }, - }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore({ - stateKey: (state: any) => state.routerReducer, - }); - - router - .navigateByUrl('/') - .then(() => { - expect(log).toEqual([ - { type: 'store', state: '' }, // init event. has nothing to do with the router - { type: 'store', state: '' }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/' }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/' }, - { type: 'router', event: 'GuardsCheckStart', url: '/' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/' }, - { type: 'router', event: 'ResolveStart', url: '/' }, - { type: 'router', event: 'ResolveEnd', url: '/' }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/' }, - ]); - }) - .then(() => { - log.splice(0); - return router.navigateByUrl('next'); - }) - .then(() => { - expect(log).toEqual([ - { type: 'store', state: '/' }, - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'store', state: '/next' }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'store', state: '/next' }, - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/next' }, - ]); - - done(); - }); - }); - - it('should continue to react to navigation after state initiates router change', (done: Function) => { - const reducer = (state: any = { state: { url: '/' } }, action: any) => { - if (action.type === ROUTER_NAVIGATION) { - return { state: { url: action.payload.routerState.url.toString() } }; - } else { - return state; - } - }; - - createTestModule({ - reducers: { reducer }, - config: { stateKey: 'reducer' }, - }); - - const router: Router = TestBed.get(Router); - const store = TestBed.get(Store); - const log = logOfRouterAndActionsAndStore(); - - store.dispatch({ - type: ROUTER_NAVIGATION, - payload: { routerState: { url: '/next' } }, - }); - waitForNavigation(router) - .then(() => { - router.navigate(['/']); - return waitForNavigation(router); - }) - .then(() => { - expect(log).toEqual([ - { type: 'store', state: { state: { url: '/' } } }, - { type: 'router', event: 'NavigationStart', url: '/next' }, - { type: 'store', state: { state: { url: '/next' } } }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/next' }, - { type: 'router', event: 'GuardsCheckStart', url: '/next' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, - { type: 'router', event: 'ResolveStart', url: '/next' }, - { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'router', event: 'NavigationEnd', url: '/next' }, - { type: 'store', state: { state: { url: '/next' } } }, - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/' }, - { type: 'store', state: { state: { url: '/' } } }, - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'router', event: 'RoutesRecognized', url: '/' }, - { type: 'router', event: 'GuardsCheckStart', url: '/' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/' }, - { type: 'router', event: 'ResolveStart', url: '/' }, - { type: 'router', event: 'ResolveEnd', url: '/' }, - { type: 'store', state: { state: { url: '/' } } }, - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/' }, - ]); - done(); - }); - }); - - it('should dispatch ROUTER_NAVIGATION later when config options set to true', () => { - const reducer = (state: string = '', action: RouterAction) => { - if (action.type === ROUTER_NAVIGATION) { - return action.payload.routerState.url.toString(); - } else { - return state; - } - }; - - createTestModule({ - reducers: { reducer }, - config: { navigationActionTiming: NavigationActionTiming.PostActivation }, - }); - - const router: Router = TestBed.get(Router); - const log = logOfRouterAndActionsAndStore(); - - router.navigateByUrl('/').then(() => { - expect(log).toEqual([ - { type: 'store', state: '' }, // init event. has nothing to do with the router - { type: 'store', state: '' }, // ROUTER_REQUEST event in the store - { type: 'action', action: ROUTER_REQUEST }, - { type: 'router', event: 'NavigationStart', url: '/' }, - { type: 'router', event: 'RoutesRecognized', url: '/' }, - /* new Router Lifecycle in Angular 4.3 */ - { type: 'router', event: 'GuardsCheckStart', url: '/' }, - { type: 'router', event: 'GuardsCheckEnd', url: '/' }, - { type: 'router', event: 'ResolveStart', url: '/' }, - { type: 'router', event: 'ResolveEnd', url: '/' }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store - { type: 'action', action: ROUTER_NAVIGATION }, - { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store - { type: 'action', action: ROUTER_NAVIGATED }, - { type: 'router', event: 'NavigationEnd', url: '/' }, - ]); - }); - }); -}); - -function waitForNavigation(router: Router, event: any = NavigationEnd) { - return router.events - .pipe( - filter(e => e instanceof event), - first() - ) - .toPromise(); -} - -/** - * Logs the events of router, store and actions$. - * Note: Because of the synchronous nature of many of those events, it may sometimes - * appear that the order is "mixed" up even if its correct. - * Example: router event is fired -> store is updated -> store log appears before router log - * Also, actions$ always fires the next action AFTER the store is updated - */ -function logOfRouterAndActionsAndStore( - options: { stateKey: StateKeyOrSelector } = { - stateKey: 'reducer', - } -): any[] { - const router: Router = TestBed.get(Router); - const store: Store = TestBed.get(Store); - // Not using effects' Actions to avoid @ngrx/effects dependency - const actions$: ScannedActionsSubject = TestBed.get(ScannedActionsSubject); - const log: any[] = []; - router.events.subscribe(e => { - if (e.hasOwnProperty('url')) { - log.push({ - type: 'router', - event: e.constructor.name, - url: (e).url.toString(), - }); - } - }); - actions$.subscribe(action => - log.push({ type: 'action', action: action.type }) - ); - store.subscribe(store => { - if (typeof options.stateKey === 'function') { - log.push({ type: 'store', state: options.stateKey(store) }); - } else { - log.push({ type: 'store', state: store[options.stateKey] }); - } - }); - return log; -} +import { Injectable, ErrorHandler } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { + NavigationEnd, + Router, + RouterStateSnapshot, + NavigationCancel, + NavigationError, + ActivatedRouteSnapshot, +} from '@angular/router'; +import { Store, ScannedActionsSubject } from '@ngrx/store'; +import { filter, first, mapTo, take } from 'rxjs/operators'; + +import { + NavigationActionTiming, + ROUTER_CANCEL, + ROUTER_ERROR, + ROUTER_NAVIGATED, + ROUTER_NAVIGATION, + ROUTER_REQUEST, + RouterAction, + routerReducer, + RouterReducerState, + RouterStateSerializer, + StateKeyOrSelector, +} from '../src'; +import { createTestModule } from './utils'; + +describe('integration spec', () => { + it('should work', (done: any) => { + const reducer = (state: string = '', action: RouterAction) => { + if (action.type === ROUTER_NAVIGATION) { + return action.payload.routerState.url.toString(); + } else { + return state; + } + }; + + createTestModule({ reducers: { reducer } }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + expect(log).toEqual([ + { type: 'store', state: '' }, // init event. has nothing to do with the router + { type: 'store', state: '' }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/' }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/' }, + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/' }, + { type: 'router', event: 'ResolveStart', url: '/' }, + { type: 'router', event: 'ResolveEnd', url: '/' }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/' }, + ]); + }) + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .then(() => { + expect(log).toEqual([ + { type: 'store', state: '/' }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'store', state: '/next' }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + { type: 'store', state: '/next' }, // ROUTER_NAVIGATED event in the store + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/next' }, + ]); + + done(); + }); + }); + + it('should have the routerState in the payload', (done: any) => { + const actionLog: RouterAction[] = []; + const reducer = (state: string = '', action: RouterAction) => { + switch (action.type) { + case ROUTER_CANCEL: + case ROUTER_ERROR: + case ROUTER_NAVIGATED: + case ROUTER_NAVIGATION: + case ROUTER_REQUEST: + actionLog.push(action); + return state; + default: + return state; + } + }; + + createTestModule({ + reducers: { reducer }, + canActivate: ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ) => state.url !== 'next', + }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + const hasRouterState = (action: RouterAction) => + !!action.payload.routerState; + + router + .navigateByUrl('/') + .then(() => { + expect(actionLog.filter(hasRouterState).length).toBe(actionLog.length); + }) + .then(() => { + actionLog.splice(0); + return router.navigateByUrl('next'); + }) + .then(() => { + expect(actionLog.filter(hasRouterState).length).toBe(actionLog.length); + done(); + }); + }); + + xit('should support preventing navigation', (done: any) => { + const reducer = (state: string = '', action: RouterAction) => { + if ( + action.type === ROUTER_NAVIGATION && + action.payload.routerState.url.toString() === '/next' + ) { + throw new Error('You shall not pass!'); + } else { + return state; + } + }; + + createTestModule({ reducers: { reducer } }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .catch(e => { + expect(e.message).toEqual('You shall not pass!'); + expect(log).toEqual([ + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + { type: 'router', event: 'NavigationError', url: '/next' }, + ]); + + done(); + }); + }); + + it('should support rolling back if navigation gets canceled (navigation initialized through router)', (done: any) => { + const reducer = (state: string = '', action: RouterAction): any => { + if (action.type === ROUTER_NAVIGATION) { + return { + url: action.payload.routerState.url.toString(), + lastAction: ROUTER_NAVIGATION, + }; + } else if (action.type === ROUTER_CANCEL) { + return { + url: action.payload.routerState.url.toString(), + storeState: action.payload.storeState.reducer, + lastAction: ROUTER_CANCEL, + }; + } else { + return state; + } + }; + + createTestModule({ + reducers: { reducer, routerReducer }, + canActivate: () => false, + }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .then(r => { + expect(r).toEqual(false); + + expect(log).toEqual([ + { + type: 'store', + state: { url: '/', lastAction: ROUTER_NAVIGATION }, + }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { + type: 'store', + state: { url: '/next', lastAction: ROUTER_NAVIGATION }, + }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + + /* new Router Lifecycle in Angular 4.3 - m */ + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + // { type: 'router', event: 'ResolveStart', url: '/next' }, + // { type: 'router', event: 'ResolveEnd', url: '/next' }, + { + type: 'store', + state: { + url: '/', + lastAction: ROUTER_CANCEL, + storeState: { url: '/', lastAction: ROUTER_NAVIGATION }, + }, + }, + { type: 'action', action: ROUTER_CANCEL }, + { type: 'router', event: 'NavigationCancel', url: '/next' }, + ]); + + done(); + }); + }); + + it('should support rolling back if navigation gets canceled (navigation initialized through store)', (done: any) => { + const CHANGE_ROUTE = 'CHANGE_ROUTE'; + const reducer = ( + state: RouterReducerState, + action: any + ): RouterReducerState => { + if (action.type === CHANGE_ROUTE) { + return { + state: { url: '/next', root: {} }, + navigationId: 123, + }; + } else { + const nextState = routerReducer(state, action); + if (nextState && nextState.state) { + return { + ...nextState, + state: { + ...nextState.state, + root: {} as any, + }, + }; + } + return nextState; + } + }; + + createTestModule({ + reducers: { reducer }, + canActivate: () => false, + config: { stateKey: 'reducer' }, + }); + + const router: Router = TestBed.get(Router); + const store: Store = TestBed.get(Store); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + store.dispatch({ type: CHANGE_ROUTE }); + return waitForNavigation(router, NavigationCancel); + }) + .then(() => { + expect(log).toEqual([ + { type: 'router', event: 'NavigationStart', url: '/next' }, + { + type: 'store', + state: { state: { url: '/next', root: {} }, navigationId: 123 }, + }, + { type: 'action', action: CHANGE_ROUTE }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { + type: 'store', + state: { state: { url: '/', root: {} }, navigationId: 2 }, + }, + { type: 'action', action: ROUTER_CANCEL }, + { type: 'router', event: 'NavigationCancel', url: '/next' }, + ]); + + done(); + }); + }); + + it('should support rolling back if navigation errors (navigation initialized through router)', (done: any) => { + const reducer = (state: string = '', action: RouterAction): any => { + if (action.type === ROUTER_NAVIGATION) { + return { + url: action.payload.routerState.url.toString(), + lastAction: ROUTER_NAVIGATION, + }; + } else if (action.type === ROUTER_ERROR) { + return { + url: action.payload.routerState.url.toString(), + storeState: action.payload.storeState.reducer, + lastAction: ROUTER_ERROR, + }; + } else { + return state; + } + }; + + createTestModule({ + reducers: { reducer, routerReducer }, + canActivate: () => { + throw new Error('BOOM!'); + }, + }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .catch(e => { + expect(e.message).toEqual('BOOM!'); + + expect(log).toEqual([ + { + type: 'store', + state: { url: '/', lastAction: ROUTER_NAVIGATION }, + }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { + type: 'store', + state: { url: '/next', lastAction: ROUTER_NAVIGATION }, + }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + + { + type: 'store', + state: { + url: '/', + lastAction: ROUTER_ERROR, + storeState: { url: '/', lastAction: ROUTER_NAVIGATION }, + }, + }, + { type: 'action', action: ROUTER_ERROR }, + { type: 'router', event: 'NavigationError', url: '/next' }, + ]); + + done(); + }); + }); + + it('should support rolling back if navigation errors and hand error to error handler (navigation initialized through store)', (done: any) => { + const CHANGE_ROUTE = 'CHANGE_ROUTE'; + const reducer = ( + state: RouterReducerState, + action: any + ): RouterReducerState => { + if (action.type === CHANGE_ROUTE) { + return { + state: { url: '/next', root: {} }, + navigationId: 123, + }; + } else { + const nextState = routerReducer(state, action); + if (nextState && nextState.state) { + return { + ...nextState, + state: { + ...nextState.state, + root: {} as any, + }, + }; + } + return nextState; + } + }; + + const routerError = new Error('BOOM!'); + class SilentErrorHandler implements ErrorHandler { + handleError(error: any) { + expect(error).toBe(routerError); + } + } + + createTestModule({ + reducers: { reducer }, + canActivate: () => { + throw routerError; + }, + providers: [{ provide: ErrorHandler, useClass: SilentErrorHandler }], + config: { stateKey: 'reducer' }, + }); + + const router: Router = TestBed.get(Router); + const store: Store = TestBed.get(Store); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + store.dispatch({ type: CHANGE_ROUTE }); + return waitForNavigation(router, NavigationError); + }) + .then(() => { + expect(log).toEqual([ + { type: 'router', event: 'NavigationStart', url: '/next' }, + { + type: 'store', + state: { state: { url: '/next', root: {} }, navigationId: 123 }, + }, + { type: 'action', action: CHANGE_ROUTE }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { + type: 'store', + state: { state: { url: '/', root: {} }, navigationId: 2 }, + }, + { type: 'action', action: ROUTER_ERROR }, + { type: 'router', event: 'NavigationError', url: '/next' }, + ]); + + done(); + }); + }); + + it('should call navigateByUrl when resetting state of the routerReducer', (done: any) => { + const reducer = (state: any, action: RouterAction) => { + const r = routerReducer(state, action); + return r && r.state + ? { url: r.state.url, navigationId: r.navigationId } + : null; + }; + + createTestModule({ reducers: { router: routerReducer, reducer } }); + + const router = TestBed.get(Router); + const store = TestBed.get(Store); + const log = logOfRouterAndActionsAndStore(); + + const routerReducerStates: any[] = []; + store.subscribe((state: any) => { + if (state.router) { + routerReducerStates.push(state.router); + } + }); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .then(() => { + expect(log).toEqual([ + { type: 'store', state: null }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'store', state: { url: '/next', navigationId: 2 } }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + { type: 'store', state: null }, // ROUTER_NAVIGATED event in the store + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/next' }, + ]); + log.splice(0); + + store.dispatch({ + type: ROUTER_NAVIGATION, + payload: { + routerState: routerReducerStates[0].state, + event: { id: routerReducerStates[0].navigationId }, + }, + }); + return waitForNavigation(router); + }) + .then(() => { + expect(log).toEqual([ + { type: 'router', event: 'NavigationStart', url: '/' }, + { type: 'store', state: { url: '/', navigationId: 1 } }, // restored + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/' }, + + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/' }, + { type: 'router', event: 'ResolveStart', url: '/' }, + { type: 'router', event: 'ResolveEnd', url: '/' }, + + { type: 'router', event: 'NavigationEnd', url: '/' }, + ]); + log.splice(0); + }) + .then(() => { + store.dispatch({ + type: ROUTER_NAVIGATION, + payload: { + routerState: routerReducerStates[3].state, + event: { id: routerReducerStates[3].navigationId }, + }, + }); + return waitForNavigation(router); + }) + .then(() => { + expect(log).toEqual([ + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'store', state: { url: '/next', navigationId: 2 } }, // restored + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + + { type: 'router', event: 'NavigationEnd', url: '/next' }, + ]); + done(); + }); + }); + + it('should support cancellation of initial navigation using canLoad guard', (done: any) => { + const reducer = (state: any, action: RouterAction) => { + const r = routerReducer(state, action); + return r && r.state + ? { url: r.state.url, navigationId: r.navigationId } + : null; + }; + + createTestModule({ + reducers: { routerReducer, reducer }, + canLoad: () => false, + }); + + const router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router.navigateByUrl('/load').then((r: boolean) => { + expect(r).toBe(false); + + expect(log).toEqual([ + { type: 'store', state: null }, // initial state + { type: 'store', state: null }, // ROUTER_REQEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/load' }, + { type: 'store', state: { url: '', navigationId: 1 } }, + { type: 'action', action: ROUTER_CANCEL }, + { type: 'router', event: 'NavigationCancel', url: '/load' }, + ]); + done(); + }); + }); + + it('should support cancellation of initial navigation when canLoad guard rejects', (done: any) => { + const reducer = (state: any, action: RouterAction) => { + const r = routerReducer(state, action); + return r && r.state + ? { url: r.state.url, navigationId: r.navigationId } + : null; + }; + + createTestModule({ + reducers: { routerReducer, reducer }, + canLoad: () => Promise.reject('boom'), + }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/load') + .then(() => { + fail(`Shouldn't be called`); + }) + .catch(err => { + expect(err).toBe('boom'); + + expect(log).toEqual([ + { type: 'store', state: null }, // initial state + { type: 'store', state: null }, // ROUTER_REQEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/load' }, + { type: 'store', state: { url: '', navigationId: 1 } }, + { type: 'action', action: ROUTER_ERROR }, + { type: 'router', event: 'NavigationError', url: '/load' }, + ]); + + done(); + }); + }); + + function shouldSupportCustomSerializer( + serializerThroughConfig: boolean, + done: Function + ) { + interface SerializedState { + url: string; + params: any; + } + + const reducer = ( + state: any, + action: RouterAction + ) => { + const r = routerReducer(state, action); + return r && r.state + ? { + url: r.state.url, + navigationId: r.navigationId, + params: r.state.params, + } + : null; + }; + + @Injectable() + class CustomSerializer implements RouterStateSerializer { + constructor(store: Store) { + // Requiring store to test Serializer with injected arguments works. + } + serialize(routerState: RouterStateSnapshot): SerializedState { + const url = `${routerState.url}-custom`; + const params = { test: 1 }; + + return { url, params }; + } + } + + if (serializerThroughConfig) { + createTestModule({ + reducers: { routerReducer, reducer }, + config: { serializer: CustomSerializer }, + }); + } else { + const providers = [ + { provide: RouterStateSerializer, useClass: CustomSerializer }, + ]; + createTestModule({ reducers: { routerReducer, reducer }, providers }); + } + + const router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .then(() => { + expect(log).toEqual([ + { type: 'store', state: null }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { + type: 'store', + state: { + url: '/next-custom', + navigationId: 2, + params: { test: 1 }, + }, + }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + { type: 'store', state: null }, // ROUTER_NAVIGATED event in the store + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/next' }, + ]); + log.splice(0); + done(); + }); + } + + it('should support a custom RouterStateSnapshot serializer via provider', (done: any) => { + shouldSupportCustomSerializer(false, done); + }); + + it('should support a custom RouterStateSnapshot serializer via config', (done: any) => { + shouldSupportCustomSerializer(true, done); + }); + + it('should support event during an async canActivate guard', (done: any) => { + createTestModule({ + reducers: { routerReducer }, + canActivate: () => { + store.dispatch({ type: 'USER_EVENT' }); + return store.pipe(take(1), mapTo(true)); + }, + }); + + const router: Router = TestBed.get(Router); + const store: Store = TestBed.get(Store); + const log = logOfRouterAndActionsAndStore(); + + router + .navigateByUrl('/') + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .then(() => { + expect(log).toEqual([ + { type: 'store', state: undefined }, // after ROUTER_REQUEST + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'store', state: undefined }, // after ROUTER_NAVIGATION + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'store', state: undefined }, // after USER_EVENT + { type: 'action', action: 'USER_EVENT' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + { type: 'store', state: undefined }, // after ROUTER_NAVIGATED + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/next' }, + ]); + + done(); + }); + }); + + it('should work when defining state key', (done: any) => { + const reducer = (state: string = '', action: RouterAction) => { + if (action.type === ROUTER_NAVIGATION) { + return action.payload.routerState.url.toString(); + } else { + return state; + } + }; + + createTestModule({ + reducers: { 'router-reducer': reducer }, + config: { stateKey: 'router-reducer' }, + }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore({ stateKey: 'router-reducer' }); + + router + .navigateByUrl('/') + .then(() => { + expect(log).toEqual([ + { type: 'store', state: '' }, // init event. has nothing to do with the router + { type: 'store', state: '' }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/' }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/' }, + { type: 'router', event: 'GuardsCheckStart', url: '/' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/' }, + { type: 'router', event: 'ResolveStart', url: '/' }, + { type: 'router', event: 'ResolveEnd', url: '/' }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/' }, + ]); + }) + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .then(() => { + expect(log).toEqual([ + { type: 'store', state: '/' }, + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'store', state: '/next' }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + { type: 'store', state: '/next' }, + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/next' }, + ]); + + done(); + }); + }); + + it('should work when defining state selector', (done: any) => { + const reducer = (state: string = '', action: RouterAction) => { + if (action.type === ROUTER_NAVIGATION) { + return action.payload.routerState.url.toString(); + } else { + return state; + } + }; + + createTestModule({ + reducers: { routerReducer: reducer }, + config: { stateKey: (state: any) => state.routerReducer }, + }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore({ + stateKey: (state: any) => state.routerReducer, + }); + + router + .navigateByUrl('/') + .then(() => { + expect(log).toEqual([ + { type: 'store', state: '' }, // init event. has nothing to do with the router + { type: 'store', state: '' }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/' }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/' }, + { type: 'router', event: 'GuardsCheckStart', url: '/' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/' }, + { type: 'router', event: 'ResolveStart', url: '/' }, + { type: 'router', event: 'ResolveEnd', url: '/' }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/' }, + ]); + }) + .then(() => { + log.splice(0); + return router.navigateByUrl('next'); + }) + .then(() => { + expect(log).toEqual([ + { type: 'store', state: '/' }, + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'store', state: '/next' }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + { type: 'store', state: '/next' }, + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/next' }, + ]); + + done(); + }); + }); + + it('should continue to react to navigation after state initiates router change', (done: Function) => { + const reducer = (state: any = { state: { url: '/' } }, action: any) => { + if (action.type === ROUTER_NAVIGATION) { + return { state: { url: action.payload.routerState.url.toString() } }; + } else { + return state; + } + }; + + createTestModule({ + reducers: { reducer }, + config: { stateKey: 'reducer' }, + }); + + const router: Router = TestBed.get(Router); + const store = TestBed.get(Store); + const log = logOfRouterAndActionsAndStore(); + + store.dispatch({ + type: ROUTER_NAVIGATION, + payload: { routerState: { url: '/next' } }, + }); + waitForNavigation(router) + .then(() => { + router.navigate(['/']); + return waitForNavigation(router); + }) + .then(() => { + expect(log).toEqual([ + { type: 'store', state: { state: { url: '/' } } }, + { type: 'router', event: 'NavigationStart', url: '/next' }, + { type: 'store', state: { state: { url: '/next' } } }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/next' }, + { type: 'router', event: 'GuardsCheckStart', url: '/next' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, + { type: 'router', event: 'ResolveStart', url: '/next' }, + { type: 'router', event: 'ResolveEnd', url: '/next' }, + { type: 'router', event: 'NavigationEnd', url: '/next' }, + { type: 'store', state: { state: { url: '/next' } } }, + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/' }, + { type: 'store', state: { state: { url: '/' } } }, + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'router', event: 'RoutesRecognized', url: '/' }, + { type: 'router', event: 'GuardsCheckStart', url: '/' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/' }, + { type: 'router', event: 'ResolveStart', url: '/' }, + { type: 'router', event: 'ResolveEnd', url: '/' }, + { type: 'store', state: { state: { url: '/' } } }, + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/' }, + ]); + done(); + }); + }); + + it('should dispatch ROUTER_NAVIGATION later when config options set to true', () => { + const reducer = (state: string = '', action: RouterAction) => { + if (action.type === ROUTER_NAVIGATION) { + return action.payload.routerState.url.toString(); + } else { + return state; + } + }; + + createTestModule({ + reducers: { reducer }, + config: { navigationActionTiming: NavigationActionTiming.PostActivation }, + }); + + const router: Router = TestBed.get(Router); + const log = logOfRouterAndActionsAndStore(); + + router.navigateByUrl('/').then(() => { + expect(log).toEqual([ + { type: 'store', state: '' }, // init event. has nothing to do with the router + { type: 'store', state: '' }, // ROUTER_REQUEST event in the store + { type: 'action', action: ROUTER_REQUEST }, + { type: 'router', event: 'NavigationStart', url: '/' }, + { type: 'router', event: 'RoutesRecognized', url: '/' }, + /* new Router Lifecycle in Angular 4.3 */ + { type: 'router', event: 'GuardsCheckStart', url: '/' }, + { type: 'router', event: 'GuardsCheckEnd', url: '/' }, + { type: 'router', event: 'ResolveStart', url: '/' }, + { type: 'router', event: 'ResolveEnd', url: '/' }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store + { type: 'action', action: ROUTER_NAVIGATION }, + { type: 'store', state: '/' }, // ROUTER_NAVIGATED event in the store + { type: 'action', action: ROUTER_NAVIGATED }, + { type: 'router', event: 'NavigationEnd', url: '/' }, + ]); + }); + }); +}); + +function waitForNavigation(router: Router, event: any = NavigationEnd) { + return router.events + .pipe( + filter(e => e instanceof event), + first() + ) + .toPromise(); +} + +/** + * Logs the events of router, store and actions$. + * Note: Because of the synchronous nature of many of those events, it may sometimes + * appear that the order is "mixed" up even if its correct. + * Example: router event is fired -> store is updated -> store log appears before router log + * Also, actions$ always fires the next action AFTER the store is updated + */ +function logOfRouterAndActionsAndStore( + options: { stateKey: StateKeyOrSelector } = { + stateKey: 'reducer', + } +): any[] { + const router: Router = TestBed.get(Router); + const store: Store = TestBed.get(Store); + // Not using effects' Actions to avoid @ngrx/effects dependency + const actions$: ScannedActionsSubject = TestBed.get(ScannedActionsSubject); + const log: any[] = []; + router.events.subscribe(e => { + if (e.hasOwnProperty('url')) { + log.push({ + type: 'router', + event: e.constructor.name, + url: (e).url.toString(), + }); + } + }); + actions$.subscribe(action => + log.push({ type: 'action', action: action.type }) + ); + store.subscribe(store => { + if (typeof options.stateKey === 'function') { + log.push({ type: 'store', state: options.stateKey(store) }); + } else { + log.push({ type: 'store', state: store[options.stateKey] }); + } + }); + return log; +} diff --git a/modules/router-store/spec/router_store_module.spec.ts b/modules/router-store/spec/router_store_module.spec.ts index 0dc0bfd6b1..c61b275fc7 100644 --- a/modules/router-store/spec/router_store_module.spec.ts +++ b/modules/router-store/spec/router_store_module.spec.ts @@ -1,216 +1,210 @@ -import { TestBed } from '@angular/core/testing'; -import { Router, RouterEvent } from '@angular/router'; -import { - routerReducer, - RouterReducerState, - StoreRouterConnectingModule, - RouterAction, - RouterState, - RouterStateSerializer, - MinimalRouterStateSerializer, - DefaultRouterStateSerializer, -} from '@ngrx/router-store'; -import { select, Store, ActionsSubject } from '@ngrx/store'; -import { withLatestFrom, filter } from 'rxjs/operators'; - -import { createTestModule } from './utils'; - -describe('Router Store Module', () => { - describe('with defining state key', () => { - const customStateKey = 'router-reducer'; - let storeRouterConnectingModule: StoreRouterConnectingModule; - let store: Store; - let router: Router; - - interface State { - [customStateKey]: RouterReducerState; - } - - beforeEach(() => { - createTestModule({ - reducers: { - [customStateKey]: routerReducer, - }, - config: { - stateKey: customStateKey, - }, - }); - - store = TestBed.get(Store); - router = TestBed.get(Router); - storeRouterConnectingModule = TestBed.get(StoreRouterConnectingModule); - }); - - it('should have custom state key as own property', () => { - expect((storeRouterConnectingModule).stateKey).toBe(customStateKey); - }); - - it('should call navigateIfNeeded with args selected by custom state key', (done: any) => { - let logs: any[] = []; - store - .pipe( - select(customStateKey), - withLatestFrom(store) - ) - .subscribe(([routerStoreState, storeState]) => { - logs.push([routerStoreState, storeState]); - }); - - spyOn( - storeRouterConnectingModule, - 'navigateIfNeeded' as never - ).and.callThrough(); - logs = []; - - // this dispatches `@ngrx/router-store/navigation` action - // and store emits its payload. - router.navigateByUrl('/').then(() => { - const actual = (( - storeRouterConnectingModule - )).navigateIfNeeded.calls.allArgs(); - - expect(actual.length).toBe(1); - expect(actual[0]).toEqual(logs[0]); - done(); - }); - }); - }); - - describe('with defining state selector', () => { - const customStateKey = 'routerReducer'; - const customStateSelector = (state: State) => state.routerReducer; - - let storeRouterConnectingModule: StoreRouterConnectingModule; - let store: Store; - let router: Router; - - interface State { - [customStateKey]: RouterReducerState; - } - - beforeEach(() => { - createTestModule({ - reducers: { - [customStateKey]: routerReducer, - }, - config: { - stateKey: customStateSelector, - }, - }); - - store = TestBed.get(Store); - router = TestBed.get(Router); - storeRouterConnectingModule = TestBed.get(StoreRouterConnectingModule); - }); - - it('should have same state selector as own property', () => { - expect((storeRouterConnectingModule).stateKey).toBe( - customStateSelector - ); - }); - - it('should call navigateIfNeeded with args selected by custom state selector', (done: any) => { - let logs: any[] = []; - store - .pipe( - select(customStateSelector), - withLatestFrom(store) - ) - .subscribe(([routerStoreState, storeState]) => { - logs.push([routerStoreState, storeState]); - }); - - spyOn( - storeRouterConnectingModule, - 'navigateIfNeeded' as never - ).and.callThrough(); - logs = []; - - // this dispatches `@ngrx/router-store/navigation` action - // and store emits its payload. - router.navigateByUrl('/').then(() => { - const actual = (( - storeRouterConnectingModule - )).navigateIfNeeded.calls.allArgs(); - - expect(actual.length).toBe(1); - expect(actual[0]).toEqual(logs[0]); - done(); - }); - }); - }); - - describe('routerState', () => { - function setup(routerState: RouterState, serializer?: any) { - createTestModule({ - reducers: {}, - config: { - routerState, - serializer, - }, - }); - - return { - actions: TestBed.get(ActionsSubject) as ActionsSubject, - router: TestBed.get(Router) as Router, - serializer: TestBed.get(RouterStateSerializer) as RouterStateSerializer, - }; - } - - const onlyRouterActions = (a: any): a is RouterAction => - a.payload && a.payload.event; - - describe('Full', () => { - it('should dispatch the full event', async () => { - const { actions, router } = setup(RouterState.Full); - actions - .pipe(filter(onlyRouterActions)) - .subscribe(({ payload }) => - expect(payload.event instanceof RouterEvent).toBe(true) - ); - - await router.navigateByUrl('/'); - }); - - it('should use the default router serializer', () => { - const { serializer } = setup(RouterState.Full); - expect(serializer).toEqual(new DefaultRouterStateSerializer()); - }); - - it('should use the provided serializer if one is provided', () => { - const { serializer } = setup( - RouterState.Full, - MinimalRouterStateSerializer - ); - expect(serializer).toEqual(new MinimalRouterStateSerializer()); - }); - }); - - describe('Minimal', () => { - it('should dispatch the navigation id with url', async () => { - const { actions, router } = setup(RouterState.Minimal); - actions - .pipe(filter(onlyRouterActions)) - .subscribe(({ payload }: any) => { - expect(payload.event instanceof RouterEvent).toBe(false); - expect(payload.event).toEqual({ id: 1, url: '/' }); - }); - - await router.navigateByUrl('/'); - }); - - it('should use the minimal router serializer', () => { - const { serializer } = setup(RouterState.Minimal); - expect(serializer).toEqual(new MinimalRouterStateSerializer()); - }); - - it('should use the provided serializer if one is provided', () => { - const { serializer } = setup( - RouterState.Minimal, - DefaultRouterStateSerializer - ); - expect(serializer).toEqual(new DefaultRouterStateSerializer()); - }); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { Router, RouterEvent } from '@angular/router'; +import { + routerReducer, + RouterReducerState, + StoreRouterConnectingModule, + RouterAction, + RouterState, + RouterStateSerializer, + MinimalRouterStateSerializer, + DefaultRouterStateSerializer, +} from '@ngrx/router-store'; +import { select, Store, ActionsSubject } from '@ngrx/store'; +import { withLatestFrom, filter } from 'rxjs/operators'; + +import { createTestModule } from './utils'; + +describe('Router Store Module', () => { + describe('with defining state key', () => { + const customStateKey = 'router-reducer'; + let storeRouterConnectingModule: StoreRouterConnectingModule; + let store: Store; + let router: Router; + + interface State { + [customStateKey]: RouterReducerState; + } + + beforeEach(() => { + createTestModule({ + reducers: { + [customStateKey]: routerReducer, + }, + config: { + stateKey: customStateKey, + }, + }); + + store = TestBed.get(Store); + router = TestBed.get(Router); + storeRouterConnectingModule = TestBed.get(StoreRouterConnectingModule); + }); + + it('should have custom state key as own property', () => { + expect((storeRouterConnectingModule).stateKey).toBe(customStateKey); + }); + + it('should call navigateIfNeeded with args selected by custom state key', (done: any) => { + let logs: any[] = []; + store + .pipe(select(customStateKey), withLatestFrom(store)) + .subscribe(([routerStoreState, storeState]) => { + logs.push([routerStoreState, storeState]); + }); + + spyOn( + storeRouterConnectingModule, + 'navigateIfNeeded' as never + ).and.callThrough(); + logs = []; + + // this dispatches `@ngrx/router-store/navigation` action + // and store emits its payload. + router.navigateByUrl('/').then(() => { + const actual = (( + storeRouterConnectingModule + )).navigateIfNeeded.calls.allArgs(); + + expect(actual.length).toBe(1); + expect(actual[0]).toEqual(logs[0]); + done(); + }); + }); + }); + + describe('with defining state selector', () => { + const customStateKey = 'routerReducer'; + const customStateSelector = (state: State) => state.routerReducer; + + let storeRouterConnectingModule: StoreRouterConnectingModule; + let store: Store; + let router: Router; + + interface State { + [customStateKey]: RouterReducerState; + } + + beforeEach(() => { + createTestModule({ + reducers: { + [customStateKey]: routerReducer, + }, + config: { + stateKey: customStateSelector, + }, + }); + + store = TestBed.get(Store); + router = TestBed.get(Router); + storeRouterConnectingModule = TestBed.get(StoreRouterConnectingModule); + }); + + it('should have same state selector as own property', () => { + expect((storeRouterConnectingModule).stateKey).toBe( + customStateSelector + ); + }); + + it('should call navigateIfNeeded with args selected by custom state selector', (done: any) => { + let logs: any[] = []; + store + .pipe(select(customStateSelector), withLatestFrom(store)) + .subscribe(([routerStoreState, storeState]) => { + logs.push([routerStoreState, storeState]); + }); + + spyOn( + storeRouterConnectingModule, + 'navigateIfNeeded' as never + ).and.callThrough(); + logs = []; + + // this dispatches `@ngrx/router-store/navigation` action + // and store emits its payload. + router.navigateByUrl('/').then(() => { + const actual = (( + storeRouterConnectingModule + )).navigateIfNeeded.calls.allArgs(); + + expect(actual.length).toBe(1); + expect(actual[0]).toEqual(logs[0]); + done(); + }); + }); + }); + + describe('routerState', () => { + function setup(routerState: RouterState, serializer?: any) { + createTestModule({ + reducers: {}, + config: { + routerState, + serializer, + }, + }); + + return { + actions: TestBed.get(ActionsSubject) as ActionsSubject, + router: TestBed.get(Router) as Router, + serializer: TestBed.get(RouterStateSerializer) as RouterStateSerializer, + }; + } + + const onlyRouterActions = (a: any): a is RouterAction => + a.payload && a.payload.event; + + describe('Full', () => { + it('should dispatch the full event', async () => { + const { actions, router } = setup(RouterState.Full); + actions + .pipe(filter(onlyRouterActions)) + .subscribe(({ payload }) => + expect(payload.event instanceof RouterEvent).toBe(true) + ); + + await router.navigateByUrl('/'); + }); + + it('should use the default router serializer', () => { + const { serializer } = setup(RouterState.Full); + expect(serializer).toEqual(new DefaultRouterStateSerializer()); + }); + + it('should use the provided serializer if one is provided', () => { + const { serializer } = setup( + RouterState.Full, + MinimalRouterStateSerializer + ); + expect(serializer).toEqual(new MinimalRouterStateSerializer()); + }); + }); + + describe('Minimal', () => { + it('should dispatch the navigation id with url', async () => { + const { actions, router } = setup(RouterState.Minimal); + actions + .pipe(filter(onlyRouterActions)) + .subscribe(({ payload }: any) => { + expect(payload.event instanceof RouterEvent).toBe(false); + expect(payload.event).toEqual({ id: 1, url: '/' }); + }); + + await router.navigateByUrl('/'); + }); + + it('should use the minimal router serializer', () => { + const { serializer } = setup(RouterState.Minimal); + expect(serializer).toEqual(new MinimalRouterStateSerializer()); + }); + + it('should use the provided serializer if one is provided', () => { + const { serializer } = setup( + RouterState.Minimal, + DefaultRouterStateSerializer + ); + expect(serializer).toEqual(new DefaultRouterStateSerializer()); + }); + }); + }); +}); diff --git a/modules/schematics/src/container/files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template b/modules/schematics/src/container/files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template index 7f5054a8fa..6c83a9b10c 100644 --- a/modules/schematics/src/container/files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template +++ b/modules/schematics/src/container/files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template @@ -1,31 +1,31 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; -import { provideMockStore, MockStore } from '@ngrx/store/testing'; - -describe('<%= classify(name) %>Component', () => { - let component: <%= classify(name) %>Component; - let fixture: ComponentFixture<<%= classify(name) %>Component>; - let store: MockStore; - - beforeEach(async() => { - TestBed.configureTestingModule({ - providers: [ provideMockStore() ], - declarations: [ <%= classify(name) %>Component ] - }); - - await TestBed.compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(<%= classify(name) %>Component); - component = fixture.componentInstance; - store = TestBed.get(Store); - - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; +import { provideMockStore, MockStore } from '@ngrx/store/testing'; + +describe('<%= classify(name) %>Component', () => { + let component: <%= classify(name) %>Component; + let fixture: ComponentFixture<<%= classify(name) %>Component>; + let store: MockStore; + + beforeEach(async() => { + TestBed.configureTestingModule({ + providers: [ provideMockStore() ], + declarations: [ <%= classify(name) %>Component ] + }); + + await TestBed.compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(<%= classify(name) %>Component); + component = fixture.componentInstance; + store = TestBed.get(Store); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/modules/schematics/src/container/integration-files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template b/modules/schematics/src/container/integration-files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template index 641495122e..f447cef647 100644 --- a/modules/schematics/src/container/integration-files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template +++ b/modules/schematics/src/container/integration-files/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template @@ -1,32 +1,32 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; -import { Store, StoreModule } from '@ngrx/store'; - -describe('<%= classify(name) %>Component', () => { - let component: <%= classify(name) %>Component; - let fixture: ComponentFixture<<%= classify(name) %>Component>; - let store: Store; - - beforeEach(async() => { - TestBed.configureTestingModule({ - imports: [ StoreModule.forRoot({}) ], - declarations: [ <%= classify(name) %>Component ] - }); - - await TestBed.compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(<%= classify(name) %>Component); - component = fixture.componentInstance; - store = TestBed.get(Store); - - spyOn(store, 'dispatch').and.callThrough(); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; +import { Store, StoreModule } from '@ngrx/store'; + +describe('<%= classify(name) %>Component', () => { + let component: <%= classify(name) %>Component; + let fixture: ComponentFixture<<%= classify(name) %>Component>; + let store: Store; + + beforeEach(async() => { + TestBed.configureTestingModule({ + imports: [ StoreModule.forRoot({}) ], + declarations: [ <%= classify(name) %>Component ] + }); + + await TestBed.compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(<%= classify(name) %>Component); + component = fixture.componentInstance; + store = TestBed.get(Store); + + spyOn(store, 'dispatch').and.callThrough(); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/modules/schematics/src/data/files/__name@dasherize@if-flat__/__name@dasherize__.service.spec.ts.template b/modules/schematics/src/data/files/__name@dasherize@if-flat__/__name@dasherize__.service.spec.ts.template index 522af1c892..1f6a8fb58a 100644 --- a/modules/schematics/src/data/files/__name@dasherize@if-flat__/__name@dasherize__.service.spec.ts.template +++ b/modules/schematics/src/data/files/__name@dasherize@if-flat__/__name@dasherize__.service.spec.ts.template @@ -1,28 +1,28 @@ -import { TestBed } from '@angular/core/testing'; -import { - EntityCollectionServiceElementsFactory -} from '@ngrx/data'; -import { <%= classify(name) %>Service } from './<%= dasherize(name) %>.service'; - -describe('<%= classify(name) %>Service', () => { - let service: <%= classify(name) %>Service; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - providers: [ - EntityCollectionServiceElementsFactory, - <%= classify(name) %>Service - ] - }); - - await TestBed.compileComponents(); - }); - - beforeEach(() => { - service = TestBed.get(<%= classify(name) %>Service) - }); - - it('should create an instance', () => { - expect(service).toBeTruthy(); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { + EntityCollectionServiceElementsFactory +} from '@ngrx/data'; +import { <%= classify(name) %>Service } from './<%= dasherize(name) %>.service'; + +describe('<%= classify(name) %>Service', () => { + let service: <%= classify(name) %>Service; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [ + EntityCollectionServiceElementsFactory, + <%= classify(name) %>Service + ] + }); + + await TestBed.compileComponents(); + }); + + beforeEach(() => { + service = TestBed.get(<%= classify(name) %>Service) + }); + + it('should create an instance', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template b/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template index f2742202f9..64ac0f0610 100644 --- a/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template +++ b/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.spec.ts.template @@ -1,25 +1,25 @@ -import { TestBed } from '@angular/core/testing'; -import { provideMockActions } from '@ngrx/effects/testing'; -import { Observable } from 'rxjs'; - -import { <%= classify(name) %>Effects } from './<%= dasherize(name) %>.effects'; - -describe('<%= classify(name) %>Effects', () => { - let actions$: Observable; - let effects: <%= classify(name) %>Effects; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - <%= classify(name) %>Effects, - provideMockActions(() => actions$) - ] - }); - - effects = TestBed.get<<%= classify(name) %>Effects>(<%= classify(name) %>Effects); - }); - - it('should be created', () => { - expect(effects).toBeTruthy(); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { Observable } from 'rxjs'; + +import { <%= classify(name) %>Effects } from './<%= dasherize(name) %>.effects'; + +describe('<%= classify(name) %>Effects', () => { + let actions$: Observable; + let effects: <%= classify(name) %>Effects; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + <%= classify(name) %>Effects, + provideMockActions(() => actions$) + ] + }); + + effects = TestBed.get<<%= classify(name) %>Effects>(<%= classify(name) %>Effects); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); +}); diff --git a/modules/store-devtools/spec/integration.spec.ts b/modules/store-devtools/spec/integration.spec.ts index 5321ea9554..75500bd65b 100644 --- a/modules/store-devtools/spec/integration.spec.ts +++ b/modules/store-devtools/spec/integration.spec.ts @@ -1,96 +1,96 @@ -import { NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { StoreModule, Store, Action } from '@ngrx/store'; -import { - StoreDevtoolsModule, - StoreDevtools, - StoreDevtoolsOptions, -} from '@ngrx/store-devtools'; - -describe('Devtools Integration', () => { - function setup(options: Partial = {}) { - @NgModule({ - imports: [ - StoreModule.forFeature('a', (state: any, action: any) => state), - ], - }) - class EagerFeatureModule {} - - @NgModule({ - imports: [ - StoreModule.forRoot({}), - EagerFeatureModule, - StoreDevtoolsModule.instrument(options), - ], - }) - class RootModule {} - - TestBed.configureTestingModule({ - imports: [RootModule], - }); - - const store = TestBed.get(Store) as Store; - const devtools = TestBed.get(StoreDevtools) as StoreDevtools; - return { store, devtools }; - } - - afterEach(() => { - const devtools = TestBed.get(StoreDevtools) as StoreDevtools; - devtools.reset(); - }); - - it('should load the store eagerly', () => { - let error = false; - - try { - const { store } = setup(); - store.subscribe(); - } catch (e) { - error = true; - } - - expect(error).toBeFalsy(); - }); - - it('should not throw if actions are ignored', (done: any) => { - const { store, devtools } = setup({ - predicate: (_, { type }: Action) => type !== 'FOO', - }); - store.subscribe(); - devtools.dispatcher.subscribe((action: Action) => { - if (action.type === 'REFRESH') { - done(); - } - }); - store.dispatch({ type: 'FOO' }); - devtools.refresh(); - }); - - it('should not throw if actions are blocked', (done: any) => { - const { store, devtools } = setup({ - actionsBlocklist: ['FOO'], - }); - store.subscribe(); - devtools.dispatcher.subscribe((action: Action) => { - if (action.type === 'REFRESH') { - done(); - } - }); - store.dispatch({ type: 'FOO' }); - devtools.refresh(); - }); - - it('should not throw if actions are safe', (done: any) => { - const { store, devtools } = setup({ - actionsSafelist: ['BAR'], - }); - store.subscribe(); - devtools.dispatcher.subscribe((action: Action) => { - if (action.type === 'REFRESH') { - done(); - } - }); - store.dispatch({ type: 'FOO' }); - devtools.refresh(); - }); -}); +import { NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { StoreModule, Store, Action } from '@ngrx/store'; +import { + StoreDevtoolsModule, + StoreDevtools, + StoreDevtoolsOptions, +} from '@ngrx/store-devtools'; + +describe('Devtools Integration', () => { + function setup(options: Partial = {}) { + @NgModule({ + imports: [ + StoreModule.forFeature('a', (state: any, action: any) => state), + ], + }) + class EagerFeatureModule {} + + @NgModule({ + imports: [ + StoreModule.forRoot({}), + EagerFeatureModule, + StoreDevtoolsModule.instrument(options), + ], + }) + class RootModule {} + + TestBed.configureTestingModule({ + imports: [RootModule], + }); + + const store = TestBed.get(Store) as Store; + const devtools = TestBed.get(StoreDevtools) as StoreDevtools; + return { store, devtools }; + } + + afterEach(() => { + const devtools = TestBed.get(StoreDevtools) as StoreDevtools; + devtools.reset(); + }); + + it('should load the store eagerly', () => { + let error = false; + + try { + const { store } = setup(); + store.subscribe(); + } catch (e) { + error = true; + } + + expect(error).toBeFalsy(); + }); + + it('should not throw if actions are ignored', (done: any) => { + const { store, devtools } = setup({ + predicate: (_, { type }: Action) => type !== 'FOO', + }); + store.subscribe(); + devtools.dispatcher.subscribe((action: Action) => { + if (action.type === 'REFRESH') { + done(); + } + }); + store.dispatch({ type: 'FOO' }); + devtools.refresh(); + }); + + it('should not throw if actions are blocked', (done: any) => { + const { store, devtools } = setup({ + actionsBlocklist: ['FOO'], + }); + store.subscribe(); + devtools.dispatcher.subscribe((action: Action) => { + if (action.type === 'REFRESH') { + done(); + } + }); + store.dispatch({ type: 'FOO' }); + devtools.refresh(); + }); + + it('should not throw if actions are safe', (done: any) => { + const { store, devtools } = setup({ + actionsSafelist: ['BAR'], + }); + store.subscribe(); + devtools.dispatcher.subscribe((action: Action) => { + if (action.type === 'REFRESH') { + done(); + } + }); + store.dispatch({ type: 'FOO' }); + devtools.refresh(); + }); +}); diff --git a/modules/store/spec/edge.spec.ts b/modules/store/spec/edge.spec.ts index 0cced79ec0..0eda23d4d0 100644 --- a/modules/store/spec/edge.spec.ts +++ b/modules/store/spec/edge.spec.ts @@ -1,61 +1,61 @@ -import { TestBed } from '@angular/core/testing'; -import { select, Store, StoreModule } from '@ngrx/store'; - -import { todoCount, todos } from './fixtures/edge_todos'; - -interface TestAppSchema { - counter1: number; - counter2: number; - counter3: number; -} - -interface Todo {} - -interface TodoAppSchema { - todoCount: number; - todos: Todo[]; -} - -describe('ngRx Store', () => { - describe('basic store actions', () => { - let store: Store; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({ todos, todoCount } as any), - ], - }); - - store = TestBed.get(Store); - }); - - it('should provide an Observable Store', () => { - expect(store).toBeDefined(); - }); - - it('should handle re-entrancy', (done: any) => { - let todosNextCount = 0; - let todosCountNextCount = 0; - - store.pipe(select('todos')).subscribe(todos => { - todosNextCount++; - store.dispatch({ type: 'SET_COUNT', payload: todos.length }); - }); - - store.pipe(select('todoCount')).subscribe(count => { - todosCountNextCount++; - }); - - store.dispatch({ type: 'ADD_TODO', payload: { name: 'test' } }); - expect(todosNextCount).toBe(2); - expect(todosCountNextCount).toBe(2); - - setTimeout(() => { - expect(todosNextCount).toBe(2); - expect(todosCountNextCount).toBe(2); - done(); - }, 10); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { select, Store, StoreModule } from '@ngrx/store'; + +import { todoCount, todos } from './fixtures/edge_todos'; + +interface TestAppSchema { + counter1: number; + counter2: number; + counter3: number; +} + +interface Todo {} + +interface TodoAppSchema { + todoCount: number; + todos: Todo[]; +} + +describe('ngRx Store', () => { + describe('basic store actions', () => { + let store: Store; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({ todos, todoCount } as any), + ], + }); + + store = TestBed.get(Store); + }); + + it('should provide an Observable Store', () => { + expect(store).toBeDefined(); + }); + + it('should handle re-entrancy', (done: any) => { + let todosNextCount = 0; + let todosCountNextCount = 0; + + store.pipe(select('todos')).subscribe(todos => { + todosNextCount++; + store.dispatch({ type: 'SET_COUNT', payload: todos.length }); + }); + + store.pipe(select('todoCount')).subscribe(count => { + todosCountNextCount++; + }); + + store.dispatch({ type: 'ADD_TODO', payload: { name: 'test' } }); + expect(todosNextCount).toBe(2); + expect(todosCountNextCount).toBe(2); + + setTimeout(() => { + expect(todosNextCount).toBe(2); + expect(todosCountNextCount).toBe(2); + done(); + }, 10); + }); + }); +}); diff --git a/modules/store/spec/integration.spec.ts b/modules/store/spec/integration.spec.ts index 9958afd561..3733a2f40c 100644 --- a/modules/store/spec/integration.spec.ts +++ b/modules/store/spec/integration.spec.ts @@ -1,508 +1,488 @@ -import { TestBed } from '@angular/core/testing'; -import { - ActionReducer, - ActionReducerMap, - select, - Store, - StoreModule, - createFeatureSelector, - createSelector, -} from '@ngrx/store'; -import { combineLatest } from 'rxjs'; -import { first, toArray, take } from 'rxjs/operators'; - -import { INITIAL_STATE, ReducerManager, State } from '../src/private_export'; -import { - ADD_TODO, - COMPLETE_ALL_TODOS, - COMPLETE_TODO, - SET_VISIBILITY_FILTER, - todos, - visibilityFilter, - VisibilityFilters, - resetId, -} from './fixtures/todos'; -import { - RouterTestingModule, - SpyNgModuleFactoryLoader, -} from '@angular/router/testing'; -import { NgModuleFactoryLoader, NgModule } from '@angular/core'; -import { Router } from '@angular/router'; - -interface Todo { - id: number; - text: string; - completed: boolean; -} - -interface TodoAppSchema { - visibilityFilter: string; - todos: Todo[]; -} - -describe('ngRx Integration spec', () => { - describe('todo integration spec', function() { - let store: Store; - let state: State; - - const initialState = { - todos: [], - visibilityFilter: VisibilityFilters.SHOW_ALL, - }; - const reducers: ActionReducerMap = { - todos: todos, - visibilityFilter: visibilityFilter, - }; - - beforeEach(() => { - resetId(); - spyOn(reducers, 'todos').and.callThrough(); - - TestBed.configureTestingModule({ - imports: [StoreModule.forRoot(reducers, { initialState })], - }); - - store = TestBed.get(Store); - state = TestBed.get(State); - }); - - it('should successfully instantiate', () => { - expect(store).toBeDefined(); - }); - - it('should combine reducers automatically if a key/value map is provided', () => { - const action = { type: 'Test Action' }; - const reducer$: ReducerManager = TestBed.get(ReducerManager); - - reducer$.pipe(first()).subscribe((reducer: ActionReducer) => { - expect(reducer).toBeDefined(); - expect(typeof reducer === 'function').toBe(true); - - reducer({ todos: [] }, action); - - expect(reducers.todos).toHaveBeenCalledWith([], action); - }); - }); - - it('should use a provided initial state', () => { - const resolvedInitialState = TestBed.get(INITIAL_STATE); - - expect(resolvedInitialState).toEqual(initialState); - }); - - it('should start with no todos and showing all filter', () => { - expect(state.value.todos.length).toEqual(0); - expect(state.value.visibilityFilter).toEqual(VisibilityFilters.SHOW_ALL); - }); - - it('should add a todo', () => { - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - - expect(state.value.todos.length).toEqual(1); - expect(state.value.todos[0].text).toEqual('first todo'); - expect(state.value.todos[0].completed).toEqual(false); - }); - - it('should add another todo', () => { - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); - - expect(state.value.todos.length).toEqual(2); - expect(state.value.todos[1].text).toEqual('second todo'); - expect(state.value.todos[1].completed).toEqual(false); - }); - - it('should complete the first todo', () => { - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ - type: COMPLETE_TODO, - payload: { id: state.value.todos[0].id }, - }); - - expect(state.value.todos[0].completed).toEqual(true); - }); - - describe('using the store.select', () => { - it('should use visibilityFilter to filter todos', () => { - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); - store.dispatch({ - type: COMPLETE_TODO, - payload: { id: state.value.todos[0].id }, - }); - - const filterVisibleTodos = (visibilityFilter: any, todos: any) => { - let predicate; - if (visibilityFilter === VisibilityFilters.SHOW_ALL) { - predicate = () => true; - } else if (visibilityFilter === VisibilityFilters.SHOW_ACTIVE) { - predicate = (todo: any) => !todo.completed; - } else { - predicate = (todo: any) => todo.completed; - } - return todos.filter(predicate); - }; - - let currentlyVisibleTodos: Todo[] = []; - - combineLatest( - store.select('visibilityFilter'), - store.select('todos'), - filterVisibleTodos - ).subscribe(visibleTodos => { - currentlyVisibleTodos = visibleTodos; - }); - - expect(currentlyVisibleTodos.length).toBe(2); - - store.dispatch({ - type: SET_VISIBILITY_FILTER, - payload: VisibilityFilters.SHOW_ACTIVE, - }); - - expect(currentlyVisibleTodos.length).toBe(1); - expect(currentlyVisibleTodos[0].completed).toBe(false); - - store.dispatch({ - type: SET_VISIBILITY_FILTER, - payload: VisibilityFilters.SHOW_COMPLETED, - }); - - expect(currentlyVisibleTodos.length).toBe(1); - expect(currentlyVisibleTodos[0].completed).toBe(true); - - store.dispatch({ type: COMPLETE_ALL_TODOS }); - - expect(currentlyVisibleTodos.length).toBe(2); - expect(currentlyVisibleTodos[0].completed).toBe(true); - expect(currentlyVisibleTodos[1].completed).toBe(true); - - store.dispatch({ - type: SET_VISIBILITY_FILTER, - payload: VisibilityFilters.SHOW_ACTIVE, - }); - - expect(currentlyVisibleTodos.length).toBe(0); - }); - - it('should use props to get a todo', (done: DoneFn) => { - const getTodosById = createSelector( - (state: TodoAppSchema) => state.todos, - (todos: Todo[], id: number) => { - return todos.find(p => p.id === id); - } - ); - - const todo$ = store.select(getTodosById, 2); - todo$ - .pipe( - take(3), - toArray() - ) - .subscribe(res => { - expect(res).toEqual([ - undefined, - { - id: 2, - text: 'second todo', - completed: false, - }, - { - id: 2, - text: 'second todo', - completed: true, - }, - ]); - done(); - }); - - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); - store.dispatch({ - type: COMPLETE_TODO, - payload: { id: 2 }, - }); - }); - - it('should use the selector and props to get a todo', (done: DoneFn) => { - const getTodosState = createFeatureSelector( - 'todos' - ); - const getTodos = createSelector(getTodosState, todos => todos); - const getTodosById = createSelector( - getTodos, - (state: TodoAppSchema, id: number) => id, - (todos, id) => todos.find(todo => todo.id === id) - ); - - const todo$ = store.select(getTodosById, 2); - todo$ - .pipe( - take(3), - toArray() - ) - .subscribe(res => { - expect(res).toEqual([ - undefined, - { id: 2, text: 'second todo', completed: false }, - { id: 2, text: 'second todo', completed: true }, - ]); - done(); - }); - - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); - store.dispatch({ - type: COMPLETE_TODO, - payload: { id: 2 }, - }); - }); - }); - - describe('using the select operator', () => { - it('should use visibilityFilter to filter todos', () => { - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); - store.dispatch({ - type: COMPLETE_TODO, - payload: { id: state.value.todos[0].id }, - }); - - const filterVisibleTodos = (visibilityFilter: any, todos: any) => { - let predicate; - if (visibilityFilter === VisibilityFilters.SHOW_ALL) { - predicate = () => true; - } else if (visibilityFilter === VisibilityFilters.SHOW_ACTIVE) { - predicate = (todo: any) => !todo.completed; - } else { - predicate = (todo: any) => todo.completed; - } - return todos.filter(predicate); - }; - - let currentlyVisibleTodos: Todo[] = []; - - combineLatest( - store.pipe(select('visibilityFilter')), - store.pipe(select('todos')), - filterVisibleTodos - ).subscribe(visibleTodos => { - currentlyVisibleTodos = visibleTodos; - }); - - expect(currentlyVisibleTodos.length).toBe(2); - - store.dispatch({ - type: SET_VISIBILITY_FILTER, - payload: VisibilityFilters.SHOW_ACTIVE, - }); - - expect(currentlyVisibleTodos.length).toBe(1); - expect(currentlyVisibleTodos[0].completed).toBe(false); - - store.dispatch({ - type: SET_VISIBILITY_FILTER, - payload: VisibilityFilters.SHOW_COMPLETED, - }); - - expect(currentlyVisibleTodos.length).toBe(1); - expect(currentlyVisibleTodos[0].completed).toBe(true); - - store.dispatch({ type: COMPLETE_ALL_TODOS }); - - expect(currentlyVisibleTodos.length).toBe(2); - expect(currentlyVisibleTodos[0].completed).toBe(true); - expect(currentlyVisibleTodos[1].completed).toBe(true); - - store.dispatch({ - type: SET_VISIBILITY_FILTER, - payload: VisibilityFilters.SHOW_ACTIVE, - }); - - expect(currentlyVisibleTodos.length).toBe(0); - }); - - it('should use the selector and props to get a todo', (done: DoneFn) => { - const getTodosState = createFeatureSelector( - 'todos' - ); - const getTodos = createSelector(getTodosState, todos => todos); - const getTodosById = createSelector( - getTodos, - (state: TodoAppSchema, id: number) => id, - (todos, id) => todos.find(todo => todo.id === id) - ); - - const todo$ = store.pipe(select(getTodosById, 2)); - todo$ - .pipe( - take(3), - toArray() - ) - .subscribe(res => { - expect(res).toEqual([ - undefined, - { id: 2, text: 'second todo', completed: false }, - { id: 2, text: 'second todo', completed: true }, - ]); - done(); - }); - - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); - store.dispatch({ - type: COMPLETE_TODO, - payload: { id: 2 }, - }); - }); - - it('should use the props in the projector to get a todo', (done: DoneFn) => { - const getTodosState = createFeatureSelector( - 'todos' - ); - - const getTodosById = createSelector( - getTodosState, - (todos: Todo[], { id }: { id: number }) => - todos.find(todo => todo.id === id) - ); - - const todo$ = store.pipe(select(getTodosById, { id: 2 })); - todo$ - .pipe( - take(3), - toArray() - ) - .subscribe(res => { - expect(res).toEqual([ - undefined, - { - id: 2, - text: 'second todo', - completed: false, - }, - { - id: 2, - text: 'second todo', - completed: true, - }, - ]); - done(); - }); - - store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); - store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); - store.dispatch({ - type: COMPLETE_TODO, - payload: { id: 2 }, - }); - }); - }); - }); - - describe('feature state', () => { - it('should initialize properly', () => { - const initialState = { - todos: [ - { - id: 1, - text: 'do things', - completed: false, - }, - ], - visibilityFilter: VisibilityFilters.SHOW_ALL, - }; - - const reducers: ActionReducerMap = { - todos: todos, - visibilityFilter: visibilityFilter, - }; - - const featureInitialState = [{ id: 1, completed: false, text: 'Item' }]; - - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot(reducers, { initialState }), - StoreModule.forFeature('items', todos, { - initialState: featureInitialState, - }), - ], - }); - - const store: Store = TestBed.get(Store); - - let expected = [ - { - todos: initialState.todos, - visibilityFilter: initialState.visibilityFilter, - items: featureInitialState, - }, - ]; - - store.pipe(select(state => state)).subscribe(state => { - expect(state).toEqual(expected.shift()); - }); - }); - - it('should initialize properly with a partial state', () => { - const initialState = { - items: [{ id: 1, completed: false, text: 'Item' }], - }; - - const reducers: ActionReducerMap = { - todos: todos, - visibilityFilter: visibilityFilter, - }; - - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({} as any, { - initialState, - }), - StoreModule.forFeature('todos', reducers), - StoreModule.forFeature('items', todos), - ], - }); - - const store: Store = TestBed.get(Store); - - const expected = { - todos: { - todos: [], - visibilityFilter: VisibilityFilters.SHOW_ALL, - }, - items: [{ id: 1, completed: false, text: 'Item' }], - }; - - store.pipe(select(state => state)).subscribe(state => { - expect(state).toEqual(expected); - }); - }); - - it('throws if forRoot() is used more than once', (done: DoneFn) => { - @NgModule({ - imports: [StoreModule.forRoot({})], - }) - class FeatureModule {} - - TestBed.configureTestingModule({ - imports: [StoreModule.forRoot({}), RouterTestingModule.withRoutes([])], - }); - - let router: Router = TestBed.get(Router); - const loader: SpyNgModuleFactoryLoader = TestBed.get( - NgModuleFactoryLoader - ); - - loader.stubbedModules = { feature: FeatureModule }; - router.resetConfig([{ path: 'feature-path', loadChildren: 'feature' }]); - - router.navigateByUrl('/feature-path').catch((err: TypeError) => { - expect(err.message).toBe( - 'StoreModule.forRoot() called twice. Feature modules should use StoreModule.forFeature() instead.' - ); - done(); - }); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { + ActionReducer, + ActionReducerMap, + select, + Store, + StoreModule, + createFeatureSelector, + createSelector, +} from '@ngrx/store'; +import { combineLatest } from 'rxjs'; +import { first, toArray, take } from 'rxjs/operators'; + +import { INITIAL_STATE, ReducerManager, State } from '../src/private_export'; +import { + ADD_TODO, + COMPLETE_ALL_TODOS, + COMPLETE_TODO, + SET_VISIBILITY_FILTER, + todos, + visibilityFilter, + VisibilityFilters, + resetId, +} from './fixtures/todos'; +import { + RouterTestingModule, + SpyNgModuleFactoryLoader, +} from '@angular/router/testing'; +import { NgModuleFactoryLoader, NgModule } from '@angular/core'; +import { Router } from '@angular/router'; + +interface Todo { + id: number; + text: string; + completed: boolean; +} + +interface TodoAppSchema { + visibilityFilter: string; + todos: Todo[]; +} + +describe('ngRx Integration spec', () => { + describe('todo integration spec', function() { + let store: Store; + let state: State; + + const initialState = { + todos: [], + visibilityFilter: VisibilityFilters.SHOW_ALL, + }; + const reducers: ActionReducerMap = { + todos: todos, + visibilityFilter: visibilityFilter, + }; + + beforeEach(() => { + resetId(); + spyOn(reducers, 'todos').and.callThrough(); + + TestBed.configureTestingModule({ + imports: [StoreModule.forRoot(reducers, { initialState })], + }); + + store = TestBed.get(Store); + state = TestBed.get(State); + }); + + it('should successfully instantiate', () => { + expect(store).toBeDefined(); + }); + + it('should combine reducers automatically if a key/value map is provided', () => { + const action = { type: 'Test Action' }; + const reducer$: ReducerManager = TestBed.get(ReducerManager); + + reducer$.pipe(first()).subscribe((reducer: ActionReducer) => { + expect(reducer).toBeDefined(); + expect(typeof reducer === 'function').toBe(true); + + reducer({ todos: [] }, action); + + expect(reducers.todos).toHaveBeenCalledWith([], action); + }); + }); + + it('should use a provided initial state', () => { + const resolvedInitialState = TestBed.get(INITIAL_STATE); + + expect(resolvedInitialState).toEqual(initialState); + }); + + it('should start with no todos and showing all filter', () => { + expect(state.value.todos.length).toEqual(0); + expect(state.value.visibilityFilter).toEqual(VisibilityFilters.SHOW_ALL); + }); + + it('should add a todo', () => { + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + + expect(state.value.todos.length).toEqual(1); + expect(state.value.todos[0].text).toEqual('first todo'); + expect(state.value.todos[0].completed).toEqual(false); + }); + + it('should add another todo', () => { + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + + expect(state.value.todos.length).toEqual(2); + expect(state.value.todos[1].text).toEqual('second todo'); + expect(state.value.todos[1].completed).toEqual(false); + }); + + it('should complete the first todo', () => { + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: state.value.todos[0].id }, + }); + + expect(state.value.todos[0].completed).toEqual(true); + }); + + describe('using the store.select', () => { + it('should use visibilityFilter to filter todos', () => { + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: state.value.todos[0].id }, + }); + + const filterVisibleTodos = (visibilityFilter: any, todos: any) => { + let predicate; + if (visibilityFilter === VisibilityFilters.SHOW_ALL) { + predicate = () => true; + } else if (visibilityFilter === VisibilityFilters.SHOW_ACTIVE) { + predicate = (todo: any) => !todo.completed; + } else { + predicate = (todo: any) => todo.completed; + } + return todos.filter(predicate); + }; + + let currentlyVisibleTodos: Todo[] = []; + + combineLatest( + store.select('visibilityFilter'), + store.select('todos'), + filterVisibleTodos + ).subscribe(visibleTodos => { + currentlyVisibleTodos = visibleTodos; + }); + + expect(currentlyVisibleTodos.length).toBe(2); + + store.dispatch({ + type: SET_VISIBILITY_FILTER, + payload: VisibilityFilters.SHOW_ACTIVE, + }); + + expect(currentlyVisibleTodos.length).toBe(1); + expect(currentlyVisibleTodos[0].completed).toBe(false); + + store.dispatch({ + type: SET_VISIBILITY_FILTER, + payload: VisibilityFilters.SHOW_COMPLETED, + }); + + expect(currentlyVisibleTodos.length).toBe(1); + expect(currentlyVisibleTodos[0].completed).toBe(true); + + store.dispatch({ type: COMPLETE_ALL_TODOS }); + + expect(currentlyVisibleTodos.length).toBe(2); + expect(currentlyVisibleTodos[0].completed).toBe(true); + expect(currentlyVisibleTodos[1].completed).toBe(true); + + store.dispatch({ + type: SET_VISIBILITY_FILTER, + payload: VisibilityFilters.SHOW_ACTIVE, + }); + + expect(currentlyVisibleTodos.length).toBe(0); + }); + + it('should use props to get a todo', (done: DoneFn) => { + const getTodosById = createSelector( + (state: TodoAppSchema) => state.todos, + (todos: Todo[], id: number) => { + return todos.find(p => p.id === id); + } + ); + + const todo$ = store.select(getTodosById, 2); + todo$.pipe(take(3), toArray()).subscribe(res => { + expect(res).toEqual([ + undefined, + { + id: 2, + text: 'second todo', + completed: false, + }, + { + id: 2, + text: 'second todo', + completed: true, + }, + ]); + done(); + }); + + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: 2 }, + }); + }); + + it('should use the selector and props to get a todo', (done: DoneFn) => { + const getTodosState = createFeatureSelector( + 'todos' + ); + const getTodos = createSelector(getTodosState, todos => todos); + const getTodosById = createSelector( + getTodos, + (state: TodoAppSchema, id: number) => id, + (todos, id) => todos.find(todo => todo.id === id) + ); + + const todo$ = store.select(getTodosById, 2); + todo$.pipe(take(3), toArray()).subscribe(res => { + expect(res).toEqual([ + undefined, + { id: 2, text: 'second todo', completed: false }, + { id: 2, text: 'second todo', completed: true }, + ]); + done(); + }); + + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: 2 }, + }); + }); + }); + + describe('using the select operator', () => { + it('should use visibilityFilter to filter todos', () => { + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: state.value.todos[0].id }, + }); + + const filterVisibleTodos = (visibilityFilter: any, todos: any) => { + let predicate; + if (visibilityFilter === VisibilityFilters.SHOW_ALL) { + predicate = () => true; + } else if (visibilityFilter === VisibilityFilters.SHOW_ACTIVE) { + predicate = (todo: any) => !todo.completed; + } else { + predicate = (todo: any) => todo.completed; + } + return todos.filter(predicate); + }; + + let currentlyVisibleTodos: Todo[] = []; + + combineLatest( + store.pipe(select('visibilityFilter')), + store.pipe(select('todos')), + filterVisibleTodos + ).subscribe(visibleTodos => { + currentlyVisibleTodos = visibleTodos; + }); + + expect(currentlyVisibleTodos.length).toBe(2); + + store.dispatch({ + type: SET_VISIBILITY_FILTER, + payload: VisibilityFilters.SHOW_ACTIVE, + }); + + expect(currentlyVisibleTodos.length).toBe(1); + expect(currentlyVisibleTodos[0].completed).toBe(false); + + store.dispatch({ + type: SET_VISIBILITY_FILTER, + payload: VisibilityFilters.SHOW_COMPLETED, + }); + + expect(currentlyVisibleTodos.length).toBe(1); + expect(currentlyVisibleTodos[0].completed).toBe(true); + + store.dispatch({ type: COMPLETE_ALL_TODOS }); + + expect(currentlyVisibleTodos.length).toBe(2); + expect(currentlyVisibleTodos[0].completed).toBe(true); + expect(currentlyVisibleTodos[1].completed).toBe(true); + + store.dispatch({ + type: SET_VISIBILITY_FILTER, + payload: VisibilityFilters.SHOW_ACTIVE, + }); + + expect(currentlyVisibleTodos.length).toBe(0); + }); + + it('should use the selector and props to get a todo', (done: DoneFn) => { + const getTodosState = createFeatureSelector( + 'todos' + ); + const getTodos = createSelector(getTodosState, todos => todos); + const getTodosById = createSelector( + getTodos, + (state: TodoAppSchema, id: number) => id, + (todos, id) => todos.find(todo => todo.id === id) + ); + + const todo$ = store.pipe(select(getTodosById, 2)); + todo$.pipe(take(3), toArray()).subscribe(res => { + expect(res).toEqual([ + undefined, + { id: 2, text: 'second todo', completed: false }, + { id: 2, text: 'second todo', completed: true }, + ]); + done(); + }); + + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: 2 }, + }); + }); + + it('should use the props in the projector to get a todo', (done: DoneFn) => { + const getTodosState = createFeatureSelector( + 'todos' + ); + + const getTodosById = createSelector( + getTodosState, + (todos: Todo[], { id }: { id: number }) => + todos.find(todo => todo.id === id) + ); + + const todo$ = store.pipe(select(getTodosById, { id: 2 })); + todo$.pipe(take(3), toArray()).subscribe(res => { + expect(res).toEqual([ + undefined, + { + id: 2, + text: 'second todo', + completed: false, + }, + { + id: 2, + text: 'second todo', + completed: true, + }, + ]); + done(); + }); + + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: 2 }, + }); + }); + }); + }); + + describe('feature state', () => { + it('should initialize properly', () => { + const initialState = { + todos: [ + { + id: 1, + text: 'do things', + completed: false, + }, + ], + visibilityFilter: VisibilityFilters.SHOW_ALL, + }; + + const reducers: ActionReducerMap = { + todos: todos, + visibilityFilter: visibilityFilter, + }; + + const featureInitialState = [{ id: 1, completed: false, text: 'Item' }]; + + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot(reducers, { initialState }), + StoreModule.forFeature('items', todos, { + initialState: featureInitialState, + }), + ], + }); + + const store: Store = TestBed.get(Store); + + let expected = [ + { + todos: initialState.todos, + visibilityFilter: initialState.visibilityFilter, + items: featureInitialState, + }, + ]; + + store.pipe(select(state => state)).subscribe(state => { + expect(state).toEqual(expected.shift()); + }); + }); + + it('should initialize properly with a partial state', () => { + const initialState = { + items: [{ id: 1, completed: false, text: 'Item' }], + }; + + const reducers: ActionReducerMap = { + todos: todos, + visibilityFilter: visibilityFilter, + }; + + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({} as any, { + initialState, + }), + StoreModule.forFeature('todos', reducers), + StoreModule.forFeature('items', todos), + ], + }); + + const store: Store = TestBed.get(Store); + + const expected = { + todos: { + todos: [], + visibilityFilter: VisibilityFilters.SHOW_ALL, + }, + items: [{ id: 1, completed: false, text: 'Item' }], + }; + + store.pipe(select(state => state)).subscribe(state => { + expect(state).toEqual(expected); + }); + }); + + it('throws if forRoot() is used more than once', (done: DoneFn) => { + @NgModule({ + imports: [StoreModule.forRoot({})], + }) + class FeatureModule {} + + TestBed.configureTestingModule({ + imports: [StoreModule.forRoot({}), RouterTestingModule.withRoutes([])], + }); + + let router: Router = TestBed.get(Router); + const loader: SpyNgModuleFactoryLoader = TestBed.get( + NgModuleFactoryLoader + ); + + loader.stubbedModules = { feature: FeatureModule }; + router.resetConfig([{ path: 'feature-path', loadChildren: 'feature' }]); + + router.navigateByUrl('/feature-path').catch((err: TypeError) => { + expect(err.message).toBe( + 'StoreModule.forRoot() called twice. Feature modules should use StoreModule.forFeature() instead.' + ); + done(); + }); + }); + }); +}); diff --git a/modules/store/spec/modules.spec.ts b/modules/store/spec/modules.spec.ts index 1d122d9631..c874de1a02 100644 --- a/modules/store/spec/modules.spec.ts +++ b/modules/store/spec/modules.spec.ts @@ -1,219 +1,219 @@ -import { InjectionToken, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { - ActionReducer, - ActionReducerMap, - combineReducers, - Store, - StoreModule, -} from '@ngrx/store'; -import { take } from 'rxjs/operators'; - -describe(`Store Modules`, () => { - type RootState = { fruit: string }; - type FeatureAState = number; - type FeatureBState = { list: number[]; index: number }; - type State = RootState & { a: FeatureAState } & { b: FeatureBState }; - - let store: Store; - - const reducersToken = new InjectionToken>( - 'Root Reducers' - ); - - const featureToken = new InjectionToken>( - 'Feature Reducers' - ); - - // Trigger here is basically an action type used to trigger state update - const createDummyReducer = (def: T, trigger: string): ActionReducer => ( - s = def, - { type, payload }: any - ) => (type === trigger ? payload : s); - const rootFruitReducer = createDummyReducer('apple', 'fruit'); - const featureAReducer = createDummyReducer(5, 'a'); - const featureBListReducer = createDummyReducer([1, 2, 3], 'bList'); - const featureBIndexReducer = createDummyReducer(2, 'bIndex'); - const featureBReducerMap: ActionReducerMap = { - list: featureBListReducer, - index: featureBIndexReducer, - }; - - describe(`: Config`, () => { - let featureAReducerFactory: any; - let rootReducerFactory: any; - - const featureAInitial = () => ({ a: 42 }); - const rootInitial = { fruit: 'orange' }; - - beforeEach(() => { - featureAReducerFactory = jasmine - .createSpy('featureAReducerFactory') - .and.callFake((rm: any, initialState?: any) => { - return (state: any, action: any) => 4; - }); - rootReducerFactory = jasmine - .createSpy('rootReducerFactory') - .and.callFake(combineReducers); - - @NgModule({ - imports: [ - StoreModule.forFeature( - 'a', - { a: featureAReducer }, - { - initialState: featureAInitial, - reducerFactory: featureAReducerFactory, - } - ), - ], - }) - class FeatureAModule {} - - @NgModule({ - imports: [ - StoreModule.forRoot(reducersToken, { - initialState: rootInitial, - reducerFactory: rootReducerFactory, - }), - FeatureAModule, - ], - providers: [ - { - provide: reducersToken, - useValue: { fruit: rootFruitReducer }, - }, - ], - }) - class RootModule {} - - TestBed.configureTestingModule({ - imports: [RootModule], - }); - - store = TestBed.get(Store); - }); - - it(`should accept configurations`, () => { - expect(featureAReducerFactory).toHaveBeenCalledWith({ - a: featureAReducer, - }); - - expect(rootReducerFactory).toHaveBeenCalledWith({ - fruit: rootFruitReducer, - }); - }); - - it(`should should use config.reducerFactory`, () => { - store.dispatch({ type: 'fruit', payload: 'banana' }); - store.dispatch({ type: 'a', payload: 42 }); - - store.pipe(take(1)).subscribe((s: any) => { - expect(s).toEqual({ - fruit: 'banana', - a: 4, - }); - }); - }); - }); - - describe(`: With initial state`, () => { - const initialState: RootState = { fruit: 'banana' }; - const reducerMap: ActionReducerMap = { fruit: rootFruitReducer }; - const noopMetaReducer = (r: Function) => (state: any, action: any) => { - return r(state, action); - }; - - const testWithMetaReducers = (metaReducers: any[]) => () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot(reducerMap, { initialState, metaReducers }), - ], - }); - - store = TestBed.get(Store); - }); - - it('should have initial state', () => { - store.pipe(take(1)).subscribe((s: any) => { - expect(s).toEqual(initialState); - }); - }); - }; - - describe( - 'should add initial state with no meta-reducers', - testWithMetaReducers([]) - ); - - describe( - 'should add initial state with registered meta-reducers', - testWithMetaReducers([noopMetaReducer]) - ); - }); - - describe(`: Nested`, () => { - @NgModule({ - imports: [StoreModule.forFeature('a', featureAReducer)], - }) - class FeatureAModule {} - - @NgModule({ - imports: [StoreModule.forFeature('b', featureBReducerMap)], - }) - class FeatureBModule {} - - @NgModule({ - imports: [StoreModule.forFeature('c', featureToken)], - providers: [ - { - provide: featureToken, - useValue: featureBReducerMap, - }, - ], - }) - class FeatureCModule {} - - @NgModule({ - imports: [ - StoreModule.forRoot(reducersToken), - FeatureAModule, - FeatureBModule, - FeatureCModule, - ], - providers: [ - { - provide: reducersToken, - useValue: { fruit: rootFruitReducer }, - }, - ], - }) - class RootModule {} - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RootModule], - }); - - store = TestBed.get(Store); - }); - - it('should nest the child module in the root store object', () => { - store.pipe(take(1)).subscribe((state: State) => { - expect(state).toEqual({ - fruit: 'apple', - a: 5, - b: { - list: [1, 2, 3], - index: 2, - }, - c: { - list: [1, 2, 3], - index: 2, - }, - } as State); - }); - }); - }); -}); +import { InjectionToken, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { + ActionReducer, + ActionReducerMap, + combineReducers, + Store, + StoreModule, +} from '@ngrx/store'; +import { take } from 'rxjs/operators'; + +describe(`Store Modules`, () => { + type RootState = { fruit: string }; + type FeatureAState = number; + type FeatureBState = { list: number[]; index: number }; + type State = RootState & { a: FeatureAState } & { b: FeatureBState }; + + let store: Store; + + const reducersToken = new InjectionToken>( + 'Root Reducers' + ); + + const featureToken = new InjectionToken>( + 'Feature Reducers' + ); + + // Trigger here is basically an action type used to trigger state update + const createDummyReducer = (def: T, trigger: string): ActionReducer => ( + s = def, + { type, payload }: any + ) => (type === trigger ? payload : s); + const rootFruitReducer = createDummyReducer('apple', 'fruit'); + const featureAReducer = createDummyReducer(5, 'a'); + const featureBListReducer = createDummyReducer([1, 2, 3], 'bList'); + const featureBIndexReducer = createDummyReducer(2, 'bIndex'); + const featureBReducerMap: ActionReducerMap = { + list: featureBListReducer, + index: featureBIndexReducer, + }; + + describe(`: Config`, () => { + let featureAReducerFactory: any; + let rootReducerFactory: any; + + const featureAInitial = () => ({ a: 42 }); + const rootInitial = { fruit: 'orange' }; + + beforeEach(() => { + featureAReducerFactory = jasmine + .createSpy('featureAReducerFactory') + .and.callFake((rm: any, initialState?: any) => { + return (state: any, action: any) => 4; + }); + rootReducerFactory = jasmine + .createSpy('rootReducerFactory') + .and.callFake(combineReducers); + + @NgModule({ + imports: [ + StoreModule.forFeature( + 'a', + { a: featureAReducer }, + { + initialState: featureAInitial, + reducerFactory: featureAReducerFactory, + } + ), + ], + }) + class FeatureAModule {} + + @NgModule({ + imports: [ + StoreModule.forRoot(reducersToken, { + initialState: rootInitial, + reducerFactory: rootReducerFactory, + }), + FeatureAModule, + ], + providers: [ + { + provide: reducersToken, + useValue: { fruit: rootFruitReducer }, + }, + ], + }) + class RootModule {} + + TestBed.configureTestingModule({ + imports: [RootModule], + }); + + store = TestBed.get(Store); + }); + + it(`should accept configurations`, () => { + expect(featureAReducerFactory).toHaveBeenCalledWith({ + a: featureAReducer, + }); + + expect(rootReducerFactory).toHaveBeenCalledWith({ + fruit: rootFruitReducer, + }); + }); + + it(`should should use config.reducerFactory`, () => { + store.dispatch({ type: 'fruit', payload: 'banana' }); + store.dispatch({ type: 'a', payload: 42 }); + + store.pipe(take(1)).subscribe((s: any) => { + expect(s).toEqual({ + fruit: 'banana', + a: 4, + }); + }); + }); + }); + + describe(`: With initial state`, () => { + const initialState: RootState = { fruit: 'banana' }; + const reducerMap: ActionReducerMap = { fruit: rootFruitReducer }; + const noopMetaReducer = (r: Function) => (state: any, action: any) => { + return r(state, action); + }; + + const testWithMetaReducers = (metaReducers: any[]) => () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot(reducerMap, { initialState, metaReducers }), + ], + }); + + store = TestBed.get(Store); + }); + + it('should have initial state', () => { + store.pipe(take(1)).subscribe((s: any) => { + expect(s).toEqual(initialState); + }); + }); + }; + + describe( + 'should add initial state with no meta-reducers', + testWithMetaReducers([]) + ); + + describe( + 'should add initial state with registered meta-reducers', + testWithMetaReducers([noopMetaReducer]) + ); + }); + + describe(`: Nested`, () => { + @NgModule({ + imports: [StoreModule.forFeature('a', featureAReducer)], + }) + class FeatureAModule {} + + @NgModule({ + imports: [StoreModule.forFeature('b', featureBReducerMap)], + }) + class FeatureBModule {} + + @NgModule({ + imports: [StoreModule.forFeature('c', featureToken)], + providers: [ + { + provide: featureToken, + useValue: featureBReducerMap, + }, + ], + }) + class FeatureCModule {} + + @NgModule({ + imports: [ + StoreModule.forRoot(reducersToken), + FeatureAModule, + FeatureBModule, + FeatureCModule, + ], + providers: [ + { + provide: reducersToken, + useValue: { fruit: rootFruitReducer }, + }, + ], + }) + class RootModule {} + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RootModule], + }); + + store = TestBed.get(Store); + }); + + it('should nest the child module in the root store object', () => { + store.pipe(take(1)).subscribe((state: State) => { + expect(state).toEqual({ + fruit: 'apple', + a: 5, + b: { + list: [1, 2, 3], + index: 2, + }, + c: { + list: [1, 2, 3], + index: 2, + }, + } as State); + }); + }); + }); +}); diff --git a/modules/store/spec/runtime_checks.spec.ts b/modules/store/spec/runtime_checks.spec.ts index 958d7df579..b94ef3cef2 100644 --- a/modules/store/spec/runtime_checks.spec.ts +++ b/modules/store/spec/runtime_checks.spec.ts @@ -180,29 +180,23 @@ describe('Runtime checks:', () => { describe('State Serialization:', () => { const invalidAction = () => ({ type: ErrorTypes.UnserializableState }); - it( - 'should throw when enabled', - fakeAsync(() => { - const store = setupStore({ strictStateSerializability: true }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).toThrowError(/Detected unserializable state/); - }) - ); - - it( - 'should not throw when disabled', - fakeAsync(() => { - const store = setupStore({ strictStateSerializability: false }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).not.toThrow(); - }) - ); + it('should throw when enabled', fakeAsync(() => { + const store = setupStore({ strictStateSerializability: true }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).toThrowError(/Detected unserializable state/); + })); + + it('should not throw when disabled', fakeAsync(() => { + const store = setupStore({ strictStateSerializability: false }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).not.toThrow(); + })); }); describe('Action Serialization:', () => { @@ -211,29 +205,23 @@ describe('Runtime checks:', () => { invalid: new Date(), }); - it( - 'should throw when enabled', - fakeAsync(() => { - const store = setupStore({ strictActionSerializability: true }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).toThrowError(/Detected unserializable action/); - }) - ); - - it( - 'should not throw when disabled', - fakeAsync(() => { - const store = setupStore({ strictActionSerializability: false }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).not.toThrow(); - }) - ); + it('should throw when enabled', fakeAsync(() => { + const store = setupStore({ strictActionSerializability: true }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).toThrowError(/Detected unserializable action/); + })); + + it('should not throw when disabled', fakeAsync(() => { + const store = setupStore({ strictActionSerializability: false }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).not.toThrow(); + })); }); describe('State Mutations', () => { @@ -241,29 +229,23 @@ describe('Runtime checks:', () => { type: ErrorTypes.MutateState, }); - it( - 'should throw when enabled', - fakeAsync(() => { - const store = setupStore({ strictStateImmutability: true }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).toThrowError(/Cannot add property/); - }) - ); - - it( - 'should not throw when disabled', - fakeAsync(() => { - const store = setupStore({ strictStateImmutability: false }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).not.toThrow(); - }) - ); + it('should throw when enabled', fakeAsync(() => { + const store = setupStore({ strictStateImmutability: true }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).toThrowError(/Cannot add property/); + })); + + it('should not throw when disabled', fakeAsync(() => { + const store = setupStore({ strictStateImmutability: false }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).not.toThrow(); + })); }); describe('Action Mutations', () => { @@ -272,29 +254,23 @@ describe('Runtime checks:', () => { foo: 'foo', }); - it( - 'should throw when enabled', - fakeAsync(() => { - const store = setupStore({ strictActionImmutability: true }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).toThrowError(/Cannot assign to read only property/); - }) - ); - - it( - 'should not throw when disabled', - fakeAsync(() => { - const store = setupStore({ strictActionImmutability: false }); - - expect(() => { - store.dispatch(invalidAction()); - flush(); - }).not.toThrow(); - }) - ); + it('should throw when enabled', fakeAsync(() => { + const store = setupStore({ strictActionImmutability: true }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).toThrowError(/Cannot assign to read only property/); + })); + + it('should not throw when disabled', fakeAsync(() => { + const store = setupStore({ strictActionImmutability: false }); + + expect(() => { + store.dispatch(invalidAction()); + flush(); + }).not.toThrow(); + })); }); }); diff --git a/modules/store/spec/state.spec.ts b/modules/store/spec/state.spec.ts index 9693fe17fa..e04c488f99 100644 --- a/modules/store/spec/state.spec.ts +++ b/modules/store/spec/state.spec.ts @@ -1,47 +1,44 @@ -import { TestBed, fakeAsync, flush } from '@angular/core/testing'; -import { INIT, Store, StoreModule, Action } from '@ngrx/store'; - -describe('ngRx State', () => { - it('should call the reducer to scan over the dispatcher', () => { - const initialState = 123; - const reducer = jasmine.createSpy('reducer').and.returnValue(initialState); - - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot( - { key: reducer }, - { initialState: { key: initialState } } - ), - ], - }); - - TestBed.get(Store); - - expect(reducer).toHaveBeenCalledWith(initialState, { - type: INIT, - }); - }); - - it( - 'should fail synchronously', - fakeAsync(() => { - function reducer(state: any, action: Action) { - if (action.type === 'THROW_ERROR') { - throw new Error('(╯°□°)╯︵ ┻━┻'); - } - - return state; - } - - TestBed.configureTestingModule({ - imports: [StoreModule.forRoot({ reducer })], - }); - - const store = TestBed.get(Store) as Store; - expect(() => { - store.dispatch({ type: 'THROW_ERROR' }); - flush(); - }).toThrow(); - }) - ); -}); +import { TestBed, fakeAsync, flush } from '@angular/core/testing'; +import { INIT, Store, StoreModule, Action } from '@ngrx/store'; + +describe('ngRx State', () => { + it('should call the reducer to scan over the dispatcher', () => { + const initialState = 123; + const reducer = jasmine.createSpy('reducer').and.returnValue(initialState); + + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot( + { key: reducer }, + { initialState: { key: initialState } } + ), + ], + }); + + TestBed.get(Store); + + expect(reducer).toHaveBeenCalledWith(initialState, { + type: INIT, + }); + }); + + it('should fail synchronously', fakeAsync(() => { + function reducer(state: any, action: Action) { + if (action.type === 'THROW_ERROR') { + throw new Error('(╯°□°)╯︵ ┻━┻'); + } + + return state; + } + + TestBed.configureTestingModule({ + imports: [StoreModule.forRoot({ reducer })], + }); + + const store = TestBed.get(Store) as Store; + expect(() => { + store.dispatch({ type: 'THROW_ERROR' }); + flush(); + }).toThrow(); + })); +}); diff --git a/modules/store/spec/store.spec.ts b/modules/store/spec/store.spec.ts index 1920f08e1d..4acdb3a4c5 100644 --- a/modules/store/spec/store.spec.ts +++ b/modules/store/spec/store.spec.ts @@ -1,705 +1,705 @@ -import { InjectionToken } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { hot } from 'jasmine-marbles'; -import { - ActionsSubject, - ReducerManager, - Store, - StoreModule, - select, - ReducerManagerDispatcher, - UPDATE, - ActionReducer, - Action, -} from '../'; -import { StoreConfig } from '../src/store_module'; -import { combineReducers } from '../src/utils'; -import { - counterReducer, - INCREMENT, - DECREMENT, - RESET, - counterReducer2, -} from './fixtures/counter'; -import Spy = jasmine.Spy; -import { take } from 'rxjs/operators'; - -interface TestAppSchema { - counter1: number; - counter2: number; - counter3: number; - counter4?: number; -} - -describe('ngRx Store', () => { - let store: Store; - let dispatcher: ActionsSubject; - - function setup( - initialState: any = { counter1: 0, counter2: 1 }, - metaReducers: any = [] - ) { - const reducers = { - counter1: counterReducer, - counter2: counterReducer, - counter3: counterReducer, - }; - - TestBed.configureTestingModule({ - imports: [StoreModule.forRoot(reducers, { initialState, metaReducers })], - }); - - store = TestBed.get(Store); - dispatcher = TestBed.get(ActionsSubject); - } - - describe('initial state', () => { - it('should handle an initial state object', (done: any) => { - setup(); - testStoreValue({ counter1: 0, counter2: 1, counter3: 0 }, done); - }); - - it('should handle an initial state function', (done: any) => { - setup(() => ({ counter1: 0, counter2: 5 })); - testStoreValue({ counter1: 0, counter2: 5, counter3: 0 }, done); - }); - - it('should keep initial state values when state is partially initialized', (done: DoneFn) => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({} as any, { - initialState: { - feature1: { - counter1: 1, - }, - feature3: { - counter3: 3, - }, - }, - }), - StoreModule.forFeature('feature1', { counter1: counterReducer }), - StoreModule.forFeature('feature2', { counter2: counterReducer }), - StoreModule.forFeature('feature3', { counter3: counterReducer }), - ], - }); - - testStoreValue( - { - feature1: { counter1: 1 }, - feature2: { counter2: 0 }, - feature3: { counter3: 3 }, - }, - done - ); - }); - - it('should reset to initial state when undefined (root ActionReducerMap)', () => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot( - { counter1: counterReducer }, - { initialState: { counter1: 1 } } - ), - ], - }); - - testInitialState(); - }); - - it('should reset to initial state when undefined (feature ActionReducer)', () => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({}), - StoreModule.forFeature('counter1', counterReducer, { - initialState: 1, - }), - ], - }); - - testInitialState(); - }); - - it('should reset to initial state when undefined (feature ActionReducerMap)', () => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({}), - StoreModule.forFeature( - 'feature1', - { counter1: counterReducer }, - { initialState: { counter1: 1 } } - ), - ], - }); - - testInitialState('feature1'); - }); - - function testInitialState(feature?: string) { - store = TestBed.get(Store); - dispatcher = TestBed.get(ActionsSubject); - - const actionSequence = '--a--b--c--d--e--f--g'; - const stateSequence = 'i-w-----x-----y--z---'; - const actionValues = { - a: { type: INCREMENT }, - b: { type: 'OTHER' }, - c: { type: RESET }, - d: { type: 'OTHER' }, // reproduces https://github.com/ngrx/platform/issues/880 because state is falsey - e: { type: INCREMENT }, - f: { type: INCREMENT }, - g: { type: 'OTHER' }, - }; - const counterSteps = hot(actionSequence, actionValues); - counterSteps.subscribe(action => store.dispatch(action)); - - const counterStateWithString = feature - ? (store as any).select(feature, 'counter1') - : store.select('counter1'); - - const counter1Values = { i: 1, w: 2, x: 0, y: 1, z: 2 }; - - expect(counterStateWithString).toBeObservable( - hot(stateSequence, counter1Values) - ); - } - - function testStoreValue(expected: any, done: DoneFn) { - store = TestBed.get(Store); - - store.pipe(take(1)).subscribe({ - next(val) { - expect(val).toEqual(expected); - }, - error: done, - complete: done, - }); - } - }); - - describe('basic store actions', () => { - beforeEach(() => setup()); - - it('should provide an Observable Store', () => { - expect(store).toBeDefined(); - }); - - const actionSequence = '--a--b--c--d--e'; - const actionValues = { - a: { type: INCREMENT }, - b: { type: INCREMENT }, - c: { type: DECREMENT }, - d: { type: RESET }, - e: { type: INCREMENT }, - }; - - it('should let you select state with a key name', () => { - const counterSteps = hot(actionSequence, actionValues); - - counterSteps.subscribe(action => store.dispatch(action)); - - const counterStateWithString = store.pipe(select('counter1')); - - const stateSequence = 'i-v--w--x--y--z'; - const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; - - expect(counterStateWithString).toBeObservable( - hot(stateSequence, counter1Values) - ); - }); - - it('should let you select state with a selector function', () => { - const counterSteps = hot(actionSequence, actionValues); - - counterSteps.subscribe(action => store.dispatch(action)); - - const counterStateWithFunc = store.pipe(select(s => s.counter1)); - - const stateSequence = 'i-v--w--x--y--z'; - const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; - - expect(counterStateWithFunc).toBeObservable( - hot(stateSequence, counter1Values) - ); - }); - - it('should correctly lift itself', () => { - const result = store.pipe(select('counter1')); - - expect(result instanceof Store).toBe(true); - }); - - it('should increment and decrement counter1', () => { - const counterSteps = hot(actionSequence, actionValues); - - counterSteps.subscribe(action => store.dispatch(action)); - - const counterState = store.pipe(select('counter1')); - - const stateSequence = 'i-v--w--x--y--z'; - const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; - - expect(counterState).toBeObservable(hot(stateSequence, counter1Values)); - }); - - it('should increment and decrement counter1 using the dispatcher', () => { - const counterSteps = hot(actionSequence, actionValues); - - counterSteps.subscribe(action => dispatcher.next(action)); - - const counterState = store.pipe(select('counter1')); - - const stateSequence = 'i-v--w--x--y--z'; - const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; - - expect(counterState).toBeObservable(hot(stateSequence, counter1Values)); - }); - - it('should increment and decrement counter2 separately', () => { - const counterSteps = hot(actionSequence, actionValues); - - counterSteps.subscribe(action => store.dispatch(action)); - - const counter1State = store.pipe(select('counter1')); - const counter2State = store.pipe(select('counter2')); - - const stateSequence = 'i-v--w--x--y--z'; - const counter2Values = { i: 1, v: 2, w: 3, x: 2, y: 0, z: 1 }; - - expect(counter2State).toBeObservable(hot(stateSequence, counter2Values)); - }); - - it('should implement the observer interface forwarding actions and errors to the dispatcher', () => { - spyOn(dispatcher, 'next'); - spyOn(dispatcher, 'error'); - - store.next(1); - store.error(2); - - expect(dispatcher.next).toHaveBeenCalledWith(1); - expect(dispatcher.error).toHaveBeenCalledWith(2); - }); - - it('should not be completable', () => { - const storeSubscription = store.subscribe(); - const dispatcherSubscription = dispatcher.subscribe(); - - store.complete(); - dispatcher.complete(); - - expect(storeSubscription.closed).toBe(false); - expect(dispatcherSubscription.closed).toBe(false); - }); - - it('should complete if the dispatcher is destroyed', () => { - const storeSubscription = store.subscribe(); - const dispatcherSubscription = dispatcher.subscribe(); - - dispatcher.ngOnDestroy(); - - expect(dispatcherSubscription.closed).toBe(true); - }); - }); - - describe(`add/remove reducers`, () => { - let addReducerSpy: Spy; - let removeReducerSpy: Spy; - let reducerManagerDispatcherSpy: Spy; - const key = 'counter4'; - - beforeEach(() => { - setup(); - const reducerManager = TestBed.get(ReducerManager); - const dispatcher = TestBed.get(ReducerManagerDispatcher); - addReducerSpy = spyOn(reducerManager, 'addReducer').and.callThrough(); - removeReducerSpy = spyOn( - reducerManager, - 'removeReducer' - ).and.callThrough(); - reducerManagerDispatcherSpy = spyOn(dispatcher, 'next').and.callThrough(); - }); - - it(`should delegate add/remove to ReducerManager`, () => { - store.addReducer(key, counterReducer); - expect(addReducerSpy).toHaveBeenCalledWith(key, counterReducer); - - store.removeReducer(key); - expect(removeReducerSpy).toHaveBeenCalledWith(key); - }); - - it(`should work with added / removed reducers`, () => { - store.addReducer(key, counterReducer); - store.pipe(take(1)).subscribe(val => { - expect(val.counter4).toBe(0); - }); - - store.removeReducer(key); - store.dispatch({ type: INCREMENT }); - store.pipe(take(1)).subscribe(val => { - expect(val.counter4).toBeUndefined(); - }); - }); - - it('should dispatch an update reducers action when a reducer is added', () => { - store.addReducer(key, counterReducer); - expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ - type: UPDATE, - features: [key], - }); - }); - - it('should dispatch an update reducers action when a reducer is removed', () => { - store.removeReducer(key); - expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ - type: UPDATE, - features: [key], - }); - }); - }); - - describe('add/remove features', () => { - let reducerManager: ReducerManager; - let reducerManagerDispatcherSpy: Spy; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [StoreModule.forRoot({})], - }); - - reducerManager = TestBed.get(ReducerManager); - const dispatcher = TestBed.get(ReducerManagerDispatcher); - reducerManagerDispatcherSpy = spyOn(dispatcher, 'next').and.callThrough(); - }); - - it('should dispatch an update reducers action when a feature is added', () => { - reducerManager.addFeature({ - key: 'feature1', - reducers: {}, - reducerFactory: combineReducers, - }); - - expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ - type: UPDATE, - features: ['feature1'], - }); - }); - - it('should dispatch an update reducers action when multiple features are added', () => { - reducerManager.addFeatures([ - { - key: 'feature1', - reducers: {}, - reducerFactory: combineReducers, - }, - { - key: 'feature2', - reducers: {}, - reducerFactory: combineReducers, - }, - ]); - - expect(reducerManagerDispatcherSpy).toHaveBeenCalledTimes(1); - expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ - type: UPDATE, - features: ['feature1', 'feature2'], - }); - }); - - it('should dispatch an update reducers action when a feature is removed', () => { - reducerManager.removeFeature( - createFeature({ - key: 'feature1', - }) - ); - - expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ - type: UPDATE, - features: ['feature1'], - }); - }); - - it('should dispatch an update reducers action when multiple features are removed', () => { - reducerManager.removeFeatures([ - createFeature({ - key: 'feature1', - }), - createFeature({ - key: 'feature2', - }), - ]); - - expect(reducerManagerDispatcherSpy).toHaveBeenCalledTimes(1); - expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ - type: UPDATE, - features: ['feature1', 'feature2'], - }); - }); - - function createFeature({ key }: { key: string }) { - return { - key, - reducers: {}, - reducerFactory: jasmine.createSpy(`reducerFactory_${key}`), - }; - } - }); - - describe('Meta Reducers', () => { - let metaReducerContainer: any; - let metaReducerSpy1: Spy; - let metaReducerSpy2: Spy; - - beforeEach(() => { - metaReducerContainer = (function() { - function metaReducer1(reducer: ActionReducer) { - return function(state: any, action: Action) { - return reducer(state, action); - }; - } - - function metaReducer2(reducer: ActionReducer) { - return function(state: any, action: Action) { - return reducer(state, action); - }; - } - - return { - metaReducer1: metaReducer1, - metaReducer2: metaReducer2, - }; - })(); - - metaReducerSpy1 = spyOn( - metaReducerContainer, - 'metaReducer1' - ).and.callThrough(); - - metaReducerSpy2 = spyOn( - metaReducerContainer, - 'metaReducer2' - ).and.callThrough(); - }); - - it('should create a meta reducer for root and call it through', () => { - setup({}, [metaReducerContainer.metaReducer1]); - const action = { type: INCREMENT }; - store.dispatch(action); - expect(metaReducerSpy1).toHaveBeenCalled(); - }); - - it('should call two meta reducers', () => { - setup({}, [ - metaReducerContainer.metaReducer1, - metaReducerContainer.metaReducer2, - ]); - const action = { type: INCREMENT }; - store.dispatch(action); - - expect(metaReducerSpy1).toHaveBeenCalled(); - expect(metaReducerSpy2).toHaveBeenCalled(); - }); - - it('should create a meta reducer for feature and call it with the expected reducer', () => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({}), - StoreModule.forFeature('counter1', counterReducer, { - metaReducers: [metaReducerContainer.metaReducer1], - }), - StoreModule.forFeature('counter2', counterReducer2, { - metaReducers: [metaReducerContainer.metaReducer2], - }), - ], - }); - - const mockStore = TestBed.get(Store); - const action = { type: INCREMENT }; - - mockStore.dispatch(action); - - expect(metaReducerSpy1).toHaveBeenCalledWith(counterReducer); - expect(metaReducerSpy2).toHaveBeenCalledWith(counterReducer2); - }); - - it('should initial state with value', (done: DoneFn) => { - const counterInitialState = 2; - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({}), - StoreModule.forFeature( - 'counterState', - { counter: counterReducer }, - { - initialState: { counter: counterInitialState }, - metaReducers: [metaReducerContainer.metaReducer1], - } - ), - ], - }); - - const mockStore = TestBed.get(Store); - - mockStore.pipe(take(1)).subscribe({ - next(val: any) { - expect(val['counterState'].counter).toEqual(counterInitialState); - }, - error: done, - complete: done, - }); - }); - }); - - describe('Feature config token', () => { - let FEATURE_CONFIG_TOKEN: InjectionToken>; - let FEATURE_CONFIG2_TOKEN: InjectionToken>; - - beforeEach(() => { - FEATURE_CONFIG_TOKEN = new InjectionToken('Feature Config'); - FEATURE_CONFIG2_TOKEN = new InjectionToken('Feature Config2'); - }); - - it('should initial state with value', (done: DoneFn) => { - const initialState = { counter1: 1 }; - const featureKey = 'counter'; - - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({}), - StoreModule.forFeature( - featureKey, - counterReducer, - FEATURE_CONFIG_TOKEN - ), - ], - providers: [ - { - provide: FEATURE_CONFIG_TOKEN, - useValue: { initialState: initialState }, - }, - ], - }); - - const mockStore = TestBed.get(Store); - - mockStore.pipe(take(1)).subscribe({ - next(val: any) { - expect(val[featureKey]).toEqual(initialState); - }, - error: done, - complete: done, - }); - }); - - it('should initial state with value for multi features', (done: DoneFn) => { - const initialState = 1; - const initialState2 = 2; - const initialState3 = 3; - const featureKey = 'counter'; - const featureKey2 = 'counter2'; - const featureKey3 = 'counter3'; - - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({}), - StoreModule.forFeature( - featureKey, - counterReducer, - FEATURE_CONFIG_TOKEN - ), - StoreModule.forFeature( - featureKey2, - counterReducer, - FEATURE_CONFIG2_TOKEN - ), - StoreModule.forFeature(featureKey3, counterReducer, { - initialState: initialState3, - }), - ], - providers: [ - { - provide: FEATURE_CONFIG_TOKEN, - useValue: { initialState: initialState }, - }, - { - provide: FEATURE_CONFIG2_TOKEN, - useValue: { initialState: initialState2 }, - }, - ], - }); - - const mockStore = TestBed.get(Store); - - mockStore.pipe(take(1)).subscribe({ - next(val: any) { - expect(val[featureKey]).toEqual(initialState); - expect(val[featureKey2]).toEqual(initialState2); - expect(val[featureKey3]).toEqual(initialState3); - }, - error: done, - complete: done, - }); - }); - - it('should create a meta reducer with config injection token and call it with the expected reducer', () => { - const metaReducerContainer = (function() { - function metaReducer1(reducer: ActionReducer) { - return function(state: any, action: Action) { - return reducer(state, action); - }; - } - - function metaReducer2(reducer: ActionReducer) { - return function(state: any, action: Action) { - return reducer(state, action); - }; - } - - return { - metaReducer1: metaReducer1, - metaReducer2: metaReducer2, - }; - })(); - - const metaReducerSpy1 = spyOn( - metaReducerContainer, - 'metaReducer1' - ).and.callThrough(); - - const metaReducerSpy2 = spyOn( - metaReducerContainer, - 'metaReducer2' - ).and.callThrough(); - - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({}), - StoreModule.forFeature( - 'counter1', - counterReducer, - FEATURE_CONFIG_TOKEN - ), - StoreModule.forFeature( - 'counter2', - counterReducer2, - FEATURE_CONFIG2_TOKEN - ), - ], - providers: [ - { - provide: FEATURE_CONFIG_TOKEN, - useValue: { metaReducers: [metaReducerContainer.metaReducer1] }, - }, - { - provide: FEATURE_CONFIG2_TOKEN, - useValue: { metaReducers: [metaReducerContainer.metaReducer2] }, - }, - ], - }); - const mockStore = TestBed.get(Store); - const action = { type: INCREMENT }; - mockStore.dispatch(action); - - expect(metaReducerSpy1).toHaveBeenCalledWith(counterReducer); - expect(metaReducerSpy2).toHaveBeenCalledWith(counterReducer2); - }); - }); -}); +import { InjectionToken } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { hot } from 'jasmine-marbles'; +import { + ActionsSubject, + ReducerManager, + Store, + StoreModule, + select, + ReducerManagerDispatcher, + UPDATE, + ActionReducer, + Action, +} from '../'; +import { StoreConfig } from '../src/store_module'; +import { combineReducers } from '../src/utils'; +import { + counterReducer, + INCREMENT, + DECREMENT, + RESET, + counterReducer2, +} from './fixtures/counter'; +import Spy = jasmine.Spy; +import { take } from 'rxjs/operators'; + +interface TestAppSchema { + counter1: number; + counter2: number; + counter3: number; + counter4?: number; +} + +describe('ngRx Store', () => { + let store: Store; + let dispatcher: ActionsSubject; + + function setup( + initialState: any = { counter1: 0, counter2: 1 }, + metaReducers: any = [] + ) { + const reducers = { + counter1: counterReducer, + counter2: counterReducer, + counter3: counterReducer, + }; + + TestBed.configureTestingModule({ + imports: [StoreModule.forRoot(reducers, { initialState, metaReducers })], + }); + + store = TestBed.get(Store); + dispatcher = TestBed.get(ActionsSubject); + } + + describe('initial state', () => { + it('should handle an initial state object', (done: any) => { + setup(); + testStoreValue({ counter1: 0, counter2: 1, counter3: 0 }, done); + }); + + it('should handle an initial state function', (done: any) => { + setup(() => ({ counter1: 0, counter2: 5 })); + testStoreValue({ counter1: 0, counter2: 5, counter3: 0 }, done); + }); + + it('should keep initial state values when state is partially initialized', (done: DoneFn) => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({} as any, { + initialState: { + feature1: { + counter1: 1, + }, + feature3: { + counter3: 3, + }, + }, + }), + StoreModule.forFeature('feature1', { counter1: counterReducer }), + StoreModule.forFeature('feature2', { counter2: counterReducer }), + StoreModule.forFeature('feature3', { counter3: counterReducer }), + ], + }); + + testStoreValue( + { + feature1: { counter1: 1 }, + feature2: { counter2: 0 }, + feature3: { counter3: 3 }, + }, + done + ); + }); + + it('should reset to initial state when undefined (root ActionReducerMap)', () => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot( + { counter1: counterReducer }, + { initialState: { counter1: 1 } } + ), + ], + }); + + testInitialState(); + }); + + it('should reset to initial state when undefined (feature ActionReducer)', () => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({}), + StoreModule.forFeature('counter1', counterReducer, { + initialState: 1, + }), + ], + }); + + testInitialState(); + }); + + it('should reset to initial state when undefined (feature ActionReducerMap)', () => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({}), + StoreModule.forFeature( + 'feature1', + { counter1: counterReducer }, + { initialState: { counter1: 1 } } + ), + ], + }); + + testInitialState('feature1'); + }); + + function testInitialState(feature?: string) { + store = TestBed.get(Store); + dispatcher = TestBed.get(ActionsSubject); + + const actionSequence = '--a--b--c--d--e--f--g'; + const stateSequence = 'i-w-----x-----y--z---'; + const actionValues = { + a: { type: INCREMENT }, + b: { type: 'OTHER' }, + c: { type: RESET }, + d: { type: 'OTHER' }, // reproduces https://github.com/ngrx/platform/issues/880 because state is falsey + e: { type: INCREMENT }, + f: { type: INCREMENT }, + g: { type: 'OTHER' }, + }; + const counterSteps = hot(actionSequence, actionValues); + counterSteps.subscribe(action => store.dispatch(action)); + + const counterStateWithString = feature + ? (store as any).select(feature, 'counter1') + : store.select('counter1'); + + const counter1Values = { i: 1, w: 2, x: 0, y: 1, z: 2 }; + + expect(counterStateWithString).toBeObservable( + hot(stateSequence, counter1Values) + ); + } + + function testStoreValue(expected: any, done: DoneFn) { + store = TestBed.get(Store); + + store.pipe(take(1)).subscribe({ + next(val) { + expect(val).toEqual(expected); + }, + error: done, + complete: done, + }); + } + }); + + describe('basic store actions', () => { + beforeEach(() => setup()); + + it('should provide an Observable Store', () => { + expect(store).toBeDefined(); + }); + + const actionSequence = '--a--b--c--d--e'; + const actionValues = { + a: { type: INCREMENT }, + b: { type: INCREMENT }, + c: { type: DECREMENT }, + d: { type: RESET }, + e: { type: INCREMENT }, + }; + + it('should let you select state with a key name', () => { + const counterSteps = hot(actionSequence, actionValues); + + counterSteps.subscribe(action => store.dispatch(action)); + + const counterStateWithString = store.pipe(select('counter1')); + + const stateSequence = 'i-v--w--x--y--z'; + const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; + + expect(counterStateWithString).toBeObservable( + hot(stateSequence, counter1Values) + ); + }); + + it('should let you select state with a selector function', () => { + const counterSteps = hot(actionSequence, actionValues); + + counterSteps.subscribe(action => store.dispatch(action)); + + const counterStateWithFunc = store.pipe(select(s => s.counter1)); + + const stateSequence = 'i-v--w--x--y--z'; + const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; + + expect(counterStateWithFunc).toBeObservable( + hot(stateSequence, counter1Values) + ); + }); + + it('should correctly lift itself', () => { + const result = store.pipe(select('counter1')); + + expect(result instanceof Store).toBe(true); + }); + + it('should increment and decrement counter1', () => { + const counterSteps = hot(actionSequence, actionValues); + + counterSteps.subscribe(action => store.dispatch(action)); + + const counterState = store.pipe(select('counter1')); + + const stateSequence = 'i-v--w--x--y--z'; + const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; + + expect(counterState).toBeObservable(hot(stateSequence, counter1Values)); + }); + + it('should increment and decrement counter1 using the dispatcher', () => { + const counterSteps = hot(actionSequence, actionValues); + + counterSteps.subscribe(action => dispatcher.next(action)); + + const counterState = store.pipe(select('counter1')); + + const stateSequence = 'i-v--w--x--y--z'; + const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 }; + + expect(counterState).toBeObservable(hot(stateSequence, counter1Values)); + }); + + it('should increment and decrement counter2 separately', () => { + const counterSteps = hot(actionSequence, actionValues); + + counterSteps.subscribe(action => store.dispatch(action)); + + const counter1State = store.pipe(select('counter1')); + const counter2State = store.pipe(select('counter2')); + + const stateSequence = 'i-v--w--x--y--z'; + const counter2Values = { i: 1, v: 2, w: 3, x: 2, y: 0, z: 1 }; + + expect(counter2State).toBeObservable(hot(stateSequence, counter2Values)); + }); + + it('should implement the observer interface forwarding actions and errors to the dispatcher', () => { + spyOn(dispatcher, 'next'); + spyOn(dispatcher, 'error'); + + store.next(1); + store.error(2); + + expect(dispatcher.next).toHaveBeenCalledWith(1); + expect(dispatcher.error).toHaveBeenCalledWith(2); + }); + + it('should not be completable', () => { + const storeSubscription = store.subscribe(); + const dispatcherSubscription = dispatcher.subscribe(); + + store.complete(); + dispatcher.complete(); + + expect(storeSubscription.closed).toBe(false); + expect(dispatcherSubscription.closed).toBe(false); + }); + + it('should complete if the dispatcher is destroyed', () => { + const storeSubscription = store.subscribe(); + const dispatcherSubscription = dispatcher.subscribe(); + + dispatcher.ngOnDestroy(); + + expect(dispatcherSubscription.closed).toBe(true); + }); + }); + + describe(`add/remove reducers`, () => { + let addReducerSpy: Spy; + let removeReducerSpy: Spy; + let reducerManagerDispatcherSpy: Spy; + const key = 'counter4'; + + beforeEach(() => { + setup(); + const reducerManager = TestBed.get(ReducerManager); + const dispatcher = TestBed.get(ReducerManagerDispatcher); + addReducerSpy = spyOn(reducerManager, 'addReducer').and.callThrough(); + removeReducerSpy = spyOn( + reducerManager, + 'removeReducer' + ).and.callThrough(); + reducerManagerDispatcherSpy = spyOn(dispatcher, 'next').and.callThrough(); + }); + + it(`should delegate add/remove to ReducerManager`, () => { + store.addReducer(key, counterReducer); + expect(addReducerSpy).toHaveBeenCalledWith(key, counterReducer); + + store.removeReducer(key); + expect(removeReducerSpy).toHaveBeenCalledWith(key); + }); + + it(`should work with added / removed reducers`, () => { + store.addReducer(key, counterReducer); + store.pipe(take(1)).subscribe(val => { + expect(val.counter4).toBe(0); + }); + + store.removeReducer(key); + store.dispatch({ type: INCREMENT }); + store.pipe(take(1)).subscribe(val => { + expect(val.counter4).toBeUndefined(); + }); + }); + + it('should dispatch an update reducers action when a reducer is added', () => { + store.addReducer(key, counterReducer); + expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ + type: UPDATE, + features: [key], + }); + }); + + it('should dispatch an update reducers action when a reducer is removed', () => { + store.removeReducer(key); + expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ + type: UPDATE, + features: [key], + }); + }); + }); + + describe('add/remove features', () => { + let reducerManager: ReducerManager; + let reducerManagerDispatcherSpy: Spy; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [StoreModule.forRoot({})], + }); + + reducerManager = TestBed.get(ReducerManager); + const dispatcher = TestBed.get(ReducerManagerDispatcher); + reducerManagerDispatcherSpy = spyOn(dispatcher, 'next').and.callThrough(); + }); + + it('should dispatch an update reducers action when a feature is added', () => { + reducerManager.addFeature({ + key: 'feature1', + reducers: {}, + reducerFactory: combineReducers, + }); + + expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ + type: UPDATE, + features: ['feature1'], + }); + }); + + it('should dispatch an update reducers action when multiple features are added', () => { + reducerManager.addFeatures([ + { + key: 'feature1', + reducers: {}, + reducerFactory: combineReducers, + }, + { + key: 'feature2', + reducers: {}, + reducerFactory: combineReducers, + }, + ]); + + expect(reducerManagerDispatcherSpy).toHaveBeenCalledTimes(1); + expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ + type: UPDATE, + features: ['feature1', 'feature2'], + }); + }); + + it('should dispatch an update reducers action when a feature is removed', () => { + reducerManager.removeFeature( + createFeature({ + key: 'feature1', + }) + ); + + expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ + type: UPDATE, + features: ['feature1'], + }); + }); + + it('should dispatch an update reducers action when multiple features are removed', () => { + reducerManager.removeFeatures([ + createFeature({ + key: 'feature1', + }), + createFeature({ + key: 'feature2', + }), + ]); + + expect(reducerManagerDispatcherSpy).toHaveBeenCalledTimes(1); + expect(reducerManagerDispatcherSpy).toHaveBeenCalledWith({ + type: UPDATE, + features: ['feature1', 'feature2'], + }); + }); + + function createFeature({ key }: { key: string }) { + return { + key, + reducers: {}, + reducerFactory: jasmine.createSpy(`reducerFactory_${key}`), + }; + } + }); + + describe('Meta Reducers', () => { + let metaReducerContainer: any; + let metaReducerSpy1: Spy; + let metaReducerSpy2: Spy; + + beforeEach(() => { + metaReducerContainer = (function() { + function metaReducer1(reducer: ActionReducer) { + return function(state: any, action: Action) { + return reducer(state, action); + }; + } + + function metaReducer2(reducer: ActionReducer) { + return function(state: any, action: Action) { + return reducer(state, action); + }; + } + + return { + metaReducer1: metaReducer1, + metaReducer2: metaReducer2, + }; + })(); + + metaReducerSpy1 = spyOn( + metaReducerContainer, + 'metaReducer1' + ).and.callThrough(); + + metaReducerSpy2 = spyOn( + metaReducerContainer, + 'metaReducer2' + ).and.callThrough(); + }); + + it('should create a meta reducer for root and call it through', () => { + setup({}, [metaReducerContainer.metaReducer1]); + const action = { type: INCREMENT }; + store.dispatch(action); + expect(metaReducerSpy1).toHaveBeenCalled(); + }); + + it('should call two meta reducers', () => { + setup({}, [ + metaReducerContainer.metaReducer1, + metaReducerContainer.metaReducer2, + ]); + const action = { type: INCREMENT }; + store.dispatch(action); + + expect(metaReducerSpy1).toHaveBeenCalled(); + expect(metaReducerSpy2).toHaveBeenCalled(); + }); + + it('should create a meta reducer for feature and call it with the expected reducer', () => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({}), + StoreModule.forFeature('counter1', counterReducer, { + metaReducers: [metaReducerContainer.metaReducer1], + }), + StoreModule.forFeature('counter2', counterReducer2, { + metaReducers: [metaReducerContainer.metaReducer2], + }), + ], + }); + + const mockStore = TestBed.get(Store); + const action = { type: INCREMENT }; + + mockStore.dispatch(action); + + expect(metaReducerSpy1).toHaveBeenCalledWith(counterReducer); + expect(metaReducerSpy2).toHaveBeenCalledWith(counterReducer2); + }); + + it('should initial state with value', (done: DoneFn) => { + const counterInitialState = 2; + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({}), + StoreModule.forFeature( + 'counterState', + { counter: counterReducer }, + { + initialState: { counter: counterInitialState }, + metaReducers: [metaReducerContainer.metaReducer1], + } + ), + ], + }); + + const mockStore = TestBed.get(Store); + + mockStore.pipe(take(1)).subscribe({ + next(val: any) { + expect(val['counterState'].counter).toEqual(counterInitialState); + }, + error: done, + complete: done, + }); + }); + }); + + describe('Feature config token', () => { + let FEATURE_CONFIG_TOKEN: InjectionToken>; + let FEATURE_CONFIG2_TOKEN: InjectionToken>; + + beforeEach(() => { + FEATURE_CONFIG_TOKEN = new InjectionToken('Feature Config'); + FEATURE_CONFIG2_TOKEN = new InjectionToken('Feature Config2'); + }); + + it('should initial state with value', (done: DoneFn) => { + const initialState = { counter1: 1 }; + const featureKey = 'counter'; + + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({}), + StoreModule.forFeature( + featureKey, + counterReducer, + FEATURE_CONFIG_TOKEN + ), + ], + providers: [ + { + provide: FEATURE_CONFIG_TOKEN, + useValue: { initialState: initialState }, + }, + ], + }); + + const mockStore = TestBed.get(Store); + + mockStore.pipe(take(1)).subscribe({ + next(val: any) { + expect(val[featureKey]).toEqual(initialState); + }, + error: done, + complete: done, + }); + }); + + it('should initial state with value for multi features', (done: DoneFn) => { + const initialState = 1; + const initialState2 = 2; + const initialState3 = 3; + const featureKey = 'counter'; + const featureKey2 = 'counter2'; + const featureKey3 = 'counter3'; + + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({}), + StoreModule.forFeature( + featureKey, + counterReducer, + FEATURE_CONFIG_TOKEN + ), + StoreModule.forFeature( + featureKey2, + counterReducer, + FEATURE_CONFIG2_TOKEN + ), + StoreModule.forFeature(featureKey3, counterReducer, { + initialState: initialState3, + }), + ], + providers: [ + { + provide: FEATURE_CONFIG_TOKEN, + useValue: { initialState: initialState }, + }, + { + provide: FEATURE_CONFIG2_TOKEN, + useValue: { initialState: initialState2 }, + }, + ], + }); + + const mockStore = TestBed.get(Store); + + mockStore.pipe(take(1)).subscribe({ + next(val: any) { + expect(val[featureKey]).toEqual(initialState); + expect(val[featureKey2]).toEqual(initialState2); + expect(val[featureKey3]).toEqual(initialState3); + }, + error: done, + complete: done, + }); + }); + + it('should create a meta reducer with config injection token and call it with the expected reducer', () => { + const metaReducerContainer = (function() { + function metaReducer1(reducer: ActionReducer) { + return function(state: any, action: Action) { + return reducer(state, action); + }; + } + + function metaReducer2(reducer: ActionReducer) { + return function(state: any, action: Action) { + return reducer(state, action); + }; + } + + return { + metaReducer1: metaReducer1, + metaReducer2: metaReducer2, + }; + })(); + + const metaReducerSpy1 = spyOn( + metaReducerContainer, + 'metaReducer1' + ).and.callThrough(); + + const metaReducerSpy2 = spyOn( + metaReducerContainer, + 'metaReducer2' + ).and.callThrough(); + + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({}), + StoreModule.forFeature( + 'counter1', + counterReducer, + FEATURE_CONFIG_TOKEN + ), + StoreModule.forFeature( + 'counter2', + counterReducer2, + FEATURE_CONFIG2_TOKEN + ), + ], + providers: [ + { + provide: FEATURE_CONFIG_TOKEN, + useValue: { metaReducers: [metaReducerContainer.metaReducer1] }, + }, + { + provide: FEATURE_CONFIG2_TOKEN, + useValue: { metaReducers: [metaReducerContainer.metaReducer2] }, + }, + ], + }); + const mockStore = TestBed.get(Store); + const action = { type: INCREMENT }; + mockStore.dispatch(action); + + expect(metaReducerSpy1).toHaveBeenCalledWith(counterReducer); + expect(metaReducerSpy2).toHaveBeenCalledWith(counterReducer2); + }); + }); +}); diff --git a/modules/store/testing/spec/mock_store.spec.ts b/modules/store/testing/spec/mock_store.spec.ts index 650733a49c..7f401b11ff 100644 --- a/modules/store/testing/spec/mock_store.spec.ts +++ b/modules/store/testing/spec/mock_store.spec.ts @@ -1,385 +1,385 @@ -import { TestBed, ComponentFixture } from '@angular/core/testing'; -import { skip, take, tap } from 'rxjs/operators'; -import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { - Store, - createSelector, - select, - StoreModule, - MemoizedSelector, - createFeatureSelector, -} from '@ngrx/store'; -import { INCREMENT } from '../../spec/fixtures/counter'; -import { Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; -import { By } from '@angular/platform-browser'; - -interface TestAppSchema { - counter1: number; - counter2: number; - counter3: number; - counter4?: number; -} - -describe('Mock Store', () => { - let mockStore: MockStore; - const initialState = { counter1: 0, counter2: 1, counter4: 3 }; - const stringSelector = 'counter4'; - const memoizedSelector = createSelector( - () => initialState, - state => state.counter4 - ); - const selectorWithPropMocked = createSelector( - () => initialState, - (state: typeof initialState, add: number) => state.counter4 + add - ); - - const selectorWithProp = createSelector( - () => initialState, - (state: typeof initialState, add: number) => state.counter4 + add - ); - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - provideMockStore({ - initialState, - selectors: [ - { selector: stringSelector, value: 87 }, - { selector: memoizedSelector, value: 98 }, - { selector: selectorWithPropMocked, value: 99 }, - ], - }), - ], - }); - - mockStore = TestBed.get(Store); - }); - - afterEach(() => { - memoizedSelector.release(); - selectorWithProp.release(); - selectorWithPropMocked.release(); - mockStore.resetSelectors(); - }); - - it('should set the initial state to a mocked one', (done: DoneFn) => { - const fixedState = { - counter1: 17, - counter2: 11, - counter3: 25, - }; - mockStore.setState(fixedState); - mockStore.pipe(take(1)).subscribe({ - next(val) { - expect(val).toEqual(fixedState); - }, - error: done.fail, - complete: done, - }); - }); - - it('should allow tracing dispatched actions', () => { - const action = { type: INCREMENT }; - mockStore.scannedActions$ - .pipe(skip(1)) - .subscribe(scannedAction => expect(scannedAction).toEqual(action)); - mockStore.dispatch(action); - }); - - it('should allow mocking of store.select with string selector using provideMockStore', () => { - const expectedValue = 87; - - mockStore - .select(stringSelector) - .subscribe(result => expect(result).toBe(expectedValue)); - }); - - it('should allow mocking of store.select with a memoized selector using provideMockStore', () => { - const expectedValue = 98; - - mockStore - .select(memoizedSelector) - .subscribe(result => expect(result).toBe(expectedValue)); - }); - - it('should allow mocking of store.pipe(select()) with a memoized selector using provideMockStore', () => { - const expectedValue = 98; - - mockStore - .pipe(select(memoizedSelector)) - .subscribe(result => expect(result).toBe(expectedValue)); - }); - - it('should allow mocking of store.select with a memoized selector with Prop using provideMockStore', () => { - const expectedValue = 99; - - mockStore - .select(selectorWithPropMocked, 100) - .subscribe(result => expect(result).toBe(expectedValue)); - }); - - it('should allow mocking of store.pipe(select()) with a memoized selector with Prop using provideMockStore', () => { - const expectedValue = 99; - - mockStore - .pipe(select(selectorWithPropMocked, 200)) - .subscribe(result => expect(result).toBe(expectedValue)); - }); - - it('should allow mocking of store.select with string selector using overrideSelector', () => { - const mockValue = 5; - - mockStore.overrideSelector('counter1', mockValue); - - mockStore - .select('counter1') - .subscribe(result => expect(result).toBe(mockValue)); - }); - - it('should allow mocking of store.select with a memoized selector using overrideSelector', () => { - const mockValue = 5; - const selector = createSelector( - () => initialState, - state => state.counter1 - ); - - mockStore.overrideSelector(selector, mockValue); - - mockStore - .select(selector) - .subscribe(result => expect(result).toBe(mockValue)); - }); - - it('should allow mocking of store.pipe(select()) with a memoized selector using overrideSelector', () => { - const mockValue = 5; - const selector = createSelector( - () => initialState, - state => state.counter2 - ); - - mockStore.overrideSelector(selector, mockValue); - - mockStore - .pipe(select(selector)) - .subscribe(result => expect(result).toBe(mockValue)); - }); - - it('should allow mocking of store.select with a memoized selector with Prop using overrideSelector', () => { - const mockValue = 100; - - mockStore.overrideSelector(selectorWithProp, mockValue); - - mockStore - .select(selectorWithProp, 200) - .subscribe(result => expect(result).toBe(mockValue)); - }); - - it('should allow mocking of store.pipe(select()) with a memoized selector with Prop using overrideSelector', () => { - const mockValue = 1000; - - mockStore.overrideSelector(selectorWithProp, mockValue); - - mockStore - .pipe(select(selectorWithProp, 200)) - .subscribe(result => expect(result).toBe(mockValue)); - }); - - it('should pass through unmocked selectors with Props using store.pipe(select())', () => { - const selectorWithProp = createSelector( - () => initialState, - (state: typeof initialState, add: number) => state.counter4 + add - ); - - mockStore - .pipe(select(selectorWithProp, 6)) - .subscribe(result => expect(result).toBe(9)); - }); - - it('should pass through unmocked selectors with Props using store.select', () => { - const selectorWithProp = createSelector( - () => initialState, - (state: typeof initialState, add: number) => state.counter4 + add - ); - - (mockStore as Store<{}>) - .select(selectorWithProp, 7) - .subscribe(result => expect(result).toBe(10)); - }); - - it('should pass through unmocked selectors', () => { - const mockValue = 5; - const selector = createSelector( - () => initialState, - state => state.counter1 - ); - const selector2 = createSelector( - () => initialState, - state => state.counter2 - ); - const selector3 = createSelector( - selector, - selector2, - (sel1, sel2) => sel1 + sel2 - ); - - mockStore.overrideSelector(selector, mockValue); - - mockStore - .pipe(select(selector2)) - .subscribe(result => expect(result).toBe(1)); - mockStore - .pipe(select(selector3)) - .subscribe(result => expect(result).toBe(6)); - }); - - it('should allow you reset mocked selectors', () => { - const mockValue = 5; - const selector = createSelector( - () => initialState, - state => state.counter1 - ); - const selector2 = createSelector( - () => initialState, - state => state.counter2 - ); - const selector3 = createSelector( - selector, - selector2, - (sel1, sel2) => sel1 + sel2 - ); - - mockStore - .pipe(select(selector3)) - .subscribe(result => expect(result).toBe(1)); - - mockStore.overrideSelector(selector, mockValue); - mockStore.overrideSelector(selector2, mockValue); - selector3.release(); - - mockStore - .pipe(select(selector3)) - .subscribe(result => expect(result).toBe(10)); - - mockStore.resetSelectors(); - selector3.release(); - - mockStore - .pipe(select(selector3)) - .subscribe(result => expect(result).toBe(1)); - }); -}); - -describe('Refreshing state', () => { - type TodoState = { - items: { name: string; done: boolean }[]; - }; - const selectTodosState = createFeatureSelector('todos'); - const todos = createSelector(selectTodosState, todos => todos.items); - const getTodoItems = (elSelector: string) => - fixture.debugElement.queryAll(By.css(elSelector)); - let mockStore: MockStore; - let mockSelector: MemoizedSelector; - const initialTodos = [{ name: 'aaa', done: false }]; - let fixture: ComponentFixture; - - @Component({ - selector: 'app-todos', - template: ` -
    -
  • - {{ todo.name }} -
  • - -

    - {{ todo.name }} -

    -
- `, - }) - class TodosComponent { - todos: Observable = this.store.pipe(select(todos)); - todosSelect: Observable = this.store.select(todos); - - constructor(private store: Store<{}>) {} - } - - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [TodosComponent], - providers: [provideMockStore()], - }).compileComponents(); - - mockStore = TestBed.get(Store); - mockSelector = mockStore.overrideSelector(todos, initialTodos); - - fixture = TestBed.createComponent(TodosComponent); - fixture.detectChanges(); - }); - - it('should work with store and select operator', () => { - const newTodos = [{ name: 'bbb', done: true }]; - mockSelector.setResult(newTodos); - mockStore.refreshState(); - - fixture.detectChanges(); - - expect(getTodoItems('li').length).toBe(1); - expect(getTodoItems('li')[0].nativeElement.textContent.trim()).toBe('bbb'); - }); - - it('should work with store.select method', () => { - const newTodos = [{ name: 'bbb', done: true }]; - mockSelector.setResult(newTodos); - mockStore.refreshState(); - - fixture.detectChanges(); - - expect(getTodoItems('p').length).toBe(1); - expect(getTodoItems('p')[0].nativeElement.textContent.trim()).toBe('bbb'); - }); -}); - -describe('Cleans up after each test', () => { - const selectData = createSelector( - (state: any) => state, - state => state.value - ); - - it('should return the mocked selectors value', (done: DoneFn) => { - TestBed.configureTestingModule({ - providers: [ - provideMockStore({ - initialState: { - value: 100, - }, - selectors: [{ selector: selectData, value: 200 }], - }), - ], - }); - - const store = TestBed.get>(Store) as Store; - store.pipe(select(selectData)).subscribe(v => { - expect(v).toBe(200); - done(); - }); - }); - - it('should return the real value', (done: DoneFn) => { - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({} as any, { - initialState: { - value: 300, - }, - }), - ], - }); - - const store = TestBed.get>(Store) as Store; - store.pipe(select(selectData)).subscribe(v => { - expect(v).toBe(300); - done(); - }); - }); -}); +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { skip, take, tap } from 'rxjs/operators'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { + Store, + createSelector, + select, + StoreModule, + MemoizedSelector, + createFeatureSelector, +} from '@ngrx/store'; +import { INCREMENT } from '../../spec/fixtures/counter'; +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { By } from '@angular/platform-browser'; + +interface TestAppSchema { + counter1: number; + counter2: number; + counter3: number; + counter4?: number; +} + +describe('Mock Store', () => { + let mockStore: MockStore; + const initialState = { counter1: 0, counter2: 1, counter4: 3 }; + const stringSelector = 'counter4'; + const memoizedSelector = createSelector( + () => initialState, + state => state.counter4 + ); + const selectorWithPropMocked = createSelector( + () => initialState, + (state: typeof initialState, add: number) => state.counter4 + add + ); + + const selectorWithProp = createSelector( + () => initialState, + (state: typeof initialState, add: number) => state.counter4 + add + ); + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideMockStore({ + initialState, + selectors: [ + { selector: stringSelector, value: 87 }, + { selector: memoizedSelector, value: 98 }, + { selector: selectorWithPropMocked, value: 99 }, + ], + }), + ], + }); + + mockStore = TestBed.get(Store); + }); + + afterEach(() => { + memoizedSelector.release(); + selectorWithProp.release(); + selectorWithPropMocked.release(); + mockStore.resetSelectors(); + }); + + it('should set the initial state to a mocked one', (done: DoneFn) => { + const fixedState = { + counter1: 17, + counter2: 11, + counter3: 25, + }; + mockStore.setState(fixedState); + mockStore.pipe(take(1)).subscribe({ + next(val) { + expect(val).toEqual(fixedState); + }, + error: done.fail, + complete: done, + }); + }); + + it('should allow tracing dispatched actions', () => { + const action = { type: INCREMENT }; + mockStore.scannedActions$ + .pipe(skip(1)) + .subscribe(scannedAction => expect(scannedAction).toEqual(action)); + mockStore.dispatch(action); + }); + + it('should allow mocking of store.select with string selector using provideMockStore', () => { + const expectedValue = 87; + + mockStore + .select(stringSelector) + .subscribe(result => expect(result).toBe(expectedValue)); + }); + + it('should allow mocking of store.select with a memoized selector using provideMockStore', () => { + const expectedValue = 98; + + mockStore + .select(memoizedSelector) + .subscribe(result => expect(result).toBe(expectedValue)); + }); + + it('should allow mocking of store.pipe(select()) with a memoized selector using provideMockStore', () => { + const expectedValue = 98; + + mockStore + .pipe(select(memoizedSelector)) + .subscribe(result => expect(result).toBe(expectedValue)); + }); + + it('should allow mocking of store.select with a memoized selector with Prop using provideMockStore', () => { + const expectedValue = 99; + + mockStore + .select(selectorWithPropMocked, 100) + .subscribe(result => expect(result).toBe(expectedValue)); + }); + + it('should allow mocking of store.pipe(select()) with a memoized selector with Prop using provideMockStore', () => { + const expectedValue = 99; + + mockStore + .pipe(select(selectorWithPropMocked, 200)) + .subscribe(result => expect(result).toBe(expectedValue)); + }); + + it('should allow mocking of store.select with string selector using overrideSelector', () => { + const mockValue = 5; + + mockStore.overrideSelector('counter1', mockValue); + + mockStore + .select('counter1') + .subscribe(result => expect(result).toBe(mockValue)); + }); + + it('should allow mocking of store.select with a memoized selector using overrideSelector', () => { + const mockValue = 5; + const selector = createSelector( + () => initialState, + state => state.counter1 + ); + + mockStore.overrideSelector(selector, mockValue); + + mockStore + .select(selector) + .subscribe(result => expect(result).toBe(mockValue)); + }); + + it('should allow mocking of store.pipe(select()) with a memoized selector using overrideSelector', () => { + const mockValue = 5; + const selector = createSelector( + () => initialState, + state => state.counter2 + ); + + mockStore.overrideSelector(selector, mockValue); + + mockStore + .pipe(select(selector)) + .subscribe(result => expect(result).toBe(mockValue)); + }); + + it('should allow mocking of store.select with a memoized selector with Prop using overrideSelector', () => { + const mockValue = 100; + + mockStore.overrideSelector(selectorWithProp, mockValue); + + mockStore + .select(selectorWithProp, 200) + .subscribe(result => expect(result).toBe(mockValue)); + }); + + it('should allow mocking of store.pipe(select()) with a memoized selector with Prop using overrideSelector', () => { + const mockValue = 1000; + + mockStore.overrideSelector(selectorWithProp, mockValue); + + mockStore + .pipe(select(selectorWithProp, 200)) + .subscribe(result => expect(result).toBe(mockValue)); + }); + + it('should pass through unmocked selectors with Props using store.pipe(select())', () => { + const selectorWithProp = createSelector( + () => initialState, + (state: typeof initialState, add: number) => state.counter4 + add + ); + + mockStore + .pipe(select(selectorWithProp, 6)) + .subscribe(result => expect(result).toBe(9)); + }); + + it('should pass through unmocked selectors with Props using store.select', () => { + const selectorWithProp = createSelector( + () => initialState, + (state: typeof initialState, add: number) => state.counter4 + add + ); + + (mockStore as Store<{}>) + .select(selectorWithProp, 7) + .subscribe(result => expect(result).toBe(10)); + }); + + it('should pass through unmocked selectors', () => { + const mockValue = 5; + const selector = createSelector( + () => initialState, + state => state.counter1 + ); + const selector2 = createSelector( + () => initialState, + state => state.counter2 + ); + const selector3 = createSelector( + selector, + selector2, + (sel1, sel2) => sel1 + sel2 + ); + + mockStore.overrideSelector(selector, mockValue); + + mockStore + .pipe(select(selector2)) + .subscribe(result => expect(result).toBe(1)); + mockStore + .pipe(select(selector3)) + .subscribe(result => expect(result).toBe(6)); + }); + + it('should allow you reset mocked selectors', () => { + const mockValue = 5; + const selector = createSelector( + () => initialState, + state => state.counter1 + ); + const selector2 = createSelector( + () => initialState, + state => state.counter2 + ); + const selector3 = createSelector( + selector, + selector2, + (sel1, sel2) => sel1 + sel2 + ); + + mockStore + .pipe(select(selector3)) + .subscribe(result => expect(result).toBe(1)); + + mockStore.overrideSelector(selector, mockValue); + mockStore.overrideSelector(selector2, mockValue); + selector3.release(); + + mockStore + .pipe(select(selector3)) + .subscribe(result => expect(result).toBe(10)); + + mockStore.resetSelectors(); + selector3.release(); + + mockStore + .pipe(select(selector3)) + .subscribe(result => expect(result).toBe(1)); + }); +}); + +describe('Refreshing state', () => { + type TodoState = { + items: { name: string; done: boolean }[]; + }; + const selectTodosState = createFeatureSelector('todos'); + const todos = createSelector(selectTodosState, todos => todos.items); + const getTodoItems = (elSelector: string) => + fixture.debugElement.queryAll(By.css(elSelector)); + let mockStore: MockStore; + let mockSelector: MemoizedSelector; + const initialTodos = [{ name: 'aaa', done: false }]; + let fixture: ComponentFixture; + + @Component({ + selector: 'app-todos', + template: ` +
    +
  • + {{ todo.name }} +
  • + +

    + {{ todo.name }} +

    +
+ `, + }) + class TodosComponent { + todos: Observable = this.store.pipe(select(todos)); + todosSelect: Observable = this.store.select(todos); + + constructor(private store: Store<{}>) {} + } + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TodosComponent], + providers: [provideMockStore()], + }).compileComponents(); + + mockStore = TestBed.get(Store); + mockSelector = mockStore.overrideSelector(todos, initialTodos); + + fixture = TestBed.createComponent(TodosComponent); + fixture.detectChanges(); + }); + + it('should work with store and select operator', () => { + const newTodos = [{ name: 'bbb', done: true }]; + mockSelector.setResult(newTodos); + mockStore.refreshState(); + + fixture.detectChanges(); + + expect(getTodoItems('li').length).toBe(1); + expect(getTodoItems('li')[0].nativeElement.textContent.trim()).toBe('bbb'); + }); + + it('should work with store.select method', () => { + const newTodos = [{ name: 'bbb', done: true }]; + mockSelector.setResult(newTodos); + mockStore.refreshState(); + + fixture.detectChanges(); + + expect(getTodoItems('p').length).toBe(1); + expect(getTodoItems('p')[0].nativeElement.textContent.trim()).toBe('bbb'); + }); +}); + +describe('Cleans up after each test', () => { + const selectData = createSelector( + (state: any) => state, + state => state.value + ); + + it('should return the mocked selectors value', (done: DoneFn) => { + TestBed.configureTestingModule({ + providers: [ + provideMockStore({ + initialState: { + value: 100, + }, + selectors: [{ selector: selectData, value: 200 }], + }), + ], + }); + + const store = TestBed.get>(Store) as Store; + store.pipe(select(selectData)).subscribe(v => { + expect(v).toBe(200); + done(); + }); + }); + + it('should return the real value', (done: DoneFn) => { + TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({} as any, { + initialState: { + value: 300, + }, + }), + ], + }); + + const store = TestBed.get>(Store) as Store; + store.pipe(select(selectData)).subscribe(v => { + expect(v).toBe(300); + done(); + }); + }); +}); diff --git a/modules/store/testing/src/mock_store.ts b/modules/store/testing/src/mock_store.ts index f89062609a..6d8bb614e2 100644 --- a/modules/store/testing/src/mock_store.ts +++ b/modules/store/testing/src/mock_store.ts @@ -88,7 +88,10 @@ export class MockStore extends Store { MockStore.selectors.set(selector, value); if (typeof selector === 'string') { - const stringSelector = createSelector(() => {}, () => value); + const stringSelector = createSelector( + () => {}, + () => value + ); return stringSelector; } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..ffdb4349c4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,20927 @@ +{ + "name": "@ngrx/platform", + "version": "8.6.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.900.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.900.0-rc.7.tgz", + "integrity": "sha512-HUs3lcBCqFg6y8+plPBcFmblEX3xzgm7udspnrfrBLsqgSo+Kko0DfUEyeOHXs8BdDIVMerjI/oAl1pdgcwyTQ==", + "requires": { + "@angular-devkit/core": "9.0.0-rc.7", + "rxjs": "6.5.3" + } + }, + "@angular-devkit/build-angular": { + "version": "0.900.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.900.0-rc.7.tgz", + "integrity": "sha512-87v9VQcuSNkRW2KzWbZOldQ/TxbPzrHOnseIWhA8NYDW4Zb1M75+iYCFR4u/SdSzkJFdUcMKO7oNFFCwDCn4fQ==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.900.0-rc.7", + "@angular-devkit/build-optimizer": "0.900.0-rc.7", + "@angular-devkit/build-webpack": "0.900.0-rc.7", + "@angular-devkit/core": "9.0.0-rc.7", + "@babel/core": "7.7.2", + "@babel/generator": "7.7.2", + "@babel/preset-env": "7.7.1", + "@ngtools/webpack": "9.0.0-rc.7", + "ajv": "6.10.2", + "autoprefixer": "9.7.1", + "babel-loader": "8.0.6", + "browserslist": "4.7.2", + "cacache": "13.0.1", + "caniuse-lite": "1.0.30001006", + "circular-dependency-plugin": "5.2.0", + "clean-css": "4.2.1", + "copy-webpack-plugin": "5.1.1", + "core-js": "3.3.6", + "file-loader": "4.2.0", + "find-cache-dir": "3.0.0", + "glob": "7.1.5", + "istanbul-instrumenter-loader": "3.0.1", + "jest-worker": "24.9.0", + "karma-source-map-support": "1.4.0", + "less": "3.10.3", + "less-loader": "5.0.0", + "license-webpack-plugin": "2.1.3", + "loader-utils": "1.2.3", + "magic-string": "0.25.4", + "mini-css-extract-plugin": "0.8.0", + "minimatch": "3.0.4", + "open": "7.0.0", + "parse5": "4.0.0", + "postcss": "7.0.21", + "postcss-import": "12.0.1", + "postcss-loader": "3.0.0", + "raw-loader": "3.1.0", + "regenerator-runtime": "0.13.3", + "rimraf": "3.0.0", + "rollup": "1.25.2", + "rxjs": "6.5.3", + "sass": "1.23.3", + "sass-loader": "8.0.0", + "semver": "6.3.0", + "source-map": "0.7.3", + "source-map-loader": "0.2.4", + "source-map-support": "0.5.16", + "speed-measure-webpack-plugin": "1.3.1", + "style-loader": "1.0.0", + "stylus": "0.54.7", + "stylus-loader": "3.0.2", + "terser": "4.4.2", + "terser-webpack-plugin": "2.2.1", + "tree-kill": "1.2.1", + "webpack": "4.41.2", + "webpack-dev-middleware": "3.7.2", + "webpack-dev-server": "3.9.0", + "webpack-merge": "4.2.2", + "webpack-sources": "1.4.3", + "webpack-subresource-integrity": "1.3.4", + "worker-plugin": "3.2.0" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "dev": true, + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "core-js": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.3.6.tgz", + "integrity": "sha512-u4oM8SHwmDuh5mWZdDg9UwNVq5s1uqq6ZDLLIs07VY+VJU91i3h4f3K/pgFvtUQPGdeStrZ+odKyfyt4EnKHfA==", + "dev": true + }, + "find-cache-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz", + "integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs-minipass": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.0.0.tgz", + "integrity": "sha512-40Qz+LFXmd9tzYVnnBmZvFfvAADfUA14TXPK1s7IfElJTIZ97rA8w4Kin7Wt5JBrC3ShnnFJO/5vPjPEeJIq9A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "glob": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "magic-string": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz", + "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "minipass": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", + "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.900.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.900.0-rc.7.tgz", + "integrity": "sha512-xxLMnXWuqqqKS2N9otMdkNWDz3Y/dgLh5VQfq2CjepD9WfHMLgRqvdPvp73X5lDMj9oUuNUj4Sl5XbDJQ9zydw==", + "dev": true, + "requires": { + "loader-utils": "1.2.3", + "source-map": "0.7.3", + "tslib": "1.10.0", + "typescript": "3.6.4", + "webpack-sources": "1.4.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.900.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.900.0-rc.7.tgz", + "integrity": "sha512-MrDNQ1PlmVy0JS3lfZqYtNsIdam//BBBZQ8sziXYdTXAMVtb9Sb9MBkbI/SKWahg5EDxvHJX52yIRkIzw4FsaA==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.900.0-rc.7", + "@angular-devkit/core": "9.0.0-rc.7", + "rxjs": "6.5.3" + } + }, + "@angular-devkit/core": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.0.0-rc.7.tgz", + "integrity": "sha512-Te43nFc5dqqc9hXcFwOyYYQPOF0yh7bbPXF2Ro+5ztwF3Cwc368OwLj0uJkdTGwVeIVjW5YsB3KXkJBcXDNTew==", + "requires": { + "ajv": "6.10.2", + "fast-json-stable-stringify": "2.0.0", + "magic-string": "0.25.4", + "rxjs": "6.5.3", + "source-map": "0.7.3" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "magic-string": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz", + "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + } + } + }, + "@angular-devkit/schematics": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-9.0.0-rc.7.tgz", + "integrity": "sha512-Fu9h8SyOQHKHhWjgWq9Rr68E0m1zf2rl7cDgkFsBFwDEfP6dPP4KK3YfBztFtLKkJeKcsBnAUY9cEo3rhY9K2w==", + "requires": { + "@angular-devkit/core": "9.0.0-rc.7", + "ora": "4.0.2", + "rxjs": "6.5.3" + }, + "dependencies": { + "cli-spinners": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz", + "integrity": "sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ==" + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "requires": { + "chalk": "^2.4.2" + } + }, + "ora": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz", + "integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==", + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + } + } + } + }, + "@angular/animations": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.0.0-rc.7.tgz", + "integrity": "sha512-N2JnT9XHu2wOo4zzjK+Aiu0Ow5NYXLIp2XSHPH31ZOzhNj5eSFZJr1Sjk0bg6oXDqCIyfcAUQnkwYKbe2NyeFQ==" + }, + "@angular/bazel": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/bazel/-/bazel-9.0.0-rc.7.tgz", + "integrity": "sha512-AhIeNxUtJK/3zsUi+G5P/+sd21rGS1GIeoMqvvBuKhcEJhNpJP1VSBtUkYuPxuYQqwDUik9fvK7BMgCjoJ2pxQ==", + "requires": { + "@microsoft/api-extractor": "^7.3.9", + "shelljs": "0.8.2", + "tsickle": "^0.37.1" + }, + "dependencies": { + "@microsoft/api-extractor": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.7.1.tgz", + "integrity": "sha512-guS1jXabKhlHDLazgpr9q/qpYxKx0Ark7KZSFy+L/XciNu9PvLkuJ5OKaBpnLU4o/QKzBXg2UQNZvML33QcSxQ==", + "requires": { + "@microsoft/api-extractor-model": "7.7.1", + "@microsoft/node-core-library": "3.18.1", + "@microsoft/ts-command-line": "4.3.6", + "@microsoft/tsdoc": "0.12.14", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "1.8.1", + "source-map": "~0.6.1", + "typescript": "~3.7.2" + } + }, + "shelljs": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.2.tgz", + "integrity": "sha512-pRXeNrCA2Wd9itwhvLp5LZQvPJ0wU6bcjaTMywHHGX5XWhVN2nzSu7WV0q+oUY7mGK3mgSkDDzP3MgjqdyIgbQ==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "tsickle": { + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.37.1.tgz", + "integrity": "sha512-0GwgOJEnsmRsrONXCvcbAWY0CvdqF3UugPVoupUpA8Ul0qCPTuqqq0ou/hLqtKZOyyulzCP6MYRjb9/J1g9bJg==" + }, + "typescript": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz", + "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==" + } + } + }, + "@angular/cdk": { + "version": "9.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.0.0-rc.6.tgz", + "integrity": "sha512-Zhd7+k+5pZnlKpl5wRTTqT8XEw5vo/sUrLQkgtdnuN+BFOIBDjdaeORqzbbKlyPmnrFJhhTbi9+zP55CX/xA5Q==", + "requires": { + "parse5": "^5.0.0" + } + }, + "@angular/cli": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-9.0.0-rc.7.tgz", + "integrity": "sha512-LdD2GrKAjtVrFxLMJqEDK0bMQI7uEAB4iJnDpFQjn9qLvBWU8qOJaZ+5KY/ZzVjBHXT1QeILX03AJw+FMCKGbw==", + "requires": { + "@angular-devkit/architect": "0.900.0-rc.7", + "@angular-devkit/core": "9.0.0-rc.7", + "@angular-devkit/schematics": "9.0.0-rc.7", + "@schematics/angular": "9.0.0-rc.7", + "@schematics/update": "0.900.0-rc.7", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "^4.1.1", + "ini": "1.3.5", + "inquirer": "7.0.0", + "npm-package-arg": "6.1.1", + "npm-pick-manifest": "3.0.2", + "open": "7.0.0", + "pacote": "9.5.8", + "read-package-tree": "5.3.1", + "rimraf": "3.0.0", + "semver": "6.3.0", + "symbol-observable": "1.2.0", + "universal-analytics": "^0.4.20", + "uuid": "^3.3.2" + }, + "dependencies": { + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@angular/common": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.0.0-rc.7.tgz", + "integrity": "sha512-2WGAizPJ6Yst/sP0boiYoGvMojcUKxqlRdn8ineCFCZlgkYZ/qYxY5R0gXwN1DPHVNKrNk1LDs11DYoGU25Htg==" + }, + "@angular/compiler": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0-rc.7.tgz", + "integrity": "sha512-kwX6475iuFEWcQc62mgd9Dq1lGMvqZXxEc0EZqsewi9Dpg/rj0ImXeYOTtIKFrgAkg03ahADBjtCComB0eKZgg==" + }, + "@angular/compiler-cli": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-9.0.0-rc.7.tgz", + "integrity": "sha512-341PP6Ewjt7x9l9tut5a7273jttAL1aY++YCy3vX6QYwIkxKGG3ZQgSfxa8M4tXwN5+wH0JkZWoSPVAmfRtsRw==", + "requires": { + "canonical-path": "1.0.0", + "chokidar": "^2.1.1", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.7.2", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "semver": "^6.3.0", + "source-map": "^0.6.1", + "yargs": "13.1.0" + }, + "dependencies": { + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@angular/core": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0-rc.7.tgz", + "integrity": "sha512-qn2lYImZNVzWOz8dR32Z9TTcW+nYX31H8Ckzn++xbL0x0W9iKg13GaNfaVJqnZI1pnzpRzzJdWXf5xQQQsKQsw==" + }, + "@angular/forms": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.0.0-rc.7.tgz", + "integrity": "sha512-hY/Dn2zg/yQb8Qk7a8RiwGJrankQAJssV4JJZSBuVGgtbdhdKv1+u9xNVcqYPlY+WuBhyvU+WoRWlooMv3c1AA==" + }, + "@angular/material": { + "version": "9.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-9.0.0-rc.6.tgz", + "integrity": "sha512-VwUZncyCqJAvgoW9ySExQPdKmTazyFSjKz7M65VTSdYJBmG/K2V+/plFXC+4Vyjy+cC+B+BIw5hBuv8YjXuKog==" + }, + "@angular/platform-browser": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.0.0-rc.7.tgz", + "integrity": "sha512-3xCVDX7QzK1V2XGwitakl/1Ekt3A0RfFqxffVa40iQH7shlAQ7SYDeTSj2Mtaz5QoqbkzLT9ZCrl9KtVllaojw==" + }, + "@angular/platform-browser-dynamic": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.0.0-rc.7.tgz", + "integrity": "sha512-r5qwnEotJm0RXNqxa7GcW9+RQMGCYMzGT/yr6CTQmLsn5Gn0ljE8DLSTpmNDIGTfwqIm1E9VIApASRLv/maeCQ==" + }, + "@angular/platform-server": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-9.0.0-rc.7.tgz", + "integrity": "sha512-BG91B85KIy5U/+lMFXCmt9Fav8mSzURayF4rGyBAsOK5EreAJogntUS6zhABukSIyQpEYVA5nIXfx19l27nfXA==", + "requires": { + "domino": "^2.1.2", + "xhr2": "^0.1.4" + } + }, + "@angular/router": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.0.0-rc.7.tgz", + "integrity": "sha512-6kviTBp43UyYj/Ah4qMhoga5YVHTVf3fmvbA4ETfTZAV0IEiPahxhn3FJ5Yal9Lk6Fc2kGmDvuDJgbn7gNbppA==" + }, + "@applitools/dom-snapshot": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@applitools/dom-snapshot/-/dom-snapshot-3.1.2.tgz", + "integrity": "sha512-1Z9xttomAusdKOGIWtP9FE+8ozA3YtJtfG/PCqfl6mBuCEjrmU2pT0JnLNIN1bilEM1Zm3fhajcfyAvX/Miw9w==", + "requires": { + "@applitools/functional-commons": "^1.5.2" + } + }, + "@applitools/eyes-common": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/@applitools/eyes-common/-/eyes-common-3.11.2.tgz", + "integrity": "sha512-8rA0uUHj55HNukthgpYb4u7K4fcBstNh/aw69rIKqeQ2dYJzIb9KNmNHc5rz/5u8ZPOxmpbrAL2dIPIh3BYHFA==", + "requires": { + "cosmiconfig": "^5.2.1", + "dateformat": "^3.0.3", + "debug": "^4.1.1", + "deepmerge": "^4.0.0", + "png-async": "^0.9.4", + "stack-trace": "^0.0.10" + } + }, + "@applitools/eyes-cypress": { + "version": "3.8.18", + "resolved": "https://registry.npmjs.org/@applitools/eyes-cypress/-/eyes-cypress-3.8.18.tgz", + "integrity": "sha512-lTXW48NGOVLbtRCBfZwlEr02/m47Dytu5qsZISyyVamlhzHd8LnE9l6MUyAw9sSYxDrgYq4+RkSyvUAyxGOa+g==", + "requires": { + "@applitools/dom-snapshot": "^3.1.2", + "@applitools/eyes-common": "^3.11.2", + "@applitools/eyes-sdk-core": "^5.20.4", + "@applitools/functional-commons": "^1.5.2", + "@applitools/visual-grid-client": "^13.2.3", + "body-parser": "^1.19.0", + "chalk": "^2.4.2", + "cors": "^2.8.5", + "express": "^4.17.1", + "lodash.flatten": "^4.4.0" + } + }, + "@applitools/eyes-sdk-core": { + "version": "5.20.4", + "resolved": "https://registry.npmjs.org/@applitools/eyes-sdk-core/-/eyes-sdk-core-5.20.4.tgz", + "integrity": "sha512-jIuKRevzREBCwX9H5OvWKKDvuJRsaoEAt3Jf0omchQs3mfOqEz49fbNn1ud61pF0GqKEjXQ0lsWcw5vURFjj4w==", + "requires": { + "@applitools/eyes-common": "^3.11.2", + "axios": "^0.19.0", + "es6-promise-pool": "^2.5.0" + } + }, + "@applitools/functional-commons": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@applitools/functional-commons/-/functional-commons-1.5.2.tgz", + "integrity": "sha512-CqNswn/F8DlkDvL39z7ne8OSQUJcoi/XXBNY2THjaadxB8GzK93lo50d6dr8gSKtG7Eha5XlgXkEKJP8jR1DmQ==" + }, + "@applitools/http-commons": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@applitools/http-commons/-/http-commons-1.3.9.tgz", + "integrity": "sha512-hGb5xhKJ9y2cMJ6ajMBuXXuy3prMSr/AtAdv38iGozeNqNc+HC51BDsaQXas95jbU6ZHEJFSsmvCYWb8O8DHrw==", + "requires": { + "@applitools/functional-commons": "^1.4.1", + "@applitools/monitoring-commons": "^1.0.15", + "agentkeepalive": "^4.0.2", + "debug": "^4.1.1", + "lodash.merge": "^4.6.2", + "node-fetch": "^2.6.0" + }, + "dependencies": { + "agentkeepalive": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.0.tgz", + "integrity": "sha512-CW/n1wxF8RpEuuiq6Vbn9S8m0VSYDMnZESqaJ6F2cWN9fY8rei2qaxweIaRgq+ek8TqfoFIsUjaGNKGGEHElSg==", + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + } + } + }, + "@applitools/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@applitools/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-7rutaN/2M5wYjOIOTKS/Zuc1Na90fJNEAqvo/jCxt7nSD1kYscHV3aCk9t7RD59gmzLMvUTIxFbjl4RUMV8qfg==", + "requires": { + "node-fetch": "^2.3.0", + "whatwg-fetch": ">=0.10.0" + } + }, + "@applitools/monitoring-commons": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@applitools/monitoring-commons/-/monitoring-commons-1.0.15.tgz", + "integrity": "sha512-I9Prok3+FIkSEI4rt+gvYNGDV4upNcj8p7JhgyOM6HRO2C+f81Xdi9Eh25XM9vaCFRIlkAMZLO0cdHR7fAa0yw==", + "requires": { + "debug": "^4.1.0" + } + }, + "@applitools/visual-grid-client": { + "version": "13.2.8", + "resolved": "https://registry.npmjs.org/@applitools/visual-grid-client/-/visual-grid-client-13.2.8.tgz", + "integrity": "sha512-uwlYPP6FoEVFcdohY34CE/MglK1qb0HWn0CH/O0ZwS2wuyeuM7aB5YR8yv5TbibbOHIpuHRoMQohChawKFVo3w==", + "requires": { + "@applitools/dom-snapshot": "^3.0.4", + "@applitools/eyes-common": "^3.11.2", + "@applitools/eyes-sdk-core": "^5.20.4", + "@applitools/functional-commons": "^1.5.2", + "@applitools/http-commons": "^1.3.9", + "@applitools/isomorphic-fetch": "^3.0.0", + "he": "^1.2.0", + "jsdom": "^15.1.1", + "lodash.mapvalues": "^4.6.0", + "mime-types": "^2.1.24", + "mkdirp": "^0.5.1", + "ndb": "^1.1.5", + "postcss-value-parser": "^4.0.2", + "throat": "^5.0.0" + } + }, + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", + "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.7.2", + "@babel/helpers": "^7.7.0", + "@babel/parser": "^7.7.2", + "@babel/template": "^7.7.0", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.7.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.2.tgz", + "integrity": "sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==", + "dev": true, + "requires": { + "@babel/types": "^7.7.2", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.0.tgz", + "integrity": "sha512-k50CQxMlYTYo+GGyUGFwpxKVtxVJi9yh61sXZji3zYHccK9RYliZGSTOgci85T+r+0VFN2nWbGM04PIqwfrpMg==", + "dev": true, + "requires": { + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.0.tgz", + "integrity": "sha512-Cd8r8zs4RKDwMG/92lpZcnn5WPQ3LAMQbCw42oqUh4s7vsSN5ANUZjMel0OOnxDLq57hoDDbai+ryygYfCTOsw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.0.tgz", + "integrity": "sha512-Su0Mdq7uSSWGZayGMMQ+z6lnL00mMCnGAbO/R0ZO9odIdB/WNU/VfQKqMQU0fdIsxQYbRjDM4BixIa93SQIpvw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.2.tgz", + "integrity": "sha512-pAil/ZixjTlrzNpjx+l/C/wJk002Wo7XbbZ8oujH/AoJ3Juv0iN/UTcPUHXKMFLqsfS0Hy6Aow8M31brUYBlQQ==", + "dev": true, + "requires": { + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.6.0" + } + }, + "@babel/helper-define-map": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.0.tgz", + "integrity": "sha512-kPKWPb0dMpZi+ov1hJiwse9dWweZsz3V9rP4KdytnX1E7z3cTNmFGglwklzFPuqIcHLIY3bgKSs4vkwXXdflQA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.7.0", + "@babel/types": "^7.7.0", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.0.tgz", + "integrity": "sha512-CDs26w2shdD1urNUAji2RJXyBFCaR+iBEGnFz3l7maizMkQe3saVw9WtjG1tz8CwbjvlFnaSLVhgnu1SWaherg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-function-name": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz", + "integrity": "sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.7.0", + "@babel/template": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz", + "integrity": "sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==", + "dev": true, + "requires": { + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.0.tgz", + "integrity": "sha512-LUe/92NqsDAkJjjCEWkNe+/PcpnisvnqdlRe19FahVapa4jndeuJ+FBiTX1rcAKWKcJGE+C3Q3tuEuxkSmCEiQ==", + "dev": true, + "requires": { + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.0.tgz", + "integrity": "sha512-QaCZLO2RtBcmvO/ekOLp8p7R5X2JriKRizeDpm5ChATAFWrrYDcDxPuCIBXKyBjY+i1vYSdcUTMIb8psfxHDPA==", + "dev": true, + "requires": { + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.0.tgz", + "integrity": "sha512-Dv3hLKIC1jyfTkClvyEkYP2OlkzNvWs5+Q8WgPbxM5LMeorons7iPP91JM+DU7tRbhqA1ZeooPaMFvQrn23RHw==", + "dev": true, + "requires": { + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.0.tgz", + "integrity": "sha512-rXEefBuheUYQyX4WjV19tuknrJFwyKw0HgzRwbkyTbB+Dshlq7eqkWbyjzToLrMZk/5wKVKdWFluiAsVkHXvuQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.7.0", + "@babel/helper-simple-access": "^7.7.0", + "@babel/helper-split-export-declaration": "^7.7.0", + "@babel/template": "^7.7.0", + "@babel/types": "^7.7.0", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.0.tgz", + "integrity": "sha512-48TeqmbazjNU/65niiiJIJRc5JozB8acui1OS7bSd6PgxfuovWsvjfWSzlgx+gPFdVveNzUdpdIg5l56Pl5jqg==", + "dev": true, + "requires": { + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", + "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.0.tgz", + "integrity": "sha512-pHx7RN8X0UNHPB/fnuDnRXVZ316ZigkO8y8D835JlZ2SSdFKb6yH9MIYRU4fy/KPe5sPHDFOPvf8QLdbAGGiyw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.7.0", + "@babel/helper-wrap-function": "^7.7.0", + "@babel/template": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.0.tgz", + "integrity": "sha512-5ALYEul5V8xNdxEeWvRsBzLMxQksT7MaStpxjJf9KsnLxpAKBtfw5NeMKZJSYDa0lKdOcy0g+JT/f5mPSulUgg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.7.0", + "@babel/helper-optimise-call-expression": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.0.tgz", + "integrity": "sha512-AJ7IZD7Eem3zZRuj5JtzFAptBw7pMlS3y8Qv09vaBWoFsle0d1kAn5Wq6Q9MyBXITPOKnxwkZKoAm4bopmv26g==", + "dev": true, + "requires": { + "@babel/template": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz", + "integrity": "sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==", + "dev": true, + "requires": { + "@babel/types": "^7.7.0" + } + }, + "@babel/helper-wrap-function": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.0.tgz", + "integrity": "sha512-sd4QjeMgQqzshSjecZjOp8uKfUtnpmCyQhKQrVJBBgeHAB/0FPi33h3AbVlVp07qQtMD4QgYSzaMI7VwncNK/w==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.7.0", + "@babel/template": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/helpers": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.0.tgz", + "integrity": "sha512-VnNwL4YOhbejHb7x/b5F39Zdg5vIQpUUNzJwx0ww1EcVRt41bbGRZWhAURrfY32T5zTT3qwNOQFWpn+P0i0a2g==", + "dev": true, + "requires": { + "@babel/template": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", + "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.0.tgz", + "integrity": "sha512-ot/EZVvf3mXtZq0Pd0+tSOfGWMizqmOohXmNZg6LNFjHOV+wOPv7BvVYh8oPR8LhpIP3ye8nNooKL50YRWxpYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.7.0", + "@babel/plugin-syntax-async-generators": "^7.2.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.0.tgz", + "integrity": "sha512-7poL3Xi+QFPC7sGAzEIbXUyYzGJwbc2+gSD0AkiC5k52kH2cqHdqxm5hNFfLW3cRSTcx9bN0Fl7/6zWcLLnKAQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", + "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-json-strings": "^7.2.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz", + "integrity": "sha512-LDBXlmADCsMZV1Y9OQwMc0MyGZ8Ta/zlD9N67BfQT8uYwkRswiu2hU6nJKrjrt/58aH/vqfQlR/9yId/7A2gWw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.0.tgz", + "integrity": "sha512-mk34H+hp7kRBWJOOAR0ZMGCydgKMD4iN9TpDRp3IIcbunltxEY89XSimc6WbtSLCDrwcdy/EEw7h5CFCzxTchw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", + "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", + "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.0.tgz", + "integrity": "sha512-hi8FUNiFIY1fnUI2n1ViB1DR0R4QeK4iHcTlW6aJkrPoTdb8Rf1EMQ6GT3f67DDkYyWgew9DFoOZ6gOoEsdzTA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.0.tgz", + "integrity": "sha512-vLI2EFLVvRBL3d8roAMqtVY0Bm9C1QzLkdS57hiKrjUBSqsQYrBsMCeOg/0KK7B0eK9V71J5mWcha9yyoI2tZw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.7.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", + "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz", + "integrity": "sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.0.tgz", + "integrity": "sha512-/b3cKIZwGeUesZheU9jNYcwrEA7f/Bo4IdPmvp7oHgvks2majB5BoT5byAql44fiNQYOPzhk2w8DbgfuafkMoA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.7.0", + "@babel/helper-define-map": "^7.7.0", + "@babel/helper-function-name": "^7.7.0", + "@babel/helper-optimise-call-expression": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.7.0", + "@babel/helper-split-export-declaration": "^7.7.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", + "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", + "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.0.tgz", + "integrity": "sha512-3QQlF7hSBnSuM1hQ0pS3pmAbWLax/uGNCbPBND9y+oJ4Y776jsyujG2k0Sn2Aj2a0QwVOiOFL5QVPA7spjvzSA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz", + "integrity": "sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", + "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", + "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.0.tgz", + "integrity": "sha512-P5HKu0d9+CzZxP5jcrWdpe7ZlFDe24bmqP6a6X8BHEBl/eizAsY8K6LX8LASZL0Jxdjm5eEfzp+FIrxCm/p8bA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", + "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", + "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz", + "integrity": "sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.0.tgz", + "integrity": "sha512-KEMyWNNWnjOom8vR/1+d+Ocz/mILZG/eyHHO06OuBQ2aNhxT62fr4y6fGOplRx+CxCSp3IFwesL8WdINfY/3kg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.7.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.0.tgz", + "integrity": "sha512-ZAuFgYjJzDNv77AjXRqzQGlQl4HdUM6j296ee4fwKVZfhDR9LAGxfvXjBkb06gNETPnN0sLqRm9Gxg4wZH6dXg==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.0.tgz", + "integrity": "sha512-u7eBA03zmUswQ9LQ7Qw0/ieC1pcAkbp5OQatbWUzY1PaBccvuJXUkYzoN1g7cqp7dbTu6Dp9bXyalBvD04AANA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.0.tgz", + "integrity": "sha512-+SicSJoKouPctL+j1pqktRVCgy+xAch1hWWTMy13j0IflnyNjaoskj+DwRQFimHbLqO3sq2oN2CXMvXq3Bgapg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.7.0" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", + "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz", + "integrity": "sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.5.5" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", + "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "dev": true, + "requires": { + "@babel/helper-call-delegate": "^7.4.4", + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", + "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.0.tgz", + "integrity": "sha512-AXmvnC+0wuj/cFkkS/HFHIojxH3ffSXE+ttulrqWjZZRaUOonfJc60e1wSNT4rV8tIunvu/R3wCp71/tLAa9xg==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz", + "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", + "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.6.2.tgz", + "integrity": "sha512-DpSvPFryKdK1x+EDJYCy28nmAaIMdxmhot62jAXF/o99iA33Zj2Lmcp3vDmz+MUh0LNYVPvfj5iC3feb3/+PFg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", + "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", + "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", + "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.0.tgz", + "integrity": "sha512-RrThb0gdrNwFAqEAAx9OWgtx6ICK69x7i9tCnMdVrxQwSDp/Abu9DXFU5Hh16VP33Rmxh04+NGW28NsIkFvFKA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/preset-env": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.1.tgz", + "integrity": "sha512-/93SWhi3PxcVTDpSqC+Dp4YxUu3qZ4m7I76k0w73wYfn7bGVuRIO4QUz95aJksbS+AD1/mT1Ie7rbkT0wSplaA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.7.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.7.0", + "@babel/plugin-proposal-dynamic-import": "^7.7.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.6.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.7.0", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", + "@babel/plugin-syntax-json-strings": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-syntax-top-level-await": "^7.7.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.7.0", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.6.3", + "@babel/plugin-transform-classes": "^7.7.0", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.6.0", + "@babel/plugin-transform-dotall-regex": "^7.7.0", + "@babel/plugin-transform-duplicate-keys": "^7.5.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.4.4", + "@babel/plugin-transform-function-name": "^7.7.0", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-member-expression-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.5.0", + "@babel/plugin-transform-modules-commonjs": "^7.7.0", + "@babel/plugin-transform-modules-systemjs": "^7.7.0", + "@babel/plugin-transform-modules-umd": "^7.7.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.0", + "@babel/plugin-transform-new-target": "^7.4.4", + "@babel/plugin-transform-object-super": "^7.5.5", + "@babel/plugin-transform-parameters": "^7.4.4", + "@babel/plugin-transform-property-literals": "^7.2.0", + "@babel/plugin-transform-regenerator": "^7.7.0", + "@babel/plugin-transform-reserved-words": "^7.2.0", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.6.2", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.4.4", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.7.0", + "@babel/types": "^7.7.1", + "browserslist": "^4.6.0", + "core-js-compat": "^3.1.1", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.5.0" + }, + "dependencies": { + "browserslist": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.2.tgz", + "integrity": "sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001004", + "electron-to-chromium": "^1.3.295", + "node-releases": "^1.1.38" + } + }, + "caniuse-lite": { + "version": "1.0.30001008", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001008.tgz", + "integrity": "sha512-b8DJyb+VVXZGRgJUa30cbk8gKHZ3LOZTBLaUEEVr2P4xpmFigOCc62CO4uzquW641Ouq1Rm9N+rWLWdSYDaDIw==", + "dev": true + } + } + }, + "@babel/runtime": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", + "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz", + "integrity": "sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/types": "^7.7.0" + } + }, + "@babel/traverse": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.2.tgz", + "integrity": "sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.7.2", + "@babel/helper-function-name": "^7.7.0", + "@babel/helper-split-export-declaration": "^7.7.0", + "@babel/parser": "^7.7.2", + "@babel/types": "^7.7.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.2.tgz", + "integrity": "sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@bazel/bazel": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@bazel/bazel/-/bazel-1.0.0.tgz", + "integrity": "sha512-bxNjlieM1HwgIgqx+AqtNeUA6bvqIQ0X5YysWuCCtT24Dd+wTs6fRSx1KGOA1NiRBrg+kpk7ebitOU8yaM+tiA==", + "dev": true, + "requires": { + "@bazel/bazel-darwin_x64": "1.0.0", + "@bazel/bazel-linux_x64": "1.0.0", + "@bazel/bazel-win32_x64": "1.0.0", + "@bazel/hide-bazel-files": "^1.0.1" + }, + "dependencies": { + "@bazel/bazel-darwin_x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@bazel/bazel-darwin_x64/-/bazel-darwin_x64-1.0.0.tgz", + "integrity": "sha512-2J8qPpUAhSsuZ1P0kMFLvAQUz8zB8mkKmGL3/8raXUnw9TblsykwAdeg3QlJwTLORn+ZqdAjOYEQIarnTpS1NA==", + "dev": true, + "optional": true + }, + "@bazel/bazel-linux_x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@bazel/bazel-linux_x64/-/bazel-linux_x64-1.0.0.tgz", + "integrity": "sha512-/ZpOrYyDNGqUyAGPHFr4Y1kn8xCG1G4Lg2VMZtfCZzDohzoYFYs8iyQGU2/8PwldH8XX+oJT9atWqSt1EyoeAw==", + "dev": true, + "optional": true + }, + "@bazel/bazel-win32_x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@bazel/bazel-win32_x64/-/bazel-win32_x64-1.0.0.tgz", + "integrity": "sha512-p5LpQ/WiijwOS+eBkdD7UewHL8JwK+8gpb4tIKqgh/a2yawgzEQPJDPBUV9ykss5t+s85BL2kEMhduuDewt/MA==", + "dev": true, + "optional": true + } + } + }, + "@bazel/buildifier": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@bazel/buildifier/-/buildifier-0.29.0.tgz", + "integrity": "sha512-skJ7vVff7x3tB8crBCtJji2wU09uH0uD2N30xfOpVUgsMJSXktAQMj8+RPdMBqJQ9XS5+O5lmRthR35Ua7xQXA==", + "requires": { + "@bazel/buildifier-darwin_x64": "0.29.0", + "@bazel/buildifier-linux_x64": "0.29.0", + "@bazel/buildifier-win32_x64": "0.29.0" + }, + "dependencies": { + "@bazel/buildifier-darwin_x64": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@bazel/buildifier-darwin_x64/-/buildifier-darwin_x64-0.29.0.tgz", + "integrity": "sha512-jiQIZo5z1c8oFokD2jObrkB04Bs+7RaUJ6WlHV9BBeJiRMGfRVUBUrOF5eJPk6VB8qknIOfHvJD/Ym5XUc1Zlw==", + "optional": true + }, + "@bazel/buildifier-linux_x64": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@bazel/buildifier-linux_x64/-/buildifier-linux_x64-0.29.0.tgz", + "integrity": "sha512-sXXY0gP4oC1nC1G8Baqd7kyBL/y9/qOqftKSkDe2Y7gBoc9GslwyexwDxTSxK0Gun/4Vcvc2eRu7b83hMOxuag==", + "optional": true + }, + "@bazel/buildifier-win32_x64": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@bazel/buildifier-win32_x64/-/buildifier-win32_x64-0.29.0.tgz", + "integrity": "sha512-hnOQfPhQNAIqbrhsHT3MWAyAZSUhKwxzEuZJZoOsGrW8fPonhKysdvfZJqfqJ6vDVYaMJKvLnSO1c9QZRhU7kQ==", + "optional": true + } + } + }, + "@bazel/hide-bazel-files": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bazel/hide-bazel-files/-/hide-bazel-files-1.0.1.tgz", + "integrity": "sha512-3unoDYZIGxtePTa/bhsWZ0pxtI/G9LYr41Kkb1ZpSaSz27ZDvj6Aysbc0u9TNGTZhqkcr/w66OC9pvmeys4YnQ==", + "dev": true + }, + "@bazel/ibazel": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@bazel/ibazel/-/ibazel-0.10.3.tgz", + "integrity": "sha512-v1nXbMTHVlMM4z4uWp6XiRoHAyUlYggF1SOboLLWRp0+D22kWixqArWqnozLw2mOtnxr97BdLjluWiho6A8Hjg==", + "dev": true + }, + "@bazel/jasmine": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@bazel/jasmine/-/jasmine-0.39.1.tgz", + "integrity": "sha512-l+IerE7rKuHYShkWk7L71x65X1mlrNFrAPk/EGlyRpuY+lDDP6P8ElHBV4e+32s0Yt5Ewi46BvGMYkOWHdbzgA==", + "dev": true, + "requires": { + "jasmine": "~3.4.0", + "jasmine-core": "~3.4.0", + "v8-coverage": "1.0.9" + }, + "dependencies": { + "jasmine": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.4.0.tgz", + "integrity": "sha512-sR9b4n+fnBFDEd7VS2el2DeHgKcPiMVn44rtKFumq9q7P/t8WrxsVIZPob4UDdgcDNCwyDqwxCt4k9TDRmjPoQ==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "jasmine-core": "~3.4.0" + } + }, + "jasmine-core": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.4.0.tgz", + "integrity": "sha512-HU/YxV4i6GcmiH4duATwAbJQMlE0MsDIR5XmSVxURxKHn3aGAdbY1/ZJFmVRbKtnLwIxxMJD7gYaPsypcbYimg==", + "dev": true + } + } + }, + "@bazel/typescript": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@bazel/typescript/-/typescript-0.39.1.tgz", + "integrity": "sha512-n4P6VKir1L3+rM/DgHa5mnlCfT7Wo0ra+TnkbTuZ08SeN/r8HTmClK6YADoDc5ORMQhzq8eXuVuLWeUUsFJyHQ==", + "dev": true, + "requires": { + "protobufjs": "6.8.8", + "semver": "5.6.0", + "source-map-support": "0.5.9", + "tsutils": "2.27.2" + }, + "dependencies": { + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "tsutils": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.2.tgz", + "integrity": "sha512-qf6rmT84TFMuxAKez2pIfR8UCai49iQsfB7YWVjV1bKpy/d0PWT5rEOSM6La9PiHZ0k1RRZQiwVdVJfQ3BPHgg==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@cypress/listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@cypress/webpack-preprocessor": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@cypress/webpack-preprocessor/-/webpack-preprocessor-4.1.0.tgz", + "integrity": "sha512-LbxsdYVpHGoC2fMOdW0aQvuvVRD7aZx8p8DrP53HISpl7bD1PqLGWKzhHn7cGG24UHycBJrbaEeKEosW29W1dg==", + "dev": true, + "requires": { + "@babel/core": "^7.0.1", + "@babel/preset-env": "^7.0.0", + "babel-loader": "^8.0.2", + "bluebird": "3.5.0", + "debug": "3.1.0" + }, + "dependencies": { + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@microsoft/api-extractor-model": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.7.1.tgz", + "integrity": "sha512-Te3TQMHb87enq5sihwGUcT1KjWv7OxZPeZZ1Zkz02iFIxXY6Lvz5qpqhiywa5aES54o4Bt8limNYZJCp0OcwmA==", + "requires": { + "@microsoft/node-core-library": "3.18.1", + "@microsoft/tsdoc": "0.12.14" + } + }, + "@microsoft/node-core-library": { + "version": "3.18.1", + "resolved": "https://registry.npmjs.org/@microsoft/node-core-library/-/node-core-library-3.18.1.tgz", + "integrity": "sha512-K2qfMSoca88UMfLFh9CmdCWYEGFZkzICr/7NR7Ql7lQaro7cn5f/unQvJ4RSXoKGG/bdIZmRQzjdJav+Pm4i+Q==", + "requires": { + "@types/node": "8.10.54", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "jju": "~1.4.0", + "semver": "~5.3.0", + "timsort": "~0.3.0", + "z-schema": "~3.18.3" + }, + "dependencies": { + "@types/node": { + "version": "8.10.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.54.tgz", + "integrity": "sha512-kaYyLYf6ICn6/isAyD4K1MyWWd5Q3JgH6bnMN089LUx88+s4W8GvK9Q6JMBVu5vsFFp7pMdSxdKmlBXwH/VFRg==" + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + } + } + }, + "@microsoft/ts-command-line": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@microsoft/ts-command-line/-/ts-command-line-4.3.6.tgz", + "integrity": "sha512-A10mTqs2SzzK1MZANFuqwpFpYa5YqgSPY7hmDtO6sQ1Y3lbZQGmKYLxgwzt+LMaH+7l7xG63eqr4UyMgJoBhyg==", + "requires": { + "@types/argparse": "1.0.33", + "argparse": "~1.0.9", + "colors": "~1.2.1" + } + }, + "@microsoft/tsdoc": { + "version": "0.12.14", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.14.tgz", + "integrity": "sha512-518yewjSga1jLdiLrcmpMFlaba5P+50b0TWNFUpC+SL9Yzf0kMi57qw+bMl+rQ08cGqH1vLx4eg9YFUbZXgZ0Q==" + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@ngtools/webpack": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.0.0-rc.7.tgz", + "integrity": "sha512-SgskH++N5MFZtYmOW6JfXi2G4jY+L6F32raQTJ1Ggp7vKsUc7VqY8P7VEg61yMNcmnwWgqSIEiLyMSyf9M7C/g==", + "dev": true, + "requires": { + "@angular-devkit/core": "9.0.0-rc.7", + "enhanced-resolve": "4.1.1", + "rxjs": "6.5.3", + "webpack-sources": "1.4.3" + }, + "dependencies": { + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@octokit/rest": { + "version": "15.18.3", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.18.3.tgz", + "integrity": "sha512-oHABAvvC83tPIuvUfWRaw9eLThFrCxBgywl+KvEwfTFjoCrMOfEaMh0r39+Ub/EEbV345GJiMzN+zPZ4kqOvbA==", + "dev": true, + "requires": { + "before-after-hook": "^1.1.0", + "btoa-lite": "^1.0.0", + "debug": "^3.1.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.0", + "lodash": "^4.17.4", + "node-fetch": "^2.1.1", + "universal-user-agent": "^2.0.0", + "url-template": "^2.0.8" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", + "dev": true + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", + "dev": true + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "dev": true, + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", + "dev": true + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", + "dev": true + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", + "dev": true + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", + "dev": true + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", + "dev": true + }, + "@samverschueren/stream-to-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", + "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, + "@schematics/angular": { + "version": "9.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-9.0.0-rc.7.tgz", + "integrity": "sha512-39vulJr2d6uxKRnD+YUKT4/japYekqZfUNlt8VjihmztlnS2oKtfyiqaUNuiSS722lEhDPUdPLzQcX3PMS8Eyw==", + "requires": { + "@angular-devkit/core": "9.0.0-rc.7", + "@angular-devkit/schematics": "9.0.0-rc.7" + } + }, + "@schematics/update": { + "version": "0.900.0-rc.7", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.900.0-rc.7.tgz", + "integrity": "sha512-idcOP3p2ui9QpoIp9kBh9N+MSF9rg8bg/AUgVmY2oes0Mg5bC0Tumrn6/V82maoznOrpi1wmf2NPHGlh3hmvmA==", + "requires": { + "@angular-devkit/core": "9.0.0-rc.7", + "@angular-devkit/schematics": "9.0.0-rc.7", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "npm-package-arg": "^7.0.0", + "pacote": "9.5.8", + "rxjs": "6.5.3", + "semver": "6.3.0", + "semver-intersect": "1.4.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.2.tgz", + "integrity": "sha512-ezZMWtHXm7Eb7Rq4Mwnx2vs79WUx2QmRg3+ZqeGroKzfDO+EprOcgRPYghsOP9JuYBfK18VojmRTGCg8Ma+ktw==", + "requires": { + "lru-cache": "^5.1.1" + } + }, + "npm-package-arg": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", + "integrity": "sha512-xXxr8y5U0kl8dVkz2oK7yZjPBvqM2fwaO5l3Yg13p03v8+E3qQcD0JNhHzjL1vyGgxcKkD0cco+NLR72iuPk3g==", + "requires": { + "hosted-git-info": "^3.0.2", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@types/argparse": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.33.tgz", + "integrity": "sha512-VQgHxyPMTj3hIlq9SY1mctqx+Jj8kpQfoLvDlVSDNOyuYs8JYfkuY3OW/4+dO657yPmNhHpePRx0/Tje5ImNVQ==" + }, + "@types/babel__core": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", + "integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.0.tgz", + "integrity": "sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/fs-extra": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-2.1.0.tgz", + "integrity": "sha1-izUCOcBFXZK408Ym7awZOGD/OV8=", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "5.0.36", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.36.tgz", + "integrity": "sha512-KEzSKuP2+3oOjYYjujue6Z3Yqis5HKA1BsIC+jZ1v3lrRNdsqyNNtX0rQf6LSuI4DJJ2z5UV//zBZCcvM0xikg==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/globby": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/globby/-/globby-8.0.0.tgz", + "integrity": "sha512-xDtsX5tlctxJzvg29r/LN12z30oJpoFP9cE8eJ8nY5cbSvN0c0RdRHrVlEq4LRh362Sd+JsqxJ3QWw0Wnyto8w==", + "dev": true, + "requires": { + "@types/glob": "*", + "fast-glob": "^2.0.2" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/jasmine": { + "version": "2.5.45", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.45.tgz", + "integrity": "sha1-WJKKYh0BTOarWcWpxBBx9zKLDKk=", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", + "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/jest": { + "version": "24.0.22", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.22.tgz", + "integrity": "sha512-t2OvhNZnrNjlzi2i0/cxbLVM59WN15I2r1Qtb7wDv28PnV9IzrPtagFRey/S9ezdLD0zyh1XGMQIEQND2YEfrw==", + "dev": true, + "requires": { + "@types/jest-diff": "*" + } + }, + "@types/jest-diff": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz", + "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/lodash": { + "version": "4.14.146", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.146.tgz", + "integrity": "sha512-JzJcmQ/ikHSv7pbvrVNKJU5j9jL9VLf3/gqs048CEnBVVVEv4kve3vLxoPHGvclutS+Il4SBIuQQ087m1eHffw==", + "dev": true + }, + "@types/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", + "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/ncp": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/ncp/-/ncp-2.0.3.tgz", + "integrity": "sha512-eZatZMWhPHUHY/1hUUqwGrWzBAE9deYR3L0QJMicpVkBUOrQslhWblw5Ye+rKuzvFG/UQ3jDolMcjhC2WKUQ5w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "12.12.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.24.tgz", + "integrity": "sha512-1Ciqv9pqwVtW6FsIUKSZNB82E5Cu1I2bBTj1xuIHXLe/1zYLl3956Nbhg2MzSYHVfl9/rmanjbQIb7LibfCnug==", + "dev": true + }, + "@types/ora": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/ora/-/ora-1.3.5.tgz", + "integrity": "sha512-CZe3oXbO1XylJT1feg+/aCzNt6tfR4XO+IkLetc85O/yaZRw271cZtS8LL/2mknd+PoR5IKAjFLzo4KWZXxung==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/rimraf": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-0.0.28.tgz", + "integrity": "sha1-VWJRm8eWPKyoq/fxKMrjtZTUHQY=", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "2.53.43", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz", + "integrity": "sha512-UBYHWph6P3tutkbXpW6XYg9ZPbTKjw/YC2hGG1/GEvWwTbvezBUv3h+mmUFw79T3RFPnmedpiXdOBbXX+4l0jg==", + "dev": true + }, + "@types/shelljs": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.6.tgz", + "integrity": "sha512-svx2eQS268awlppL/P8wgDLBrsDXdKznABHJcuqXyWpSKJgE1s2clXlBvAwbO/lehTmG06NtEWJRkAk4tAgenA==", + "dev": true, + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", + "dev": true + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/webpack-sources": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.5.tgz", + "integrity": "sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", + "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abab": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz", + "integrity": "sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==" + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==" + } + } + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==" + }, + "add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", + "dev": true + }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "requires": { + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" + }, + "ansi-escapes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "requires": { + "type-fest": "^0.8.1" + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "app-root-path": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", + "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true, + "requires": { + "default-require-extensions": "^1.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "arch": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", + "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arraybuffer.slice": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", + "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "autoprefixer": { + "version": "9.7.1", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.1.tgz", + "integrity": "sha512-w3b5y1PXWlhYulevrTJ0lizkQ5CyqfeU6BIRDbuhsMupstHQOeb1Ur80tcB1zxSu7AwyY/qCQ7Vvqklh31ZBFw==", + "dev": true, + "requires": { + "browserslist": "^4.7.2", + "caniuse-lite": "^1.0.30001006", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.21", + "postcss-value-parser": "^4.0.2" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + } + } + }, + "axobject-query": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", + "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "babel-loader": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", + "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-polyfill": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", + "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=", + "requires": { + "babel-runtime": "^6.22.0", + "core-js": "^2.4.0", + "regenerator-runtime": "^0.10.0" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "before-after-hook": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.4.0.tgz", + "integrity": "sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg==", + "dev": true + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "blob": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "dev": true + }, + "blocking-proxy": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-0.0.5.tgz", + "integrity": "sha1-RikF4Nz76pcPQao3Ij3anAexkSs=", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + }, + "dependencies": { + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + } + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.x.x" + } + }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.2.tgz", + "integrity": "sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001004", + "electron-to-chromium": "^1.3.295", + "node-releases": "^1.1.38" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", + "dev": true + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cacache": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cachedir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-1.3.0.tgz", + "integrity": "sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg==", + "dev": true, + "requires": { + "os-homedir": "^1.0.1" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } + }, + "caniuse-lite": { + "version": "1.0.30001006", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001006.tgz", + "integrity": "sha512-MXnUVX27aGs/QINz+QG1sWSLDr3P1A3Hq5EUWoIt0T7K24DuvMxZEnh3Y5aHlJW6Bz2aApJdSewdYLd8zQnUuw==", + "dev": true + }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==" + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" + }, + "carlo": { + "version": "0.9.46", + "resolved": "https://registry.npmjs.org/carlo/-/carlo-0.9.46.tgz", + "integrity": "sha512-FwZ/wxjqe+5RgzF2SRsPSWsVB9+McAVRWW0tRkmbh7fBjrf3HFZZbcr8vr61p1K+NBaAPv57DRjxgIyfbHmd7g==", + "requires": { + "debug": "^4.1.0", + "puppeteer-core": "~1.12.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "dev": true + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + }, + "dependencies": { + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "chokidar-cli": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-1.2.3.tgz", + "integrity": "sha512-HcHjqeQaT/u0Swy4eaqqg0NhPjsXq6ZN9YzP48EYc81FXBLQuvMXBsrEMDkgH+Puup1mBc0gD0qqECDy/WiMOA==", + "dev": true, + "requires": { + "bluebird": "3.5.5", + "chokidar": "2.1.5", + "lodash": "4.17.15", + "yargs": "13.3.0" + }, + "dependencies": { + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "dev": true + }, + "chokidar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", + "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + } + } + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-dependency-plugin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", + "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", + "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "dev": true + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "codelyzer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.0.tgz", + "integrity": "sha512-izfUfhEOOgAizszPlEDxo71DK/C4wprZw0vkY6UWcOSTQvN1JyfXf9DXwaV7WX+/JC+hH0ShXfdtGLA9Rca7LA==", + "dev": true, + "requires": { + "app-root-path": "^2.2.1", + "aria-query": "^3.0.0", + "axobject-query": "^2.0.2", + "css-selector-tokenizer": "^0.7.1", + "cssauron": "^1.4.0", + "damerau-levenshtein": "^1.0.4", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==" + }, + "combine-lists": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", + "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", + "dev": true, + "requires": { + "lodash": "^4.5.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-func": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", + "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^3.0.0" + }, + "dependencies": { + "dot-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + } + } + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + }, + "dependencies": { + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "dev": true + } + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "conventional-changelog": { + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-1.1.24.tgz", + "integrity": "sha512-2WcSUst4Y3Z4hHvoMTWXMJr/DmgVdLiMOVY1Kak2LfFz+GIz2KDp5naqbFesYbfXPmaZ5p491dO0FWZIJoJw1Q==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^1.6.6", + "conventional-changelog-atom": "^0.2.8", + "conventional-changelog-codemirror": "^0.3.8", + "conventional-changelog-core": "^2.0.11", + "conventional-changelog-ember": "^0.3.12", + "conventional-changelog-eslint": "^1.0.9", + "conventional-changelog-express": "^0.3.6", + "conventional-changelog-jquery": "^0.1.0", + "conventional-changelog-jscs": "^0.1.0", + "conventional-changelog-jshint": "^0.3.8", + "conventional-changelog-preset-loader": "^1.1.8" + } + }, + "conventional-changelog-angular": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz", + "integrity": "sha512-suQnFSqCxRwyBxY68pYTsFkG0taIdinHLNEAX5ivtw8bCRnIgnpvcHmlR/yjUyZIrNPYAoXlY1WiEKWgSE4BNg==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-atom": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-0.2.8.tgz", + "integrity": "sha512-8pPZqhMbrnltNBizjoDCb/Sz85KyUXNDQxuAEYAU5V/eHn0okMBVjqc8aHWYpHrytyZWvMGbayOlDv7i8kEf6g==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-cli": { + "version": "1.3.22", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-1.3.22.tgz", + "integrity": "sha512-pnjdIJbxjkZ5VdAX/H1wndr1G10CY8MuZgnXuJhIHglOXfIrXygb7KZC836GW9uo1u8PjEIvIw/bKX0lOmOzZg==", + "dev": true, + "requires": { + "add-stream": "^1.0.0", + "conventional-changelog": "^1.1.24", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "tempfile": "^1.1.1" + } + }, + "conventional-changelog-codemirror": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.3.8.tgz", + "integrity": "sha512-3HFZKtBXTaUCHvz7ai6nk2+psRIkldDoNzCsom0egDtVmPsvvHZkzjynhdQyULfacRSsBTaiQ0ol6nBOL4dDiQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-core": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-2.0.11.tgz", + "integrity": "sha512-HvTE6RlqeEZ/NFPtQeFLsIDOLrGP3bXYr7lFLMhCVsbduF1MXIe8OODkwMFyo1i9ku9NWBwVnVn0jDmIFXjDRg==", + "dev": true, + "requires": { + "conventional-changelog-writer": "^3.0.9", + "conventional-commits-parser": "^2.1.7", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "^1.3.6", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^1.3.6", + "lodash": "^4.2.1", + "normalize-package-data": "^2.3.5", + "q": "^1.5.1", + "read-pkg": "^1.1.0", + "read-pkg-up": "^1.0.1", + "through2": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "conventional-changelog-ember": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-0.3.12.tgz", + "integrity": "sha512-mmJzA7uzbrOqeF89dMMi6z17O07ORTXlTMArnLG9ZTX4oLaKNolUlxFUFlFm9JUoVWajVpaHQWjxH1EOQ+ARoQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-eslint": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-1.0.9.tgz", + "integrity": "sha512-h87nfVh2fdk9fJIvz26wCBsbDC/KxqCc5wSlNMZbXcARtbgNbNDIF7Y7ctokFdnxkzVdaHsbINkh548T9eBA7Q==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-express": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-0.3.6.tgz", + "integrity": "sha512-3iWVtBJZ9RnRnZveNDzOD8QRn6g6vUif0qVTWWyi5nUIAbuN1FfPVyKdAlJJfp5Im+dE8Kiy/d2SpaX/0X678Q==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jquery": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-0.1.0.tgz", + "integrity": "sha1-Agg5cWLjhGmG5xJztsecW1+A9RA=", + "dev": true, + "requires": { + "q": "^1.4.1" + } + }, + "conventional-changelog-jscs": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jscs/-/conventional-changelog-jscs-0.1.0.tgz", + "integrity": "sha1-BHnrRDzH1yxYvwvPDvHURKkvDlw=", + "dev": true, + "requires": { + "q": "^1.4.1" + } + }, + "conventional-changelog-jshint": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-0.3.8.tgz", + "integrity": "sha512-hn9QU4ZI/5V50wKPJNPGT4gEWgiBFpV6adieILW4MaUFynuDYOvQ71EMSj3EznJyKi/KzuXpc9dGmX8njZMjig==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-preset-loader": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-1.1.8.tgz", + "integrity": "sha512-MkksM4G4YdrMlT2MbTsV2F6LXu/hZR0Tc/yenRrDIKRwBl/SP7ER4ZDlglqJsCzLJi4UonBc52Bkm5hzrOVCcw==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-3.0.9.tgz", + "integrity": "sha512-n9KbsxlJxRQsUnK6wIBRnARacvNnN4C/nxnxCkH+B/R1JS2Fa+DiP1dU4I59mEDEjgnFaN2+9wr1P1s7GYB5/Q==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "conventional-commits-filter": "^1.1.6", + "dateformat": "^3.0.0", + "handlebars": "^4.0.2", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "semver": "^5.5.0", + "split": "^1.0.0", + "through2": "^2.0.0" + } + }, + "conventional-commits-filter": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-1.1.6.tgz", + "integrity": "sha512-KcDgtCRKJCQhyk6VLT7zR+ZOyCnerfemE/CsR3iQpzRRFbLEs0Y6rwk3mpDvtOh04X223z+1xyJ582Stfct/0Q==", + "dev": true, + "requires": { + "is-subset": "^0.1.1", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-2.1.7.tgz", + "integrity": "sha512-BoMaddIEJ6B4QVMSDu9IkVImlGOSGA1I2BQyOZHeLQ6qVOJLcLKn97+fL6dGbzWEiqDzfH4OkcveULmeq2MHFQ==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.0", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0", + "trim-off-newlines": "^1.0.0" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "copy-webpack-plugin": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", + "dev": true, + "requires": { + "cacache": "^12.0.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.1", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "webpack-log": "^2.0.0" + } + }, + "core-js": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" + }, + "core-js-compat": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.0.tgz", + "integrity": "sha512-pgQUcgT2+v9/yxHgMynYjNj7nmxLRXv3UC39rjCjDwpe63ev2rioQTju1PKLYUBbPCQQvZNWvQC8tBJd65q11g==", + "dev": true, + "requires": { + "browserslist": "^4.7.2", + "semver": "^6.3.0" + }, + "dependencies": { + "browserslist": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.2.tgz", + "integrity": "sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001004", + "electron-to-chromium": "^1.3.295", + "node-releases": "^1.1.38" + } + }, + "caniuse-lite": { + "version": "1.0.30001008", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001008.tgz", + "integrity": "sha512-b8DJyb+VVXZGRgJUa30cbk8gKHZ3LOZTBLaUEEVr2P4xpmFigOCc62CO4uzquW641Ouq1Rm9N+rWLWdSYDaDIw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "coveralls": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-2.13.3.tgz", + "integrity": "sha512-iiAmn+l1XqRwNLXhW8Rs5qHZRFMYp9ZIPjEOVRpC/c4so6Y/f4/lFi0FfR5B9cCqgyhkJ5cZmbvcVRfP8MHchw==", + "dev": true, + "requires": { + "js-yaml": "3.6.1", + "lcov-parse": "0.0.10", + "log-driver": "1.2.5", + "minimist": "1.2.0", + "request": "2.79.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "js-yaml": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", + "integrity": "sha1-bl/mfYsgXOTSL60Ft3geja3MSzA=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "dev": true + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "qs": "~6.3.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "~0.4.1", + "uuid": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + } + } + }, + "cp-file": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-3.2.0.tgz", + "integrity": "sha1-b4NhYlRiTwrViqSqjQdvAmvn4Yg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nested-error-stacks": "^1.0.1", + "object-assign": "^4.0.1", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.0", + "readable-stream": "^2.1.4" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "cpy": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cpy/-/cpy-4.0.1.tgz", + "integrity": "sha1-tnJn66LzlgugalphrJQDNCKDNCQ=", + "dev": true, + "requires": { + "cp-file": "^3.1.0", + "globby": "^4.0.0", + "meow": "^3.6.0", + "nested-error-stacks": "^1.0.0", + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globby": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", + "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^6.0.1", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + } + } + }, + "cpy-cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cpy-cli/-/cpy-cli-1.0.1.tgz", + "integrity": "sha1-Z/taSi3sKMqKv/N13kuecfanVhw=", + "dev": true, + "requires": { + "cpy": "^4.0.0", + "meow": "^3.6.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.x.x" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dev": true, + "requires": { + "css": "^2.0.0" + } + }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cssom": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.2.tgz", + "integrity": "sha512-fVXFVBr7JPDcgqa92UNr6HIpeMypyG/XVloB+512KH43Z2aum8ZNVzRapWR4mZ/f2UlRMymIoDO3aFJmQ6Y3RA==" + }, + "cssstyle": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.0.0.tgz", + "integrity": "sha512-QXSAu2WBsSRXCPjvI43Y40m6fMevvyRm8JVAuF9ksQz5jha4pWP1wpaK7Yu5oLFc6+XAY+hj8YhefyXcBB53gg==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "cypress": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-3.6.1.tgz", + "integrity": "sha512-6n0oqENdz/oQ7EJ6IgESNb2M7Bo/70qX9jSJsAziJTC3kICfEMmJUlrAnP9bn+ut24MlXQST5nRXhUP5nRIx6A==", + "dev": true, + "requires": { + "@cypress/listr-verbose-renderer": "0.4.1", + "@cypress/xvfb": "1.2.4", + "@types/sizzle": "2.3.2", + "arch": "2.1.1", + "bluebird": "3.5.0", + "cachedir": "1.3.0", + "chalk": "2.4.2", + "check-more-types": "2.24.0", + "commander": "2.15.1", + "common-tags": "1.8.0", + "debug": "3.2.6", + "execa": "0.10.0", + "executable": "4.1.1", + "extract-zip": "1.6.7", + "fs-extra": "5.0.0", + "getos": "3.1.1", + "is-ci": "1.2.1", + "is-installed-globally": "0.1.0", + "lazy-ass": "1.6.0", + "listr": "0.12.0", + "lodash": "4.17.15", + "log-symbols": "2.2.0", + "minimist": "1.2.0", + "moment": "2.24.0", + "ramda": "0.24.1", + "request": "2.88.0", + "request-progress": "3.0.0", + "supports-color": "5.5.0", + "tmp": "0.1.0", + "untildify": "3.0.3", + "url": "0.11.0", + "yauzl": "2.10.0" + }, + "dependencies": { + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, + "requires": { + "rimraf": "^2.6.3" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } + }, + "damerau-levenshtein": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz", + "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==", + "dev": true + }, + "dargs": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", + "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=" + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-freeze": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", + "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "default-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true, + "requires": { + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "requires": { + "clone": "^1.0.2" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + } + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "dependency-graph": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", + "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "domino": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.4.tgz", + "integrity": "sha512-l70mlQ7IjPKC8kT7GljQXJZmt5OqFL+RE91ik5y5WWQtsd9wP8R7gpFnNu96fK5MqAAZRXfLLsnzKtkty5fWGQ==" + }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.3.306", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.306.tgz", + "integrity": "sha512-frDqXvrIROoYvikSKTIKbHbzO6M3/qC6kCIt/1FOa9kALe++c4VAJnwjSFvf1tYLEUsP2n9XZ4XSCyqc3l7A/A==", + "dev": true + }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.2.tgz", + "integrity": "sha1-a1m+cws0jAElsKRYneHDVavPen4=", + "dev": true, + "requires": { + "accepts": "1.3.3", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "2.3.3", + "engine.io-parser": "1.3.2", + "ws": "1.1.1" + }, + "dependencies": { + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true, + "requires": { + "mime-types": "~2.1.11", + "negotiator": "0.6.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "0.7.2" + } + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "ws": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz", + "integrity": "sha1-CC3bbGQehdS7RR8D1S8G6r2x8Bg=", + "dev": true, + "requires": { + "options": ">=0.0.5", + "ultron": "1.0.x" + } + } + } + }, + "engine.io-client": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.2.tgz", + "integrity": "sha1-w4dnVH8qfRhPV1L28K1QEAZwN2Y=", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "2.3.3", + "engine.io-parser": "1.3.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parsejson": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "1.1.1", + "xmlhttprequest-ssl": "1.5.3", + "yeast": "0.1.2" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "0.7.2" + } + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + }, + "ws": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz", + "integrity": "sha1-CC3bbGQehdS7RR8D1S8G6r2x8Bg=", + "dev": true, + "requires": { + "options": ">=0.0.5", + "ultron": "1.0.x" + } + } + } + }, + "engine.io-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", + "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "0.0.6", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.4", + "has-binary": "0.1.7", + "wtf-8": "1.0.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=" + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", + "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promise-pool": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/es6-promise-pool/-/es6-promise-pool-2.5.0.tgz", + "integrity": "sha1-FHxhKza0fxBQJ/nSv1SlmKmdnMs=" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "dev": true + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "requires": { + "pify": "^2.2.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "expand-braces": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", + "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", + "dev": true, + "requires": { + "array-slice": "^0.2.3", + "array-unique": "^0.2.1", + "braces": "^0.1.2" + }, + "dependencies": { + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", + "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", + "dev": true, + "requires": { + "expand-range": "^0.1.0" + } + }, + "expand-range": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", + "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", + "dev": true, + "requires": { + "is-number": "^0.1.1", + "repeat-string": "^0.2.2" + } + }, + "is-number": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", + "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", + "dev": true + }, + "repeat-string": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", + "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", + "dev": true + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "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.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "dev": true, + "requires": { + "bser": "^2.0.0" + } + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "requires": { + "pend": "~1.2.0" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + }, + "figures": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", + "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.2.0.tgz", + "integrity": "sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "fn-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz", + "integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=", + "dev": true + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "foreground-child": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "dev": true, + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-extra": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0" + }, + "dependencies": { + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", + "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.3", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.4.0", + "bundled": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.7", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "g-status": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/g-status/-/g-status-2.0.2.tgz", + "integrity": "sha512-kQoE9qH+T1AHKgSSD0Hkv98bobE90ILQcXAF4wvGgsr7uFqNvwmh8j+Lq3l0RVt3E3HjSbv2B9biEGcEtpHLCA==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "matcher": "^1.0.0", + "simple-git": "^1.85.0" + } + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "requires": { + "is-property": "^1.0.2" + } + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "^1.0.0" + } + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz", + "integrity": "sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA==", + "dev": true + }, + "get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "meow": "^3.3.0", + "normalize-package-data": "^2.3.0", + "parse-github-repo-url": "^1.3.0", + "through2": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + } + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getos": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.1.1.tgz", + "integrity": "sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg==", + "dev": true, + "requires": { + "async": "2.6.1" + }, + "dependencies": { + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-raw-commits": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-1.3.6.tgz", + "integrity": "sha512-svsK26tQ8vEKnMshTDatSIQSMDdz8CxIIqKsvPqbtV23Etmw6VNaFAitu8zwZ0VrOne7FztwPyRLxK7/DIUTQg==", + "dev": true, + "requires": { + "dargs": "^4.0.1", + "lodash.template": "^4.0.2", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0" + } + }, + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", + "dev": true, + "requires": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "git-semver-tags": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-1.3.6.tgz", + "integrity": "sha512-2jHlJnln4D/ECk9FxGEBh3k44wgYdWjWDtMmJPaecjoRmxKo3Y1Lh8GMYuOPu04CHw86NTAODchYjC5pnpMQig==", + "dev": true, + "requires": { + "meow": "^4.0.0", + "semver": "^5.5.0" + } + }, + "gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", + "dev": true, + "requires": { + "ini": "^1.3.2" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "requires": { + "ini": "^1.3.4" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + } + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", + "dev": true + }, + "handlebars": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", + "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + } + } + }, + "has-binary": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", + "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", + "dev": true, + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-parser-js": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", + "dev": true + }, + "http-proxy": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "requires": { + "ms": "^2.0.0" + } + }, + "husky": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz", + "integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.7", + "execa": "^1.0.0", + "find-up": "^3.0.0", + "get-stdin": "^6.0.0", + "is-ci": "^2.0.0", + "pkg-dir": "^3.0.0", + "please-upgrade-node": "^3.1.1", + "read-pkg": "^4.0.1", + "run-node": "^1.0.0", + "slash": "^2.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", + "dev": true, + "requires": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz", + "integrity": "sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "dev": true + }, + "is-my-json-valid": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.0.tgz", + "integrity": "sha512-XTHBZSIIxNsIsZXg7XB5l8z/OBFosl1Wao4tXLpeC7eKU4Vm/kdop2azkPqULwnfGQjmeDIyey9g7afMMtdWAA==", + "dev": true, + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + }, + "is-observable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "dev": true, + "requires": { + "symbol-observable": "^1.1.0" + } + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + }, + "dependencies": { + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + } + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" + }, + "is-reference": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", + "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", + "dev": true, + "requires": { + "@types/estree": "0.0.39" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul-api": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", + "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", + "dev": true, + "requires": { + "async": "^2.1.4", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.2.1", + "istanbul-lib-hook": "^1.2.2", + "istanbul-lib-instrument": "^1.10.2", + "istanbul-lib-report": "^1.1.5", + "istanbul-lib-source-maps": "^1.2.6", + "istanbul-reports": "^1.5.1", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz", + "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.2.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "istanbul-instrumenter-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", + "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", + "dev": true, + "requires": { + "convert-source-map": "^1.5.0", + "istanbul-lib-instrument": "^1.7.3", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "requires": { + "ajv": "^5.0.0" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz", + "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==", + "dev": true, + "requires": { + "append-transform": "^0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz", + "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^1.2.1", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz", + "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==", + "dev": true, + "requires": { + "handlebars": "^4.0.3" + } + }, + "jasmine": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.5.0.tgz", + "integrity": "sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ==", + "dev": true, + "requires": { + "glob": "^7.1.4", + "jasmine-core": "~3.5.0" + }, + "dependencies": { + "jasmine-core": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", + "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", + "dev": true + } + } + }, + "jasmine-core": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.5.2.tgz", + "integrity": "sha1-b2G9eQYeJ/Q+b5NV5Es8bKtv8pc=", + "dev": true + }, + "jasmine-marbles": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/jasmine-marbles/-/jasmine-marbles-0.4.1.tgz", + "integrity": "sha512-i5jjbwSX2dxMCt6uhNZCHh8QhjD4rJtUZDhCqz6NwelQezV6ScM22cuvWNlDQrGSvdM71elKUfGQrSMJYIoS2g==", + "dev": true, + "requires": { + "lodash": "^4.5.0" + } + }, + "jasmine-spec-reporter": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-3.2.0.tgz", + "integrity": "sha1-/b6FqAzN07J2dGvHf96Dwc53Pv8=", + "dev": true, + "requires": { + "colors": "1.1.2" + }, + "dependencies": { + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + } + } + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, + "requires": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + } + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + }, + "dependencies": { + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + } + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "jest-environment-jsdom-thirteen": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom-thirteen/-/jest-environment-jsdom-thirteen-1.0.1.tgz", + "integrity": "sha512-Zi7OuKF7HMLlBvomitd5eKp5Ykc4Wvw0d+i+cpbCaE+7kmvL24SO4ssDmKrT++aANXR4T8+pmoJIlav5gr2peQ==", + "dev": true, + "requires": { + "jest-mock": "^24.0.0", + "jest-util": "^24.0.0", + "jsdom": "^13.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "dev": true + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "jsdom": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.2.0.tgz", + "integrity": "sha512-cG1NtMWO9hWpqRNRR3dSvEQa8bFI6iLlqU2x4kwX51FQjp0qus8T9aBaAO6iGp3DeBrhdwuKxckknohkmfvsFw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.0.9", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.5", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + }, + "dependencies": { + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + } + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-preset-angular": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-7.1.1.tgz", + "integrity": "sha512-/uJUi9IHoCxtB6aH6I+llpHjyfGfP2q32lcBCUKKk16v1uYKXvj1Nfdlvfvu/eqjZVoKEDLP+Ejup4bbsc0gAA==", + "dev": true, + "requires": { + "jest-environment-jsdom-thirteen": "^1.0.0", + "pretty-format": "^24.0.0", + "ts-jest": "^24.0.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + }, + "dependencies": { + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + } + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + }, + "dependencies": { + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + } + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + } + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-zone-patch": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/jest-zone-patch/-/jest-zone-patch-0.0.10.tgz", + "integrity": "sha512-K5uHLHgMgi2Eyj74gbY+xSeGGekb5U48bXsgDwgipRbFdaekyZK+TAcp8auamqU4UjrAt5S4sIUZz/2bBNyTTA==", + "dev": true + }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=" + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", + "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", + "requires": { + "abab": "^2.0.0", + "acorn": "^7.1.0", + "acorn-globals": "^4.3.2", + "array-equal": "^1.0.0", + "cssom": "^0.4.1", + "cssstyle": "^2.0.0", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.1", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.2.0", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.7", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^7.0.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "karma": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-1.4.1.tgz", + "integrity": "sha1-QZgacdVCN2BrCj6oxYyQdz9BZQ4=", + "dev": true, + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.12.4", + "chokidar": "^1.4.1", + "colors": "^1.1.0", + "combine-lists": "^1.0.0", + "connect": "^3.3.5", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "expand-braces": "^0.1.1", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^3.8.0", + "log4js": "^0.6.31", + "mime": "^1.3.4", + "minimatch": "^3.0.0", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.3.3", + "safe-buffer": "^5.0.1", + "socket.io": "1.7.2", + "source-map": "^0.5.3", + "tmp": "0.0.28", + "useragent": "^2.1.10" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "tmp": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz", + "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "karma-chrome-launcher": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.0.0.tgz", + "integrity": "sha1-wnkMWjKxVXfQ//Wk1aJwOztDnCU=", + "dev": true, + "requires": { + "fs-access": "^1.0.0", + "which": "^1.2.1" + } + }, + "karma-cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz", + "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-0.2.3.tgz", + "integrity": "sha1-EfG+nPqTdVp3usOasW4xWnEAtcU=", + "dev": true, + "requires": { + "istanbul-api": "^1.1.1" + } + }, + "karma-jasmine": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz", + "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=", + "dev": true + }, + "karma-jasmine-html-reporter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", + "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", + "dev": true, + "requires": { + "karma-jasmine": "^1.0.2" + } + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "lcov-parse": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", + "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=", + "dev": true + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "less": { + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/less/-/less-3.10.3.tgz", + "integrity": "sha512-vz32vqfgmoxF1h3K4J+yKCtajH0PWmjkIFgbs5d78E/c/e+UQTnI+lWK+1eQRE95PXM2mC3rJlLSSP9VQHnaow==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz", + "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "license-webpack-plugin": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.3.tgz", + "integrity": "sha512-vTSY5r9HOq4sxR2BIxdIXWKI+9n3b+DoQkhKHedB3TdSxTfXUDRxKXdAj5iejR+qNXprXsxvEu9W+zOhgGIkAw==", + "dev": true, + "requires": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + } + }, + "lint-staged": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.2.1.tgz", + "integrity": "sha512-n0tDGR/rTCgQNwXnUf/eWIpPNddGWxC32ANTNYsj2k02iZb7Cz5ox2tytwBu+2r0zDXMEMKw7Y9OD/qsav561A==", + "dev": true, + "requires": { + "chalk": "^2.3.1", + "commander": "^2.14.1", + "cosmiconfig": "^5.2.0", + "debug": "^3.1.0", + "dedent": "^0.7.0", + "del": "^3.0.0", + "execa": "^1.0.0", + "g-status": "^2.0.2", + "is-glob": "^4.0.0", + "is-windows": "^1.0.2", + "listr": "^0.14.2", + "listr-update-renderer": "^0.5.0", + "lodash": "^4.17.11", + "log-symbols": "^2.2.0", + "micromatch": "^3.1.8", + "npm-which": "^3.0.1", + "p-map": "^1.1.1", + "path-is-inside": "^1.0.2", + "pify": "^3.0.0", + "please-upgrade-node": "^3.0.2", + "staged-git-files": "1.1.2", + "string-argv": "^0.0.2", + "stringify-object": "^3.2.2", + "yup": "^0.27.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "listr": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", + "dev": true, + "requires": { + "@samverschueren/stream-to-observable": "^0.3.0", + "is-observable": "^1.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "listr-update-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + } + } + }, + "listr-verbose-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", + "date-fns": "^1.27.2", + "figures": "^2.0.0" + }, + "dependencies": { + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + } + } + }, + "log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + } + } + }, + "listr": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", + "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "figures": "^1.7.0", + "indent-string": "^2.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.2.0", + "listr-verbose-renderer": "^0.4.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "ora": "^0.2.3", + "p-map": "^1.1.1", + "rxjs": "^5.0.0-beta.11", + "stream-to-observable": "^0.1.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "ora": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", + "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "cli-cursor": "^1.0.2", + "cli-spinners": "^0.1.2", + "object-assign": "^4.0.1" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz", + "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "log-driver": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.5.tgz", + "integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY=", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "log-update": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", + "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", + "dev": true, + "requires": { + "ansi-escapes": "^1.0.0", + "cli-cursor": "^1.0.2" + }, + "dependencies": { + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + } + } + }, + "log4js": { + "version": "0.6.38", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", + "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", + "dev": true, + "requires": { + "readable-stream": "~1.0.2", + "semver": "~4.3.3" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "loglevel": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", + "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==", + "dev": true + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "macos-release": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", + "dev": true + }, + "magic-string": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz", + "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-fetch-happen": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", + "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "matcher": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz", + "integrity": "sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.4" + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mini-css-extract-plugin": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", + "integrity": "sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", + "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", + "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minipass-pipeline": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz", + "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", + "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "dev": true + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true + }, + "ndb": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ndb/-/ndb-1.1.5.tgz", + "integrity": "sha512-YiK+1wRe9SnZIyZ3kLDmogV2/Z0+8uPgHNbk3ExXzJg6IEFSenfskkn82v5ajPUJKkeEpsaK+ae1eT1be8Hllw==", + "requires": { + "carlo": "^0.9.46", + "chokidar": "^3.0.2", + "debug": "^4.1.1", + "isbinaryfile": "^3.0.3", + "mime": "^2.4.4", + "node-pty": "^0.9.0-beta18", + "opn": "^5.5.0", + "update-notifier": "^2.5.0", + "which": "^1.3.1", + "ws": "^6.2.1", + "xterm": "^3.14.5" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "optional": true + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "requires": { + "picomatch": "^2.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nested-error-stacks": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz", + "integrity": "sha1-GfYZWRUZ8JZ2mlupqG5u7sgjw88=", + "dev": true, + "requires": { + "inherits": "~2.0.1" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "node-fetch-npm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-forge": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node-pty": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.9.0.tgz", + "integrity": "sha512-MBnCQl83FTYOu7B4xWw10AW77AAh7ThCE1VXEv+JeWj8mSpGo+0bwgsV+b23ljBFwEM9OmsOv3kM27iUPPm84g==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "node-releases": { + "version": "1.1.39", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.39.tgz", + "integrity": "sha512-8MRC/ErwNCHOlAFycy9OPca46fQYUjbJRDcZTHVWIGXIjYLM73k70vv3WkYutVnM4cCo4hE0MqBVVZjP6vjISA==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz", + "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-path": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz", + "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==", + "dev": true, + "requires": { + "which": "^1.2.10" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-registry-fetch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.2.tgz", + "integrity": "sha512-Z0IFtPEozNdeZRPh3aHHxdG+ZRpzcbQaJLthsm3VhNf6DScicTFRHZzK82u8RsJUsUHkX+QH/zcB/5pmd20H4A==", + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + } + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", + "integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=", + "dev": true, + "requires": { + "commander": "^2.9.0", + "npm-path": "^2.0.2", + "which": "^1.2.10" + } + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" + }, + "nyc": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-10.3.2.tgz", + "integrity": "sha1-8n9NkfKp2zbCT1dP9cbv/wIz3kY=", + "dev": true, + "requires": { + "archy": "^1.0.0", + "arrify": "^1.0.1", + "caching-transform": "^1.0.0", + "convert-source-map": "^1.3.0", + "debug-log": "^1.0.1", + "default-require-extensions": "^1.0.0", + "find-cache-dir": "^0.1.1", + "find-up": "^1.1.2", + "foreground-child": "^1.5.3", + "glob": "^7.0.6", + "istanbul-lib-coverage": "^1.1.0", + "istanbul-lib-hook": "^1.0.6", + "istanbul-lib-instrument": "^1.7.1", + "istanbul-lib-report": "^1.1.0", + "istanbul-lib-source-maps": "^1.2.0", + "istanbul-reports": "^1.1.0", + "md5-hex": "^1.2.0", + "merge-source-map": "^1.0.2", + "micromatch": "^2.3.11", + "mkdirp": "^0.5.0", + "resolve-from": "^2.0.0", + "rimraf": "^2.5.4", + "signal-exit": "^3.0.1", + "spawn-wrap": "1.2.4", + "test-exclude": "^4.1.0", + "yargs": "^7.1.0", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true, + "requires": { + "default-require-extensions": "^1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", + "integrity": "sha1-onTthawIhJtr14R8RYB0XcUa37E=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "babel-code-frame": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", + "dev": true, + "requires": { + "chalk": "^1.1.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "babel-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.1.tgz", + "integrity": "sha1-5xX0hsWN7SVknYiJRNUqoHxdlJc=", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.2.0", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.10.0" + } + }, + "babel-template": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.24.1.tgz", + "integrity": "sha1-BK5RTx+Ts6JTfyoPYKWkX7gwgzM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1", + "babylon": "^6.11.0", + "lodash": "^4.2.0" + } + }, + "babel-traverse": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.24.1.tgz", + "integrity": "sha1-qzZnP9NW+aCUhlnnszjV/q2zFpU=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1", + "babylon": "^6.15.0", + "debug": "^2.2.0", + "globals": "^9.0.0", + "invariant": "^2.2.0", + "lodash": "^4.2.0" + } + }, + "babel-types": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.24.1.tgz", + "integrity": "sha1-oTaHncFbNga9oNkMH8dDBML/CXU=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "esutils": "^2.0.2", + "lodash": "^4.2.0", + "to-fast-properties": "^1.0.1" + } + }, + "babylon": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.0.tgz", + "integrity": "sha1-N9qUiHhIi5xOPEA4iT+jMUs/yTI=", + "dev": true + }, + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "dev": true, + "requires": { + "balanced-match": "^0.4.1", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "caching-transform": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-1.0.1.tgz", + "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", + "dev": true, + "requires": { + "md5-hex": "^1.2.0", + "mkdirp": "^0.5.1", + "write-file-atomic": "^1.1.4" + } + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "optional": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "optional": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "optional": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "optional": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "convert-source-map": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", + "dev": true + }, + "core-js": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", + "dev": true + }, + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "debug": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz", + "integrity": "sha1-qfpvvpykPPHnn3O3XAGJy7fW21o=", + "dev": true, + "requires": { + "ms": "0.7.3" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "default-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true, + "requires": { + "strip-bom": "^2.0.0" + } + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^1.1.3", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "foreground-child": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "dev": true, + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "globals": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz", + "integrity": "sha1-DAymltm5u2lNLlRwvTd3fKrVAoY=", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "handlebars": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.8.tgz", + "integrity": "sha1-Irh1zT8ObL6jAxTxROgrx6cv9CA=", + "dev": true, + "requires": { + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "hosted-git-info": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", + "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-dotfile": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.2.tgz", + "integrity": "sha1-LBMjg/ORmfjtwmjKAbmwB9IFzE0=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "istanbul-lib-coverage": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz", + "integrity": "sha1-ysoZ3srvNSW11jMdcB8/O3rUhSg=", + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.0.6.tgz", + "integrity": "sha1-wIZtHoHPLVMZJJUQEx/Bbe5JIx8=", + "dev": true, + "requires": { + "append-transform": "^0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.1.tgz", + "integrity": "sha1-Fp4xvGLHeIUamUOd2Zw8wSGE02A=", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.13.0", + "istanbul-lib-coverage": "^1.1.0", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.0.tgz", + "integrity": "sha1-RExOzKmvqTz1hPVrEPGVv3aMB3A=", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^1.1.0", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.0.tgz", + "integrity": "sha1-jHcG1Jfib+62rz4MKP1bBmlZjQ4=", + "dev": true, + "requires": { + "debug": "^2.6.3", + "istanbul-lib-coverage": "^1.1.0", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" + } + }, + "istanbul-reports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.0.tgz", + "integrity": "sha1-HvO3lYiSGc+1+tFjZfbOEI1fjGY=", + "dev": true, + "requires": { + "handlebars": "^4.0.3" + } + }, + "js-tokens": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", + "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=", + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "kind-of": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.0.tgz", + "integrity": "sha1-tYq+TVwEStM3JqjBUltIz4kb/wc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true, + "optional": true + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", + "dev": true, + "requires": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "md5-hex": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-1.3.0.tgz", + "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", + "dev": true, + "requires": { + "md5-o-matic": "^0.1.1" + } + }, + "md5-o-matic": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/md5-o-matic/-/md5-o-matic-0.1.1.tgz", + "integrity": "sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=", + "dev": true + }, + "merge-source-map": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.3.tgz", + "integrity": "sha1-2hQV8nIqURnbB7FMT5c0EIY6Kr8=", + "dev": true, + "requires": { + "source-map": "^0.5.3" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", + "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=", + "dev": true + }, + "normalize-package-data": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", + "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + } + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "randomatic": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz", + "integrity": "sha1-EQ3Kv/OX6dz/fAeJzMCkmt8exbs=", + "dev": true, + "requires": { + "is-number": "^2.0.2", + "kind-of": "^3.0.2" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + }, + "regex-cache": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", + "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3", + "is-primitive": "^2.0.0" + } + }, + "remove-trailing-separator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz", + "integrity": "sha1-YV67lq9VlVLUv0BXyENtSGq2PMQ=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "optional": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "spawn-wrap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.2.4.tgz", + "integrity": "sha1-kg6yEadpwJPuv71bDnpdLmirLkA=", + "dev": true, + "requires": { + "foreground-child": "^1.3.3", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.3.3", + "signal-exit": "^2.0.0", + "which": "^1.2.4" + }, + "dependencies": { + "signal-exit": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-2.1.2.tgz", + "integrity": "sha1-N1h5sfkuvDszRIDQONxUam1VhWQ=", + "dev": true + } + } + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "^1.0.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "test-exclude": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.0.tgz", + "integrity": "sha1-BMpwtzkN04yY1KADoXOAbKeZHJE=", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "micromatch": "^2.3.11", + "object-assign": "^4.1.0", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1" + } + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "uglify-js": { + "version": "2.8.22", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.22.tgz", + "integrity": "sha1-1Uk0d4qNoUkD+imjJvskwKtRoaA=", + "dev": true, + "optional": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "~1.0.0", + "spdx-expression-parse": "~1.0.0" + } + }, + "which": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", + "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + } + } + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", + "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.0.0.tgz", + "integrity": "sha512-K6EKzYqnwQzk+/dzJAQSBORub3xlBTxMz+ntpZpH/LyCa1o6KjXhuN+2npAaI9jaSmU3R1Q8NWf4KUWcyytGsQ==", + "requires": { + "is-wsl": "^2.1.0" + }, + "dependencies": { + "is-wsl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==" + } + } + }, + "opencollective": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/opencollective/-/opencollective-1.0.3.tgz", + "integrity": "sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE=", + "requires": { + "babel-polyfill": "6.23.0", + "chalk": "1.1.3", + "inquirer": "3.0.6", + "minimist": "1.2.0", + "node-fetch": "1.6.3", + "opn": "4.0.2" + }, + "dependencies": { + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "inquirer": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz", + "integrity": "sha1-4EqqnQW3o8ubD0B9BDdfBEcZA0c=", + "requires": { + "ansi-escapes": "^1.1.0", + "chalk": "^1.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.1", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx": "^4.1.0", + "string-width": "^2.0.0", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "node-fetch": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", + "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "options": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", + "dev": true + }, + "ora": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-1.4.0.tgz", + "integrity": "sha512-iMK1DOQxzzh2MBlVsU42G80mnrvUhqsMh74phHtDlrcTZPK0pH6o7l7DRshK+0YsxDyEuaOkziVdvM3T0QTzpw==", + "dev": true, + "requires": { + "chalk": "^2.1.0", + "cli-cursor": "^2.1.0", + "cli-spinners": "^1.0.1", + "log-symbols": "^2.1.0" + }, + "dependencies": { + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-spinners": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", + "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + } + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "dev": true, + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + }, + "dependencies": { + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "9.5.8", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.8.tgz", + "integrity": "sha512-0Tl8Oi/K0Lo4MZmH0/6IsT3gpGf9eEAznLXEQPKgPq7FscnbUOyopnVpwXlnQdIbCUaojWy1Wd7VMyqfVsRrIw==", + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^3.0.0", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + } + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-github-repo-url": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "parsejson": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", + "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picomatch": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", + "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==" + }, + "pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "png-async": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/png-async/-/png-async-0.9.4.tgz", + "integrity": "sha512-B//AXX9TkneKfgtOpT1mdUnnhk2BImGD+a98vImsMU8uo1dBeHyW/kM2erWZ/CsYteTPU/xKG+t6T62heHkC3A==" + }, + "portfinder": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-import": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", + "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + } + }, + "postcss-value-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", + "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "prompts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.0.tgz", + "integrity": "sha512-NfbbPPg/74fT7wk2XYQ7hAIp9zJyZp5Fu19iRbORqqy1BhtrkZ0fPafBU+7bmn8ie69DpT0R6QpJIN2oisYjJg==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.3" + } + }, + "property-expr": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-1.5.1.tgz", + "integrity": "sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g==", + "dev": true + }, + "protobufjs": { + "version": "6.8.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", + "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "dev": true, + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "10.17.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", + "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", + "dev": true + } + } + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "requires": { + "genfun": "^5.0.0" + } + }, + "protractor": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.1.2.tgz", + "integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=", + "dev": true, + "requires": { + "@types/node": "^6.0.46", + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "~2.53.39", + "blocking-proxy": "0.0.5", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "^2.5.3", + "jasminewd2": "^2.1.0", + "optimist": "~0.6.0", + "q": "1.4.1", + "saucelabs": "~1.3.0", + "selenium-webdriver": "3.0.1", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "^1.0.0", + "webdriver-manager": "^12.0.6" + }, + "dependencies": { + "@types/node": { + "version": "6.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.9.tgz", + "integrity": "sha512-leP/gxHunuazPdZaCvsCefPQxinqUDsCxCR5xaDUrY2MkYxQRFZZwU5e7GojyYsGB7QVtCi7iVEl/hoFXQYc+w==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "jasmine": { + "version": "2.99.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.99.0.tgz", + "integrity": "sha1-jKctEC5jm4Z8ZImFbg4YqceqQrc=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.99.0" + } + }, + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "webdriver-manager": { + "version": "12.1.7", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", + "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + } + } + } + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "puppeteer-core": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-1.12.2.tgz", + "integrity": "sha512-M+atMV5e+MwJdR+OwQVZ1xqAIwh3Ou4nUxNuf334GwpcLG+LDj5BwIph4J9y8YAViByRtWGL+uF8qX2Ggzb+Fg==", + "requires": { + "debug": "^4.1.0", + "extract-zip": "^1.6.6", + "https-proxy-agent": "^2.2.1", + "mime": "^2.0.3", + "progress": "^2.0.1", + "proxy-from-env": "^1.0.0", + "rimraf": "^2.6.1", + "ws": "^6.1.0" + }, + "dependencies": { + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "raw-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-3.1.0.tgz", + "integrity": "sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^2.0.1" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "react-is": { + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz", + "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-package-json": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", + "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", + "requires": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" + }, + "regenerator-transform": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", + "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", + "dev": true, + "requires": { + "private": "^0.1.6" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz", + "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + } + } + }, + "regexpu-core": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", + "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.1.0", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + } + }, + "registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "requires": { + "rc": "^1.0.1" + } + }, + "regjsgen": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "dev": true + }, + "regjsparser": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", + "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-in-file": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-3.4.4.tgz", + "integrity": "sha512-ehq0dFsxSpfPiPLBU5kli38Ud8bZL0CQKG8WQVbvhmyilXaMJ8y4LtDZs/K3MD8C0+rHbsfW8c9r2bUEy0B/6Q==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "glob": "^7.1.3", + "yargs": "^13.2.2" + }, + "dependencies": { + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + } + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, + "request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "requires": { + "lodash": "^4.17.15" + } + }, + "request-promise-native": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "requires": { + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "requires": { + "path-parse": "^1.0.5" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rollup": { + "version": "1.25.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.25.2.tgz", + "integrity": "sha512-+7z6Wab/L45QCPcfpuTZKwKiB0tynj05s/+s2U3F2Bi7rOLPr9UcjUwO7/xpjlPNXA/hwnth6jBExFRGyf3tMg==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + } + }, + "rollup-plugin-alias": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-alias/-/rollup-plugin-alias-1.5.2.tgz", + "integrity": "sha512-ODeZXhTxpD48sfcYLAFc1BGrsXKDj7o1CSNH3uYbdK3o0NxyMmaQPTNgW+ko+am92DLC8QSTe4kyxTuEkI5S5w==", + "dev": true, + "requires": { + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "rollup-plugin-commonjs": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", + "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "rollup-plugin-node-resolve": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", + "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", + "dev": true, + "requires": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.1", + "rollup-pluginutils": "^2.8.1" + }, + "dependencies": { + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + }, + "resolve": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz", + "integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "rollup-plugin-sourcemaps": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", + "integrity": "sha1-YhJaqUCHqt97g+9N+vYptHMTXoc=", + "dev": true, + "requires": { + "rollup-pluginutils": "^2.0.1", + "source-map-resolve": "^0.5.0" + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", + "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" + }, + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sander": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", + "integrity": "sha1-dB4kXiMfB8r7b98PEzrfohalAq0=", + "dev": true, + "requires": { + "es6-promise": "^3.1.2", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2" + }, + "dependencies": { + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", + "dev": true + } + } + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "sass": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.23.3.tgz", + "integrity": "sha512-1DKRZxJMOh4Bme16AbWTyYeJAjTlrvw2+fWshHHaepeJfGq2soFZTnt0YhWit+bohtDu4LdyPoEj6VFD4APHog==", + "dev": true, + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.3.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.7" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "sass-loader": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.0.tgz", + "integrity": "sha512-+qeMu563PN7rPdit2+n5uuYVR0SSVwm0JsOUsaJXzgYcClWSlmX0iHDnmeOobPkf5kUglVot3QS6SyLyaQoJ4w==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "saucelabs": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.3.0.tgz", + "integrity": "sha1-0kDoAJ33+ocwbsRXimm6O1xCT+4=", + "dev": true, + "requires": { + "https-proxy-agent": "^1.0.0" + }, + "dependencies": { + "agent-base": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", + "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", + "dev": true, + "requires": { + "extend": "~3.0.0", + "semver": "~5.0.1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "https-proxy-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", + "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", + "dev": true, + "requires": { + "agent-base": "2", + "debug": "2", + "extend": "3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "semver": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", + "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", + "dev": true + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "requires": { + "xmlchars": "^2.1.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz", + "integrity": "sha1-ot6l2kqX9mcuiefKcnbO+jZRR6c=", + "dev": true, + "requires": { + "adm-zip": "^0.4.7", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "selfsigned": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", + "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "dev": true, + "requires": { + "node-forge": "0.9.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "requires": { + "semver": "^5.0.3" + } + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "requires": { + "semver": "^5.0.0" + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "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.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-git": { + "version": "1.126.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.126.0.tgz", + "integrity": "sha512-47mqHxgZnN8XRa9HbpWprzUv3Ooqz9RY/LSZgvA7jCkW8jcwLahMz7LKugY91KZehfG0sCVPtgXiU72hd6b1Bw==", + "dev": true, + "requires": { + "debug": "^4.0.1" + } + }, + "sisteransi": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", + "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.x.x" + } + }, + "socket.io": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.2.tgz", + "integrity": "sha1-g7u98ueSY7N4kA2kA+eEPgXcO3E=", + "dev": true, + "requires": { + "debug": "2.3.3", + "engine.io": "1.8.2", + "has-binary": "0.1.7", + "object-assign": "4.1.0", + "socket.io-adapter": "0.5.0", + "socket.io-client": "1.7.2", + "socket.io-parser": "2.3.1" + }, + "dependencies": { + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "0.7.2" + } + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + }, + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", + "dev": true + } + } + }, + "socket.io-adapter": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", + "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", + "dev": true, + "requires": { + "debug": "2.3.3", + "socket.io-parser": "2.3.1" + }, + "dependencies": { + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "0.7.2" + } + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "socket.io-client": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.2.tgz", + "integrity": "sha1-Of2ww91FDjIbfkDP2DYS7FM91kQ=", + "dev": true, + "requires": { + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "2.3.3", + "engine.io-client": "1.8.2", + "has-binary": "0.1.7", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseuri": "0.0.5", + "socket.io-parser": "2.3.1", + "to-array": "0.1.4" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "0.7.2" + } + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "socket.io-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", + "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", + "dev": true, + "requires": { + "component-emitter": "1.1.2", + "debug": "2.2.0", + "isarray": "0.0.1", + "json3": "3.3.2" + }, + "dependencies": { + "component-emitter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", + "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", + "dev": true + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "socks": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sorcery": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", + "integrity": "sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=", + "dev": true, + "requires": { + "buffer-crc32": "^0.2.5", + "minimist": "^1.2.0", + "sander": "^0.5.0", + "sourcemap-codec": "^1.3.0" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-loader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", + "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "dev": true, + "requires": { + "async": "^2.5.0", + "loader-utils": "^1.1.0" + } + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "sourcemap-codec": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", + "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==" + }, + "spawn-wrap": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", + "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "dev": true, + "requires": { + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + }, + "spdy": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", + "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "speed-measure-webpack-plugin": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz", + "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "dev": true, + "requires": { + "through2": "^2.0.2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "staged-git-files": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.2.tgz", + "integrity": "sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "stream-to-observable": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz", + "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-argv": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz", + "integrity": "sha1-2sMECGkMIfPDYwo/86BYd73L1zY=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "style-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.0.0.tgz", + "integrity": "sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.0.1" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, + "stylus": { + "version": "0.54.7", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.7.tgz", + "integrity": "sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug==", + "dev": true, + "requires": { + "css-parse": "~2.0.0", + "debug": "~3.1.0", + "glob": "^7.1.3", + "mkdirp": "~0.5.x", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "semver": "^6.0.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "synchronous-promise": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.10.tgz", + "integrity": "sha512-6PC+JRGmNjiG3kJ56ZMNWDPL8hjyghF5cMXIFOKg+NiwwEZZIvxTWd0pinWKyD227odg9ygF8xVhhz7gb8Uq7A==", + "dev": true + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tempfile": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz", + "integrity": "sha1-W8xOrsxKsscH2LwR2ZzMmiyyh/I=", + "dev": true, + "requires": { + "os-tmpdir": "^1.0.0", + "uuid": "^2.0.1" + }, + "dependencies": { + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "requires": { + "execa": "^0.7.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, + "terser": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", + "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "terser-webpack-plugin": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.2.1.tgz", + "integrity": "sha512-jwdauV5Al7zopR6OAYvIIRcxXCSvLjZjr7uZE8l2tIWb/ryrGN48sJftqGf5k9z09tWhajx53ldp0XPI080YnA==", + "dev": true, + "requires": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.0.0", + "jest-worker": "^24.9.0", + "schema-utils": "^2.5.0", + "serialize-javascript": "^2.1.0", + "source-map": "^0.6.1", + "terser": "^4.3.9", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "dev": true, + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + } + }, + "find-cache-dir": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs-minipass": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.0.0.tgz", + "integrity": "sha512-40Qz+LFXmd9tzYVnnBmZvFfvAADfUA14TXPK1s7IfElJTIZ97rA8w4Kin7Wt5JBrC3ShnnFJO/5vPjPEeJIq9A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "minipass": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", + "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "schema-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, + "tree-kill": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", + "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "ts-jest": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.1.0.tgz", + "integrity": "sha512-HEGfrIEAZKfu1pkaxB9au17b1d9b56YZSqz5eCVE8mX68+5reOvlM93xGOzzCREIov9mdH7JBG+s0UyNAqr0tQ==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "0.x", + "resolve": "1.x", + "semver": "^5.5", + "yargs-parser": "10.x" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "ts-loader": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz", + "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^3.1.4", + "semver": "^5.0.1" + } + }, + "ts-node": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-5.0.1.tgz", + "integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "chalk": "^2.3.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.3", + "yn": "^2.0.0" + } + }, + "ts-snippet": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ts-snippet/-/ts-snippet-4.2.0.tgz", + "integrity": "sha512-tyJROhE6hqvTPc/drhTVu2UfaxWjTtp49UZqMGMSIkgXXO5Stq+4PRwmIQOuiamue5Bu9E7ez9Q9b0walTyQ7w==", + "dev": true, + "requires": { + "tsutils": "^3.0.0" + }, + "dependencies": { + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tslint": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tsutils": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.20.0.tgz", + "integrity": "sha512-qPOBy1/hwLdBxh/TNIpim5qL1WRMR0tgVGBB6shjnpw6/SuS5ZKYyXXxKDYsMsMtVdFOcL+XPGZVEuc+eCOo4A==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", + "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", + "dev": true + }, + "uglify-js": { + "version": "3.6.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.8.tgz", + "integrity": "sha512-XhHJ3S3ZyMwP8kY1Gkugqx3CJh2C3O0y8NPiSxtm1tyD/pktLAkFZsFGpuNfTZddKDQ/bbDBLAd2YyA1pbi8HQ==", + "dev": true, + "requires": { + "commander": "~2.20.3", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "ultron": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", + "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", + "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "universal-analytics": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.20.tgz", + "integrity": "sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw==", + "requires": { + "debug": "^3.0.0", + "request": "^2.88.0", + "uuid": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "universal-user-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.1.0.tgz", + "integrity": "sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q==", + "dev": true, + "requires": { + "os-name": "^3.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "untildify": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz", + "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==", + "dev": true + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "useragent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "dev": true, + "requires": { + "lru-cache": "4.1.x", + "tmp": "0.0.x" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util-promisify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + }, + "v8-coverage": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/v8-coverage/-/v8-coverage-1.0.9.tgz", + "integrity": "sha512-JolsCH1JDI2QULrxkAGZaovJPvg/Q0p20Uj0F5N8fPtYDtz38gNBRPQ/WVXlLLd3d8WHvKN96AfE4XFk4u0g2g==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "foreground-child": "^1.5.6", + "istanbul-lib-coverage": "^1.2.0", + "istanbul-lib-report": "^1.1.3", + "istanbul-reports": "^1.3.0", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "spawn-wrap": "^1.4.2", + "test-exclude": "^5.2.2", + "uuid": "^3.3.2", + "v8-to-istanbul": "1.2.0", + "yargs": "^11.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.1.tgz", + "integrity": "sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "v8-to-istanbul": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-1.2.0.tgz", + "integrity": "sha512-rVSmjdEfJmOHN8GYCbg+XUhbzXZr7DzdaXIslB9DdcopGZEMsW5x5qIdxr/8DcW7msULHNnvs/xUY1TszvhKRw==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "requires": { + "builtins": "^1.0.3" + } + }, + "validator": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", + "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "dependencies": { + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "requires": { + "defaults": "^1.0.3" + } + }, + "webdriver-js-extender": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz", + "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^2.53.35", + "selenium-webdriver": "^2.53.2" + }, + "dependencies": { + "adm-zip": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", + "integrity": "sha1-ph7VrmkFw66lizplfSUDMJEFJzY=", + "dev": true + }, + "sax": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", + "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk=", + "dev": true + }, + "selenium-webdriver": { + "version": "2.53.3", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", + "integrity": "sha1-0p/1qVff8aG0ncRXdW5OS/vc4IU=", + "dev": true, + "requires": { + "adm-zip": "0.4.4", + "rimraf": "^2.2.8", + "tmp": "0.0.24", + "ws": "^1.0.1", + "xml2js": "0.4.4" + } + }, + "tmp": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", + "integrity": "sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI=", + "dev": true + }, + "ws": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", + "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", + "dev": true, + "requires": { + "options": ">=0.0.5", + "ultron": "1.0.x" + } + }, + "xml2js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", + "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=", + "dev": true, + "requires": { + "sax": "0.6.x", + "xmlbuilder": ">=1.0.0" + } + } + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "webpack": { + "version": "4.41.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz", + "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.1", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", + "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", + "dev": true + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", + "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.2.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.4", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.25", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.4.0", + "spdy": "^4.0.1", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "webpack-subresource-integrity": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.3.4.tgz", + "integrity": "sha512-6XbGYzjh30cGQT/NsC+9IAkJP8IL7/t47sbwR5DLSsamiD56Rwv4/+hsgEHsviPvrEFZ0JRAQtCRN3UsR2Pw9g==", + "dev": true, + "requires": { + "webpack-sources": "^1.3.0" + } + }, + "websocket-driver": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0 <0.4.11", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "requires": { + "string-width": "^2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "windows-release": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", + "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", + "dev": true, + "requires": { + "execa": "^1.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "worker-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-3.2.0.tgz", + "integrity": "sha512-W5nRkw7+HlbsEt3qRP6MczwDDISjiRj2GYt9+bpe8A2La00TmJdwzG5bpdMXhRt1qcWmwAvl1TiKaHRa+XDS9Q==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz", + "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==", + "requires": { + "async-limiter": "^1.0.0" + } + }, + "wtf-8": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", + "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", + "dev": true + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xml2js": { + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", + "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "util.promisify": "~1.0.0", + "xmlbuilder": "~11.0.0" + }, + "dependencies": { + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xmlhttprequest-ssl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", + "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "xterm": { + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-3.14.5.tgz", + "integrity": "sha512-DVmQ8jlEtL+WbBKUZuMxHMBgK/yeIZwkXB81bH+MGaKKnJGYwA+770hzhXPfwEIokK9On9YIFPRleVp/5G7z9g==" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yargs": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.1.0.tgz", + "integrity": "sha512-1UhJbXfzHiPqkfXNHYhiz79qM/kZqjTE8yGlEjZa85Q+3+OwcV6NRkV7XOV1W2Eom2bzILeUn55pQYffjVOLAg==", + "requires": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "requires": { + "fd-slicer": "~1.0.1" + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "yup": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.27.0.tgz", + "integrity": "sha512-v1yFnE4+u9za42gG/b/081E7uNW9mUj3qtkmelLbW5YPROZzSH/KUUyJu9Wt8vxFJcT9otL/eZopS0YK1L5yPQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.0.0", + "fn-name": "~2.0.1", + "lodash": "^4.17.11", + "property-expr": "^1.5.0", + "synchronous-promise": "^2.0.6", + "toposort": "^2.0.2" + } + }, + "z-schema": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", + "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", + "requires": { + "commander": "^2.7.1", + "lodash.get": "^4.0.0", + "lodash.isequal": "^4.0.0", + "validator": "^8.0.0" + } + }, + "zone.js": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.2.tgz", + "integrity": "sha512-UAYfiuvxLN4oyuqhJwd21Uxb4CNawrq6fPS/05Su5L4G+1TN+HVDJMUHNMobVQDFJRir2cLAODXwluaOKB7HFg==" + } + } +} diff --git a/package.json b/package.json index a455807176..eefe705e3c 100644 --- a/package.json +++ b/package.json @@ -190,4 +190,4 @@ "pre-commit": "lint-staged" } } -} \ No newline at end of file +} diff --git a/projects/example-app/src/app/auth/containers/login-page.component.spec.ts b/projects/example-app/src/app/auth/containers/login-page.component.spec.ts index 2dfdbbdfdb..a26aacc90d 100644 --- a/projects/example-app/src/app/auth/containers/login-page.component.spec.ts +++ b/projects/example-app/src/app/auth/containers/login-page.component.spec.ts @@ -29,7 +29,7 @@ describe('Login Page', () => { fixture = TestBed.createComponent(LoginPageComponent); instance = fixture.componentInstance; - store = TestBed.get(Store); + store = TestBed.get(Store); spyOn(store, 'dispatch'); }); diff --git a/projects/example-app/src/app/auth/services/auth-guard.service.spec.ts b/projects/example-app/src/app/auth/services/auth-guard.service.spec.ts index a2483de2ba..f29db8bf90 100644 --- a/projects/example-app/src/app/auth/services/auth-guard.service.spec.ts +++ b/projects/example-app/src/app/auth/services/auth-guard.service.spec.ts @@ -1,37 +1,37 @@ -import { TestBed } from '@angular/core/testing'; -import { Store, MemoizedSelector } from '@ngrx/store'; -import { cold } from 'jasmine-marbles'; -import { AuthGuard } from '@example-app/auth/services'; -import * as fromAuth from '@example-app/auth/reducers'; -import { provideMockStore, MockStore } from '@ngrx/store/testing'; - -describe('Auth Guard', () => { - let guard: AuthGuard; - let store: MockStore; - let loggedIn: MemoizedSelector; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [AuthGuard, provideMockStore()], - }); - - store = TestBed.get(Store); - guard = TestBed.get(AuthGuard); - - loggedIn = store.overrideSelector(fromAuth.selectLoggedIn, false); - }); - - it('should return false if the user state is not logged in', () => { - const expected = cold('(a|)', { a: false }); - - expect(guard.canActivate()).toBeObservable(expected); - }); - - it('should return true if the user state is logged in', () => { - const expected = cold('(a|)', { a: true }); - - loggedIn.setResult(true); - - expect(guard.canActivate()).toBeObservable(expected); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { Store, MemoizedSelector } from '@ngrx/store'; +import { cold } from 'jasmine-marbles'; +import { AuthGuard } from '@example-app/auth/services'; +import * as fromAuth from '@example-app/auth/reducers'; +import { provideMockStore, MockStore } from '@ngrx/store/testing'; + +describe('Auth Guard', () => { + let guard: AuthGuard; + let store: MockStore; + let loggedIn: MemoizedSelector; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AuthGuard, provideMockStore()], + }); + + store = TestBed.get(Store); + guard = TestBed.get(AuthGuard); + + loggedIn = store.overrideSelector(fromAuth.selectLoggedIn, false); + }); + + it('should return false if the user state is not logged in', () => { + const expected = cold('(a|)', { a: false }); + + expect(guard.canActivate()).toBeObservable(expected); + }); + + it('should return true if the user state is logged in', () => { + const expected = cold('(a|)', { a: true }); + + loggedIn.setResult(true); + + expect(guard.canActivate()).toBeObservable(expected); + }); +}); diff --git a/projects/example-app/src/app/books/containers/collection-page.component.spec.ts b/projects/example-app/src/app/books/containers/collection-page.component.spec.ts index dcb057922f..692c131c5b 100644 --- a/projects/example-app/src/app/books/containers/collection-page.component.spec.ts +++ b/projects/example-app/src/app/books/containers/collection-page.component.spec.ts @@ -42,7 +42,7 @@ describe('Collection Page', () => { fixture = TestBed.createComponent(CollectionPageComponent); instance = fixture.componentInstance; - store = TestBed.get(Store); + store = TestBed.get(Store); spyOn(store, 'dispatch'); }); diff --git a/projects/example-app/src/app/books/containers/find-book-page.component.spec.ts b/projects/example-app/src/app/books/containers/find-book-page.component.spec.ts index 8c9ff345c8..0b9e421c5d 100644 --- a/projects/example-app/src/app/books/containers/find-book-page.component.spec.ts +++ b/projects/example-app/src/app/books/containers/find-book-page.component.spec.ts @@ -55,7 +55,7 @@ describe('Find Book Page', () => { fixture = TestBed.createComponent(FindBookPageComponent); instance = fixture.componentInstance; - store = TestBed.get(Store); + store = TestBed.get(Store); spyOn(store, 'dispatch'); }); diff --git a/projects/example-app/src/app/books/containers/selected-book-page.component.spec.ts b/projects/example-app/src/app/books/containers/selected-book-page.component.spec.ts index 6a69c6226c..04caa2694f 100644 --- a/projects/example-app/src/app/books/containers/selected-book-page.component.spec.ts +++ b/projects/example-app/src/app/books/containers/selected-book-page.component.spec.ts @@ -34,7 +34,7 @@ describe('Selected Book Page', () => { fixture = TestBed.createComponent(SelectedBookPageComponent); instance = fixture.componentInstance; - store = TestBed.get(Store); + store = TestBed.get(Store); spyOn(store, 'dispatch'); }); diff --git a/projects/example-app/src/app/books/containers/view-book-page.component.spec.ts b/projects/example-app/src/app/books/containers/view-book-page.component.spec.ts index 4e1800f24d..c12bee010f 100644 --- a/projects/example-app/src/app/books/containers/view-book-page.component.spec.ts +++ b/projects/example-app/src/app/books/containers/view-book-page.component.spec.ts @@ -41,7 +41,7 @@ describe('View Book Page', () => { }); fixture = TestBed.createComponent(ViewBookPageComponent); - store = TestBed.get(Store); + store = TestBed.get(Store); route = TestBed.get(ActivatedRoute); jest.spyOn(store, 'dispatch'); diff --git a/projects/example-app/src/app/books/effects/book.effects.spec.ts b/projects/example-app/src/app/books/effects/book.effects.spec.ts index 3c9cac2d37..4228570b4b 100644 --- a/projects/example-app/src/app/books/effects/book.effects.spec.ts +++ b/projects/example-app/src/app/books/effects/book.effects.spec.ts @@ -1,93 +1,93 @@ -import { TestBed } from '@angular/core/testing'; - -import { Actions } from '@ngrx/effects'; -import { provideMockActions } from '@ngrx/effects/testing'; -import { cold, getTestScheduler, hot } from 'jasmine-marbles'; -import { Observable } from 'rxjs'; - -import { - BooksApiActions, - FindBookPageActions, -} from '@example-app/books/actions'; -import { BookEffects } from '@example-app/books/effects'; -import { Book } from '@example-app/books/models'; -import { GoogleBooksService } from '@example-app/core/services/google-books.service'; - -describe('BookEffects', () => { - let effects: BookEffects; - let googleBooksService: any; - let actions$: Observable; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - BookEffects, - { - provide: GoogleBooksService, - useValue: { searchBooks: jest.fn() }, - }, - provideMockActions(() => actions$), - ], - }); - - effects = TestBed.get(BookEffects); - googleBooksService = TestBed.get(GoogleBooksService); - actions$ = TestBed.get(Actions); - }); - - describe('search$', () => { - it('should return a book.SearchComplete, with the books, on success, after the de-bounce', () => { - const book1 = { id: '111', volumeInfo: {} } as Book; - const book2 = { id: '222', volumeInfo: {} } as Book; - const books = [book1, book2]; - const action = FindBookPageActions.searchBooks({ query: 'query' }); - const completion = BooksApiActions.searchSuccess({ books }); - - actions$ = hot('-a---', { a: action }); - const response = cold('-a|', { a: books }); - const expected = cold('-----b', { b: completion }); - googleBooksService.searchBooks = jest.fn(() => response); - - expect( - effects.search$({ - debounce: 30, - scheduler: getTestScheduler(), - }) - ).toBeObservable(expected); - }); - - it('should return a book.SearchError if the books service throws', () => { - const action = FindBookPageActions.searchBooks({ query: 'query' }); - const completion = BooksApiActions.searchFailure({ - errorMsg: 'Unexpected Error. Try again later.', - }); - const error = { message: 'Unexpected Error. Try again later.' }; - - actions$ = hot('-a---', { a: action }); - const response = cold('-#|', {}, error); - const expected = cold('-----b', { b: completion }); - googleBooksService.searchBooks = jest.fn(() => response); - - expect( - effects.search$({ - debounce: 30, - scheduler: getTestScheduler(), - }) - ).toBeObservable(expected); - }); - - it(`should not do anything if the query is an empty string`, () => { - const action = FindBookPageActions.searchBooks({ query: '' }); - - actions$ = hot('-a---', { a: action }); - const expected = cold('---'); - - expect( - effects.search$({ - debounce: 30, - scheduler: getTestScheduler(), - }) - ).toBeObservable(expected); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; + +import { Actions } from '@ngrx/effects'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { cold, getTestScheduler, hot } from 'jasmine-marbles'; +import { Observable } from 'rxjs'; + +import { + BooksApiActions, + FindBookPageActions, +} from '@example-app/books/actions'; +import { BookEffects } from '@example-app/books/effects'; +import { Book } from '@example-app/books/models'; +import { GoogleBooksService } from '@example-app/core/services/google-books.service'; + +describe('BookEffects', () => { + let effects: BookEffects; + let googleBooksService: any; + let actions$: Observable; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + BookEffects, + { + provide: GoogleBooksService, + useValue: { searchBooks: jest.fn() }, + }, + provideMockActions(() => actions$), + ], + }); + + effects = TestBed.get(BookEffects); + googleBooksService = TestBed.get(GoogleBooksService); + actions$ = TestBed.get(Actions); + }); + + describe('search$', () => { + it('should return a book.SearchComplete, with the books, on success, after the de-bounce', () => { + const book1 = { id: '111', volumeInfo: {} } as Book; + const book2 = { id: '222', volumeInfo: {} } as Book; + const books = [book1, book2]; + const action = FindBookPageActions.searchBooks({ query: 'query' }); + const completion = BooksApiActions.searchSuccess({ books }); + + actions$ = hot('-a---', { a: action }); + const response = cold('-a|', { a: books }); + const expected = cold('-----b', { b: completion }); + googleBooksService.searchBooks = jest.fn(() => response); + + expect( + effects.search$({ + debounce: 30, + scheduler: getTestScheduler(), + }) + ).toBeObservable(expected); + }); + + it('should return a book.SearchError if the books service throws', () => { + const action = FindBookPageActions.searchBooks({ query: 'query' }); + const completion = BooksApiActions.searchFailure({ + errorMsg: 'Unexpected Error. Try again later.', + }); + const error = { message: 'Unexpected Error. Try again later.' }; + + actions$ = hot('-a---', { a: action }); + const response = cold('-#|', {}, error); + const expected = cold('-----b', { b: completion }); + googleBooksService.searchBooks = jest.fn(() => response); + + expect( + effects.search$({ + debounce: 30, + scheduler: getTestScheduler(), + }) + ).toBeObservable(expected); + }); + + it(`should not do anything if the query is an empty string`, () => { + const action = FindBookPageActions.searchBooks({ query: '' }); + + actions$ = hot('-a---', { a: action }); + const expected = cold('---'); + + expect( + effects.search$({ + debounce: 30, + scheduler: getTestScheduler(), + }) + ).toBeObservable(expected); + }); + }); +}); diff --git a/projects/example-app/src/app/books/effects/collection.effects.spec.ts b/projects/example-app/src/app/books/effects/collection.effects.spec.ts index 2b5f0cf34e..31138fe38e 100644 --- a/projects/example-app/src/app/books/effects/collection.effects.spec.ts +++ b/projects/example-app/src/app/books/effects/collection.effects.spec.ts @@ -1,152 +1,152 @@ -import { TestBed } from '@angular/core/testing'; - -import { - CollectionApiActions, - CollectionPageActions, - SelectedBookPageActions, -} from '@example-app/books/actions'; -import { CollectionEffects } from '@example-app/books/effects'; -import { Book } from '@example-app/books/models'; -import { - BookStorageService, - LOCAL_STORAGE_TOKEN, -} from '@example-app/core/services'; -import { Actions } from '@ngrx/effects'; -import { provideMockActions } from '@ngrx/effects/testing'; -import { cold, hot } from 'jasmine-marbles'; -import { Observable } from 'rxjs'; - -describe('CollectionEffects', () => { - let db: any; - let effects: CollectionEffects; - let actions$: Observable; - - const book1 = { id: '111', volumeInfo: {} } as Book; - const book2 = { id: '222', volumeInfo: {} } as Book; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - CollectionEffects, - { - provide: BookStorageService, - useValue: { - supported: jest.fn(), - deleteStoredCollection: jest.fn(), - addToCollection: jest.fn(), - getCollection: jest.fn(), - removeFromCollection: jest.fn(), - }, - }, - { - provide: LOCAL_STORAGE_TOKEN, - useValue: { - removeItem: jest.fn(), - setItem: jest.fn(), - getItem: jest.fn(_ => JSON.stringify([])), - }, - }, - provideMockActions(() => actions$), - ], - }); - - db = TestBed.get(BookStorageService); - effects = TestBed.get(CollectionEffects); - actions$ = TestBed.get(Actions); - }); - describe('checkStorageSupport$', () => { - it('should call db.checkStorageSupport when initially subscribed to', () => { - effects.checkStorageSupport$.subscribe(); - expect(db.supported).toHaveBeenCalled(); - }); - }); - describe('loadCollection$', () => { - it('should return a collection.LoadSuccess, with the books, on success', () => { - const action = CollectionPageActions.loadCollection(); - const completion = CollectionApiActions.loadBooksSuccess({ - books: [book1, book2], - }); - - actions$ = hot('-a', { a: action }); - const response = cold('-a|', { a: [book1, book2] }); - const expected = cold('--c', { c: completion }); - db.getCollection = jest.fn(() => response); - - expect(effects.loadCollection$).toBeObservable(expected); - }); - - it('should return a collection.LoadFail, if the query throws', () => { - const action = CollectionPageActions.loadCollection(); - const error = 'Error!'; - const completion = CollectionApiActions.loadBooksFailure({ error }); - - actions$ = hot('-a', { a: action }); - const response = cold('-#', {}, error); - const expected = cold('--c', { c: completion }); - db.getCollection = jest.fn(() => response); - - expect(effects.loadCollection$).toBeObservable(expected); - }); - }); - - describe('addBookToCollection$', () => { - it('should return a collection.AddBookSuccess, with the book, on success', () => { - const action = SelectedBookPageActions.addBook({ book: book1 }); - const completion = CollectionApiActions.addBookSuccess({ book: book1 }); - - actions$ = hot('-a', { a: action }); - const response = cold('-b', { b: true }); - const expected = cold('--c', { c: completion }); - db.addToCollection = jest.fn(() => response); - - expect(effects.addBookToCollection$).toBeObservable(expected); - expect(db.addToCollection).toHaveBeenCalledWith([book1]); - }); - - it('should return a collection.AddBookFail, with the book, when the db insert throws', () => { - const action = SelectedBookPageActions.addBook({ book: book1 }); - const completion = CollectionApiActions.addBookFailure({ book: book1 }); - const error = 'Error!'; - - actions$ = hot('-a', { a: action }); - const response = cold('-#', {}, error); - const expected = cold('--c', { c: completion }); - db.addToCollection = jest.fn(() => response); - - expect(effects.addBookToCollection$).toBeObservable(expected); - }); - - describe('removeBookFromCollection$', () => { - it('should return a collection.RemoveBookSuccess, with the book, on success', () => { - const action = SelectedBookPageActions.removeBook({ book: book1 }); - const completion = CollectionApiActions.removeBookSuccess({ - book: book1, - }); - - actions$ = hot('-a', { a: action }); - const response = cold('-b', { b: true }); - const expected = cold('--c', { c: completion }); - db.removeFromCollection = jest.fn(() => response); - - expect(effects.removeBookFromCollection$).toBeObservable(expected); - expect(db.removeFromCollection).toHaveBeenCalledWith([book1.id]); - }); - - it('should return a collection.RemoveBookFail, with the book, when the db insert throws', () => { - const action = SelectedBookPageActions.removeBook({ book: book1 }); - const completion = CollectionApiActions.removeBookFailure({ - book: book1, - }); - const error = 'Error!'; - - actions$ = hot('-a', { a: action }); - const response = cold('-#', {}, error); - const expected = cold('--c', { c: completion }); - db.removeFromCollection = jest.fn(() => response); - - expect(effects.removeBookFromCollection$).toBeObservable(expected); - expect(db.removeFromCollection).toHaveBeenCalledWith([book1.id]); - }); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; + +import { + CollectionApiActions, + CollectionPageActions, + SelectedBookPageActions, +} from '@example-app/books/actions'; +import { CollectionEffects } from '@example-app/books/effects'; +import { Book } from '@example-app/books/models'; +import { + BookStorageService, + LOCAL_STORAGE_TOKEN, +} from '@example-app/core/services'; +import { Actions } from '@ngrx/effects'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { cold, hot } from 'jasmine-marbles'; +import { Observable } from 'rxjs'; + +describe('CollectionEffects', () => { + let db: any; + let effects: CollectionEffects; + let actions$: Observable; + + const book1 = { id: '111', volumeInfo: {} } as Book; + const book2 = { id: '222', volumeInfo: {} } as Book; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CollectionEffects, + { + provide: BookStorageService, + useValue: { + supported: jest.fn(), + deleteStoredCollection: jest.fn(), + addToCollection: jest.fn(), + getCollection: jest.fn(), + removeFromCollection: jest.fn(), + }, + }, + { + provide: LOCAL_STORAGE_TOKEN, + useValue: { + removeItem: jest.fn(), + setItem: jest.fn(), + getItem: jest.fn(_ => JSON.stringify([])), + }, + }, + provideMockActions(() => actions$), + ], + }); + + db = TestBed.get(BookStorageService); + effects = TestBed.get(CollectionEffects); + actions$ = TestBed.get(Actions); + }); + describe('checkStorageSupport$', () => { + it('should call db.checkStorageSupport when initially subscribed to', () => { + effects.checkStorageSupport$.subscribe(); + expect(db.supported).toHaveBeenCalled(); + }); + }); + describe('loadCollection$', () => { + it('should return a collection.LoadSuccess, with the books, on success', () => { + const action = CollectionPageActions.loadCollection(); + const completion = CollectionApiActions.loadBooksSuccess({ + books: [book1, book2], + }); + + actions$ = hot('-a', { a: action }); + const response = cold('-a|', { a: [book1, book2] }); + const expected = cold('--c', { c: completion }); + db.getCollection = jest.fn(() => response); + + expect(effects.loadCollection$).toBeObservable(expected); + }); + + it('should return a collection.LoadFail, if the query throws', () => { + const action = CollectionPageActions.loadCollection(); + const error = 'Error!'; + const completion = CollectionApiActions.loadBooksFailure({ error }); + + actions$ = hot('-a', { a: action }); + const response = cold('-#', {}, error); + const expected = cold('--c', { c: completion }); + db.getCollection = jest.fn(() => response); + + expect(effects.loadCollection$).toBeObservable(expected); + }); + }); + + describe('addBookToCollection$', () => { + it('should return a collection.AddBookSuccess, with the book, on success', () => { + const action = SelectedBookPageActions.addBook({ book: book1 }); + const completion = CollectionApiActions.addBookSuccess({ book: book1 }); + + actions$ = hot('-a', { a: action }); + const response = cold('-b', { b: true }); + const expected = cold('--c', { c: completion }); + db.addToCollection = jest.fn(() => response); + + expect(effects.addBookToCollection$).toBeObservable(expected); + expect(db.addToCollection).toHaveBeenCalledWith([book1]); + }); + + it('should return a collection.AddBookFail, with the book, when the db insert throws', () => { + const action = SelectedBookPageActions.addBook({ book: book1 }); + const completion = CollectionApiActions.addBookFailure({ book: book1 }); + const error = 'Error!'; + + actions$ = hot('-a', { a: action }); + const response = cold('-#', {}, error); + const expected = cold('--c', { c: completion }); + db.addToCollection = jest.fn(() => response); + + expect(effects.addBookToCollection$).toBeObservable(expected); + }); + + describe('removeBookFromCollection$', () => { + it('should return a collection.RemoveBookSuccess, with the book, on success', () => { + const action = SelectedBookPageActions.removeBook({ book: book1 }); + const completion = CollectionApiActions.removeBookSuccess({ + book: book1, + }); + + actions$ = hot('-a', { a: action }); + const response = cold('-b', { b: true }); + const expected = cold('--c', { c: completion }); + db.removeFromCollection = jest.fn(() => response); + + expect(effects.removeBookFromCollection$).toBeObservable(expected); + expect(db.removeFromCollection).toHaveBeenCalledWith([book1.id]); + }); + + it('should return a collection.RemoveBookFail, with the book, when the db insert throws', () => { + const action = SelectedBookPageActions.removeBook({ book: book1 }); + const completion = CollectionApiActions.removeBookFailure({ + book: book1, + }); + const error = 'Error!'; + + actions$ = hot('-a', { a: action }); + const response = cold('-#', {}, error); + const expected = cold('--c', { c: completion }); + db.removeFromCollection = jest.fn(() => response); + + expect(effects.removeBookFromCollection$).toBeObservable(expected); + expect(db.removeFromCollection).toHaveBeenCalledWith([book1.id]); + }); + }); + }); +}); diff --git a/projects/example-app/src/app/core/effects/router.effects.spec.ts b/projects/example-app/src/app/core/effects/router.effects.spec.ts index 71ea5de9a1..015e3bcc9f 100644 --- a/projects/example-app/src/app/core/effects/router.effects.spec.ts +++ b/projects/example-app/src/app/core/effects/router.effects.spec.ts @@ -1,41 +1,41 @@ -import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; -import { Title } from '@angular/platform-browser'; -import { TestBed } from '@angular/core/testing'; - -import { of } from 'rxjs'; - -import { RouterEffects } from '@example-app/core/effects'; - -describe('RouterEffects', () => { - let effects: RouterEffects; - let titleService: Title; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - RouterEffects, - { - provide: Router, - useValue: { events: of(new NavigationEnd(1, '', '')) }, - }, - { - provide: ActivatedRoute, - useValue: { data: of({ title: 'Search' }) }, - }, - { provide: Title, useValue: { setTitle: jest.fn() } }, - ], - }); - - effects = TestBed.get(RouterEffects); - titleService = TestBed.get(Title); - }); - - describe('updateTitle$', () => { - it('should update the title on router navigation', () => { - effects.updateTitle$.subscribe(); - expect(titleService.setTitle).toHaveBeenCalledWith( - 'Book Collection - Search' - ); - }); - }); -}); +import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; +import { Title } from '@angular/platform-browser'; +import { TestBed } from '@angular/core/testing'; + +import { of } from 'rxjs'; + +import { RouterEffects } from '@example-app/core/effects'; + +describe('RouterEffects', () => { + let effects: RouterEffects; + let titleService: Title; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + RouterEffects, + { + provide: Router, + useValue: { events: of(new NavigationEnd(1, '', '')) }, + }, + { + provide: ActivatedRoute, + useValue: { data: of({ title: 'Search' }) }, + }, + { provide: Title, useValue: { setTitle: jest.fn() } }, + ], + }); + + effects = TestBed.get(RouterEffects); + titleService = TestBed.get(Title); + }); + + describe('updateTitle$', () => { + it('should update the title on router navigation', () => { + effects.updateTitle$.subscribe(); + expect(titleService.setTitle).toHaveBeenCalledWith( + 'Book Collection - Search' + ); + }); + }); +}); diff --git a/projects/example-app/src/app/core/effects/user.effects.spec.ts b/projects/example-app/src/app/core/effects/user.effects.spec.ts index 6e06615f54..aee801c3d0 100644 --- a/projects/example-app/src/app/core/effects/user.effects.spec.ts +++ b/projects/example-app/src/app/core/effects/user.effects.spec.ts @@ -1,65 +1,59 @@ -import { Action } from '@ngrx/store'; -import { TestBed, fakeAsync, tick } from '@angular/core/testing'; - -import { UserEffects } from '@example-app/core/effects'; -import { UserActions } from '@example-app/core/actions'; - -describe('UserEffects', () => { - let effects: UserEffects; - const eventsMap: { [key: string]: any } = {}; - - beforeAll(() => { - document.addEventListener = jest.fn((event, cb) => { - eventsMap[event] = cb; - }); - }); - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [UserEffects], - }); - - effects = TestBed.get(UserEffects); - }); - - describe('idle$', () => { - it( - 'should trigger idleTimeout action after 5 minutes', - fakeAsync(() => { - let action: Action | undefined; - effects.idle$.subscribe(res => (action = res)); - - // Initial action to trigger the effect - eventsMap['click'](); - - tick(2 * 60 * 1000); - expect(action).toBeUndefined(); - - tick(3 * 60 * 1000); - expect(action).toBeDefined(); - expect(action!.type).toBe(UserActions.idleTimeout.type); - }) - ); - - it( - 'should reset timeout on user activity', - fakeAsync(() => { - let action: Action | undefined; - effects.idle$.subscribe(res => (action = res)); - - // Initial action to trigger the effect - eventsMap['keydown'](); - - tick(4 * 60 * 1000); - eventsMap['mousemove'](); - - tick(4 * 60 * 1000); - expect(action).toBeUndefined(); - - tick(1 * 60 * 1000); - expect(action).toBeDefined(); - expect(action!.type).toBe(UserActions.idleTimeout.type); - }) - ); - }); -}); +import { Action } from '@ngrx/store'; +import { TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { UserEffects } from '@example-app/core/effects'; +import { UserActions } from '@example-app/core/actions'; + +describe('UserEffects', () => { + let effects: UserEffects; + const eventsMap: { [key: string]: any } = {}; + + beforeAll(() => { + document.addEventListener = jest.fn((event, cb) => { + eventsMap[event] = cb; + }); + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [UserEffects], + }); + + effects = TestBed.get(UserEffects); + }); + + describe('idle$', () => { + it('should trigger idleTimeout action after 5 minutes', fakeAsync(() => { + let action: Action | undefined; + effects.idle$.subscribe(res => (action = res)); + + // Initial action to trigger the effect + eventsMap['click'](); + + tick(2 * 60 * 1000); + expect(action).toBeUndefined(); + + tick(3 * 60 * 1000); + expect(action).toBeDefined(); + expect(action!.type).toBe(UserActions.idleTimeout.type); + })); + + it('should reset timeout on user activity', fakeAsync(() => { + let action: Action | undefined; + effects.idle$.subscribe(res => (action = res)); + + // Initial action to trigger the effect + eventsMap['keydown'](); + + tick(4 * 60 * 1000); + eventsMap['mousemove'](); + + tick(4 * 60 * 1000); + expect(action).toBeUndefined(); + + tick(1 * 60 * 1000); + expect(action).toBeDefined(); + expect(action!.type).toBe(UserActions.idleTimeout.type); + })); + }); +}); diff --git a/projects/example-app/src/app/core/services/book-storage.service.spec.ts b/projects/example-app/src/app/core/services/book-storage.service.spec.ts index 00e5949ae4..fb17bf0b1b 100644 --- a/projects/example-app/src/app/core/services/book-storage.service.spec.ts +++ b/projects/example-app/src/app/core/services/book-storage.service.spec.ts @@ -1,148 +1,148 @@ -import { TestBed } from '@angular/core/testing'; - -import { cold } from 'jasmine-marbles'; - -import { Book } from '@example-app/books/models'; -import { - BookStorageService, - LOCAL_STORAGE_TOKEN, -} from '@example-app/core/services/book-storage.service'; - -describe('BookStorageService', () => { - let fixture: any; - - let localStorageFake: Storage & any = { - removeItem: jest.fn(), - setItem: jest.fn(), - getItem: jest.fn(_ => JSON.stringify(persistedCollection)), - }; - - const book1 = { id: '111', volumeInfo: {} } as Book; - const book2 = { id: '222', volumeInfo: {} } as Book; - const book3 = { id: '333', volumeInfo: {} } as Book; - const book4 = { id: '444', volumeInfo: {} } as Book; - - const persistedStorageKey = 'books-app'; - const persistedCollection = [book2, book4]; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - { - provide: LOCAL_STORAGE_TOKEN, - useValue: localStorageFake, - }, - ], - }); - fixture = TestBed.get(BookStorageService); - }); - - describe('supported', () => { - it('should have truthy value if localStorage provider set', () => { - const expected = cold('(-a|)', { a: true }); - expect(fixture.supported()).toBeObservable(expected); - }); - - it('should throw error if localStorage provider not available', () => { - TestBed.resetTestingModule().configureTestingModule({ - providers: [ - { - provide: LOCAL_STORAGE_TOKEN, - useValue: null, - }, - ], - }); - - fixture = TestBed.get(BookStorageService); - const expected = cold('#', {}, 'Local Storage Not Supported'); - expect(fixture.supported()).toBeObservable(expected); - }); - }); - - describe('getCollection', () => { - it('should call get collection', () => { - const expected = cold('(-a|)', { a: persistedCollection }); - expect(fixture.getCollection()).toBeObservable(expected); - expect(localStorageFake.getItem).toHaveBeenCalledWith( - persistedStorageKey - ); - localStorageFake.getItem.mockClear(); - }); - }); - - describe('addToCollection', () => { - it('should add single item', () => { - const result = [...persistedCollection, book1]; - const expected = cold('(-a|)', { a: result }); - expect(fixture.addToCollection([book1])).toBeObservable(expected); - expect(localStorageFake.setItem).toHaveBeenCalledWith( - persistedStorageKey, - JSON.stringify(result) - ); - - localStorageFake.setItem.mockClear(); - }); - - it('should add multiple items', () => { - const result = [...persistedCollection, book1, book3]; - const expected = cold('(-a|)', { a: result }); - expect(fixture.addToCollection([book1, book3])).toBeObservable(expected); - expect(localStorageFake.setItem).toHaveBeenCalledWith( - persistedStorageKey, - JSON.stringify(result) - ); - localStorageFake.setItem.mockClear(); - }); - }); - - describe('removeFromCollection', () => { - it('should remove item from collection', () => { - const filterCollection = persistedCollection.filter( - f => f.id !== book2.id - ); - const expected = cold('(-a|)', { a: filterCollection }); - expect(fixture.removeFromCollection([book2.id])).toBeObservable(expected); - expect(localStorageFake.getItem).toHaveBeenCalledWith( - persistedStorageKey - ); - expect(localStorageFake.setItem).toHaveBeenCalledWith( - persistedStorageKey, - JSON.stringify(filterCollection) - ); - localStorageFake.getItem.mockClear(); - }); - - it('should remove multiple items from collection', () => { - const filterCollection = persistedCollection.filter( - f => f.id !== book4.id - ); - const expected = cold('(-a|)', { a: filterCollection }); - expect(fixture.removeFromCollection([book4.id])).toBeObservable(expected); - expect(localStorageFake.getItem).toHaveBeenCalledWith( - persistedStorageKey - ); - expect(localStorageFake.setItem).toHaveBeenCalledWith( - persistedStorageKey, - JSON.stringify(filterCollection) - ); - localStorageFake.getItem.mockClear(); - }); - - it('should ignore items not present in collection', () => { - const filterCollection = persistedCollection; - const expected = cold('(-a|)', { a: filterCollection }); - expect(fixture.removeFromCollection([book1.id])).toBeObservable(expected); - }); - }); - - describe('deleteCollection', () => { - it('should delete storage key and collection', () => { - const expected = cold('(-a|)', { a: true }); - expect(fixture.deleteCollection()).toBeObservable(expected); - expect(localStorageFake.removeItem).toHaveBeenCalledWith( - persistedStorageKey - ); - localStorageFake.removeItem.mockClear(); - }); - }); -}); +import { TestBed } from '@angular/core/testing'; + +import { cold } from 'jasmine-marbles'; + +import { Book } from '@example-app/books/models'; +import { + BookStorageService, + LOCAL_STORAGE_TOKEN, +} from '@example-app/core/services/book-storage.service'; + +describe('BookStorageService', () => { + let fixture: any; + + let localStorageFake: Storage & any = { + removeItem: jest.fn(), + setItem: jest.fn(), + getItem: jest.fn(_ => JSON.stringify(persistedCollection)), + }; + + const book1 = { id: '111', volumeInfo: {} } as Book; + const book2 = { id: '222', volumeInfo: {} } as Book; + const book3 = { id: '333', volumeInfo: {} } as Book; + const book4 = { id: '444', volumeInfo: {} } as Book; + + const persistedStorageKey = 'books-app'; + const persistedCollection = [book2, book4]; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + { + provide: LOCAL_STORAGE_TOKEN, + useValue: localStorageFake, + }, + ], + }); + fixture = TestBed.get(BookStorageService); + }); + + describe('supported', () => { + it('should have truthy value if localStorage provider set', () => { + const expected = cold('(-a|)', { a: true }); + expect(fixture.supported()).toBeObservable(expected); + }); + + it('should throw error if localStorage provider not available', () => { + TestBed.resetTestingModule().configureTestingModule({ + providers: [ + { + provide: LOCAL_STORAGE_TOKEN, + useValue: null, + }, + ], + }); + + fixture = TestBed.get(BookStorageService); + const expected = cold('#', {}, 'Local Storage Not Supported'); + expect(fixture.supported()).toBeObservable(expected); + }); + }); + + describe('getCollection', () => { + it('should call get collection', () => { + const expected = cold('(-a|)', { a: persistedCollection }); + expect(fixture.getCollection()).toBeObservable(expected); + expect(localStorageFake.getItem).toHaveBeenCalledWith( + persistedStorageKey + ); + localStorageFake.getItem.mockClear(); + }); + }); + + describe('addToCollection', () => { + it('should add single item', () => { + const result = [...persistedCollection, book1]; + const expected = cold('(-a|)', { a: result }); + expect(fixture.addToCollection([book1])).toBeObservable(expected); + expect(localStorageFake.setItem).toHaveBeenCalledWith( + persistedStorageKey, + JSON.stringify(result) + ); + + localStorageFake.setItem.mockClear(); + }); + + it('should add multiple items', () => { + const result = [...persistedCollection, book1, book3]; + const expected = cold('(-a|)', { a: result }); + expect(fixture.addToCollection([book1, book3])).toBeObservable(expected); + expect(localStorageFake.setItem).toHaveBeenCalledWith( + persistedStorageKey, + JSON.stringify(result) + ); + localStorageFake.setItem.mockClear(); + }); + }); + + describe('removeFromCollection', () => { + it('should remove item from collection', () => { + const filterCollection = persistedCollection.filter( + f => f.id !== book2.id + ); + const expected = cold('(-a|)', { a: filterCollection }); + expect(fixture.removeFromCollection([book2.id])).toBeObservable(expected); + expect(localStorageFake.getItem).toHaveBeenCalledWith( + persistedStorageKey + ); + expect(localStorageFake.setItem).toHaveBeenCalledWith( + persistedStorageKey, + JSON.stringify(filterCollection) + ); + localStorageFake.getItem.mockClear(); + }); + + it('should remove multiple items from collection', () => { + const filterCollection = persistedCollection.filter( + f => f.id !== book4.id + ); + const expected = cold('(-a|)', { a: filterCollection }); + expect(fixture.removeFromCollection([book4.id])).toBeObservable(expected); + expect(localStorageFake.getItem).toHaveBeenCalledWith( + persistedStorageKey + ); + expect(localStorageFake.setItem).toHaveBeenCalledWith( + persistedStorageKey, + JSON.stringify(filterCollection) + ); + localStorageFake.getItem.mockClear(); + }); + + it('should ignore items not present in collection', () => { + const filterCollection = persistedCollection; + const expected = cold('(-a|)', { a: filterCollection }); + expect(fixture.removeFromCollection([book1.id])).toBeObservable(expected); + }); + }); + + describe('deleteCollection', () => { + it('should delete storage key and collection', () => { + const expected = cold('(-a|)', { a: true }); + expect(fixture.deleteCollection()).toBeObservable(expected); + expect(localStorageFake.removeItem).toHaveBeenCalledWith( + persistedStorageKey + ); + localStorageFake.removeItem.mockClear(); + }); + }); +}); diff --git a/projects/example-app/src/app/core/services/google-books.service.spec.ts b/projects/example-app/src/app/core/services/google-books.service.spec.ts index c51b29fbc1..9070d6f0ef 100644 --- a/projects/example-app/src/app/core/services/google-books.service.spec.ts +++ b/projects/example-app/src/app/core/services/google-books.service.spec.ts @@ -1,57 +1,57 @@ -import { TestBed } from '@angular/core/testing'; -import { HttpClient } from '@angular/common/http'; - -import { cold } from 'jasmine-marbles'; - -import { GoogleBooksService } from '@example-app/core/services/google-books.service'; - -describe('Service: GoogleBooks', () => { - let service: GoogleBooksService; - let http: HttpClient; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{ provide: HttpClient, useValue: { get: jest.fn() } }], - }); - - service = TestBed.get(GoogleBooksService); - http = TestBed.get(HttpClient); - }); - - const data = { - title: 'Book Title', - author: 'John Smith', - volumeId: '12345', - }; - - const books = { - items: [ - { id: '12345', volumeInfo: { title: 'Title' } }, - { id: '67890', volumeInfo: { title: 'Another Title' } }, - ], - }; - - const queryTitle = 'Book Title'; - - it('should call the search api and return the search results', () => { - const response = cold('-a|', { a: books }); - const expected = cold('-b|', { b: books.items }); - http.get = jest.fn(() => response); - - expect(service.searchBooks(queryTitle)).toBeObservable(expected); - expect(http.get).toHaveBeenCalledWith( - `https://www.googleapis.com/books/v1/volumes?orderBy=newest&q=${queryTitle}` - ); - }); - - it('should retrieve the book from the volumeId', () => { - const response = cold('-a|', { a: data }); - const expected = cold('-b|', { b: data }); - http.get = jest.fn(() => response); - - expect(service.retrieveBook(data.volumeId)).toBeObservable(expected); - expect(http.get).toHaveBeenCalledWith( - `https://www.googleapis.com/books/v1/volumes/${data.volumeId}` - ); - }); -}); +import { TestBed } from '@angular/core/testing'; +import { HttpClient } from '@angular/common/http'; + +import { cold } from 'jasmine-marbles'; + +import { GoogleBooksService } from '@example-app/core/services/google-books.service'; + +describe('Service: GoogleBooks', () => { + let service: GoogleBooksService; + let http: HttpClient; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ provide: HttpClient, useValue: { get: jest.fn() } }], + }); + + service = TestBed.get(GoogleBooksService); + http = TestBed.get(HttpClient); + }); + + const data = { + title: 'Book Title', + author: 'John Smith', + volumeId: '12345', + }; + + const books = { + items: [ + { id: '12345', volumeInfo: { title: 'Title' } }, + { id: '67890', volumeInfo: { title: 'Another Title' } }, + ], + }; + + const queryTitle = 'Book Title'; + + it('should call the search api and return the search results', () => { + const response = cold('-a|', { a: books }); + const expected = cold('-b|', { b: books.items }); + http.get = jest.fn(() => response); + + expect(service.searchBooks(queryTitle)).toBeObservable(expected); + expect(http.get).toHaveBeenCalledWith( + `https://www.googleapis.com/books/v1/volumes?orderBy=newest&q=${queryTitle}` + ); + }); + + it('should retrieve the book from the volumeId', () => { + const response = cold('-a|', { a: data }); + const expected = cold('-b|', { b: data }); + http.get = jest.fn(() => response); + + expect(service.retrieveBook(data.volumeId)).toBeObservable(expected); + expect(http.get).toHaveBeenCalledWith( + `https://www.googleapis.com/books/v1/volumes/${data.volumeId}` + ); + }); +}); diff --git a/projects/ngrx.io/content/examples/testing-store/src/app/user-greeting.component.spec.ts b/projects/ngrx.io/content/examples/testing-store/src/app/user-greeting.component.spec.ts index 44bdc4d960..a60d67a6be 100644 --- a/projects/ngrx.io/content/examples/testing-store/src/app/user-greeting.component.spec.ts +++ b/projects/ngrx.io/content/examples/testing-store/src/app/user-greeting.component.spec.ts @@ -1,37 +1,37 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { Store, MemoizedSelector } from '@ngrx/store'; -import { provideMockStore, MockStore } from '@ngrx/store/testing'; -import { UserGreetingComponent } from './user-greeting.component'; -import * as fromAuth from './reducers'; - -describe('User Greeting Component', () => { - let fixture: ComponentFixture; - let mockStore: MockStore; - let mockUsernameSelector: MemoizedSelector; - const queryDivText = () => - fixture.debugElement.queryAll(By.css('div'))[0].nativeElement.textContent; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore()], - declarations: [UserGreetingComponent], - }); - - fixture = TestBed.createComponent(UserGreetingComponent); - mockStore = TestBed.get(Store); - mockUsernameSelector = mockStore.overrideSelector(fromAuth.getUsername, 'John'); - fixture.detectChanges(); - }); - - it('should greet John when the username is John', () => { - expect(queryDivText()).toBe('Greetings, John!'); - }); - - it('should greet Brandon when the username is Brandon', () => { - mockUsernameSelector.setResult('Brandon'); - mockStore.refreshState(); - fixture.detectChanges(); - expect(queryDivText()).toBe('Greetings, Brandon!'); - }); -}); +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { Store, MemoizedSelector } from '@ngrx/store'; +import { provideMockStore, MockStore } from '@ngrx/store/testing'; +import { UserGreetingComponent } from './user-greeting.component'; +import * as fromAuth from './reducers'; + +describe('User Greeting Component', () => { + let fixture: ComponentFixture; + let mockStore: MockStore; + let mockUsernameSelector: MemoizedSelector; + const queryDivText = () => + fixture.debugElement.queryAll(By.css('div'))[0].nativeElement.textContent; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore()], + declarations: [UserGreetingComponent], + }); + + fixture = TestBed.createComponent(UserGreetingComponent); + mockStore = TestBed.inject(Store); + mockUsernameSelector = mockStore.overrideSelector(fromAuth.getUsername, 'John'); + fixture.detectChanges(); + }); + + it('should greet John when the username is John', () => { + expect(queryDivText()).toBe('Greetings, John!'); + }); + + it('should greet Brandon when the username is Brandon', () => { + mockUsernameSelector.setResult('Brandon'); + mockStore.refreshState(); + fixture.detectChanges(); + expect(queryDivText()).toBe('Greetings, Brandon!'); + }); +}); diff --git a/projects/ngrx.io/content/guide/effects/testing.md b/projects/ngrx.io/content/guide/effects/testing.md index f97f0141f2..ef7db2e3fc 100644 --- a/projects/ngrx.io/content/guide/effects/testing.md +++ b/projects/ngrx.io/content/guide/effects/testing.md @@ -1,333 +1,333 @@ -# Testing - -## Test helpers - -### `provideMockActions` - -An Effect subscribes to the `Actions` Observable to perform side effects. -`provideMockActions` provides a mock provider of the `Actions` Observable to subscribe to, for each test individually. - - -import { provideMockActions } from '@ngrx/effects/testing'; - -let actions$: Observable<Action>; - -TestBed.configureTestingModule({ - providers: [provideMockActions(() => actions$)], -}); - - -Later in the test cases, we assign the `actions$` variable to a stream of actions. - - -// by creating an Observable -actions$ = of({ type: 'Action One' }); - -// or by using a marble diagram -actions$ = hot('--a-', { a: { type: 'Action One' } }); - - -### Effects with parameters - -For time dependant effects, for example `debounceTime`, we must be able override the default RxJS scheduler with the `TestScheduler` during our test. -That's why we create the effect as a function with parameters. By doing this we can assign default parameter values for the effect, and override these values later in the test cases. - -This practice also allows us to hide the implementation details of the effect. -In the `debounceTime` test case, we can we can set the debounce time to a controlled value. - - -search$ = createEffect(() => ({ - // assign default values - debounce = 300, - scheduler = asyncScheduler -} = {}) => - this.actions$.pipe( - ofType(BookActions.search), - debounceTime(debounce, scheduler), - ... - ) -); - - - -// override the default values -effects.search$({ - debounce: 30, - scheduler: getTestScheduler(), -}); - - -## Testing practices - -### Marble diagrams - -Testing Effects via marble diagrams is particularly useful when the Effect is time sensitive or when the Effect has a lot of behavior. - -
- -For a detailed look on the marble syntax, see [Writing marble tests](https://rxjs.dev/guide/testing/marble-testing). - -The `hot`, `cold`, and `toBeObservable` methods are imported from [`jasmine-marbles`](https://www.npmjs.com/package/jasmine-marbles). - -
- - -// create an actions stream to represent a user that is typing -actions$ = hot('-a-b-', { - a: { type: '[Customers Page] Search Customers', name: 'J' }, - b: { type: '[Customers Page] Search Customers', name: 'Jes' }, -}) - -// mock the service to prevent an HTTP request to return an array of customers -customersServiceSpy.searchCustomers.and.returnValue( - cold('--a|', { a: [...] }) -); - -// expect the first action to debounce and not to dispatch -// expect the second action to result in a SUCCESS action -const expected = hot('-------a', { - a: { - type: '[Customers API] Search Customers Success', - customers: [...], - }, -}); - -expect( - effects.searchCustomers$({ - debounce: 20, - scheduler: getTestScheduler(), - }) -).toBeObservable(expected); - - -### With `TestScheduler` - -Instead of using `jasmine-marbles`, we can also run tests with the [RxJS `TestScheduler`](https://rxjs.dev/guide/testing/marble-testing). - -To use the `TestScheduler` we first have to instantiate it, -this can be done in the test case or within a `beforeEach` block. - - -import { TestScheduler } from 'rxjs/testing'; - -let testScheduler: TestScheduler; - -beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); -}); - - -The `TestScheduler` provides a `run` method which expects a callback, it's here where we write the test for an effect. -The callback method provides helper methods to mock Observable streams, and also assertion helper methods to verify the output of a stream. - - -// more info about the API can be found at https://rxjs.dev/guide/testing/marble-testing#api -testScheduler.run(({ cold, hot, expectObservable }) => { - // use the `hot` and `cold` helper methods to create the action and service streams - actions$ = hot('-a', { a : { type: '[Customers Page] Get Customers' }}); - customersServiceSpy.getAllCustomers.and.returnValue(cold('--a|', { a: [...] })); - - // use the `expectObservable` helper method to assert if the output matches the expected output - expectObservable(effects.getAll$).toBe('---c', { - c: { - type: '[Customers API] Get Customers Success', - customers: [...], - } - }); -}); - - -By using the `TestScheduler` we can also test effects dependant on a scheduler. -Instead of creating an effect as a method to override properties in test cases, as shown in [`Effects with parameters`](#effects-with-parameters), we can rewrite the test case by using the `TestScheduler`. - - -testScheduler.run(({ cold, hot, expectObservable }) => { - // create an actions stream to represent a user that is typing - actions$ = hot('-a-b-', { - a: { type: '[Customers Page] Search Customers', name: 'J' }, - b: { type: '[Customers Page] Search Customers', name: 'Jes' }, - }) - - // mock the service to prevent an HTTP request to return an array of customers - customersServiceSpy.searchCustomers.and.returnValue( - cold('--a|', { a: [...] }) - ); - - // the `300ms` is the set debounce time - // the `5ms` represents the time for the actions stream and the service to return a value - expectObservable(effects.searchCustomers).toBe('300ms 5ms c', { - c: { - type: '[Customers API] Search Customers Success', - customers: [...], - }, - }); -}); - - -### With Observables - -To test simple Effects, it might be easier to create an Observable instead of using a marble diagram. - - -// create an actions stream and immediately dispatch a GET action -actions$ = of({ type: '[Customers Page] Get Customers' }); - -// mock the service to prevent an HTTP request -customersServiceSpy.getAllCustomers.and.returnValue(of([...])); - -// subscribe to the Effect stream and verify it dispatches a SUCCESS action -effects.getAll$.subscribe(action => { - expect(action).toEqual({ - type: '[Customers API] Get Customers Success', - customers: [...], - }); -}); - - -### With `ReplaySubject` - -As an alternative, it's also possible to use `ReplaySubject`. - - -// create a ReplaySubject -actions$ = new ReplaySubject(1); - -// mock the service to prevent an HTTP request -customersServiceSpy.getAllCustomers.and.returnValue(of([...])); - -// dispatch the GET action -(actions$ as ReplaySubject).next({ type: '[Customers Page] Get Customers' }) - -// subscribe to the Effect stream and verify it dispatches a SUCCESS action -effects.getAll$.subscribe(action => { - expect(action).toEqual({ - type: '[Customers API] Get Customers Success', - customers: [...], - }); -}); - - -## Examples - -### A non-dispatching Effect - -Until now, we only saw Effects that dispatch an Action and we verified the dispatched action. -With an Effect that does not dispatch an action, we can't verify the Effects stream. -What we can do, is verify the side-effect has been called. - -An example of this is to verify we navigate to the correct page. - - -it('should navigate to the customers detail page', () => { - actions$ = of({ type: '[Customers Page] Customer Selected', name: 'Bob' }); - - // create a spy to verify the navigation will be called - spyOn(router, 'navigateByUrl'); - - // subscribe to execute the Effect - effects.selectCustomer$.subscribe(); - - // verify the navigation has been called - expect(router.navigateByUrl).toHaveBeenCalledWith('customers/bob'); -}); - - -### Effect that uses state - -Leverage [`MockStore`](/guide/store/testing#using-a-mock-store) and [`MockSelectors`](/guide/store/testing#using-mock-selectors) to test Effects that are selecting slices of the state. - -An example of this is to not fetch an entity (customer in this case) when it's already in the store state. - - -let actions$: Observable<Action>; - -TestBed.configureTestingModule({ - providers: [ - CustomersEffects, - provideMockActions(() => actions$), - // mock the Store and the selectors that are used within the Effect - provideMockStore({ - selectors: [ - { - selector: selectCustomers, - value: { - Bob: { name: 'Bob' }, - }, - }, - ], - }), - ], -}); - -effects = TestBed.get<CustomersEffects>(CustomersEffects); - -it('should not fetch if the user is already in the store', () => { - actions$ = hot('-a--', { - a: { type: '[Customers Page] Search Customers', name: 'Bob' }, - }); - - // there is no output, because Bob is already in the Store state - const expected = hot('----'); - - expect(effects.getByName$).toBeObservable(expected); -}); - - -### Setup without `TestBed` - -Instead of using the Angular `TestBed`, we can instantiate the Effect class. - - -it('should get customers', () => { - // instead of using `provideMockActions`, - // define the actions stream by creating a new `Actions` instance - const actions = new Actions( - hot('-a--', { - a: { type: '[Customers Page] Get Customers' }, - }) - ); - - // create the effect - const effects = new CustomersEffects(actions, customersServiceSpy); - - // expect remains the same - effects.getAll$.subscribe(action => { - expect(action).toEqual({ - type: '[Customers API] Get Customers Success', - customers: [...], - }); - }); -}) - - -For an Effect with store interaction, it's possible to create an Observable `Store`. - - -it('should get customers', () => { - // create the store, this can be just an Observable - const store = of({}) as Store<Action>; - - // instead of using `provideMockActions`, - // define the actions stream by creating a new `Actions` instance - const actions = new Actions( - hot('-a--', { - a: { type: '[Search Customers Page] Get Customer', name: 'Bob' }, - }) - ); - - // mock the selector - selectCustomers.setResult({ - Bob: { name: 'Bob' }, - }); - - // create the effect - const effects = new CustomersEffects(store, actions, customersServiceSpy); - - // there is no output, because Bob is already in the Store state - const expected = hot('----'); - - expect(effects.getByName$).toBeObservable(expected); -}); - +# Testing + +## Test helpers + +### `provideMockActions` + +An Effect subscribes to the `Actions` Observable to perform side effects. +`provideMockActions` provides a mock provider of the `Actions` Observable to subscribe to, for each test individually. + + +import { provideMockActions } from '@ngrx/effects/testing'; + +let actions$: Observable<Action>; + +TestBed.configureTestingModule({ + providers: [provideMockActions(() => actions$)], +}); + + +Later in the test cases, we assign the `actions$` variable to a stream of actions. + + +// by creating an Observable +actions$ = of({ type: 'Action One' }); + +// or by using a marble diagram +actions$ = hot('--a-', { a: { type: 'Action One' } }); + + +### Effects with parameters + +For time dependant effects, for example `debounceTime`, we must be able override the default RxJS scheduler with the `TestScheduler` during our test. +That's why we create the effect as a function with parameters. By doing this we can assign default parameter values for the effect, and override these values later in the test cases. + +This practice also allows us to hide the implementation details of the effect. +In the `debounceTime` test case, we can we can set the debounce time to a controlled value. + + +search$ = createEffect(() => ({ + // assign default values + debounce = 300, + scheduler = asyncScheduler +} = {}) => + this.actions$.pipe( + ofType(BookActions.search), + debounceTime(debounce, scheduler), + ... + ) +); + + + +// override the default values +effects.search$({ + debounce: 30, + scheduler: getTestScheduler(), +}); + + +## Testing practices + +### Marble diagrams + +Testing Effects via marble diagrams is particularly useful when the Effect is time sensitive or when the Effect has a lot of behavior. + +
+ +For a detailed look on the marble syntax, see [Writing marble tests](https://rxjs.dev/guide/testing/marble-testing). + +The `hot`, `cold`, and `toBeObservable` methods are imported from [`jasmine-marbles`](https://www.npmjs.com/package/jasmine-marbles). + +
+ + +// create an actions stream to represent a user that is typing +actions$ = hot('-a-b-', { + a: { type: '[Customers Page] Search Customers', name: 'J' }, + b: { type: '[Customers Page] Search Customers', name: 'Jes' }, +}) + +// mock the service to prevent an HTTP request to return an array of customers +customersServiceSpy.searchCustomers.and.returnValue( + cold('--a|', { a: [...] }) +); + +// expect the first action to debounce and not to dispatch +// expect the second action to result in a SUCCESS action +const expected = hot('-------a', { + a: { + type: '[Customers API] Search Customers Success', + customers: [...], + }, +}); + +expect( + effects.searchCustomers$({ + debounce: 20, + scheduler: getTestScheduler(), + }) +).toBeObservable(expected); + + +### With `TestScheduler` + +Instead of using `jasmine-marbles`, we can also run tests with the [RxJS `TestScheduler`](https://rxjs.dev/guide/testing/marble-testing). + +To use the `TestScheduler` we first have to instantiate it, +this can be done in the test case or within a `beforeEach` block. + + +import { TestScheduler } from 'rxjs/testing'; + +let testScheduler: TestScheduler; + +beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); +}); + + +The `TestScheduler` provides a `run` method which expects a callback, it's here where we write the test for an effect. +The callback method provides helper methods to mock Observable streams, and also assertion helper methods to verify the output of a stream. + + +// more info about the API can be found at https://rxjs.dev/guide/testing/marble-testing#api +testScheduler.run(({ cold, hot, expectObservable }) => { + // use the `hot` and `cold` helper methods to create the action and service streams + actions$ = hot('-a', { a : { type: '[Customers Page] Get Customers' }}); + customersServiceSpy.getAllCustomers.and.returnValue(cold('--a|', { a: [...] })); + + // use the `expectObservable` helper method to assert if the output matches the expected output + expectObservable(effects.getAll$).toBe('---c', { + c: { + type: '[Customers API] Get Customers Success', + customers: [...], + } + }); +}); + + +By using the `TestScheduler` we can also test effects dependant on a scheduler. +Instead of creating an effect as a method to override properties in test cases, as shown in [`Effects with parameters`](#effects-with-parameters), we can rewrite the test case by using the `TestScheduler`. + + +testScheduler.run(({ cold, hot, expectObservable }) => { + // create an actions stream to represent a user that is typing + actions$ = hot('-a-b-', { + a: { type: '[Customers Page] Search Customers', name: 'J' }, + b: { type: '[Customers Page] Search Customers', name: 'Jes' }, + }) + + // mock the service to prevent an HTTP request to return an array of customers + customersServiceSpy.searchCustomers.and.returnValue( + cold('--a|', { a: [...] }) + ); + + // the `300ms` is the set debounce time + // the `5ms` represents the time for the actions stream and the service to return a value + expectObservable(effects.searchCustomers).toBe('300ms 5ms c', { + c: { + type: '[Customers API] Search Customers Success', + customers: [...], + }, + }); +}); + + +### With Observables + +To test simple Effects, it might be easier to create an Observable instead of using a marble diagram. + + +// create an actions stream and immediately dispatch a GET action +actions$ = of({ type: '[Customers Page] Get Customers' }); + +// mock the service to prevent an HTTP request +customersServiceSpy.getAllCustomers.and.returnValue(of([...])); + +// subscribe to the Effect stream and verify it dispatches a SUCCESS action +effects.getAll$.subscribe(action => { + expect(action).toEqual({ + type: '[Customers API] Get Customers Success', + customers: [...], + }); +}); + + +### With `ReplaySubject` + +As an alternative, it's also possible to use `ReplaySubject`. + + +// create a ReplaySubject +actions$ = new ReplaySubject(1); + +// mock the service to prevent an HTTP request +customersServiceSpy.getAllCustomers.and.returnValue(of([...])); + +// dispatch the GET action +(actions$ as ReplaySubject).next({ type: '[Customers Page] Get Customers' }) + +// subscribe to the Effect stream and verify it dispatches a SUCCESS action +effects.getAll$.subscribe(action => { + expect(action).toEqual({ + type: '[Customers API] Get Customers Success', + customers: [...], + }); +}); + + +## Examples + +### A non-dispatching Effect + +Until now, we only saw Effects that dispatch an Action and we verified the dispatched action. +With an Effect that does not dispatch an action, we can't verify the Effects stream. +What we can do, is verify the side-effect has been called. + +An example of this is to verify we navigate to the correct page. + + +it('should navigate to the customers detail page', () => { + actions$ = of({ type: '[Customers Page] Customer Selected', name: 'Bob' }); + + // create a spy to verify the navigation will be called + spyOn(router, 'navigateByUrl'); + + // subscribe to execute the Effect + effects.selectCustomer$.subscribe(); + + // verify the navigation has been called + expect(router.navigateByUrl).toHaveBeenCalledWith('customers/bob'); +}); + + +### Effect that uses state + +Leverage [`MockStore`](/guide/store/testing#using-a-mock-store) and [`MockSelectors`](/guide/store/testing#using-mock-selectors) to test Effects that are selecting slices of the state. + +An example of this is to not fetch an entity (customer in this case) when it's already in the store state. + + +let actions$: Observable<Action>; + +TestBed.configureTestingModule({ + providers: [ + CustomersEffects, + provideMockActions(() => actions$), + // mock the Store and the selectors that are used within the Effect + provideMockStore({ + selectors: [ + { + selector: selectCustomers, + value: { + Bob: { name: 'Bob' }, + }, + }, + ], + }), + ], +}); + +effects = TestBed.inject<CustomersEffects>(CustomersEffects); + +it('should not fetch if the user is already in the store', () => { + actions$ = hot('-a--', { + a: { type: '[Customers Page] Search Customers', name: 'Bob' }, + }); + + // there is no output, because Bob is already in the Store state + const expected = hot('----'); + + expect(effects.getByName$).toBeObservable(expected); +}); + + +### Setup without `TestBed` + +Instead of using the Angular `TestBed`, we can instantiate the Effect class. + + +it('should get customers', () => { + // instead of using `provideMockActions`, + // define the actions stream by creating a new `Actions` instance + const actions = new Actions( + hot('-a--', { + a: { type: '[Customers Page] Get Customers' }, + }) + ); + + // create the effect + const effects = new CustomersEffects(actions, customersServiceSpy); + + // expect remains the same + effects.getAll$.subscribe(action => { + expect(action).toEqual({ + type: '[Customers API] Get Customers Success', + customers: [...], + }); + }); +}) + + +For an Effect with store interaction, it's possible to create an Observable `Store`. + + +it('should get customers', () => { + // create the store, this can be just an Observable + const store = of({}) as Store<Action>; + + // instead of using `provideMockActions`, + // define the actions stream by creating a new `Actions` instance + const actions = new Actions( + hot('-a--', { + a: { type: '[Search Customers Page] Get Customer', name: 'Bob' }, + }) + ); + + // mock the selector + selectCustomers.setResult({ + Bob: { name: 'Bob' }, + }); + + // create the effect + const effects = new CustomersEffects(store, actions, customersServiceSpy); + + // there is no output, because Bob is already in the Store state + const expected = hot('----'); + + expect(effects.getByName$).toBeObservable(expected); +}); + diff --git a/projects/ngrx.io/content/guide/migration/v4.md b/projects/ngrx.io/content/guide/migration/v4.md index e27ca2d682..31890cc72f 100644 --- a/projects/ngrx.io/content/guide/migration/v4.md +++ b/projects/ngrx.io/content/guide/migration/v4.md @@ -1,569 +1,569 @@ -# V4 Update Guide - -## Dependencies - -You need to have the latest versions of TypeScript and RxJS to use NgRx version 4 libraries. - -TypeScript 2.4.x -RxJS 5.4.x - -## @ngrx/core - -`@ngrx/core` is no longer needed and conflicts with @ngrx/store. Remove the dependency from your project. - -BEFORE: - -```ts -import { compose } from '@ngrx/core/compose'; -``` - -AFTER: - -```ts -import { compose } from '@ngrx/store'; -``` - -## @ngrx/store - -### Action interface - -The `payload` property has been removed from the `Action` interface. It was a source of type-safety -issues, especially when used with `@ngrx/effects`. If your interface/class has a payload, you need to provide -the type. - -BEFORE: - -```ts -import { Action } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { Effect, Actions } from '@ngrx/effects'; - -@Injectable() -export class MyEffects { - @Effect() - someEffect$: Observable = this.actions$ - .ofType(UserActions.LOGIN) - .pipe( - map(action => action.payload), - map(() => new AnotherAction()) - ); - - constructor(private actions$: Actions) {} -} -``` - -AFTER: - -```ts -import { Action } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { Effect, Actions } from '@ngrx/effects'; -import { Login } from '../actions/auth'; - -@Injectable() -export class MyEffects { - @Effect() - someEffect$: Observable = this.actions$ - .ofType(UserActions.LOGIN) - .pipe( - map(action => action.payload), - map(() => new AnotherAction()) - ); - - constructor(private actions$: Actions) {} -} -``` - -If you prefer to keep the `payload` interface property, you can provide your own parameterized version. - -```ts -export interface ActionWithPayload extends Action { - payload: T; -} -``` - -And if you need an unsafe version to help with transition. - -```ts -export interface UnsafeAction extends Action { - payload?: any; -} -``` - -### Registering Reducers - -Previously to be AOT compatible, it was required to pass a function to the `provideStore` method to compose the reducers into one root reducer. The `initialState` was also provided to the method as an object in the second argument. - -BEFORE: - -`reducers/index.ts` - -```ts -const reducers = { - auth: fromAuth.reducer, - layout: fromLayout.reducer, -}; - -const rootReducer = combineReducers(reducers); - -export function reducer(state: any, action: any) { - return rootReducer(state, action); -} -``` - -`app.module.ts` - -```ts -import { StoreModule } from '@ngrx/store'; -import { reducer } from './reducers'; - -@NgModule({ - imports: [ - StoreModule.provideStore(reducer, { - auth: { - loggedIn: true, - }, - }), - ], -}) -export class AppModule {} -``` - -This has been simplified to only require a map of reducers that will be composed together by the library. A second argument is a configuration object where you provide the `initialState`. - -AFTER: - -`reducers/index.ts` - -```ts -import { ActionReducerMap } from '@ngrx/store'; - -export interface State { - auth: fromAuth.State; - layout: fromLayout.State; -} - -export const reducers: ActionReducerMap = { - auth: fromAuth.reducer, - layout: fromLayout.reducer, -}; -``` - -`app.module.ts` - -```ts -import { StoreModule } from '@ngrx/store'; -import { reducers } from './reducers'; - -@NgModule({ - imports: [ - StoreModule.forRoot(reducers, { - initialState: { - auth: { - loggedIn: true, - }, - }, - }), - ], -}) -export class AppModule {} -``` - -## @ngrx/effects - -### Registering Effects - -BEFORE: - -`app.module.ts` - -```ts -@NgModule({ - imports: [EffectsModule.run(SourceA), EffectsModule.run(SourceB)], -}) -export class AppModule {} -``` - -AFTER: - -The `EffectsModule.forRoot` method is _required_ in your root `AppModule`. Provide an empty array -if you don't need to register any root-level effects. - -`app.module.ts` - -```ts -@NgModule({ - imports: [EffectsModule.forRoot([SourceA, SourceB, SourceC])], -}) -export class AppModule {} -``` - -Import `EffectsModule.forFeature` in any NgModule, whether be the `AppModule` or a feature module. - -`feature.module.ts` - -```ts -@NgModule({ - imports: [ - EffectsModule.forFeature([FeatureSourceA, FeatureSourceB, FeatureSourceC]), - ], -}) -export class FeatureModule {} -``` - -### Init Action - -The `@ngrx/store/init` action now fires prior to effects starting. Use defer() for the same behaviour. - -BEFORE: - -`app.effects.ts` - -```ts -import { Dispatcher, Action } from '@ngrx/store'; -import { Actions, Effect } from '@ngrx/effects'; - -import * as auth from '../actions/auth.actions'; - -@Injectable() -export class AppEffects { - @Effect() - init$: Observable = this.actions$ - .ofType(Dispatcher.INIT) - .switchMap(action => { - return of(new auth.LoginAction()); - }); - - constructor(private actions$: Actions) {} -} -``` - -AFTER: - -`app.effects.ts` - -```ts -import { Action } from '@ngrx/store'; -import { Actions, Effect } from '@ngrx/effects'; -import { defer } from 'rxjs'; - -import * as auth from '../actions/auth.actions'; - -@Injectable() -export class AppEffects { - @Effect() - init$: Observable = defer(() => { - return of(new auth.LoginAction()); - }); - - constructor(private actions$: Actions) {} -} -``` - -### Testing Effects - -BEFORE: - -```ts -import { EffectsTestingModule, EffectsRunner } from '@ngrx/effects/testing'; -import { MyEffects } from './my-effects'; - -describe('My Effects', () => { - let effects: MyEffects; - let runner: EffectsRunner; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [EffectsTestingModule], - providers: [ - MyEffects, - // other providers - ], - }); - - effects = TestBed.get(MyEffects); - runner = TestBed.get(EffectsRunner); - }); - - it('should work', () => { - runner.queue(SomeAction); - - effects.someSource$.subscribe(result => { - expect(result).toBe(AnotherAction); - }); - }); -}); -``` - -AFTER: - -```ts -import { TestBed } from '@angular/core/testing'; -import { provideMockActions } from '@ngrx/effects/testing'; -import { hot, cold } from 'jasmine-marbles'; -import { MyEffects } from './my-effects'; -import { ReplaySubject } from 'rxjs/ReplaySubject'; - -describe('My Effects', () => { - let effects: MyEffects; - let actions: Observable; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - MyEffects, - provideMockActions(() => actions), - // other providers - ], - }); - - effects = TestBed.get(MyEffects); - }); - - it('should work', () => { - actions = hot('--a-', { a: SomeAction, ... }); - - const expected = cold('--b', { b: AnotherAction }); - - expect(effects.someSource$).toBeObservable(expected); - }); - - it('should work also', () => { - actions = new ReplaySubject(1); - - actions.next(SomeAction); - - effects.someSource$.subscribe(result => { - expect(result).toBe(AnotherAction); - }); - }); -}); -``` - -## @ngrx/router-store - -### Registering the module - -BEFORE: - -`reducers/index.ts` - -```ts -import * as fromRouter from '@ngrx/router-store'; - -export interface State { - router: fromRouter.RouterState; -} - -const reducers = { - router: fromRouter.routerReducer, -}; - -const rootReducer = combineReducers(reducers); - -export function reducer(state: any, action: any) { - return rootReducer(state, action); -} -``` - -`app.module.ts` - -```ts -import { RouterModule } from '@angular/router'; -import { RouterStoreModule } from '@ngrx/router-store'; -import { reducer } from './reducers'; - -@NgModule({ - imports: [ - StoreModule.provideStore(reducer), - RouterModule.forRoot([ - // some routes - ]) - RouterStoreModule.connectRouter() - ] -}) -export class AppModule {} -``` - -AFTER: - -`reducers/index.ts` - -```ts -import * as fromRouter from '@ngrx/router-store'; - -export interface State { - routerReducer: fromRouter.RouterReducerState; -} - -export const reducers = { - routerReducer: fromRouter.routerReducer, -}; -``` - -`app.module.ts` - -```ts -import { StoreRouterConnectingModule } from '@ngrx/router-store'; -import { reducers } from './reducers'; - -@NgModule({ - imports: [ - StoreModule.forRoot(reducers), - RouterModule.forRoot([ - // some routes - ]), - StoreRouterConnectingModule, - ], -}) -export class AppModule {} -``` - -### Navigation actions - -Navigation actions are not provided as part of the V4 package. You provide your own -custom navigation actions that use the `Router` within effects to navigate. - -BEFORE: - -```ts -import { go, back, forward } from '@ngrx/router-store'; - -store.dispatch( - go(['/path', { routeParam: 1 }], { page: 1 }, { replaceUrl: false }) -); - -store.dispatch(back()); - -store.dispatch(forward()); -``` - -AFTER: - -```ts -import { Action } from '@ngrx/store'; -import { NavigationExtras } from '@angular/router'; - -export const GO = '[Router] Go'; -export const BACK = '[Router] Back'; -export const FORWARD = '[Router] Forward'; - -export class Go implements Action { - readonly type = GO; - - constructor( - public payload: { - path: any[]; - query?: object; - extras?: NavigationExtras; - } - ) {} -} - -export class Back implements Action { - readonly type = BACK; -} - -export class Forward implements Action { - readonly type = FORWARD; -} - -export type Actions = Go | Back | Forward; -``` - -```ts -import * as RouterActions from './actions/router'; - -store.dispatch(new RouterActions.Go({ - path: ['/path', { routeParam: 1 }], - query: { page: 1 }, - extras: { replaceUrl: false } -}); - -store.dispatch(new RouterActions.Back()); - -store.dispatch(new RouterActions.Forward()); -``` - -```ts -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { Location } from '@angular/common'; -import { Effect, Actions } from '@ngrx/effects'; -import { map, tap } from 'rxjs/operators'; -import * as RouterActions from './actions/router'; - -@Injectable() -export class RouterEffects { - @Effect({ dispatch: false }) - navigate$ = this.actions$.ofType(RouterActions.GO).pipe( - map((action: RouterActions.Go) => action.payload), - tap(({ path, query: queryParams, extras }) => - this.router.navigate(path, { queryParams, ...extras }) - ) - ); - - @Effect({ dispatch: false }) - navigateBack$ = this.actions$ - .ofType(RouterActions.BACK) - .do(() => this.location.back()); - - @Effect({ dispatch: false }) - navigateForward$ = this.actions$ - .ofType(RouterActions.FORWARD) - .do(() => this.location.forward()); - - constructor( - private actions$: Actions, - private router: Router, - private location: Location - ) {} -} -``` - -## @ngrx/store-devtools - -### Instrumentation method - -**NOTE:** store-devtools currently causes severe performance problems when -used with router-store. We are working to -[fix this](https://github.com/ngrx/platform/issues/97), but for now, avoid -using them together. - -BEFORE: - -`app.module.ts` - -```ts -import { StoreDevtoolsModule } from '@ngrx/store-devtools'; - -@NgModule({ - imports: [ - StoreDevtoolsModule.instrumentStore({ maxAge: 50 }), - // OR - StoreDevtoolsModule.instrumentOnlyWithExtension({ - maxAge: 50, - }), - ], -}) -export class AppModule {} -``` - -AFTER: - -`app.module.ts` - -```ts -import { StoreDevtoolsModule } from '@ngrx/store-devtools'; -import { environment } from '../environments/environment'; // Angular CLI environment - -@NgModule({ - imports: [ - !environment.production - ? StoreDevtoolsModule.instrument({ maxAge: 50 }) - : [], - ], -}) -export class AppModule {} -``` +# V4 Update Guide + +## Dependencies + +You need to have the latest versions of TypeScript and RxJS to use NgRx version 4 libraries. + +TypeScript 2.4.x +RxJS 5.4.x + +## @ngrx/core + +`@ngrx/core` is no longer needed and conflicts with @ngrx/store. Remove the dependency from your project. + +BEFORE: + +```ts +import { compose } from '@ngrx/core/compose'; +``` + +AFTER: + +```ts +import { compose } from '@ngrx/store'; +``` + +## @ngrx/store + +### Action interface + +The `payload` property has been removed from the `Action` interface. It was a source of type-safety +issues, especially when used with `@ngrx/effects`. If your interface/class has a payload, you need to provide +the type. + +BEFORE: + +```ts +import { Action } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Effect, Actions } from '@ngrx/effects'; + +@Injectable() +export class MyEffects { + @Effect() + someEffect$: Observable = this.actions$ + .ofType(UserActions.LOGIN) + .pipe( + map(action => action.payload), + map(() => new AnotherAction()) + ); + + constructor(private actions$: Actions) {} +} +``` + +AFTER: + +```ts +import { Action } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Effect, Actions } from '@ngrx/effects'; +import { Login } from '../actions/auth'; + +@Injectable() +export class MyEffects { + @Effect() + someEffect$: Observable = this.actions$ + .ofType(UserActions.LOGIN) + .pipe( + map(action => action.payload), + map(() => new AnotherAction()) + ); + + constructor(private actions$: Actions) {} +} +``` + +If you prefer to keep the `payload` interface property, you can provide your own parameterized version. + +```ts +export interface ActionWithPayload extends Action { + payload: T; +} +``` + +And if you need an unsafe version to help with transition. + +```ts +export interface UnsafeAction extends Action { + payload?: any; +} +``` + +### Registering Reducers + +Previously to be AOT compatible, it was required to pass a function to the `provideStore` method to compose the reducers into one root reducer. The `initialState` was also provided to the method as an object in the second argument. + +BEFORE: + +`reducers/index.ts` + +```ts +const reducers = { + auth: fromAuth.reducer, + layout: fromLayout.reducer, +}; + +const rootReducer = combineReducers(reducers); + +export function reducer(state: any, action: any) { + return rootReducer(state, action); +} +``` + +`app.module.ts` + +```ts +import { StoreModule } from '@ngrx/store'; +import { reducer } from './reducers'; + +@NgModule({ + imports: [ + StoreModule.provideStore(reducer, { + auth: { + loggedIn: true, + }, + }), + ], +}) +export class AppModule {} +``` + +This has been simplified to only require a map of reducers that will be composed together by the library. A second argument is a configuration object where you provide the `initialState`. + +AFTER: + +`reducers/index.ts` + +```ts +import { ActionReducerMap } from '@ngrx/store'; + +export interface State { + auth: fromAuth.State; + layout: fromLayout.State; +} + +export const reducers: ActionReducerMap = { + auth: fromAuth.reducer, + layout: fromLayout.reducer, +}; +``` + +`app.module.ts` + +```ts +import { StoreModule } from '@ngrx/store'; +import { reducers } from './reducers'; + +@NgModule({ + imports: [ + StoreModule.forRoot(reducers, { + initialState: { + auth: { + loggedIn: true, + }, + }, + }), + ], +}) +export class AppModule {} +``` + +## @ngrx/effects + +### Registering Effects + +BEFORE: + +`app.module.ts` + +```ts +@NgModule({ + imports: [EffectsModule.run(SourceA), EffectsModule.run(SourceB)], +}) +export class AppModule {} +``` + +AFTER: + +The `EffectsModule.forRoot` method is _required_ in your root `AppModule`. Provide an empty array +if you don't need to register any root-level effects. + +`app.module.ts` + +```ts +@NgModule({ + imports: [EffectsModule.forRoot([SourceA, SourceB, SourceC])], +}) +export class AppModule {} +``` + +Import `EffectsModule.forFeature` in any NgModule, whether be the `AppModule` or a feature module. + +`feature.module.ts` + +```ts +@NgModule({ + imports: [ + EffectsModule.forFeature([FeatureSourceA, FeatureSourceB, FeatureSourceC]), + ], +}) +export class FeatureModule {} +``` + +### Init Action + +The `@ngrx/store/init` action now fires prior to effects starting. Use defer() for the same behaviour. + +BEFORE: + +`app.effects.ts` + +```ts +import { Dispatcher, Action } from '@ngrx/store'; +import { Actions, Effect } from '@ngrx/effects'; + +import * as auth from '../actions/auth.actions'; + +@Injectable() +export class AppEffects { + @Effect() + init$: Observable = this.actions$ + .ofType(Dispatcher.INIT) + .switchMap(action => { + return of(new auth.LoginAction()); + }); + + constructor(private actions$: Actions) {} +} +``` + +AFTER: + +`app.effects.ts` + +```ts +import { Action } from '@ngrx/store'; +import { Actions, Effect } from '@ngrx/effects'; +import { defer } from 'rxjs'; + +import * as auth from '../actions/auth.actions'; + +@Injectable() +export class AppEffects { + @Effect() + init$: Observable = defer(() => { + return of(new auth.LoginAction()); + }); + + constructor(private actions$: Actions) {} +} +``` + +### Testing Effects + +BEFORE: + +```ts +import { EffectsTestingModule, EffectsRunner } from '@ngrx/effects/testing'; +import { MyEffects } from './my-effects'; + +describe('My Effects', () => { + let effects: MyEffects; + let runner: EffectsRunner; + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [EffectsTestingModule], + providers: [ + MyEffects, + // other providers + ], + }); + + effects = TestBed.inject(MyEffects); + runner = TestBed.inject(EffectsRunner); + }); + + it('should work', () => { + runner.queue(SomeAction); + + effects.someSource$.subscribe(result => { + expect(result).toBe(AnotherAction); + }); + }); +}); +``` + +AFTER: + +```ts +import { TestBed } from '@angular/core/testing'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { hot, cold } from 'jasmine-marbles'; +import { MyEffects } from './my-effects'; +import { ReplaySubject } from 'rxjs/ReplaySubject'; + +describe('My Effects', () => { + let effects: MyEffects; + let actions: Observable; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + MyEffects, + provideMockActions(() => actions), + // other providers + ], + }); + + effects = TestBed.inject(MyEffects); + }); + + it('should work', () => { + actions = hot('--a-', { a: SomeAction, ... }); + + const expected = cold('--b', { b: AnotherAction }); + + expect(effects.someSource$).toBeObservable(expected); + }); + + it('should work also', () => { + actions = new ReplaySubject(1); + + actions.next(SomeAction); + + effects.someSource$.subscribe(result => { + expect(result).toBe(AnotherAction); + }); + }); +}); +``` + +## @ngrx/router-store + +### Registering the module + +BEFORE: + +`reducers/index.ts` + +```ts +import * as fromRouter from '@ngrx/router-store'; + +export interface State { + router: fromRouter.RouterState; +} + +const reducers = { + router: fromRouter.routerReducer, +}; + +const rootReducer = combineReducers(reducers); + +export function reducer(state: any, action: any) { + return rootReducer(state, action); +} +``` + +`app.module.ts` + +```ts +import { RouterModule } from '@angular/router'; +import { RouterStoreModule } from '@ngrx/router-store'; +import { reducer } from './reducers'; + +@NgModule({ + imports: [ + StoreModule.provideStore(reducer), + RouterModule.forRoot([ + // some routes + ]) + RouterStoreModule.connectRouter() + ] +}) +export class AppModule {} +``` + +AFTER: + +`reducers/index.ts` + +```ts +import * as fromRouter from '@ngrx/router-store'; + +export interface State { + routerReducer: fromRouter.RouterReducerState; +} + +export const reducers = { + routerReducer: fromRouter.routerReducer, +}; +``` + +`app.module.ts` + +```ts +import { StoreRouterConnectingModule } from '@ngrx/router-store'; +import { reducers } from './reducers'; + +@NgModule({ + imports: [ + StoreModule.forRoot(reducers), + RouterModule.forRoot([ + // some routes + ]), + StoreRouterConnectingModule, + ], +}) +export class AppModule {} +``` + +### Navigation actions + +Navigation actions are not provided as part of the V4 package. You provide your own +custom navigation actions that use the `Router` within effects to navigate. + +BEFORE: + +```ts +import { go, back, forward } from '@ngrx/router-store'; + +store.dispatch( + go(['/path', { routeParam: 1 }], { page: 1 }, { replaceUrl: false }) +); + +store.dispatch(back()); + +store.dispatch(forward()); +``` + +AFTER: + +```ts +import { Action } from '@ngrx/store'; +import { NavigationExtras } from '@angular/router'; + +export const GO = '[Router] Go'; +export const BACK = '[Router] Back'; +export const FORWARD = '[Router] Forward'; + +export class Go implements Action { + readonly type = GO; + + constructor( + public payload: { + path: any[]; + query?: object; + extras?: NavigationExtras; + } + ) {} +} + +export class Back implements Action { + readonly type = BACK; +} + +export class Forward implements Action { + readonly type = FORWARD; +} + +export type Actions = Go | Back | Forward; +``` + +```ts +import * as RouterActions from './actions/router'; + +store.dispatch(new RouterActions.Go({ + path: ['/path', { routeParam: 1 }], + query: { page: 1 }, + extras: { replaceUrl: false } +}); + +store.dispatch(new RouterActions.Back()); + +store.dispatch(new RouterActions.Forward()); +``` + +```ts +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Location } from '@angular/common'; +import { Effect, Actions } from '@ngrx/effects'; +import { map, tap } from 'rxjs/operators'; +import * as RouterActions from './actions/router'; + +@Injectable() +export class RouterEffects { + @Effect({ dispatch: false }) + navigate$ = this.actions$.ofType(RouterActions.GO).pipe( + map((action: RouterActions.Go) => action.payload), + tap(({ path, query: queryParams, extras }) => + this.router.navigate(path, { queryParams, ...extras }) + ) + ); + + @Effect({ dispatch: false }) + navigateBack$ = this.actions$ + .ofType(RouterActions.BACK) + .do(() => this.location.back()); + + @Effect({ dispatch: false }) + navigateForward$ = this.actions$ + .ofType(RouterActions.FORWARD) + .do(() => this.location.forward()); + + constructor( + private actions$: Actions, + private router: Router, + private location: Location + ) {} +} +``` + +## @ngrx/store-devtools + +### Instrumentation method + +**NOTE:** store-devtools currently causes severe performance problems when +used with router-store. We are working to +[fix this](https://github.com/ngrx/platform/issues/97), but for now, avoid +using them together. + +BEFORE: + +`app.module.ts` + +```ts +import { StoreDevtoolsModule } from '@ngrx/store-devtools'; + +@NgModule({ + imports: [ + StoreDevtoolsModule.instrumentStore({ maxAge: 50 }), + // OR + StoreDevtoolsModule.instrumentOnlyWithExtension({ + maxAge: 50, + }), + ], +}) +export class AppModule {} +``` + +AFTER: + +`app.module.ts` + +```ts +import { StoreDevtoolsModule } from '@ngrx/store-devtools'; +import { environment } from '../environments/environment'; // Angular CLI environment + +@NgModule({ + imports: [ + !environment.production + ? StoreDevtoolsModule.instrument({ maxAge: 50 }) + : [], + ], +}) +export class AppModule {} +``` diff --git a/projects/ngrx.io/content/guide/store/testing.md b/projects/ngrx.io/content/guide/store/testing.md index 14f42b1831..2dc8d2ce95 100644 --- a/projects/ngrx.io/content/guide/store/testing.md +++ b/projects/ngrx.io/content/guide/store/testing.md @@ -1,127 +1,127 @@ -# Testing - -### Using a Mock Store - -The `provideMockStore()` function registers providers that allow you to mock out the `Store` for testing functionality that has a dependency on `Store` without setting up reducers. -You can write tests validating behaviors corresponding to the specific state snapshot easily. - -
- -**Note:** All dispatched actions don't affect the state, but you can see them in the `Actions` stream. - -
- -Usage: - - -import { TestBed } from '@angular/core/testing'; -import { Store } from '@ngrx/store'; -import { provideMockStore, MockStore } from '@ngrx/store/testing'; -import { cold } from 'jasmine-marbles'; - -import { AuthGuard } from '../guards/auth.guard'; - -describe('Auth Guard', () => { - let guard: AuthGuard; - let store: MockStore<{ loggedIn: boolean }>; - const initialState = { loggedIn: false }; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - // any modules needed - ], - providers: [ - AuthGuard, - provideMockStore({ initialState }), - // other providers - ], - }); - - store = TestBed.get<Store>(Store); - guard = TestBed.get<AuthGuard>(AuthGuard); - }); - - it('should return false if the user state is not logged in', () => { - const expected = cold('(a|)', { a: false }); - - expect(guard.canActivate()).toBeObservable(expected); - }); - - it('should return true if the user state is logged in', () => { - store.setState({ loggedIn: true }); - - const expected = cold('(a|)', { a: true }); - - expect(guard.canActivate()).toBeObservable(expected); - }); -}); - - -### Using Mock Selectors - -`MockStore` also provides the ability to mock individual selectors to return a passed value using the `overrideSelector()` method. When the selector is invoked by the `select` method, the returned value is overridden by the passed value, regardless of the current state in the store. - -`overrideSelector()` returns a `MemoizedSelector`. To update the mock selector to return a different value, use the `MemoizedSelector`'s `setResult()` method. Updating a selector's mock value will not cause it to emit automatically. To trigger an emission from all selectors, use the `MockStore.refreshState()` method after updating the desired selectors. - -`overrideSelector()` supports mocking the `select` method (used in RxJS pipe) and the `Store` `select` instance method using a string or selector. - -Usage: - - - - - -In this example, we mock the `getUsername` selector by using `overrideSelector`, passing in the `getUsername` selector with a default mocked return value of `'John'`. In the second test, we use `setResult()` to update the mock selector to return `'Brandon'`, then we use `MockStore.refreshState()` to trigger an emission from the `getUsername` selector. - -
- -**Note:** `MockStore` will reset all of the mocked selectors after each test (in the `afterEach()` hook) by calling the `MockStore.resetSelectors()` method. - -
- -Try the . - -### Integration Testing - -An integration test should verify that the `Store` coherently works together with our components and services that inject `Store`. An integration test will not mock the store or individual selectors, as unit tests do, but will instead integrate a `Store` by using `StoreModule.forRoot` in your `TestBed` configuration. Here is an example of an integration test for the `MyCounterComponent` introduced in the [getting started tutorial](guide/store#tutorial). - - - - -The integration test sets up the dependent `Store` by importing the `StoreModule`. In this example, we assert that clicking a button dispatches an action that causes the state to be updated with an incremented, decremented, or reset counter value, which is correctly emitted by the selector. - -### Testing selectors - -You can use the projector function used by the selector by accessing the `.projector` property. - - -export interface State { - evenNums: number[]; - oddNums: number[]; -} - -export const selectSumEvenNums = createSelector( - (state: State) => state.evenNums, - evenNums => evenNums.reduce((prev, curr) => prev + curr) -); -export const selectSumOddNums = createSelector( - (state: State) => state.oddNums, - oddNums => oddNums.reduce((prev, curr) => prev + curr) -); -export const selectTotal = createSelector( - selectSumEvenNums, - selectSumOddNums, - (evenSum, oddSum) => evenSum + oddSum -); - - - -import * as fromMyReducers from './my-reducers'; - -describe('My Selectors', () => { - it('should calc selectTotal', () => { - expect(fromMyReducers.selectTotal.projector(2, 3)).toBe(5); - }); -}); - +# Testing + +### Using a Mock Store + +The `provideMockStore()` function registers providers that allow you to mock out the `Store` for testing functionality that has a dependency on `Store` without setting up reducers. +You can write tests validating behaviors corresponding to the specific state snapshot easily. + +
+ +**Note:** All dispatched actions don't affect the state, but you can see them in the `Actions` stream. + +
+ +Usage: + + +import { TestBed } from '@angular/core/testing'; +import { Store } from '@ngrx/store'; +import { provideMockStore, MockStore } from '@ngrx/store/testing'; +import { cold } from 'jasmine-marbles'; + +import { AuthGuard } from '../guards/auth.guard'; + +describe('Auth Guard', () => { + let guard: AuthGuard; + let store: MockStore<{ loggedIn: boolean }>; + const initialState = { loggedIn: false }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + // any modules needed + ], + providers: [ + AuthGuard, + provideMockStore({ initialState }), + // other providers + ], + }); + + store = TestBed.inject<Store>(Store); + guard = TestBed.inject<AuthGuard>(AuthGuard); + }); + + it('should return false if the user state is not logged in', () => { + const expected = cold('(a|)', { a: false }); + + expect(guard.canActivate()).toBeObservable(expected); + }); + + it('should return true if the user state is logged in', () => { + store.setState({ loggedIn: true }); + + const expected = cold('(a|)', { a: true }); + + expect(guard.canActivate()).toBeObservable(expected); + }); +}); + + +### Using Mock Selectors + +`MockStore` also provides the ability to mock individual selectors to return a passed value using the `overrideSelector()` method. When the selector is invoked by the `select` method, the returned value is overridden by the passed value, regardless of the current state in the store. + +`overrideSelector()` returns a `MemoizedSelector`. To update the mock selector to return a different value, use the `MemoizedSelector`'s `setResult()` method. Updating a selector's mock value will not cause it to emit automatically. To trigger an emission from all selectors, use the `MockStore.refreshState()` method after updating the desired selectors. + +`overrideSelector()` supports mocking the `select` method (used in RxJS pipe) and the `Store` `select` instance method using a string or selector. + +Usage: + + + + + +In this example, we mock the `getUsername` selector by using `overrideSelector`, passing in the `getUsername` selector with a default mocked return value of `'John'`. In the second test, we use `setResult()` to update the mock selector to return `'Brandon'`, then we use `MockStore.refreshState()` to trigger an emission from the `getUsername` selector. + +
+ +**Note:** `MockStore` will reset all of the mocked selectors after each test (in the `afterEach()` hook) by calling the `MockStore.resetSelectors()` method. + +
+ +Try the . + +### Integration Testing + +An integration test should verify that the `Store` coherently works together with our components and services that inject `Store`. An integration test will not mock the store or individual selectors, as unit tests do, but will instead integrate a `Store` by using `StoreModule.forRoot` in your `TestBed` configuration. Here is an example of an integration test for the `MyCounterComponent` introduced in the [getting started tutorial](guide/store#tutorial). + + + + +The integration test sets up the dependent `Store` by importing the `StoreModule`. In this example, we assert that clicking a button dispatches an action that causes the state to be updated with an incremented, decremented, or reset counter value, which is correctly emitted by the selector. + +### Testing selectors + +You can use the projector function used by the selector by accessing the `.projector` property. + + +export interface State { + evenNums: number[]; + oddNums: number[]; +} + +export const selectSumEvenNums = createSelector( + (state: State) => state.evenNums, + evenNums => evenNums.reduce((prev, curr) => prev + curr) +); +export const selectSumOddNums = createSelector( + (state: State) => state.oddNums, + oddNums => oddNums.reduce((prev, curr) => prev + curr) +); +export const selectTotal = createSelector( + selectSumEvenNums, + selectSumOddNums, + (evenSum, oddSum) => evenSum + oddSum +); + + + +import * as fromMyReducers from './my-reducers'; + +describe('My Selectors', () => { + it('should calc selectTotal', () => { + expect(fromMyReducers.selectTotal.projector(2, 3)).toBe(5); + }); +}); + diff --git a/projects/ngrx.io/src/app/app.component.spec.ts b/projects/ngrx.io/src/app/app.component.spec.ts index 30427af502..8cebff4135 100644 --- a/projects/ngrx.io/src/app/app.component.spec.ts +++ b/projects/ngrx.io/src/app/app.component.spec.ts @@ -1,621 +1,621 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; -import { Deployment } from './shared/deployment.service'; -import { DocumentService, DocumentContents } from './documents/document.service'; -import { ElementRef, Component, Input } from '@angular/core'; -import { of, Observable, ReplaySubject } from 'rxjs'; -import { LocationService } from './shared/location.service'; -import { NavigationService, CurrentNodes, VersionInfo, NavigationViews } from './navigation/navigation.service'; -import { ScrollService } from './shared/scroll.service'; -import { SearchService } from './search/search.service'; -import { TocService, TocItem } from './shared/toc.service'; -import { MatProgressBarModule, MatToolbarModule, MatSidenavModule, MatSidenav } from '@angular/material'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { MockSearchService } from 'testing/search.service'; -import { NotificationComponent } from './layout/notification/notification.component'; -import { By } from '@angular/platform-browser'; -import { SearchResultsComponent } from './shared/search-results/search-results.component'; - -const hideToCBreakPoint = 800; - -describe('AppComponent', () => { - let component: AppComponent; - let fixture: ComponentFixture; - let searchService: SearchService; - let deployment: Deployment; - let locationService: LocationService; - let locationServiceReplaceSpy: jasmine.Spy; - let scrollService: ScrollService; - let tocService: TocService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ - AppComponent, - MockMatIconComponent, - MockAioNotificationComponent, - MockAioTopMenuComponent, - SearchResultsComponent, - MockAioNavMenuComponent, - MockAioSelectComponent, - MockAioModeBannerComponent, - MockAioDocViewerComponent, - MockAioDtComponent, - MockAioLazyCeComponent, - MockAioFooterComponent, - MockAioSearchBoxComponent - ], - imports: [ - MatProgressBarModule, - MatToolbarModule, - MatSidenavModule, - BrowserAnimationsModule - ], - providers: [ - { provide: Deployment, - useClass: MockDeployment - }, - { provide: DocumentService, - useClass: MockDocumentService - }, - { provide: ElementRef, - useClass: MockElementRef - }, - { - provide: LocationService, - useClass: MockLocationService, - }, - { provide: NavigationService, - useClass: MockNavigationService - }, - { provide: ScrollService, - useClass: MockScrollService - }, - { provide: SearchService, - useClass: MockSearchService - }, - { provide: TocService, - useClass: MockTocService - } - ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AppComponent); - component = fixture.componentInstance; - component.notification = { showNotification: 'show' } as NotificationComponent; - component.sidenav = { opened: true, toggle: () => {}} as MatSidenav; - spyOn(component, 'onResize').and.callThrough(); - searchService = TestBed.get(SearchService); - deployment = TestBed.get(Deployment); - locationService = TestBed.get(LocationService); - locationServiceReplaceSpy = spyOn(locationService, 'replace'); - scrollService = TestBed.get(ScrollService); - tocService = TestBed.get(TocService); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('ngOnInit', () => { - it('should set the currentDocument from the DocumentService', () => { - expect(component.currentDocument).toEqual({ id: '1', contents: 'contents' }); - }); - - it('should size the window', () => { - expect(component.onResize).toHaveBeenCalledTimes(1); - }); - - it('should initialize the search worker', () => { - expect(searchService.initWorker).toHaveBeenCalledWith('app/search/search-worker.js', 2000); - }); - - describe('archive redirection', () => { - - it('should redirect to docs if we are in archive mode and are not hitting a docs, api, guide, or tutorial page', () => { - deployment.mode = 'archive'; - locationService.currentPath = of('events'); - component.ngOnInit(); - expect(locationService.replace).toHaveBeenCalledWith('docs'); - }); - - it('should not redirect to docs if we are hitting a docs page', () => { - deployment.mode = 'archive'; - locationService.currentPath = of('docs'); - locationServiceReplaceSpy.calls.reset(); - component.ngOnInit(); - expect(locationService.replace).not.toHaveBeenCalled(); - }); - - it('should not redirect to docs if we are hitting a api page', () => { - deployment.mode = 'archive'; - locationService.currentPath = of('api'); - locationServiceReplaceSpy.calls.reset(); - component.ngOnInit(); - expect(locationService.replace).not.toHaveBeenCalled(); - }); - - it('should not redirect to docs if we are hitting a guide page', () => { - deployment.mode = 'archive'; - locationService.currentPath = of('guide'); - locationServiceReplaceSpy.calls.reset(); - component.ngOnInit(); - expect(locationService.replace).not.toHaveBeenCalled(); - }); - - it('should not redirect to docs if we are hitting a tutorial page', () => { - deployment.mode = 'archive'; - locationService.currentPath = of('tutorial'); - locationServiceReplaceSpy.calls.reset(); - component.ngOnInit(); - expect(locationService.replace).not.toHaveBeenCalled(); - }); - }); - - it('should auto scroll if the path changes while on the current page', () => { - component.currentPath = 'docs'; - locationService.currentPath = of('docs'); - spyOn(scrollService, 'scroll'); - component.ngOnInit(); - expect(scrollService.scroll).toHaveBeenCalledTimes(1); - }); - - it('should not auto scroll if the path changes to a different page', () => { - component.currentPath = 'docs'; - locationService.currentPath = of('events'); - spyOn(scrollService, 'scroll'); - component.ngOnInit(); - expect(scrollService.scroll).not.toHaveBeenCalled(); - }); - - describe('version picker', () => { - it('should contain next and stable by default', () => { - expect(component.docVersions).toContain({ title: 'next', url: 'https://next.ngrx.io/docs' }); - expect(component.docVersions).toContain({ title: 'stable (v6.3)', url: 'https://ngrx.io/docs' }); - }); - - it('should add the current version if in archive mode', () => { - deployment.mode = 'archive'; - component.ngOnInit(); - expect(component.docVersions).toContain({ title: 'v6 (v6.3)'}); - }); - - it('should find the current version by deployment mode and append the raw version info to the title', () => { - expect(component.currentDocVersion.title).toBe('stable (v6.3)'); - }); - - it('should find the current version by the current maajor version and append the raw version info to the title', () => { - deployment.mode = 'archive'; - component.ngOnInit(); - expect(component.currentDocVersion.title).toBe('v6 (v6.3)'); - }); - }); - - describe('hasFloatingToc', () => { - it('should initially be false', () => { - const fixture2 = TestBed.createComponent(AppComponent); - const component2 = fixture2.componentInstance; - - expect(component2.hasFloatingToc).toBe(false); - }); - - it('should be false on narrow screens', () => { - component.onResize(hideToCBreakPoint - 1); - - tocService.tocList.next([{}, {}, {}] as TocItem[]); - expect(component.hasFloatingToc).toBe(false); - - tocService.tocList.next([]); - expect(component.hasFloatingToc).toBe(false); - - tocService.tocList.next([{}, {}, {}] as TocItem[]); - expect(component.hasFloatingToc).toBe(false); - }); - - it('should be true on wide screens unless the toc is empty', () => { - component.onResize(hideToCBreakPoint + 1); - - tocService.tocList.next([{}, {}, {}] as TocItem[]); - expect(component.hasFloatingToc).toBe(true); - - tocService.tocList.next([]); - expect(component.hasFloatingToc).toBe(false); - - tocService.tocList.next([{}, {}, {}] as TocItem[]); - expect(component.hasFloatingToc).toBe(true); - }); - - it('should be false when toc is empty', () => { - tocService.tocList.next([]); - - component.onResize(hideToCBreakPoint + 1); - expect(component.hasFloatingToc).toBe(false); - - component.onResize(hideToCBreakPoint - 1); - expect(component.hasFloatingToc).toBe(false); - - component.onResize(hideToCBreakPoint + 1); - expect(component.hasFloatingToc).toBe(false); - }); - - it('should be true when toc is not empty unless the screen is narrow', () => { - tocService.tocList.next([{}, {}, {}] as TocItem[]); - - component.onResize(hideToCBreakPoint + 1); - expect(component.hasFloatingToc).toBe(true); - - component.onResize(hideToCBreakPoint - 1); - expect(component.hasFloatingToc).toBe(false); - - component.onResize(hideToCBreakPoint + 1); - expect(component.hasFloatingToc).toBe(true); - }); - }); - }); - - describe('onDocVersionChange', () => { - it('should navigate to the new version url', () => { - component.docVersions = [ - { title: 'next', url: 'https://next.ngrx.io'}, - { title: 'stable (v6.3)', url: 'https://ngrx.io' } - ]; - spyOn(locationService, 'go'); - component.onDocVersionChange(1); - expect(locationService.go).toHaveBeenCalledTimes(1); - }); - it('should not navigate to new version if it does not define a Url', () => { - component.docVersions = [ - { title: 'next', url: 'https://next.ngrx.io'}, - { title: 'stable (v6.3)', url: 'https://ngrx.io' }, - { title: 'v1'} - ]; - spyOn(locationService, 'go'); - component.onDocVersionChange(2); - expect(locationService.go).not.toHaveBeenCalled(); - }); - }); - - describe('onResize', () => { - it('should set isSideBySide to true if the window width is greater than 992 pixels', () => { - component.isSideBySide = false; - component.onResize(993); - expect(component.isSideBySide).toBeTruthy(); - }); - - it('should set isSideBySide to false if the window width is less than or equal to 992 pixels', () => { - component.isSideBySide = true; - component.onResize(992); - expect(component.isSideBySide).toBeFalsy(); - }); - - it('should set hasFloatingToc to true if the window width is greater than 800 and the toc list is greater than zero', () => { - tocService.tocList.next([{}] as TocItem[]); - component.onResize(801); - expect(component.hasFloatingToc).toBeTruthy(); - }); - - it('should set hasFloatingToc to false if the window width is less than or equal to 800 and the toc list is greater than zero', () => { - tocService.tocList.next([{}] as TocItem[]); - component.onResize(800); - expect(component.hasFloatingToc).toBeFalsy(); - }); - - it('should toggle the sidenav closed if it is not a doc page and the screen is wide enough to display menu items ' - + 'in the top-bar', () => { - const sideNavToggleSpy = spyOn(component.sidenav, 'toggle'); - sideNavToggleSpy.calls.reset(); - component.updateSideNav(); - component.onResize(993); - expect(component.sidenav.toggle).toHaveBeenCalledWith(false); - }); - }); - - // describe('click handler', () => { - // it('should hide the search results if we clicked outside both the "search box" and the "search results"', () => { - // component.searchElements = new QueryList(); - // component.searchElements. - // console.log(component.searchElements.length); - // }); - // }); - - describe('search', () => { - - let docViewer: HTMLElement; - - beforeEach(() => { - const documentViewerDebugElement = fixture.debugElement.query(By.css('aio-doc-viewer')); - docViewer = documentViewerDebugElement.nativeElement; - }); - - describe('click handling', () => { - it('should intercept clicks not on the search elements and hide the search results', () => { - component.showSearchResults = true; - fixture.detectChanges(); - // docViewer is a commonly-clicked, non-search element - docViewer.click(); - expect(component.showSearchResults).toBe(false); - }); - - it('should clear "only" the search query param from the URL', () => { - // Mock out the current state of the URL query params - spyOn(locationService, 'search').and.returnValue({ - a: 'some-A', - b: 'some-B', - search: 'some-C', - }); - spyOn(locationService, 'setSearch'); - // docViewer is a commonly-clicked, non-search element - docViewer.click(); - // Check that the query params were updated correctly - expect(locationService.setSearch).toHaveBeenCalledWith('', { - a: 'some-A', - b: 'some-B', - search: undefined, - }); - }); - - it('should not intercept clicks on the searchResults', () => { - component.showSearchResults = true; - fixture.detectChanges(); - - const searchResults = fixture.debugElement.query( - By.directive(SearchResultsComponent) - ); - searchResults.nativeElement.click(); - fixture.detectChanges(); - - expect(component.showSearchResults).toBe(true); - }); - - it('should return the result of handleAnchorClick when anchor is clicked', () => { - const anchorElement: HTMLAnchorElement = document.createElement('a'); - spyOn(locationService, 'handleAnchorClick').and.returnValue(true); - expect(component.onClick(anchorElement, 1, false, false, true)).toBeTruthy(); - expect(locationService.handleAnchorClick).toHaveBeenCalledTimes(1); - }); - }); - }); - - it('updateHostClasses', () => { - component.notificationAnimating = true; - component.hostClasses = ''; - component.updateHostClasses(); - expect(component.hostClasses) - .toBe('mode-stable sidenav-open page-1 folder-1 view-view aio-notification-show aio-notification-animating'); - }); - - describe('updateSideNav', () => { - it('should preserve the current sidenav open state if view type does not change', () => { - component.isSideBySide = true; - component.sidenav.opened = true; - const toggleSpy = spyOn(component.sidenav, 'toggle'); - - component.updateSideNav(); - expect(component.sidenav.toggle).toHaveBeenCalledWith(true); - expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); - - component.isSideBySide = false; - toggleSpy.calls.reset(); - component.updateSideNav(); - expect(component.sidenav.toggle).toHaveBeenCalledWith(false); - expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); - }); - - it('should open if changed from a non sidenav doc to a sidenav doc and close if changed from sidenav doc to non sidenav doc', () => { - const toggleSpy = spyOn(component.sidenav, 'toggle'); - component.isSideBySide = true; - component.currentNodes = { - 'SideNav': { url: '', view: '', nodes: [] } - }; - component.updateSideNav(); - expect(component.sidenav.toggle).toHaveBeenCalledWith(true); - expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); - - component.currentNodes = {}; - toggleSpy.calls.reset(); - component.updateSideNav(); - expect(component.sidenav.toggle).toHaveBeenCalledWith(false); - expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); - }); - }); - - describe('restrain scrolling inside an element when the cursor is over it', () => { - it('should prevent scrolling up when already at the top', () => { - const scrollUpEvent = { - deltaY: -1, - currentTarget: { scrollTop: 0 }, - preventDefault: () => {} - } as any; - spyOn(scrollUpEvent, 'preventDefault'); - component.restrainScrolling(scrollUpEvent); - expect(scrollUpEvent.preventDefault).toHaveBeenCalledTimes(1); - }); - - it('should prevent scrolling down when already at the bottom', () => { - const scrollUpEvent = { - deltaY: 1, - currentTarget: { - scrollTop: 10, - scrollHeight: 20, - clientHeight: 10 - }, - preventDefault: () => {} - } as any; - spyOn(scrollUpEvent, 'preventDefault'); - component.restrainScrolling(scrollUpEvent); - expect(scrollUpEvent.preventDefault).toHaveBeenCalledTimes(1); - }); - }); - - describe('key handling', () => { - - beforeEach(() => { - spyOn(component, 'focusSearchBox'); - spyOn(component, 'hideSearchResults'); - }); - - it('should focus search box on forward slash key "/"', () => { - component.onKeyUp('/', 190); - expect(component.focusSearchBox).toHaveBeenCalledTimes(1); - }); - - it('should focus search box on forward slash keycode', () => { - component.onKeyUp('', 191); - expect(component.focusSearchBox).toHaveBeenCalledTimes(1); - }); - - it('should hide the search results and focus search box if results are being shown on escape key', () => { - component.showSearchResults = true; - component.onKeyUp('Escape', 28); - expect(component.focusSearchBox).toHaveBeenCalledTimes(1); - expect(component.hideSearchResults).toHaveBeenCalledTimes(1); - }); - - it('should hide the search results and focus search box if results are being shown on escape keycode', () => { - component.showSearchResults = true; - component.onKeyUp('', 27); - expect(component.focusSearchBox).toHaveBeenCalledTimes(1); - expect(component.hideSearchResults).toHaveBeenCalledTimes(1); - }); - }); -}); - -// Mock Dependencies - -class MockLocationService { - currentPath = of('path'); - replace = () => {}; - go = () => {}; - handleAnchorClick = () => true; - setSearch = () => {}; - search = () => {}; -} - - -class MockDeployment { - mode = 'stable'; -} - -class MockDocumentService { - currentDocument: Observable = of({ id: '1', contents: 'contents' }); -} - -class MockElementRef { - nativeElement: Element; -} - -class MockNavigationService { - currentNodes: Observable = of({ 'view': { url: 'path', view: 'view', nodes: []}}) - versionInfo: Observable = of({ major: 6, raw: '6.3'}); - navigationViews: Observable = of({ 'docVersions' : [{ title: 'v5'}]}); -} - -export class MockTocService { - genToc = jasmine.createSpy('TocService#genToc'); - reset = jasmine.createSpy('TocService#reset'); - tocList = new ReplaySubject(1); -} - -class MockScrollService { - scroll = () => {}; - scrollToTop = () => {}; -} - -// Mock Child Components - - -@Component({ - // tslint:disable-next-line:component-selector - selector: 'mat-icon', - template: '' -}) -class MockMatIconComponent { - @Input() svgIcon; -} - -@Component({ - selector: 'aio-notification', - template: '' -}) -class MockAioNotificationComponent { - @Input() dismissOnContentClick; - showNotification = 'show' -} - -@Component({ - selector: 'aio-top-menu', - template: '' -}) -class MockAioTopMenuComponent { - @Input() nodes; -} - -@Component({ - selector: 'aio-nav-menu', - template: '' -}) -class MockAioNavMenuComponent { - @Input() nodes; - @Input() currentNode; - @Input() isWide; -} - -@Component({ - selector: 'aio-select', - template: '' -}) -class MockAioSelectComponent { - @Input() options; - @Input() selected; -} - -@Component({ - selector: 'aio-mode-banner', - template: '' -}) -class MockAioModeBannerComponent { - @Input() mode; - @Input() version; -} - -@Component({ - selector: 'aio-doc-viewer', - template: '' -}) -class MockAioDocViewerComponent { - @Input() doc; -} - -@Component({ - selector: 'aio-dt', - template: '' -}) -class MockAioDtComponent { - @Input() on; - @Input() doc; -} - -@Component({ - selector: 'aio-lazy-ce', - template: '' -}) -class MockAioLazyCeComponent { } - -@Component({ - selector: 'aio-footer', - template: '' -}) -class MockAioFooterComponent { - @Input() nodes; - @Input() versionInfo; -} - -@Component({ - selector: 'aio-search-box', - template: '' -}) -class MockAioSearchBoxComponent {} +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +import { Deployment } from './shared/deployment.service'; +import { DocumentService, DocumentContents } from './documents/document.service'; +import { ElementRef, Component, Input } from '@angular/core'; +import { of, Observable, ReplaySubject } from 'rxjs'; +import { LocationService } from './shared/location.service'; +import { NavigationService, CurrentNodes, VersionInfo, NavigationViews } from './navigation/navigation.service'; +import { ScrollService } from './shared/scroll.service'; +import { SearchService } from './search/search.service'; +import { TocService, TocItem } from './shared/toc.service'; +import { MatProgressBarModule, MatToolbarModule, MatSidenavModule, MatSidenav } from '@angular/material'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { MockSearchService } from 'testing/search.service'; +import { NotificationComponent } from './layout/notification/notification.component'; +import { By } from '@angular/platform-browser'; +import { SearchResultsComponent } from './shared/search-results/search-results.component'; + +const hideToCBreakPoint = 800; + +describe('AppComponent', () => { + let component: AppComponent; + let fixture: ComponentFixture; + let searchService: SearchService; + let deployment: Deployment; + let locationService: LocationService; + let locationServiceReplaceSpy: jasmine.Spy; + let scrollService: ScrollService; + let tocService: TocService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent, + MockMatIconComponent, + MockAioNotificationComponent, + MockAioTopMenuComponent, + SearchResultsComponent, + MockAioNavMenuComponent, + MockAioSelectComponent, + MockAioModeBannerComponent, + MockAioDocViewerComponent, + MockAioDtComponent, + MockAioLazyCeComponent, + MockAioFooterComponent, + MockAioSearchBoxComponent + ], + imports: [ + MatProgressBarModule, + MatToolbarModule, + MatSidenavModule, + BrowserAnimationsModule + ], + providers: [ + { provide: Deployment, + useClass: MockDeployment + }, + { provide: DocumentService, + useClass: MockDocumentService + }, + { provide: ElementRef, + useClass: MockElementRef + }, + { + provide: LocationService, + useClass: MockLocationService, + }, + { provide: NavigationService, + useClass: MockNavigationService + }, + { provide: ScrollService, + useClass: MockScrollService + }, + { provide: SearchService, + useClass: MockSearchService + }, + { provide: TocService, + useClass: MockTocService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AppComponent); + component = fixture.componentInstance; + component.notification = { showNotification: 'show' } as NotificationComponent; + component.sidenav = { opened: true, toggle: () => {}} as MatSidenav; + spyOn(component, 'onResize').and.callThrough(); + searchService = TestBed.get(SearchService); + deployment = TestBed.get(Deployment); + locationService = TestBed.get(LocationService); + locationServiceReplaceSpy = spyOn(locationService, 'replace'); + scrollService = TestBed.get(ScrollService); + tocService = TestBed.get(TocService); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + it('should set the currentDocument from the DocumentService', () => { + expect(component.currentDocument).toEqual({ id: '1', contents: 'contents' }); + }); + + it('should size the window', () => { + expect(component.onResize).toHaveBeenCalledTimes(1); + }); + + it('should initialize the search worker', () => { + expect(searchService.initWorker).toHaveBeenCalledWith('app/search/search-worker.js', 2000); + }); + + describe('archive redirection', () => { + + it('should redirect to docs if we are in archive mode and are not hitting a docs, api, guide, or tutorial page', () => { + deployment.mode = 'archive'; + locationService.currentPath = of('events'); + component.ngOnInit(); + expect(locationService.replace).toHaveBeenCalledWith('docs'); + }); + + it('should not redirect to docs if we are hitting a docs page', () => { + deployment.mode = 'archive'; + locationService.currentPath = of('docs'); + locationServiceReplaceSpy.calls.reset(); + component.ngOnInit(); + expect(locationService.replace).not.toHaveBeenCalled(); + }); + + it('should not redirect to docs if we are hitting a api page', () => { + deployment.mode = 'archive'; + locationService.currentPath = of('api'); + locationServiceReplaceSpy.calls.reset(); + component.ngOnInit(); + expect(locationService.replace).not.toHaveBeenCalled(); + }); + + it('should not redirect to docs if we are hitting a guide page', () => { + deployment.mode = 'archive'; + locationService.currentPath = of('guide'); + locationServiceReplaceSpy.calls.reset(); + component.ngOnInit(); + expect(locationService.replace).not.toHaveBeenCalled(); + }); + + it('should not redirect to docs if we are hitting a tutorial page', () => { + deployment.mode = 'archive'; + locationService.currentPath = of('tutorial'); + locationServiceReplaceSpy.calls.reset(); + component.ngOnInit(); + expect(locationService.replace).not.toHaveBeenCalled(); + }); + }); + + it('should auto scroll if the path changes while on the current page', () => { + component.currentPath = 'docs'; + locationService.currentPath = of('docs'); + spyOn(scrollService, 'scroll'); + component.ngOnInit(); + expect(scrollService.scroll).toHaveBeenCalledTimes(1); + }); + + it('should not auto scroll if the path changes to a different page', () => { + component.currentPath = 'docs'; + locationService.currentPath = of('events'); + spyOn(scrollService, 'scroll'); + component.ngOnInit(); + expect(scrollService.scroll).not.toHaveBeenCalled(); + }); + + describe('version picker', () => { + it('should contain next and stable by default', () => { + expect(component.docVersions).toContain({ title: 'next', url: 'https://next.ngrx.io/docs' }); + expect(component.docVersions).toContain({ title: 'stable (v6.3)', url: 'https://ngrx.io/docs' }); + }); + + it('should add the current version if in archive mode', () => { + deployment.mode = 'archive'; + component.ngOnInit(); + expect(component.docVersions).toContain({ title: 'v6 (v6.3)'}); + }); + + it('should find the current version by deployment mode and append the raw version info to the title', () => { + expect(component.currentDocVersion.title).toBe('stable (v6.3)'); + }); + + it('should find the current version by the current maajor version and append the raw version info to the title', () => { + deployment.mode = 'archive'; + component.ngOnInit(); + expect(component.currentDocVersion.title).toBe('v6 (v6.3)'); + }); + }); + + describe('hasFloatingToc', () => { + it('should initially be false', () => { + const fixture2 = TestBed.createComponent(AppComponent); + const component2 = fixture2.componentInstance; + + expect(component2.hasFloatingToc).toBe(false); + }); + + it('should be false on narrow screens', () => { + component.onResize(hideToCBreakPoint - 1); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(false); + + tocService.tocList.next([]); + expect(component.hasFloatingToc).toBe(false); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(false); + }); + + it('should be true on wide screens unless the toc is empty', () => { + component.onResize(hideToCBreakPoint + 1); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(true); + + tocService.tocList.next([]); + expect(component.hasFloatingToc).toBe(false); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(true); + }); + + it('should be false when toc is empty', () => { + tocService.tocList.next([]); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(false); + + component.onResize(hideToCBreakPoint - 1); + expect(component.hasFloatingToc).toBe(false); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(false); + }); + + it('should be true when toc is not empty unless the screen is narrow', () => { + tocService.tocList.next([{}, {}, {}] as TocItem[]); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(true); + + component.onResize(hideToCBreakPoint - 1); + expect(component.hasFloatingToc).toBe(false); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(true); + }); + }); + }); + + describe('onDocVersionChange', () => { + it('should navigate to the new version url', () => { + component.docVersions = [ + { title: 'next', url: 'https://next.ngrx.io'}, + { title: 'stable (v6.3)', url: 'https://ngrx.io' } + ]; + spyOn(locationService, 'go'); + component.onDocVersionChange(1); + expect(locationService.go).toHaveBeenCalledTimes(1); + }); + it('should not navigate to new version if it does not define a Url', () => { + component.docVersions = [ + { title: 'next', url: 'https://next.ngrx.io'}, + { title: 'stable (v6.3)', url: 'https://ngrx.io' }, + { title: 'v1'} + ]; + spyOn(locationService, 'go'); + component.onDocVersionChange(2); + expect(locationService.go).not.toHaveBeenCalled(); + }); + }); + + describe('onResize', () => { + it('should set isSideBySide to true if the window width is greater than 992 pixels', () => { + component.isSideBySide = false; + component.onResize(993); + expect(component.isSideBySide).toBeTruthy(); + }); + + it('should set isSideBySide to false if the window width is less than or equal to 992 pixels', () => { + component.isSideBySide = true; + component.onResize(992); + expect(component.isSideBySide).toBeFalsy(); + }); + + it('should set hasFloatingToc to true if the window width is greater than 800 and the toc list is greater than zero', () => { + tocService.tocList.next([{}] as TocItem[]); + component.onResize(801); + expect(component.hasFloatingToc).toBeTruthy(); + }); + + it('should set hasFloatingToc to false if the window width is less than or equal to 800 and the toc list is greater than zero', () => { + tocService.tocList.next([{}] as TocItem[]); + component.onResize(800); + expect(component.hasFloatingToc).toBeFalsy(); + }); + + it('should toggle the sidenav closed if it is not a doc page and the screen is wide enough to display menu items ' + + 'in the top-bar', () => { + const sideNavToggleSpy = spyOn(component.sidenav, 'toggle'); + sideNavToggleSpy.calls.reset(); + component.updateSideNav(); + component.onResize(993); + expect(component.sidenav.toggle).toHaveBeenCalledWith(false); + }); + }); + + // describe('click handler', () => { + // it('should hide the search results if we clicked outside both the "search box" and the "search results"', () => { + // component.searchElements = new QueryList(); + // component.searchElements. + // console.log(component.searchElements.length); + // }); + // }); + + describe('search', () => { + + let docViewer: HTMLElement; + + beforeEach(() => { + const documentViewerDebugElement = fixture.debugElement.query(By.css('aio-doc-viewer')); + docViewer = documentViewerDebugElement.nativeElement; + }); + + describe('click handling', () => { + it('should intercept clicks not on the search elements and hide the search results', () => { + component.showSearchResults = true; + fixture.detectChanges(); + // docViewer is a commonly-clicked, non-search element + docViewer.click(); + expect(component.showSearchResults).toBe(false); + }); + + it('should clear "only" the search query param from the URL', () => { + // Mock out the current state of the URL query params + spyOn(locationService, 'search').and.returnValue({ + a: 'some-A', + b: 'some-B', + search: 'some-C', + }); + spyOn(locationService, 'setSearch'); + // docViewer is a commonly-clicked, non-search element + docViewer.click(); + // Check that the query params were updated correctly + expect(locationService.setSearch).toHaveBeenCalledWith('', { + a: 'some-A', + b: 'some-B', + search: undefined, + }); + }); + + it('should not intercept clicks on the searchResults', () => { + component.showSearchResults = true; + fixture.detectChanges(); + + const searchResults = fixture.debugElement.query( + By.directive(SearchResultsComponent) + ); + searchResults.nativeElement.click(); + fixture.detectChanges(); + + expect(component.showSearchResults).toBe(true); + }); + + it('should return the result of handleAnchorClick when anchor is clicked', () => { + const anchorElement: HTMLAnchorElement = document.createElement('a'); + spyOn(locationService, 'handleAnchorClick').and.returnValue(true); + expect(component.onClick(anchorElement, 1, false, false, true)).toBeTruthy(); + expect(locationService.handleAnchorClick).toHaveBeenCalledTimes(1); + }); + }); + }); + + it('updateHostClasses', () => { + component.notificationAnimating = true; + component.hostClasses = ''; + component.updateHostClasses(); + expect(component.hostClasses) + .toBe('mode-stable sidenav-open page-1 folder-1 view-view aio-notification-show aio-notification-animating'); + }); + + describe('updateSideNav', () => { + it('should preserve the current sidenav open state if view type does not change', () => { + component.isSideBySide = true; + component.sidenav.opened = true; + const toggleSpy = spyOn(component.sidenav, 'toggle'); + + component.updateSideNav(); + expect(component.sidenav.toggle).toHaveBeenCalledWith(true); + expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); + + component.isSideBySide = false; + toggleSpy.calls.reset(); + component.updateSideNav(); + expect(component.sidenav.toggle).toHaveBeenCalledWith(false); + expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); + }); + + it('should open if changed from a non sidenav doc to a sidenav doc and close if changed from sidenav doc to non sidenav doc', () => { + const toggleSpy = spyOn(component.sidenav, 'toggle'); + component.isSideBySide = true; + component.currentNodes = { + 'SideNav': { url: '', view: '', nodes: [] } + }; + component.updateSideNav(); + expect(component.sidenav.toggle).toHaveBeenCalledWith(true); + expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); + + component.currentNodes = {}; + toggleSpy.calls.reset(); + component.updateSideNav(); + expect(component.sidenav.toggle).toHaveBeenCalledWith(false); + expect(component.sidenav.toggle).toHaveBeenCalledTimes(1); + }); + }); + + describe('restrain scrolling inside an element when the cursor is over it', () => { + it('should prevent scrolling up when already at the top', () => { + const scrollUpEvent = { + deltaY: -1, + currentTarget: { scrollTop: 0 }, + preventDefault: () => {} + } as any; + spyOn(scrollUpEvent, 'preventDefault'); + component.restrainScrolling(scrollUpEvent); + expect(scrollUpEvent.preventDefault).toHaveBeenCalledTimes(1); + }); + + it('should prevent scrolling down when already at the bottom', () => { + const scrollUpEvent = { + deltaY: 1, + currentTarget: { + scrollTop: 10, + scrollHeight: 20, + clientHeight: 10 + }, + preventDefault: () => {} + } as any; + spyOn(scrollUpEvent, 'preventDefault'); + component.restrainScrolling(scrollUpEvent); + expect(scrollUpEvent.preventDefault).toHaveBeenCalledTimes(1); + }); + }); + + describe('key handling', () => { + + beforeEach(() => { + spyOn(component, 'focusSearchBox'); + spyOn(component, 'hideSearchResults'); + }); + + it('should focus search box on forward slash key "/"', () => { + component.onKeyUp('/', 190); + expect(component.focusSearchBox).toHaveBeenCalledTimes(1); + }); + + it('should focus search box on forward slash keycode', () => { + component.onKeyUp('', 191); + expect(component.focusSearchBox).toHaveBeenCalledTimes(1); + }); + + it('should hide the search results and focus search box if results are being shown on escape key', () => { + component.showSearchResults = true; + component.onKeyUp('Escape', 28); + expect(component.focusSearchBox).toHaveBeenCalledTimes(1); + expect(component.hideSearchResults).toHaveBeenCalledTimes(1); + }); + + it('should hide the search results and focus search box if results are being shown on escape keycode', () => { + component.showSearchResults = true; + component.onKeyUp('', 27); + expect(component.focusSearchBox).toHaveBeenCalledTimes(1); + expect(component.hideSearchResults).toHaveBeenCalledTimes(1); + }); + }); +}); + +// Mock Dependencies + +class MockLocationService { + currentPath = of('path'); + replace = () => {}; + go = () => {}; + handleAnchorClick = () => true; + setSearch = () => {}; + search = () => {}; +} + + +class MockDeployment { + mode = 'stable'; +} + +class MockDocumentService { + currentDocument: Observable = of({ id: '1', contents: 'contents' }); +} + +class MockElementRef { + nativeElement: Element; +} + +class MockNavigationService { + currentNodes: Observable = of({ 'view': { url: 'path', view: 'view', nodes: []}}) + versionInfo: Observable = of({ major: 6, raw: '6.3'}); + navigationViews: Observable = of({ 'docVersions' : [{ title: 'v5'}]}); +} + +export class MockTocService { + genToc = jasmine.createSpy('TocService#genToc'); + reset = jasmine.createSpy('TocService#reset'); + tocList = new ReplaySubject(1); +} + +class MockScrollService { + scroll = () => {}; + scrollToTop = () => {}; +} + +// Mock Child Components + + +@Component({ + // tslint:disable-next-line:component-selector + selector: 'mat-icon', + template: '' +}) +class MockMatIconComponent { + @Input() svgIcon; +} + +@Component({ + selector: 'aio-notification', + template: '' +}) +class MockAioNotificationComponent { + @Input() dismissOnContentClick; + showNotification = 'show' +} + +@Component({ + selector: 'aio-top-menu', + template: '' +}) +class MockAioTopMenuComponent { + @Input() nodes; +} + +@Component({ + selector: 'aio-nav-menu', + template: '' +}) +class MockAioNavMenuComponent { + @Input() nodes; + @Input() currentNode; + @Input() isWide; +} + +@Component({ + selector: 'aio-select', + template: '' +}) +class MockAioSelectComponent { + @Input() options; + @Input() selected; +} + +@Component({ + selector: 'aio-mode-banner', + template: '' +}) +class MockAioModeBannerComponent { + @Input() mode; + @Input() version; +} + +@Component({ + selector: 'aio-doc-viewer', + template: '' +}) +class MockAioDocViewerComponent { + @Input() doc; +} + +@Component({ + selector: 'aio-dt', + template: '' +}) +class MockAioDtComponent { + @Input() on; + @Input() doc; +} + +@Component({ + selector: 'aio-lazy-ce', + template: '' +}) +class MockAioLazyCeComponent { } + +@Component({ + selector: 'aio-footer', + template: '' +}) +class MockAioFooterComponent { + @Input() nodes; + @Input() versionInfo; +} + +@Component({ + selector: 'aio-search-box', + template: '' +}) +class MockAioSearchBoxComponent {} diff --git a/projects/ngrx.io/src/app/custom-elements/code/code.component.spec.ts b/projects/ngrx.io/src/app/custom-elements/code/code.component.spec.ts index badb1f16a5..c6f7cff6fb 100644 --- a/projects/ngrx.io/src/app/custom-elements/code/code.component.spec.ts +++ b/projects/ngrx.io/src/app/custom-elements/code/code.component.spec.ts @@ -1,326 +1,326 @@ -import { Component, ViewChild, AfterViewInit } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatSnackBar } from '@angular/material'; -import { By } from '@angular/platform-browser'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { first } from 'rxjs/operators'; - -import { CodeComponent } from './code.component'; -import { CodeModule } from './code.module'; -import { CopierService } from 'app/shared//copier.service'; -import { Logger } from 'app/shared/logger.service'; -import { PrettyPrinter } from './pretty-printer.service'; - -const oneLineCode = 'const foo = "bar";'; - -const smallMultiLineCode = ` -<hero-details> - <h2>Bah Dah Bing</h2> - <hero-team> - <h3>NYC Team</h3> - </hero-team> -</hero-details>`; - -const bigMultiLineCode = - smallMultiLineCode + smallMultiLineCode + smallMultiLineCode; - -describe('CodeComponent', () => { - let hostComponent: HostComponent; - let fixture: ComponentFixture; - - // WARNING: Chance of cross-test pollution - // CodeComponent injects PrettyPrintService - // Once PrettyPrintService runs once _anywhere_, its ctor loads `prettify.js` - // which sets `window['prettyPrintOne']` - // That global survives these tests unless - // we take strict measures to wipe it out in the `afterAll` - // and make sure THAT runs after the tests by making component creation async - afterAll(() => { - delete (window as any)['prettyPrint']; - delete (window as any)['prettyPrintOne']; - }); - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, CodeModule], - declarations: [HostComponent], - providers: [ - PrettyPrinter, - CopierService, - { provide: Logger, useClass: TestLogger }, - ], - }).compileComponents(); - }); - - // Must be async because - // CodeComponent creates PrettyPrintService which async loads `prettify.js`. - // If not async, `afterAll` finishes before tests do! - beforeEach(async(() => { - fixture = TestBed.createComponent(HostComponent); - hostComponent = fixture.componentInstance; - - fixture.detectChanges(); - })); - - describe('pretty printing', () => { - const untilCodeFormatted = () => { - const emitter = hostComponent.codeComponent.codeFormatted; - return emitter.pipe(first()).toPromise(); - }; - const hasLineNumbers = async () => { - // presence of `
  • `s are a tell-tale for line numbers - await untilCodeFormatted(); - return 0 < fixture.nativeElement.querySelectorAll('li').length; - }; - - it('should format a one-line code sample', async () => { - hostComponent.setCode(oneLineCode); - await untilCodeFormatted(); - - // 'pln' spans are a tell-tale for syntax highlighting - const spans = fixture.nativeElement.querySelectorAll('span.pln'); - expect(spans.length).toBeGreaterThan(0, 'formatted spans'); - }); - - it('should format a one-line code sample without linenums by default', async () => { - hostComponent.setCode(oneLineCode); - expect(await hasLineNumbers()).toBe(false); - }); - - it('should add line numbers to one-line code sample when linenums set true', async () => { - hostComponent.linenums = 'true'; - fixture.detectChanges(); - - expect(await hasLineNumbers()).toBe(true); - }); - - it('should format a small multi-line code without linenums by default', async () => { - hostComponent.setCode(smallMultiLineCode); - expect(await hasLineNumbers()).toBe(false); - }); - - it('should add line numbers to a big multi-line code by default', async () => { - hostComponent.setCode(bigMultiLineCode); - expect(await hasLineNumbers()).toBe(true); - }); - - it('should format big multi-line code without linenums when linenums set false', async () => { - hostComponent.linenums = false; - fixture.detectChanges(); - - hostComponent.setCode(bigMultiLineCode); - expect(await hasLineNumbers()).toBe(false); - }); - }); - - describe('whitespace handling', () => { - it('should remove common indentation from the code before rendering', () => { - hostComponent.linenums = false; - fixture.detectChanges(); - - hostComponent.setCode( - " abc\n let x = text.split('\\n');\n ghi\n\n jkl\n" - ); - const codeContent = fixture.nativeElement.querySelector('code') - .textContent; - expect(codeContent).toEqual( - "abc\n let x = text.split('\\n');\nghi\n\njkl" - ); - }); - - it('should trim whitespace from the code before rendering', () => { - hostComponent.linenums = false; - fixture.detectChanges(); - - hostComponent.setCode('\n\n\n' + smallMultiLineCode + '\n\n\n'); - const codeContent = fixture.nativeElement.querySelector('code') - .textContent; - expect(codeContent).toEqual(codeContent.trim()); - }); - - it('should trim whitespace from code before computing whether to format linenums', () => { - hostComponent.setCode('\n\n\n' + oneLineCode + '\n\n\n'); - - // `
  • `s are a tell-tale for line numbers - const lis = fixture.nativeElement.querySelectorAll('li'); - expect(lis.length).toBe(0, 'should be no linenums'); - }); - }); - - describe('error message', () => { - function getErrorMessage() { - const missing: HTMLElement = fixture.nativeElement.querySelector( - '.code-missing' - ); - return missing ? missing.textContent : null; - } - - it('should not display "code-missing" class when there is some code', () => { - expect(getErrorMessage()).toBeNull( - 'should not have element with "code-missing" class' - ); - }); - - it('should display error message when there is no code (after trimming)', () => { - hostComponent.setCode(' \n '); - expect(getErrorMessage()).toContain('missing'); - }); - - it('should show path and region in missing-code error message', () => { - hostComponent.path = 'fizz/buzz/foo.html'; - hostComponent.region = 'something'; - fixture.detectChanges(); - - hostComponent.setCode(' \n '); - expect(getErrorMessage()).toMatch( - /for[\s\S]fizz\/buzz\/foo\.html#something$/ - ); - }); - - it('should show path only in missing-code error message when no region', () => { - hostComponent.path = 'fizz/buzz/foo.html'; - fixture.detectChanges(); - - hostComponent.setCode(' \n '); - expect(getErrorMessage()).toMatch(/for[\s\S]fizz\/buzz\/foo\.html$/); - }); - - it('should show simple missing-code error message when no path/region', () => { - hostComponent.setCode(' \n '); - expect(getErrorMessage()).toMatch(/missing.$/); - }); - }); - - describe('copy button', () => { - function getButton() { - const btnDe = fixture.debugElement.query(By.css('button')); - return btnDe ? btnDe.nativeElement : null; - } - - it('should be hidden if the `hideCopy` input is true', () => { - hostComponent.hideCopy = true; - fixture.detectChanges(); - expect(getButton()).toBe(null); - }); - - it('should have title', () => { - expect(getButton().title).toBe('Copy code snippet'); - }); - - it('should have no aria-label by default', () => { - expect(getButton().getAttribute('aria-label')).toBe(''); - }); - - it('should have aria-label explaining what is being copied when header passed in', () => { - hostComponent.header = 'a/b/c/foo.ts'; - fixture.detectChanges(); - expect(getButton().getAttribute('aria-label')).toContain( - hostComponent.header - ); - }); - - it('should call copier service when clicked', () => { - const copierService: CopierService = TestBed.get(CopierService); - const spy = spyOn(copierService, 'copyText'); - expect(spy.calls.count()).toBe(0, 'before click'); - getButton().click(); - expect(spy.calls.count()).toBe(1, 'after click'); - }); - - it('should copy code text when clicked', () => { - const copierService: CopierService = TestBed.get(CopierService); - const spy = spyOn(copierService, 'copyText'); - getButton().click(); - expect(spy.calls.argsFor(0)[0]).toBe(oneLineCode, 'after click'); - }); - - it('should preserve newlines in the copied code', () => { - const copierService: CopierService = TestBed.get(CopierService); - const spy = spyOn(copierService, 'copyText'); - const expectedCode = smallMultiLineCode - .trim() - .replace(/</g, '<') - .replace(/>/g, '>'); - let actualCode; - - hostComponent.setCode(smallMultiLineCode); - - [false, true, 42].forEach(linenums => { - hostComponent.linenums = linenums; - fixture.detectChanges(); - getButton().click(); - actualCode = spy.calls.mostRecent().args[0]; - - expect(actualCode).toBe(expectedCode, `when linenums=${linenums}`); - expect(actualCode.match(/\r?\n/g).length).toBe(5); - - spy.calls.reset(); - }); - }); - - it('should display a message when copy succeeds', () => { - const snackBar: MatSnackBar = TestBed.get(MatSnackBar); - const copierService: CopierService = TestBed.get(CopierService); - spyOn(snackBar, 'open'); - spyOn(copierService, 'copyText').and.returnValue(true); - getButton().click(); - expect(snackBar.open).toHaveBeenCalledWith('Code Copied', '', { - duration: 800, - }); - }); - - it('should display an error when copy fails', () => { - const snackBar: MatSnackBar = TestBed.get(MatSnackBar); - const copierService: CopierService = TestBed.get(CopierService); - const logger: TestLogger = TestBed.get(Logger); - spyOn(snackBar, 'open'); - spyOn(copierService, 'copyText').and.returnValue(false); - getButton().click(); - expect(snackBar.open).toHaveBeenCalledWith( - 'Copy failed. Please try again!', - '', - { duration: 800 } - ); - expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith(jasmine.any(Error)); - expect(logger.error.calls.mostRecent().args[0].message).toMatch( - /^ERROR copying code to clipboard:/ - ); - }); - }); -}); - -//// Test helpers //// -// tslint:disable:member-ordering -@Component({ - selector: 'aio-host-comp', - template: ` - - `, -}) -class HostComponent implements AfterViewInit { - hideCopy: boolean; - language: string; - linenums: boolean | number | string; - path: string; - region: string; - header: string; - - @ViewChild(CodeComponent) codeComponent: CodeComponent; - - ngAfterViewInit() { - this.setCode(oneLineCode); - } - - /** Changes the displayed code on the code component. */ - setCode(code: string) { - this.codeComponent.code = code; - } -} - -class TestLogger { - log = jasmine.createSpy('log'); - error = jasmine.createSpy('error'); -} +import { Component, ViewChild, AfterViewInit } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatSnackBar } from '@angular/material'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { first } from 'rxjs/operators'; + +import { CodeComponent } from './code.component'; +import { CodeModule } from './code.module'; +import { CopierService } from 'app/shared//copier.service'; +import { Logger } from 'app/shared/logger.service'; +import { PrettyPrinter } from './pretty-printer.service'; + +const oneLineCode = 'const foo = "bar";'; + +const smallMultiLineCode = ` +<hero-details> + <h2>Bah Dah Bing</h2> + <hero-team> + <h3>NYC Team</h3> + </hero-team> +</hero-details>`; + +const bigMultiLineCode = + smallMultiLineCode + smallMultiLineCode + smallMultiLineCode; + +describe('CodeComponent', () => { + let hostComponent: HostComponent; + let fixture: ComponentFixture; + + // WARNING: Chance of cross-test pollution + // CodeComponent injects PrettyPrintService + // Once PrettyPrintService runs once _anywhere_, its ctor loads `prettify.js` + // which sets `window['prettyPrintOne']` + // That global survives these tests unless + // we take strict measures to wipe it out in the `afterAll` + // and make sure THAT runs after the tests by making component creation async + afterAll(() => { + delete (window as any)['prettyPrint']; + delete (window as any)['prettyPrintOne']; + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, CodeModule], + declarations: [HostComponent], + providers: [ + PrettyPrinter, + CopierService, + { provide: Logger, useClass: TestLogger }, + ], + }).compileComponents(); + }); + + // Must be async because + // CodeComponent creates PrettyPrintService which async loads `prettify.js`. + // If not async, `afterAll` finishes before tests do! + beforeEach(async(() => { + fixture = TestBed.createComponent(HostComponent); + hostComponent = fixture.componentInstance; + + fixture.detectChanges(); + })); + + describe('pretty printing', () => { + const untilCodeFormatted = () => { + const emitter = hostComponent.codeComponent.codeFormatted; + return emitter.pipe(first()).toPromise(); + }; + const hasLineNumbers = async () => { + // presence of `
  • `s are a tell-tale for line numbers + await untilCodeFormatted(); + return 0 < fixture.nativeElement.querySelectorAll('li').length; + }; + + it('should format a one-line code sample', async () => { + hostComponent.setCode(oneLineCode); + await untilCodeFormatted(); + + // 'pln' spans are a tell-tale for syntax highlighting + const spans = fixture.nativeElement.querySelectorAll('span.pln'); + expect(spans.length).toBeGreaterThan(0, 'formatted spans'); + }); + + it('should format a one-line code sample without linenums by default', async () => { + hostComponent.setCode(oneLineCode); + expect(await hasLineNumbers()).toBe(false); + }); + + it('should add line numbers to one-line code sample when linenums set true', async () => { + hostComponent.linenums = 'true'; + fixture.detectChanges(); + + expect(await hasLineNumbers()).toBe(true); + }); + + it('should format a small multi-line code without linenums by default', async () => { + hostComponent.setCode(smallMultiLineCode); + expect(await hasLineNumbers()).toBe(false); + }); + + it('should add line numbers to a big multi-line code by default', async () => { + hostComponent.setCode(bigMultiLineCode); + expect(await hasLineNumbers()).toBe(true); + }); + + it('should format big multi-line code without linenums when linenums set false', async () => { + hostComponent.linenums = false; + fixture.detectChanges(); + + hostComponent.setCode(bigMultiLineCode); + expect(await hasLineNumbers()).toBe(false); + }); + }); + + describe('whitespace handling', () => { + it('should remove common indentation from the code before rendering', () => { + hostComponent.linenums = false; + fixture.detectChanges(); + + hostComponent.setCode( + " abc\n let x = text.split('\\n');\n ghi\n\n jkl\n" + ); + const codeContent = fixture.nativeElement.querySelector('code') + .textContent; + expect(codeContent).toEqual( + "abc\n let x = text.split('\\n');\nghi\n\njkl" + ); + }); + + it('should trim whitespace from the code before rendering', () => { + hostComponent.linenums = false; + fixture.detectChanges(); + + hostComponent.setCode('\n\n\n' + smallMultiLineCode + '\n\n\n'); + const codeContent = fixture.nativeElement.querySelector('code') + .textContent; + expect(codeContent).toEqual(codeContent.trim()); + }); + + it('should trim whitespace from code before computing whether to format linenums', () => { + hostComponent.setCode('\n\n\n' + oneLineCode + '\n\n\n'); + + // `
  • `s are a tell-tale for line numbers + const lis = fixture.nativeElement.querySelectorAll('li'); + expect(lis.length).toBe(0, 'should be no linenums'); + }); + }); + + describe('error message', () => { + function getErrorMessage() { + const missing: HTMLElement = fixture.nativeElement.querySelector( + '.code-missing' + ); + return missing ? missing.textContent : null; + } + + it('should not display "code-missing" class when there is some code', () => { + expect(getErrorMessage()).toBeNull( + 'should not have element with "code-missing" class' + ); + }); + + it('should display error message when there is no code (after trimming)', () => { + hostComponent.setCode(' \n '); + expect(getErrorMessage()).toContain('missing'); + }); + + it('should show path and region in missing-code error message', () => { + hostComponent.path = 'fizz/buzz/foo.html'; + hostComponent.region = 'something'; + fixture.detectChanges(); + + hostComponent.setCode(' \n '); + expect(getErrorMessage()).toMatch( + /for[\s\S]fizz\/buzz\/foo\.html#something$/ + ); + }); + + it('should show path only in missing-code error message when no region', () => { + hostComponent.path = 'fizz/buzz/foo.html'; + fixture.detectChanges(); + + hostComponent.setCode(' \n '); + expect(getErrorMessage()).toMatch(/for[\s\S]fizz\/buzz\/foo\.html$/); + }); + + it('should show simple missing-code error message when no path/region', () => { + hostComponent.setCode(' \n '); + expect(getErrorMessage()).toMatch(/missing.$/); + }); + }); + + describe('copy button', () => { + function getButton() { + const btnDe = fixture.debugElement.query(By.css('button')); + return btnDe ? btnDe.nativeElement : null; + } + + it('should be hidden if the `hideCopy` input is true', () => { + hostComponent.hideCopy = true; + fixture.detectChanges(); + expect(getButton()).toBe(null); + }); + + it('should have title', () => { + expect(getButton().title).toBe('Copy code snippet'); + }); + + it('should have no aria-label by default', () => { + expect(getButton().getAttribute('aria-label')).toBe(''); + }); + + it('should have aria-label explaining what is being copied when header passed in', () => { + hostComponent.header = 'a/b/c/foo.ts'; + fixture.detectChanges(); + expect(getButton().getAttribute('aria-label')).toContain( + hostComponent.header + ); + }); + + it('should call copier service when clicked', () => { + const copierService: CopierService = TestBed.get(CopierService); + const spy = spyOn(copierService, 'copyText'); + expect(spy.calls.count()).toBe(0, 'before click'); + getButton().click(); + expect(spy.calls.count()).toBe(1, 'after click'); + }); + + it('should copy code text when clicked', () => { + const copierService: CopierService = TestBed.get(CopierService); + const spy = spyOn(copierService, 'copyText'); + getButton().click(); + expect(spy.calls.argsFor(0)[0]).toBe(oneLineCode, 'after click'); + }); + + it('should preserve newlines in the copied code', () => { + const copierService: CopierService = TestBed.get(CopierService); + const spy = spyOn(copierService, 'copyText'); + const expectedCode = smallMultiLineCode + .trim() + .replace(/</g, '<') + .replace(/>/g, '>'); + let actualCode; + + hostComponent.setCode(smallMultiLineCode); + + [false, true, 42].forEach(linenums => { + hostComponent.linenums = linenums; + fixture.detectChanges(); + getButton().click(); + actualCode = spy.calls.mostRecent().args[0]; + + expect(actualCode).toBe(expectedCode, `when linenums=${linenums}`); + expect(actualCode.match(/\r?\n/g).length).toBe(5); + + spy.calls.reset(); + }); + }); + + it('should display a message when copy succeeds', () => { + const snackBar: MatSnackBar = TestBed.get(MatSnackBar); + const copierService: CopierService = TestBed.get(CopierService); + spyOn(snackBar, 'open'); + spyOn(copierService, 'copyText').and.returnValue(true); + getButton().click(); + expect(snackBar.open).toHaveBeenCalledWith('Code Copied', '', { + duration: 800, + }); + }); + + it('should display an error when copy fails', () => { + const snackBar: MatSnackBar = TestBed.get(MatSnackBar); + const copierService: CopierService = TestBed.get(CopierService); + const logger: TestLogger = TestBed.get(Logger); + spyOn(snackBar, 'open'); + spyOn(copierService, 'copyText').and.returnValue(false); + getButton().click(); + expect(snackBar.open).toHaveBeenCalledWith( + 'Copy failed. Please try again!', + '', + { duration: 800 } + ); + expect(logger.error).toHaveBeenCalledTimes(1); + expect(logger.error).toHaveBeenCalledWith(jasmine.any(Error)); + expect(logger.error.calls.mostRecent().args[0].message).toMatch( + /^ERROR copying code to clipboard:/ + ); + }); + }); +}); + +//// Test helpers //// +// tslint:disable:member-ordering +@Component({ + selector: 'aio-host-comp', + template: ` + + `, +}) +class HostComponent implements AfterViewInit { + hideCopy: boolean; + language: string; + linenums: boolean | number | string; + path: string; + region: string; + header: string; + + @ViewChild(CodeComponent) codeComponent: CodeComponent; + + ngAfterViewInit() { + this.setCode(oneLineCode); + } + + /** Changes the displayed code on the code component. */ + setCode(code: string) { + this.codeComponent.code = code; + } +} + +class TestLogger { + log = jasmine.createSpy('log'); + error = jasmine.createSpy('error'); +} diff --git a/projects/ngrx.io/src/app/custom-elements/search/file-not-found-search.component.spec.ts b/projects/ngrx.io/src/app/custom-elements/search/file-not-found-search.component.spec.ts index 3cf746e7a9..b989ca393a 100644 --- a/projects/ngrx.io/src/app/custom-elements/search/file-not-found-search.component.spec.ts +++ b/projects/ngrx.io/src/app/custom-elements/search/file-not-found-search.component.spec.ts @@ -1,47 +1,47 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { Subject } from 'rxjs'; -import { LocationService } from 'app/shared/location.service'; -import { MockLocationService } from 'testing/location.service'; -import { SearchResults } from 'app/search/interfaces'; -import { SearchResultsComponent } from 'app/shared/search-results/search-results.component'; -import { SearchService } from 'app/search/search.service'; -import { FileNotFoundSearchComponent } from './file-not-found-search.component'; - - -describe('FileNotFoundSearchComponent', () => { - let fixture: ComponentFixture; - let searchService: SearchService; - let searchResultSubject: Subject; - - beforeEach(() => { - - TestBed.configureTestingModule({ - declarations: [ FileNotFoundSearchComponent, SearchResultsComponent ], - providers: [ - { provide: LocationService, useValue: new MockLocationService('base/initial-url?some-query') }, - SearchService - ] - }); - - fixture = TestBed.createComponent(FileNotFoundSearchComponent); - searchService = TestBed.get(SearchService); - searchResultSubject = new Subject(); - spyOn(searchService, 'search').and.callFake(() => searchResultSubject.asObservable()); - fixture.detectChanges(); - }); - - it('should run a search with a query built from the current url', () => { - expect(searchService.search).toHaveBeenCalledWith('base initial url'); - }); - - it('should pass through any results to the `aio-search-results` component', () => { - const searchResultsComponent = fixture.debugElement.query(By.directive(SearchResultsComponent)).componentInstance; - expect(searchResultsComponent.searchResults).toBe(null); - - const results = { query: 'base initial url', results: []}; - searchResultSubject.next(results); - fixture.detectChanges(); - expect(searchResultsComponent.searchResults).toEqual(results); - }); -}); +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { Subject } from 'rxjs'; +import { LocationService } from 'app/shared/location.service'; +import { MockLocationService } from 'testing/location.service'; +import { SearchResults } from 'app/search/interfaces'; +import { SearchResultsComponent } from 'app/shared/search-results/search-results.component'; +import { SearchService } from 'app/search/search.service'; +import { FileNotFoundSearchComponent } from './file-not-found-search.component'; + + +describe('FileNotFoundSearchComponent', () => { + let fixture: ComponentFixture; + let searchService: SearchService; + let searchResultSubject: Subject; + + beforeEach(() => { + + TestBed.configureTestingModule({ + declarations: [ FileNotFoundSearchComponent, SearchResultsComponent ], + providers: [ + { provide: LocationService, useValue: new MockLocationService('base/initial-url?some-query') }, + SearchService + ] + }); + + fixture = TestBed.createComponent(FileNotFoundSearchComponent); + searchService = TestBed.get(SearchService); + searchResultSubject = new Subject(); + spyOn(searchService, 'search').and.callFake(() => searchResultSubject.asObservable()); + fixture.detectChanges(); + }); + + it('should run a search with a query built from the current url', () => { + expect(searchService.search).toHaveBeenCalledWith('base initial url'); + }); + + it('should pass through any results to the `aio-search-results` component', () => { + const searchResultsComponent = fixture.debugElement.query(By.directive(SearchResultsComponent)).componentInstance; + expect(searchResultsComponent.searchResults).toBe(null); + + const results = { query: 'base initial url', results: []}; + searchResultSubject.next(results); + fixture.detectChanges(); + expect(searchResultsComponent.searchResults).toEqual(results); + }); +}); diff --git a/projects/ngrx.io/src/app/custom-elements/toc/toc.component.spec.ts b/projects/ngrx.io/src/app/custom-elements/toc/toc.component.spec.ts index b3c885296c..56b6b48e89 100644 --- a/projects/ngrx.io/src/app/custom-elements/toc/toc.component.spec.ts +++ b/projects/ngrx.io/src/app/custom-elements/toc/toc.component.spec.ts @@ -1,524 +1,524 @@ -import { Component, CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { asapScheduler as asap, BehaviorSubject } from 'rxjs'; - -import { ScrollService } from 'app/shared/scroll.service'; -import { TocItem, TocService } from 'app/shared/toc.service'; -import { TocComponent } from './toc.component'; - -describe('TocComponent', () => { - let tocComponentDe: DebugElement; - let tocComponent: TocComponent; - let tocService: TestTocService; - - let page: { - listItems: DebugElement[]; - tocHeading: DebugElement; - tocHeadingButtonEmbedded: DebugElement; - tocH1Heading: DebugElement; - tocMoreButton: DebugElement; - }; - - function setPage(): typeof page { - return { - listItems: tocComponentDe.queryAll(By.css('ul.toc-list>li')), - tocHeading: tocComponentDe.query(By.css('.toc-heading')), - tocHeadingButtonEmbedded: tocComponentDe.query( - By.css('button.toc-heading.embedded') - ), - tocH1Heading: tocComponentDe.query(By.css('.h1')), - tocMoreButton: tocComponentDe.query(By.css('button.toc-more-items')), - }; - } - - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [ - HostEmbeddedTocComponent, - HostNotEmbeddedTocComponent, - TocComponent, - ], - providers: [ - { provide: ScrollService, useClass: TestScrollService }, - { provide: TocService, useClass: TestTocService }, - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA], - }); - }); - - describe('when embedded in doc body', () => { - let fixture: ComponentFixture; - - beforeEach(() => { - fixture = TestBed.createComponent(HostEmbeddedTocComponent); - tocComponentDe = fixture.debugElement.children[0]; - tocComponent = tocComponentDe.componentInstance; - tocService = TestBed.get(TocService); - }); - - it('should create tocComponent', () => { - expect(tocComponent).toBeTruthy(); - }); - - it('should be in embedded state', () => { - expect(tocComponent.isEmbedded).toEqual(true); - }); - - it('should not display a ToC initially', () => { - expect(tocComponent.type).toEqual('None'); - }); - - it('should not display anything when no h2 or h3 TocItems', () => { - tocService.tocList.next([tocItem('H1', 'h1')]); - fixture.detectChanges(); - expect(tocComponentDe.children.length).toEqual(0); - }); - - it('should update when the TocItems are updated', () => { - tocService.tocList.next([tocItem('Heading A')]); - fixture.detectChanges(); - expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1); - - tocService.tocList.next([ - tocItem('Heading A'), - tocItem('Heading B'), - tocItem('Heading C'), - ]); - fixture.detectChanges(); - expect(tocComponentDe.queryAll(By.css('li')).length).toBe(3); - }); - - it('should only display H2 and H3 TocItems', () => { - tocService.tocList.next([ - tocItem('Heading A', 'h1'), - tocItem('Heading B'), - tocItem('Heading C', 'h3'), - ]); - fixture.detectChanges(); - - const tocItems = tocComponentDe.queryAll(By.css('li')); - const textContents = tocItems.map(item => - item.nativeNode.textContent.trim() - ); - - expect(tocItems.length).toBe(2); - expect(textContents.find(text => text === 'Heading A')).toBeFalsy(); - expect(textContents.find(text => text === 'Heading B')).toBeTruthy(); - expect(textContents.find(text => text === 'Heading C')).toBeTruthy(); - expect(setPage().tocH1Heading).toBeFalsy(); - }); - - it('should stop listening for TocItems once destroyed', () => { - tocService.tocList.next([tocItem('Heading A')]); - fixture.detectChanges(); - expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1); - - tocComponent.ngOnDestroy(); - tocService.tocList.next([ - tocItem('Heading A', 'h1'), - tocItem('Heading B'), - tocItem('Heading C'), - ]); - fixture.detectChanges(); - expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1); - }); - - describe('when fewer than `maxPrimary` TocItems', () => { - beforeEach(() => { - tocService.tocList.next([ - tocItem('Heading A'), - tocItem('Heading B'), - tocItem('Heading C'), - tocItem('Heading D'), - ]); - fixture.detectChanges(); - page = setPage(); - }); - - it('should have four displayed items', () => { - expect(page.listItems.length).toEqual(4); - }); - - it('should not have secondary items', () => { - expect(tocComponent.type).toEqual('EmbeddedSimple'); - const aSecond = page.listItems.find(item => item.classes.secondary); - expect(aSecond).toBeFalsy('should not find a secondary'); - }); - - it('should not display expando buttons', () => { - expect(page.tocHeadingButtonEmbedded).toBeFalsy( - 'top expand/collapse button' - ); - expect(page.tocMoreButton).toBeFalsy('bottom more button'); - }); - }); - - describe('when many TocItems', () => { - let scrollToTopSpy: jasmine.Spy; - - beforeEach(() => { - fixture.detectChanges(); - page = setPage(); - scrollToTopSpy = TestBed.get(ScrollService).scrollToTop; - }); - - it('should have more than 4 displayed items', () => { - expect(page.listItems.length).toBeGreaterThan(4); - }); - - it('should not display the h1 item', () => { - expect(page.listItems.find(item => item.classes.h1)).toBeFalsy( - 'should not find h1 item' - ); - }); - - it('should be in "collapsed" (not expanded) state at the start', () => { - expect(tocComponent.isCollapsed).toBeTruthy(); - }); - - it('should have "collapsed" class at the start', () => { - expect(tocComponentDe.children[0].classes.collapsed).toEqual(true); - }); - - it('should display expando buttons', () => { - expect(page.tocHeadingButtonEmbedded).toBeTruthy( - 'top expand/collapse button' - ); - expect(page.tocMoreButton).toBeTruthy('bottom more button'); - }); - - it('should have secondary items', () => { - expect(tocComponent.type).toEqual('EmbeddedExpandable'); - }); - - // CSS will hide items with the secondary class when collapsed - it('should have secondary item with a secondary class', () => { - const aSecondary = page.listItems.find(item => item.classes.secondary); - expect(aSecondary).toBeTruthy('should find a secondary'); - }); - - describe('after click tocHeading button', () => { - beforeEach(() => { - page.tocHeadingButtonEmbedded.nativeElement.click(); - fixture.detectChanges(); - }); - - it('should not be "collapsed"', () => { - expect(tocComponent.isCollapsed).toEqual(false); - }); - - it('should not have "collapsed" class', () => { - expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy(); - }); - - it('should not scroll', () => { - expect(scrollToTopSpy).not.toHaveBeenCalled(); - }); - - it('should be "collapsed" after clicking again', () => { - page.tocHeadingButtonEmbedded.nativeElement.click(); - fixture.detectChanges(); - expect(tocComponent.isCollapsed).toEqual(true); - }); - - it('should not scroll after clicking again', () => { - page.tocHeadingButtonEmbedded.nativeElement.click(); - fixture.detectChanges(); - expect(scrollToTopSpy).not.toHaveBeenCalled(); - }); - }); - - describe('after click tocMore button', () => { - beforeEach(() => { - page.tocMoreButton.nativeElement.click(); - fixture.detectChanges(); - }); - - it('should not be "collapsed"', () => { - expect(tocComponent.isCollapsed).toEqual(false); - }); - - it('should not have "collapsed" class', () => { - expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy(); - }); - - it('should not scroll', () => { - expect(scrollToTopSpy).not.toHaveBeenCalled(); - }); - - it('should be "collapsed" after clicking again', () => { - page.tocMoreButton.nativeElement.click(); - fixture.detectChanges(); - expect(tocComponent.isCollapsed).toEqual(true); - }); - - it('should be "collapsed" after clicking tocHeadingButton', () => { - page.tocMoreButton.nativeElement.click(); - fixture.detectChanges(); - expect(tocComponent.isCollapsed).toEqual(true); - }); - - it('should scroll after clicking again', () => { - page.tocMoreButton.nativeElement.click(); - fixture.detectChanges(); - expect(scrollToTopSpy).toHaveBeenCalled(); - }); - }); - }); - }); - - describe('when in side panel (not embedded)', () => { - let fixture: ComponentFixture; - - beforeEach(() => { - fixture = TestBed.createComponent(HostNotEmbeddedTocComponent); - - tocComponentDe = fixture.debugElement.children[0]; - tocComponent = tocComponentDe.componentInstance; - tocService = TestBed.get(TocService); - - fixture.detectChanges(); - page = setPage(); - }); - - it('should not be in embedded state', () => { - expect(tocComponent.isEmbedded).toEqual(false); - expect(tocComponent.type).toEqual('Floating'); - }); - - it('should display all items (including h1s)', () => { - expect(page.listItems.length).toEqual(getTestTocList().length); - }); - - it('should not have secondary items', () => { - expect(tocComponent.type).toEqual('Floating'); - const aSecond = page.listItems.find(item => item.classes.secondary); - expect(aSecond).toBeFalsy('should not find a secondary'); - }); - - it('should not display expando buttons', () => { - expect(page.tocHeadingButtonEmbedded).toBeFalsy( - 'top expand/collapse button' - ); - expect(page.tocMoreButton).toBeFalsy('bottom more button'); - }); - - it('should display H1 title', () => { - expect(page.tocH1Heading).toBeTruthy(); - }); - - describe('#activeIndex', () => { - it("should keep track of `TocService`'s `activeItemIndex`", () => { - expect(tocComponent.activeIndex).toBeNull(); - - tocService.setActiveIndex(42); - expect(tocComponent.activeIndex).toBe(42); - - tocService.setActiveIndex(null); - expect(tocComponent.activeIndex).toBeNull(); - }); - - it('should stop tracking `activeItemIndex` once destroyed', () => { - tocService.setActiveIndex(42); - expect(tocComponent.activeIndex).toBe(42); - - tocComponent.ngOnDestroy(); - - tocService.setActiveIndex(43); - expect(tocComponent.activeIndex).toBe(42); - - tocService.setActiveIndex(null); - expect(tocComponent.activeIndex).toBe(42); - }); - - it('should set the `active` class to the active anchor (and only that)', () => { - expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); - - tocComponent.activeIndex = 1; - fixture.detectChanges(); - expect(page.listItems.filter(By.css('.active')).length).toBe(1); - expect(page.listItems.findIndex(By.css('.active'))).toBe(1); - - tocComponent.activeIndex = null; - fixture.detectChanges(); - expect(page.listItems.filter(By.css('.active')).length).toBe(0); - expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); - - tocComponent.activeIndex = 0; - fixture.detectChanges(); - expect(page.listItems.filter(By.css('.active')).length).toBe(1); - expect(page.listItems.findIndex(By.css('.active'))).toBe(0); - - tocComponent.activeIndex = 1337; - fixture.detectChanges(); - expect(page.listItems.filter(By.css('.active')).length).toBe(0); - expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); - - tocComponent.activeIndex = page.listItems.length - 1; - fixture.detectChanges(); - expect(page.listItems.filter(By.css('.active')).length).toBe(1); - expect(page.listItems.findIndex(By.css('.active'))).toBe( - page.listItems.length - 1 - ); - }); - - it('should re-apply the `active` class when the list elements change', () => { - const getActiveTextContent = () => - page.listItems - .find(By.css('.active'))! - .nativeElement.textContent.trim(); - - tocComponent.activeIndex = 1; - fixture.detectChanges(); - expect(getActiveTextContent()).toBe('Heading one'); - - tocComponent.tocList = [tocItem('New 1'), tocItem('New 2')]; - fixture.detectChanges(); - page = setPage(); - expect(getActiveTextContent()).toBe('New 2'); - - tocComponent.tocList.unshift(tocItem('New 0')); - fixture.detectChanges(); - page = setPage(); - expect(getActiveTextContent()).toBe('New 1'); - - tocComponent.tocList = [tocItem('Very New 1')]; - fixture.detectChanges(); - page = setPage(); - expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); - - tocComponent.activeIndex = 0; - fixture.detectChanges(); - expect(getActiveTextContent()).toBe('Very New 1'); - }); - - describe('should scroll the active ToC item into viewport (if not already visible)', () => { - let parentScrollTop: number; - - beforeEach(() => { - const hostElem = fixture.nativeElement; - const firstItem = page.listItems[0].nativeElement; - - Object.assign(hostElem.style, { - display: 'block', - maxHeight: `${hostElem.clientHeight - firstItem.clientHeight}px`, - overflow: 'auto', - position: 'relative', - }); - Object.defineProperty(hostElem, 'scrollTop', { - get: () => parentScrollTop, - set: v => (parentScrollTop = v), - }); - - parentScrollTop = 0; - }); - - it('when the `activeIndex` changes', () => { - tocService.setActiveIndex(0); - fixture.detectChanges(); - - expect(parentScrollTop).toBe(0); - - tocService.setActiveIndex(1); - fixture.detectChanges(); - - expect(parentScrollTop).toBe(0); - - tocService.setActiveIndex(page.listItems.length - 1); - fixture.detectChanges(); - - expect(parentScrollTop).toBeGreaterThan(0); - }); - - it('when the `tocList` changes', () => { - const tocList = tocComponent.tocList; - - tocComponent.tocList = []; - fixture.detectChanges(); - - expect(parentScrollTop).toBe(0); - - tocService.setActiveIndex(tocList.length - 1); - fixture.detectChanges(); - - expect(parentScrollTop).toBe(0); - - tocComponent.tocList = tocList; - fixture.detectChanges(); - - expect(parentScrollTop).toBeGreaterThan(0); - }); - - it('not after it has been destroyed', () => { - const tocList = tocComponent.tocList; - tocComponent.ngOnDestroy(); - - tocService.setActiveIndex(page.listItems.length - 1); - fixture.detectChanges(); - - expect(parentScrollTop).toBe(0); - - tocComponent.tocList = []; - fixture.detectChanges(); - - expect(parentScrollTop).toBe(0); - - tocComponent.tocList = tocList; - fixture.detectChanges(); - - expect(parentScrollTop).toBe(0); - }); - }); - }); - }); -}); - -//// helpers //// -@Component({ - selector: 'aio-embedded-host', - template: '', -}) -class HostEmbeddedTocComponent {} - -@Component({ - selector: 'aio-not-embedded-host', - template: '', -}) -class HostNotEmbeddedTocComponent {} - -class TestScrollService { - scrollToTop = jasmine.createSpy('scrollToTop'); -} - -class TestTocService { - tocList = new BehaviorSubject(getTestTocList()); - activeItemIndex = new BehaviorSubject(null); - setActiveIndex(index: number | null) { - this.activeItemIndex.next(index); - if (asap.scheduled !== undefined) { - asap.flush(); - } - } -} - -function tocItem(title: string, level = 'h2', href = '', content = title) { - return { title, href, level, content }; -} - -function getTestTocList() { - return [ - tocItem('Title', 'h1', 'fizz/buzz#title', 'Title'), - tocItem( - 'Heading one', - 'h2', - 'fizz/buzz#heading-one-special-id', - 'Heading one' - ), - tocItem('H2 Two', 'h2', 'fizz/buzz#h2-two', 'H2 Two'), - tocItem('H2 Three', 'h2', 'fizz/buzz#h2-three', 'H2 Three'), - tocItem('H3 3a', 'h3', 'fizz/buzz#h3-3a', 'H3 3a'), - tocItem('H3 3b', 'h3', 'fizz/buzz#h3-3b', 'H3 3b'), - tocItem('H2 4', 'h2', 'fizz/buzz#h2-four', 'H2 four'), - ]; -} +import { Component, CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { asapScheduler as asap, BehaviorSubject } from 'rxjs'; + +import { ScrollService } from 'app/shared/scroll.service'; +import { TocItem, TocService } from 'app/shared/toc.service'; +import { TocComponent } from './toc.component'; + +describe('TocComponent', () => { + let tocComponentDe: DebugElement; + let tocComponent: TocComponent; + let tocService: TestTocService; + + let page: { + listItems: DebugElement[]; + tocHeading: DebugElement; + tocHeadingButtonEmbedded: DebugElement; + tocH1Heading: DebugElement; + tocMoreButton: DebugElement; + }; + + function setPage(): typeof page { + return { + listItems: tocComponentDe.queryAll(By.css('ul.toc-list>li')), + tocHeading: tocComponentDe.query(By.css('.toc-heading')), + tocHeadingButtonEmbedded: tocComponentDe.query( + By.css('button.toc-heading.embedded') + ), + tocH1Heading: tocComponentDe.query(By.css('.h1')), + tocMoreButton: tocComponentDe.query(By.css('button.toc-more-items')), + }; + } + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + HostEmbeddedTocComponent, + HostNotEmbeddedTocComponent, + TocComponent, + ], + providers: [ + { provide: ScrollService, useClass: TestScrollService }, + { provide: TocService, useClass: TestTocService }, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }); + }); + + describe('when embedded in doc body', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(HostEmbeddedTocComponent); + tocComponentDe = fixture.debugElement.children[0]; + tocComponent = tocComponentDe.componentInstance; + tocService = TestBed.get(TocService); + }); + + it('should create tocComponent', () => { + expect(tocComponent).toBeTruthy(); + }); + + it('should be in embedded state', () => { + expect(tocComponent.isEmbedded).toEqual(true); + }); + + it('should not display a ToC initially', () => { + expect(tocComponent.type).toEqual('None'); + }); + + it('should not display anything when no h2 or h3 TocItems', () => { + tocService.tocList.next([tocItem('H1', 'h1')]); + fixture.detectChanges(); + expect(tocComponentDe.children.length).toEqual(0); + }); + + it('should update when the TocItems are updated', () => { + tocService.tocList.next([tocItem('Heading A')]); + fixture.detectChanges(); + expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1); + + tocService.tocList.next([ + tocItem('Heading A'), + tocItem('Heading B'), + tocItem('Heading C'), + ]); + fixture.detectChanges(); + expect(tocComponentDe.queryAll(By.css('li')).length).toBe(3); + }); + + it('should only display H2 and H3 TocItems', () => { + tocService.tocList.next([ + tocItem('Heading A', 'h1'), + tocItem('Heading B'), + tocItem('Heading C', 'h3'), + ]); + fixture.detectChanges(); + + const tocItems = tocComponentDe.queryAll(By.css('li')); + const textContents = tocItems.map(item => + item.nativeNode.textContent.trim() + ); + + expect(tocItems.length).toBe(2); + expect(textContents.find(text => text === 'Heading A')).toBeFalsy(); + expect(textContents.find(text => text === 'Heading B')).toBeTruthy(); + expect(textContents.find(text => text === 'Heading C')).toBeTruthy(); + expect(setPage().tocH1Heading).toBeFalsy(); + }); + + it('should stop listening for TocItems once destroyed', () => { + tocService.tocList.next([tocItem('Heading A')]); + fixture.detectChanges(); + expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1); + + tocComponent.ngOnDestroy(); + tocService.tocList.next([ + tocItem('Heading A', 'h1'), + tocItem('Heading B'), + tocItem('Heading C'), + ]); + fixture.detectChanges(); + expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1); + }); + + describe('when fewer than `maxPrimary` TocItems', () => { + beforeEach(() => { + tocService.tocList.next([ + tocItem('Heading A'), + tocItem('Heading B'), + tocItem('Heading C'), + tocItem('Heading D'), + ]); + fixture.detectChanges(); + page = setPage(); + }); + + it('should have four displayed items', () => { + expect(page.listItems.length).toEqual(4); + }); + + it('should not have secondary items', () => { + expect(tocComponent.type).toEqual('EmbeddedSimple'); + const aSecond = page.listItems.find(item => item.classes.secondary); + expect(aSecond).toBeFalsy('should not find a secondary'); + }); + + it('should not display expando buttons', () => { + expect(page.tocHeadingButtonEmbedded).toBeFalsy( + 'top expand/collapse button' + ); + expect(page.tocMoreButton).toBeFalsy('bottom more button'); + }); + }); + + describe('when many TocItems', () => { + let scrollToTopSpy: jasmine.Spy; + + beforeEach(() => { + fixture.detectChanges(); + page = setPage(); + scrollToTopSpy = TestBed.get(ScrollService).scrollToTop; + }); + + it('should have more than 4 displayed items', () => { + expect(page.listItems.length).toBeGreaterThan(4); + }); + + it('should not display the h1 item', () => { + expect(page.listItems.find(item => item.classes.h1)).toBeFalsy( + 'should not find h1 item' + ); + }); + + it('should be in "collapsed" (not expanded) state at the start', () => { + expect(tocComponent.isCollapsed).toBeTruthy(); + }); + + it('should have "collapsed" class at the start', () => { + expect(tocComponentDe.children[0].classes.collapsed).toEqual(true); + }); + + it('should display expando buttons', () => { + expect(page.tocHeadingButtonEmbedded).toBeTruthy( + 'top expand/collapse button' + ); + expect(page.tocMoreButton).toBeTruthy('bottom more button'); + }); + + it('should have secondary items', () => { + expect(tocComponent.type).toEqual('EmbeddedExpandable'); + }); + + // CSS will hide items with the secondary class when collapsed + it('should have secondary item with a secondary class', () => { + const aSecondary = page.listItems.find(item => item.classes.secondary); + expect(aSecondary).toBeTruthy('should find a secondary'); + }); + + describe('after click tocHeading button', () => { + beforeEach(() => { + page.tocHeadingButtonEmbedded.nativeElement.click(); + fixture.detectChanges(); + }); + + it('should not be "collapsed"', () => { + expect(tocComponent.isCollapsed).toEqual(false); + }); + + it('should not have "collapsed" class', () => { + expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy(); + }); + + it('should not scroll', () => { + expect(scrollToTopSpy).not.toHaveBeenCalled(); + }); + + it('should be "collapsed" after clicking again', () => { + page.tocHeadingButtonEmbedded.nativeElement.click(); + fixture.detectChanges(); + expect(tocComponent.isCollapsed).toEqual(true); + }); + + it('should not scroll after clicking again', () => { + page.tocHeadingButtonEmbedded.nativeElement.click(); + fixture.detectChanges(); + expect(scrollToTopSpy).not.toHaveBeenCalled(); + }); + }); + + describe('after click tocMore button', () => { + beforeEach(() => { + page.tocMoreButton.nativeElement.click(); + fixture.detectChanges(); + }); + + it('should not be "collapsed"', () => { + expect(tocComponent.isCollapsed).toEqual(false); + }); + + it('should not have "collapsed" class', () => { + expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy(); + }); + + it('should not scroll', () => { + expect(scrollToTopSpy).not.toHaveBeenCalled(); + }); + + it('should be "collapsed" after clicking again', () => { + page.tocMoreButton.nativeElement.click(); + fixture.detectChanges(); + expect(tocComponent.isCollapsed).toEqual(true); + }); + + it('should be "collapsed" after clicking tocHeadingButton', () => { + page.tocMoreButton.nativeElement.click(); + fixture.detectChanges(); + expect(tocComponent.isCollapsed).toEqual(true); + }); + + it('should scroll after clicking again', () => { + page.tocMoreButton.nativeElement.click(); + fixture.detectChanges(); + expect(scrollToTopSpy).toHaveBeenCalled(); + }); + }); + }); + }); + + describe('when in side panel (not embedded)', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(HostNotEmbeddedTocComponent); + + tocComponentDe = fixture.debugElement.children[0]; + tocComponent = tocComponentDe.componentInstance; + tocService = TestBed.get(TocService); + + fixture.detectChanges(); + page = setPage(); + }); + + it('should not be in embedded state', () => { + expect(tocComponent.isEmbedded).toEqual(false); + expect(tocComponent.type).toEqual('Floating'); + }); + + it('should display all items (including h1s)', () => { + expect(page.listItems.length).toEqual(getTestTocList().length); + }); + + it('should not have secondary items', () => { + expect(tocComponent.type).toEqual('Floating'); + const aSecond = page.listItems.find(item => item.classes.secondary); + expect(aSecond).toBeFalsy('should not find a secondary'); + }); + + it('should not display expando buttons', () => { + expect(page.tocHeadingButtonEmbedded).toBeFalsy( + 'top expand/collapse button' + ); + expect(page.tocMoreButton).toBeFalsy('bottom more button'); + }); + + it('should display H1 title', () => { + expect(page.tocH1Heading).toBeTruthy(); + }); + + describe('#activeIndex', () => { + it("should keep track of `TocService`'s `activeItemIndex`", () => { + expect(tocComponent.activeIndex).toBeNull(); + + tocService.setActiveIndex(42); + expect(tocComponent.activeIndex).toBe(42); + + tocService.setActiveIndex(null); + expect(tocComponent.activeIndex).toBeNull(); + }); + + it('should stop tracking `activeItemIndex` once destroyed', () => { + tocService.setActiveIndex(42); + expect(tocComponent.activeIndex).toBe(42); + + tocComponent.ngOnDestroy(); + + tocService.setActiveIndex(43); + expect(tocComponent.activeIndex).toBe(42); + + tocService.setActiveIndex(null); + expect(tocComponent.activeIndex).toBe(42); + }); + + it('should set the `active` class to the active anchor (and only that)', () => { + expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); + + tocComponent.activeIndex = 1; + fixture.detectChanges(); + expect(page.listItems.filter(By.css('.active')).length).toBe(1); + expect(page.listItems.findIndex(By.css('.active'))).toBe(1); + + tocComponent.activeIndex = null; + fixture.detectChanges(); + expect(page.listItems.filter(By.css('.active')).length).toBe(0); + expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); + + tocComponent.activeIndex = 0; + fixture.detectChanges(); + expect(page.listItems.filter(By.css('.active')).length).toBe(1); + expect(page.listItems.findIndex(By.css('.active'))).toBe(0); + + tocComponent.activeIndex = 1337; + fixture.detectChanges(); + expect(page.listItems.filter(By.css('.active')).length).toBe(0); + expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); + + tocComponent.activeIndex = page.listItems.length - 1; + fixture.detectChanges(); + expect(page.listItems.filter(By.css('.active')).length).toBe(1); + expect(page.listItems.findIndex(By.css('.active'))).toBe( + page.listItems.length - 1 + ); + }); + + it('should re-apply the `active` class when the list elements change', () => { + const getActiveTextContent = () => + page.listItems + .find(By.css('.active'))! + .nativeElement.textContent.trim(); + + tocComponent.activeIndex = 1; + fixture.detectChanges(); + expect(getActiveTextContent()).toBe('Heading one'); + + tocComponent.tocList = [tocItem('New 1'), tocItem('New 2')]; + fixture.detectChanges(); + page = setPage(); + expect(getActiveTextContent()).toBe('New 2'); + + tocComponent.tocList.unshift(tocItem('New 0')); + fixture.detectChanges(); + page = setPage(); + expect(getActiveTextContent()).toBe('New 1'); + + tocComponent.tocList = [tocItem('Very New 1')]; + fixture.detectChanges(); + page = setPage(); + expect(page.listItems.findIndex(By.css('.active'))).toBe(-1); + + tocComponent.activeIndex = 0; + fixture.detectChanges(); + expect(getActiveTextContent()).toBe('Very New 1'); + }); + + describe('should scroll the active ToC item into viewport (if not already visible)', () => { + let parentScrollTop: number; + + beforeEach(() => { + const hostElem = fixture.nativeElement; + const firstItem = page.listItems[0].nativeElement; + + Object.assign(hostElem.style, { + display: 'block', + maxHeight: `${hostElem.clientHeight - firstItem.clientHeight}px`, + overflow: 'auto', + position: 'relative', + }); + Object.defineProperty(hostElem, 'scrollTop', { + get: () => parentScrollTop, + set: v => (parentScrollTop = v), + }); + + parentScrollTop = 0; + }); + + it('when the `activeIndex` changes', () => { + tocService.setActiveIndex(0); + fixture.detectChanges(); + + expect(parentScrollTop).toBe(0); + + tocService.setActiveIndex(1); + fixture.detectChanges(); + + expect(parentScrollTop).toBe(0); + + tocService.setActiveIndex(page.listItems.length - 1); + fixture.detectChanges(); + + expect(parentScrollTop).toBeGreaterThan(0); + }); + + it('when the `tocList` changes', () => { + const tocList = tocComponent.tocList; + + tocComponent.tocList = []; + fixture.detectChanges(); + + expect(parentScrollTop).toBe(0); + + tocService.setActiveIndex(tocList.length - 1); + fixture.detectChanges(); + + expect(parentScrollTop).toBe(0); + + tocComponent.tocList = tocList; + fixture.detectChanges(); + + expect(parentScrollTop).toBeGreaterThan(0); + }); + + it('not after it has been destroyed', () => { + const tocList = tocComponent.tocList; + tocComponent.ngOnDestroy(); + + tocService.setActiveIndex(page.listItems.length - 1); + fixture.detectChanges(); + + expect(parentScrollTop).toBe(0); + + tocComponent.tocList = []; + fixture.detectChanges(); + + expect(parentScrollTop).toBe(0); + + tocComponent.tocList = tocList; + fixture.detectChanges(); + + expect(parentScrollTop).toBe(0); + }); + }); + }); + }); +}); + +//// helpers //// +@Component({ + selector: 'aio-embedded-host', + template: '', +}) +class HostEmbeddedTocComponent {} + +@Component({ + selector: 'aio-not-embedded-host', + template: '', +}) +class HostNotEmbeddedTocComponent {} + +class TestScrollService { + scrollToTop = jasmine.createSpy('scrollToTop'); +} + +class TestTocService { + tocList = new BehaviorSubject(getTestTocList()); + activeItemIndex = new BehaviorSubject(null); + setActiveIndex(index: number | null) { + this.activeItemIndex.next(index); + if (asap.scheduled !== undefined) { + asap.flush(); + } + } +} + +function tocItem(title: string, level = 'h2', href = '', content = title) { + return { title, href, level, content }; +} + +function getTestTocList() { + return [ + tocItem('Title', 'h1', 'fizz/buzz#title', 'Title'), + tocItem( + 'Heading one', + 'h2', + 'fizz/buzz#heading-one-special-id', + 'Heading one' + ), + tocItem('H2 Two', 'h2', 'fizz/buzz#h2-two', 'H2 Two'), + tocItem('H2 Three', 'h2', 'fizz/buzz#h2-three', 'H2 Three'), + tocItem('H3 3a', 'h3', 'fizz/buzz#h3-3a', 'H3 3a'), + tocItem('H3 3b', 'h3', 'fizz/buzz#h3-3b', 'H3 3b'), + tocItem('H2 4', 'h2', 'fizz/buzz#h2-four', 'H2 four'), + ]; +} diff --git a/projects/ngrx.io/src/app/layout/doc-viewer/doc-viewer.component.spec.ts b/projects/ngrx.io/src/app/layout/doc-viewer/doc-viewer.component.spec.ts index 93c11102dd..17ff21fd5d 100644 --- a/projects/ngrx.io/src/app/layout/doc-viewer/doc-viewer.component.spec.ts +++ b/projects/ngrx.io/src/app/layout/doc-viewer/doc-viewer.component.spec.ts @@ -1,733 +1,733 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Meta, Title } from '@angular/platform-browser'; - -import { Observable, of } from 'rxjs'; - -import { FILE_NOT_FOUND_ID, FETCHING_ERROR_ID } from 'app/documents/document.service'; -import { Logger } from 'app/shared/logger.service'; -import { CustomElementsModule } from 'app/custom-elements/custom-elements.module'; -import { TocService } from 'app/shared/toc.service'; -import { ElementsLoader } from 'app/custom-elements/elements-loader'; -import { -MockTitle, MockTocService, ObservableWithSubscriptionSpies, -TestDocViewerComponent, TestModule, TestParentComponent, MockElementsLoader -} from 'testing/doc-viewer-utils'; -import { MockLogger } from 'testing/logger.service'; -import { DocViewerComponent, NO_ANIMATIONS } from './doc-viewer.component'; - -describe('DocViewerComponent', () => { - let parentFixture: ComponentFixture; - let parentComponent: TestParentComponent; - let docViewerEl: HTMLElement; - let docViewer: TestDocViewerComponent; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [CustomElementsModule, TestModule], - }); - - parentFixture = TestBed.createComponent(TestParentComponent); - parentComponent = parentFixture.componentInstance; - - parentFixture.detectChanges(); - - docViewerEl = parentFixture.debugElement.children[0].nativeElement; - docViewer = parentComponent.docViewer as any; - }); - - it('should create a `DocViewer`', () => { - expect(docViewer).toEqual(jasmine.any(DocViewerComponent)); - }); - - describe('#doc', () => { - let renderSpy: jasmine.Spy; - - const setCurrentDoc = (contents: string|null, id = 'fizz/buzz') => { - parentComponent.currentDoc = {contents, id}; - parentFixture.detectChanges(); - }; - - beforeEach(() => renderSpy = spyOn(docViewer, 'render').and.returnValue([null])); - - it('should render the new document', () => { - setCurrentDoc('foo', 'bar'); - expect(renderSpy).toHaveBeenCalledTimes(1); - expect(renderSpy.calls.mostRecent().args).toEqual([{id: 'bar', contents: 'foo'}]); - - setCurrentDoc(null, 'baz'); - expect(renderSpy).toHaveBeenCalledTimes(2); - expect(renderSpy.calls.mostRecent().args).toEqual([{id: 'baz', contents: null}]); - }); - - it('should unsubscribe from the previous "render" observable upon new document', () => { - const obs = new ObservableWithSubscriptionSpies(); - renderSpy.and.returnValue(obs); - - setCurrentDoc('foo', 'bar'); - expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); - expect(obs.unsubscribeSpies[0]).not.toHaveBeenCalled(); - - setCurrentDoc('baz', 'qux'); - expect(obs.subscribeSpy).toHaveBeenCalledTimes(2); - expect(obs.unsubscribeSpies[0]).toHaveBeenCalledTimes(1); - }); - - it('should ignore falsy document values', () => { - parentComponent.currentDoc = null; - parentFixture.detectChanges(); - - expect(renderSpy).not.toHaveBeenCalled(); - - parentComponent.currentDoc = undefined; - parentFixture.detectChanges(); - - expect(renderSpy).not.toHaveBeenCalled(); - }); - }); - - describe('#ngOnDestroy()', () => { - it('should stop responding to document changes', () => { - const renderSpy = spyOn(docViewer, 'render').and.returnValue([undefined]); - - expect(renderSpy).not.toHaveBeenCalled(); - - docViewer.doc = {contents: 'Some content', id: 'some-id'}; - expect(renderSpy).toHaveBeenCalledTimes(1); - - docViewer.ngOnDestroy(); - - docViewer.doc = {contents: 'Other content', id: 'other-id'}; - expect(renderSpy).toHaveBeenCalledTimes(1); - - docViewer.doc = {contents: 'More content', id: 'more-id'}; - expect(renderSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe('#prepareTitleAndToc()', () => { - const EMPTY_DOC = ''; - const DOC_WITHOUT_H1 = 'Some content'; - const DOC_WITH_H1 = '

    Features

    Some content'; - const DOC_WITH_NO_TOC_H1 = '

    Features

    Some content'; - const DOC_WITH_EMBEDDED_TOC = '

    Features

    Some content'; - const DOC_WITH_EMBEDDED_TOC_WITHOUT_H1 = 'Some content'; - const DOC_WITH_EMBEDDED_TOC_WITH_NO_TOC_H1 = 'Some content'; - const DOC_WITH_HIDDEN_H1_CONTENT = '

    linkFeatures

    Some content'; - let titleService: MockTitle; - let tocService: MockTocService; - let targetEl: HTMLElement; - - const getTocEl = () => targetEl.querySelector('aio-toc'); - const doPrepareTitleAndToc = (contents: string, docId = '') => { - targetEl.innerHTML = contents; - return docViewer.prepareTitleAndToc(targetEl, docId); - }; - const doAddTitleAndToc = (contents: string, docId = '') => { - const addTitleAndToc = doPrepareTitleAndToc(contents, docId); - return addTitleAndToc(); - }; - - beforeEach(() => { - titleService = TestBed.get(Title); - tocService = TestBed.get(TocService); - - targetEl = document.createElement('div'); - document.body.appendChild(targetEl); // Required for `innerText` to work as expected. - }); - - afterEach(() => document.body.removeChild(targetEl)); - - it('should return a function for doing the actual work', () => { - const addTitleAndToc = doPrepareTitleAndToc(DOC_WITH_H1); - - expect(getTocEl()).toBeTruthy(); - expect(titleService.setTitle).not.toHaveBeenCalled(); - expect(tocService.reset).not.toHaveBeenCalled(); - expect(tocService.genToc).not.toHaveBeenCalled(); - - addTitleAndToc(); - - expect(titleService.setTitle).toHaveBeenCalledTimes(1); - expect(tocService.reset).toHaveBeenCalledTimes(1); - expect(tocService.genToc).toHaveBeenCalledTimes(1); - }); - - describe('(title)', () => { - it('should set the title if there is an `

    ` heading', () => { - doAddTitleAndToc(DOC_WITH_H1); - expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Features'); - }); - - it('should set the title if there is a `.no-toc` `

    ` heading', () => { - doAddTitleAndToc(DOC_WITH_NO_TOC_H1); - expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Features'); - }); - - it('should set the default title if there is no `

    ` heading', () => { - doAddTitleAndToc(DOC_WITHOUT_H1); - expect(titleService.setTitle).toHaveBeenCalledWith('NgRx'); - - doAddTitleAndToc(EMPTY_DOC); - expect(titleService.setTitle).toHaveBeenCalledWith('NgRx'); - }); - - it('should not include hidden content of the `

    ` heading in the title', () => { - doAddTitleAndToc(DOC_WITH_HIDDEN_H1_CONTENT); - expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Features'); - }); - - it('should fall back to `textContent` if `innerText` is not available', () => { - const querySelector_ = targetEl.querySelector; - spyOn(targetEl, 'querySelector').and.callFake((selector: string) => { - const elem = querySelector_.call(targetEl, selector); - return elem && Object.defineProperties(elem, { - innerText: {value: undefined}, - textContent: {value: 'Text Content'}, - }); - }); - - doAddTitleAndToc(DOC_WITH_HIDDEN_H1_CONTENT); - - expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Text Content'); - }); - - it('should still use `innerText` if available but empty', () => { - const querySelector_ = targetEl.querySelector; - spyOn(targetEl, 'querySelector').and.callFake((selector: string) => { - const elem = querySelector_.call(targetEl, selector); - return elem && Object.defineProperties(elem, { - innerText: { value: '' }, - textContent: { value: 'Text Content' } - }); - }); - - doAddTitleAndToc(DOC_WITH_HIDDEN_H1_CONTENT); - - expect(titleService.setTitle).toHaveBeenCalledWith('NgRx'); - }); - }); - - describe('(ToC)', () => { - describe('needed', () => { - it('should add an embedded ToC element if there is an `

    ` heading', () => { - doPrepareTitleAndToc(DOC_WITH_H1); - const tocEl = getTocEl()!; - - expect(tocEl).toBeTruthy(); - expect(tocEl.classList.contains('embedded')).toBe(true); - }); - - it('should not add a second ToC element if there a hard coded one in place', () => { - doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC); - expect(targetEl.querySelectorAll('aio-toc').length).toEqual(1); - }); - }); - - - describe('not needed', () => { - it('should not add a ToC element if there is a `.no-toc` `

    ` heading', () => { - doPrepareTitleAndToc(DOC_WITH_NO_TOC_H1); - expect(getTocEl()).toBeFalsy(); - }); - - it('should not add a ToC element if there is no `

    ` heading', () => { - doPrepareTitleAndToc(DOC_WITHOUT_H1); - expect(getTocEl()).toBeFalsy(); - - doPrepareTitleAndToc(EMPTY_DOC); - expect(getTocEl()).toBeFalsy(); - }); - - it('should remove ToC a hard coded one', () => { - doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC_WITHOUT_H1); - expect(getTocEl()).toBeFalsy(); - - doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC_WITH_NO_TOC_H1); - expect(getTocEl()).toBeFalsy(); - }); - }); - - - it('should generate ToC entries if there is an `

    ` heading', () => { - doAddTitleAndToc(DOC_WITH_H1, 'foo'); - - expect(tocService.genToc).toHaveBeenCalledTimes(1); - expect(tocService.genToc).toHaveBeenCalledWith(targetEl, 'foo'); - }); - - it('should not generate ToC entries if there is a `.no-toc` `

    ` heading', () => { - doAddTitleAndToc(DOC_WITH_NO_TOC_H1); - expect(tocService.genToc).not.toHaveBeenCalled(); - }); - - it('should not generate ToC entries if there is no `

    ` heading', () => { - doAddTitleAndToc(DOC_WITHOUT_H1); - doAddTitleAndToc(EMPTY_DOC); - - expect(tocService.genToc).not.toHaveBeenCalled(); - }); - - it('should always reset the ToC (before generating the new one)', () => { - doAddTitleAndToc(DOC_WITH_H1, 'foo'); - expect(tocService.reset).toHaveBeenCalledTimes(1); - expect(tocService.reset).toHaveBeenCalledBefore(tocService.genToc); - expect(tocService.genToc).toHaveBeenCalledWith(targetEl, 'foo'); - - tocService.genToc.calls.reset(); - - doAddTitleAndToc(DOC_WITH_NO_TOC_H1, 'bar'); - expect(tocService.reset).toHaveBeenCalledTimes(2); - expect(tocService.genToc).not.toHaveBeenCalled(); - - doAddTitleAndToc(DOC_WITHOUT_H1, 'baz'); - expect(tocService.reset).toHaveBeenCalledTimes(3); - expect(tocService.genToc).not.toHaveBeenCalled(); - - doAddTitleAndToc(EMPTY_DOC, 'qux'); - expect(tocService.reset).toHaveBeenCalledTimes(4); - expect(tocService.genToc).not.toHaveBeenCalled(); - }); - }); - }); - - describe('#render()', () => { - let prepareTitleAndTocSpy: jasmine.Spy; - let swapViewsSpy: jasmine.Spy; - let loadElementsSpy: jasmine.Spy; - - const doRender = (contents: string | null, id = 'foo') => - docViewer.render({contents, id}).toPromise(); - - beforeEach(() => { - const elementsLoader = TestBed.get(ElementsLoader) as MockElementsLoader; - loadElementsSpy = elementsLoader.loadContainedCustomElements.and.returnValue(of(undefined)); - prepareTitleAndTocSpy = spyOn(docViewer, 'prepareTitleAndToc'); - swapViewsSpy = spyOn(docViewer, 'swapViews').and.returnValue(of(undefined)); - }); - - it('should return an `Observable`', () => { - expect(docViewer.render({contents: '', id: ''})).toEqual(jasmine.any(Observable)); - }); - - describe('(contents, title, ToC)', () => { - beforeEach(() => swapViewsSpy.and.callThrough()); - - it('should display the document contents', async () => { - const contents = '

    Hello,

    world!
    '; - await doRender(contents); - - expect(docViewerEl.innerHTML).toContain(contents); - expect(docViewerEl.textContent).toBe('Hello, world!'); - }); - - it('should display nothing if the document has no contents', async () => { - await doRender('Test'); - expect(docViewerEl.textContent).toBe('Test'); - - await doRender(''); - expect(docViewerEl.textContent).toBe(''); - - docViewer.currViewContainer.innerHTML = 'Test'; - expect(docViewerEl.textContent).toBe('Test'); - - await doRender(null); - expect(docViewerEl.textContent).toBe(''); - }); - - it('should prepare the title and ToC (before embedding components)', async () => { - prepareTitleAndTocSpy.and.callFake((targetEl: HTMLElement, docId: string) => { - expect(targetEl.innerHTML).toBe('Some content'); - expect(docId).toBe('foo'); - }); - - await doRender('Some content', 'foo'); - - expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); - expect(prepareTitleAndTocSpy).toHaveBeenCalledBefore(loadElementsSpy); - }); - - it('should set the title and ToC (after the content has been set)', async () => { - const addTitleAndTocSpy = jasmine.createSpy('addTitleAndToc'); - prepareTitleAndTocSpy.and.returnValue(addTitleAndTocSpy); - - addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('Foo content')); - await doRender('Foo content'); - expect(addTitleAndTocSpy).toHaveBeenCalledTimes(1); - - addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('Bar content')); - await doRender('Bar content'); - expect(addTitleAndTocSpy).toHaveBeenCalledTimes(2); - - addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('')); - await doRender(''); - expect(addTitleAndTocSpy).toHaveBeenCalledTimes(3); - - addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('Qux content')); - await doRender('Qux content'); - expect(addTitleAndTocSpy).toHaveBeenCalledTimes(4); - }); - - it('should remove the "noindex" meta tag if the document is valid', async () => { - await doRender('foo', 'bar'); - expect(TestBed.get(Meta).removeTag).toHaveBeenCalledWith('name="robots"'); - }); - - it('should add the "noindex" meta tag if the document is 404', async () => { - await doRender('missing', FILE_NOT_FOUND_ID); - expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); - }); - - it('should add a "noindex" meta tag if the document fetching fails', async () => { - await doRender('error', FETCHING_ERROR_ID); - expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); - }); - }); - - describe('(embedding components)', () => { - it('should embed components', async () => { - await doRender('Some content'); - expect(loadElementsSpy).toHaveBeenCalledTimes(1); - expect(loadElementsSpy).toHaveBeenCalledWith(docViewer.nextViewContainer); - }); - - it('should attempt to embed components even if the document is empty', async () => { - await doRender(''); - await doRender(null); - - expect(loadElementsSpy).toHaveBeenCalledTimes(2); - expect(loadElementsSpy.calls.argsFor(0)).toEqual([docViewer.nextViewContainer]); - expect(loadElementsSpy.calls.argsFor(1)).toEqual([docViewer.nextViewContainer]); - }); - - it('should unsubscribe from the previous "embed" observable when unsubscribed from', () => { - const obs = new ObservableWithSubscriptionSpies(); - loadElementsSpy.and.returnValue(obs); - - const renderObservable = docViewer.render({contents: 'Some content', id: 'foo'}); - const subscription = renderObservable.subscribe(); - - expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); - expect(obs.unsubscribeSpies[0]).not.toHaveBeenCalled(); - - subscription.unsubscribe(); - - expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); - expect(obs.unsubscribeSpies[0]).toHaveBeenCalledTimes(1); - }); - }); - - describe('(swapping views)', () => { - it('should still swap the views if the document is empty', async () => { - await doRender(''); - expect(swapViewsSpy).toHaveBeenCalledTimes(1); - - await doRender(null); - expect(swapViewsSpy).toHaveBeenCalledTimes(2); - }); - - it('should pass the `addTitleAndToc` callback', async () => { - const addTitleAndTocSpy = jasmine.createSpy('addTitleAndToc'); - prepareTitleAndTocSpy.and.returnValue(addTitleAndTocSpy); - - await doRender('
    '); - - expect(swapViewsSpy).toHaveBeenCalledWith(addTitleAndTocSpy); - }); - - it('should unsubscribe from the previous "swap" observable when unsubscribed from', () => { - const obs = new ObservableWithSubscriptionSpies(); - swapViewsSpy.and.returnValue(obs); - - const renderObservable = docViewer.render({contents: 'Hello, world!', id: 'foo'}); - const subscription = renderObservable.subscribe(); - - expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); - expect(obs.unsubscribeSpies[0]).not.toHaveBeenCalled(); - - subscription.unsubscribe(); - - expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); - expect(obs.unsubscribeSpies[0]).toHaveBeenCalledTimes(1); - }); - }); - - describe('(on error) should clean up, log the error and recover', () => { - let logger: MockLogger; - - beforeEach(() => logger = TestBed.get(Logger)); - - it('when `prepareTitleAndTocSpy()` fails', async () => { - const error = Error('Typical `prepareTitleAndToc()` error'); - prepareTitleAndTocSpy.and.callFake(() => { - expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); - throw error; - }); - - await doRender('Some content', 'foo'); - - expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); - expect(swapViewsSpy).not.toHaveBeenCalled(); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - expect(logger.output.error).toEqual([ - [jasmine.any(Error)] - ]); - expect(logger.output.error[0][0].message).toEqual(`[DocViewer] Error preparing document 'foo': ${error.stack}`); - expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); - }); - - it('when `EmbedComponentsService.embedInto()` fails', async () => { - const error = Error('Typical `embedInto()` error'); - loadElementsSpy.and.callFake(() => { - expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); - throw error; - }); - - await doRender('Some content', 'bar'); - - expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); - expect(loadElementsSpy).toHaveBeenCalledTimes(1); - expect(swapViewsSpy).not.toHaveBeenCalled(); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - expect(logger.output.error).toEqual([ - [jasmine.any(Error)] - ]); - expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); - }); - - it('when `swapViews()` fails', async () => { - const error = Error('Typical `swapViews()` error'); - swapViewsSpy.and.callFake(() => { - expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); - throw error; - }); - - await doRender('Some content', 'qux'); - - expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); - expect(swapViewsSpy).toHaveBeenCalledTimes(1); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - expect(logger.output.error).toEqual([ - [jasmine.any(Error)] - ]); - expect(logger.output.error[0][0].message).toEqual(`[DocViewer] Error preparing document 'qux': ${error.stack}`); - expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); - }); - - it('when something fails with non-Error', async () => { - const error = 'Typical string error'; - swapViewsSpy.and.callFake(() => { - expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); - throw error; - }); - - await doRender('Some content', 'qux'); - - expect(swapViewsSpy).toHaveBeenCalledTimes(1); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - expect(logger.output.error).toEqual([ - [jasmine.any(Error)] - ]); - expect(logger.output.error[0][0].message).toEqual(`[DocViewer] Error preparing document 'qux': ${error}`); - expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); - }); - }); - - describe('(events)', () => { - it('should emit `docReady` after loading elements', async () => { - const onDocReadySpy = jasmine.createSpy('onDocReady'); - docViewer.docReady.subscribe(onDocReadySpy); - - await doRender('Some content'); - - expect(onDocReadySpy).toHaveBeenCalledTimes(1); - expect(loadElementsSpy).toHaveBeenCalledBefore(onDocReadySpy); - }); - - it('should emit `docReady` before swapping views', async () => { - const onDocReadySpy = jasmine.createSpy('onDocReady'); - docViewer.docReady.subscribe(onDocReadySpy); - - await doRender('Some content'); - - expect(onDocReadySpy).toHaveBeenCalledTimes(1); - expect(onDocReadySpy).toHaveBeenCalledBefore(swapViewsSpy); - }); - - it('should emit `docRendered` after swapping views', async () => { - const onDocRenderedSpy = jasmine.createSpy('onDocRendered'); - docViewer.docRendered.subscribe(onDocRenderedSpy); - - await doRender('Some content'); - - expect(onDocRenderedSpy).toHaveBeenCalledTimes(1); - expect(swapViewsSpy).toHaveBeenCalledBefore(onDocRenderedSpy); - }); - }); - }); - - describe('#swapViews()', () => { - let oldCurrViewContainer: HTMLElement; - let oldNextViewContainer: HTMLElement; - - const doSwapViews = (cb?: () => void) => - new Promise((resolve, reject) => - docViewer.swapViews(cb).subscribe(resolve, reject)); - - beforeEach(() => { - oldCurrViewContainer = docViewer.currViewContainer; - oldNextViewContainer = docViewer.nextViewContainer; - - oldCurrViewContainer.innerHTML = 'Current view'; - oldNextViewContainer.innerHTML = 'Next view'; - - docViewerEl.appendChild(oldCurrViewContainer); - - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); - }); - - [true, false].forEach(animationsEnabled => { - describe(`(animationsEnabled: ${animationsEnabled})`, () => { - beforeEach(() => DocViewerComponent.animationsEnabled = animationsEnabled); - afterEach(() => DocViewerComponent.animationsEnabled = true); - - [true, false].forEach(noAnimations => { - describe(`(.${NO_ANIMATIONS}: ${noAnimations})`, () => { - beforeEach(() => docViewerEl.classList[noAnimations ? 'add' : 'remove'](NO_ANIMATIONS)); - - it('should return an observable', done => { - docViewer.swapViews().subscribe(done, done.fail); - }); - - it('should swap the views', async () => { - await doSwapViews(); - - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - expect(docViewer.currViewContainer).toBe(oldNextViewContainer); - expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer); - - await doSwapViews(); - - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); - expect(docViewer.currViewContainer).toBe(oldCurrViewContainer); - expect(docViewer.nextViewContainer).toBe(oldNextViewContainer); - }); - - it('should emit `docRemoved` after removing the leaving view', async () => { - const onDocRemovedSpy = jasmine.createSpy('onDocRemoved').and.callFake(() => { - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); - }); - - docViewer.docRemoved.subscribe(onDocRemovedSpy); - - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); - - await doSwapViews(); - - expect(onDocRemovedSpy).toHaveBeenCalledTimes(1); - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - }); - - it('should not emit `docRemoved` if the leaving view is already removed', async () => { - const onDocRemovedSpy = jasmine.createSpy('onDocRemoved'); - - docViewer.docRemoved.subscribe(onDocRemovedSpy); - docViewerEl.removeChild(oldCurrViewContainer); - - await doSwapViews(); - - expect(onDocRemovedSpy).not.toHaveBeenCalled(); - }); - - it('should emit `docInserted` after inserting the entering view', async () => { - const onDocInsertedSpy = jasmine.createSpy('onDocInserted').and.callFake(() => { - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - }); - - docViewer.docInserted.subscribe(onDocInsertedSpy); - - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); - - await doSwapViews(); - - expect(onDocInsertedSpy).toHaveBeenCalledTimes(1); - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - }); - - it('should call the callback after inserting the entering view', async () => { - const onInsertedCb = jasmine.createSpy('onInsertedCb').and.callFake(() => { - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - }); - const onDocInsertedSpy = jasmine.createSpy('onDocInserted'); - - docViewer.docInserted.subscribe(onDocInsertedSpy); - - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); - - await doSwapViews(onInsertedCb); - - expect(onInsertedCb).toHaveBeenCalledTimes(1); - expect(onInsertedCb).toHaveBeenCalledBefore(onDocInsertedSpy); - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - }); - - it('should empty the previous view', async () => { - await doSwapViews(); - - expect(docViewer.currViewContainer.innerHTML).toBe('Next view'); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - - docViewer.nextViewContainer.innerHTML = 'Next view 2'; - await doSwapViews(); - - expect(docViewer.currViewContainer.innerHTML).toBe('Next view 2'); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - }); - - if (animationsEnabled && !noAnimations) { - // Only test this when there are animations. Without animations, the views are swapped - // synchronously, so there is no need (or way) to abort. - it('should abort swapping if the returned observable is unsubscribed from', async () => { - docViewer.swapViews().subscribe().unsubscribe(); - await doSwapViews(); - - // Since the first call was cancelled, only one swapping should have taken place. - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - expect(docViewer.currViewContainer).toBe(oldNextViewContainer); - expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer); - expect(docViewer.currViewContainer.innerHTML).toBe('Next view'); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - }); - } else { - it('should swap views synchronously when animations are disabled', () => { - const cbSpy = jasmine.createSpy('cb'); - - docViewer.swapViews(cbSpy).subscribe(); - - expect(cbSpy).toHaveBeenCalledTimes(1); - expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); - expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); - expect(docViewer.currViewContainer).toBe(oldNextViewContainer); - expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer); - expect(docViewer.currViewContainer.innerHTML).toBe('Next view'); - expect(docViewer.nextViewContainer.innerHTML).toBe(''); - }); - } - }); - }); - }); - }); - }); -}); +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Meta, Title } from '@angular/platform-browser'; + +import { Observable, of } from 'rxjs'; + +import { FILE_NOT_FOUND_ID, FETCHING_ERROR_ID } from 'app/documents/document.service'; +import { Logger } from 'app/shared/logger.service'; +import { CustomElementsModule } from 'app/custom-elements/custom-elements.module'; +import { TocService } from 'app/shared/toc.service'; +import { ElementsLoader } from 'app/custom-elements/elements-loader'; +import { +MockTitle, MockTocService, ObservableWithSubscriptionSpies, +TestDocViewerComponent, TestModule, TestParentComponent, MockElementsLoader +} from 'testing/doc-viewer-utils'; +import { MockLogger } from 'testing/logger.service'; +import { DocViewerComponent, NO_ANIMATIONS } from './doc-viewer.component'; + +describe('DocViewerComponent', () => { + let parentFixture: ComponentFixture; + let parentComponent: TestParentComponent; + let docViewerEl: HTMLElement; + let docViewer: TestDocViewerComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CustomElementsModule, TestModule], + }); + + parentFixture = TestBed.createComponent(TestParentComponent); + parentComponent = parentFixture.componentInstance; + + parentFixture.detectChanges(); + + docViewerEl = parentFixture.debugElement.children[0].nativeElement; + docViewer = parentComponent.docViewer as any; + }); + + it('should create a `DocViewer`', () => { + expect(docViewer).toEqual(jasmine.any(DocViewerComponent)); + }); + + describe('#doc', () => { + let renderSpy: jasmine.Spy; + + const setCurrentDoc = (contents: string|null, id = 'fizz/buzz') => { + parentComponent.currentDoc = {contents, id}; + parentFixture.detectChanges(); + }; + + beforeEach(() => renderSpy = spyOn(docViewer, 'render').and.returnValue([null])); + + it('should render the new document', () => { + setCurrentDoc('foo', 'bar'); + expect(renderSpy).toHaveBeenCalledTimes(1); + expect(renderSpy.calls.mostRecent().args).toEqual([{id: 'bar', contents: 'foo'}]); + + setCurrentDoc(null, 'baz'); + expect(renderSpy).toHaveBeenCalledTimes(2); + expect(renderSpy.calls.mostRecent().args).toEqual([{id: 'baz', contents: null}]); + }); + + it('should unsubscribe from the previous "render" observable upon new document', () => { + const obs = new ObservableWithSubscriptionSpies(); + renderSpy.and.returnValue(obs); + + setCurrentDoc('foo', 'bar'); + expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); + expect(obs.unsubscribeSpies[0]).not.toHaveBeenCalled(); + + setCurrentDoc('baz', 'qux'); + expect(obs.subscribeSpy).toHaveBeenCalledTimes(2); + expect(obs.unsubscribeSpies[0]).toHaveBeenCalledTimes(1); + }); + + it('should ignore falsy document values', () => { + parentComponent.currentDoc = null; + parentFixture.detectChanges(); + + expect(renderSpy).not.toHaveBeenCalled(); + + parentComponent.currentDoc = undefined; + parentFixture.detectChanges(); + + expect(renderSpy).not.toHaveBeenCalled(); + }); + }); + + describe('#ngOnDestroy()', () => { + it('should stop responding to document changes', () => { + const renderSpy = spyOn(docViewer, 'render').and.returnValue([undefined]); + + expect(renderSpy).not.toHaveBeenCalled(); + + docViewer.doc = {contents: 'Some content', id: 'some-id'}; + expect(renderSpy).toHaveBeenCalledTimes(1); + + docViewer.ngOnDestroy(); + + docViewer.doc = {contents: 'Other content', id: 'other-id'}; + expect(renderSpy).toHaveBeenCalledTimes(1); + + docViewer.doc = {contents: 'More content', id: 'more-id'}; + expect(renderSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe('#prepareTitleAndToc()', () => { + const EMPTY_DOC = ''; + const DOC_WITHOUT_H1 = 'Some content'; + const DOC_WITH_H1 = '

    Features

    Some content'; + const DOC_WITH_NO_TOC_H1 = '

    Features

    Some content'; + const DOC_WITH_EMBEDDED_TOC = '

    Features

    Some content'; + const DOC_WITH_EMBEDDED_TOC_WITHOUT_H1 = 'Some content'; + const DOC_WITH_EMBEDDED_TOC_WITH_NO_TOC_H1 = 'Some content'; + const DOC_WITH_HIDDEN_H1_CONTENT = '

    linkFeatures

    Some content'; + let titleService: MockTitle; + let tocService: MockTocService; + let targetEl: HTMLElement; + + const getTocEl = () => targetEl.querySelector('aio-toc'); + const doPrepareTitleAndToc = (contents: string, docId = '') => { + targetEl.innerHTML = contents; + return docViewer.prepareTitleAndToc(targetEl, docId); + }; + const doAddTitleAndToc = (contents: string, docId = '') => { + const addTitleAndToc = doPrepareTitleAndToc(contents, docId); + return addTitleAndToc(); + }; + + beforeEach(() => { + titleService = TestBed.get(Title); + tocService = TestBed.get(TocService); + + targetEl = document.createElement('div'); + document.body.appendChild(targetEl); // Required for `innerText` to work as expected. + }); + + afterEach(() => document.body.removeChild(targetEl)); + + it('should return a function for doing the actual work', () => { + const addTitleAndToc = doPrepareTitleAndToc(DOC_WITH_H1); + + expect(getTocEl()).toBeTruthy(); + expect(titleService.setTitle).not.toHaveBeenCalled(); + expect(tocService.reset).not.toHaveBeenCalled(); + expect(tocService.genToc).not.toHaveBeenCalled(); + + addTitleAndToc(); + + expect(titleService.setTitle).toHaveBeenCalledTimes(1); + expect(tocService.reset).toHaveBeenCalledTimes(1); + expect(tocService.genToc).toHaveBeenCalledTimes(1); + }); + + describe('(title)', () => { + it('should set the title if there is an `

    ` heading', () => { + doAddTitleAndToc(DOC_WITH_H1); + expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Features'); + }); + + it('should set the title if there is a `.no-toc` `

    ` heading', () => { + doAddTitleAndToc(DOC_WITH_NO_TOC_H1); + expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Features'); + }); + + it('should set the default title if there is no `

    ` heading', () => { + doAddTitleAndToc(DOC_WITHOUT_H1); + expect(titleService.setTitle).toHaveBeenCalledWith('NgRx'); + + doAddTitleAndToc(EMPTY_DOC); + expect(titleService.setTitle).toHaveBeenCalledWith('NgRx'); + }); + + it('should not include hidden content of the `

    ` heading in the title', () => { + doAddTitleAndToc(DOC_WITH_HIDDEN_H1_CONTENT); + expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Features'); + }); + + it('should fall back to `textContent` if `innerText` is not available', () => { + const querySelector_ = targetEl.querySelector; + spyOn(targetEl, 'querySelector').and.callFake((selector: string) => { + const elem = querySelector_.call(targetEl, selector); + return elem && Object.defineProperties(elem, { + innerText: {value: undefined}, + textContent: {value: 'Text Content'}, + }); + }); + + doAddTitleAndToc(DOC_WITH_HIDDEN_H1_CONTENT); + + expect(titleService.setTitle).toHaveBeenCalledWith('NgRx - Text Content'); + }); + + it('should still use `innerText` if available but empty', () => { + const querySelector_ = targetEl.querySelector; + spyOn(targetEl, 'querySelector').and.callFake((selector: string) => { + const elem = querySelector_.call(targetEl, selector); + return elem && Object.defineProperties(elem, { + innerText: { value: '' }, + textContent: { value: 'Text Content' } + }); + }); + + doAddTitleAndToc(DOC_WITH_HIDDEN_H1_CONTENT); + + expect(titleService.setTitle).toHaveBeenCalledWith('NgRx'); + }); + }); + + describe('(ToC)', () => { + describe('needed', () => { + it('should add an embedded ToC element if there is an `

    ` heading', () => { + doPrepareTitleAndToc(DOC_WITH_H1); + const tocEl = getTocEl()!; + + expect(tocEl).toBeTruthy(); + expect(tocEl.classList.contains('embedded')).toBe(true); + }); + + it('should not add a second ToC element if there a hard coded one in place', () => { + doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC); + expect(targetEl.querySelectorAll('aio-toc').length).toEqual(1); + }); + }); + + + describe('not needed', () => { + it('should not add a ToC element if there is a `.no-toc` `

    ` heading', () => { + doPrepareTitleAndToc(DOC_WITH_NO_TOC_H1); + expect(getTocEl()).toBeFalsy(); + }); + + it('should not add a ToC element if there is no `

    ` heading', () => { + doPrepareTitleAndToc(DOC_WITHOUT_H1); + expect(getTocEl()).toBeFalsy(); + + doPrepareTitleAndToc(EMPTY_DOC); + expect(getTocEl()).toBeFalsy(); + }); + + it('should remove ToC a hard coded one', () => { + doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC_WITHOUT_H1); + expect(getTocEl()).toBeFalsy(); + + doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC_WITH_NO_TOC_H1); + expect(getTocEl()).toBeFalsy(); + }); + }); + + + it('should generate ToC entries if there is an `

    ` heading', () => { + doAddTitleAndToc(DOC_WITH_H1, 'foo'); + + expect(tocService.genToc).toHaveBeenCalledTimes(1); + expect(tocService.genToc).toHaveBeenCalledWith(targetEl, 'foo'); + }); + + it('should not generate ToC entries if there is a `.no-toc` `

    ` heading', () => { + doAddTitleAndToc(DOC_WITH_NO_TOC_H1); + expect(tocService.genToc).not.toHaveBeenCalled(); + }); + + it('should not generate ToC entries if there is no `

    ` heading', () => { + doAddTitleAndToc(DOC_WITHOUT_H1); + doAddTitleAndToc(EMPTY_DOC); + + expect(tocService.genToc).not.toHaveBeenCalled(); + }); + + it('should always reset the ToC (before generating the new one)', () => { + doAddTitleAndToc(DOC_WITH_H1, 'foo'); + expect(tocService.reset).toHaveBeenCalledTimes(1); + expect(tocService.reset).toHaveBeenCalledBefore(tocService.genToc); + expect(tocService.genToc).toHaveBeenCalledWith(targetEl, 'foo'); + + tocService.genToc.calls.reset(); + + doAddTitleAndToc(DOC_WITH_NO_TOC_H1, 'bar'); + expect(tocService.reset).toHaveBeenCalledTimes(2); + expect(tocService.genToc).not.toHaveBeenCalled(); + + doAddTitleAndToc(DOC_WITHOUT_H1, 'baz'); + expect(tocService.reset).toHaveBeenCalledTimes(3); + expect(tocService.genToc).not.toHaveBeenCalled(); + + doAddTitleAndToc(EMPTY_DOC, 'qux'); + expect(tocService.reset).toHaveBeenCalledTimes(4); + expect(tocService.genToc).not.toHaveBeenCalled(); + }); + }); + }); + + describe('#render()', () => { + let prepareTitleAndTocSpy: jasmine.Spy; + let swapViewsSpy: jasmine.Spy; + let loadElementsSpy: jasmine.Spy; + + const doRender = (contents: string | null, id = 'foo') => + docViewer.render({contents, id}).toPromise(); + + beforeEach(() => { + const elementsLoader = TestBed.get(ElementsLoader) as MockElementsLoader; + loadElementsSpy = elementsLoader.loadContainedCustomElements.and.returnValue(of(undefined)); + prepareTitleAndTocSpy = spyOn(docViewer, 'prepareTitleAndToc'); + swapViewsSpy = spyOn(docViewer, 'swapViews').and.returnValue(of(undefined)); + }); + + it('should return an `Observable`', () => { + expect(docViewer.render({contents: '', id: ''})).toEqual(jasmine.any(Observable)); + }); + + describe('(contents, title, ToC)', () => { + beforeEach(() => swapViewsSpy.and.callThrough()); + + it('should display the document contents', async () => { + const contents = '

    Hello,

    world!
    '; + await doRender(contents); + + expect(docViewerEl.innerHTML).toContain(contents); + expect(docViewerEl.textContent).toBe('Hello, world!'); + }); + + it('should display nothing if the document has no contents', async () => { + await doRender('Test'); + expect(docViewerEl.textContent).toBe('Test'); + + await doRender(''); + expect(docViewerEl.textContent).toBe(''); + + docViewer.currViewContainer.innerHTML = 'Test'; + expect(docViewerEl.textContent).toBe('Test'); + + await doRender(null); + expect(docViewerEl.textContent).toBe(''); + }); + + it('should prepare the title and ToC (before embedding components)', async () => { + prepareTitleAndTocSpy.and.callFake((targetEl: HTMLElement, docId: string) => { + expect(targetEl.innerHTML).toBe('Some content'); + expect(docId).toBe('foo'); + }); + + await doRender('Some content', 'foo'); + + expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); + expect(prepareTitleAndTocSpy).toHaveBeenCalledBefore(loadElementsSpy); + }); + + it('should set the title and ToC (after the content has been set)', async () => { + const addTitleAndTocSpy = jasmine.createSpy('addTitleAndToc'); + prepareTitleAndTocSpy.and.returnValue(addTitleAndTocSpy); + + addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('Foo content')); + await doRender('Foo content'); + expect(addTitleAndTocSpy).toHaveBeenCalledTimes(1); + + addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('Bar content')); + await doRender('Bar content'); + expect(addTitleAndTocSpy).toHaveBeenCalledTimes(2); + + addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('')); + await doRender(''); + expect(addTitleAndTocSpy).toHaveBeenCalledTimes(3); + + addTitleAndTocSpy.and.callFake(() => expect(docViewerEl.textContent).toBe('Qux content')); + await doRender('Qux content'); + expect(addTitleAndTocSpy).toHaveBeenCalledTimes(4); + }); + + it('should remove the "noindex" meta tag if the document is valid', async () => { + await doRender('foo', 'bar'); + expect(TestBed.get(Meta).removeTag).toHaveBeenCalledWith('name="robots"'); + }); + + it('should add the "noindex" meta tag if the document is 404', async () => { + await doRender('missing', FILE_NOT_FOUND_ID); + expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); + }); + + it('should add a "noindex" meta tag if the document fetching fails', async () => { + await doRender('error', FETCHING_ERROR_ID); + expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); + }); + }); + + describe('(embedding components)', () => { + it('should embed components', async () => { + await doRender('Some content'); + expect(loadElementsSpy).toHaveBeenCalledTimes(1); + expect(loadElementsSpy).toHaveBeenCalledWith(docViewer.nextViewContainer); + }); + + it('should attempt to embed components even if the document is empty', async () => { + await doRender(''); + await doRender(null); + + expect(loadElementsSpy).toHaveBeenCalledTimes(2); + expect(loadElementsSpy.calls.argsFor(0)).toEqual([docViewer.nextViewContainer]); + expect(loadElementsSpy.calls.argsFor(1)).toEqual([docViewer.nextViewContainer]); + }); + + it('should unsubscribe from the previous "embed" observable when unsubscribed from', () => { + const obs = new ObservableWithSubscriptionSpies(); + loadElementsSpy.and.returnValue(obs); + + const renderObservable = docViewer.render({contents: 'Some content', id: 'foo'}); + const subscription = renderObservable.subscribe(); + + expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); + expect(obs.unsubscribeSpies[0]).not.toHaveBeenCalled(); + + subscription.unsubscribe(); + + expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); + expect(obs.unsubscribeSpies[0]).toHaveBeenCalledTimes(1); + }); + }); + + describe('(swapping views)', () => { + it('should still swap the views if the document is empty', async () => { + await doRender(''); + expect(swapViewsSpy).toHaveBeenCalledTimes(1); + + await doRender(null); + expect(swapViewsSpy).toHaveBeenCalledTimes(2); + }); + + it('should pass the `addTitleAndToc` callback', async () => { + const addTitleAndTocSpy = jasmine.createSpy('addTitleAndToc'); + prepareTitleAndTocSpy.and.returnValue(addTitleAndTocSpy); + + await doRender('
    '); + + expect(swapViewsSpy).toHaveBeenCalledWith(addTitleAndTocSpy); + }); + + it('should unsubscribe from the previous "swap" observable when unsubscribed from', () => { + const obs = new ObservableWithSubscriptionSpies(); + swapViewsSpy.and.returnValue(obs); + + const renderObservable = docViewer.render({contents: 'Hello, world!', id: 'foo'}); + const subscription = renderObservable.subscribe(); + + expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); + expect(obs.unsubscribeSpies[0]).not.toHaveBeenCalled(); + + subscription.unsubscribe(); + + expect(obs.subscribeSpy).toHaveBeenCalledTimes(1); + expect(obs.unsubscribeSpies[0]).toHaveBeenCalledTimes(1); + }); + }); + + describe('(on error) should clean up, log the error and recover', () => { + let logger: MockLogger; + + beforeEach(() => logger = TestBed.get(Logger)); + + it('when `prepareTitleAndTocSpy()` fails', async () => { + const error = Error('Typical `prepareTitleAndToc()` error'); + prepareTitleAndTocSpy.and.callFake(() => { + expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); + throw error; + }); + + await doRender('Some content', 'foo'); + + expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); + expect(swapViewsSpy).not.toHaveBeenCalled(); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + expect(logger.output.error).toEqual([ + [jasmine.any(Error)] + ]); + expect(logger.output.error[0][0].message).toEqual(`[DocViewer] Error preparing document 'foo': ${error.stack}`); + expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); + }); + + it('when `EmbedComponentsService.embedInto()` fails', async () => { + const error = Error('Typical `embedInto()` error'); + loadElementsSpy.and.callFake(() => { + expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); + throw error; + }); + + await doRender('Some content', 'bar'); + + expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); + expect(loadElementsSpy).toHaveBeenCalledTimes(1); + expect(swapViewsSpy).not.toHaveBeenCalled(); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + expect(logger.output.error).toEqual([ + [jasmine.any(Error)] + ]); + expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); + }); + + it('when `swapViews()` fails', async () => { + const error = Error('Typical `swapViews()` error'); + swapViewsSpy.and.callFake(() => { + expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); + throw error; + }); + + await doRender('Some content', 'qux'); + + expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1); + expect(swapViewsSpy).toHaveBeenCalledTimes(1); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + expect(logger.output.error).toEqual([ + [jasmine.any(Error)] + ]); + expect(logger.output.error[0][0].message).toEqual(`[DocViewer] Error preparing document 'qux': ${error.stack}`); + expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); + }); + + it('when something fails with non-Error', async () => { + const error = 'Typical string error'; + swapViewsSpy.and.callFake(() => { + expect(docViewer.nextViewContainer.innerHTML).not.toBe(''); + throw error; + }); + + await doRender('Some content', 'qux'); + + expect(swapViewsSpy).toHaveBeenCalledTimes(1); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + expect(logger.output.error).toEqual([ + [jasmine.any(Error)] + ]); + expect(logger.output.error[0][0].message).toEqual(`[DocViewer] Error preparing document 'qux': ${error}`); + expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' }); + }); + }); + + describe('(events)', () => { + it('should emit `docReady` after loading elements', async () => { + const onDocReadySpy = jasmine.createSpy('onDocReady'); + docViewer.docReady.subscribe(onDocReadySpy); + + await doRender('Some content'); + + expect(onDocReadySpy).toHaveBeenCalledTimes(1); + expect(loadElementsSpy).toHaveBeenCalledBefore(onDocReadySpy); + }); + + it('should emit `docReady` before swapping views', async () => { + const onDocReadySpy = jasmine.createSpy('onDocReady'); + docViewer.docReady.subscribe(onDocReadySpy); + + await doRender('Some content'); + + expect(onDocReadySpy).toHaveBeenCalledTimes(1); + expect(onDocReadySpy).toHaveBeenCalledBefore(swapViewsSpy); + }); + + it('should emit `docRendered` after swapping views', async () => { + const onDocRenderedSpy = jasmine.createSpy('onDocRendered'); + docViewer.docRendered.subscribe(onDocRenderedSpy); + + await doRender('Some content'); + + expect(onDocRenderedSpy).toHaveBeenCalledTimes(1); + expect(swapViewsSpy).toHaveBeenCalledBefore(onDocRenderedSpy); + }); + }); + }); + + describe('#swapViews()', () => { + let oldCurrViewContainer: HTMLElement; + let oldNextViewContainer: HTMLElement; + + const doSwapViews = (cb?: () => void) => + new Promise((resolve, reject) => + docViewer.swapViews(cb).subscribe(resolve, reject)); + + beforeEach(() => { + oldCurrViewContainer = docViewer.currViewContainer; + oldNextViewContainer = docViewer.nextViewContainer; + + oldCurrViewContainer.innerHTML = 'Current view'; + oldNextViewContainer.innerHTML = 'Next view'; + + docViewerEl.appendChild(oldCurrViewContainer); + + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); + }); + + [true, false].forEach(animationsEnabled => { + describe(`(animationsEnabled: ${animationsEnabled})`, () => { + beforeEach(() => DocViewerComponent.animationsEnabled = animationsEnabled); + afterEach(() => DocViewerComponent.animationsEnabled = true); + + [true, false].forEach(noAnimations => { + describe(`(.${NO_ANIMATIONS}: ${noAnimations})`, () => { + beforeEach(() => docViewerEl.classList[noAnimations ? 'add' : 'remove'](NO_ANIMATIONS)); + + it('should return an observable', done => { + docViewer.swapViews().subscribe(done, done.fail); + }); + + it('should swap the views', async () => { + await doSwapViews(); + + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + expect(docViewer.currViewContainer).toBe(oldNextViewContainer); + expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer); + + await doSwapViews(); + + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); + expect(docViewer.currViewContainer).toBe(oldCurrViewContainer); + expect(docViewer.nextViewContainer).toBe(oldNextViewContainer); + }); + + it('should emit `docRemoved` after removing the leaving view', async () => { + const onDocRemovedSpy = jasmine.createSpy('onDocRemoved').and.callFake(() => { + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); + }); + + docViewer.docRemoved.subscribe(onDocRemovedSpy); + + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); + + await doSwapViews(); + + expect(onDocRemovedSpy).toHaveBeenCalledTimes(1); + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + }); + + it('should not emit `docRemoved` if the leaving view is already removed', async () => { + const onDocRemovedSpy = jasmine.createSpy('onDocRemoved'); + + docViewer.docRemoved.subscribe(onDocRemovedSpy); + docViewerEl.removeChild(oldCurrViewContainer); + + await doSwapViews(); + + expect(onDocRemovedSpy).not.toHaveBeenCalled(); + }); + + it('should emit `docInserted` after inserting the entering view', async () => { + const onDocInsertedSpy = jasmine.createSpy('onDocInserted').and.callFake(() => { + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + }); + + docViewer.docInserted.subscribe(onDocInsertedSpy); + + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); + + await doSwapViews(); + + expect(onDocInsertedSpy).toHaveBeenCalledTimes(1); + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + }); + + it('should call the callback after inserting the entering view', async () => { + const onInsertedCb = jasmine.createSpy('onInsertedCb').and.callFake(() => { + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + }); + const onDocInsertedSpy = jasmine.createSpy('onDocInserted'); + + docViewer.docInserted.subscribe(onDocInsertedSpy); + + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(true); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(false); + + await doSwapViews(onInsertedCb); + + expect(onInsertedCb).toHaveBeenCalledTimes(1); + expect(onInsertedCb).toHaveBeenCalledBefore(onDocInsertedSpy); + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + }); + + it('should empty the previous view', async () => { + await doSwapViews(); + + expect(docViewer.currViewContainer.innerHTML).toBe('Next view'); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + + docViewer.nextViewContainer.innerHTML = 'Next view 2'; + await doSwapViews(); + + expect(docViewer.currViewContainer.innerHTML).toBe('Next view 2'); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + }); + + if (animationsEnabled && !noAnimations) { + // Only test this when there are animations. Without animations, the views are swapped + // synchronously, so there is no need (or way) to abort. + it('should abort swapping if the returned observable is unsubscribed from', async () => { + docViewer.swapViews().subscribe().unsubscribe(); + await doSwapViews(); + + // Since the first call was cancelled, only one swapping should have taken place. + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + expect(docViewer.currViewContainer).toBe(oldNextViewContainer); + expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer); + expect(docViewer.currViewContainer.innerHTML).toBe('Next view'); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + }); + } else { + it('should swap views synchronously when animations are disabled', () => { + const cbSpy = jasmine.createSpy('cb'); + + docViewer.swapViews(cbSpy).subscribe(); + + expect(cbSpy).toHaveBeenCalledTimes(1); + expect(docViewerEl.contains(oldCurrViewContainer)).toBe(false); + expect(docViewerEl.contains(oldNextViewContainer)).toBe(true); + expect(docViewer.currViewContainer).toBe(oldNextViewContainer); + expect(docViewer.nextViewContainer).toBe(oldCurrViewContainer); + expect(docViewer.currViewContainer.innerHTML).toBe('Next view'); + expect(docViewer.nextViewContainer.innerHTML).toBe(''); + }); + } + }); + }); + }); + }); + }); +}); diff --git a/projects/ngrx.io/src/app/layout/notification/notification.component.spec.ts b/projects/ngrx.io/src/app/layout/notification/notification.component.spec.ts index b823ad518f..4cb48bc26e 100644 --- a/projects/ngrx.io/src/app/layout/notification/notification.component.spec.ts +++ b/projects/ngrx.io/src/app/layout/notification/notification.component.spec.ts @@ -1,135 +1,135 @@ -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { CurrentDateToken } from 'app/shared/current-date'; -import { NotificationComponent } from './notification.component'; -import { WindowToken } from 'app/shared/window'; - -describe('NotificationComponent', () => { - let component: NotificationComponent; - let fixture: ComponentFixture; - - function configTestingModule(now = new Date('2018-01-20')) { - TestBed.configureTestingModule({ - declarations: [TestComponent, NotificationComponent], - providers: [ - { provide: WindowToken, useClass: MockWindow }, - { provide: CurrentDateToken, useValue: now }, - ], - imports: [NoopAnimationsModule], - schemas: [NO_ERRORS_SCHEMA] - }); - } - - function createComponent() { - fixture = TestBed.createComponent(TestComponent); - const debugElement = fixture.debugElement.query(By.directive(NotificationComponent)); - component = debugElement.componentInstance; - component.ngOnInit(); - fixture.detectChanges(); - } - - describe('content projection', () => { - it('should display the message text', () => { - configTestingModule(); - createComponent(); - expect(fixture.nativeElement.innerHTML).toContain('Version 6 of Angular Now Available!'); - }); - - it('should render HTML elements', () => { - configTestingModule(); - createComponent(); - const button = fixture.debugElement.query(By.css('.action-button')); - expect(button.nativeElement.textContent).toEqual('Learn More'); - }); - - it('should process Angular directives', () => { - configTestingModule(); - createComponent(); - const badSpans = fixture.debugElement.queryAll(By.css('.bad')); - expect(badSpans.length).toEqual(0); - }); - }); - - it('should call dismiss() when the message link is clicked, if dismissOnContentClick is true', () => { - configTestingModule(); - createComponent(); - spyOn(component, 'dismiss'); - component.dismissOnContentClick = true; - const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement; - message.click(); - expect(component.dismiss).toHaveBeenCalled(); - }); - - it('should not call dismiss() when the message link is clicked, if dismissOnContentClick is false', () => { - configTestingModule(); - createComponent(); - spyOn(component, 'dismiss'); - component.dismissOnContentClick = false; - const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement; - message.click(); - expect(component.dismiss).not.toHaveBeenCalled(); - }); - - it('should call dismiss() when the close button is clicked', () => { - configTestingModule(); - createComponent(); - spyOn(component, 'dismiss'); - fixture.debugElement.query(By.css('button')).triggerEventHandler('click', null); - fixture.detectChanges(); - expect(component.dismiss).toHaveBeenCalled(); - }); - - it('should hide the notification when dismiss is called', () => { - configTestingModule(); - createComponent(); - expect(component.showNotification).toBe('show'); - component.dismiss(); - expect(component.showNotification).toBe('hide'); - }); - - it('should update localStorage key when dismiss is called', () => { - configTestingModule(); - createComponent(); - const setItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.setItem; - component.dismiss(); - expect(setItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018', 'hide'); - }); - - it('should not show the notification if the date is after the expiry date', () => { - configTestingModule(new Date('2018-01-23')); - createComponent(); - expect(component.showNotification).toBe('hide'); - }); - - it('should not show the notification if the there is a "hide" flag in localStorage', () => { - configTestingModule(); - const getItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.getItem; - getItemSpy.and.returnValue('hide'); - createComponent(); - expect(getItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018'); - expect(component.showNotification).toBe('hide'); - }); -}); - -@Component({ - template: ` - - - - This should not appear - Version 6 of Angular Now Available! - Learn More - - - ` -}) -class TestComponent { -} - -class MockWindow { - localStorage = jasmine.createSpyObj('localStorage', ['getItem', 'setItem']); -} +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { CurrentDateToken } from 'app/shared/current-date'; +import { NotificationComponent } from './notification.component'; +import { WindowToken } from 'app/shared/window'; + +describe('NotificationComponent', () => { + let component: NotificationComponent; + let fixture: ComponentFixture; + + function configTestingModule(now = new Date('2018-01-20')) { + TestBed.configureTestingModule({ + declarations: [TestComponent, NotificationComponent], + providers: [ + { provide: WindowToken, useClass: MockWindow }, + { provide: CurrentDateToken, useValue: now }, + ], + imports: [NoopAnimationsModule], + schemas: [NO_ERRORS_SCHEMA] + }); + } + + function createComponent() { + fixture = TestBed.createComponent(TestComponent); + const debugElement = fixture.debugElement.query(By.directive(NotificationComponent)); + component = debugElement.componentInstance; + component.ngOnInit(); + fixture.detectChanges(); + } + + describe('content projection', () => { + it('should display the message text', () => { + configTestingModule(); + createComponent(); + expect(fixture.nativeElement.innerHTML).toContain('Version 6 of Angular Now Available!'); + }); + + it('should render HTML elements', () => { + configTestingModule(); + createComponent(); + const button = fixture.debugElement.query(By.css('.action-button')); + expect(button.nativeElement.textContent).toEqual('Learn More'); + }); + + it('should process Angular directives', () => { + configTestingModule(); + createComponent(); + const badSpans = fixture.debugElement.queryAll(By.css('.bad')); + expect(badSpans.length).toEqual(0); + }); + }); + + it('should call dismiss() when the message link is clicked, if dismissOnContentClick is true', () => { + configTestingModule(); + createComponent(); + spyOn(component, 'dismiss'); + component.dismissOnContentClick = true; + const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement; + message.click(); + expect(component.dismiss).toHaveBeenCalled(); + }); + + it('should not call dismiss() when the message link is clicked, if dismissOnContentClick is false', () => { + configTestingModule(); + createComponent(); + spyOn(component, 'dismiss'); + component.dismissOnContentClick = false; + const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement; + message.click(); + expect(component.dismiss).not.toHaveBeenCalled(); + }); + + it('should call dismiss() when the close button is clicked', () => { + configTestingModule(); + createComponent(); + spyOn(component, 'dismiss'); + fixture.debugElement.query(By.css('button')).triggerEventHandler('click', null); + fixture.detectChanges(); + expect(component.dismiss).toHaveBeenCalled(); + }); + + it('should hide the notification when dismiss is called', () => { + configTestingModule(); + createComponent(); + expect(component.showNotification).toBe('show'); + component.dismiss(); + expect(component.showNotification).toBe('hide'); + }); + + it('should update localStorage key when dismiss is called', () => { + configTestingModule(); + createComponent(); + const setItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.setItem; + component.dismiss(); + expect(setItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018', 'hide'); + }); + + it('should not show the notification if the date is after the expiry date', () => { + configTestingModule(new Date('2018-01-23')); + createComponent(); + expect(component.showNotification).toBe('hide'); + }); + + it('should not show the notification if the there is a "hide" flag in localStorage', () => { + configTestingModule(); + const getItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.getItem; + getItemSpy.and.returnValue('hide'); + createComponent(); + expect(getItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018'); + expect(component.showNotification).toBe('hide'); + }); +}); + +@Component({ + template: ` + + + + This should not appear + Version 6 of Angular Now Available! + Learn More + + + ` +}) +class TestComponent { +} + +class MockWindow { + localStorage = jasmine.createSpyObj('localStorage', ['getItem', 'setItem']); +}