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

@sprint-logger/qa-rn

v0.3.1

Published

Sprint QA widget for React Native / Expo host apps — in-context bug reporting and QA queue, authed via a mobile_widget PAT.

Downloads

496

Readme

@sprint-logger/qa-rn

Sprint's QA widget for React Native / Expo host apps — in-context bug reporting and a QA work queue, embedded as a movable FAB + bottom sheet.

This is the native counterpart to Sprint's web QA widget (qa-widget.js). It talks to the same /api/widget/* contract, but authenticates differently: native can't use the web SDK's cross-origin cookie probe, so it uses a mobile_widget Personal Access Token the tester pastes in once per device.

Install

pnpm add @sprint-logger/qa-rn
# peer deps (most Expo apps already have the first three):
pnpm add react-native-gesture-handler react-native-reanimated \
  @react-native-async-storage/async-storage
# optional — only if you want screenshot capture:
pnpm add react-native-view-shot

Mount once near the app root, inside a GestureHandlerRootView.

Two host controls + the guard chain

Visibility is decided every launch (and on foreground / interval). The widget renders only when the whole chain passes — otherwise it silently returns nothing (no FAB, no network).

Host controls (yours):

  • Build-time bundle gate — EXPO_PUBLIC_ENABLE_SPRINT_QA. Your own baked-in env, gating the mount. Decides whether the widget is even shipped. Changing it requires a rebuild. This is the dev's "include it or not" switch.
  • Runtime config from your OWN backend — fetchConfig. The no-rebuild kill switch. Your app fetches { widgetKey, enabled } from your backend at launch. Flip enabled to false in your backend, drop the key, or repoint the app at a backend env that lacks those vars → the widget disappears on the next fetch. No rebuild, no store update. A throw or null return is treated as off (fail-safe).

Sprint-side guards (driven live by /api/widget/config):

  • L1 — enabled. The project's widget_enabled toggle (a Sprint manager's per-project kill switch).
  • L2 — host match. host must be in the project's qa_urls.
  • L3 — identity. A valid mobile_widget PAT (paste prompt if absent).

Recommended: runtime config from your backend (no-rebuild kill switch)

import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SprintQaWidget, makeScreenshotCapture } from "@sprint-logger/qa-rn";
import { useRef } from "react";

export default function App() {
  const rootRef = useRef(null);
  return (
    <GestureHandlerRootView style={{ flex: 1 }} ref={rootRef}>
      <YourApp />
      {/* build-time bundle gate */}
      {process.env.EXPO_PUBLIC_ENABLE_SPRINT_QA === "true" && (
        <SprintQaWidget
          host="my-app-staging"                 // must be in the project's qa_urls
          // runtime config from YOUR backend — the no-rebuild switch:
          fetchConfig={async () => {
            const r = await fetch(MY_BACKEND + "/mobile-config").then((x) => x.json());
            return { widgetKey: r.sprintQaKey, enabled: r.sprintQaEnabled };
          }}
          refreshIntervalMs={60_000}            // optional: re-check every minute
          captureScreenshot={makeScreenshotCapture(rootRef)}
          getContext={() => ({ route: currentRouteName() })}
        />
      )}
    </GestureHandlerRootView>
  );
}

When your backend stops returning sprintQaEnabled:true (or stops returning the key), the widget hides on the next launch/foreground — or within refreshIntervalMs if you set it. No app rebuild.

Static key (known at build time)

If you don't have a backend config endpoint, pass widgetKey directly. You then only have Sprint's widget_enabled toggle as the no-rebuild switch:

<SprintQaWidget widgetKey="pk_qa_xxxxxxxx" host="my-app-staging" />

Auto-captured repro context

Every report carries context so the dev has a repro starting point:

  • Captured by the SDK automatically — viewport (Dimensions) and a device/OS string (ReactNative/<os> <version>). No wiring needed.

  • Supplied by you via getContext — the SDK can't see your current screen (only your navigation library knows it), so pass it in. The thunk is read at report time, so the route is always current:

    getContext={() => ({
      route: navigationRef.getCurrentRoute()?.name,   // → task repro link
      extra: { userId: session?.id, build: Constants.expoConfig?.version },
    })}

    route becomes the task's repro location; extra is folded into the auto-captured context comment. Both are optional.

The PAT flow (per tester, per device)

  1. In Sprint: Settings → Personal access tokens → Mint → Type: "Mobile QA widget", pick the tenant, copy the sprint_pat_… token (shown once).
  2. In the app: tap the QA FAB → paste the token in the "Connect" prompt. It persists per-device (AsyncStorage); you won't be asked again.

The token is tenant-scoped and expires (default 90 days). Revoking it in Sprint denies it on the next call — that's the only fast kill switch (see below).

What auto-updates vs what needs a rebuild

The data/auth contract (endpoints, config, tenant resolution) updates live — Sprint can change server behavior and your installed app picks it up. The UI is compiled into your binary at EAS build time: there is no <script src> to hot-load like the web widget. So a UI change in this package means: bump the version → eas build → store review → user update.

Security implication: a fix to client-side widget code can't be hot-pushed to devices. The fast mitigations are server-side and don't wait on a rebuild: revoke the tester's PAT, or flip the project's widget_enabled off (L1). The mandatory PAT expiry bounds the blast radius of a leaked-but-unrevoked token.

Screenshot PII warning

A captured screenshot is visible to everyone on the project's QA roster (the same tenant). Don't surface capture on screens showing other users' personal data without a redaction step. Capture is always tester-initiated and optional (omit react-native-view-shot to disable it entirely).