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

@bravely-studios/account-web

v0.5.0

Published

Bravely Account web facade: OAuth 2.1 + PKCE sign-in, BAS lifecycle, entitlement cache, activation state machine, C-ux M2/M3/M4 components. Used by all 10 utility web variants + bravely.dev.

Readme

@bravely-studios/account-web

Thin TypeScript facade over the Bravely identity API for browser-based Bravely Studios apps. Used internally across the Bravely web app family.

What it does

  • OAuth 2.1 + PKCE sign-in via auth.bravely.dev — RFC 7636 S256.
  • Session forever (0.4.0, D4) — durable IndexedDB session plus the grant_type=refresh_token rotation loop: pre-expiry background refresh, 401 refresh-then-retry-once, and destructive sign-out ONLY on a definitive invalid_grant / 401-after-refresh. Transport failures never wipe tokens.
  • Login-first screen (0.4.0, D1)<BravelySignInScreen>: icon + name
    • ONE value-prop line + the locked provider picker + passive legal links; recoverable offline state; onShown seam for login_screen_shown. Compact per-step geometry (0.4.1, D3 v1.2): the column caps at SIGN_IN_COMPACT_MAX_WIDTH (480px) — sign-in never inherits offer geometry.
  • 3-slot offer grid (0.4.0, D3; 0.4.1 v1.2)<OfferSlotGrid> + the locked slot-copy factory (buildOfferSlots), per-app themes, HEIGHT-FIRST full-viewport scale-to-fit INCLUDING scale-up (scale fills viewportHeight × 0.92, width capped at viewportWidth × 0.95; clamp [0.5, 3.0], scroll fallback below, no max-width column cap), slot-card baseline row alignment (subgrid; CTA bottom-pinned), slot_viewed/slot_selected hooks.
  • Install metrics (0.4.0, D6)getOrMintInstallId() + emitAppFirstOpenedIfNeeded() (persist-first fire-once sentinel; the host fires the actual analytics event).
  • Entitlement cache — 72h offline fallback.
  • Activation state machine — checkout-to-active flow (v1.1.0: the user_skipped lane is removed — no skip/guest affordance).
  • Paddle account actions — BAS-authed checkout session and customer-portal session helpers through identity.bravely.dev.
  • Bravely-Deprecation handling — soft warnings + hard BravelyClientKilledError on HTTP 426 kill-switch.
  • DPoP-ready — RFC 9449 proof generation.

Install

npm install @bravely-studios/account-web

Usage

import { BravelyAccountManager } from "@bravely-studios/account-web";

const manager = new BravelyAccountManager({
  authority: "https://auth.bravely.dev",
  appSlug: "diskaroo",
  clientVersion: "1.3.1",
  // Optional: capture SECRET-FREE breadcrumbs (HTTP status + step) from the
  // auth / checkout / activation / sign-out failure paths into the host's
  // diagnostic ring buffer. Omit for no-op (no behavior change).
  log: (event, ctx) => DiagnosticLog.warn("bravely-account", event, ctx),
});

// On page load
await manager.restore();
manager.onStateChange((state) => {
  if (state.kind === "signed_in") renderApp(state);
});

// On a sign-in button click
await manager.signIn();

// React 19? Subscribe via useSyncExternalStore — `getState` returns a stable
// reference between updates (0.2.1+), so no `_cachedState` workaround needed.
//
//   const state = useSyncExternalStore(
//     manager.onStateChange.bind(manager),
//     manager.getState.bind(manager),
//   );

// Entitlement gate
if (await manager.hasEntitlement("diskaroo_pro")) {
  showProFeatures();
}

// Paid upgrade
await manager.openCheckout("annual");

// Manage subscription
const portal = await manager.createPaddlePortalSession();
window.open(portal.url, "_blank");

Module map

