diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52402cb9..29dab6ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,8 +6,9 @@ Please look there.
**_This_** Changelog covers changes to the repository and the demo applications.
-# 0.6.1 (TBD)
+# 0.6.1 (2018-05-25)
+* Refactored for EntityAction operators as required by Beta 6
* Add example of extending EntityDataServices with custom `HeroDataService` as described in `entity-dataservice.md` (#151).
diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md
index 5d275edd..d3dbf923 100644
--- a/lib/CHANGELOG.md
+++ b/lib/CHANGELOG.md
@@ -1,5 +1,60 @@
# Angular ngrx-data library ChangeLog
+
+
+# 6.0.1-beta.6 (2018-05-24)
+
+## _EntityActions_ replaced by _EntityAction operators_
+
+_BREAKING CHANGE_
+
+Sub-classing of `Observable` is deprecated in v.6, in favor of custom pipeable operators.
+
+Accordingly, `EntityActions` has been removed.
+Use the _EntityAction_ operators, `ofEntityOp` and `ofEntityType` instead.
+
+Before
+
+```typescript
+// Select HeroActions
+entityActions.ofEntityType('Hero).pipe(...);
+
+// Select QUERY_ALL operations
+entityActions.ofOp(EntityOp.QUERY_ALL).pipe(...);
+```
+
+After
+
+```typescript
+// Select HeroActions
+entityActions.pipe(ofEntityType('Hero), ...);
+
+// Select QUERY_ALL operations
+entityActions.pipe(ofEntityOp(EntityOp.QUERY_ALL, ...);
+```
+
+The `EntityActions.where` and `EntityActions.until` methods have not been replaced.
+Use standard RxJS `filter` and `takeUntil` operators instead.
+
+## Other Features
+
+* `NgrxDataModuleWithoutEffects` is now public rather than internal.
+ Useful for devs who prefer to opt out of @ngrx/effects for entities and to
+ handle HTTP calls on their own.
+
+Import it instead of `NgrxDataModule`, like this.
+
+```typescript
+@NgModule({
+ imports: [
+ NgrxDataModuleWithoutEffects.forRoot(appNgrxDataModuleConfig),
+ ...
+ ],
+ ...
+})
+export class EntityAppModule {...}
+```
+
# 6.0.1-beta.5 (2018-05-23)
@@ -247,7 +302,7 @@ Extends `DefaultDataServiceConfig` so you can specify them.
For example, instead of setting the `PluralNames` for `Hero` you could fully specify the
singular and plural resource URLS in the `DefaultDataServiceConfig` like this:
-```javascript
+```typescript
// store/entity-metadata.ts
// Not needed for data access when set Hero's HttpResourceUrls
diff --git a/lib/package.json b/lib/package.json
index 5a5854fa..982673de 100644
--- a/lib/package.json
+++ b/lib/package.json
@@ -1,6 +1,6 @@
{
"name": "ngrx-data",
- "version": "6.0.0-beta.5",
+ "version": "6.0.0-beta.6",
"repository": "https://github.com/johnpapa/angular-ngrx-data.git",
"license": "MIT",
"peerDependencies": {
diff --git a/lib/src/actions/entity-action-operators.spec.ts b/lib/src/actions/entity-action-operators.spec.ts
new file mode 100644
index 00000000..3762475f
--- /dev/null
+++ b/lib/src/actions/entity-action-operators.spec.ts
@@ -0,0 +1,162 @@
+import { Action } from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
+
+import { Subject } from 'rxjs';
+
+import { EntityAction, EntityActionFactory } from './entity-action';
+import { EntityOp } from './entity-op';
+
+import { ofEntityType, ofEntityOp } from './entity-action-operators';
+
+class Hero {
+ id: number;
+ name: string;
+}
+
+// Todo: consider marble testing
+describe('EntityAction Operators', () => {
+ // factory never changes in these tests
+ const entityActionFactory = new EntityActionFactory();
+
+ let results: any[];
+ let actions: Subject;
+
+ const testActions = {
+ foo: { type: 'Foo' },
+ hero_query_all: entityActionFactory.create('Hero', EntityOp.QUERY_ALL),
+ villain_query_many: entityActionFactory.create(
+ 'Villain',
+ EntityOp.QUERY_MANY
+ ),
+ hero_delete: entityActionFactory.create(
+ 'Hero',
+ EntityOp.SAVE_DELETE_ONE,
+ 42
+ ),
+ bar: ({ type: 'Bar', payload: 'bar' })
+ };
+
+ function dispatchTestActions() {
+ Object.keys(testActions).forEach(a => actions.next((testActions)[a]));
+ }
+
+ beforeEach(() => {
+ actions = new Subject();
+ results = [];
+ });
+
+ ///////////////
+
+ it('#ofEntityType()', () => {
+ // EntityActions of any kind
+ actions.pipe(ofEntityType()).subscribe(ea => results.push(ea));
+
+ const expectedActions = [
+ testActions.hero_query_all,
+ testActions.villain_query_many,
+ testActions.hero_delete
+ ];
+ dispatchTestActions();
+ expect(results).toEqual(expectedActions);
+ });
+
+ it(`#ofEntityType('SomeType')`, () => {
+ // EntityActions of one type
+ actions.pipe(ofEntityType('Hero')).subscribe(ea => results.push(ea));
+
+ const expectedActions = [
+ testActions.hero_query_all,
+ testActions.hero_delete
+ ];
+ dispatchTestActions();
+ expect(results).toEqual(expectedActions);
+ });
+
+ it(`#ofEntityType('Type1', 'Type2', 'Type3')`, () => {
+ // n.b. 'Bar' is not an EntityType even though it is an action type
+ actions
+ .pipe(ofEntityType('Hero', 'Villain', 'Bar'))
+ .subscribe(ea => results.push(ea));
+
+ ofEntityTypeTest();
+ });
+
+ it('#ofEntityType(...arrayOfTypeNames)', () => {
+ const types = ['Hero', 'Villain', 'Bar'];
+
+ actions.pipe(ofEntityType(...types)).subscribe(ea => results.push(ea));
+ ofEntityTypeTest();
+ });
+
+ it('#ofEntityType(arrayOfTypeNames)', () => {
+ const types = ['Hero', 'Villain', 'Bar'];
+
+ actions.pipe(ofEntityType(types)).subscribe(ea => results.push(ea));
+ ofEntityTypeTest();
+ });
+
+ function ofEntityTypeTest() {
+ const expectedActions = [
+ testActions.hero_query_all,
+ testActions.villain_query_many,
+ testActions.hero_delete
+ // testActions.bar, // 'Bar' is not an EntityType
+ ];
+ dispatchTestActions();
+ expect(results).toEqual(expectedActions);
+ }
+
+ it('#ofEntityType(...) is case sensitive', () => {
+ // EntityActions of the 'hero' type, but it's lowercase so shouldn't match
+ actions.pipe(ofEntityType('hero')).subscribe(ea => results.push(ea));
+
+ dispatchTestActions();
+ expect(results).toEqual([], 'should not match anything');
+ });
+
+ ///////////////
+
+ it('#ofEntityOp with string args', () => {
+ actions
+ .pipe(ofEntityOp(EntityOp.QUERY_ALL, EntityOp.QUERY_MANY))
+ .subscribe(ea => results.push(ea));
+
+ ofEntityOpTest();
+ });
+
+ it('#ofEntityOp with ...rest args', () => {
+ const ops = [EntityOp.QUERY_ALL, EntityOp.QUERY_MANY];
+
+ actions.pipe(ofEntityOp(...ops)).subscribe(ea => results.push(ea));
+ ofEntityOpTest();
+ });
+
+ it('#ofEntityOp with array args', () => {
+ const ops = [EntityOp.QUERY_ALL, EntityOp.QUERY_MANY];
+
+ actions.pipe(ofEntityOp(ops)).subscribe(ea => results.push(ea));
+ ofEntityOpTest();
+ });
+
+ it('#ofEntityOp()', () => {
+ // EntityOps of any kind
+ actions.pipe(ofEntityOp()).subscribe(ea => results.push(ea));
+
+ const expectedActions = [
+ testActions.hero_query_all,
+ testActions.villain_query_many,
+ testActions.hero_delete
+ ];
+ dispatchTestActions();
+ expect(results).toEqual(expectedActions);
+ });
+
+ function ofEntityOpTest() {
+ const expectedActions = [
+ testActions.hero_query_all,
+ testActions.villain_query_many
+ ];
+ dispatchTestActions();
+ expect(results).toEqual(expectedActions);
+ }
+});
diff --git a/lib/src/actions/entity-action-operators.ts b/lib/src/actions/entity-action-operators.ts
new file mode 100644
index 00000000..063698f2
--- /dev/null
+++ b/lib/src/actions/entity-action-operators.ts
@@ -0,0 +1,80 @@
+import { Action } from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
+
+import { Observable, OperatorFunction } from 'rxjs';
+import { filter } from 'rxjs/operators';
+
+import { EntityAction } from './entity-action';
+import { EntityOp } from './entity-op';
+import { flattenArgs } from '../utils/utilities';
+
+/**
+ * Select actions concerning one of the allowed Entity operations
+ * @param allowedEntityOps Entity operations (e.g, EntityOp.QUERY_ALL) whose actions should be selected
+ * Example:
+ * ```
+ * this.actions.pipe(ofEntityOp(EntityOp.QUERY_ALL, EntityOp.QUERY_MANY), ...)
+ * this.actions.pipe(ofEntityOp(...queryOps), ...)
+ * this.actions.pipe(ofEntityOp(queryOps), ...)
+ * this.actions.pipe(ofEntityOp(), ...) // any action with a defined `op` property
+ * ```
+ */
+export function ofEntityOp(
+ allowedOps: string[] | EntityOp[]
+): OperatorFunction;
+export function ofEntityOp(
+ ...allowedOps: (string | EntityOp)[]
+): OperatorFunction;
+export function ofEntityOp(
+ ...allowedEntityOps: any[]
+): OperatorFunction {
+ const ops: string[] = flattenArgs(allowedEntityOps);
+ switch (ops.length) {
+ case 0:
+ return filter((action: EntityAction): action is T => !!action.op);
+ case 1:
+ const op = ops[0];
+ return filter((action: EntityAction): action is T => op === action.op);
+ default:
+ return filter((action: EntityAction): action is T =>
+ ops.some(entityOp => entityOp === action.op)
+ );
+ }
+}
+
+/**
+ * Select actions concerning one of the allowed Entity types
+ * @param allowedEntityNames Entity-type names (e.g, 'Hero') whose actions should be selected
+ * Example:
+ * ```
+ * this.actions.pipe(ofEntityType(), ...) // ayn EntityAction with a defined entity type property
+ * this.actions.pipe(ofEntityType('Hero'), ...) // EntityActions for the Hero entity
+ * this.actions.pipe(ofEntityType('Hero', 'Villain', 'Sidekick'), ...)
+ * this.actions.pipe(ofEntityType(...theChosen), ...)
+ * this.actions.pipe(ofEntityType(theChosen), ...)
+ * ```
+ */
+export function ofEntityType(
+ allowedEntityNames?: string[]
+): OperatorFunction;
+export function ofEntityType(
+ ...allowedEntityNames: string[]
+): OperatorFunction;
+export function ofEntityType(
+ ...allowedEntityNames: any[]
+): OperatorFunction {
+ const names: string[] = flattenArgs(allowedEntityNames);
+ switch (names.length) {
+ case 0:
+ return filter((action: EntityAction): action is T => !!action.entityName);
+ case 1:
+ const name = names[0];
+ return filter(
+ (action: EntityAction): action is T => name === action.entityName
+ );
+ default:
+ return filter((action: EntityAction): action is T =>
+ names.some(entityName => entityName === action.entityName)
+ );
+ }
+}
diff --git a/lib/src/actions/entity-actions.spec.ts b/lib/src/actions/entity-actions.spec.ts
deleted file mode 100644
index 15b6a826..00000000
--- a/lib/src/actions/entity-actions.spec.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-import { Action } from '@ngrx/store';
-import { Actions } from '@ngrx/effects';
-
-import { Subject } from 'rxjs';
-
-import { EntityAction, EntityActionFactory } from './entity-action';
-import { EntityActions } from './entity-actions';
-import { EntityOp } from './entity-op';
-
-class Hero {
- id: number;
- name: string;
-}
-
-// Todo: consider marble testing
-describe('EntityActions', () => {
- // factory never changes in these tests
- const entityActionFactory = new EntityActionFactory();
-
- let eas: EntityActions;
- let results: any[];
- let source: Subject;
-
- const testActions = {
- foo: { type: 'Foo' },
- hero_query_all: entityActionFactory.create('Hero', EntityOp.QUERY_ALL),
- villain_query_many: entityActionFactory.create(
- 'Villain',
- EntityOp.QUERY_MANY
- ),
- hero_delete: entityActionFactory.create(
- 'Hero',
- EntityOp.SAVE_DELETE_ONE,
- 42
- ),
- bar: ({ type: 'Bar', payload: 'bar' })
- };
-
- function dispatchTestActions() {
- Object.keys(testActions).forEach(a => source.next((testActions)[a]));
- }
-
- beforeEach(() => {
- source = new Subject();
- const actions = new Actions(source);
- eas = new EntityActions(actions);
- results = [];
- });
-
- it('#where', () => {
- // Filter for the 'Hero' EntityAction with a payload
- eas
- .where(ea => ea.entityName === 'Hero' && ea.payload != null)
- .subscribe(ea => results.push(ea));
-
- // This is it
- const expectedActions = [testActions.hero_delete];
- dispatchTestActions();
- expect(results).toEqual(expectedActions);
- });
-
- ///////////////
-
- it('#ofEntityType()', () => {
- // EntityActions of any kind
- eas.ofEntityType().subscribe(ea => results.push(ea));
-
- const expectedActions = [
- testActions.hero_query_all,
- testActions.villain_query_many,
- testActions.hero_delete
- ];
- dispatchTestActions();
- expect(results).toEqual(expectedActions);
- });
-
- it(`#ofEntityType('SomeType')`, () => {
- // EntityActions of one type
- eas.ofEntityType('Hero').subscribe(ea => results.push(ea));
-
- const expectedActions = [
- testActions.hero_query_all,
- testActions.hero_delete
- ];
- dispatchTestActions();
- expect(results).toEqual(expectedActions);
- });
-
- it(`#ofEntityType('Type1', 'Type2', 'Type3')`, () => {
- // n.b. 'Bar' is not an EntityType even though it is an action type
- eas
- .ofEntityType('Hero', 'Villain', 'Bar')
- .subscribe(ea => results.push(ea));
-
- ofEntityTypesTest();
- });
-
- it('#ofEntityType(...arrayOfTypeNames)', () => {
- const types = ['Hero', 'Villain', 'Bar'];
-
- eas.ofEntityType(...types).subscribe(ea => results.push(ea));
- ofEntityTypesTest();
- });
-
- it('#ofEntityType(arrayOfTypeNames)', () => {
- const types = ['Hero', 'Villain', 'Bar'];
-
- eas.ofEntityType(types).subscribe(ea => results.push(ea));
- ofEntityTypesTest();
- });
-
- function ofEntityTypesTest() {
- const expectedActions = [
- testActions.hero_query_all,
- testActions.villain_query_many,
- testActions.hero_delete
- // testActions.bar, // 'Bar' is not an EntityType
- ];
- dispatchTestActions();
- expect(results).toEqual(expectedActions);
- }
-
- it('#ofEntityType(...) is case sensitive', () => {
- // EntityActions of the 'hero' type, but it's lowercase so shouldn't match
- eas.ofEntityType('hero').subscribe(ea => results.push(ea));
-
- dispatchTestActions();
- expect(results).toEqual([], 'should not match anything');
- });
-
- ///////////////
-
- it('#ofOp with string args', () => {
- eas
- .ofOp(EntityOp.QUERY_ALL, EntityOp.QUERY_MANY)
- .subscribe(ea => results.push(ea));
-
- ofOpTest();
- });
-
- it('#ofOp with ...rest args', () => {
- const ops = [EntityOp.QUERY_ALL, EntityOp.QUERY_MANY];
-
- eas.ofOp(...ops).subscribe(ea => results.push(ea));
- ofOpTest();
- });
-
- it('#ofOp with array args', () => {
- const ops = [EntityOp.QUERY_ALL, EntityOp.QUERY_MANY];
-
- eas.ofOp(ops).subscribe(ea => results.push(ea));
- ofOpTest();
- });
-
- function ofOpTest() {
- const expectedActions = [
- testActions.hero_query_all,
- testActions.villain_query_many
- ];
- dispatchTestActions();
- expect(results).toEqual(expectedActions);
- }
-
- ///////////////
- const testTypeNames = [
- entityActionFactory.formatActionType(EntityOp.QUERY_ALL, 'Hero'),
- entityActionFactory.formatActionType(EntityOp.QUERY_MANY, 'Villain')
- ];
-
- it('#ofType with string args', () => {
- eas.ofType(testTypeNames).subscribe(ea => results.push(ea));
- ofTypeTest();
- });
-
- it('#ofType with ...rest args', () => {
- eas.ofType(...testTypeNames).subscribe(ea => results.push(ea));
- ofTypeTest();
- });
-
- it('#ofType with array args', () => {
- eas.ofType(testTypeNames).subscribe(ea => results.push(ea));
- ofTypeTest();
- });
-
- function ofTypeTest() {
- const expectedActions = [
- testActions.hero_query_all,
- testActions.villain_query_many
- ];
- dispatchTestActions();
- expect(results).toEqual(expectedActions);
- }
-
- ///////////////
-
- it('#until(notifier) completes the subscriber', () => {
- const stop = new Subject();
- let completed = 0;
-
- const actions = eas.ofEntityType().until(stop); // completes and unsubscribes
-
- actions.subscribe(null, null, () => completed++);
- actions.subscribe(ea => results.push(ea), null, () => completed++);
-
- const action = entityActionFactory.create(
- 'Hero',
- EntityOp.SAVE_DELETE_ONE,
- 42
- );
-
- source.next(action);
- source.next(action);
- source.next(action);
- stop.next();
-
- // The following should be ignored.
- source.next(action);
- source.next(action);
-
- expect(results.length).toBe(3, 'should have 3 results');
- expect(completed).toBe(2, 'should have completed both subscriptions');
- });
-});
diff --git a/lib/src/actions/entity-actions.ts b/lib/src/actions/entity-actions.ts
deleted file mode 100644
index b5785a1e..00000000
--- a/lib/src/actions/entity-actions.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Action } from '@ngrx/store';
-import { Actions } from '@ngrx/effects';
-
-import { Observable, Operator } from 'rxjs';
-import { filter, takeUntil } from 'rxjs/operators';
-
-import { EntityAction } from './entity-action';
-import { EntityOp } from './entity-op';
-import { flattenArgs } from '../utils/utilities';
-
-/**
- * Observable of entity actions dispatched to the store.
- * EntityAction-oriented filter operators for ease-of-use.
- * Imitates `Actions.ofType()` in ngrx/entity.
- */
-@Injectable()
-export class EntityActions<
- V extends EntityAction = EntityAction
-> extends Observable {
- // Inject the ngrx/effect Actions observable that watches dispatches to the store
- constructor(source?: Actions) {
- super();
-
- if (source) {
- // ONLY look at EntityActions (source is deprecated but no known substitute)
- /* tslint:disable-next-line:deprecation */
- this.source = source.pipe(
- filter((action: any) => action.op && action.entityName)
- );
- }
- }
-
- // Can't name it `filter` because exists in `import 'rxjs/operator';` (issue 97).
- // 'where' was an alias for `filter` long ago but no import risk now.
-
- /**
- * Filter EntityActions based on a predicate.
- * @param predicate -returns true if EntityAction passes the test.
- * Example:
- * this.actions$.where(ea => ea.op.includes(EntityAction.OP_SUCCESS)) // Successful hero action
- */
- where(predicate: (ea: EntityAction) => boolean) {
- return filter(predicate)(this) as EntityActions;
- }
-
- lift(operator: Operator): Observable {
- const observable = new EntityActions();
- // source is deprecated but no known substitute
- /* tslint:disable-next-line:deprecation */
- observable.source = this;
-
- // "Force-casts" below because can't change signature of Lift.
- // operator is deprecated but no known substitute)
- /* tslint:disable-next-line:deprecation */
- observable.operator = >(operator);
- return >(observable);
- }
-
- /**
- * Entity actions concerning any of the given entity types
- * @param allowedEntityNames - names of entities whose actions should pass through.
- * Example:
- * ```
- * this.actions$.ofEntityType() // an EntityAction of any entity type
- * this.actions$.ofEntityType('Hero') // EntityActions for the Hero entity
- * this.actions$.ofEntityType('Hero', 'Villain', 'Sidekick')
- * this.actions$.ofEntityType(...theChosen)
- * this.actions$.ofEntityType(theChosen)
- * ```
- */
- ofEntityType(allowedEntityNames?: string[]): EntityActions;
- ofEntityType(...allowedEntityNames: string[]): EntityActions;
- ofEntityType(...allowedEntityNames: any[]): EntityActions {
- const names: string[] = flattenArgs(allowedEntityNames);
- switch (names.length) {
- case 0:
- return this.where(ea => !!ea.entityName);
- case 1:
- const name = names[0];
- return this.where(ea => name === ea.entityName);
- default:
- return this.where(
- ea => ea.entityName && names.some(n => n === ea.entityName)
- );
- }
- }
-
- /**
- * Entity actions concerning any of the given `EntityOp`s
- * @param allowedOps - `EntityOp`s whose actions should pass through.
- * Example:
- * ```
- * this.actions$.ofOp(EntityOp.QUERY_ALL, EntityOp.QUERY_MANY)
- * this.actions$.ofOp(...queryOps)
- * this.actions$.ofOp(queryOps)
- * ```
- */
- ofOp(allowedOps: string[] | EntityOp[]): EntityActions;
- ofOp(...allowedOps: (string | EntityOp)[]): EntityActions;
- ofOp(...allowedOps: any[]) {
- // string is the runtime type of an EntityOp enum
- const ops: string[] = flattenArgs(allowedOps);
- return this.where(ea => ops.some(op => op === ea.op));
- }
-
- /**
- * Entity actions of the given type(s)
- * @param allowedTypes - `Action.type`s whose actions should pass through.
- * Example:
- * ```
- * this.actions$.ofTypes('GET_ALL_HEROES', 'GET_ALL_SIDEKICKS')
- * this.actions$.ofTypes(...someTypes)
- * this.actions$.ofTypes(someTypes)
- * ```
- */
- ofType(allowedTypes: string[]): EntityActions;
- ofType(...allowedTypes: string[]): EntityActions;
- ofType(...allowedTypes: any[]): EntityActions {
- const types: string[] = flattenArgs(allowedTypes);
- return this.where(ea => types.some(type => type === ea.type));
- }
-
- /**
- * Continue emitting actions until the `notifier` says stop.
- * When the `notifier` emits a next value, this observable completes
- * and subscribers are unsubscribed.
- * Uses RxJS `takeUntil().`
- * @param notifier - observable that stops the source with a `next()`.
- * Example:
- * this.actions$.ofEntityType('Hero').until(this.onDestroy);
- */
- until(notifier: Observable): EntityActions {
- return takeUntil(notifier)(this) as EntityActions;
- }
-}
diff --git a/lib/src/effects/entity-effects.marbles.spec.ts b/lib/src/effects/entity-effects.marbles.spec.ts
index bbe1933a..23f46909 100644
--- a/lib/src/effects/entity-effects.marbles.spec.ts
+++ b/lib/src/effects/entity-effects.marbles.spec.ts
@@ -2,10 +2,12 @@
import { TestBed } from '@angular/core/testing';
import { cold, hot } from 'jasmine-marbles';
-import { Observable } from 'rxjs';
+import { Observable, Subject } from 'rxjs';
+
+import { Actions } from '@ngrx/effects';
+import { provideMockActions } from '@ngrx/effects/testing';
import { EntityAction, EntityActionFactory } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { EntityOp, OP_ERROR } from '../actions/entity-op';
import {
@@ -26,19 +28,7 @@ import { EntityEffects } from './entity-effects';
import { Logger } from '../utils/interfaces';
import { Update } from '../utils/ngrx-entity-models';
-
-export class TestEntityActions extends EntityActions {
- set stream(source: Observable) {
- // source is deprecated but no known substitute
- /* tslint:disable-next-line:deprecation */
- this.source = source;
- }
-}
-
-// For AOT
-export function getActions() {
- return new TestEntityActions();
-}
+import { TestHotObservable } from 'jasmine-marbles/src/test-observables';
export class TestEntityDataService {
dataServiceSpy: any;
@@ -70,7 +60,7 @@ describe('EntityEffects (marble testing)', () => {
let effects: EntityEffects;
let entityActionFactory: EntityActionFactory;
let testEntityDataService: TestEntityDataService;
- let actions$: TestEntityActions;
+ let actions: Observable;
let logger: Logger;
beforeEach(() => {
@@ -79,8 +69,8 @@ describe('EntityEffects (marble testing)', () => {
TestBed.configureTestingModule({
providers: [
EntityEffects,
+ provideMockActions(() => actions),
EntityActionFactory,
- { provide: EntityActions, useFactory: getActions },
{ provide: EntityDataService, useFactory: getDataService },
{ provide: Logger, useValue: logger },
{
@@ -92,7 +82,7 @@ describe('EntityEffects (marble testing)', () => {
entityActionFactory = TestBed.get(EntityActionFactory);
effects = TestBed.get(EntityEffects);
testEntityDataService = TestBed.get(EntityDataService);
- actions$ = TestBed.get(EntityActions);
+ actions = TestBed.get(Actions);
});
it('should return a QUERY_ALL_SUCCESS with the heroes on success', () => {
@@ -107,7 +97,8 @@ describe('EntityEffects (marble testing)', () => {
heroes
);
- actions$.stream = hot('-a---', { a: action });
+ const x = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: heroes });
const expected = cold('----b', { b: completion });
@@ -122,7 +113,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'GET', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.getAll.and.returnValue(response);
@@ -142,7 +133,7 @@ describe('EntityEffects (marble testing)', () => {
EntityOp.QUERY_BY_KEY_SUCCESS
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: undefined });
const expected = cold('----b', { b: completion });
@@ -161,7 +152,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'DELETE', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.getById.and.returnValue(response);
@@ -183,7 +174,7 @@ describe('EntityEffects (marble testing)', () => {
heroes
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: heroes });
const expected = cold('----b', { b: completion });
@@ -202,7 +193,7 @@ describe('EntityEffects (marble testing)', () => {
});
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.getWithQuery.and.returnValue(response);
@@ -225,7 +216,7 @@ describe('EntityEffects (marble testing)', () => {
hero
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: hero });
const expected = cold('----b', { b: completion });
@@ -245,7 +236,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.add.and.returnValue(response);
@@ -265,7 +256,7 @@ describe('EntityEffects (marble testing)', () => {
42
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: 42 });
const expected = cold('----b', { b: completion });
@@ -284,7 +275,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'DELETE', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.delete.and.returnValue(response);
@@ -306,7 +297,7 @@ describe('EntityEffects (marble testing)', () => {
update
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: update });
const expected = cold('----b', { b: completion });
@@ -326,7 +317,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.update.and.returnValue(response);
@@ -348,7 +339,7 @@ describe('EntityEffects (marble testing)', () => {
hero
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: hero });
const expected = cold('----b', { b: completion });
@@ -368,7 +359,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.add.and.returnValue(response);
@@ -387,7 +378,7 @@ describe('EntityEffects (marble testing)', () => {
EntityOp.SAVE_DELETE_ONE_OPTIMISTIC_SUCCESS
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: undefined });
const expected = cold('----b', { b: completion });
@@ -406,7 +397,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'DELETE', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.delete.and.returnValue(response);
@@ -428,7 +419,7 @@ describe('EntityEffects (marble testing)', () => {
update
);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
// delay the response 3 ticks
const response = cold('---a|', { a: update });
const expected = cold('----b', { b: completion });
@@ -448,7 +439,7 @@ describe('EntityEffects (marble testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const response = cold('----#|', {}, error);
const expected = cold('-----b', { b: completion });
testEntityDataService.dataServiceSpy.update.and.returnValue(response);
@@ -460,7 +451,7 @@ describe('EntityEffects (marble testing)', () => {
// Would clear the cached collection
const action = entityActionFactory.create('Hero', EntityOp.REMOVE_ALL);
- actions$.stream = hot('-a---', { a: action });
+ actions = hot('-a---', { a: action });
const expected = cold('---');
expect(effects.persist$).toBeObservable(expected);
diff --git a/lib/src/effects/entity-effects.spec.ts b/lib/src/effects/entity-effects.spec.ts
index db173f22..8a359f9e 100644
--- a/lib/src/effects/entity-effects.spec.ts
+++ b/lib/src/effects/entity-effects.spec.ts
@@ -1,11 +1,12 @@
// Not using marble testing
import { TestBed } from '@angular/core/testing';
+import { Action } from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
-import { Observable, of, merge, throwError } from 'rxjs';
+import { Observable, of, merge, Subject, throwError } from 'rxjs';
import { delay, first } from 'rxjs/operators';
import { EntityAction, EntityActionFactory } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { EntityOp, OP_ERROR } from '../actions/entity-op';
import {
@@ -27,18 +28,6 @@ import { EntityEffects } from './entity-effects';
import { Logger } from '../utils/interfaces';
import { Update } from '../utils/ngrx-entity-models';
-export class TestEntityActions extends EntityActions {
- set stream(source: Observable) {
- // source is deprecated but no known substitute
- /* tslint:disable-next-line:deprecation */
- this.source = source;
- }
-}
-
-export function getActions() {
- return new TestEntityActions();
-}
-
export class TestEntityDataService {
dataServiceSpy: any;
@@ -70,10 +59,10 @@ describe('EntityEffects (normal testing)', () => {
// factory never changes in these tests
const entityActionFactory = new EntityActionFactory();
+ let actions$: Subject;
let effects: EntityEffects;
- let testEntityDataService: TestEntityDataService;
- let actions$: TestEntityActions;
let logger: Logger;
+ let testEntityDataService: TestEntityDataService;
function expectCompletion(completion: EntityAction) {
effects.persist$.subscribe(
@@ -88,11 +77,12 @@ describe('EntityEffects (normal testing)', () => {
beforeEach(() => {
logger = jasmine.createSpyObj('Logger', ['error', 'log', 'warn']);
+ actions$ = new Subject();
TestBed.configureTestingModule({
providers: [
EntityEffects,
- { provide: EntityActions, useFactory: getActions },
+ { provide: Actions, useValue: actions$ },
{ provide: EntityActionFactory, useValue: entityActionFactory },
{ provide: EntityDataService, useFactory: getDataService },
{ provide: Logger, useValue: logger },
@@ -103,9 +93,9 @@ describe('EntityEffects (normal testing)', () => {
]
});
+ actions$ = TestBed.get(Actions);
effects = TestBed.get(EntityEffects);
testEntityDataService = TestBed.get(EntityDataService);
- actions$ = TestBed.get(EntityActions);
});
it('should return a QUERY_ALL_SUCCESS, with the heroes, on success', () => {
@@ -123,7 +113,7 @@ describe('EntityEffects (normal testing)', () => {
heroes
);
- actions$.stream = of(action);
+ actions$.next(action);
expectCompletion(completion);
});
@@ -148,7 +138,7 @@ describe('EntityEffects (normal testing)', () => {
heroes
);
- actions$.stream = of(action);
+ actions$.next(action);
expectCompletion(completion);
});
@@ -172,7 +162,7 @@ describe('EntityEffects (normal testing)', () => {
heroes
);
- actions$.stream = of(action);
+ actions$.next(action);
expectCompletion(completion);
});
@@ -182,7 +172,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'GET', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.getAll.and.returnValue(response);
@@ -201,7 +191,7 @@ describe('EntityEffects (normal testing)', () => {
EntityOp.QUERY_BY_KEY_SUCCESS
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(undefined);
testEntityDataService.dataServiceSpy.getById.and.returnValue(response);
@@ -218,7 +208,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'DELETE', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.getById.and.returnValue(response);
@@ -239,7 +229,7 @@ describe('EntityEffects (normal testing)', () => {
heroes
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(heroes);
testEntityDataService.dataServiceSpy.getWithQuery.and.returnValue(response);
@@ -256,7 +246,7 @@ describe('EntityEffects (normal testing)', () => {
});
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.getWithQuery.and.returnValue(response);
@@ -277,7 +267,7 @@ describe('EntityEffects (normal testing)', () => {
hero
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(hero);
testEntityDataService.dataServiceSpy.add.and.returnValue(response);
@@ -295,7 +285,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.add.and.returnValue(response);
@@ -314,7 +304,7 @@ describe('EntityEffects (normal testing)', () => {
42
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(42); // dataservice successful delete returns the deleted entity id
testEntityDataService.dataServiceSpy.delete.and.returnValue(response);
@@ -331,7 +321,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'DELETE', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.delete.and.returnValue(response);
@@ -352,7 +342,7 @@ describe('EntityEffects (normal testing)', () => {
update
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(update);
testEntityDataService.dataServiceSpy.update.and.returnValue(response);
@@ -370,7 +360,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.update.and.returnValue(response);
@@ -391,7 +381,7 @@ describe('EntityEffects (normal testing)', () => {
hero
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(hero);
testEntityDataService.dataServiceSpy.add.and.returnValue(response);
@@ -409,7 +399,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.add.and.returnValue(response);
@@ -427,7 +417,7 @@ describe('EntityEffects (normal testing)', () => {
EntityOp.SAVE_DELETE_ONE_OPTIMISTIC_SUCCESS
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(undefined);
testEntityDataService.dataServiceSpy.delete.and.returnValue(response);
@@ -444,7 +434,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'DELETE', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.delete.and.returnValue(response);
@@ -465,7 +455,7 @@ describe('EntityEffects (normal testing)', () => {
update
);
- actions$.stream = of(action);
+ actions$.next(action);
const response = of(update);
testEntityDataService.dataServiceSpy.update.and.returnValue(response);
@@ -483,7 +473,7 @@ describe('EntityEffects (normal testing)', () => {
const completion = makeEntityErrorCompletion(action, 'PUT', httpError);
const error = completion.payload.error;
- actions$.stream = of(action);
+ actions$.next(action);
const response = throwError(error);
testEntityDataService.dataServiceSpy.update.and.returnValue(response);
@@ -494,7 +484,7 @@ describe('EntityEffects (normal testing)', () => {
// Would clear the cached collection
const action = entityActionFactory.create('Hero', EntityOp.REMOVE_ALL);
- actions$.stream = of(action);
+ actions$.next(action);
const sentinel = 'no persist$ effect';
merge(
diff --git a/lib/src/effects/entity-effects.ts b/lib/src/effects/entity-effects.ts
index 8080b513..32a87ef3 100644
--- a/lib/src/effects/entity-effects.ts
+++ b/lib/src/effects/entity-effects.ts
@@ -1,13 +1,13 @@
import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store';
-import { Effect } from '@ngrx/effects';
+import { Effect, Actions } from '@ngrx/effects';
import { Observable, of } from 'rxjs';
import { concatMap, catchError, map } from 'rxjs/operators';
import { EntityAction, EntityActionFactory } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { EntityOp } from '../actions/entity-op';
+import { ofEntityOp } from '../actions/entity-action-operators';
import { EntityDataService } from '../dataservices/entity-data.service';
import { PersistenceResultHandler } from '../dataservices/persistence-result-handler.service';
@@ -29,12 +29,13 @@ export class EntityEffects {
@Effect()
// Concurrent persistence requests considered unsafe.
// `concatMap` ensures each request must complete-or-fail before making the next request.
- persist$: Observable = this.actions$
- .ofOp(persistOps)
- .pipe(concatMap(action => this.persist(action)));
+ persist$: Observable = this.actions.pipe(
+ ofEntityOp(persistOps),
+ concatMap(action => this.persist(action))
+ );
constructor(
- private actions$: EntityActions,
+ private actions: Actions,
private dataService: EntityDataService,
private entityActionFactory: EntityActionFactory,
private resultHandler: PersistenceResultHandler
diff --git a/lib/src/entity-services/entity-collection-service-base.ts b/lib/src/entity-services/entity-collection-service-base.ts
index 843dcf0d..092ddd54 100644
--- a/lib/src/entity-services/entity-collection-service-base.ts
+++ b/lib/src/entity-services/entity-collection-service-base.ts
@@ -1,11 +1,11 @@
import { Injectable } from '@angular/core';
import { Action, Store } from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
import { Observable } from 'rxjs';
import { Dictionary, IdSelector, Update } from '../utils/ngrx-entity-models';
import { EntityAction } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { EntityOp } from '../actions/entity-op';
import { EntityActionGuard } from '../actions/entity-action-guard';
import { EntityCache } from '../reducers/entity-cache';
@@ -329,13 +329,13 @@ export class EntityCollectionServiceBase<
entities$: Observable | Store;
/** Observable of actions related to this entity type. */
- entityActions$: EntityActions;
+ entityActions$: Observable;
/** Observable of the map of entity keys to entities */
entityMap$: Observable> | Store>;
/** Observable of error actions related to this entity type. */
- errors$: EntityActions;
+ errors$: Observable;
/** Observable of the filter pattern applied by the entity collection's filter function */
filter$: Observable | Store;
diff --git a/lib/src/entity-services/entity-collection-service.spec.ts b/lib/src/entity-services/entity-collection-service.spec.ts
index a964f89c..66f3cf29 100644
--- a/lib/src/entity-services/entity-collection-service.spec.ts
+++ b/lib/src/entity-services/entity-collection-service.spec.ts
@@ -1,11 +1,11 @@
/** TODO: much more testing */
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Action, StoreModule, Store } from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
import { Subject } from 'rxjs';
import { EntityAction, EntityActionFactory } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { EntityOp } from '../actions/entity-op';
import { EntityCache } from '../reducers/entity-cache';
@@ -16,7 +16,7 @@ import {
EntityCollectionServiceFactory
} from './entity-services-interfaces';
-import { _NgrxDataModuleWithoutEffects } from '../ngrx-data.module';
+import { NgrxDataModuleWithoutEffects } from '../ngrx-data-without-effects.module';
import { commandDispatchTest } from '../dispatchers/entity-dispatcher.spec';
@@ -95,12 +95,11 @@ const heroMetadata = {
function entityServiceFactorySetup() {
const actions$ = new Subject();
- const entityActions = new EntityActions(actions$);
TestBed.configureTestingModule({
- imports: [StoreModule.forRoot({}), _NgrxDataModuleWithoutEffects],
+ imports: [StoreModule.forRoot({}), NgrxDataModuleWithoutEffects],
providers: [
- { provide: EntityActions, useValue: entityActions },
+ { provide: Actions, useValue: actions$ },
{
provide: ENTITY_METADATA_TOKEN,
multi: true,
@@ -123,7 +122,6 @@ function entityServiceFactorySetup() {
return {
actions$,
- entityActions,
entityActionFactory,
entityCollectionServiceFactory,
testStore
diff --git a/lib/src/index.ts b/lib/src/index.ts
index 1042365c..fd025a57 100644
--- a/lib/src/index.ts
+++ b/lib/src/index.ts
@@ -2,7 +2,7 @@
// NO BARRELS or else `ng build --aot` of any app using ngrx-data produces strange errors
// actions
export * from './actions/entity-action';
-export * from './actions/entity-actions';
+export * from './actions/entity-action-operators';
export * from './actions/entity-action-guard';
export * from './actions/entity-cache-actions';
export * from './actions/entity-op';
@@ -57,4 +57,9 @@ export * from './utils/default-logger';
export * from './utils/default-pluralizer';
export * from './utils/utilities';
-export { NgrxDataModule, NgrxDataModuleConfig } from './ngrx-data.module';
+// NgrxDataModule
+export { NgrxDataModule } from './ngrx-data.module';
+export {
+ NgrxDataModuleWithoutEffects,
+ NgrxDataModuleConfig
+} from './ngrx-data-without-effects.module';
diff --git a/lib/src/ngrx-data-without-effects.module.ts b/lib/src/ngrx-data-without-effects.module.ts
new file mode 100644
index 00000000..51705fd8
--- /dev/null
+++ b/lib/src/ngrx-data-without-effects.module.ts
@@ -0,0 +1,191 @@
+import {
+ ModuleWithProviders,
+ NgModule,
+ Inject,
+ Injector,
+ InjectionToken,
+ Optional,
+ OnDestroy
+} from '@angular/core';
+
+import {
+ Action,
+ ActionReducer,
+ combineReducers,
+ MetaReducer,
+ ReducerManager,
+ StoreModule
+} from '@ngrx/store';
+
+import { EntityAction, EntityActionFactory } from './actions/entity-action';
+
+import { EntityDispatcherFactory } from './dispatchers/entity-dispatcher-factory';
+
+import { EntityDefinitionService } from './entity-metadata/entity-definition.service';
+import {
+ EntityMetadataMap,
+ ENTITY_METADATA_TOKEN
+} from './entity-metadata/entity-metadata';
+
+import { EntityCache } from './reducers/entity-cache';
+import { entityCacheSelectorProvider } from './selectors/entity-cache-selector';
+import { EntityCollectionServiceFactory } from './entity-services/entity-services-interfaces';
+import { DefaultEntityCollectionServiceFactory } from './entity-services/default-entity-collection-service-factory';
+import { EntityCollection } from './reducers/entity-collection';
+import { EntityCollectionCreator } from './reducers/entity-collection-creator';
+import {
+ EntityCollectionReducerFactory,
+ EntityCollectionReducerMethodsFactory
+} from './reducers/entity-collection-reducer';
+import { EntityEffects } from './effects/entity-effects';
+
+import { DefaultEntityCollectionReducerMethodsFactory } from './reducers/default-entity-collection-reducer-methods';
+import {
+ createEntityReducer,
+ EntityReducerFactory
+} from './reducers/entity-reducer';
+import {
+ ENTITY_CACHE_NAME,
+ ENTITY_CACHE_NAME_TOKEN,
+ ENTITY_CACHE_META_REDUCERS,
+ ENTITY_COLLECTION_META_REDUCERS,
+ ENTITY_CACHE_REDUCER,
+ INITIAL_ENTITY_CACHE_STATE
+} from './reducers/constants';
+
+import { Logger, Pluralizer, PLURAL_NAMES_TOKEN } from './utils/interfaces';
+
+import { EntitySelectors } from './selectors/entity-selectors';
+import { EntitySelectorsFactory } from './selectors/entity-selectors';
+import { EntitySelectors$Factory } from './selectors/entity-selectors$';
+import { EntityServices } from './entity-services/entity-services-interfaces';
+import { EntityServicesBase } from './entity-services/entity-services-base';
+
+import { DefaultLogger } from './utils/default-logger';
+import { DefaultPluralizer } from './utils/default-pluralizer';
+
+export interface NgrxDataModuleConfig {
+ entityMetadata?: EntityMetadataMap;
+ entityCacheMetaReducers?: (
+ | MetaReducer
+ | InjectionToken>)[];
+ entityCollectionMetaReducers?: MetaReducer[];
+ // Initial EntityCache state or a function that returns that state
+ initialEntityCacheState?: EntityCache | (() => EntityCache);
+ pluralNames?: { [name: string]: string };
+}
+
+/**
+ * Module without effects or dataservices which means no HTTP calls
+ * This module helpful for internal testing.
+ * Also helpful for apps that handle server access on their own and
+ * therefore opt-out of @ngrx/effects for entities
+ */
+@NgModule({
+ imports: [
+ StoreModule // rely on Store feature providers rather than Store.forFeature()
+ ],
+ providers: [
+ EntityActionFactory,
+ entityCacheSelectorProvider,
+ EntityCollectionCreator,
+ EntityCollectionReducerFactory,
+ EntityDefinitionService,
+ EntityDispatcherFactory,
+ EntityReducerFactory,
+ EntitySelectorsFactory,
+ EntitySelectors$Factory,
+ {
+ provide: EntityCollectionReducerMethodsFactory,
+ useClass: DefaultEntityCollectionReducerMethodsFactory
+ },
+ { provide: ENTITY_CACHE_NAME_TOKEN, useValue: ENTITY_CACHE_NAME },
+ {
+ provide: ENTITY_CACHE_REDUCER,
+ deps: [EntityReducerFactory],
+ useFactory: createEntityReducer
+ },
+ {
+ provide: EntityCollectionServiceFactory,
+ useClass: DefaultEntityCollectionServiceFactory
+ },
+ {
+ provide: EntityServices,
+ useClass: EntityServicesBase
+ },
+ { provide: Logger, useClass: DefaultLogger }
+ ]
+})
+export class NgrxDataModuleWithoutEffects implements OnDestroy {
+ private entityCacheFeature: any;
+
+ static forRoot(config: NgrxDataModuleConfig): ModuleWithProviders {
+ return {
+ ngModule: NgrxDataModuleWithoutEffects,
+ providers: [
+ {
+ provide: ENTITY_CACHE_META_REDUCERS,
+ useValue: config.entityCacheMetaReducers
+ ? config.entityCacheMetaReducers
+ : []
+ },
+ {
+ provide: ENTITY_COLLECTION_META_REDUCERS,
+ useValue: config.entityCollectionMetaReducers
+ ? config.entityCollectionMetaReducers
+ : []
+ },
+ {
+ provide: PLURAL_NAMES_TOKEN,
+ multi: true,
+ useValue: config.pluralNames ? config.pluralNames : {}
+ }
+ ]
+ };
+ }
+
+ constructor(
+ private reducerManager: ReducerManager,
+ @Inject(ENTITY_CACHE_REDUCER)
+ private entityCacheReducer: ActionReducer,
+ private injector: Injector,
+ // optional params
+ @Optional()
+ @Inject(ENTITY_CACHE_NAME_TOKEN)
+ private entityCacheName: string,
+ @Optional()
+ @Inject(INITIAL_ENTITY_CACHE_STATE)
+ private initialState: any,
+ @Optional()
+ @Inject(ENTITY_CACHE_META_REDUCERS)
+ private metaReducers: (
+ | MetaReducer
+ | InjectionToken>)[]
+ ) {
+ // Add the ngrx-data feature to the Store's features
+ // as Store.forFeature does for StoreFeatureModule
+ const key = entityCacheName || ENTITY_CACHE_NAME;
+
+ initialState =
+ typeof initialState === 'function' ? initialState() : initialState;
+
+ const reducers: MetaReducer[] = (
+ metaReducers || []
+ ).map(mr => {
+ return mr instanceof InjectionToken ? injector.get(mr) : mr;
+ });
+
+ this.entityCacheFeature = {
+ key,
+ reducers: entityCacheReducer,
+ reducerFactory: combineReducers,
+ initialState: initialState || {},
+ metaReducers: reducers
+ };
+ reducerManager.addFeature(this.entityCacheFeature);
+ }
+
+ ngOnDestroy() {
+ this.reducerManager.removeFeature(this.entityCacheFeature);
+ }
+}
diff --git a/lib/src/ngrx-data.module.spec.ts b/lib/src/ngrx-data.module.spec.ts
index 78e09a05..958ca352 100644
--- a/lib/src/ngrx-data.module.spec.ts
+++ b/lib/src/ngrx-data.module.spec.ts
@@ -11,12 +11,12 @@ import { Actions, Effect, EffectsModule } from '@ngrx/effects';
// Not using marble testing
import { TestBed } from '@angular/core/testing';
-import { Observable, of } from 'rxjs';
-import { map, skip } from 'rxjs/operators';
+import { Observable, of, Subject } from 'rxjs';
+import { map, skip, tap } from 'rxjs/operators';
import { EntityAction, EntityActionFactory } from './actions/entity-action';
-import { EntityActions } from './actions/entity-actions';
import { EntityOp, OP_ERROR } from './actions/entity-op';
+import { ofEntityOp } from './actions/entity-action-operators';
import { EntityCache } from './reducers/entity-cache';
import { EntityCollection } from './reducers/entity-collection';
@@ -25,14 +25,6 @@ import { EntityCollectionCreator } from './reducers/entity-collection-creator';
import { EntityEffects, persistOps } from './effects/entity-effects';
import { NgrxDataModule } from './ngrx-data.module';
-class TestEntityActions extends EntityActions {
- set stream(source: Observable) {
- // source is deprecated but no known substitute
- /* tslint:disable-next-line:deprecation */
- this.source = source;
- }
-}
-
const TEST_ACTION = 'test/get-everything-succeeded';
const EC_METAREDUCER_TOKEN = new InjectionToken<
MetaReducer
@@ -41,9 +33,13 @@ const EC_METAREDUCER_TOKEN = new InjectionToken<
@Injectable()
class TestEntityEffects {
@Effect()
- test$: Observable = this.actions$
- .ofOp(persistOps)
- .pipe(map(this.testHook));
+ test$: Observable = this.actions.pipe(
+ // tap(action => {
+ // console.log('test$ effect', action);
+ // }),
+ ofEntityOp(persistOps),
+ map(this.testHook)
+ );
testHook(action: EntityAction) {
return {
@@ -53,7 +49,7 @@ class TestEntityEffects {
};
}
- constructor(private actions$: EntityActions) {}
+ constructor(private actions: Actions) {}
}
class Hero {
@@ -80,7 +76,6 @@ describe('NgrxDataModule', () => {
const entityActionFactory = new EntityActionFactory();
let actions$: Actions;
- let entityAction$: TestEntityActions;
let store: Store;
let testEffects: TestEntityEffects;
@@ -97,7 +92,6 @@ describe('NgrxDataModule', () => {
});
actions$ = TestBed.get(Actions);
- entityAction$ = TestBed.get(EntityActions);
store = TestBed.get(Store);
testEffects = TestBed.get(EntityEffects);
@@ -105,12 +99,17 @@ describe('NgrxDataModule', () => {
});
it('should invoke test effect with an EntityAction', () => {
- const action = entityActionFactory.create('Hero', EntityOp.QUERY_ALL);
const actions: Action[] = [];
// listen for actions after the next dispatched action
- actions$.pipe(skip(1)).subscribe(act => actions.push(act));
+ actions$
+ .pipe(
+ // tap(act => console.log('test action', act)),
+ skip(1) // Skip QUERY_ALL
+ )
+ .subscribe(act => actions.push(act));
+ const action = entityActionFactory.create('Hero', EntityOp.QUERY_ALL);
store.dispatch(action);
expect(actions.length).toBe(1, 'expect one effect action');
expect(actions[0].type).toBe('test-action');
@@ -122,7 +121,7 @@ describe('NgrxDataModule', () => {
// listen for actions after the next dispatched action
actions$.pipe(skip(1)).subscribe(act => actions.push(act));
- store.dispatch({ type: 'dummy-action' });
+ store.dispatch({ type: 'not-an-entity-action' });
expect(actions.length).toBe(0);
});
});
diff --git a/lib/src/ngrx-data.module.ts b/lib/src/ngrx-data.module.ts
index 4dc44b88..59ed07fe 100644
--- a/lib/src/ngrx-data.module.ts
+++ b/lib/src/ngrx-data.module.ts
@@ -1,24 +1,6 @@
-import {
- ModuleWithProviders,
- NgModule,
- Inject,
- Injector,
- InjectionToken,
- Optional,
- OnDestroy
-} from '@angular/core';
-import {
- Action,
- ActionReducer,
- combineReducers,
- MetaReducer,
- ReducerManager,
- StoreModule
-} from '@ngrx/store';
-import { EffectsModule, EffectSources } from '@ngrx/effects';
+import { ModuleWithProviders, NgModule } from '@angular/core';
-import { EntityAction, EntityActionFactory } from './actions/entity-action';
-import { EntityActions } from './actions/entity-actions';
+import { EffectsModule, EffectSources } from '@ngrx/effects';
import { DefaultDataServiceFactory } from './dataservices/default-data.service';
import { EntityDataService } from './dataservices/entity-data.service';
@@ -32,164 +14,34 @@ import {
DefaultHttpUrlGenerator
} from './dataservices/http-url-generator';
-import { EntityDispatcherFactory } from './dispatchers/entity-dispatcher-factory';
-
-import { EntityDefinitionService } from './entity-metadata/entity-definition.service';
-import {
- EntityMetadataMap,
- ENTITY_METADATA_TOKEN
-} from './entity-metadata/entity-metadata';
+import { ENTITY_METADATA_TOKEN } from './entity-metadata/entity-metadata';
-import { EntityCache } from './reducers/entity-cache';
-import { entityCacheSelectorProvider } from './selectors/entity-cache-selector';
-import { EntityCollectionServiceFactory } from './entity-services/entity-services-interfaces';
-import { DefaultEntityCollectionServiceFactory } from './entity-services/default-entity-collection-service-factory';
-import { EntityCollection } from './reducers/entity-collection';
-import { EntityCollectionCreator } from './reducers/entity-collection-creator';
-import {
- EntityCollectionReducerFactory,
- EntityCollectionReducerMethodsFactory
-} from './reducers/entity-collection-reducer';
import { EntityEffects } from './effects/entity-effects';
-import { DefaultEntityCollectionReducerMethodsFactory } from './reducers/default-entity-collection-reducer-methods';
-import {
- createEntityReducer,
- EntityReducerFactory
-} from './reducers/entity-reducer';
import {
- ENTITY_CACHE_NAME,
- ENTITY_CACHE_NAME_TOKEN,
ENTITY_CACHE_META_REDUCERS,
- ENTITY_COLLECTION_META_REDUCERS,
- ENTITY_CACHE_REDUCER,
- INITIAL_ENTITY_CACHE_STATE
+ ENTITY_COLLECTION_META_REDUCERS
} from './reducers/constants';
-
-import { Logger, Pluralizer, PLURAL_NAMES_TOKEN } from './utils/interfaces';
-
-import { EntitySelectors } from './selectors/entity-selectors';
-import { EntitySelectorsFactory } from './selectors/entity-selectors';
-import { EntitySelectors$Factory } from './selectors/entity-selectors$';
-import { EntityServices } from './entity-services/entity-services-interfaces';
-import { EntityServicesBase } from './entity-services/entity-services-base';
-
-import { DefaultLogger } from './utils/default-logger';
+import { Pluralizer, PLURAL_NAMES_TOKEN } from './utils/interfaces';
import { DefaultPluralizer } from './utils/default-pluralizer';
-export interface NgrxDataModuleConfig {
- entityMetadata?: EntityMetadataMap;
- entityCacheMetaReducers?: (
- | MetaReducer
- | InjectionToken>)[];
- entityCollectionMetaReducers?: MetaReducer[];
- // Initial EntityCache state or a function that returns that state
- initialEntityCacheState?: EntityCache | (() => EntityCache);
- pluralNames?: { [name: string]: string };
-}
-
-/**
- * Module without effects which means no HTTP calls
- * It is helpful for internal testing but not for users
- */
-@NgModule({
- imports: [
- StoreModule // rely on Store feature providers rather than Store.forFeature()
- ],
- providers: [
- EntityActionFactory,
- entityCacheSelectorProvider,
- EntityCollectionCreator,
- EntityCollectionReducerFactory,
- EntityDefinitionService,
- EntityDispatcherFactory,
- EntityReducerFactory,
- EntitySelectorsFactory,
- EntitySelectors$Factory,
- {
- provide: EntityCollectionReducerMethodsFactory,
- useClass: DefaultEntityCollectionReducerMethodsFactory
- },
- { provide: ENTITY_CACHE_NAME_TOKEN, useValue: ENTITY_CACHE_NAME },
- {
- provide: ENTITY_CACHE_REDUCER,
- deps: [EntityReducerFactory],
- useFactory: createEntityReducer
- },
- {
- provide: EntityCollectionServiceFactory,
- useClass: DefaultEntityCollectionServiceFactory
- },
- {
- provide: EntityServices,
- useClass: EntityServicesBase
- },
- { provide: Logger, useClass: DefaultLogger }
- ]
-})
-// tslint:disable-next-line:class-name
-export class _NgrxDataModuleWithoutEffects implements OnDestroy {
- private entityCacheFeature: any;
-
- constructor(
- private reducerManager: ReducerManager,
- @Inject(ENTITY_CACHE_REDUCER)
- private entityCacheReducer: ActionReducer,
- private injector: Injector,
- // optional params
- @Optional()
- @Inject(ENTITY_CACHE_NAME_TOKEN)
- private entityCacheName: string,
- @Optional()
- @Inject(INITIAL_ENTITY_CACHE_STATE)
- private initialState: any,
- @Optional()
- @Inject(ENTITY_CACHE_META_REDUCERS)
- private metaReducers: (
- | MetaReducer
- | InjectionToken>)[]
- ) {
- // Add the ngrx-data feature to the Store's features
- // as Store.forFeature does for StoreFeatureModule
- const key = entityCacheName || ENTITY_CACHE_NAME;
-
- initialState =
- typeof initialState === 'function' ? initialState() : initialState;
-
- const reducers: MetaReducer[] = (
- metaReducers || []
- ).map(mr => {
- return mr instanceof InjectionToken ? injector.get(mr) : mr;
- });
-
- this.entityCacheFeature = {
- key,
- reducers: entityCacheReducer,
- reducerFactory: combineReducers,
- initialState: initialState || {},
- metaReducers: reducers
- };
- reducerManager.addFeature(this.entityCacheFeature);
- }
-
- ngOnDestroy() {
- this.reducerManager.removeFeature(this.entityCacheFeature);
- }
-}
+import {
+ NgrxDataModuleConfig,
+ NgrxDataModuleWithoutEffects
+} from './ngrx-data-without-effects.module';
/**
- * Ngrx-data main module
+ * Ngrx-data main module includes effects and HTTP data services
* Configure with `forRoot`.
* No `forFeature` yet.
*/
@NgModule({
imports: [
- _NgrxDataModuleWithoutEffects,
+ NgrxDataModuleWithoutEffects,
EffectsModule // do not supply effects because can't replace later
],
providers: [
DefaultDataServiceFactory,
- EntityActions,
EntityDataService,
{ provide: HttpUrlGenerator, useClass: DefaultHttpUrlGenerator },
{ provide: Pluralizer, useClass: DefaultPluralizer },
diff --git a/lib/src/selectors/entity-selectors$.spec.ts b/lib/src/selectors/entity-selectors$.spec.ts
index 3a97e4c7..69d66ab4 100644
--- a/lib/src/selectors/entity-selectors$.spec.ts
+++ b/lib/src/selectors/entity-selectors$.spec.ts
@@ -1,9 +1,9 @@
import { Action, createSelector, MemoizedSelector, Store } from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { EntityAction, EntityActionFactory } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { EntityOp } from '../actions/entity-op';
import { EntityCache } from '../reducers/entity-cache';
@@ -72,7 +72,6 @@ describe('EntitySelectors$', () => {
let state$: BehaviorSubject<{ entityCache: EntityCache }>;
let actions$: Subject;
- let entityActions: EntityActions;
const nextCacheState = (cache: EntityCache) =>
state$.next({ entityCache: cache });
@@ -83,7 +82,6 @@ describe('EntitySelectors$', () => {
beforeEach(() => {
actions$ = new Subject();
- entityActions = new EntityActions(actions$);
state$ = new BehaviorSubject({ entityCache: emptyCache });
store = new Store<{ entityCache: EntityCache }>(state$, null, null);
@@ -103,7 +101,7 @@ describe('EntitySelectors$', () => {
// EntitySelectorFactory
factory = new EntitySelectors$Factory(
store,
- entityActions,
+ actions$ as any,
createEntityCacheSelector(ENTITY_CACHE_NAME)
);
diff --git a/lib/src/selectors/entity-selectors$.ts b/lib/src/selectors/entity-selectors$.ts
index 1ef7bd69..5bb3c0ee 100644
--- a/lib/src/selectors/entity-selectors$.ts
+++ b/lib/src/selectors/entity-selectors$.ts
@@ -6,13 +6,15 @@ import {
Selector,
Store
} from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
import { Observable } from 'rxjs';
+import { filter } from 'rxjs/operators';
import { Dictionary } from '../utils/ngrx-entity-models';
import { EntityAction } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { OP_ERROR } from '../actions/entity-op';
+import { ofEntityType } from '../actions/entity-action-operators';
import {
ENTITY_CACHE_SELECTOR_TOKEN,
EntityCacheSelector
@@ -40,13 +42,13 @@ export interface EntitySelectors$ {
readonly entities$: Observable | Store;
/** Observable of actions related to this entity type. */
- readonly entityActions$: EntityActions;
+ readonly entityActions$: Observable;
/** Observable of the map of entity keys to entities */
readonly entityMap$: Observable> | Store>;
/** Observable of error actions related to this entity type. */
- readonly errors$: EntityActions;
+ readonly errors$: Observable;
/** Observable of the filter pattern applied by the entity collection's filter function */
readonly filter$: Observable | Store;
@@ -74,7 +76,7 @@ export class EntitySelectors$Factory {
constructor(
private store: Store,
- private entityActions$: EntityActions,
+ private actions: Actions,
@Inject(ENTITY_CACHE_SELECTOR_TOKEN)
private selectEntityCache: EntityCacheSelector
) {
@@ -104,9 +106,9 @@ export class EntitySelectors$Factory {
selectors$[name$] = this.store.select((selectors)[name]);
}
});
- selectors$.entityActions$ = this.entityActions$.ofEntityType(entityName);
- selectors$.errors$ = selectors$.entityActions$.where((ea: EntityAction) =>
- ea.op.endsWith(OP_ERROR)
+ selectors$.entityActions$ = this.actions.pipe(ofEntityType(entityName));
+ selectors$.errors$ = selectors$.entityActions$.pipe(
+ filter((ea: EntityAction) => ea.op.endsWith(OP_ERROR))
);
return selectors$ as S$;
}
diff --git a/lib/src/selectors/related-entity-selectors.spec.ts b/lib/src/selectors/related-entity-selectors.spec.ts
index 0c72c20b..5b4fcdfe 100644
--- a/lib/src/selectors/related-entity-selectors.spec.ts
+++ b/lib/src/selectors/related-entity-selectors.spec.ts
@@ -6,13 +6,13 @@ import {
StoreModule,
Store
} from '@ngrx/store';
+import { Actions } from '@ngrx/effects';
import { Observable, Subject } from 'rxjs';
import { skip } from 'rxjs/operators';
import { Dictionary, Update } from '../utils/ngrx-entity-models';
import { EntityAction, EntityActionFactory } from '../actions/entity-action';
-import { EntityActions } from '../actions/entity-actions';
import { EntityOp } from '../actions/entity-op';
import { EntityCache } from '../reducers/entity-cache';
@@ -26,7 +26,7 @@ import {
import { EntitySelectorsFactory } from '../selectors/entity-selectors';
-import { _NgrxDataModuleWithoutEffects } from '../ngrx-data.module';
+import { NgrxDataModuleWithoutEffects } from '../ngrx-data-without-effects.module';
const entityMetadataMap: EntityMetadataMap = {
Battle: {},
@@ -46,10 +46,10 @@ describe('Related-entity Selectors', () => {
beforeEach(() => {
TestBed.configureTestingModule({
- imports: [StoreModule.forRoot({}), _NgrxDataModuleWithoutEffects],
+ imports: [StoreModule.forRoot({}), NgrxDataModuleWithoutEffects],
providers: [
// required by NgrxData but not used in these tests
- { provide: EntityActions, useValue: null },
+ { provide: Actions, useValue: null },
{
provide: ENTITY_METADATA_TOKEN,
multi: true,
diff --git a/lib/src/utils/utilities.ts b/lib/src/utils/utilities.ts
index 66989455..b75ff129 100644
--- a/lib/src/utils/utilities.ts
+++ b/lib/src/utils/utilities.ts
@@ -15,10 +15,10 @@ export function defaultSelectId(entity: any) {
* Allows fn with ...rest signature to be called with an array instead of spread
* Example:
* ```
- * // EntityActions.ofOp
+ * // See entity-action-operators.ts
* const persistOps = [EntityOp.QUERY_ALL, EntityOp.ADD, ...];
- * ofOp(...persistOps) // works
- * ofOp(persistOps) // also works
+ * actions.pipe(ofEntityOp(...persistOps)) // works
+ * actions.pipe(ofEntityOp(persistOps)) // also works
* ```
* */
export function flattenArgs(args?: any[]): T[] {
diff --git a/package-lock.json b/package-lock.json
index 9212d77f..2e1383c6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3479,6 +3479,13 @@
"integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
"dev": true
},
+ "async-each-series": {
+ "version": "0.1.1",
+ "resolved":
+ "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz",
+ "integrity": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=",
+ "dev": true
+ },
"async-foreach": {
"version": "0.1.3",
"resolved":
@@ -4129,6 +4136,282 @@
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
"dev": true
},
+ "browser-sync": {
+ "version": "2.24.4",
+ "resolved":
+ "https://registry.npmjs.org/browser-sync/-/browser-sync-2.24.4.tgz",
+ "integrity":
+ "sha512-qfXv8vQA/Dctub2v44v/vPuvfC4XNd6bn+W5vWZVuhuy6w91lPsdY6qhalT2s2PjnJ3FR6kWq5wkTQgN26eKzA==",
+ "dev": true,
+ "requires": {
+ "browser-sync-ui": "1.0.1",
+ "bs-recipes": "1.3.4",
+ "chokidar": "1.7.0",
+ "connect": "3.5.0",
+ "connect-history-api-fallback": "1.5.0",
+ "dev-ip": "1.0.1",
+ "easy-extender": "2.3.2",
+ "eazy-logger": "3.0.2",
+ "etag": "1.8.1",
+ "fresh": "0.5.2",
+ "fs-extra": "3.0.1",
+ "http-proxy": "1.15.2",
+ "immutable": "3.8.2",
+ "localtunnel": "1.9.0",
+ "micromatch": "2.3.11",
+ "opn": "4.0.2",
+ "portscanner": "2.1.1",
+ "qs": "6.2.3",
+ "raw-body": "2.3.2",
+ "resp-modifier": "6.0.2",
+ "rx": "4.1.0",
+ "serve-index": "1.8.0",
+ "serve-static": "1.13.2",
+ "server-destroy": "1.0.1",
+ "socket.io": "2.0.4",
+ "ua-parser-js": "0.7.17",
+ "yargs": "6.4.0"
+ },
+ "dependencies": {
+ "batch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.3.tgz",
+ "integrity": "sha1-PzQU84AyF0O/wQQvmoP/HVgk1GQ=",
+ "dev": true
+ },
+ "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.2",
+ "strip-ansi": "3.0.1",
+ "wrap-ansi": "2.1.0"
+ }
+ },
+ "connect": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.5.0.tgz",
+ "integrity": "sha1-s1dSWgtMH1BZnNmD4dnv7qlncZg=",
+ "dev": true,
+ "requires": {
+ "debug": "2.2.0",
+ "finalhandler": "0.5.0",
+ "parseurl": "1.3.2",
+ "utils-merge": "1.0.0"
+ }
+ },
+ "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"
+ }
+ },
+ "eventemitter3": {
+ "version": "1.2.0",
+ "resolved":
+ "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
+ "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=",
+ "dev": true
+ },
+ "finalhandler": {
+ "version": "0.5.0",
+ "resolved":
+ "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.0.tgz",
+ "integrity": "sha1-6VCKvs6bbbqHGmlCodeRG5GRGsc=",
+ "dev": true,
+ "requires": {
+ "debug": "2.2.0",
+ "escape-html": "1.0.3",
+ "on-finished": "2.3.0",
+ "statuses": "1.3.1",
+ "unpipe": "1.0.0"
+ }
+ },
+ "fs-extra": {
+ "version": "3.0.1",
+ "resolved":
+ "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz",
+ "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "jsonfile": "3.0.1",
+ "universalify": "0.1.1"
+ }
+ },
+ "http-errors": {
+ "version": "1.5.1",
+ "resolved":
+ "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz",
+ "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "setprototypeof": "1.0.2",
+ "statuses": "1.3.1"
+ }
+ },
+ "http-proxy": {
+ "version": "1.15.2",
+ "resolved":
+ "https://registry.npmjs.org/http-proxy/-/http-proxy-1.15.2.tgz",
+ "integrity": "sha1-ZC/cr/5S00SNK9o7AHnpQJBk2jE=",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "1.2.0",
+ "requires-port": "1.0.0"
+ }
+ },
+ "jsonfile": {
+ "version": "3.0.1",
+ "resolved":
+ "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz",
+ "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
+ "dev": true
+ },
+ "opn": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz",
+ "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=",
+ "dev": true,
+ "requires": {
+ "object-assign": "4.1.1",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "qs": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz",
+ "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=",
+ "dev": true
+ },
+ "rx": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz",
+ "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=",
+ "dev": true
+ },
+ "serve-index": {
+ "version": "1.8.0",
+ "resolved":
+ "https://registry.npmjs.org/serve-index/-/serve-index-1.8.0.tgz",
+ "integrity": "sha1-fF2WwT+xMRAfk8HFd0+FFqHnjTs=",
+ "dev": true,
+ "requires": {
+ "accepts": "1.3.5",
+ "batch": "0.5.3",
+ "debug": "2.2.0",
+ "escape-html": "1.0.3",
+ "http-errors": "1.5.1",
+ "mime-types": "2.1.18",
+ "parseurl": "1.3.2"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.0.2",
+ "resolved":
+ "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz",
+ "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg=",
+ "dev": true
+ },
+ "statuses": {
+ "version": "1.3.1",
+ "resolved":
+ "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+ "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.0",
+ "resolved":
+ "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
+ "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=",
+ "dev": true
+ },
+ "window-size": {
+ "version": "0.2.0",
+ "resolved":
+ "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz",
+ "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.4.0.tgz",
+ "integrity": "sha1-gW4ahm1VmMzzTlWW3c4i2S2kkNQ=",
+ "dev": true,
+ "requires": {
+ "camelcase": "3.0.0",
+ "cliui": "3.2.0",
+ "decamelize": "1.2.0",
+ "get-caller-file": "1.0.2",
+ "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",
+ "window-size": "0.2.0",
+ "y18n": "3.2.1",
+ "yargs-parser": "4.2.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "4.2.1",
+ "resolved":
+ "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
+ "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=",
+ "dev": true,
+ "requires": {
+ "camelcase": "3.0.0"
+ }
+ }
+ }
+ },
+ "browser-sync-ui": {
+ "version": "1.0.1",
+ "resolved":
+ "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-1.0.1.tgz",
+ "integrity":
+ "sha512-RIxmwVVcUFhRd1zxp7m2FfLnXHf59x4Gtj8HFwTA//3VgYI3AKkaQAuDL8KDJnE59XqCshxZa13JYuIWtZlKQg==",
+ "dev": true,
+ "requires": {
+ "async-each-series": "0.1.1",
+ "connect-history-api-fallback": "1.5.0",
+ "immutable": "3.8.2",
+ "server-destroy": "1.0.1",
+ "socket.io-client": "2.0.4",
+ "stream-throttle": "0.1.3"
+ }
+ },
"browserify-aes": {
"version": "1.2.0",
"resolved":
@@ -4221,6 +4504,13 @@
"electron-to-chromium": "1.3.45"
}
},
+ "bs-recipes": {
+ "version": "1.3.4",
+ "resolved":
+ "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz",
+ "integrity": "sha1-DS1NSKcYyMBEdp/cT4lZLci2lYU=",
+ "dev": true
+ },
"bson": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.0.6.tgz",
@@ -5020,6 +5310,16 @@
"integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=",
"dev": true
},
+ "connect-logger": {
+ "version": "0.0.1",
+ "resolved":
+ "https://registry.npmjs.org/connect-logger/-/connect-logger-0.0.1.tgz",
+ "integrity": "sha1-TZmZeKHSC7RgjnzUNNdBZSJVF0s=",
+ "dev": true,
+ "requires": {
+ "moment": "2.22.1"
+ }
+ },
"console-browserify": {
"version": "1.1.0",
"resolved":
@@ -5662,6 +5962,12 @@
"integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=",
"dev": true
},
+ "dev-ip": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz",
+ "integrity": "sha1-p2o+0YVb56ASu4rBbLgPPADcKPA=",
+ "dev": true
+ },
"di": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
@@ -5861,6 +6167,34 @@
"stream-shift": "1.0.0"
}
},
+ "easy-extender": {
+ "version": "2.3.2",
+ "resolved":
+ "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.2.tgz",
+ "integrity": "sha1-PTJI/r4rFZYHMW2PnPSRwWZIIh0=",
+ "dev": true,
+ "requires": {
+ "lodash": "3.10.1"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+ "dev": true
+ }
+ }
+ },
+ "eazy-logger": {
+ "version": "3.0.2",
+ "resolved":
+ "https://registry.npmjs.org/eazy-logger/-/eazy-logger-3.0.2.tgz",
+ "integrity": "sha1-oyWqXlPROiIliJsqxBE7K5Y29Pw=",
+ "dev": true,
+ "requires": {
+ "tfunk": "3.1.0"
+ }
+ },
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
@@ -8961,6 +9295,12 @@
"dev": true,
"optional": true
},
+ "immutable": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
+ "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=",
+ "dev": true
+ },
"import-lazy": {
"version": "2.1.0",
"resolved":
@@ -9366,6 +9706,17 @@
"kind-of": "3.2.2"
}
},
+ "is-number-like": {
+ "version": "1.0.8",
+ "resolved":
+ "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz",
+ "integrity":
+ "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==",
+ "dev": true,
+ "requires": {
+ "lodash.isfinite": "3.3.2"
+ }
+ },
"is-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
@@ -10378,6 +10729,36 @@
"ejs": "2.5.9"
}
},
+ "limiter": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.3.tgz",
+ "integrity":
+ "sha512-zrycnIMsLw/3ZxTbW7HCez56rcFGecWTx5OZNplzcXUUmJLmoYArC6qdJzmAN5BWiNXGcpjhF9RQ1HSv5zebEw==",
+ "dev": true
+ },
+ "lite-server": {
+ "version": "2.3.0",
+ "resolved":
+ "https://registry.npmjs.org/lite-server/-/lite-server-2.3.0.tgz",
+ "integrity": "sha1-W0zI9dX9SDYQVICrKsSKOg3isMg=",
+ "dev": true,
+ "requires": {
+ "browser-sync": "2.24.4",
+ "connect-history-api-fallback": "1.5.0",
+ "connect-logger": "0.0.1",
+ "lodash": "4.17.5",
+ "minimist": "1.2.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved":
+ "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
"load-json-file": {
"version": "1.1.0",
"resolved":
@@ -10419,6 +10800,96 @@
"json5": "0.5.1"
}
},
+ "localtunnel": {
+ "version": "1.9.0",
+ "resolved":
+ "https://registry.npmjs.org/localtunnel/-/localtunnel-1.9.0.tgz",
+ "integrity":
+ "sha512-wCIiIHJ8kKIcWkTQE3m1VRABvsH2ZuOkiOpZUofUCf6Q42v3VIZ+Q0YfX1Z4sYDRj0muiKL1bLvz1FeoxsPO0w==",
+ "dev": true,
+ "requires": {
+ "axios": "0.17.1",
+ "debug": "2.6.8",
+ "openurl": "1.1.1",
+ "yargs": "6.6.0"
+ },
+ "dependencies": {
+ "axios": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz",
+ "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=",
+ "dev": true,
+ "requires": {
+ "follow-redirects": "1.4.1",
+ "is-buffer": "1.1.6"
+ }
+ },
+ "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.2",
+ "strip-ansi": "3.0.1",
+ "wrap-ansi": "2.1.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.8",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
+ "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+ "dev": true,
+ "requires": {
+ "ms": "2.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": "6.6.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
+ "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
+ "dev": true,
+ "requires": {
+ "camelcase": "3.0.0",
+ "cliui": "3.2.0",
+ "decamelize": "1.2.0",
+ "get-caller-file": "1.0.2",
+ "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": "4.2.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "4.2.1",
+ "resolved":
+ "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
+ "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=",
+ "dev": true,
+ "requires": {
+ "camelcase": "3.0.0"
+ }
+ }
+ }
+ },
"locate-path": {
"version": "2.0.0",
"resolved":
@@ -10470,6 +10941,13 @@
"https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
+ "lodash.isfinite": {
+ "version": "3.3.2",
+ "resolved":
+ "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz",
+ "integrity": "sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=",
+ "dev": true
+ },
"lodash.mergewith": {
"version": "4.6.1",
"resolved":
@@ -11123,6 +11601,13 @@
}
}
},
+ "moment": {
+ "version": "2.22.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz",
+ "integrity":
+ "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==",
+ "dev": true
+ },
"mongodb": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.7.tgz",
@@ -12105,6 +12590,13 @@
"integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
"dev": true
},
+ "object-path": {
+ "version": "0.9.2",
+ "resolved":
+ "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz",
+ "integrity": "sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=",
+ "dev": true
+ },
"object-visit": {
"version": "1.0.1",
"resolved":
@@ -12226,6 +12718,12 @@
"integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=",
"dev": true
},
+ "openurl": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz",
+ "integrity": "sha1-OHW0sO96UsFW8NtB1GCduw+Us4c=",
+ "dev": true
+ },
"opn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz",
@@ -12709,6 +13207,17 @@
"mkdirp": "0.5.1"
}
},
+ "portscanner": {
+ "version": "2.1.1",
+ "resolved":
+ "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz",
+ "integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=",
+ "dev": true,
+ "requires": {
+ "async": "1.5.2",
+ "is-number-like": "1.0.8"
+ }
+ },
"posix-character-classes": {
"version": "0.1.1",
"resolved":
@@ -13849,6 +14358,17 @@
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
"dev": true
},
+ "resp-modifier": {
+ "version": "6.0.2",
+ "resolved":
+ "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz",
+ "integrity": "sha1-sSTeXE+6/LpUH0j/pzlw9KpFa08=",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "minimatch": "3.0.4"
+ }
+ },
"restore-cursor": {
"version": "2.0.0",
"resolved":
@@ -14297,6 +14817,13 @@
"send": "0.16.2"
}
},
+ "server-destroy": {
+ "version": "1.0.1",
+ "resolved":
+ "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
+ "integrity": "sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0=",
+ "dev": true
+ },
"set-blocking": {
"version": "2.0.0",
"resolved":
@@ -15647,6 +16174,17 @@
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
"dev": true
},
+ "stream-throttle": {
+ "version": "0.1.3",
+ "resolved":
+ "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz",
+ "integrity": "sha1-rdV8jXzHOoFjDTHNVdOWHPr7qcM=",
+ "dev": true,
+ "requires": {
+ "commander": "2.14.1",
+ "limiter": "1.1.3"
+ }
+ },
"streamroller": {
"version": "0.7.0",
"resolved":
@@ -15917,6 +16455,45 @@
"execa": "0.7.0"
}
},
+ "tfunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tfunk/-/tfunk-3.1.0.tgz",
+ "integrity": "sha1-OORBT8ZJd9h6/apy+sttKfgve1s=",
+ "dev": true,
+ "requires": {
+ "chalk": "1.1.3",
+ "object-path": "0.9.2"
+ },
+ "dependencies": {
+ "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.5",
+ "has-ansi": "2.0.0",
+ "strip-ansi": "3.0.1",
+ "supports-color": "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
+ }
+ }
+ },
"then-fs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/then-fs/-/then-fs-2.0.0.tgz",
@@ -16358,6 +16935,14 @@
"sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==",
"dev": true
},
+ "ua-parser-js": {
+ "version": "0.7.17",
+ "resolved":
+ "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
+ "integrity":
+ "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==",
+ "dev": true
+ },
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
diff --git a/package.json b/package.json
index da284b4f..0d5fb727 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"start": "ng serve -o",
"build": "ng build",
"build-prod": "ng build --prod --source-map",
+ "start-lite": "lite-server --baseDir=\"dist/app\"",
"build-lib": "ng-packagr -p lib",
"build-all": "rm -rf dist && npm run build-setup && npm run build-prod",
"build-setup":
@@ -82,6 +83,7 @@
"karma-coverage-istanbul-reporter": "^1.4.2",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
+ "lite-server": "^2.3.0",
"ng-packagr": "^3.0.0-rc.5",
"prettier": "1.12.1",
"pretty-quick": "^1.4.1",
diff --git a/src/app/store/entity/ngrx-data-toast.service.ts b/src/app/store/entity/ngrx-data-toast.service.ts
index d220d93e..0ea18e46 100644
--- a/src/app/store/entity/ngrx-data-toast.service.ts
+++ b/src/app/store/entity/ngrx-data-toast.service.ts
@@ -1,13 +1,22 @@
import { Injectable } from '@angular/core';
-import { EntityActions, OP_ERROR, OP_SUCCESS } from 'ngrx-data';
+import { Actions } from '@ngrx/effects';
+
+import { filter } from 'rxjs/operators';
+import { EntityAction, ofEntityOp, OP_ERROR, OP_SUCCESS } from 'ngrx-data';
import { ToastService } from '../../core/toast.service';
/** Report ngrx-data success/error actions as toast messages **/
@Injectable()
export class NgrxDataToastService {
- constructor(actions$: EntityActions, toast: ToastService) {
+ constructor(actions$: Actions, toast: ToastService) {
actions$
- .where(ea => ea.op.endsWith(OP_SUCCESS) || ea.op.endsWith(OP_ERROR))
+ .pipe(
+ ofEntityOp(),
+ filter(
+ (ea: EntityAction) =>
+ ea.op.endsWith(OP_SUCCESS) || ea.op.endsWith(OP_ERROR)
+ )
+ )
// this service never dies so no need to unsubscribe
.subscribe(action =>
toast.openSnackBar(`${action.entityName} action`, action.op)
diff --git a/src/app/villains/villain-editor/villain-editor.component.ts b/src/app/villains/villain-editor/villain-editor.component.ts
index 05ebe631..342a972b 100644
--- a/src/app/villains/villain-editor/villain-editor.component.ts
+++ b/src/app/villains/villain-editor/villain-editor.component.ts
@@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
-import { EntityOp } from 'ngrx-data';
+import { EntityOp, ofEntityOp } from 'ngrx-data';
import { combineLatest, Observable, Subject } from 'rxjs';
import { delay, map, shareReplay, startWith, takeUntil } from 'rxjs/operators';
@@ -45,15 +45,14 @@ export class VillainEditorComponent implements OnInit, OnDestroy {
shareReplay(1)
);
- this.error$ = this.villainsService.errors$
- .ofOp(EntityOp.QUERY_BY_KEY_ERROR)
- .pipe(
- map(errorAction => errorAction.payload.error.message),
- // delay guards against `ExpressionChangedAfterItHasBeenCheckedError`
- delay(1),
- // startWith(''), // prime it for loading$
- takeUntil(this.destroy$)
- );
+ this.error$ = this.villainsService.errors$.pipe(
+ ofEntityOp(EntityOp.QUERY_BY_KEY_ERROR),
+ map(errorAction => errorAction.payload.error.message),
+ // delay guards against `ExpressionChangedAfterItHasBeenCheckedError`
+ delay(1),
+ // startWith(''), // prime it for loading$
+ takeUntil(this.destroy$)
+ );
this.loading$ = combineLatest(this.error$, this.villain$).pipe(
map(([errorMsg, villain]) => !villain && !errorMsg)