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

@ops-ai/toggly-sveltekit

v0.4.2

Published

Request-scoped feature flags and signed SSR hydration for SvelteKit

Readme

Toggly SvelteKit SDK

Supports Svelte ^5.0.0 and SvelteKit ^2.0.0 on Node 22.12 or later. The current packed host uses Svelte 5.57.0, SvelteKit 2.70.3, and adapter-node 5.5.7.

Request-scoped server feature flags and signed SSR hydration for SvelteKit. Use with Toggly.io or explicit offline defaults. A feature flag selects an application branch without deploying new code; targeting rules can vary that branch by user, request or entity.

Install and requirements

npm install @ops-ai/toggly-sveltekit

Svelte 5, SvelteKit 2, Node 22.12+ and adapter-node. Uses Node core0.9.1+ and signed-defs1.2.6+. Browser signature verification needs WebCrypto (HTTPS or localhost). Other server adapters need separate validation.

Server hook

// src/hooks.server.ts — keep backend keys in server-only modules.
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import { createTogglyClient, createTogglyHandle } from '@ops-ai/toggly-sveltekit/server';
const client = createTogglyClient({
  appKey: env.TOGGLY_APP_KEY,
  environment: env.TOGGLY_ENVIRONMENT ?? 'Production',
  verifySignatures: true,
  featureDefaults: { 'new-dashboard': false },
});
await client.init();
process.once('SIGTERM', () => {
  void client.close();
});
export const handle = createTogglyHandle({
  client,
  context: () => ({ identity: '', groups: [], claims: {} }),
  frontend: {
    appKey: publicEnv.PUBLIC_TOGGLY_APP_KEY,
    environment: publicEnv.PUBLIC_TOGGLY_ENVIRONMENT ?? 'Production',
    expose: ['new-dashboard', 'ExpressCheckout'],
    featureDefaults: { 'new-dashboard': false },
  },
});

Use your authenticated session in context(event); identity, groups, claims and request fields are copied once. The hook captures User-Agent, Accept-Language and cf-ipcountry; callback request fields may override them. Never set identity on the process-wide Node client from request handling.

clientContext(event, context) explicitly projects public identity/groups/claims and an optional host-minted instanceId into the frontend snapshot. Omitted means anonymous frontend targeting. Never expose private session claims or credentials. The frontend key must be a Front-end App Key generated in App Settings. Flags must be Available to Client SDK and local browser origins allowed.

A nonblank projected instanceId is trimmed and forwarded to the frontend signed definitions request as i, suppressing client identity/groups/claims targeting, including targeting already in a configured base URL. Backend request evaluation still uses context(event) independently. Project only a token supplied by your host; do not mint with a Backend App Key.

Load and hydrate

Return await loadToggly(event) from +layout.server.ts as toggly, plus your public key/environment. Do not return the server client. The snapshot fetch verifies signatures and exposes only listed frontend flag keys, retaining entity gates.

<script lang="ts">
  import { onMount, onDestroy } from 'svelte';
  import { createToggly } from '@ops-ai/toggly-sveltekit';
  import Feature from '@ops-ai/toggly-sveltekit/Feature.svelte';
  import type { LayoutData } from './$types';
  export let data: LayoutData;
  const toggly = createToggly(data.toggly, {
    appKey: data.publicKey,
    environment: data.environment,
  });
  $: toggly.update(data.toggly);
  onMount(() => {
    void toggly.start();
  });
  onDestroy(() => toggly.dispose());
</script>

<Feature {toggly} feature="new-dashboard">
  <p>New dashboard</p>
</Feature>
<Feature {toggly} feature="new-dashboard" options={{ negate: true }}>
  <p>Classic dashboard</p>
</Feature>
<slot />

Use separate Feature blocks for enabled and disabled content. Keep their feature keys, requirement, entity and defaults identical, and add negate: true to the disabled block. Both evaluate the current snapshot synchronously; no loading state is introduced.

Synchronous initialization selects the same SSR and hydration branch. update(snapshot) handles new server data after navigation/login/logout and rejects prior session completions. Invalidate your server load when authentication changes. This instance has no global Svelte stores.

API

