SDK
Web / JavaScript
6.5 KB gzipped, no framework, one dependency. Runs in the browser, in Node, and through a server render without changing anything.
Install
npm install @ripstop/webQuickstart
import { Ripstop } from '@ripstop/web';
const ripstop = await Ripstop.init({
apiKey: 'rs_pub_your_key',
appVersion: __APP_VERSION__, // whatever your build injects
});
const decision = await ripstop.check();
switch (decision.type) {
case 'maintenance': /* down on purpose */ break;
case 'force': location.reload(); break;
case 'soft': /* a nudge: ripstop.snooze() */ break;
case 'none': /* carry on */ break;
}What “force” means here
On mobile it means the binary is stale and the user has to visit a store. On the web there is no store: a reload gets the latest code. So force almost always means a tab that has been open since before you shipped a breaking change, and the right response is usually location.reload().
Remote config
const checkout = ripstop.value('checkout_enabled', true);
const limit = ripstop.value('upload_limit', 10);Always pass a fallback. On a first load with no network there is no payload yet, and the map is empty. That is the fail-open path working as intended.
Server rendering
Ripstop.init runs anywhere fetch exists. With no localStorage it falls back to in-memory storage automatically, so a server render gets a decision and simply does not persist a cache.
Options
| Option | Default | What it does |
|---|---|---|
apiKey | no default | Your app’s public SDK key. Safe to ship |
appVersion | no default | The build you are running; rules evaluate against it |
locale | en | Which wall copy to resolve; falls back to en per key |
minFetchInterval | 6 hours | How long a payload is fresh enough to skip the network |
timeoutMs | 5000 | Fetch budget. After that, cache |
storage | localStorage | Swap for MemoryStorage, sessionStorage, your own |
signingKeys | pinned | Override for self-hosted deployments and tests |
fetchImpl | global | Inject your own for tests or a proxy |
The cache is re-verified on read
This matters more on the web than anywhere else. localStorage is two keystrokes away in devtools, so a cache that were trusted would make every wall a polite request. Every cached payload is stored with its signature and checked again before it is allowed to decide anything.
Testing your integration
const gate = await Ripstop.init({
apiKey: 'rs_pub_test',
appVersion: '3.0.0',
signingKeys: { k1: myTestPublicKeyB64 },
storage: new MemoryStorage(),
fetchImpl: myStubFetch,
});
expect((await gate.check()).type).toBe('force');Source
- github.com/ripstop-dev/ripstop-web (MIT).
- 71 tests, 62 of them the protocol’s own golden vectors.