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

@industream/hub-react

v1.0.0

Published

React integration helpers for the Industream Hub (auth bridge, theme/language sync, route mirroring).

Readme

@industream/hub-react

React helpers for integrating an SPA with the Industream Hub: theme / language sync, JWT-aware fetch wiring, and route mirroring with the menu shell. Published on npmjs; apps inside the ironstream workspace link the sources instead. Releasing a version: see docs/publishing.md.

Layout

src/
├── hub-connector.ts      Pure-TS façade around the menu-origin
│                         <industream-login> web component.
│                         Standalone-safe: returns null on failure.
├── HubProvider.tsx       React Context provider + useHub() hook.
├── useHubAuth.ts         Reactive auth-state hook (status + signIn).
├── auth-token.ts         Module-level access-token getter consumed by
│                         fetch wrappers outside the React tree.
└── useRouteAdvertiser.ts Hook that mirrors react-router navigation
                          to the parent iframe (and vice-versa).

Consuming from another app

From another repository, install the published package:

pnpm add @industream/hub-react

The @industream scope also exists on GitHub Packages, and npm routes a scope to a single registry. If the machine or repository .npmrc sends @industream to GitHub Packages, add a repository-local .npmrc that sends it back to npmjs:

@industream:registry=https://registry.npmjs.org/

Inside the ironstream workspace, link the sources instead (no build needed):

"dependencies": {
  "@industream/hub-react": "workspace:*"
}

Then:

import {
  HubProvider,
  useHub,
  useHubAuth,
  setAccessTokenGetter,
  useRouteAdvertiser,
} from '@industream/hub-react'

Peer deps: react >=18, react-dom >=18, react-router >=7. The consumer must already depend on those.

Authentication

How the plumbing works

The <industream-login> element (loaded at runtime from the menu origin) does not hold the session itself. It talks to a hidden iframe on the menu origin, which connects to a SharedWorker — the single holder of the Hub session for every app and tab on the machine:

matcat tab ──┐
menu tab   ──┼─ hidden iframe (menu origin) ── SharedWorker ── hub /auth/*
other app  ──┘                                   │
                                     access token (15 min TTL)
                                     refresh token (30 days, IndexedDB)

The worker refreshes the access token proactively in the background (at ~75% of its TTL, via POST /auth/refresh), so a user parked on the UI for hours never sees an expiry. When a refresh finally fails (refresh token expired), the worker broadcasts a login-required state and the element reopens its modal.

Because the session lives in the worker, signing in anywhere signs in everywhere: the worker pushes authenticated / logged-out events to all connected apps, and the element re-emits them as DOM CustomEvents. That is what subscribeAuthChanged listens to.

getAccessToken() vs signIn()

Two different jobs — both are needed:

| | getAccessToken() | signIn() | |---|---|---| | Prompting | never (interactive: false) | may open the login modal | | Callers | fetch wrappers on every request; useHubAuth's probe | an explicit "sign in" user action only | | Job | read the current session (fresh token or null) | acquire a session |

Never call signIn() on load or from background code: every probe or API call without a session would pop the modal.

useHubAuth()

Reactive auth state for gate-style UX:

const { status, signIn } = useHubAuth()

| status | Meaning | Typical UI | |---|---|---| | loading | hub connection not settled yet | spinner | | authenticated | valid session available | render the app | | unauthenticated | hub reachable, no session — signIn() can recover | "Sign in" button | | unavailable | no hub configured / unreachable — signIn() is a no-op | error card / standalone |

Status is probed from getAccessToken() (when the connection settles, after signIn(), and on every authenticated/logged-out element event), not pushed with a payload: the probe is the single source of truth. Two implementation details worth knowing before touching the hook:

  • Probe epochs — overlapping probes (hub swap + auth event) can resolve out of order; each probe takes a ticket and only the latest may publish, so a stale result never overwrites a fresher status.
  • Shared in-flight sign-in — concurrent signIn() calls (button double-click) share one attempt; the element's behavior on parallel interactive requests is undefined.

The hook never stores the token. HTTP clients keep pulling it per request via getAccessToken() (auth-token.ts), which is what makes background refresh transparent.

Standalone fallback

HubProvider reads the menu origin from a runtime config function the consumer provides (so this package doesn't dictate where to put your own runtime config). When the menu origin is empty or the menu can't be reached within the timeout, the connector resolves to null and consumers should fall back to local-only behavior (e.g. their own theme selector + localStorage).

What it does NOT cover

  • App-specific theme palettes (matcat-ui has 3 variants, the hub speaks 2 — every consumer maps its own way using useHub().hub?.broadcast / subscribeCustom('set-theme', …) and the set-language analog).
  • App-specific HTTP clients (each consumer wraps its own fetch and pulls the token via getAccessToken() from auth-token).
  • The menu shell side of the protocol — that lives in industream-hub/packages/industream-menu.