| File | Responsibility | |-----------------------------------------|-----------------------------------------------------------------------------------| | BravelyAccountManager.ts | Public facade. Sign-in, sign-out, entitlements, checkout, portal, activation. | | EntitlementCache.ts | 72h offline cache with TTL + invalidation. | | ActivationStateMachine.ts | Port of the canonical activation machine. | | oauth.ts | PKCE S256 helpers + authorize URL builder. | | storage.ts | sessionStorage / IndexedDB / memory adapters. | | dpop.ts | WebCrypto ES256 keypair + RFC 9449 proofs (Gate 2-ready). | | deprecation.ts | Bravely-Deprecation header parser + error classes. | | types.ts | TS types mirroring the OpenAPI 3.1 schemas. | | displayName.ts | Slug → display-name lookup (diskarooDiskaroo). | | components/ActivationLadder.tsx | M3 — post-checkout 4-phase ladder (Activating <App> Pro…). | | components/CrossAppCard.tsx | M4 — third-quadrant card (You own N Bravely Pro apps on this account.). | | components/BravelyProviderButtons.tsx | Canonical Apple/Google/Email picker — mirrors the Swift SwiftUI surface. | | components/BravelySignInScreen.tsx | D1 — the login-first first screen (icon + name + value prop + picker). | | components/OfferSlotGrid.tsx | D3 — locked 3-slot offer grid + copy factory + viewport fit (scale-up). | | installMetrics.ts | D6 — install-id mint + fire-once app_first_opened sentinel helpers. | | hooks/useActivationLaneFromUrl.ts | M3 — detect ?upgraded / ?checkout / ?subscription return params. | | hooks/useFreshLaunchRestoration.ts | M2 — silent rehydrate + brief Synced N items from your <device>. toast. |

C-ux M2/M3/M4 exports (0.2.0)

Wave A of the C-ux M2-M4 rollout (cux-m2-m4-rollout-plan.md). New exports let the four D-Web variants — prodjectly, scry-web, printscreenly-web, todoingly-web — mount the foundational surfaces in Wave B-D.

<ActivationLadder> (M3)

import { ActivationLadder } from "@bravely-studios/account-web";

<ActivationLadder
  state={manager.getActivationState()}
  appSlug="diskaroo"
  orderId={paddleOrderId ?? null}
  onRetry={() => manager.pollForActivation()}
  onContactSupport={() => window.open("mailto:[email protected]")}
/>

Renders nothing unless the manager is in post_checkout_activation. Auto-ticks elapsed every second; rolls through the 4 locked phases at 0/15s/60s/120s. Phase copy is byte-identical to bravely-commerce-router/docs/activation-state-machine.json — the drift-test in __tests__/ActivationLadder.test.tsx enforces it.

<CrossAppCard> (M4)

import { CrossAppCard } from "@bravely-studios/account-web";

<CrossAppCard
  entitlements={state.entitlements}
  currentAppSlug="diskaroo"
  variant="card"        // or "footer-chip"
  dismissible={false}   // journey-doc default = persistent
/>

Renders nothing when the user has zero cross-app entitlements. Excludes the current app's own <slug>_pro from the count; treats bravely_premium as a single bundle token.

useActivationLaneFromUrl() (M3)

const { inActivationLane, source, clearUrlParam } = useActivationLaneFromUrl({
  manager,
  autoStartPolling: true,
});

useEffect(() => {
  if (inActivationLane) clearUrlParam();
}, [inActivationLane]);

Detects the three observed post-checkout return URL patterns: ?upgraded=true (prodjectly), ?checkout=complete (scry-web), ?subscription=success (todoingly-web). Auto-calls manager.notifyCheckoutCompleted() and, if autoStartPolling, kicks off manager.pollForActivation().

<BravelyProviderButtons> (0.3.5)

import { BravelyProviderButtons } from "@bravely-studios/account-web";

<BravelyProviderButtons
  style={{ variant: "dark", accent: "#0ea5e9" }}
  isBusy={isAuthorizing}
  onTap={(provider) => manager.signIn({ loginHint: provider })}
/>

Drop-in 3-button provider picker (Apple / Google / Email) sized to the hosted shell at auth.bravely.dev: 48px height, 12px radius, 10px stack gap, 14px label. Web-mirror of the Swift BravelyProviderButtons component, so the iOS/Mac/Web surfaces of the same app look identical. Pure React + inline SVG; no UI-library or CSS-file dependency. Pass style.accent as any CSS color string — color-mix handles the email button tint at runtime.

useFreshLaunchRestoration() (M2)

const fresh = useFreshLaunchRestoration({
  manager,
  itemsLabel: "tasks",
  resolveOtherDeviceName: () => null, // Gate 1 fallback
});

