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

@trueyy-sdk/web

v1.0.1

Published

React components for embedding Trueyy interview monitoring.

Readme

@trueyy-sdk/web

React components for embedding Trueyy's live interview integrity monitoring into your ATS — drop-in, themable, fully typed.

npm types

The Trueyy SDK is two packages — you'll use both for a full integration: @trueyy-sdk/web (React frontend, this one) embeds the live monitoring UI, and @trueyy-sdk/node (backend) mints the tokens it needs + handles webhooks.

This package is for your frontend (React 17+). It gives you three drop-in components plus a typed hook surface so you can build a custom UI on top of Trueyy's live data plane.

For your backend (where session tokens are minted and webhooks are received), install @trueyy-sdk/node.


Install

npm install @trueyy-sdk/web
# or
pnpm add @trueyy-sdk/web
# or
yarn add @trueyy-sdk/web

Then import the default theme stylesheet once (typically in your app entry):

import "@trueyy-sdk/web/styles.css";

If you prefer full control, skip the stylesheet and style the components yourself — every component accepts className and theme tokens are CSS variables.


The components

| Component | Side | Purpose | |---|---|---| | <TrueyyProvider> | Both | Holds the session JWT, opens the WebSocket, refreshes tokens before expiry. Mount once per page. | | <TrueyyMonitor> | Interviewer | Live panel: risk pulses, 30-second window scores, transcript stream, "capture now" button. | | <TrueyyJoin> | Candidate | 3-step pre-join: install Helper → grant permissions → open meeting. | | <TrueyyReplay> | Interviewer | Post-hoc session review: scrubbable transcript, risk timeline, screenshots, final report. |

You almost always wrap content in <TrueyyProvider> and put one of the other three inside.


60-second quick start

Interviewer's live monitoring page

import { TrueyyProvider, TrueyyMonitor } from "@trueyy-sdk/web";
import "@trueyy-sdk/web/styles.css";

function InterviewerPage({ interviewerToken }) {
  return (
    <TrueyyProvider
      token={interviewerToken}
      theme={{ primary: "#3B82F6", radius: "8px" }}
      onTokenExpiring={async () => {
        // Hit YOUR backend, which calls trueyy.tokens.mint(round_id, role) via @trueyy-sdk/node
        const res = await fetch("/our-ats/refresh-trueyy-token");
        return res.text();
      }}
    >
      <TrueyyMonitor
        onRiskAlert={(e) => acmeAnalytics.track("trueyy_risk", e)}
      />
    </TrueyyProvider>
  );
}

Candidate's pre-join page

import { TrueyyProvider, TrueyyJoin } from "@trueyy-sdk/web";

function CandidateJoinPage({ candidateToken, helperToken, zoomUrl }) {
  // The Helper needs its own token. Inject it on window so <TrueyyJoin> can
  // hand it to the local Helper. (A future API will accept it as a prop.)
  (window as any).__TRUEYY_HELPER_TOKEN__ = helperToken;

  return (
    <TrueyyProvider token={candidateToken}>
      <TrueyyJoin
        meetingUrl={zoomUrl}
        cortexUrl="https://api.trueyy.com"
        onReady={() => console.log("Candidate joined")}
      />
    </TrueyyProvider>
  );
}

Recruiter reviewing a finished interview

import { TrueyyReplay } from "@trueyy-sdk/web";

function ReviewPage({ sessionId }) {
  return (
    <TrueyyReplay
      sessionId={sessionId}
      fetchSessionDetail={async (id) => {
        // YOUR backend, which calls trueyy.reports.get(round_id) via @trueyy-sdk/node
        const res = await fetch(`/our-ats/sessions/${id}/replay`);
        return res.json();
      }}
    />
  );
}

Replay does not open a live socket — it reads from your aggregated copy.


How tokens flow (the architecture in one diagram)

                                                ┌────────────────────┐
                                                │  Trueyy Cloud      │
                                                │  (api.trueyy.com)  │
                                                └─────────┬──────────┘
                                                          │
   ┌────────────┐    POST /our-ats/schedule    ┌──────────┴──────────┐
   │  Your      │ ─────────────────────────────►  Your ATS Backend   │
   │  frontend  │                              │  + @trueyy-sdk/node     │
   │  + @trueyy-sdk/│ ◄──── candidate_token  ──────│  (holds tk_live_*)  │
   │   web      │       interviewer_token      └─────────┬───────────┘
   └────┬───────┘       helper_token                     │
        │                                                │ POST /v1/sessions
        │                                                │ (returns 3 JWTs)
        │  <TrueyyProvider token={…}>                    ▼
        │  opens WSS to Trueyy Cloud                Trueyy Cloud mints JWTs (5min)
        │                                                │
        └───── WSS auth: Bearer <session-JWT> ───────────┘

Your backend never sends the master tk_live_* to the browser — it mints short-lived (5-min) session JWTs and hands those to the frontend. Damage radius of a leaked browser JWT: one session, five minutes.


<TrueyyProvider> reference

<TrueyyProvider
  token={sessionJwt}                       // REQUIRED — 5-min JWT
  role="candidate" | "interviewer" | "helper"   // OPTIONAL — inferred from JWT aud
  baseUrl="https://api.trueyy.com"         // OPTIONAL — defaults to prod
  theme={{ primary, radius }}              // OPTIONAL — CSS-var overrides
  onTokenExpiring={async () => string}     // OPTIONAL but recommended
>
  {children}
</TrueyyProvider>

theme sets CSS variables on the root element:

| Prop | CSS variable | Default | |---|---|---| | primary | --trueyy-primary | #3B82F6 | | radius | --trueyy-radius | 8px |

