feat(extension): replace whitelist/blacklist with allowlist/denylist (#851)

This commit is contained in:
Nathan Bierema 2021-09-06 20:07:44 +00:00 committed by GitHub
parent 1695dee135
commit 42728c2114
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 108 additions and 70 deletions

View File

@ -100,7 +100,7 @@ To specify [extensions options](https://github.com/zalmoxisus/redux-devtools-
const composeEnhancers = const composeEnhancers =
typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extensions options like name, actionsBlacklist, actionsCreators, serialize... // Specify extensions options like name, actionsDenylist, actionsCreators, serialize...
}) })
: compose; : compose;
@ -143,7 +143,7 @@ import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension'; import { composeWithDevTools } from 'redux-devtools-extension';
const composeEnhancers = composeWithDevTools({ const composeEnhancers = composeWithDevTools({
// Specify name here, actionsBlacklist, actionsCreators and other options if needed // Specify name here, actionsDenylist, actionsCreators and other options if needed
}); });
const store = createStore( const store = createStore(
reducer, reducer,
@ -165,7 +165,7 @@ import { devToolsEnhancer } from 'redux-devtools-extension';
const store = createStore( const store = createStore(
reducer, reducer,
/* preloadedState, */ devToolsEnhancer() /* preloadedState, */ devToolsEnhancer()
// Specify name here, actionsBlacklist, actionsCreators and other options if needed // Specify name here, actionsDenylist, actionsCreators and other options if needed
); );
``` ```

View File

@ -240,9 +240,9 @@ const store = createStore(
); );
``` ```
### `actionsBlacklist` / `actionsWhitelist` ### `actionsDenylist` / `actionsAllowlist`
_string or array of strings as regex_ - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsWhitelist` specified, `actionsBlacklist` is ignored. _string or array of strings as regex_ - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsAllowlist` specified, `actionsDenylist` is ignored.
Example: Example:
@ -251,16 +251,16 @@ createStore(
reducer, reducer,
remotedev({ remotedev({
sendTo: 'http://localhost:8000', sendTo: 'http://localhost:8000',
actionsBlacklist: 'SOME_ACTION', actionsDenylist: 'SOME_ACTION',
// or actionsBlacklist: ['SOME_ACTION', 'SOME_OTHER_ACTION'] // or actionsDenylist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
// or just actionsBlacklist: 'SOME_' to omit both // or just actionsDenylist: 'SOME_' to omit both
}) })
); );
``` ```
### `predicate` ### `predicate`
_function_ - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters. _function_ - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsDenylist`/`actionsAllowlist` parameters.
Example of usage: Example of usage:
```js ```js

View File

@ -65,13 +65,13 @@ const store = createStore(
/* preloadedState, */ compose( /* preloadedState, */ compose(
devToolsEnhancer({ devToolsEnhancer({
instaceID: 1, instaceID: 1,
name: 'Blacklisted', name: 'Denylisted',
actionsBlacklist: '...', actionsDenylist: '...',
}), }),
devToolsEnhancer({ devToolsEnhancer({
instaceID: 2, instaceID: 2,
name: 'Whitelisted', name: 'Allowlisted',
actionsWhitelist: '...', actionsAllowlist: '...',
}) })
) )
); );

View File

@ -5,13 +5,13 @@ import { LiftedState, PerformAction } from '@redux-devtools/instrument';
export type FilterStateValue = export type FilterStateValue =
| 'DO_NOT_FILTER' | 'DO_NOT_FILTER'
| 'BLACKLIST_SPECIFIC' | 'DENYLIST_SPECIFIC'
| 'WHITELIST_SPECIFIC'; | 'ALLOWLIST_SPECIFIC';
export const FilterState: { [K in FilterStateValue]: FilterStateValue } = { export const FilterState: { [K in FilterStateValue]: FilterStateValue } = {
DO_NOT_FILTER: 'DO_NOT_FILTER', DO_NOT_FILTER: 'DO_NOT_FILTER',
BLACKLIST_SPECIFIC: 'BLACKLIST_SPECIFIC', DENYLIST_SPECIFIC: 'DENYLIST_SPECIFIC',
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC', ALLOWLIST_SPECIFIC: 'ALLOWLIST_SPECIFIC',
}; };
function isArray(arg: unknown): arg is readonly unknown[] { function isArray(arg: unknown): arg is readonly unknown[] {
@ -19,19 +19,17 @@ function isArray(arg: unknown): arg is readonly unknown[] {
} }
interface LocalFilter { interface LocalFilter {
readonly whitelist: string | undefined; readonly allowlist: string | undefined;
readonly blacklist: string | undefined; readonly denylist: string | undefined;
} }
export function getLocalFilter(config: Config): LocalFilter | undefined { export function getLocalFilter(config: Config): LocalFilter | undefined {
if (config.actionsBlacklist || config.actionsWhitelist) { const denylist = config.actionsDenylist ?? config.actionsBlacklist;
const allowlist = config.actionsAllowlist ?? config.actionsWhitelist;
if (denylist || allowlist) {
return { return {
whitelist: isArray(config.actionsWhitelist) allowlist: isArray(allowlist) ? allowlist.join('|') : allowlist,
? config.actionsWhitelist.join('|') denylist: isArray(denylist) ? denylist.join('|') : denylist,
: config.actionsWhitelist,
blacklist: isArray(config.actionsBlacklist)
? config.actionsBlacklist.join('|')
: config.actionsBlacklist,
}; };
} }
return undefined; return undefined;
@ -56,11 +54,11 @@ export function isFiltered<A extends Action<unknown>>(
return false; return false;
} }
const { whitelist, blacklist } = localFilter || window.devToolsOptions || {}; const { allowlist, denylist } = localFilter || window.devToolsOptions || {};
const actionType = ((action as A).type || action) as string; const actionType = ((action as A).type || action) as string;
return ( return (
(whitelist && !actionType.match(whitelist)) || (allowlist && !actionType.match(allowlist)) ||
(blacklist && actionType.match(blacklist)) (denylist && actionType.match(denylist))
); );
} }

View File

@ -72,7 +72,7 @@ let reportId: string | null | undefined;
function deprecateParam(oldParam: string, newParam: string) { function deprecateParam(oldParam: string, newParam: string) {
/* eslint-disable no-console */ /* eslint-disable no-console */
console.warn( console.warn(
`${oldParam} parameter is deprecated, use ${newParam} instead: https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md` `${oldParam} parameter is deprecated, use ${newParam} instead: https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md`
); );
/* eslint-enable no-console */ /* eslint-enable no-console */
} }
@ -84,8 +84,16 @@ export interface SerializeWithImmutable extends Serialize {
export interface ConfigWithExpandedMaxAge { export interface ConfigWithExpandedMaxAge {
instanceId?: number; instanceId?: number;
/**
* @deprecated Use actionsDenylist instead.
*/
readonly actionsBlacklist?: string | readonly string[]; readonly actionsBlacklist?: string | readonly string[];
/**
* @deprecated Use actionsAllowlist instead.
*/
readonly actionsWhitelist?: string | readonly string[]; readonly actionsWhitelist?: string | readonly string[];
readonly actionsDenylist?: string | readonly string[];
readonly actionsAllowlist?: string | readonly string[];
serialize?: boolean | SerializeWithImmutable; serialize?: boolean | SerializeWithImmutable;
readonly serializeState?: readonly serializeState?:
| boolean | boolean
@ -213,6 +221,14 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<unknown>>(
latency = 500, latency = 500,
} = config; } = config;
// Deprecate actionsWhitelist and actionsBlacklist
if (config.actionsWhitelist) {
deprecateParam('actionsWhiteList', 'actionsAllowlist');
}
if (config.actionsBlacklist) {
deprecateParam('actionsBlacklist', 'actionsDenylist');
}
// Deprecate statesFilter and actionsFilter // Deprecate statesFilter and actionsFilter
if (statesFilter) { if (statesFilter) {
deprecateParam('statesFilter', 'stateSanitizer'); deprecateParam('statesFilter', 'stateSanitizer');

View File

@ -29,8 +29,8 @@ export default ({ options, saveOption }: OptionsProps) => {
id="filter-hide" id="filter-hide"
name="filter" name="filter"
type="radio" type="radio"
checked={options.filter === FilterState.BLACKLIST_SPECIFIC} checked={options.filter === FilterState.DENYLIST_SPECIFIC}
onChange={() => saveOption('filter', FilterState.BLACKLIST_SPECIFIC)} onChange={() => saveOption('filter', FilterState.DENYLIST_SPECIFIC)}
/> />
<label className="option__label" htmlFor="filter-hide"> <label className="option__label" htmlFor="filter-hide">
Hide the following: Hide the following:
@ -38,9 +38,9 @@ export default ({ options, saveOption }: OptionsProps) => {
<br /> <br />
<textarea <textarea
className="option__textarea" className="option__textarea"
value={options.blacklist} value={options.denylist}
disabled={options.filter !== FilterState.BLACKLIST_SPECIFIC} disabled={options.filter !== FilterState.DENYLIST_SPECIFIC}
onChange={(e) => saveOption('blacklist', e.target.value)} onChange={(e) => saveOption('denylist', e.target.value)}
/> />
<div className="option__hint">Each action from the new line</div> <div className="option__hint">Each action from the new line</div>
</div> </div>
@ -51,8 +51,8 @@ export default ({ options, saveOption }: OptionsProps) => {
id="filter-show" id="filter-show"
name="filter" name="filter"
type="radio" type="radio"
checked={options.filter === FilterState.WHITELIST_SPECIFIC} checked={options.filter === FilterState.ALLOWLIST_SPECIFIC}
onChange={() => saveOption('filter', FilterState.WHITELIST_SPECIFIC)} onChange={() => saveOption('filter', FilterState.ALLOWLIST_SPECIFIC)}
/> />
<label className="option__label" htmlFor="filter-show"> <label className="option__label" htmlFor="filter-show">
Show the following: Show the following:
@ -60,9 +60,9 @@ export default ({ options, saveOption }: OptionsProps) => {
<br /> <br />
<textarea <textarea
className="option__textarea" className="option__textarea"
value={options.whitelist} value={options.allowlist}
disabled={options.filter !== FilterState.WHITELIST_SPECIFIC} disabled={options.filter !== FilterState.ALLOWLIST_SPECIFIC}
onChange={(e) => saveOption('whitelist', e.target.value)} onChange={(e) => saveOption('allowlist', e.target.value)}
/> />
<div className="option__hint">Each action from the new line</div> <div className="option__hint">Each action from the new line</div>
</div> </div>

View File

@ -6,8 +6,8 @@ export interface Options {
readonly projectPath: string; readonly projectPath: string;
readonly maxAge: number; readonly maxAge: number;
readonly filter: FilterStateValue; readonly filter: FilterStateValue;
readonly whitelist: string; readonly allowlist: string;
readonly blacklist: string; readonly denylist: string;
readonly shouldCatchErrors: boolean; readonly shouldCatchErrors: boolean;
readonly inject: boolean; readonly inject: boolean;
readonly urls: string; readonly urls: string;
@ -19,7 +19,13 @@ interface OldOrNewOptions {
readonly editor: string; readonly editor: string;
readonly projectPath: string; readonly projectPath: string;
readonly maxAge: number; readonly maxAge: number;
readonly filter: FilterStateValue | boolean; readonly filter:
| FilterStateValue
| 'WHITELIST_SPECIFIC'
| 'BLACKLIST_SPECIFIC'
| boolean;
readonly allowlist: string;
readonly denylist: string;
readonly whitelist: string; readonly whitelist: string;
readonly blacklist: string; readonly blacklist: string;
readonly shouldCatchErrors: boolean; readonly shouldCatchErrors: boolean;
@ -54,10 +60,14 @@ const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
// Migrate the old `filter` option from 2.2.1 // Migrate the old `filter` option from 2.2.1
typeof oldOptions.filter === 'boolean' typeof oldOptions.filter === 'boolean'
? oldOptions.filter && oldOptions.whitelist.length > 0 ? oldOptions.filter && oldOptions.whitelist.length > 0
? FilterState.WHITELIST_SPECIFIC ? FilterState.ALLOWLIST_SPECIFIC
: oldOptions.filter : oldOptions.filter
? FilterState.BLACKLIST_SPECIFIC ? FilterState.DENYLIST_SPECIFIC
: FilterState.DO_NOT_FILTER : FilterState.DO_NOT_FILTER
: oldOptions.filter === 'WHITELIST_SPECIFIC'
? FilterState.ALLOWLIST_SPECIFIC
: oldOptions.filter === 'BLACKLIST_SPECIFIC'
? FilterState.DENYLIST_SPECIFIC
: oldOptions.filter, : oldOptions.filter,
}); });
@ -73,6 +83,8 @@ const get = (callback: (options: Options) => void) => {
filter: FilterState.DO_NOT_FILTER, filter: FilterState.DO_NOT_FILTER,
whitelist: '', whitelist: '',
blacklist: '', blacklist: '',
allowlist: '',
denylist: '',
shouldCatchErrors: false, shouldCatchErrors: false,
inject: true, inject: true,
urls: '^https?://localhost|0\\.0\\.0\\.0:\\d+\n^https?://.+\\.github\\.io', urls: '^https?://localhost|0\\.0\\.0\\.0:\\d+\n^https?://.+\\.github\\.io',
@ -98,14 +110,14 @@ export const injectOptions = (newOptions: Options) => {
options = { options = {
...newOptions, ...newOptions,
whitelist: allowlist:
newOptions.filter !== FilterState.DO_NOT_FILTER newOptions.filter !== FilterState.DO_NOT_FILTER
? toReg(newOptions.whitelist)! ? toReg(newOptions.allowlist)!
: newOptions.whitelist, : newOptions.allowlist,
blacklist: denylist:
newOptions.filter !== FilterState.DO_NOT_FILTER newOptions.filter !== FilterState.DO_NOT_FILTER
? toReg(newOptions.blacklist)! ? toReg(newOptions.denylist)!
: newOptions.blacklist, : newOptions.denylist,
}; };
let s = document.createElement('script'); let s = document.createElement('script');
s.type = 'text/javascript'; s.type = 'text/javascript';

View File

@ -181,7 +181,7 @@ describe('Redux enhancer', () => {
window.store = createStore( window.store = createStore(
counter, counter,
window.__REDUX_DEVTOOLS_EXTENSION__({ window.__REDUX_DEVTOOLS_EXTENSION__({
actionsBlacklist: ['SOME_ACTION'], actionsDenylist: ['SOME_ACTION'],
statesFilter: (state) => state, statesFilter: (state) => state,
serializeState: (key, value) => value, serializeState: (key, value) => value,
}) })

View File

@ -32,7 +32,7 @@ import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension'; import { composeWithDevTools } from 'redux-devtools-extension';
const composeEnhancers = composeWithDevTools({ const composeEnhancers = composeWithDevTools({
// Specify here name, actionsBlacklist, actionsCreators and other options // Specify here name, actionsDenylist, actionsCreators and other options
}); });
const store = createStore( const store = createStore(
reducer, reducer,

View File

@ -56,16 +56,28 @@ export interface EnhancerOptions {
/** /**
* *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers). * *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).
* If `actionsWhitelist` specified, `actionsBlacklist` is ignored. * If `actionsWhitelist` specified, `actionsBlacklist` is ignored.
* @deprecated Use actionsDenylist instead.
*/ */
actionsBlacklist?: string | string[]; actionsBlacklist?: string | string[];
/** /**
* *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers). * *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).
* If `actionsWhitelist` specified, `actionsBlacklist` is ignored. * If `actionsWhitelist` specified, `actionsBlacklist` is ignored.
* @deprecated Use actionsAllowlist instead.
*/ */
actionsWhitelist?: string | string[]; actionsWhitelist?: string | string[];
/**
* *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).
* If `actionsAllowlist` specified, `actionsDenylist` is ignored.
*/
actionsDenylist?: string | string[];
/**
* *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).
* If `actionsAllowlist` specified, `actionsDenylist` is ignored.
*/
actionsAllowlist?: string | string[];
/** /**
* called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. * called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor.
* Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters. * Use it as a more advanced version of `actionsDenylist`/`actionsAllowlist` parameters.
*/ */
predicate?: <S, A extends Action>(state: S, action: A) => boolean; predicate?: <S, A extends Action>(state: S, action: A) => boolean;
/** /**

View File

@ -10,8 +10,8 @@ interface State {
export const FilterState = { export const FilterState = {
DO_NOT_FILTER: 'DO_NOT_FILTER', DO_NOT_FILTER: 'DO_NOT_FILTER',
BLACKLIST_SPECIFIC: 'BLACKLIST_SPECIFIC', DENYLIST_SPECIFIC: 'DENYLIST_SPECIFIC',
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC', ALLOWLIST_SPECIFIC: 'ALLOWLIST_SPECIFIC',
}; };
export function arrToRegex(v: string | string[]) { export function arrToRegex(v: string | string[]) {
@ -41,20 +41,20 @@ function filterStates(
} }
interface Config { interface Config {
actionsBlacklist?: string[]; actionsDenylist?: string[];
actionsWhitelist?: string[]; actionsAllowlist?: string[];
} }
interface LocalFilter { interface LocalFilter {
whitelist?: string; allowlist?: string;
blacklist?: string; denylist?: string;
} }
export function getLocalFilter(config: Config): LocalFilter | undefined { export function getLocalFilter(config: Config): LocalFilter | undefined {
if (config.actionsBlacklist || config.actionsWhitelist) { if (config.actionsDenylist || config.actionsAllowlist) {
return { return {
whitelist: config.actionsWhitelist && config.actionsWhitelist.join('|'), allowlist: config.actionsAllowlist && config.actionsAllowlist.join('|'),
blacklist: config.actionsBlacklist && config.actionsBlacklist.join('|'), denylist: config.actionsDenylist && config.actionsDenylist.join('|'),
}; };
} }
return undefined; return undefined;
@ -63,10 +63,10 @@ export function getLocalFilter(config: Config): LocalFilter | undefined {
interface DevToolsOptions { interface DevToolsOptions {
filter?: filter?:
| typeof FilterState.DO_NOT_FILTER | typeof FilterState.DO_NOT_FILTER
| typeof FilterState.BLACKLIST_SPECIFIC | typeof FilterState.DENYLIST_SPECIFIC
| typeof FilterState.WHITELIST_SPECIFIC; | typeof FilterState.ALLOWLIST_SPECIFIC;
whitelist?: string; allowlist?: string;
blacklist?: string; denylist?: string;
} }
function getDevToolsOptions() { function getDevToolsOptions() {
return ( return (
@ -90,12 +90,12 @@ export function isFiltered(
) )
return false; return false;
const { whitelist, blacklist } = localFilter || opts; const { allowlist, denylist } = localFilter || opts;
return ( return (
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
(whitelist && !(type as string).match(whitelist)) || (allowlist && !(type as string).match(allowlist)) ||
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
(blacklist && (type as string).match(blacklist)) (denylist && (type as string).match(denylist))
); );
} }