useEffect(() => {
  fresh.setSyncedCount(myCollection.length);
}, [myCollection.length]);

return fresh.shouldShowToast ? <Toast>{fresh.toastText}</Toast> : null;

First-launch detector. UI is silent for 3 s after sign-in; then Synced N items from your <device>. shows for the host page to dismiss. The banned phrase family (Welcome back. Restoring your Pro features) is absent by design. Gate 1 device-name resolver is null; the hook drops the from your <device> anchor automatically.

Manager additions

// Cross-app filter — excludes <currentApp>_pro, includes bravely_premium.
const others = manager.crossAppEntitlements();

// Account-wide entitlement snapshot (0.3.6) — powers M4 cross-app awareness
// card and the Pro portfolio tile. Returns one row per app in the catalog.
const accountEnt = await manager.getAccountEntitlements();
// accountEnt.apps: AppEntitlement[]  — one row per catalog app
// accountEnt.active_entitlements: string[]  — all active lookup_keys family-wide
// accountEnt.subscription: SubscriptionInfo | null

// Post-checkout polling runner — 30 retries × 1s..8s capped backoff.
const result = await manager.pollForActivation();
// result.outcome: "active" | "exhausted" | "timeout" | "not_signed_in"

// Paddle customer-portal session — callers decide how to open the URL.
const portal = await manager.createPaddlePortalSession();
// portal.url is the hosted customer-portal URL.

Storage adapters

  • sessionStorage — PKCE verifier + state ONLY (single OAuth-dance secrets; they die with the tab by design).
  • IndexedDB — the durable session (bas, ba_id, email, expires_at, refresh_token), entitlement cache, DPoP key handle, and the install metrics keys. Survives tab close, browser restart, and app updates (D4 session-forever). Key bytes never leave the browser (extractable=false). Pre-0.4.0 sessionStorage session rows migrate to IndexedDB on first read.
  • In-memory — SSR / test fallback.

Host pages can swap in their own ServiceWorker-backed adapter by passing storage into the manager config.

Activation state machine

getActivationState() returns the current state from the canonical machine. Host pages render UI off the name (restoring_session, verifying_entitlement, entitlement_cached_valid, post_checkout_activation, etc.) and the busy flag (whether to show a spinner). CLAUDE.md hard rule feedback_no_etas: never render a predicted ETA — always elapsed time.

DPoP gate transition

  • Gate 1 (today): BAS-authed manager requests keep Authorization: Bearer <bas> for router compatibility and also attach a valid DPoP proof header with ath. The server runs in off mode and accepts the Bearer scheme without verifying the proof.
  • Gate 2 (next): server enforcement can start from real client traffic because getAppDataToken(), openCheckout(), createPaddlePortalSession(), entitlement refreshes, and activation polls already carry proof headers.

Login-first cutover surfaces (0.4.0)

<BravelySignInScreen> (D1)

import { BravelySignInScreen, getOrMintInstallId } from "@bravely-studios/account-web";

<BravelySignInScreen
  appName="Todoing.ly"
  appIconSrc="/icon-256.png"
  valueProp="Every task, every device, always in sync."
  style={{ variant: "dark", accent: "#3B6EF0" }}
  isBusy={isAuthorizing}
  onContinue={(provider) => manager.signIn({ provider })}
  offline={cantReach}
  onRetry={() => retryProbe()}
  onShown={async () => {
    posthog.capture("login_screen_shown", { install_id: await getOrMintInstallId() });
  }}
/>

The first screen on first launch (spec onboarding.login_first). No skip, no guest lane, no sign-in/sign-up fork — the screen IS both. onShown fires once per mount; the host MUST emit login_screen_shown there.

<OfferSlotGrid> + buildOfferSlots() (D3)

import {
  OfferSlotGrid, buildOfferSlots, offerThemeForSlug,
  planTokenForSlot, telemetryValueForSlot,
} from "@bravely-studios/account-web";

const slots = buildOfferSlots({ appName: "Scry", appSlug: "scry", type: "RVA" });

