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

@txlemetry/browser-common

v0.1.1

Published

Shared browser utilities and extension primitives for Txlemetry browser SDKs

Readme

@txlemetry/browser-common

Internal shared browser utilities and extension primitives for Txlemetry JavaScript SDKs. This package is published so unbundled SDK outputs can resolve it at runtime, but it is not a public API surface and does not provide compatibility guarantees outside Txlemetry SDK packages.

The shared extension contract includes the interface an extension implements (Extension), the host capabilities it is handed (Client), and small shared runtime primitives such as Publisher.

An extension written against this contract runs unchanged across major versions of the web SDK:

  • v1 is synchronous; extensions are registered statically.
  • v2 is asynchronous; extensions are loaded dynamically.

Each SDK provides a client adapter that implements Client over its own internals, so extension code never depends on a specific SDK.

Concepts

Extension

What you implement. The host calls only setup and dispose:

import type { Disposable, Extension } from '@txlemetry/browser-common'

export function webContext(): Extension {
  let removeProperties: Disposable | undefined

  return {
    name: 'webContext',
    setup(client) {
      removeProperties = client.registerDynamicEventProperties(() => ({
        $current_url: window.location.href,
      }))
    },
    dispose() {
      removeProperties?.dispose()
    },
  }
}

setup(client) may be async (read async state before you're ready); dispose() may be async (final flush). Static config the app sets goes in your constructor, not on the Client.

Anything in setup that returns a Disposable must be held by the extension and disposed in dispose().

Client

What an extension is given in setup — the host's capability surface:

  • identity & session (synchronous reads): distinctId, anonymousId, groups, session
  • events: capture(...), registerDynamicEventProperties(...) (contribute properties), onEvent(...) (observe)
  • transport: apiRequest(path, init?)
  • server config: getRemoteConfig() (current), onRemoteConfig(...) (changes)
  • lifecycle: onNewSession(...)
  • registry: getExtension(token)
  • storage & logging: kv, logger

Synchronous members are always-ready in-memory reads; everything that does I/O or waits for readiness (capture, apiRequest, kv, getRemoteConfig) is asynchronous.

Publisher

Use Publisher<T> when an extension provides its own event stream to other extensions or to app-facing controls. Keep the publisher private, expose only its listener, and dispose it when the extension is torn down:

import { Publisher, type Listener } from '@txlemetry/browser-common'

const changes = new Publisher<FeatureFlagsChange>()

export const onChange: Listener<FeatureFlagsChange> = changes.listener

changes.publish({ flag: 'beta-ui', value: true })
changes.dispose()

Cross-extension dependencies

Extensions depend on one another through tokens, never implementation imports:

import { FeatureFlags } from './feature-flags/token'

const flags = client.getExtension(FeatureFlags) // FeatureFlagsExtension | undefined
if (flags && (await flags.getFeatureFlag('beta-ui'))) {
  /* … */
}

A token is implementation-free, so importing it never pulls the provider's code into your bundle — each extension stays independently tree-shakable and lazily loadable. An extension that provides a capability declares its token(s) in provides.

Authoring

See the develop-extension skill (.agents/skills/develop-extension/SKILL.md) for the full guide: the capability cheatsheet, the rules (enrichers are synchronous, dispose your disposables, design for asynchronous readiness, cross-extension state goes through getExtension, not shared storage), and the v1 → Client porting map.

Status

Early and internal. The package currently defines the extension contract and the shared Publisher helper. Additional shared runtime helpers — key-value stores, the registry implementation, and a test Client — will land alongside the first ported extension.