| Surface | Behavior | | ----------------------------------------------------------------- | ------------------------------------------------------------------------------- | | createToggly(snapshot, options) | Creates a synchronous Svelte readable store and evaluation methods | | isEnabled(key, { entity, defaultValue }) | Browser boolean; missing default false; entity gate without entity fails closed | | gate(keys, { requirement, negate, entity, defaultValue }) | all/any, optional negation; empty gate true before negation | | getVariant(key) / getVariantValue(key) | Assigned { name, configurationValue? } | null; requires enableVariants | | start() / update(snapshot) / dispose() | Browser refresh lifecycle, context replacement, cleanup | | notifyLocalGatesChanged() | Notify reactive readers after a local prerequisite changes | | event.locals.toggly.isEnabled(key, { entity }) | Async request-bound Node evaluation using configured defaults | | event.locals.toggly.gate(keys, { requirement, negate, entity }) | Async request-bound composite gate | | loadToggly(event) | One verified, allowlisted frontend fetch per request | | requireFeature(event, keys, options) | Throws HTTP404 on a failed server gate; suitable for loads/actions |

Pass explicit entities: { kind: 'Order', key: 'ord-vip', attributes: { Vip: true } }. The Node core owns rule evaluation; shared entity/local-gate packages own browser gate evaluation. No evaluator is duplicated here.

Variants

Set enableVariants: true on both ServerOptions.frontend and the browser createToggly(snapshot, options) call to opt into A/B variant assignment. When enabled, the frontend snapshot fetch uses /evaluated-variants-signed instead of /evaluated-signed, and the browser store exposes:

const toggly = createToggly(data.toggly, {
  appKey: data.publicKey,
  environment: data.environment,
  enableVariants: true,
});
const variant = toggly.getVariant('checkout-flow'); // { name, configurationValue? } | null
const configurationValue = toggly.getVariantValue<{ color: string }>('checkout-flow'); // soft-typed; optional isT guard

getVariant(key) returns null when variants are disabled, the flag is off (including via local gates), or no variant name was assigned; otherwise it returns { name, configurationValue? }. getVariantValue(key) returns configurationValue ?? null. isEnabled / gate / Feature.svelte keep evaluating the boolean enabled value regardless of enableVariants, so existing gating code is unaffected. Both server and browser must set enableVariants consistently to keep SSR and hydration on the same branch.

Browser telemetry

A keyed browser store reports aggregate telemetry by default. A nonblank snapshot.context.instanceId supplies i; otherwise its identity supplies u. The SDK never mints a token. Direct isEnabled calls and Feature gates count the effective enabled/disabled result after entity and local gates, before negation. Composite gates count only evaluated leaves. Snapshot hydration, refresh and navigation do not themselves record checks; reactive consumers record checks when they evaluate the new snapshot. Usage and views are explicit:

const toggly = createToggly(data.toggly, {
  appKey: data.publicKey,
  environment: data.environment,
  enableTelemetry: true,
  metricsBaseUrl: 'https://metrics.toggly.io',
  telemetryFlushIntervalMs: 30000,
});
toggly.recordUsage('checkout');
toggly.recordView('checkout');
toggly.incrementCounter('orders', 1);
toggly.setGauge('cart-total', 42);
await toggly.flushTelemetry();

Usage and view methods accept an optional variant string; omitting it uses enabled. Counter and gauge names are application-level metrics. Feature and metric names must be nonblank. Variant names contain 1 through 64 ASCII letters, digits, underscores or hyphens. Invalid inputs are dropped without changing evaluation results. onTelemetryDiagnostic optionally receives bounded diagnostic codes.

The collector URL is independent of baseURI; it defaults to https://metrics.toggly.io and appends /api/frontend/telemetry. Payloads contain the frontend key, environment, attribution, aggregate feature counts and metrics. They include at most one of i or u and omit groups, claims, entity attributes and authentication headers. Allow the browser origin in your frontend key settings.

