@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.
Fetches, caches and refreshes Rule Cascade (README.md) 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).
getthrows 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).
getthrows and drops the cached manifest, so a later outage does not bring it back. onStaleis called on everygetthat falls back.stale(rulesetId)describes the lastgetfor that ruleset and isundefinedonce a latergetsucceeds.
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 timersConcurrent 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
docs/caching.md.
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. examples/frontend-react is a complete form built this way.
@rules-cascade/common
The contract the other Rule Cascade packages share. It has no dependencies and evaluates nothing.
rules-cascade-core (Java)
The Rule Cascade runtime for the JVM. Java 17+, no dependencies: it works on maps and lists, so it sits behind whatever JSON or YAML library your service already uses.