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

@avsbhq/edge

v1.0.0

Published

Edge-runtime SDK for the [A vs B](https://app.avsb.cloud) platform.

Readme

@avsbhq/edge

Edge-runtime SDK for the A vs B platform.

Single package, six runtimes. Evaluate feature flags and track events in Cloudflare Workers, Vercel Edge Functions, Fastly Compute, Netlify Edge Functions, Deno Deploy, Bun, and AWS Lambda@Edge. Built on @avsbhq/core; each runtime adapter handles the platform-specific KV caching, request lifecycles, and event flushing patterns.


1. Install

npm install @avsbhq/edge

No mandatory peer dependencies. Each runtime adapter imports the platform-specific types it needs — install only what your runtime requires.

Supported runtimes:

  • Cloudflare Workers (with KV/R2 datafile cache)
  • Vercel Edge Functions (with Edge Config cache)
  • Fastly Compute@Edge (with KV Store cache)
  • Netlify Edge Functions
  • Deno Deploy
  • Bun
  • AWS Lambda@Edge

2. Quickstart

Cloudflare Workers

// worker.ts
import { createCloudflareHandler } from '@avsbhq/edge/cloudflare'

const handler = createCloudflareHandler({
  sdkKey: () => env.AVSB_SDK_KEY,
  kvNamespace: (env) => env.AVSB_KV,
  contextFrom: (req, env) => ({
    kind: 'user' as const,
    key: req.headers.get('cf-connecting-user') ?? 'anon',
  }),
})

export default {
  async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const { client } = await handler.init(req, env, ctx)

    const flag = client.getBoolFlag('new-feature', false)

    const response = new Response(
      JSON.stringify({ enabled: flag.isEnabled() }),
      { headers: { 'Content-Type': 'application/json' } }
    )

    // Flush events after response is sent
    ctx.waitUntil(client.flushEvents())
    return response
  },
}

Vercel Edge Functions

// api/feature-check/route.ts (Edge runtime)
import { createVercelEdgeHandler } from '@avsbhq/edge/vercel'
import { createClient } from '@vercel/edge-config'

export const runtime = 'edge'

const handler = createVercelEdgeHandler({
  sdkKey: process.env.AVSB_SDK_KEY!,
  edgeConfig: createClient(process.env.EDGE_CONFIG),
  contextFrom: (req) => ({
    kind: 'user' as const,
    key: req.headers.get('x-user-id') ?? 'anon',
  }),
})

export async function GET(req: Request) {
  const { client } = await handler.init(req)
  const flag = client.getBoolFlag('homepage-hero', false)
  return Response.json({ variant: flag.variationKey })
}

Fastly Compute@Edge

import { createFastlyHandler } from '@avsbhq/edge/fastly'

const handler = createFastlyHandler({
  sdkKey: fastly.env.get('AVSB_SDK_KEY') ?? '',
  kvStore: 'AVSB_KV',
  contextFrom: (req) => ({
    kind: 'user' as const,
    key: req.headers.get('x-user-id') ?? 'anon',
  }),
})

addEventListener('fetch', async (event) => {
  const { client } = await handler.init(event.request)
  const flag = client.getBoolFlag('my-flag', false)
  event.respondWith(
    new Response(JSON.stringify({ value: flag.value }))
  )
})

Netlify Edge Functions

// netlify/edge-functions/feature.ts
import { createNetlifyEdgeHandler } from '@avsbhq/edge/netlify'

const handler = createNetlifyEdgeHandler({
  sdkKey: Deno.env.get('AVSB_SDK_KEY') ?? '',
  contextFrom: (req) => ({
    kind: 'user' as const,
    key: new URL(req.url).searchParams.get('uid') ?? 'anon',
  }),
})

export default async function (req: Request) {
  const { client } = await handler.init(req)
  const flag = client.getStringFlag('theme', 'light')
  return Response.json({ theme: flag.value })
}