When authentication or a host token changes, rerun the owning server load (for example, with your app's explicit invalidation dependency). If a load projects URL parameters, read those parameters in the load so SvelteKit tracks token-only navigation too.

BrowserOptions.instanceId is an initial convenience when the first snapshot omits it. Every later update(next) owns the complete context: an omitted or blank token clears it, including any configured URL token, and restores identity targeting. Queued events retain their original attribution across updates.

The store owns one bounded reporter across route-driven refresh reconnects. Regular sends use gzip when available; hidden/pagehide and the final dispose() send use plain JSON with browser keepalive. Keep dispose() in the owning layout's destruction handler. Set enableTelemetry: false to disable collection and transport. Keyless stores, SSR and build-time imports never create frontend telemetry; server evaluation remains owned by the supplied Node client.

Refresh and failures

Browser options include appKey, environment, baseURI, allowedKeyIds, maxSignatureAgeSeconds, refreshInterval (180000ms; zero disables polling), enableLiveUpdates (default true), timeout (5000ms), localGates and onError. Signatures are always verified. The adapter reconnects WebSockets and fetches on update messages. Disposal stops polling/sockets and prevents late publication.

Matching SSR/verified snapshots survive failed browser refreshes. Optional signed storage can also restore definitions after a fresh offline restart. Parsed-flag caches are never trusted. New server requests use explicit exposed defaults on frontend fetch/verification failure. Backend defaults/cache behavior is controlled by the supplied Node client. No key is a supported offline mode. frontend.onError reports failed snapshot fetches.

Local gates use { id, flagKeys, isEnabled }; they AND with remote values and cannot enable a remotely disabled flag. Keep initial local gate state identical for SSR/hydration.

Prerender produces build-time defaults/snapshots, not per-user server evaluation or actions. Personalized routes require a running adapter-node server. Presentation gates supplement authentication/authorization; they do not replace either.

Development

npm install
npm run build
npm run test:coverage
npx playwright install chromium
npm run test:host

The SvelteKit sample includes a real adapter-node host, filter matrix and browser smoke tests. Full guide. MIT license. Toggly.

Persistent signed definitions

Pass an optional storage adapter to browser configuration to restore matching definitions after a fresh process or browser restart without fetching signing keys:

const storage = {
  getItem: (key: string) => window.localStorage.getItem(key),
  setItem: (key: string, value: string) => window.localStorage.setItem(key, value),
};
const toggly = createToggly(data.toggly, { appKey: data.publicKey, storage });

These callbacks defer browser storage access until start() runs after mounting. The adapter stores a versioned exact signed envelope and only the public key that verified it. It partitions records by endpoint, app, environment and complete evaluated URL, including the active token or identity/groups/claims. Restore rechecks the current key pins, public-key constraints/expiry, signature age and complete entity schema before publishing; it never trusts a parsed-flag cache or fetches a URL supplied by a stored record. Failed storage access, corrupt data and verification failure leave defaults or already verified state intact.

loadToggly marks its fallback snapshot source: 'defaults', allowing a fresh browser store to restore the matching signed record before the network attempt. A successful server snapshot carries source: 'signed', its verified signedTimestamp and selected public signingKey. These are trusted host hydration metadata, not portable signed credentials: no raw backend definitions or signed envelope are serialized. Browser allowedKeyIds also applies before signed SSR values seed the UI. Older stored state never replaces signed SSR or live state. The layout retains observed key trust across navigation; current observed keys take precedence over stored keys. Manual snapshots without source metadata remain authoritative; explicitly label application defaults source: 'defaults' when they may be replaced by a verified cache.

Signature timestamps cannot move backwards within a running layout/context. Signing-key notifications replace the in-memory key-cache instance and retire historical stored contexts through an endpoint generation marker. If retirement cannot be saved, the client stops using persistence for that layout session, including navigation and reconnects; repair storage access or clear affected storage before a later restart.

Storage is application/origin-owned local trust material. A party able to replace both stored keys and envelopes can replace that trust anchor unless independent allowedKeyIds pins constrain it. Detecting rollback of the entire store after process loss needs external protected state and is not promised. maxSignatureAgeSeconds is rechecked on restore; unset/nonpositive values disable age expiry. Future timestamps and expired keys are rejected. Storage keys contain targeting data, so apply the application's privacy/logout lifecycle.

Definition persistence does not provide cached HTML/assets, offline server loads or actions. A full offline page launch needs an application-owned offline shell. A server-side snapshot failure still returns only the explicit exposed defaults.