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

@hodlen/cacheables

v1.2.3

Published

A simple in-memory cache written in Typescript with automatic cache invalidation and an elegant syntax.

Downloads

7

Readme

Cacheables

License: MIT Language Build

A simple in-memory cache with support of different cache policies and elegant syntax written in Typescript.

  • Elegant syntax: Wrap existing API calls to save some of those precious API calls.
  • Fully typed results. No type casting required.
  • Supports different cache policies.
  • Written in Typescript.
  • Integrated Logs: Check on the timing of your API calls.
  • Helper function to build cache keys.
  • Works in the browser and Node.js.
  • No dependencies.
  • Extensively tested.
  • Small: 1.43 kB minified and gzipped.
// without caching
fetch('https://some-url.com/api')

// with caching
cache.cacheable(() => fetch('https://some-url.com/api'), 'key')

Installation

npm install cacheables

Quickstart

https://codesandbox.io/s/quickstart-cacheables-5zh6h?file=/src/index.ts

Usage

// Import Cacheables
import { Cacheables } from "cacheables"

const apiUrl = "https://goweather.herokuapp.com/weather/Karlsruhe"

// Create a new cache instance
const cache = new Cacheables({
  logTiming: true,
  log: true
})

const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

// Wrap the existing API call `fetch(apiUrl)` and assign a cache
// key `weather` to it. This example uses the cache policy 'max-age'
// which invalidates the cache after a certain time.
// The method returns a fully typed Promise just like `fetch(apiUrl)`
// would but with the benefit of caching the result.
const getWeatherData = () =>
  cache.cacheable(() => fetch(apiUrl), 'weather', {
    cachePolicy: 'max-age',
    maxAge: 5000,
  })

const start = async () => {
  // Fetch some fresh weather data and store it in our cache.
  const weatherData = await getWeatherData()

  /** 3 seconds later **/
  await wait(3000)

  // The cached weather data is returned as the
  // maxAge of 5 seconds did not yet expire.
  const cachedWeatherData = await getWeatherData()

  /** Another 3 seconds later **/
  await wait(3000)

  // Now that the maxAge is expired, the resource
  // will be fetched and stored in our cache.
  const freshWeatherData = await getWeatherData()
}

start()

cacheable serves both as the getter and setter. This method will return a cached resource if available or use the provided argument resource to fill the cache and return a value.

Be aware that there is no exclusive cache getter (like cache.get('key)). This is by design as the Promise provided by the first argument to cacheable is used to infer the return type of the cached resource.

API

new Cacheables(options?): Cacheables

  • Creates a new Cacheables instance.

Arguments

- options?: CacheOptions
interface CacheOptions {
  enabled?: boolean    // Enable/disable the cache, can be set anytime, default: true.
  log?: boolean        // Log hits to the cache, default: false. 
  logTiming?: boolean  // Log the timing, default: false.
}

Example:

import { Cacheables } from 'cacheables'

const cache = new Cacheables({
  logTiming: true
})

cache.cacheable(resource, key, options?): Promise<T>

  • If a resource exists in the cache (determined by the presence of a value with key key) cacheable decides on returning a cache based on the provided cache policy.
  • If there's no resource in the cache, the provided resource will be called and used to store a cache value with key key and the value is returned.

Arguments

- resource: () => Promise<T>

A function that returns a Promise<T>.

- key: string

A key to store the cache at.
See Cacheables.key() for a safe and easy way to generate unique keys.

- options?: CacheableOptions (optional)

An object defining the cache policy and possibly other options in the future. The default cache policy is cache-only. See Cache Policies.

type CacheableOptions = {
  cachePolicy: 'cache-only' | 'network-only-non-concurrent' | 'network-only' | 'max-age' | 'stale-while-revalidate' // See cache policies for details
  maxAge?: number // Required if cache policy is `max-age` and optional if cache policy is `stale-while-revalidate`
}

Example

const cachedApiResponse = await cache.cacheable(
  () => fetch('https://github.com/'),
  'key',
  {
    cachePolicy: 'max-age',
    maxAge: 10000
  }
)

cache.delete(key: string): void

Arguments

- key: string

Delete a cache for a certain key.

Example

cache.delete('key')

cache.clear(): void

Delete all cached resources.

cache.keys(): string[]

Returns all the cache keys

cache.isCached(key: string): boolean

Arguments

- key: string

Returns whether a cacheable is present for a certain key.

Example

const aIsCached = cache.isCached('a')

Cacheables.key(...args: (string | number)[]): string

A static helper function to easily build safe and consistent cache keys.

Example

const id = '5d3c5be6-2da4-11ec-8d3d-0242ac130003'
console.log(Cacheables.key('user', id))
// 'user:5d3c5be6-2da4-11ec-8d3d-0242ac130003'

Cache Policies

Cacheables comes with multiple cache policies.
Each policy has different behaviour when it comes to preheating the cache (i.e. the first time it is requested) and balancing network requests.

| Cache Policy | Behaviour | |-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | cache-only (default) | All requests should return a value from the cache. | | network-only | All requests should be handled by the network.Simultaneous requests trigger simultaneous network requests. | | network-only-non-concurrent | All requests should be handled by the network but no concurrent network requests are allowed.All requests made in the timeframe of a network request are resolved once that is finished. | | max-age | All requests should be checked against max-age.If max-age is expired, a network request is triggered.All requests made in the timeframe of a network request are resolved once that is finished. | | stale-while-revalidate | All requests immediately return a cached value.If no network request is running and maxAge is provided and reached or maxAge is not provided, a network request is triggered, 'silently' updating the cache in the background.After the network request finished, subsequent requests will receive the updated cached value. |

Cache Only (default)

The default and simplest cache policy. If there is a cache, return it.
If there is no cache yet, all calls will be resolved by the first network request (i.e. non-concurrent).

Example
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'cache-only' })

Network Only

The opposite of cache-only.
Simultaneous requests trigger simultaneous network requests.

Example
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'network-only' })

Network Only – Non Concurrent

A version of network-only but only one network request is running at any point in time.
All requests should be handled by the network but no concurrent network requests are allowed. All requests made in the timeframe of a network request are resolved once that is finished.

Example
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'network-only-non-concurrent' })

Max Age

The cache policy max-age defines after what time a cached value is treated as invalid.
All requests should be checked against max-age. If max-age is expired, a network request is triggered. All requests made in the timeframe of a network request are resolved once that is finished.

Example
// Trigger a network request if the cached value is older than 1 second.
cache.cacheable(() => fetch(url), 'a', { 
  cachePolicy: 'max-age',
  maxAge: 1000
})

Stale While Revalidate

The cache policy stale-while-revalidate will return a cached value immediately and – if there is no network request already running and maxAge is either provided and reached or not provided – trigger a network request to 'silently' update the cache in the background.

Example without maxAge
// If there is a cache, return it but 'silently' update the cache.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'stale-while-revalidate'})
Example with maxAge
// If there is a cache, return it and 'silently' update the cache if it's older than 1 second.
cache.cacheable(() => fetch(url), 'a', {
  cachePolicy: 'stale-while-revalidate',
  maxAge: 1000
})

Cache Policy Composition

A single cacheable can be requested with different cache policies at any time.

Example

// If there is a cache, return it.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'cache-only' })

// If there is a cache, return it but 'silently' update the cache.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'stale-while-revalidate' })

In Progress

PRs welcome

  • [ ] ~~Cache invalidation callback~~
  • [ ] Adapters to store cache not only in memory
  • [X] Cache policies
  • [X] Tests

License

The MIT License (MIT). Please see License File for more information.