export const config = { path: '/api/theme' }

Deno Deploy

import { createDenoHandler } from '@avsbhq/edge/deno'

const handler = createDenoHandler({
  sdkKey: Deno.env.get('AVSB_SDK_KEY') ?? '',
  contextFrom: (req) => ({
    kind: 'user' as const,
    key: req.headers.get('x-user-id') ?? 'anon',
  }),
})

Deno.serve(async (req) => {
  const { client } = await handler.init(req)
  const flag = client.getBoolFlag('beta-feature', false)
  return Response.json({ enabled: flag.isEnabled() })
})

Bun

import { createBunHandler } from '@avsbhq/edge/bun'

const handler = createBunHandler({
  sdkKey: Bun.env.AVSB_SDK_KEY ?? '',
  contextFrom: (req) => ({
    kind: 'user' as const,
    key: req.headers.get('x-user-id') ?? 'anon',
  }),
})

Bun.serve({
  async fetch(req) {
    const { client } = await handler.init(req)
    const flag = client.getBoolFlag('new-ui', false)
    return Response.json({ enabled: flag.isEnabled() })
  },
})

AWS Lambda@Edge

import { createLambdaEdgeHandler } from '@avsbhq/edge/lambda'

const handler = createLambdaEdgeHandler({
  sdkKey: process.env.AVSB_SDK_KEY ?? '',
  contextFrom: (event) => ({
    kind: 'user' as const,
    key: event.Records[0].cf.request.headers['x-user-id']?.[0]?.value ?? 'anon',
  }),
})

export const lambdaHandler = async (event: CloudFrontRequestEvent) => {
  const { client } = await handler.init(event)
  const flag = client.getBoolFlag('origin-routing', false)
  // Modify CloudFront request based on flag
  const cf = event.Records[0].cf
  if (flag.isEnabled()) cf.request.uri = '/v2' + cf.request.uri
  return cf.request
}

3. SDK keys

Use a server SDK key (sdk-server-...) — edge functions run server-side code. Store it in your runtime's secret store:

| Runtime | Secret storage | |---|---| | Cloudflare Workers | wrangler secret put AVSB_SDK_KEY | | Vercel Edge | Vercel dashboard → Environment Variables | | Fastly | fastly secret store | | Netlify | Netlify dashboard → Environment Variables | | Deno Deploy | Deno dashboard → Environment Variables | | Bun | .env file or platform secret store | | Lambda@Edge | AWS Secrets Manager or Lambda environment |


4. Identity

All adapters accept a contextFrom(req) function that extracts the EvalContext from the incoming request:

contextFrom: (req) => ({
  kind: 'user' as const,
  key: req.headers.get('x-user-id') ?? anonymousId(req),
  country: req.cf?.country,  // Cloudflare: geo from CF object
})

For multi-context:

contextFrom: (req) => ({
  kind: 'multi' as const,
  user: {
    kind: 'user' as const,
    key: req.headers.get('x-user-id') ?? 'anon',
  },
  organization: {
    kind: 'organization' as const,
    key: req.headers.get('x-org-id') ?? 'none',
    tier: req.headers.get('x-org-tier') ?? 'free',
  },
})

There is no identify() call in edge contexts — the context is immutable per request.


5. Multi-context

Pass a multi-context through contextFrom:

contextFrom: (req) => ({
  kind: 'multi' as const,
  user: { kind: 'user' as const, key: getUserId(req) },
  organization: { kind: 'organization' as const, key: getOrgId(req) },
})

The edge client evaluates rules against all context kinds simultaneously.


6. Reading flags

The AvsbEdgeClient exposes the same typed methods as @avsbhq/browser:

const boolFlag    = client.getBoolFlag('dark-mode', false)
const strFlag     = client.getStringFlag('theme', 'light')
const numFlag     = client.getNumberFlag('max-results', 25)
const jsonFlag    = client.getJsonFlag<{ timeout: number }>('api-config', { timeout: 5000 })
const genericFlag = client.getFlag<boolean>('checkout-v2', false)
const all         = client.getAllFlags()

