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

@redux-offline/offline-side-effects

v0.1.1

Published

The JS focused library for offline first async side effects. This library takes all the learnings of building Redux-Offline and extracts them into a pure JS module that can be used in almost any context.

Downloads

5

Readme

offline-side-effects

The JS focused library for offline first async side effects. This library takes all the learnings of building Redux-Offline and extracts them into a pure JS module that can be used in almost any context.

The main aspects that this library focuses on are:

  • Requests pausing and recording when offline
  • Optimistic updates
  • Request retries using exponential backoff
  • Rollback on errors
  • Requests persistence across sessions

Options

The library accepts some config options for high level customisation:

type Options = {
  queue: {
    peek: (outbox: Action[]) => Action;
    enqueue: (outbox: Action[], action: Action) => Action[];
    dequeue: (outbox: Action[], completed: Action) => Action[];
  };
  effect: (requestInfo: RequestInfo) => Promise<unknown>;
  discard: (error: UnknownError, action: Action, retries: number) => Promise<boolean> | boolean;
  retry: (action: Action, retries: number) => number | null;
  alterStream: (
    defaultMiddlewareChain: DefaultMiddlewareChain,
    context: Context
  ) => Middleware<any, any>[];
};

API

The library contains 3 actors: Middleware, Listeners and Triggers. The different parts of the system are glued together by the Context object.

Context

The Context is an object that contains the provided options, the provided listeners, and the state updater. The Context object provides everything you need to alter the internals of the library.

type Context = {
  updater: Updater;
  options: Options;
  listeners: Listeners;
};

Updater

The updater is a tuple that holds the internal state, and an updateState function. It can be used to read/update the internal state.

type State = {
  outbox: Action[];
  status: 'idle' | 'busy' | 'paused';
  retryScheduled: number | null;
  retryCount: number;
  lastTransaction: number;
};
type UpdateState = (type: Updates, payload?: any) => void;
type Updater = [State, UpdateState];

Middleware

The middleware are functions that perform a specific step in the side effect lifecycle. The library comes with the following default middleware chain:

const defaultMiddlewareChain = [processOutbox, send, retry];
  • Process Outbox. In charge of peeking the next action, checking if it's safe to perform a side-effect, and awaiting for the retry time to expire. We say a side-effect is safe to be performed when the state is idle and the next action exists.
  • Send. Receives the peeked action from Process Outbox, performs the side-effect, and if successful commits with the received data.
  • Retry. Receives the error and peeked action from Send, if error is defined checks if needs to discard based on the retry count. In case of discarding, rollback with the error. If it doesn't need to discard, schedules a retry attempt.

A middleware will receive as first argument the async function next, the remaining arguments will be whatever the previous middleware in the chain passed as arguments when calling next. If the next function is not called, the middleware chain will finish.

// Example

const processOutbox = async next => {
  const action = peek(outbox);
  if (action) {
    await next(action); // where next will be the "send" middleware
  }
};

const send = async (next, action) => {
  const response = await fetch(action.meta.url);
  const data = await response.json();
  await next(data, action); // in this case, the "store" middleware will receive as arguments data and action
};

const store = async (next, data, action) => {
  await idb.save(action.meta.key, data);
  next(); // and so on, until the chain is over or a middleware doesn't call next().
};

Listeners

The listeners are callbacks that can be called from within the any Middleware, or Triggers. These are the appropriate mechanism for sharing the internal library state with the userland application code. The library provides some default listeners that are being used by the default middleware and triggers. But the user can extend these to include any other listener that they deem necessary. This is very useful because the user can define a custom middleware that in turn has access to custom listeners.

export type Listeners = {
  [customListenerName: string]: (...args: any[]) => void;
  onRequest: (action: Action) => void;
  onCommit: (data: unknown, action: Action['meta']['commit']) => void;
  onRollback: (error: UnknownError, action: Action['meta']['rollback']) => void;
  onStatusChange: (status: string) => void;
  onSerialize: (state: State) => void;
  onRetry: (delay: number) => void;
};

Triggers

The triggers are the way the userland application code signals the library that something happened that should run the side-effects stream.

The library defines the following triggers:

  • actionWasRequested: (action: Action) => void You call this function to add a new side-effect action.
  • togglePause: (paused: boolean) => void You call this function to pause the side-effect stream. Useful for pausing the requests when there's no internet connection.
  • rehydrateState: (newState: State) => void You call this function when you want to load the outbox from a persisted source.
  • restartProcess: () => void You call this function when you want to force restart the side-effect stream.
  • resetState: () => void You call this function when you want to reset the internal state to the initial values. Useful for when you want to logout from an application.