<OfferSlotGrid
  slots={slots} // provisioned slots only — filter before passing
  theme={offerThemeForSlug("scry")}
  onSlotViewed={(s) => posthog.capture("slot_viewed", { offer_slot: telemetryValueForSlot(s) })}
  onSelect={(s) => {
    posthog.capture("slot_selected", { offer_slot: telemetryValueForSlot(s) });
    manager.openCheckout(planTokenForSlot(s, "RVA"));
  }}
/>

The web sibling of Swift OfferSlotGrid / C# BravelyOfferGrid: the locked 3-slot copy (HOOK → GSO → FRONT_LTV) with HEIGHT-FIRST full-viewport scale-to-fit including scale-up (v1.2: the scale fills viewportHeight × 0.92; width binds only as a cap at viewportWidth × 0.95; clamped to [0.5, 3.0]; scroll fallback below 0.5; single-column stack under 901px; NO max-width column cap). In the 3-up layout the cards share row tracks (CSS subgrid) so every section row baseline-aligns to its tallest sibling and the CTAs pin to the card bottom. The host fires paywall_shown when it presents the page.

Install metrics (D6)

import { getOrMintInstallId, emitAppFirstOpenedIfNeeded } from "@bravely-studios/account-web";

// At app entry, before any UI gating:
const { installId } = await emitAppFirstOpenedIfNeeded({
  emit: ({ installId }) => posthog.capture("app_first_opened", { install_id: installId }),
});
manager.setInstallId(installId); // every auth exchange now carries install_id

Persist-first sentinel: the durable IndexedDB sentinel row is written BEFORE the emit, so a crash can only under-count — never double-fire.

Changelog

0.4.1 — 2026-06-11

D3 v1.2 patch (Jeff field feedback 2026-06-11; plan §4):

  • Per-step sizing (a): <BravelySignInScreen> renders COMPACT — the column caps at SIGN_IN_COMPACT_MAX_WIDTH (480px) and self-centers; sign-in never inherits offer geometry. computeOfferFit is now HEIGHT-FIRST: the scale fills viewportHeight × 0.92; width binds only as a cap at viewportWidth × 0.95 (widthCapBound reports when it does). The single-arg signature is unchanged (it IS the offer-step formula); the pad field + OFFER_FIT_PAD are deprecated and ignored — breathing room now comes from the fractions (OFFER_FIT_HEIGHT_FRACTION / OFFER_FIT_WIDTH_CAP_FRACTION).
  • Slot-card baseline alignment (b): in the 3-up layout the cards share the parent's row tracks via CSS subgrid — every section row (name/tagline/price/price-sub/bullets/guarantee/bonus/nudge/CTA) equalizes to its tallest sibling; a missing section (the LTV "/mo", the HOOK bonus, the RVA HOOK guarantee) leaves its track empty instead of pulling content up; the CTA pins to the shared bottom row. Non-subgrid browsers gracefully keep the flex-column card; stacked mode unchanged.
  • Locked copy (c): bonus headlines are now FREE full access to [the 8 other premium utilities] — 9 apps in all (GSO) / FREE [lifetime] access to [the 8 other premium utilities] — 9 apps in all (LTV; accent lifetime span kept); slot-1 nudge is Want the other 8 apps free? Go Annual →. The "all 9 apps, every platform, every device, + the bonus bundle" tagline STAYS (total count is correct). Byte-identical with the Swift/C#/get.bravely.dev surfaces.

0.4.0 — 2026-06-11

Login-first cutover W1 (plan §§2, 4, 5, 7 — D1/D3/D4/D6):

  • Session forever (D4): bas / ba_id / email moved from sessionStorage to IndexedDB (PKCE verifier/state stay session-scoped; legacy rows migrate on first read); expires_at persisted from every token response; the grant_type=refresh_token rotation loop implemented against {authority}/oauth/token (router ≥1.6.0) with single-flight dedupe; pre-expiry background refresh (48h window) on restore(); 401 → refresh-then-retry-once on every BAS-authed call; session resurrection from a durable refresh token when the BAS row is missing; destructive sign-out ONLY on definitive invalid_grant / 401-after-refresh — transport/5xx/429 failures keep every token.
  • <BravelySignInScreen> (D1): the canonical login-first first screen.
  • <OfferSlotGrid> (D3): the locked 3-slot offer grid + copy factory, per-app themes, viewport fit with scale-up.
  • Install metrics (D6): getOrMintInstallId() + emitAppFirstOpenedIfNeeded().
  • Activation machine v1.1.0: user_skipped removed in lockstep with bravely-commerce-router (legacy event strings are a no-op).
  • Types: CheckoutPlan gains "onetime" (interval-true IVA Slot-1 token per the 2026-06-10 catalog rename); API baseline note → 1.6.0.
  • Fix: stale "magic-link only" comment on the email provider button (password is primary per ADR 0014).

