Skip to content

Commit

Permalink
fix: Do not expose internal stores to extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
mnasyrov committed Jan 11, 2022
1 parent 1121e86 commit 27420cb
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/rx-effects/src/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export type Effect<Event, Result = void, ErrorType = Error> = EffectState<
readonly destroy: () => void;
};

const PENDING_COUNT_STATE = declareState(0);
const PENDING_COUNT_STATE = declareState(0, { internal: true });
const increaseCount = (count: number): number => count + 1;
const decreaseCount = (count: number): number => count - 1;

Expand Down
7 changes: 5 additions & 2 deletions packages/rx-effects/src/stateDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export type StateDeclarationOptions<State> = Readonly<{

/** A comparator for detecting changes between old and new states */
stateComparator?: (prevState: State, nextState: State) => boolean;

/** @internal */
internal?: boolean;
}>;

/**
Expand All @@ -56,7 +59,7 @@ export function declareState<State>(
stateOrFactory: State | StateFactory<State>,
options?: StateDeclarationOptions<State>,
): StateDeclaration<State> {
const { name, stateComparator } = options ?? {};
const { name, stateComparator, internal } = options ?? {};

let initialState: State;
let stateFactory: StateFactory<State>;
Expand All @@ -79,7 +82,7 @@ export function declareState<State>(

const storeFactory = (values?: Partial<State>): Store<State> => {
const state = stateFactory(values);
return createStore(state, { name, stateComparator });
return createStore(state, { name, stateComparator, internal });
};

return {
Expand Down
8 changes: 8 additions & 0 deletions packages/rx-effects/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Controller } from './controller';
import { StateMutation } from './stateMutation';
import { mapQuery, StateQuery, StateQueryOptions } from './stateQuery';
import { STORE_EVENT_BUS } from './storeEvents';
import { setInternalStoreFlag } from './storeMetadata';
import { DEFAULT_COMPARATOR, isReadonlyArray } from './utils';

let STORE_SEQ_NUMBER = 0;
Expand Down Expand Up @@ -67,6 +68,9 @@ export type StoreOptions<State> = Readonly<{

/** A comparator for detecting changes between old and new states */
stateComparator?: (prevState: State, nextState: State) => boolean;

/** @internal */
internal?: boolean;
}>;

/**
Expand Down Expand Up @@ -180,6 +184,10 @@ export function createStore<State>(
}
}

if (options?.internal) {
setInternalStoreFlag(store);
}

STORE_EVENT_BUS.next({ type: 'created', store });

return store;
Expand Down
15 changes: 15 additions & 0 deletions packages/rx-effects/src/storeExtensions.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createAction } from './action';
import { createScope } from './scope';
import { createStore } from './store';
import { registerStoreExtension } from './storeExtensions';
import { createStoreLoggerExtension } from './storeLoggerExtension';

describe('registerStoreExtension()', () => {
it('should register an extension', () => {
Expand All @@ -22,4 +25,16 @@ describe('registerStoreExtension()', () => {
createStore<number>(0, { name: 'test' });
expect.assertions(0);
});

it('should not emit events from internal stores', () => {
const eventHandler = jest.fn();

registerStoreExtension(() => ({
onStoreEvent: eventHandler,
}));

createStore<number>(0, { name: 'test', internal: true });

expect(eventHandler).toBeCalledTimes(0);
});
});
6 changes: 4 additions & 2 deletions packages/rx-effects/src/storeExtensions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Subscription } from 'rxjs';
import { STORE_EVENT_BUS, StoreEvent } from './storeEvents';
import { getStateMutationMetadata } from './storeMetadata';
import { getStateMutationMetadata, isInternalStore } from './storeMetadata';

export type StoreExtensionApi = Readonly<{
getStateMutationMetadata: typeof getStateMutationMetadata;
Expand All @@ -20,6 +20,8 @@ export function registerStoreExtension(
const middleware = extension(api);

return STORE_EVENT_BUS.subscribe((event) => {
middleware.onStoreEvent?.(event);
if (middleware.onStoreEvent && !isInternalStore(event.store)) {
middleware.onStoreEvent(event);
}
});
}
17 changes: 17 additions & 0 deletions packages/rx-effects/src/storeLoggerExtension.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { createAction } from './action';
import { createScope } from './scope';
import { StateMutation } from './stateMutation';
import { createStore } from './store';
import { createStoreActions } from './storeActions';
Expand Down Expand Up @@ -62,4 +64,19 @@ describe('createStoreLoggerExtension()', () => {
createStore<number>(0);
expect(logger).nthCalledWith(1, expect.stringMatching(/^#\d/), 'created');
});

it('should not log events from internal stores', () => {
const logger = jest.fn();

registerStoreExtension(createStoreLoggerExtension(logger));

const scope = createScope();
const action = createAction<number>();
scope.handleAction(action, () => {
// Do nothing
});
action(1);

expect(logger).toBeCalledTimes(0);
});
});
10 changes: 10 additions & 0 deletions packages/rx-effects/src/storeMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { StateMutation } from './stateMutation';
import { Store } from './store';

const MUTATION_NAME_SYMBOL = Symbol();
const INTERNAL_STORE_SYMBOL = Symbol();

export type StateMutationMetadata = Readonly<{
name?: string;
Expand All @@ -21,3 +23,11 @@ export function setStateMutationName<State>(
): void {
(mutation as any)[MUTATION_NAME_SYMBOL] = name;
}

export function isInternalStore(store: Store<any>): boolean {
return (store as any)[INTERNAL_STORE_SYMBOL] === true;
}

export function setInternalStoreFlag(store: Store<any>): void {
(store as any)[INTERNAL_STORE_SYMBOL] = true;
}

0 comments on commit 27420cb

Please sign in to comment.