All return Flag<T>. The flag object carries .value, .isEnabled(), .variationKey, .source, .reasons.

Edge runtimes have no persistent state between requests. The datafile is cached in the runtime-native KV store and fetched on cold start or TTL expiry.


7. Tracking events

client.track('page_viewed', {
  value: 1,
  properties: { path: new URL(req.url).pathname },
})

// Always flush after the response is sent
ctx.waitUntil(client.flushEvents())  // Cloudflare
// Other runtimes: await client.flushEvents() after response

Events queued during request processing are flushed asynchronously after the response is sent. Never await the flush inside the response path — use ctx.waitUntil (CF) or fire-and-forget.


8. Error handling

Each adapter accepts an optional logger and onError callback:

const handler = createCloudflareHandler({
  sdkKey: () => env.AVSB_SDK_KEY,
  kvNamespace: (env) => env.AVSB_KV,
  contextFrom: (req) => ({ kind: 'user', key: 'u_1' }),
  logger: createLogger({ level: 'warn', transports: [consoleTransport()] }),
  onError: (err, source) => {
    // source: 'init' | 'eval' | 'track' | 'cache'
    console.error('avsb error', source, err.message)
  },
})

If the datafile cache is stale or unavailable, the edge client serves defaults and logs a warning. It never throws — all errors are handled gracefully.


9. SSR / hydration

For server-side rendering in edge functions, evaluate flags in the request handler and pass the values to your rendered HTML or JSON response:

const flag = client.getBoolFlag('homepage-hero', false)
const html = renderToString(<App heroVariant={flag.variationKey ?? 'control'} />)

For Next.js App Router edge middleware (not full rendering), use @avsbhq/next/middleware which is built on the same adapters.


10. Graceful shutdown

Edge runtimes have no persistent process — each request is a fresh invocation. There is no close() to call. Always flush events using the runtime's deferred-work mechanism:

// Cloudflare Workers
ctx.waitUntil(client.flushEvents())

// Vercel Edge (native fetch keepalive)
void client.flushEvents()

// Deno / Bun / Netlify
await client.flushEvents()  // after response is sent

11. Testing

import { AvsbEdgeClient } from '@avsbhq/edge'
import { createMockClient } from '@avsbhq/test'

const mock = createMockClient({
  flags: [
    TestData.flag('new-feature').booleanFlag().fallthroughVariation(true).build(),
  ],
})

// Test your handler with the mock injected
const response = await handleRequest(req, { avsbClient: mock })
expect(response.status).toBe(200)

12. Migration

From LaunchDarkly Cloudflare

| LaunchDarkly Cloudflare | @avsbhq/edge/cloudflare | |---|---| | init(sdkKey, { kvNamespace }) | createCloudflareHandler({ sdkKey, kvNamespace, contextFrom }) | | client.variation('key', ctx, default) | client.getBoolFlag('key', default).value | | client.variationDetail('key', ctx, default) | client.getFlag('key', default) | | client.flush(ctx) | ctx.waitUntil(client.flushEvents()) |

From Statsig Edge

| Statsig Edge | @avsbhq/edge | |---|---| | StatsigServer.initialize(key) | createCloudflareHandler({ sdkKey, contextFrom }) | | checkGate(user, 'gate') | client.getBoolFlag('gate', false).isEnabled() | | logEvent(user, 'event', value) | client.track('event', { value }) | | flush() | ctx.waitUntil(client.flushEvents()) |

Key differences:

  • All adapters are per-request — no singleton initialization needed.
  • The datafile is cached in the runtime's native KV store, not fetched on every request.
  • Multi-context is native — pass { kind: 'multi', ... } from contextFrom.
  • No setInterval or EventSource — edge environments have no persistent event loop.