npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@wwt-as/validated-actions

v1.1.1

Published

A middleware and higher order function to enable validation of actions before they ever reach a reducer

Downloads

8

Readme

linting tests build

@wwt-as/validated-actions

A middleware and higher order function to enable validation of actions before they ever reach a reducer.

The purpose of this package is to allow TypeScript user the ability to verify that data from external sources (web requests, flat files, etc.) have the expected shape. However the package itself does not care what the validation function actually does or if you are using the validatable actions in places other than the boundries.

Install

$ npm install @wwt-as/validated-actions --save

Install createValidateActionsMiddleware

import { createValidateActionsMiddleware } from '@wwt-as/validated-actions';

const middlewareEnhancer = applyMiddleware(createValidateActionsMiddleware());

const store = createStore(reducer, undefined, middlewareEnhancer);
return { actionCreator: validatableActionCreator, store };

makeValidateable

import { makeValidatable } from '@wwt-as/validated-actions';

const validateableActionCreator = makeValidateable(actionCreator)(
  validationFunction,
  validationFailedActionCreator,
);

Notes on makeValidateable

In order to leverage the typing and avoid pain when trying to reduce the actions, a strongly typed action creator is required. The below examples use action creators that have been tested with @wwt-as/validated-actions, The action creator for the validation failed action should only take error payload as a parameter.

Example with typesafe-actions
const actionCreator = makeValidatable(
  createAction(TestActions.testAction)<TestType>(),
)(
  validationFunction,
  createAction(TestActions.testActionValidationFailure)<TestErrorType>(),
);

type TestActionTypes =
  | ActionType<typeof actionCreator>
  | ActionType<typeof actionCreator.onValidationFailureAction>;

const reducer = (
  state = initialState,
  action: TestActionTypes,
): TestStateType => {
  switch (action.type) {
    case getType(actionCreator):
      return { ...state, success: action.payload };
    case getType(actionCreator.onValidationFailureAction):
      return { ...state, failure: action.payload };
    default:
      return state;
  }
};
Example with @redux/toolkit
const actionCreator = makeValidatable(
  ReduxToolkit.createAction<TestType>(TestActions.testAction),
)(
  validationFunction,
  ReduxToolkit.createAction<TestErrorType>(
    TestActions.testActionValidationFailure,
  ),
);

// must use the builder syntax for type inference
const reducer = ReduxToolkit.createReducer({} as TestStateType, (builder) => {
  builder.addCase(actionCreator, (state, action) => ({
    ...state,
    success: action.payload,
  }));
  builder.addCase(actionCreator.onValidationFailureAction, (state, action) => ({
    ...state,
    failure: action.payload,
  }));
});
Example with a hand rolled action creator
// It's important to use string constants or literals for the action types,
// as well as strongly typing the parameters of both of the action creators.
const validatableActionCreator = makeValidatable((payload: TestType) => ({
  type: TestActions.testAction,
  payload,
}))(validationFunction, (payload: TestErrorType) => ({
  type: TestActions.testActionValidationFailure,
  payload,
}));

type TestActionTypesHomeRolled =
  | ReturnType<typeof validatableActionCreator>
  | ReturnType<typeof validatableActionCreator.onValidationFailureAction>;

const reducer = (state = initialState, action: TestActionTypesHomeRolled) => {
  switch (action.type) {
    case TestActions.testAction:
      return { ...state, success: action.payload };
    case TestActions.testActionValidationFailure:
      return { ...state, failure: action.payload };
    default:
      return state;
  }
};

Validation Function

The validation function used can be asynchronous or synchronous. The return value can be void, undefined, or the same type as the original payload. Returning void or undefined, signifies that the original payload was valid and to pass the original action along. Returning a payload type signifies that the original payload was invalid, but you were able to fix the issue, and that the original action can be passed on with the new paylaod instead. To signal that a paylaod is invalid, you must throw an error. the error thrown should be the type specified when creating the validated action, but due to thrown types not having types in typescript you may throw what ever you like.

Example
// In this example the payload of the failure action should be a string
const exampleValidationFunction = async (payload: number) => {
  if (typeof payload == 'string') {
    // We can try to fix a payload.
    const newValue = parseFloat(payload);
    if (newValue === NaN) {
      // If we can't fix the payload then we can throw an error.
      throw 'payload was not a number!';
    }
    // If we can fix the payload then we can return an updated payload.
    return newValue;
  }
  if (typeof payload !== number) {
    // Sometimes we just know when something isn't going to work!
    throw 'payload was not a number!';
  }
  // If the function completes without returning or throwing then the original payload is used
  // the same effect can be acheived by returning null or undefined
};