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/electron-feature-flags-toggly

v1.4.0

Published

Toggly feature flags SDK for Electron — main-process evaluated-signed client with Flutter-like renderer DX

Readme

@ops-ai/electron-feature-flags-toggly

Feature flags for Electron apps with a Flutter-like DX. The main process owns your App Key, fetches /evaluated-signed definitions, verifies signatures, caches under userData, and keeps a live WebSocket. The renderer uses window.toggly (exposed from preload) — no App Key in Chromium.

Install

npm install @ops-ai/electron-feature-flags-toggly

Peer: Electron ≥ 28. React ≥ 18 is optional (only for @ops-ai/electron-feature-flags-toggly/react).

Electron compatibility

The declared Electron floor is 28. Actual packed native hosts are validated on Electron 28.3.3 and Electron 44.3.0. Each host runs the SDK from a packed artifact in a real Electron main process, the compiled CommonJS preload with context isolation, the renderer IPC bridge, and the optional React helpers. The Electron peer range preserves compatible retained majors; this evidence does not make an untested Electron major a tested support claim.

For local host validation, run npm run test:hosts. It installs disposable host applications with public npm dependencies and launches hidden Electron BrowserWindows. On Linux, provide a display server such as Xvfb for normal Chromium multiprocess execution.

Setup (3 snippets)

1. Main process

import { app, BrowserWindow, ipcMain, powerMonitor } from 'electron'
import { createRequire } from 'node:module'
import {
  initToggly,
  registerTogglyIpc,
  attachTogglyLifecycle,
  isFeatureOn,
} from '@ops-ai/electron-feature-flags-toggly/main'

const require = createRequire(import.meta.url)
const togglyPreload = require.resolve('@ops-ai/electron-feature-flags-toggly/preload/entry')

await app.whenReady()

await initToggly({
  appKey: process.env.TOGGLY_APP_KEY,
  environment: 'Production',
  userDataPath: app.getPath('userData'),
  flagDefaults: {
    NewDashboard: false,
    BetaMenu: false,
  },
})

registerTogglyIpc(ipcMain, () => BrowserWindow.getAllWindows())
attachTogglyLifecycle(app, powerMonitor)

// Trusted process can gate menus / windows directly:
if (isFeatureOn('BetaMenu')) {
  // …
}

2. Preload bridge

const window = new BrowserWindow({
  webPreferences: {
    preload: togglyPreload,
    contextIsolation: true,
    nodeIntegration: false,
  },
})

Electron executes webPreferences.preload files with its CommonJS loader, even when your main process uses ESM. preload/entry is the package's compiled CommonJS entry: it calls exposeToggly() and exposes only window.toggly to the renderer. Resolve it from the main process as shown; do not point webPreferences.preload at a source-level ESM import file.

If you need to add your own preload APIs, bundle your preload as CommonJS and include @ops-ai/electron-feature-flags-toggly/preload in that bundle rather than externalizing it. Electron's sandboxed preload loader cannot resolve an arbitrary package with require() at runtime.

3. Renderer

import { isFeatureOn, evaluateFeatureGate } from '@ops-ai/electron-feature-flags-toggly'

if (isFeatureOn('NewDashboard')) {
  showDashboard()
}

const unlock = evaluateFeatureGate(
  ['FeatureA', 'FeatureB'],
  'any',
  false,
)

React (optional)

import { Feature, useFeatureFlag } from '@ops-ai/electron-feature-flags-toggly/react'

export function App() {
  const { isEnabled } = useFeatureFlag('NewDashboard')

  return (
    <>
      <Feature featureKey="NewDashboard">
        <NewDashboard />
      </Feature>
      <Feature featureKey="NewDashboard" negate>
        <LegacyDashboard />
      </Feature>
      {isEnabled && <Badge />}
    </>
  )
}

Hooks use defaultValue until their first committed evaluation and expose isReady. Abandoned renders and StrictMode effect replay do not send duplicate checks. Changed definitions, local gates or evaluation inputs recompute; unchanged snapshot notifications do not. refresh() explicitly evaluates again.

Usage and business metrics

Telemetry defaults on when the main client has an application key. Configure enableTelemetry: false to opt out, metricsBaseUrl for an independent base URL (an explicit path is preserved), or telemetryFlushIntervalMs (30000–60000; default 45000, with 20% scheduling jitter). Invalid intervals fall back to 45000; invalid URLs disable reporting without breaking flag evaluation. Main-only telemetryFetch and onTelemetryDiagnostic support independent transport and bounded payload-free diagnostics.

The main client owns one reporter shared by its windows. Renderer/preload/React never create a transport. Checks record the effective leaf after entity/local gates and before aggregate negation, preserving short circuiting. Cached direct reads count; definitions loading and snapshot reads alone do not. Usage/view remain explicit, including in React components:

