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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rules-cascade/client

v1.0.0-alpha.11

Published

Rule Cascade in the browser and in Node.js clients: fetch, cache and refresh the client manifest, and evaluate it in a React form with useRuleEvaluation.

Downloads

276

Readme

@rules-cascade/client

Fetches, caches and refreshes Rule Cascade manifests from a rule server, and evaluates them in React. It runs in browsers and Node.js; evaluation itself is @rules-cascade/core.

| Entry point | Use it for | Extra dependency | |---|---|---| | @rules-cascade/client | createManifestClient, localStorageAdapter | none | | @rules-cascade/client/react | The useRuleEvaluation hook | react 18 or later (optional peer) |

Fetch a manifest

import { createManifestClient } from '@rules-cascade/client';
import { evaluate } from '@rules-cascade/core';

const manifests = createManifestClient({ baseUrl: '/api/rules' });
const manifest = await manifests.get('acme.payments.transfer');   // cached; revalidated with the ETag
evaluate(manifest, request).decision;

Through an outage of the manifest endpoint

createManifestClient keeps the last manifest it fetched for each ruleset. When the server cannot be reached and a manifest is cached, get returns the cached one instead of throwing, so the form keeps giving feedback; the server still decides when the operation is submitted.

const manifests = createManifestClient({
  baseUrl: '/api/rules',
  onStale: ({ rulesetId, error, fetchedAt }) => console.warn(`${rulesetId}: using the rules fetched at ${new Date(fetchedAt).toISOString()}: ${error.message}`),
});
const manifest = await manifests.get('acme.payments.transfer');  // fresh, revalidated (304), or the cached one during an outage
manifests.stale('acme.payments.transfer');                       // undefined, or { rulesetId, manifest, error, fetchedAt } when the last get fell back
  • "Cannot be reached" means: the request failed, or the answer was 408, 429, a 5xx status, or not a client manifest (for example the error page of a proxy).
  • get throws when nothing is cached for that ruleset. The cache lives in memory, for the lifetime of the client object: it does not survive a page reload.
  • Any other 4xx answer is a refusal by the server (the ruleset is gone, or the caller may not read it). get throws and drops the cached manifest, so a later outage does not bring it back.
  • onStale is called on every get that falls back. stale(rulesetId) describes the last get for that ruleset and is undefined once a later get succeeds.

Caching manifests

createManifestClient revalidates on every get by default. Two other modes trade freshness for fewer requests, and every mode can keep its cache in localStorage and refresh in the background:

import { createManifestClient, localStorageAdapter } from '@rules-cascade/client';

const manifests = createManifestClient({
  baseUrl: '/api/rules',
  mode: 'ttl',                     // 'revalidate' (default) | 'ttl' | 'permanent'
  ttlMs: 5 * 60_000,               // no request while the cached copy is younger; refreshed in the background
  refreshCron: '*/15 * * * *',     // optional, any mode
  storage: localStorageAdapter(),  // survives a page reload; does nothing where storage is unavailable
  onUpdate: (rulesetId, manifest) => console.info(`${rulesetId} is now ${manifest.checksum}`),
});
await manifests.get('acme.payments.transfer', { checksum });   // permanent: fetch by checksum URL
manifests.close();                                               // stop the timers

Concurrent get calls for one ruleset share a request. staleIfError: false turns off the fallback to the cached copy. The modes, the server's matching cache headers and CDN guidance are in Caching.

In React

import { useRuleEvaluation } from '@rules-cascade/client/react';

const { allowed, states, computed, findingsFor } = useRuleEvaluation(manifest, {
  entity: 'Transfer', operation: 'create', data: form, actor, resolutions,
});

A third argument takes the custom operators the manifest needs. Pass the same object on every render, for example a module-level constant. The React example of the repository is a complete form built this way.