You can also override --trueyy-bg, --trueyy-text, --trueyy-muted, --trueyy-border directly in your CSS.


<TrueyyMonitor> reference

<TrueyyMonitor
  onRiskAlert={(event) => void}            // fires on every risk-pulse
  onConsentChange={(event) => void}        // fires on consent given/declined/revoked
/>

Renders three columns:

  1. Risk pulses — incoming alerts color-coded by severity.
  2. Window analysis — every 30 seconds, the LLM-synthesized risk + score + one-line summary.
  3. Transcript — live transcript, two-speaker.

A consent banner appears above the columns when the candidate withdraws (or declines) consent — capture has already stopped server-side at that point. onConsentChange also forwards every transition to your app.

Plus a "Capture now" button that triggers an on-demand screenshot capture pipeline.

The component is intentionally simple. For a custom UI, drop the component and use the hooks instead.


<TrueyyJoin> reference

<TrueyyJoin
  meetingUrl={string}                      // REQUIRED — Zoom / Meet / Teams URL
  cortexUrl="https://api.trueyy.com"       // OPTIONAL
  onReady={() => void}                     // OPTIONAL — fires after meeting opens
  renderConsent={(c) => ReactNode}         // OPTIONAL — replace the built-in consent UI
  consentHandledExternally={boolean}       // OPTIONAL — you own the consent flow (see below)
  onConsentChange={(status) => void}       // OPTIONAL — 'needed'|'given'|'declined'|'revoked'
/>

Consent first, then 3 steps:

  1. Consent (GDPR Art. 7) — before anything else, the candidate is shown the monitoring consent text and must agree. Capture is hard-gated server-side: no candidate data is captured for a session until consent is granted, so this is a compliance requirement, not just UI.
  2. Install the helper — auto-detects presence; offers per-OS download if missing.
  3. Connect helper — hands the Helper its token so it can start on the candidate's machine.
  4. Open meeting — opens meetingUrl in a new tab and signals that the candidate has joined.

A "Withdraw monitoring consent" control is shown throughout the session (GDPR Art. 7(3)). Withdrawal notifies the interviewer immediately and stops capture; the candidate can re-consent to resume.

Candidate consent

The default consent UI is compliance-safe out of the box — do nothing and your candidates get a valid, versioned, provable consent flow. Two escape hatches:

  • renderConsent — supply your own UI while keeping the logic. You receive the useConsent() state (status, text, agree, decline, revoke, busy).
  • consentHandledExternally — your ATS collects consent itself and calls client.consent.grant(version) directly. This only hides the built-in UI; Trueyy Cloud still blocks capture until consent is granted, so you accept the compliance responsibility for showing informed consent before granting.

For a fully custom flow, use the hook directly:

import { useConsent } from "@trueyy-sdk/web";

function MyConsent() {
  const { status, text, agree, decline, revoke, busy } = useConsent();
  // status: 'loading' | 'needed' | 'given' | 'declined' | 'revoked'
}

Or the raw client methods (framework-agnostic, @trueyy-sdk/web-core): client.consent.text(), .grant(version), .decline(), .revoke().


<TrueyyReplay> reference

<TrueyyReplay
  sessionId={string}
  fetchSessionDetail={async (id) => TrueyySessionDetail}
/>

The fetchSessionDetail callback is your call — typically a fetch to your own backend that aggregates Trueyy Cloud data + your own stored webhook history.

Expected shape:

interface TrueyySessionDetail {
  session_id: string;
  title: string;
  status: string;
  transcripts: { speaker, text, captured_at }[];
  risk_pulses: { kind, severity, occurred_at }[];
  windows: { risk, score, summary, start_time }[];
}

Hooks (for custom UIs)

If the built-in components don't fit your design, build your own using the same data plane:

import {
  useTrueyyClient,
  useRiskStream,
  useWindowResults,
  useTranscriptStream,
} from "@trueyy-sdk/web";

function MyCustomMonitor() {
  const client = useTrueyyClient();
  const risks = useRiskStream(50);          // last 50 risk pulses
  const windows = useWindowResults(50);     // last 50 windows
  const transcript = useTranscriptStream(200);

  return (
    <div>
      <button onClick={() => client.captureNow()}>Capture</button>
      {risks.map((r, i) => <MyRiskCard key={i} event={r} />)}
      {/* ... */}
    </div>
  );
}

All hooks return reactive state that updates as new events stream in. Unsubscribe happens automatically on unmount.


TypeScript

Everything is typed. Useful exports:

import type {
  // Component props
  TrueyyProviderProps,
  TrueyyMonitorProps,
  TrueyyJoinProps,
  TrueyyReplayProps,
  TrueyyTheme,
  TrueyySessionDetail,
  // Event payloads
  SessionRole,
  RiskPulseEvent,
  WindowResultEvent,
  LiveTranscriptEvent,
  CandidateStatusEvent,
} from "@trueyy-sdk/web";

Examples

A full React app embedding the SDK lives in this monorepo:

Clone and run:

git clone https://github.com/Anti-Cheating/trueyy-sdk.git
cd trueyy-sdk
pnpm install
pnpm -F react-ats dev
# Open http://localhost:5173/?interviewer_token=<paste a real JWT>

Browser support

Targets evergreen browsers — Chrome, Edge, Firefox, Safari (last 2 versions each). Uses fetch, WebSocket, AbortController, BroadcastChannel.


What about Vue / Angular / Svelte?

The data plane lives in @trueyy-sdk/web-core and is framework-agnostic. We'd love community-maintained adapters — see the web-core README for the wrapping pattern.


License

MIT © Trueyy

Issues, PRs, and discussions: github.com/Anti-Cheating/trueyy-sdk