import {recordUsage, recordView, incrementCounter, setGauge, flushTelemetry}
  from '@ops-ai/electron-feature-flags-toggly/renderer'
recordUsage('Checkout')             // variant defaults to "enabled"
recordView('Cart', 'blue')
incrementCounter('orders', 2)
setGauge('cartItems', 3)
await flushTelemetry()

These methods are also available on the main client, /main, /react and window.toggly. Variants are 1–64 ASCII letters/digits/underscore/hyphen. Counters use nonnegative integer deltas; gauges use finite nonnegative values, up to 1000000 per call. Metrics are app-level bare names. Payloads contain only application/environment, aggregate checks/usage/views and business metrics. A nonblank host-minted instanceId is sent as i; otherwise the existing identity (including the generated anonymous UUID) is sent as u, never both. Claims, groups and entity data are excluded. Do not mint tokens with Backend keys in the client. The server must explicitly accept client identity. Ordinary native requests use Node gzip and omit credentials and Origin. No browser lifecycle or network library is included in renderer code.

New telemetry IPC handlers accept only the exact top-level frame of a live window returned by getWindows; they reject unlisted windows, subframes, extra arguments, invalid types/variants/numeric bounds and keys over 1024 UTF-16 code units. Renderers cannot set telemetry configuration or destination. Provide the window list shown above to enable these methods. Legacy flag and context IPC authorization behavior with an omitted list remains unchanged; those payloads are now bounded/validated (gates up to 2000 keys, contexts up to 2000 values/eight levels and a conservative 16KiB string budget).

Attach attachTogglyLifecycle(app, powerMonitor) once after each initialization. It flushes on window blur, all windows closing and native suspend. On normal app quit it closes the owner first, initiates one plain final envelope with no retry, then lets quit continue within five seconds. closeToggly() and client.close() remain synchronous best-effort cleanup; use awaitable flushTelemetry() when deterministic completion is needed before shutdown. Closing one window does not dispose another's owner. Reinitialization closes the previous owner, discards its pending telemetry and removes its IPC/lifecycle listeners; register the new owner's IPC and lifecycle again. The registration functions return detach callbacks. Forced process termination cannot guarantee delivery. Telemetry is never written to the feature disk cache.

API overview

| Surface | Entry | |---------|--------| | Main | @ops-ai/electron-feature-flags-toggly/main | | Preload bridge | @ops-ai/electron-feature-flags-toggly/preload/entry | | Custom bundled preload | @ops-ai/electron-feature-flags-toggly/preload | | Renderer | @ops-ai/electron-feature-flags-toggly or /renderer | | React | @ops-ai/electron-feature-flags-toggly/react |

Main

  • initToggly(config) → Promise<flags>
  • getToggly() / closeToggly()
  • isFeatureOn / isFeatureOff / evaluateFeatureGate
  • setContext / clearContext / addHook
  • registerTogglyIpc(ipcMain, getWindows) / attachTogglyLifecycle(app, powerMonitor?)
  • recordUsage / recordView / incrementCounter / setGauge / flushTelemetry

Renderer (window.toggly)

  • isFeatureOn(key, entityContext?, kind?)
  • evaluateFeatureGate(keys, requirement?, negate?, entityContext?, kind?)
  • getFlags() / setContext / clearContext
  • onFlagsUpdated(callback) → unsubscribe

Accepted events retain the identity and application context captured at evaluation, even when hooks change context. Context updates share one 2000-entry/256KiB queue budget. setContext({ instanceId }) updates the existing owner through the validated bridge. A blank token clears it; changing identity clears an omitted token, while group-only updates retain it. Token-based definitions requests omit identity, groups and claims, including inherited base-URL values.

Configuration

| Option | Default | Notes | |--------|---------|--------| | enableTelemetry | true with app key | Shared main-process reporter | | metricsBaseUrl | https://metrics.toggly.io | Independent metrics base URL | | telemetryFlushIntervalMs | 45000 | 30000–60000ms, jittered | | appKey | — | Frontend App Key | | environment | Production | | | baseURI | https://definitions.toggly.io | | | userDataPath | required | app.getPath('userData') | | flagDefaults | {} | Offline / no-key fallback | | instanceId | undefined | Optional host-minted token; preferred over identity | | identity / groups / claims | | Targeting context | | verifySignatures | false | ES256 via JWKS | | enableLiveUpdates | true when appKey set | WebSocket in main | | connectTimeout | 5000 | ms | | featureFlagsRefreshInterval | 180000 | ms |

Entity context

Entity-gated flags fail closed without a context. Pass an entity on each read:

isFeatureOn('OrderBadge', order, 'Order')

Signed definitions

Set verifySignatures: true to verify evaluated-signed envelopes in the main process before caching or serving results to the renderer.

Example

See example/ for a minimal main + preload + HTML app.

License

MIT © opsAI LLC