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
npm install @ripstop/web

Quickstart

TypeScript
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

TypeScript
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

OptionDefaultWhat it does
apiKeyno defaultYour app’s public SDK key. Safe to ship
appVersionno defaultThe build you are running; rules evaluate against it
localeenWhich wall copy to resolve; falls back to en per key
minFetchInterval6 hoursHow long a payload is fresh enough to skip the network
timeoutMs5000Fetch budget. After that, cache
storagelocalStorageSwap for MemoryStorage, sessionStorage, your own
signingKeyspinnedOverride for self-hosted deployments and tests
fetchImplglobalInject 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

TypeScript
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

⌘K