From 0d1e6e7559ec0a6dee68312d63d9b37a4f0285cd Mon Sep 17 00:00:00 2001 From: Danny Blue Date: Tue, 10 Apr 2018 10:25:34 -0400 Subject: [PATCH] feat(store): create action stream that shows the action lifecycle (#255) * feat(store): create action stream that shows the action lifecycle * feat(store): update unit test to show that all cbs are called correctly * feat(store): update docs * feat(store): remove ActionContext generic arg * chore: misc cleanup * feat(store): add ofActionErrored Operator * feat(store): update changelog * feat(store): update changelog --- CHANGELOG.md | 322 +++++++++++------- docs/advanced/action-handlers.md | 20 +- packages/store/src/actions-stream.ts | 13 +- packages/store/src/of-action.ts | 65 +++- packages/store/src/public_api.ts | 2 +- packages/store/src/state-factory.ts | 4 +- packages/store/src/state-stream.ts | 12 +- packages/store/src/store.ts | 21 +- packages/store/tests/action.spec.ts | 109 ++++-- packages/store/tests/dispatch.spec.ts | 2 +- packages/store/tests/state.spec.ts | 10 +- .../websocket-plugin/src/websocket-handler.ts | 10 +- yarn.lock | 241 +++++++------ 13 files changed, 505 insertions(+), 326 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67c4ea09f..008f2d5b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,248 +1,312 @@ # Master -- Fix: Observable dispatch issues #235 -- Fix: Websocket error when socket undefined -- Fix: Devtools not disabling -- Fix: Action Stream has too much exposed -- Chore: Loosen ng deps + +* Fix: Observable dispatch issues #235 +* Fix: Websocket error when socket undefined +* Fix: Devtools not disabling +* Fix: Action Stream has too much exposed +* Chore: Loosen ng deps + +### BREAKING + +* feat(store): create action stream that shows the action lifecycle #255 + +To keep the same behavior switch from the ofAction operator to the ofActionDispatched operator # 3.0.0-beta.0 4/5/18 -- BREAKING: Types are now required on actions -- Feature: Devtools can emit actions manually now -- Feature: Better integration w/ RX6 -- Fix: Various websocket fixes -- Fix: Fixes for `ofAction` -- Chore: Rename Beta Feature `takeLast` to `cancelUncompleted` + +* BREAKING: Types are now required on actions +* Feature: Devtools can emit actions manually now +* Feature: Better integration w/ RX6 +* Fix: Various websocket fixes +* Fix: Fixes for `ofAction` +* Chore: Rename Beta Feature `takeLast` to `cancelUncompleted` # 2.1.0-beta.9 4/4/18 -- Fix: Websocket and form errors caused by #212 + +* Fix: Websocket and form errors caused by #212 # 2.1.0-beta.8 4/4/18 -- Fix: Minification issues #212 + +* Fix: Minification issues #212 # 2.1.0-beta.7 4/3/18 -- Fix: Websocket plugin sending dupe objects + +* Fix: Websocket plugin sending dupe objects # 2.1.0-beta.6 4/3/18 -- Fix: Action types on objects not matching correctly + +* Fix: Action types on objects not matching correctly # 2.1.0-beta.5 4/3/18 -- Fix: Revert #206 -- Fix: Websocket send message correctly + +* Fix: Revert #206 +* Fix: Websocket send message correctly # 2.1.0-beta.4 4/3/18 -- Feature: Added `snapshot` method to store to get raw value -- Fix: Return `T` from `selectSnapshot` -- Fix: Minification issues #206 -- Fix: Websockets not emitting correctly + +* Feature: Added `snapshot` method to store to get raw value +* Fix: Return `T` from `selectSnapshot` +* Fix: Minification issues #206 +* Fix: Websockets not emitting correctly # 2.1.0-beta.3 4/3/18 -- Fix: Revert devtools trigger, due to circular + +* Fix: Revert devtools trigger, due to circular # 2.1.0-beta.2 4/2/18 -- Feature: Devtools can now trigger actions adhoc -- Fix: Websocket optional typekey + +* Feature: Devtools can now trigger actions adhoc +* Fix: Websocket optional typekey # 2.1.0-beta.1 4/2/18 -- Fix: Websocket pass options as partial + +* Fix: Websocket pass options as partial # 2.1.0-beta.0 4/2/18 -- Feature: Lifecycle events -- Feature: Forms plugin -- Fature: takeLast on actions -- Feature: Websocket plugin -- Feature: Snapshot selects + +* Feature: Lifecycle events +* Feature: Forms plugin +* Fature: takeLast on actions +* Feature: Websocket plugin +* Feature: Snapshot selects # 2.0.0 3/27/18 + First off, 2.0 is a huge change. I want to appologize to everyone about that but given all the community feedback and ideas, I really wanted to get these ideas in before it was too late and we were stuck on a API. I can promise that there will -not be this big of a breaking change after this release in the future without +not be this big of a breaking change after this release in the future without first deprecation periods. -- Fix: Prevent null exceptions when accessing state before loaded -- Fix: Move action stream to subject -- Fix: Ensure metadata exists for a Selector (#181) +* Fix: Prevent null exceptions when accessing state before loaded +* Fix: Move action stream to subject +* Fix: Ensure metadata exists for a Selector (#181) # 2.0.0-rc.24 3/26/18 -- Feature: Expose Init and Update Actions -- Fix: Devtools showing duplicate entries -- Fix: Storage plugin not populating for lazy loaded states + +* Feature: Expose Init and Update Actions +* Fix: Devtools showing duplicate entries +* Fix: Storage plugin not populating for lazy loaded states # 2.0.0-rc.23 3/26/18 -- Fix: Subscriptions firing twice + +* Fix: Subscriptions firing twice # 2.0.0-rc.22 3/26/18 -- BREAKING: Storage plugin fixes #154 -- Fix: AoT Regression in Storage Plugin + +* BREAKING: Storage plugin fixes #154 +* Fix: AoT Regression in Storage Plugin # 2.0.0-rc.21 3/25/18 -- BREAKING: Plugins are now their own packages -- Fix: Subscribe firing multiple times -- Fix: Devtools only passing type and payload + +* BREAKING: Plugins are now their own packages +* Fix: Subscribe firing multiple times +* Fix: Devtools only passing type and payload # 2.0.0-rc.20 3/23/18 -- BREAKING: We moved the npm package from `ngxs` to `@ngxs/store`! -- Fix: devtools not sending payload #132 -- Fix: better if condition for localstorage + +* BREAKING: We moved the npm package from `ngxs` to `@ngxs/store`! +* Fix: devtools not sending payload #132 +* Fix: better if condition for localstorage # 2.0.0-rc.19 3/23/18 -- Feature: add overloading to select method in Store #130 -- Fix: add empty options object, to prevent errors #131 -- Fix: feature-module bugs #135 + +* Feature: add overloading to select method in Store #130 +* Fix: add empty options object, to prevent errors #131 +* Fix: feature-module bugs #135 # 2.0.0-rc.18 3/20/18 -- Fix: Types on devtools + +* Fix: Types on devtools # 2.0.0-rc.17 3/20/18 -- Feature: Extend devtool options -- Feature: Jump to Action and Jump to State in Dev Tools + +* Feature: Extend devtool options +* Feature: Jump to Action and Jump to State in Dev Tools # 2.0.0-rc.16 3/20/18 -- Fix: Action stream not getting passed correct args, causing error in FF + +* Fix: Action stream not getting passed correct args, causing error in FF # 2.0.0-rc.15 3/20/18 -- Fix: Lazy load issue introduced by #126 + +* Fix: Lazy load issue introduced by #126 # 2.0.0-rc.14 3/20/18 -- Feature: NgxsLoggerPlugin log action payload if present -- Fix: Issues with feature states #126 + +* Feature: NgxsLoggerPlugin log action payload if present +* Fix: Issues with feature states #126 # 2.0.0-rc.13 3/20/18 -- Feature: State can listen to action multiple times + +* Feature: State can listen to action multiple times # 2.0.0-rc.12 3/20/18 -- Fix: Dev tools showing wrong state + +* Fix: Dev tools showing wrong state # 2.0.0-rc.11 3/20/18 -- BREAKING: Remove string selects, they re not type safe and bad idea -- Feature: Extend `store.select` to support class selectors -- Feature: Expose state stream for users to subscribe to -- Fix: Fix Subscribe dispatching twice #104 + +* BREAKING: Remove string selects, they re not type safe and bad idea +* Feature: Extend `store.select` to support class selectors +* Feature: Expose state stream for users to subscribe to +* Fix: Fix Subscribe dispatching twice #104 # 2.0.0-rc.10 3/19/18 -- BREAKING: Rename `EventStream` to `Actions` -- BREAKING: Rename plugins to have NGXS Prefix + +* BREAKING: Rename `EventStream` to `Actions` +* BREAKING: Rename plugins to have NGXS Prefix # 2.0.0-rc.9 3/18/18 -- Feature: Memoized Selectors -- Fix: Default to empty object if no default passed + +* Feature: Memoized Selectors +* Fix: Default to empty object if no default passed # 2.0.0-rc.8 3/18/18 -- Fix: Patch value not updating state + +* Fix: Patch value not updating state # 2.0.0-rc.7 3/18/18 -- Fix: Patch value patching wrong path + +* Fix: Patch value patching wrong path # 2.0.0-rc.6 3/18/18 -- Fix: topological sort -- Fix: defaults not working correctly if plain boolean/string/number + +* Fix: topological sort +* Fix: defaults not working correctly if plain boolean/string/number # 2.0.0-rc.5 3/18/18 -- Fix: patchState typings + +* Fix: patchState typings # 2.0.0-rc.4 3/18/18 -- Fix: patchState typings + +* Fix: patchState typings # 2.0.0-rc.3 3/18/18 -- Fix: Add typings for patchState + +* Fix: Add typings for patchState # 2.0.0-rc.2 3/18/18 -- Feature: Add `patchValue` to make updating state easier + +* Feature: Add `patchValue` to make updating state easier # 2.0.0-rc.1 3/18/18 + ## Bug Fixes -- Fix: Action `state` arguments cached when destructured, switch to `getState()` -- Fix: Class selectors not working with sub stores -- Fix: missing dispatch on state context interface + +* Fix: Action `state` arguments cached when destructured, switch to `getState()` +* Fix: Class selectors not working with sub stores +* Fix: missing dispatch on state context interface # 2.0.0-rc.0 3/17/18 -- Breaking: `@Store()` decorator is now `@State()` -- Breaking: `Ngxs` service is now `Store` -- Breaking: Stores should be now renamed to State. Before: `ZooStore` should be `ZooState` -- Breaking: `@Mutation` is gone in favor of just `@Action` -- Breaking: Action's first argument is state context object, `{ state, setState }` -- Breaking: You use `setState` to set the state now rather than returning it in actions -- Breaking: Events are now just called Actions -- Breaking: `ofEvent` is now called `ofAction` -- Breaking: Plugins `next` fn now returns an observable -- Breaking: Local Storage plugins removed Strategy in favor of passing your own engine -- Feature: Simplified APIs by removing Mutations, decreased boilerplate -- Feature: Added sub state capability -- Feature: Add `store.selectOnce()` shortcut function -- Feature: Better tpyings -- Feature: Add `dispatch` function in state context for easier dispatching -- Fix: `dispatch().subscribe()` now works correctly -- Fix: Promises now resolve correctly + +* Breaking: `@Store()` decorator is now `@State()` +* Breaking: `Ngxs` service is now `Store` +* Breaking: Stores should be now renamed to State. Before: `ZooStore` should be `ZooState` +* Breaking: `@Mutation` is gone in favor of just `@Action` +* Breaking: Action's first argument is state context object, `{ state, setState }` +* Breaking: You use `setState` to set the state now rather than returning it in actions +* Breaking: Events are now just called Actions +* Breaking: `ofEvent` is now called `ofAction` +* Breaking: Plugins `next` fn now returns an observable +* Breaking: Local Storage plugins removed Strategy in favor of passing your own engine +* Feature: Simplified APIs by removing Mutations, decreased boilerplate +* Feature: Added sub state capability +* Feature: Add `store.selectOnce()` shortcut function +* Feature: Better tpyings +* Feature: Add `dispatch` function in state context for easier dispatching +* Fix: `dispatch().subscribe()` now works correctly +* Fix: Promises now resolve correctly # 1.5.3 2/12/18 -- Fix: Promises not emitting results + +* Fix: Promises not emitting results # 1.5.2 2/12/18 -- Fix: Devtools plugin not returning correct value + +* Fix: Devtools plugin not returning correct value # 1.5.1 2/11/18 -- Feature: Add `sessionStorage` strategy to local storage plugin + +* Feature: Add `sessionStorage` strategy to local storage plugin # 1.5.0 2/11/18 -- Feature: Updated Plugin System -- Feature: Add generics to store -- Feature: Implement global error handling -- Fix: Improve DI for lazy loadedd stores -- Fix: Fix dev tools showing previous state -- Chore: Remove redux dev tools by default + +* Feature: Updated Plugin System +* Feature: Add generics to store +* Feature: Implement global error handling +* Fix: Improve DI for lazy loadedd stores +* Fix: Fix dev tools showing previous state +* Chore: Remove redux dev tools by default # 1.4.8 2/5/18 -- Fix: Typo in return + +* Fix: Typo in return # 1.4.7 2/5/18 -- Fix: Catch multiple stores being init'd -- Fix: Clone defaults to prevent mutations + +* Fix: Catch multiple stores being init'd +* Fix: Clone defaults to prevent mutations # 1.4.6 2/4/18 -- Fix: Plugin injector errors + +* Fix: Plugin injector errors # 1.4.5 2/4/18 -- Fix: Store injector errors -- Fix: Empty local storage throwing null error + +* Fix: Store injector errors +* Fix: Empty local storage throwing null error # 1.4.4 2/4/18 -- Fix: Stores injector errors + +* Fix: Stores injector errors # 1.4.3 2/4/18 -- Fix: Stores init'd twice + +* Fix: Stores init'd twice # 1.4.2 2/4/18 -- Fix: Feature stores throwing errors + +* Fix: Feature stores throwing errors # 1.4.1 2/4/18 -- Fix: Misc type improvement -- Fix: `forRoot` plugins not working properly -- Fix: LocalStorage plugin name spelling + +* Fix: Misc type improvement +* Fix: `forRoot` plugins not working properly +* Fix: LocalStorage plugin name spelling # 1.4.0 2/3/18 -- Feature: Composition + +* Feature: Composition # 1.3.0 2/3/18 -- Feature: Localstore plugin -- Fix: Better dev tools init + +* Feature: Localstore plugin +* Fix: Better dev tools init # 1.2.1 2/3/18 -- Fix: Dev tools init -- Fix: Plugins not recieving proper context -- Fix: Allow multiple forFeature + +* Fix: Dev tools init +* Fix: Plugins not recieving proper context +* Fix: Allow multiple forFeature # 1.2.0 2/3/18 -- Feature: Dev Tools Integration -- Chore: Tests! -- Fix: Better builds + +* Feature: Dev Tools Integration +* Chore: Tests! +* Fix: Better builds # 1.1.0 2/3/18 -- Fix: Export plugin interface + +* Fix: Export plugin interface # 1.1.0 2/3/18 -- Feature: Plugins improvements -- Feature: Init event -- Feature: Logger plugin + +* Feature: Plugins improvements +* Feature: Init event +* Feature: Logger plugin # 1.0.4 2/2/18 -- Inital release! + +* Inital release! diff --git a/docs/advanced/action-handlers.md b/docs/advanced/action-handlers.md index 6953aa0be..8a956b409 100644 --- a/docs/advanced/action-handlers.md +++ b/docs/advanced/action-handlers.md @@ -1,19 +1,29 @@ # Action Handlers + Event sourcing involves modeling the state changes made by applications as an immutable sequence or “log” of events. Instead of focussing on current state, you focus on the changes that have occurred over time. It is the practice of modelling your system as a sequence of events. In NGXS, we called this Action Handlers. Typically actions directly correspond to state changes but it can be difficult to always make your component react -based on state. As a side effect of this paradigm, we end up creating lots of intermediate state properrties +based on state. As a side effect of this paradigm, we end up creating lots of intermediate state properties to do things like reset a form/etc. Action handlers let us drive our components based on state along with events that emit. For example, if we were to have a shopping cart and we were to delete an item out of it you might want to show a notification that it was successfully removed. In a pure state driven application, you might create some kind -of message array to make the dialog show up. With Action Handlers, we can respond to the action directly. +of message array to make the dialog show up. With Action Handlers, we can respond to the action directly. The action handler is a observable that recieves all the actions dispatched before the state takes any action on it. -Since its an observable, we can use pipes and we created a `ofAction` pipe to make filtering the actions easier. + +Actions in NGXS also have a lifecycle. Since any potential action can be async we tag actions showing whether they are "DISPATCHED" or "COMPLETED". This gives you the ability to react to actions at different points in their existence. + +Since its an observable, we can use pipes and created 4 of them. + +* `ofAction`: triggers when any of the below lifecycle events happen +* `ofActionDispatched`: triggers when an action has been dispatched but NOT when it completes +* `ofActionCompleted`: triggers when an action has been completed but NOT when it is dispatched +* `ofActionErrored`: triggers when an action has caused an error to be thrown + Below is a action handler that filters for `RouteNavigate` actions and then tells the router to navigate to that route. @@ -24,7 +34,7 @@ import { Actions, ofAction } from '@ngxs/store'; export class RouteHandler { constructor(private router: Router, private actions$: Actions) { this.actions$ - .pipe(ofAction(RouteNavigate)) + .pipe(ofActionDispatched(RouteNavigate)) .subscribe(({ payload }) => this.router.navigate([payload])); } } @@ -38,7 +48,7 @@ export class CartComponent { constructor(private actions$: Actions) {} ngOnInit() { - this.actions$.pipe(ofAction(CartDeleteSuccess)).subscribe(() => alert('Item deleted')); + this.actions$.pipe(ofActionComplete(CartDelete)).subscribe(() => alert('Item deleted')); } } ``` diff --git a/packages/store/src/actions-stream.ts b/packages/store/src/actions-stream.ts index da0124cc2..83c482011 100644 --- a/packages/store/src/actions-stream.ts +++ b/packages/store/src/actions-stream.ts @@ -2,11 +2,22 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; +export enum ActionStatus { + Dispatched = 'DISPATCHED', + Completed = 'COMPLETED', + Errored = 'Errored' +} + +export interface ActionContext { + status: ActionStatus; + action: any; +} + /** * Internal Action stream that is emitted anytime an action is dispatched. */ @Injectable() -export class InternalActions extends Subject {} +export class InternalActions extends Subject {} /** * Action stream that is emitted anytime an action is dispatched. diff --git a/packages/store/src/of-action.ts b/packages/store/src/of-action.ts index 183560f28..9b8930e79 100644 --- a/packages/store/src/of-action.ts +++ b/packages/store/src/of-action.ts @@ -1,6 +1,10 @@ import { filter } from 'rxjs/operators'; import { getActionTypeFromInstance } from './utils'; +import { ActionContext, ActionStatus } from './actions-stream'; +import { map } from 'rxjs/operators/map'; +import { Observable } from 'rxjs/Observable'; + // TODO: Fix when RXJS 6 is released // import { OperatorFunction } from 'rxjs/interfaces'; @@ -9,12 +13,65 @@ export function ofAction(...allowedTypes); /** * RxJS operator for selecting out specific actions. + * + * This will grab actions that have just been dispatched as well as actions that have completed */ export function ofAction(...allowedTypes: any[]) { - const allowedMap = allowedTypes.reduce((acc: any, klass: any) => { - acc[klass.type] = true; + return ofActionOperator(allowedTypes); +} + +/** + * RxJS operator for selecting out specific actions. + * + * This will ONLY grab actions that have just been dispatched + */ +export function ofActionDispatched(...allowedTypes: any[]) { + return ofActionOperator(allowedTypes, ActionStatus.Dispatched); +} + +/** + * RxJS operator for selecting out specific actions. + * + * This will ONLY grab actions that have just been completed + */ +export function ofActionComplete(...allowedTypes: any[]) { + return ofActionOperator(allowedTypes, ActionStatus.Completed); +} + +/** + * RxJS operator for selecting out specific actions. + * + * This will ONLY grab actions that have thrown an error + */ +export function ofActionErrored(...allowedTypes: any[]) { + return ofActionOperator(allowedTypes, ActionStatus.Errored); +} + +function ofActionOperator(allowedTypes: any[], status?: ActionStatus) { + const allowedMap = createAllowedMap(allowedTypes); + + return function(o: Observable) { + return o.pipe(filterStatus(allowedMap, status), mapAction()); + }; +} + +function filterStatus(allowedTypes: { [key: string]: boolean }, status?: ActionStatus) { + return filter((ctx: ActionContext) => { + const actionType = getActionTypeFromInstance(ctx.action); + const type = allowedTypes[actionType]; + + return status ? type && ctx.status === status : type; + }); +} + +function mapAction() { + return map((ctx: ActionContext) => ctx.action); +} + +function createAllowedMap(types: any[]): { [key: string]: boolean } { + return types.reduce((acc: any, klass: any) => { + acc[getActionTypeFromInstance(klass)] = true; + return acc; }, {}); - - return filter(action => allowedMap[getActionTypeFromInstance(action)]); } diff --git a/packages/store/src/public_api.ts b/packages/store/src/public_api.ts index 2de36113f..c8e008772 100644 --- a/packages/store/src/public_api.ts +++ b/packages/store/src/public_api.ts @@ -4,7 +4,7 @@ export { Store } from './store'; export { State } from './state'; export { Select } from './select'; export { Actions } from './actions-stream'; -export { ofAction } from './of-action'; +export { ofAction, ofActionComplete, ofActionDispatched } from './of-action'; export { NgxsPlugin, NgxsPluginFn, StateContext } from './symbols'; export { Selector } from './selector'; export { getActionTypeFromInstance, actionMatcher } from './utils'; diff --git a/packages/store/src/state-factory.ts b/packages/store/src/state-factory.ts index d30f15dea..9a677d58c 100644 --- a/packages/store/src/state-factory.ts +++ b/packages/store/src/state-factory.ts @@ -8,7 +8,7 @@ import { forkJoin } from 'rxjs/observable/forkJoin'; import { META_KEY, StateContext, ActionOptions } from './symbols'; import { topologicalSort, buildGraph, findFullParentPath, nameToState, MetaDataModel, isObject } from './internals'; import { getActionTypeFromInstance, setValue, getValue } from './utils'; -import { ofAction } from './of-action'; +import { ofActionDispatched } from './of-action'; @Injectable() export class StateFactory { @@ -117,7 +117,7 @@ export class StateFactory { if (result instanceof Observable) { result = result.pipe( (actionMeta.options).cancelUncompleted - ? takeUntil(actions$.pipe(ofAction(action.constructor))) + ? takeUntil(actions$.pipe(ofActionDispatched(action))) : map(r => r) ); // act like a noop } else { diff --git a/packages/store/src/state-stream.ts b/packages/store/src/state-stream.ts index ba2b0fac7..e992b539d 100644 --- a/packages/store/src/state-stream.ts +++ b/packages/store/src/state-stream.ts @@ -1,4 +1,4 @@ -import { Injectable, Optional, SkipSelf } from '@angular/core'; +import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; /** @@ -6,15 +6,7 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject'; */ @Injectable() export class StateStream extends BehaviorSubject { - constructor( - @Optional() - @SkipSelf() - parent: StateStream - ) { + constructor() { super({}); - - if (parent) { - Object.assign(this, parent); - } } } diff --git a/packages/store/src/store.ts b/packages/store/src/store.ts index 49ef021c2..b6338cfa0 100644 --- a/packages/store/src/store.ts +++ b/packages/store/src/store.ts @@ -5,9 +5,10 @@ import { distinctUntilChanged, catchError, take, shareReplay } from 'rxjs/operat import { forkJoin } from 'rxjs/observable/forkJoin'; import { map } from 'rxjs/operators/map'; import { of } from 'rxjs/observable/of'; +import { tap } from 'rxjs/operators/tap'; import { compose } from './compose'; -import { InternalActions } from './actions-stream'; +import { InternalActions, ActionStatus } from './actions-stream'; import { StateFactory } from './state-factory'; import { StateStream } from './state-stream'; import { PluginManager } from './plugin-manager'; @@ -105,7 +106,7 @@ export class Store { return this._stateStream.getValue(); } - private _dispatch(action): Observable { + private _dispatch(action: any): Observable { const prevState = this._stateStream.getValue(); const plugins = this._pluginManager.plugins; @@ -116,7 +117,7 @@ export class Store { this._stateStream.next(nextState); } - this._actions.next(nextAction); + this._actions.next({ action, status: ActionStatus.Dispatched }); return this._storeFactory .invokeActions( @@ -126,7 +127,19 @@ export class Store { this._actions, action ) - .pipe(map(() => this._stateStream.getValue())); + .pipe( + tap(() => { + this._actions.next({ action, status: ActionStatus.Completed }); + }), + map(() => { + return this._stateStream.getValue(); + }), + catchError(err => { + this._actions.next({ action, status: ActionStatus.Errored }); + + return of(err); + }) + ); } ])(prevState, action) as Observable).pipe(shareReplay()); } diff --git a/packages/store/tests/action.spec.ts b/packages/store/tests/action.spec.ts index 4d79034ac..4e2d27c91 100644 --- a/packages/store/tests/action.spec.ts +++ b/packages/store/tests/action.spec.ts @@ -1,65 +1,100 @@ +import { TestBed } from '@angular/core/testing'; + import { Action } from '../src/action'; import { State } from '../src/state'; import { META_KEY } from '../src/symbols'; -import { timer } from 'rxjs/observable/timer'; -import { TestBed } from '@angular/core/testing'; import { NgxsModule } from '../src/module'; import { Store } from '../src/store'; import { Actions } from '../src/actions-stream'; -import { tap } from 'rxjs/operators'; +import { ofActionComplete, ofActionDispatched, ofAction, ofActionErrored } from '../src/of-action'; +import { _throw } from 'rxjs/observable/throw'; describe('Action', () => { - it('supports multiple actions', () => { - class Action1 { - static type = 'ACTION 1'; - } + let store: Store; + let actions: Actions; - class Action2 { - static type = 'ACTION 2'; - } + class Action1 { + static type = 'ACTION 1'; + } + + class Action2 { + static type = 'ACTION 2'; + } + + class ErrorAction { + static type = 'ErrorAction'; + } + + @State({ + name: 'bar' + }) + class BarStore { + @Action([Action1, Action2]) + foo() {} - @State({ - name: 'bar' - }) - class BarStore { - @Action([Action1, Action2]) - foo() {} + @Action(ErrorAction) + onError() { + return _throw(new Error('this is a test error')); } + } + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NgxsModule.forRoot([BarStore])] + }); + + store = TestBed.get(Store); + actions = TestBed.get(Actions); + }); + + it('supports multiple actions', () => { const meta = BarStore[META_KEY]; expect(meta.actions[Action1.type]).toBeDefined(); expect(meta.actions[Action2.type]).toBeDefined(); }); -}); -describe('Actions', () => { - it('basic', () => { - let happened = false; + it('calls actions on dispatch and on complete', () => { + let callbackCalledCount = 0; - class Action1 { - static type = 'ACTION 1'; - } - - @State({ name: 'Bar' }) - class BarStore { - @Action(Action1) - bar() { - return timer(10).pipe(tap(() => (happened = true))); - } - } + actions.pipe(ofAction(Action1)).subscribe(action => { + callbackCalledCount++; + }); - TestBed.configureTestingModule({ - imports: [NgxsModule.forRoot([BarStore])] + actions.pipe(ofActionDispatched(Action1)).subscribe(action => { + callbackCalledCount++; }); - const store = TestBed.get(Store); - const actions = TestBed.get(Actions); + actions.pipe(ofActionComplete(Action1)).subscribe(action => { + callbackCalledCount++; - actions.subscribe(action => { - expect(happened).toBeFalsy(); + expect(callbackCalledCount).toBe(4); }); store.dispatch(new Action1()); }); + + it('calls only the dispatched and error action', () => { + let callbackCalledCount = 0; + + actions.pipe(ofAction(Action1)).subscribe(action => { + callbackCalledCount++; + }); + + actions.pipe(ofActionDispatched(ErrorAction)).subscribe(action => { + callbackCalledCount++; + }); + + actions.pipe(ofActionComplete(ErrorAction)).subscribe(action => { + callbackCalledCount++; + }); + + actions.pipe(ofActionErrored(ErrorAction)).subscribe(action => { + callbackCalledCount++; + + expect(callbackCalledCount).toBe(2); + }); + + store.dispatch(new ErrorAction()); + }); }); diff --git a/packages/store/tests/dispatch.spec.ts b/packages/store/tests/dispatch.spec.ts index 23d98da42..30be6d606 100644 --- a/packages/store/tests/dispatch.spec.ts +++ b/packages/store/tests/dispatch.spec.ts @@ -19,7 +19,7 @@ describe('Dispatch', () => { } it( - 'should correctly dispatch the event', + 'should correctly dispatch the action', async(() => { @State({ name: 'counter', diff --git a/packages/store/tests/state.spec.ts b/packages/store/tests/state.spec.ts index 627a0d912..a4450b6df 100644 --- a/packages/store/tests/state.spec.ts +++ b/packages/store/tests/state.spec.ts @@ -1,6 +1,6 @@ -import { ensureStoreMetadata } from '../src/internals'; import { State } from '../src/state'; import { Action } from '../src/action'; +import { META_KEY } from '../src/symbols'; describe('Store', () => { it('describes correct name', () => { @@ -9,7 +9,7 @@ describe('Store', () => { }) class BarState {} - const meta = ensureStoreMetadata(BarState); + const meta = BarState[META_KEY]; expect(meta.name).toBe('moo'); }); @@ -37,8 +37,8 @@ describe('Store', () => { drink() {} } - const meta = ensureStoreMetadata(BarS2tore); - expect(meta.actions[Eat['type']]).toBeDefined(); - expect(meta.actions[Drink['type']]).toBeDefined(); + const meta = BarS2tore[META_KEY]; + expect(meta.actions[Eat.type]).toBeDefined(); + expect(meta.actions[Drink.type]).toBeDefined(); }); }); diff --git a/packages/websocket-plugin/src/websocket-handler.ts b/packages/websocket-plugin/src/websocket-handler.ts index 798089943..278f12bd6 100644 --- a/packages/websocket-plugin/src/websocket-handler.ts +++ b/packages/websocket-plugin/src/websocket-handler.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@angular/core'; import { WebSocketSubject } from './websocket-subject'; -import { Actions, Store } from '@ngxs/store'; +import { Actions, Store, getValue, ofActionDispatched } from '@ngxs/store'; import { ConnectWebSocket, DisconnectWebSocket, @@ -9,8 +9,6 @@ import { NgxsWebsocketPluginOptions, WebsocketMessageError } from './symbols'; -import { filter } from 'rxjs/operators'; -import { getValue } from '@ngxs/store'; @Injectable() export class WebSocketHandler { @@ -20,9 +18,9 @@ export class WebSocketHandler { socket: WebSocketSubject, @Inject(NGXS_WEBSOCKET_OPTIONS) config: NgxsWebsocketPluginOptions ) { - actions.pipe(filter(t => t instanceof ConnectWebSocket)).subscribe(event => socket.connect(event.payload)); - actions.pipe(filter(t => t instanceof DisconnectWebSocket)).subscribe(event => socket.disconnect()); - actions.pipe(filter(t => t instanceof SendWebSocketMessage)).subscribe(({ payload }) => socket.send(payload)); + actions.pipe(ofActionDispatched(ConnectWebSocket)).subscribe(event => socket.connect(event.payload)); + actions.pipe(ofActionDispatched(DisconnectWebSocket)).subscribe(event => socket.disconnect()); + actions.pipe(ofActionDispatched(SendWebSocketMessage)).subscribe(({ payload }) => socket.send(payload)); socket.subscribe( msg => { const type = getValue(msg, config.typeKey); diff --git a/yarn.lock b/yarn.lock index 22cd88de7..3e373cc24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,8 +28,8 @@ rxjs "^5.5.6" "@angular/cli@~1.7.3": - version "1.7.3" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.7.3.tgz#ac917b69240bb1b340421e9f985135890e0d53b4" + version "1.7.4" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.7.4.tgz#b6c31b5fc6f8ea07e55b1b01a26422f5358a4ea6" dependencies: "@angular-devkit/build-optimizer" "0.3.2" "@angular-devkit/core" "0.3.2" @@ -154,14 +154,14 @@ tslib "^1.7.1" "@babel/code-frame@^7.0.0-beta.35": - version "7.0.0-beta.42" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.42.tgz#a9c83233fa7cd06b39dc77adbb908616ff4f1962" + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9" dependencies: - "@babel/highlight" "7.0.0-beta.42" + "@babel/highlight" "7.0.0-beta.44" -"@babel/highlight@7.0.0-beta.42": - version "7.0.0-beta.42" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.42.tgz#a502a1c0d6f99b2b0e81d468a1b0c0e81e3f3623" +"@babel/highlight@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5" dependencies: chalk "^2.0.0" esutils "^2.0.2" @@ -353,8 +353,8 @@ "@types/jasmine" "*" "@types/node@*", "@types/node@~9.6.0": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.0.tgz#d3480ee666df9784b1001a1872a2f6ccefb6c2d7" + version "9.6.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.2.tgz#e49ac1adb458835e95ca6487bc20f916b37aff23" "@types/semver@^5.5.0": version "5.5.0" @@ -716,8 +716,8 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" atob@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + version "2.1.0" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.0.tgz#ab2b150e51d7b122b9efc8d7340c06b6c41076bc" autoprefixer@^7.1.1, autoprefixer@^7.2.3: version "7.2.6" @@ -739,8 +739,8 @@ aws-sign2@~0.7.0: resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" aws4@^1.2.1, aws4@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + version "1.7.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" axios@^0.15.3: version "0.15.3" @@ -995,16 +995,14 @@ braces@^1.8.2: repeat-element "^1.1.2" braces@^2.3.0, braces@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" - define-property "^1.0.0" extend-shallow "^2.0.1" fill-range "^4.0.0" isobject "^3.0.1" - kind-of "^6.0.2" repeat-element "^1.1.2" snapdragon "^0.8.1" snapdragon-node "^2.0.1" @@ -1016,8 +1014,8 @@ brorand@^1.0.1: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" browser-pack@^6.0.1: - version "6.0.4" - resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.4.tgz#9a73beb3b48f9e36868be007b64400102c04a99f" + version "6.1.0" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774" dependencies: JSONStream "^1.0.3" combine-source-map "~0.8.0" @@ -1037,8 +1035,8 @@ browser-resolve@^1.11.0, browser-resolve@^1.11.2, browser-resolve@^1.7.0: resolve "1.1.7" browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f" + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" dependencies: buffer-xor "^1.0.3" cipher-base "^1.0.0" @@ -1312,8 +1310,8 @@ camelcase@^4.0.0, camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805: - version "1.0.30000820" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000820.tgz#6e36ee75187a2c83d26d6504a1af47cc580324d2" + version "1.0.30000824" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000824.tgz#de3bc1ba0bff4937302f8cb2a8632a8cc1c07f9a" capture-stack-trace@^1.0.0: version "1.0.0" @@ -1543,16 +1541,7 @@ combine-lists@^1.0.0: dependencies: lodash "^4.5.0" -combine-source-map@~0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e" - dependencies: - convert-source-map "~1.1.0" - inline-source-map "~0.6.0" - lodash.memoize "~3.0.3" - source-map "~0.5.3" - -combine-source-map@~0.8.0: +combine-source-map@^0.8.0, combine-source-map@~0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" dependencies: @@ -1652,8 +1641,8 @@ concat-stream@~1.5.0, concat-stream@~1.5.1: typedarray "~0.0.5" configstore@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" + version "3.1.2" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" dependencies: dot-prop "^4.1.0" graceful-fs "^4.1.2" @@ -1693,10 +1682,6 @@ content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" -content-type-parser@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" - content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -1709,8 +1694,8 @@ conventional-changelog-angular@^1.3.3: q "^1.5.1" conventional-commits-parser@^2.1.0: - version "2.1.6" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-2.1.6.tgz#e594bbd8342d3e6758aa0344ca074719d69a7dc0" + version "2.1.7" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-2.1.7.tgz#eca45ed6140d72ba9722ee4132674d639e644e8e" dependencies: JSONStream "^1.0.4" is-text-path "^1.0.0" @@ -1765,8 +1750,8 @@ copy-webpack-plugin@~4.4.1: serialize-javascript "^1.4.0" core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" + version "2.5.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b" core-object@^3.1.0: version "3.1.5" @@ -1980,6 +1965,14 @@ data-uri-to-buffer@1: version "1.2.0" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" +data-urls@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.0.0.tgz#24802de4e81c298ea8a9388bb0d8e461c774684f" + dependencies: + abab "^1.0.4" + whatwg-mimetype "^2.0.0" + whatwg-url "^6.4.0" + date-fns@^1.27.2: version "1.29.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" @@ -2314,8 +2307,8 @@ ejs@^2.5.7: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.8.tgz#2ab6954619f225e6193b7ac5f7c39c48fefe4380" electron-to-chromium@^1.3.30: - version "1.3.40" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.40.tgz#1fbd6d97befd72b8a6f921dc38d22413d2f6fddf" + version "1.3.42" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.42.tgz#95c33bf01d0cc405556aec899fe61fd4d76ea0f9" elegant-spinner@^1.0.1: version "1.0.1" @@ -2438,8 +2431,8 @@ es-to-primitive@^1.1.1: is-symbol "^1.0.1" es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.41" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.41.tgz#bab3e982d750f0112f0cb9e6abed72c59eb33eb2" + version "0.10.42" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.42.tgz#8c07dd33af04d5dcd1310b5cef13bea63a89ba8d" dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" @@ -3112,8 +3105,8 @@ getpass@^0.1.1: assert-plus "^1.0.0" git-raw-commits@^1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-1.3.5.tgz#0951ae8dc80e5cee8ef54934db4ef65a6d161c60" + version "1.3.6" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-1.3.6.tgz#27c35a32a67777c1ecd412a239a6c19d71b95aff" dependencies: dargs "^4.0.1" lodash.template "^4.0.2" @@ -3437,14 +3430,13 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" html-minifier@^3.2.3: - version "3.5.12" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.12.tgz#6bfad4d0327f5b8d2b62f5854654ac3703b9b031" + version "3.5.14" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.14.tgz#88653b24b344274e3e3d7052f1541ebea054ac60" dependencies: camel-case "3.0.x" clean-css "4.1.x" commander "2.15.x" he "1.1.x" - ncname "1.0.x" param-case "2.1.x" relateurl "0.2.x" uglify-js "3.3.x" @@ -3477,7 +3469,7 @@ http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" -http-errors@1.6.2, http-errors@~1.6.2: +http-errors@1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" dependencies: @@ -3486,6 +3478,15 @@ http-errors@1.6.2, http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-parser-js@>=0.4.0: version "0.4.11" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.11.tgz#5b720849c650903c27e521633d94696ee95f3529" @@ -3661,14 +3662,15 @@ inline-source-map@~0.6.0: source-map "~0.5.3" insert-module-globals@^7.0.0: - version "7.0.4" - resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.4.tgz#1f144b807884507894bdbbb564c0d70075460251" + version "7.0.6" + resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.6.tgz#15a31d9d394e76d08838b9173016911d7fd4ea1b" dependencies: JSONStream "^1.0.3" - combine-source-map "~0.7.1" + combine-source-map "^0.8.0" concat-stream "^1.6.1" is-buffer "^1.1.0" lexical-scope "^1.2.0" + path-is-absolute "^1.0.1" process "~0.11.0" through2 "^2.0.0" xtend "^4.0.0" @@ -4060,8 +4062,8 @@ istanbul-api@^1.1.14: once "^1.4.0" istanbul-instrumenter-loader@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz#9f553923b22360bac95e617aaba01add1f7db0b2" + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz#9957bd59252b373fae5c52b7b5188e6fde2a0949" dependencies: convert-source-map "^1.5.0" istanbul-lib-instrument "^1.7.3" @@ -4271,17 +4273,16 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" jsdom@^11.5.1: - version "11.6.2" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.6.2.tgz#25d1ef332d48adf77fc5221fe2619967923f16bb" + version "11.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.7.0.tgz#8b45b657dae90d6d2d3a5f5d1126bb7102d0a172" dependencies: abab "^1.0.4" acorn "^5.3.0" acorn-globals "^4.1.0" array-equal "^1.0.0" - browser-process-hrtime "^0.1.2" - content-type-parser "^1.0.2" cssom ">= 0.3.2 < 0.4.0" cssstyle ">= 0.2.37 < 0.3.0" + data-urls "^1.0.0" domexception "^1.0.0" escodegen "^1.9.0" html-encoding-sniffer "^1.0.2" @@ -4297,6 +4298,7 @@ jsdom@^11.5.1: 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.0" ws "^4.0.0" xml-name-validator "^3.0.0" @@ -4314,8 +4316,8 @@ json-loader@^0.5.4: resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" json-parse-better-errors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" json-schema-traverse@^0.3.0: version "0.3.1" @@ -4494,8 +4496,8 @@ lcid@^1.0.0: invert-kv "^1.0.0" left-pad@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee" + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" less-loader@^4.0.5: version "4.1.0" @@ -4558,8 +4560,8 @@ license-webpack-plugin@^1.0.0: ejs "^2.5.7" lint-staged@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-7.0.0.tgz#57926c63201e7bd38ca0576d74391efa699b4a9d" + version "7.0.4" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-7.0.4.tgz#1aa7f27427e4c4c85d4d6524ac98aac10cbaf1b8" dependencies: app-root-path "^2.0.1" chalk "^2.3.1" @@ -4580,7 +4582,8 @@ lint-staged@^7.0.0: path-is-inside "^1.0.2" pify "^3.0.0" please-upgrade-node "^3.0.1" - staged-git-files "1.1.0" + staged-git-files "1.1.1" + string-argv "^0.0.2" stringify-object "^3.2.2" listr-silent-renderer@^1.1.1: @@ -5205,12 +5208,6 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" -ncname@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" - dependencies: - xml-char-classes "^1.0.0" - negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" @@ -5228,8 +5225,8 @@ next-tick@1: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" ng-packagr@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-2.4.1.tgz#27ab5ad933725a489c5eda7e5656ce5a945c39c2" + version "2.4.2" + resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-2.4.2.tgz#e74b744cad9e44aa7ddfdee5901b097527d03d23" dependencies: "@ngtools/json-schema" "^1.1.0" autoprefixer "^7.1.1" @@ -5258,7 +5255,7 @@ ng-packagr@^2.3.0: strip-bom "^3.0.0" stylus "^0.54.5" tar "^4.4.1" - uglify-js "^3.0.7" + uglify-js "^3.3.20" update-notifier "^2.3.0" no-case@^2.2.0: @@ -5803,7 +5800,7 @@ path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" -path-is-absolute@^1.0.0: +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -5964,8 +5961,8 @@ postcss-loader@^2.0.10: schema-utils "^0.4.0" postcss-url@^7.1.2, postcss-url@^7.3.0: - version "7.3.1" - resolved "https://registry.yarnpkg.com/postcss-url/-/postcss-url-7.3.1.tgz#b43ae0f0dae4cd06c831fa3aeac2d7a5b73754ed" + version "7.3.2" + resolved "https://registry.yarnpkg.com/postcss-url/-/postcss-url-7.3.2.tgz#5fea273807fb84b38c461c3c9a9e8abd235f7120" dependencies: mime "^1.4.1" minimatch "^3.0.4" @@ -6258,15 +6255,15 @@ read-pkg@^3.0.0: path-type "^3.0.0" "readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3: - version "2.3.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: 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.0.3" + string_decoder "~1.1.1" util-deprecate "~1.0.1" readable-stream@1.0: @@ -6570,8 +6567,8 @@ require-from-string@^1.1.0: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" require-from-string@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff" + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" require-main-filename@^1.0.1: version "1.0.1" @@ -6621,8 +6618,8 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2, resolve@^1.4.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.6.0.tgz#0fbd21278b27b4004481c395349e7aba60a9ff5c" + version "1.7.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.0.tgz#2bdf5374811207285df0df652b78f118ab8f3c5e" dependencies: path-parse "^1.0.5" @@ -6713,13 +6710,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^5.4.2, rxjs@^5.5.0, rxjs@^5.5.6: - version "5.5.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.7.tgz#afb3d1642b069b2fbf203903d6501d1acb4cda27" - dependencies: - symbol-observable "1.0.1" - -rxjs@^5.5.2: +rxjs@^5.4.2, rxjs@^5.5.0, rxjs@^5.5.2, rxjs@^5.5.6: version "5.5.8" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.8.tgz#b2b0809a57614ad6254c03d7446dea0d83ca3791" dependencies: @@ -7264,9 +7255,9 @@ stack-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.1.tgz#d4f33ab54e8e38778b0ca5cfd3b3afb12db68620" -staged-git-files@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.0.tgz#1a9bb131c1885601023c7aaddd3d54c22142c526" +staged-git-files@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.1.tgz#37c2218ef0d6d26178b1310719309a16a59f8f7b" static-extend@^0.1.1: version "0.1.2" @@ -7275,14 +7266,18 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.3.1 < 2", statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" +"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + stdout-stream@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" @@ -7350,6 +7345,10 @@ streamroller@^0.7.0: mkdirp "^0.5.1" readable-stream "^2.3.0" +string-argv@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -7365,9 +7364,9 @@ string-width@^2.0.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string_decoder@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.0.tgz#384f322ee8a848e500effde99901bba849c5d403" +string_decoder@^1.0.0, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: safe-buffer "~5.1.0" @@ -7375,7 +7374,7 @@ string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -string_decoder@~1.0.0, string_decoder@~1.0.3: +string_decoder@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" dependencies: @@ -7691,8 +7690,8 @@ ts-node@~5.0.1: yn "^2.0.0" tsickle@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736" + version "0.27.5" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.5.tgz#41e1a41a5acf971cbb2b0558a9590779234d591f" dependencies: minimist "^1.2.0" mkdirp "^0.5.1" @@ -7725,8 +7724,8 @@ tsscmp@~1.0.0: resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" tsutils@^2.12.1: - version "2.22.2" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.22.2.tgz#0b9f3d87aa3eb95bd32d26ce2b88aa329a657951" + version "2.26.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.26.1.tgz#9e4a0cb9ff173863f34c22a961969081270d1878" dependencies: tslib "^1.8.1" @@ -7780,9 +7779,9 @@ uglify-es@^3.3.4: commander "~2.13.0" source-map "~0.6.1" -uglify-js@3.3.x, uglify-js@^3.0.7: - version "3.3.16" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.16.tgz#23ba13efa27aa00885be7417819e8a9787f94028" +uglify-js@3.3.x, uglify-js@^3.3.20: + version "3.3.20" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.20.tgz#dc8bdee7d454c7d31dddc36f922d170bfcee3a0a" dependencies: commander "~2.15.0" source-map "~0.6.1" @@ -7938,8 +7937,8 @@ url-parse@1.0.x: requires-port "1.0.x" url-parse@^1.1.8: - version "1.2.0" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.2.0.tgz#3a19e8aaa6d023ddd27dcc44cb4fc8f7fec23986" + version "1.3.0" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.3.0.tgz#04a06c420d22beb9804f7ada2d57ad13160a4258" dependencies: querystringify "~1.0.0" requires-port "~1.0.0" @@ -8163,6 +8162,10 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: dependencies: iconv-lite "0.4.19" +whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4" + whatwg-url@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.0.tgz#08fdf2b9e872783a7a1f6216260a1d66cc722e08" @@ -8265,10 +8268,6 @@ xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" -xml-char-classes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" - xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -8397,5 +8396,5 @@ yn@^2.0.0: resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" zone.js@^0.8.17: - version "0.8.20" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.20.tgz#a218c48db09464b19ff6fc8f0d4bb5b1046e185d" + version "0.8.26" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.26.tgz#7bdd72f7668c5a7ad6b118148b4ea39c59d08d2d"