e-volv
Docs menu

Browser

@e-volv/logs/browser is the e-volv Launch SDK for web pages. It is a client SDK: with a public (evk_pub_…) key it receives evaluated values for the current context — never targeting rules — so a flag check is synchronous, never throws and never blocks on the network. A server (evk_) key is refused with a warning: it would be readable by every visitor. Values are cached in one localStorage entry, refreshed in the background and on focus, and exposures ride the same transport as your logs. The bundle is small and audited in CI: the telemetry entry is 3.3 KB gzipped, flags add 4.7 KB, the OpenFeature provider 0.5 KB.

OpenFeature provider

If your stack already speaks OpenFeature, install the provider alongside the SDK:

shell
npm install @e-volv/logs @e-volv/openfeature-web @openfeature/web-sdk
typescript
import { OpenFeature } from '@openfeature/web-sdk';
import { init } from '@e-volv/logs/browser';
import { browserFlags } from '@e-volv/logs/browser/flags';
import { EvolveWebProvider } from '@e-volv/openfeature-web';

const evolve = init({
  key: 'evk_pub_…',
  flags: browserFlags(),
});
await OpenFeature.setProviderAndWait(new EvolveWebProvider(evolve.flags));
await OpenFeature.setContext({ targetingKey: user.id, plan: user.plan });

const flags = OpenFeature.getClient();
const checkout = await flags.getBooleanDetails('checkout.new', false);
// checkout.value / checkout.variant / checkout.reason, and
// checkout.flagMetadata.evolveReason names the native e-volv reason.

OpenFeature.setContext maps to the Launch identify; flag changes emit ProviderEvents.ConfigurationChanged with flagsChanged set to the keys that moved. The native e-volv reason is always available on flagMetadata.evolveReason.

Native API

typescript
import { init } from '@e-volv/logs/browser';
import { browserFlags } from '@e-volv/logs/browser/flags';

const evolve = init({
  key: 'evk_pub_…', // a public client key — never a server (evk_) key
  service: 'web',
  flags: browserFlags({ context: { targetingKey: user.id } }),
});

await evolve.flags.ready(); // optional: true once the first values arrived

if (evolve.flags.bool('checkout.new', false)) renderNewCheckout();

// On login/logout, re-identify: values for the new context are fetched and
// cached under the same key.
await evolve.flags.identify({ targetingKey: nextUser.id, plan: nextUser.plan });

Evaluation methods: bool, string, number, json and detail — all synchronous, all safe. A flag absent from the served values returns your default with FLAG_NOT_FOUND; a value of the wrong type returns your default with TYPE_MISMATCH.

Options

OptionDefaultMeaning
flags.mode'poll'poll | offline. Client SDKs never stream; offline never contacts the control plane (bootstrap values or defaults only).
flags.pollIntervalSeconds60 (min 15)Poll period. Hidden tabs do not poll at all.
flags.urlderivedBase URL ending in /api/public/v1/flags; defaults to the origin of the Observer url option, else the e-volv cloud.
flags.cachelocalStorageOne localStorage key per client key (evolve-flags-<hash>); false disables caching.
flags.bootstrapnoneA values map served before the first fetch completes.
flags.context{}The initial context, set with the page load.
flags.exposures.dedupeWindowSeconds300Suppress repeats of the same (flag, variant, subject) within the window; suppressed counts ride the next exposure.
flags.privateAttributes[]Attribute names removed from the context before anything is sent.

The origin allowlist

A browser request carries the page’s own Origin, and the control plane only answers from origins on the environment key’s allowlist — edited under the environment’s Access action. A refused origin produces exactly one warning and the SDK keeps serving defaults while retrying every five minutes:

text
e-volv flags: this client key is not allowed from here — add this origin or app id under the environment's Access action

What the page sends — and what it keeps

  • The context travels as ?context= JSON, capped at 4,096 characters: attributes are dropped largest-first — never key, kind or targetingKey — and the trim is logged once.
  • privateAttributes are removed before anything is sent. A rule that targets a private attribute can never match a client key — the attribute simply is not there. Use private attributes for anything you would not paste into a URL.
  • The cache is one localStorage entry per key (evolve-flags-…) holding the served values and a fingerprint of the fitted context — no rules, no personal data beyond what the rules needed. A cache from another context is served only until the first fetch for the new context completes, then overwritten. Safari private mode or a full store simply disables caching; evaluation is unaffected.
  • The SDK names itself with the x-evolve-sdk header (browsers cannot set User-Agent) and its last batch leaves with keepalive on pagehide.

Install check and lifecycle

typescript
// Install check: is this key bound, and to which environment?
const ping = await evolve.flags.verify();
// { environment: 'production', keyKind: 'client', flags: 42, etag: '3f2a…' }
// null when the control plane did not answer.
typescript
// Single-page apps: remove listeners and flush on sign-out/navigation.
await evolve.close();

The SDKs index lists every platform — including the edge runtimes SDK for Cloudflare Workers and Vercel Edge — and the HTTP API page documents the wire format the SDK speaks.