0.3.10 — 2026-06-05

  • Feature: optional install_id passthrough for PHASE-2 install→account reconcile. ManagerConfig gains an optional installId; the manager exposes setInstallId() / getInstallId(), and threads the value onto both legs of sign-in — the /oauth/authorize query (persisted to the app-auth code) and the /oauth/token exchange body. The wire field name is install_id. Strictly additive: with no install_id, the authorize URL and token body are byte-identical to prior releases (asserted by an equality test). Server-side persistence/reconcile already live in the commerce router; this release only emits the field.

0.3.9 — 2026-06-03

  • Feature: added an optional host-injected log seam to ManagerConfig ((event, ctx) => void). When supplied, BravelyAccountManager emits SECRET-FREE breadcrumbs (HTTP status + server {error} code + step) at the previously-silent failure paths — signOut revoke, pollForActivation (HTTP + network errors), and refreshEntitlements (401 / non-2xx / network) — so the host's diagnostic ring buffer can capture the real cause. The four prior hardcoded console.info lines (DPoP unavailable, deprecation hints) now route through this seam too. Omitting log is a no-op (no behavior change). The seam is also threaded into EntitlementCache (previously unreachable) so its offline_entitlement_served marker reaches the host sink. Breadcrumbs never carry tokens, passwords, OAuth codes, full emails, or PII. (Logging Wave 4.)

0.3.5 — 2026-05-14

  • Feature: added <BravelyProviderButtons> — the canonical 3-button provider picker (Apple / Google / Email) used across the Bravely web family. Mirrors the SwiftUI surface in bravely-account-swift so the iOS/Mac/Web variants of the same utility share visual treatment. Pure React + inline SVGs; no runtime dependency, no CSS file.
  • Types: exported BravelyProviderButtonsProps, BravelyProviderButtonStyle, and ProviderHint.

0.3.4 — 2026-05-13

  • Infra: package now publishes to registry.npmjs.org (public scope). Previously hosted on GitHub Packages. No code changes; behavior is identical to 0.3.3.

0.3.3 — 2026-05-13

  • Feature: BravelyAccountManager enters fresh_launch_restoration when a BAS exists but the entitlement cache is missing, smoothing the cold-start UX before the first entitlement read completes.

0.3.2 — 2026-05-13

  • Fix: BAS-authenticated manager requests now attach an RFC 9449 DPoP proof header, including ath, while preserving the Gate 1 Authorization: Bearer <bas> scheme required by current router endpoints.
  • Fix: fresh entitlement cache writes carry the generated DPoP JKT thumbprint so Gate 2 cache binding can inspect the local key identity.

0.3.0 — 2026-05-13

  • Feature: added createPaddlePortalSession(), a BAS-authed manager primitive for POST /api/paddle-portal. It returns the hosted Paddle customer-portal URL DTO and leaves checkout activation behavior unchanged.
  • Types: exported PaddlePortalSession and aligned the API baseline note to bravely-commerce-router OpenAPI 1.2.1.

0.2.1 — 2026-05-12

  • Fix: getState() now returns a stable reference between writes. Previously it cloned on every read, which broke React 19's useSyncExternalStore (snapshot identity changed every render → React error #185 / "Maximum update depth exceeded"). The clone now happens once, inside setState(), before listeners fire. Listener payloads and the next getState() call return the same object reference. Three consumers (prodjectly, printscreenly-web, todoingly-web) carry module-level _cachedState workarounds that become redundant with this release — they can be dropped in a follow-up sweep.
  • Fix: internal libVersion default aligned to package version (was hard-coded to "0.2.0").
  • Docs: install snippet uses the correct @bravely-studios scope.

0.2.0 — 2026-05-11

  • Initial C-ux M2/M3/M4 facade shipped.

License

Proprietary. (c) 2026 Bravely Studios LLC.