
Security News
The Nightmare Before Deployment
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.
nebenan-redux-tools
Advanced tools
A set of redux helpers.
NOT COMPLETE
import { reducer as experiments } from 'nebenan-redux-tools/lib/experiments/state';
const reducers = {
// It is assumed that the experiment reducer's state
// is available under `state.experiments`
experiments
};
// ...
const createFromState = (initialState) => (
createStore(
combineReducers(reducers),
initialState,
composeEnhancers(applyMiddleware(...middleware)),
)
);
export default createFromState
NOT COMPLETE
import { reducer as token } from 'nebenan-redux-tools/lib/token';
const reducers = {
// It is assumed that the experiment reducer's state
// is available under `state.token`
token
};
// ...
const createFromState = (initialState) => (
createStore(
combineReducers(reducers),
initialState,
composeEnhancers(applyMiddleware(...middleware)),
)
);
export default createFromState
The promise middleware gets triggered by dispatching actions with promise key in payload. The middleware will dispatch resolved or rejected actions when the promise is fulfilled.
dispatch({
type: types.GET_USERS,
promise: {
getPromise: () => getUsersFromStorageAsync(),
// Ensures the returned promise by the dispatched action to resolve even if the provided promise rejected
// graceful: true
}
})
// reducer
import { resolved, rejected } from 'nebenan-redux-tools/lib/promise';
export default (state = getDefaultState(), action) => {
switch(action.type) {
case resolved(types.GET_USERS): {
const usersFromStorage = action.payload;
return { ...state, users: usersFromStorage };
}
case rejected(types.GET_USERS): {
const usersFromStorage = [];
return { ...state, users: usersFromStorage };
}
default: {
return state;
}
}
}
Prevents unnecessary promise executions by checking state beforehand. Dispatches resolved action on prevented execution.
dispatch({
type: types.GET_USERS,
promise: {
getPromise: () => getUsersFromStorageAsync(),
shouldExecute: (state) => !state.isFetched,
}
})
The network middleware gets triggered by dispatching actions with request in payload. The middleware will fire the request and dispatches resolved or rejected actions when the request is finished.
Example:
dispatch({
type: types.GET_USERS,
request: {
url: "/users"
}
})
import { resolved, rejected } from 'nebenan-redux-tools/lib/network/types';
export default (state = getDefaultState(), action) => {
switch(action.type) {
case resolved(types.GET_USERS): {
const usersFromResponse = action.payload;
return { ...state, users: usersFromResponse };
}
case rejected(types.GET_USERS): {
const usersFromResponse = [];
return { ...state, users: usersFromResponse };
}
default: {
return state;
}
}
}
Requests to trustworthy endpoints will enable the X-Translations-Lang Header and API Token features.
Set trusted domain via global config:
import { configureNetwork } from 'nebenan-redux-tools/lib/network';
configureNetwork({
trustedDomain: "nebenan.de",
...otherSettings
})
Requests to trustworthy endpoints will have the X-AUTH-TOKEN header set, containing the api token configured in request options or through token middleware.
Default: state.token (see token middleware)
Overwrite:
dispatch({
type: "some-type",
request: {
url: "/some/endpoint",
token: "special-api-token"
}
})
Requests to trustworthy endpoints will have the X-Translations-Lang header set, containing the locale configured in request options or through global config.
Default via global config:
import { configureNetwork } from 'nebenan-redux-tools/lib/network';
configureNetwork({
locale: 'de-DE',
...otherSettings
})
Overwrite:
dispatch({
type: "some-type",
request: {
url: "/some/endpoint",
locale: "en-US"
}
})
Prevents unnecessary requests by checking state beforehand.
dispatch({
type: "some-type",
request: {
url: "/some/endpoint",
shouldRequest(state) {
return !state.isFetched;
}
}
})
Allows to cancel running requests. Just pass in a AbortSignal.
const controller = new AbortController();
dispatch({
type: "some-type",
request: {
url: "/some/endpoint",
signal: controller.signal,
}
})
// later in app
controller.abort();
request.type determines used HTTP method
querySends out GET request. Collects parameters from request.query and applies pagination query options if there are any.
dispatch({
type: "some-type",
request: {
url: "/users",
type: 'query',
query: { q: 'Peter' }
}
})
delete / post / putdispatch({
type: "some-type",
request: {
url: "/some/endpoint/123",
type: 'put',
payload: { user: { email, password }}
}
})
There is some boilerplate needed to fully support paginated lists loaded over the network. These helpers help with managing state and sending pagination params over the network.
The endpoint you are accessing needs to fulfill following requirements:
per_pagelower / highertotal_count (state.total needs to be updated manually if field does not exist)assignPaginationDefaults / paginationGenerator helpersimport { paginationGenerator, assignPaginationDefaults } from 'nebenan-redux-tools/lib/network/pagination';
const getDefaultState = () => assignPaginationDefaults({ entities: {} });
const getPaginationUpdates = paginationGenerator(types.BOOKMARKS_FETCH);
export const reducer = (state = getDefaultState(), action) => {
let newState = state;
const update = getPaginationUpdates(newState, action);
if (update) newState = updeep(update, newState);
switch (action.type) {
case resolved(types.BOOKMARKS_FETCH): {
const { result, entities } = parse(action.payload);
const collection = newState.collection.concat(result.bookmarks);
return updeep({ collection, entities }, newState);
}
default: {
return newState;
}
}
}
The state now holds the following information
{
// (managed by paginationGenerator)
currentPage: 0,
// datetime of last fetch (managed by paginationGenerator)
lastFetched: 0,
// request in progress (managed by paginationGenerator)
isFetching: false,
// request failed (managed by paginationGenerator)
isFailed: false,
// total amount of items over all pages (managed by paginationGenerator)
total: null,
// item ids for current page
collection: [],
}
Pagination query params will be added for query requests with either request.pagination.first or request.pagination.last set.
Only works for endpoints supporting lower/higher params.
dispatch({
type: types.GET_USERS,
request: {
url: "/users",
type: 'query',
pagination: {
per_page: 10, // optional
first: firstUserId, // optional
last: lastUserId // optional
}
}
})
Responses can be mocked by using the promise middleware. You can keep all of your request options in the action and don't need to modify reducers. Quite useful during development if you are waiting for backend to support new endpoints.
dispatch({
type: types.GET_USERS,
promise: Promise.resolve([
{
id: 1,
name: "Peter"
},
{
id: 2,
name: "Christina"
}
]),
request: {
url: "/users",
type: 'query'
}
})
// reducer
import { resolved } from 'nebenan-redux-tools/lib/network/types';
export default (state = getDefaultState(), action) => {
switch(action.type) {
case resolved(types.GET_USERS): {
const usersFromResponse = action.payload;
return { ...state, users: usersFromResponse };
}
default: {
return state;
}
}
}
Requests can be created programmatically too.
import { createRequest } from 'nebenan-redux-tools/lib/network';
const sendFile = (file, token) => (
createRequest({
token,
payload: { file },
url: '/file-upload',
type: 'post',
multipart: true,
})
)
const handleSuccess = (payload) => {
// same as action.payload
console.log(payload);
}
sendFile(newFile, token).then(handleSuccess);
The network layer uses axios under the hood. To access axios specific features or extend functionality you can hook into request / response generation on a global or local scale.
Hooks:
requestHook(requestConfig, requestOptions)
requestConfig the axios request config to be mutatedrequestOptions the request object provided inside the actionresponseHook(body, requestConfig, requestOptions)
body response data to be mutatedrequestConfig the axios request configrequestOptions the request object provided inside the actionimport { configureNetwork } from 'nebenan-redux-tools/lib/network';
configureNetwork({
requestHook: (requestConfig, requestOptions) => {
// ...
},
responseHook: (body, requestConfig, requestOptions) => {
// ...
},
...networkSettings
})
import { createRequest } from 'nebenan-redux-tools/lib/network';
const sendFile = (file, token, onProgress) => (
createRequest({
token,
payload: { file },
url: '/file-upload',
type: 'post',
multipart: true,
// same as requestHook
customize: (requestConfig) => {
requestConfig.onUploadProgress = onProgress;
}
})
)
FAQs
A set of redux helpers
The npm package nebenan-redux-tools receives a total of 23 weekly downloads. As such, nebenan-redux-tools popularity was classified as not popular.
We found that nebenan-redux-tools demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 7 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.

Research
/Security News
Impostor NuGet package Tracer.Fody.NLog typosquats Tracer.Fody and its author, using homoglyph tricks, and exfiltrates Stratis wallet JSON/passwords to a Russian IP address.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.