@crosschq/interview-report-widget
v0.5.0
Published
Crosschq Interview Report widget — self-contained, embeddable report renderer for partner integrations (push-mode first: you fetch the report JSON server-side and hand it to render()).
Readme
@crosschq/interview-report-widget
Self-contained, embeddable renderer for the Crosschq Interview Report. Drop it into any web app (React, Vue, plain HTML) and render a full interview report — both report versions plus an optional Video & Transcript tab — inside your own UI.
- Self-contained: Vue runtime and every dependency are bundled. No peer
dependencies, no runtime
dependenciesin this package at all. - Framework-agnostic: ESM for bundlers, UMD for
<script src>. - Push-mode first: you fetch the report JSON server-side with your
partner credentials and hand it to
render()— the widget makes zero network calls of its own.
Install
npm install @crosschq/interview-report-widgetQuick start (push mode, recommended)
import { render } from '@crosschq/interview-report-widget'
import '@crosschq/interview-report-widget/style.css'
// Fetch this server-side with your Crosschq partner API key:
// GET /interviews/{id}/report/
const reportData = await fetchReportFromYourBackend(interviewId)
const handle = render({
interviewID: interviewId,
target: document.querySelector('#crosschq-report'),
reportData,
// optional: transcriptionData enables the "Video & Transcript" tab
})
// Later, on unmount:
handle.destroy()React example, TypeScript types, UMD usage, pull mode, and the payload
contract are documented in dist/INTEGRATION.md
(shipped inside this package).
Security model
This package is designed so that no credential ever ships in, or is persisted by, the widget:
- No secrets in the bundle. The published artifact contains no API keys, no Crosschq hostnames, no telemetry/analytics, and no environment-variable references. CI enforces this on every release (tarball allowlist audit + bundle content scan).
- Push mode = zero network. The widget renders the JSON you pass in and initiates no requests. Credentials stay on your server.
- Pull mode is advanced and proxy-only. If your host cannot fetch
server-side, the widget can fetch through your proxy using a
browser-safe
bearerTokenyou supply atrender()time. That token is held in memory only — never written tolocalStorage, cookies, or anywhere else — and must never be a Crosschq organization secret key. Your proxy validates it and re-authenticates to Crosschq server-side. - Untrusted-input rendering. Payload-derived HTML is sanitized with DOMPurify before it reaches the DOM.
- Supply chain. Zero runtime dependencies for consumers. Releases are published only from CI, by a tag-triggered pipeline authenticated with npm trusted publishing (OIDC) — no long-lived npm token exists. The tarball carries no provenance attestation: npm accepts those only from public source repositories, and the Crosschq source repository is private.
CSS isolation
The stylesheet is fully scoped: every rule (Tailwind preflight,
utilities, theme CSS variables) applies only under .crosschq-widget (the
element you render into) or .crosschq-widget-portal (a body-level host
the widget creates for tooltips/sheets/dialogs and removes on destroy()).
Including style.css cannot restyle your page, and no CSS variables are
defined on :root.
CSP
In push mode the widget needs nothing allowlisted that points at Crosschq:
scripts, styles, and icons are bundled; no fonts are shipped (it uses Geist
if your page already loads it, else system fonts). The only external origins
are URLs inside the payload you fetch yourself (candidate photo, reviewer
avatars via img-src; recording host via media-src if you enable the
Video & Transcript tab — that host also needs permissive CORS for the audio
waveform). Details in dist/INTEGRATION.md.
Versioning
Semantic versioning. While the package is pre-1.0, minor versions may adjust
the render() contract; 1.0.0 locks it. The running version is exported
as version (and window.CrosschqWidget.version in UMD).
Support
Integration questions / API changes: [email protected]
