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

@tkesgar/reine

v0.2.1

Published

Stale-while-revalidate caching utility library

Downloads

1

Readme

@tkesgar/reine

Build Status codecov npm bundle size npm TypeScript tested with jest

Ketika ngirim foto cookies buat Moona-senpai terus dikira foto telur dadar... 😭

reine provides createSWR, a simple in-memory caching helper based on the stale-while-revalidatestrategy:

// Assume that renderToString takes 1000 ms
const renderPage = createSWR(() => renderer.renderToString(app, ctx));

app.use(async () => {
  const html = await renderPage();
  return html;
});

It is ~500 bytes gzipped, has no dependencies, and can be used in Node.js or browser environment.

Installation

$ npm install @tkesgar/reine

Usage

The module exports a function createSWR.

import createSWR from "@tkesgar/reine";

createSWR(asyncFn, opts = {})

Wraps asyncFn using stale-while-revalidate strategy into a new asynchronous function.

Available options:

  • maxAge (default: 1000): the minimum age in miliseconds for the value to be considered stale.
  • staleAge (default: 2000): the minimum age in miliseconds for the value to be considered old.
  • revalidateErrorHandler: an error handler that will be called with the error if asyncFn throws an error when trying to revalidate (i.e. value is style).
  • initialValue: if a value is provided, it will be used as initial value. The SWR instance will always start at fresh state.
const wrappedFetchData = createSWR(fetchData, {
  maxAge: 30000,
  staleAge: 60000,
  revalidateErrorHandler(err) {
    log.error({ err }, "Failed to fetch data from network; using stale data");
  },
  initialValue: null,
});

wrappedFn.age: number

Returns the age of current value.

If the value is not available yet, the value will be Infinity.

wrappedFn.status: "fresh" | "stale" | "old"

Returns the current state of value based on its age.

wrappedFn.reset([value])

If no value is provided, sets the status to be "old", causing the next call to execute the function.

If a value is provided, sets the current value to the provided value and refreshes the cache.

wrappedFn(): Promise

Retrieves the value for the function. Depending on the status:

  • If status is fresh, the currently available data will be returned. The function will not be executed.
  • If status is stale, the currently available data will be returned. However, the function will be executed and the value will be "refreshed".
    • If the function throws an error and revalidateErrorHandler is available, it will be called with the error value as argument.
  • If status is old, the function will be executed and the value is returned.
    • If the function throws an error, the function will throws the error.

Recipes

Simple usage

const fetchWinner = createSWR(() => fetchData("/api/user/winner/info"));

const winner = await fetchWinner();
console.log(await winner.username);

Log revalidation errors

If there is no error handler, the error will be silently ignored; the SWR instance will return the stale value instead.

It is recommended to provide an error handler to some logging mechanism.

const fetchWinner = createSWR(() => fetchData("/api/user/winner/info"), {
  revalidationErrorHandler(err) {
    log.warn({ err }, "Failed to fetch winner info; using stale render result");
  },
});

const winner = await fetchWinner();
console.log(await winner.username);

Providing initial value

If an initial value is provided, the state will start as "fresh". This avoids the first call to be delayed.

const fetchWinner = createSWR(() => fetchData("/api/user/winner/info"), {
  initialValue: { id: 323, username: "pavolia_reine" },
});

const winner = await fetchWinner();
console.log(await winner.username);

Preload SWR instance

To avoid delays or errors for the first call, simply call the function first.

const fetchWinner = createSWR(() => fetchData("/api/user/winner/info"));
await fetchWinner();

const winner = await fetchWinner();
console.log(await winner.username);

It is possible to make preload non-blocking; however, be aware that the future call will throw error if the function throws error again.

const fetchWinner = createSWR(() => fetchData("/api/user/winner/info"));
fetchWinner().catch((err) => {
  log.warn({ err }, "Failed to fetch winner info");
});

const winner = await fetchWinner();
console.log(await winner.username);

Always revalidate

Setting maxAge to 0 and staleAge to Infinity will cause the function to always revalidate the value on every calls, but returns the stale value instantly. This behaviour might be desirable if stale values are acceptable and revalidation is "cheap".

const fetchWinner = createSWR(() => fetchData("/api/user/winner/info"), {
  maxAge: 0,
  staleAge: Infinity,
});
await fetchWinner();

const winner = await fetchWinner();
console.log(await winner.username);

Cache invalidation

The cache can be invalidated for certain use cases, such as receiving an update event for the cached resource.

const fetchWinner = createSWR(() => fetchData("/api/user/winner/info"), {
  revalidateErrorHandler(err) {
    log.warn({ err }, "Failed to fetch winner data");
  },
});

externalService.on("match-finished", () => {
  fetchWinner.reset();
});

To avoid delays, simply call the function after invalidating the cache.

externalService.on("match-finished", () => {
  fetchWinner.reset();
  fetchWinner().catch((err) => {
    log.warn({ err }, "Failed to fetch winner data");
  });
});

Alternatively, it is also possible to set the current value.

externalService.on("match-finished", (result) => {
  fetchWinner.reset(result.winner);
});

Questions

Does createSWR supports custom cache key/function arguments?

No, createSWR call returns a single wrapped function that will be executed without arguments.

Does createSWR supports custom cache storage (e.g. Redis)?

No, only in-memory.

Contribute

Feel free to send issues or create pull requests.

License

Licensed under MIT License.