@repros/sdk 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -434,7 +434,10 @@ function mountToolbar(session2, options) {
434
434
  function ensureElapsedTimer() {
435
435
  if (elapsedTimer) return;
436
436
  elapsedTimer = setInterval(() => {
437
- if (state.kind === "active" && !state.minimized) render();
437
+ if (state.kind !== "active") return;
438
+ const selector = state.minimized ? ".rp-pill .rp-mono" : ".rp-elapsed";
439
+ const el = root.querySelector(selector);
440
+ if (el) el.textContent = formatElapsed(Date.now() - startedAt);
438
441
  }, 1e3);
439
442
  }
440
443
  async function handleSubmit() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/claim.ts","../../annotation-toolkit/src/select.ts","../../annotation-toolkit/src/draw.ts","../../annotation-toolkit/src/composite.ts","../src/capture/screenshot.ts","../src/toolbar/styles.ts","../src/toolbar/submit.ts","../src/toolbar/annotation.ts","../src/toolbar/toolbar.ts","../src/capture/buffer.ts","../src/capture/serialize.ts","../src/capture/console.ts","../src/capture/errors.ts","../src/capture/network.ts","../src/capture/index.ts"],"sourcesContent":["import { claim, readClaimToken, type ReprosSession } from \"./claim\";\nimport { mountToolbar } from \"./toolbar/toolbar\";\nimport { startCapture } from \"./capture\";\n\nexport type { ReprosSession };\nexport type { ToolbarMode, MountToolbarOptions, ToolbarCounts } from \"./toolbar/toolbar\";\nexport { mountToolbar } from \"./toolbar/toolbar\";\nexport { captureScreenshot } from \"./capture\";\n\nexport interface InitOptions {\n /** Override for local development or self-hosted testing. Defaults to the production Repros app. */\n apiBase?: string;\n}\n\nlet session: ReprosSession | null = null;\nlet initStarted = false;\n\n/**\n * Entry point for orgs with their own build step:\n *\n * import { init } from \"@repros/sdk\";\n * init();\n *\n * Strict no-op by default: if the page wasn't opened via a Repros\n * customer-session link (no `repros_claim` query param), this does\n * nothing at all — no network call, no UI, nothing mounted. That's the\n * whole point of the claim step existing: the SDK can sit installed on\n * every page of an org's app, permanently, and stay invisible until a\n * customer arrives through an actual one-time link.\n *\n * On a successful claim this also mounts the customer toolbar (consent\n * screen, then a minimal recording panel with a \"Send report\" action).\n * Console/window-error/network capture only starts once the customer\n * actually accepts that consent screen — never before, and it stops again\n * the moment the report is sent. The note button is still a placeholder —\n * the annotation toolkit (a separate card) is what wires it up.\n */\nexport async function init(options: InitOptions = {}): Promise<ReprosSession | null> {\n if (initStarted) return session;\n initStarted = true;\n\n const token = readClaimToken();\n if (!token) return null;\n\n session = await claim(token, options.apiBase);\n if (!session) return null;\n const activeSession = session; // local const so the closures below don't depend on the mutable module-level `session`\n\n let stopCapture: (() => void) | null = null;\n const toolbar = mountToolbar(activeSession, {\n mode: \"customer\",\n onAccepted: () => {\n stopCapture = startCapture(activeSession, (counts) => toolbar.setCounts(counts));\n },\n onSubmitted: () => stopCapture?.(),\n });\n\n return session;\n}\n\nexport function getSession(): ReprosSession | null {\n return session;\n}\n","// www. explicitly — repros.dev 308-redirects to www.repros.dev, and a\n// redirect turns a simple cross-origin POST into a preflighted one for no\n// reason (same fix as apps/dashboard/app/(authed)/account/ApiTokens.tsx).\nconst DEFAULT_API_BASE = \"https://www.repros.dev\";\n\nconst QUERY_PARAM = \"repros_claim\";\n\nexport interface ReprosSession {\n sessionId: string;\n /** The claim token, kept in memory only — the toolbar's submit call is keyed on it, same as claim. */\n token: string;\n apiBase: string;\n}\n\ninterface ClaimResponse {\n ok: boolean;\n sessionId?: string;\n reason?: string;\n}\n\n/** Reads and strips the claim token from the current URL, if present. */\nexport function readClaimToken(): string | null {\n if (typeof window === \"undefined\") return null;\n const url = new URL(window.location.href);\n const token = url.searchParams.get(QUERY_PARAM);\n if (!token) return null;\n\n // The token is single-purpose and shouldn't linger in browser history or\n // leak via Referer once we've read it — same care as the claim landing\n // page's own no-referrer metadata.\n url.searchParams.delete(QUERY_PARAM);\n window.history.replaceState(window.history.state, \"\", url.toString());\n\n return token;\n}\n\n/**\n * Calls the claim endpoint. Never throws — a bad or expired token, a\n * network blip, or the endpoint being unreachable should all just mean\n * \"no session\", not a broken page for the customer.\n */\nexport async function claim(token: string, apiBase = DEFAULT_API_BASE): Promise<ReprosSession | null> {\n try {\n const res = await fetch(`${apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/claim`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token }),\n });\n const data = (await res.json().catch(() => null)) as ClaimResponse | null;\n if (!res.ok || !data?.ok || !data.sessionId) {\n console.warn(\"[Repros SDK] couldn't claim report link:\", data?.reason ?? res.status);\n return null;\n }\n return { sessionId: data.sessionId, token, apiBase };\n } catch (err) {\n console.warn(\"[Repros SDK] couldn't reach Repros to claim report link:\", err);\n return null;\n }\n}\n","import type { AnnotationTarget } from \"./types\";\n\n/**\n * DevTools-inspector-style element picking: hover highlights whatever's\n * under the cursor, click locks it in. Listens on the capture phase and\n * preventDefault/stopPropagation on the click so the host page's own click\n * handlers never fire — picking an element must not also activate it (a\n * button, a link).\n *\n * Returns a cancel function; also self-cancels on Escape or a successful\n * pick. Callers are responsible for hiding their own UI (e.g. the toolbar\n * panel) before calling this, so it doesn't get highlighted/picked itself.\n */\nexport function startElementSelect(onPick: (target: AnnotationTarget) => void, onCancel: () => void): () => void {\n const highlight = document.createElement(\"div\");\n highlight.style.cssText =\n \"position:fixed;pointer-events:none;z-index:2147483645;border:2px solid #7c3aed;\" +\n \"background:rgba(124,58,237,0.15);border-radius:2px;display:none;box-sizing:border-box;\";\n document.body.appendChild(highlight);\n\n let lastEl: Element | null = null;\n\n function updateHighlight(el: Element) {\n if (el === lastEl) return;\n lastEl = el;\n const r = el.getBoundingClientRect();\n Object.assign(highlight.style, {\n display: \"block\",\n left: `${r.left}px`,\n top: `${r.top}px`,\n width: `${r.width}px`,\n height: `${r.height}px`,\n });\n }\n\n function onMouseMove(e: MouseEvent) {\n const el = document.elementFromPoint(e.clientX, e.clientY);\n if (el && el !== highlight) updateHighlight(el);\n }\n\n function onClick(e: MouseEvent) {\n e.preventDefault();\n e.stopPropagation();\n const el = lastEl;\n cleanup();\n if (!el) {\n onCancel();\n return;\n }\n const r = el.getBoundingClientRect();\n onPick({ rect: { x: r.left + window.scrollX, y: r.top + window.scrollY, width: r.width, height: r.height } });\n }\n\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === \"Escape\") {\n cleanup();\n onCancel();\n }\n }\n\n function cleanup() {\n window.removeEventListener(\"mousemove\", onMouseMove, true);\n window.removeEventListener(\"click\", onClick, true);\n window.removeEventListener(\"keydown\", onKeyDown, true);\n highlight.remove();\n }\n\n window.addEventListener(\"mousemove\", onMouseMove, true);\n window.addEventListener(\"click\", onClick, true);\n window.addEventListener(\"keydown\", onKeyDown, true);\n\n return cleanup;\n}\n","import type { AnnotationTarget } from \"./types\";\n\nconst MIN_DRAG_PX = 4;\n\n/**\n * Free-draw: drag a rectangle anywhere on the page. A drag shorter than\n * MIN_DRAG_PX in either dimension is treated as a mis-click, not a\n * zero-size annotation, and cancels rather than picking.\n */\nexport function startFreeDraw(onPick: (target: AnnotationTarget) => void, onCancel: () => void): () => void {\n const overlay = document.createElement(\"div\");\n overlay.style.cssText = \"position:fixed;inset:0;z-index:2147483645;cursor:crosshair;background:rgba(0,0,0,0.01);\";\n\n const box = document.createElement(\"div\");\n box.style.cssText =\n \"position:fixed;pointer-events:none;z-index:2147483645;border:2px solid #7c3aed;\" +\n \"background:rgba(124,58,237,0.15);display:none;box-sizing:border-box;\";\n\n document.body.appendChild(overlay);\n document.body.appendChild(box);\n\n let dragging = false;\n let startX = 0;\n let startY = 0;\n\n function viewportRect(curX: number, curY: number) {\n return {\n x: Math.min(startX, curX),\n y: Math.min(startY, curY),\n width: Math.abs(curX - startX),\n height: Math.abs(curY - startY),\n };\n }\n\n function onDown(e: MouseEvent) {\n dragging = true;\n startX = e.clientX;\n startY = e.clientY;\n box.style.display = \"block\";\n paint(viewportRect(e.clientX, e.clientY));\n }\n\n function paint(r: { x: number; y: number; width: number; height: number }) {\n Object.assign(box.style, { left: `${r.x}px`, top: `${r.y}px`, width: `${r.width}px`, height: `${r.height}px` });\n }\n\n function onMove(e: MouseEvent) {\n if (dragging) paint(viewportRect(e.clientX, e.clientY));\n }\n\n function onUp(e: MouseEvent) {\n if (!dragging) return;\n dragging = false;\n const r = viewportRect(e.clientX, e.clientY);\n cleanup();\n if (r.width < MIN_DRAG_PX || r.height < MIN_DRAG_PX) {\n onCancel();\n return;\n }\n onPick({ rect: { x: r.x + window.scrollX, y: r.y + window.scrollY, width: r.width, height: r.height } });\n }\n\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === \"Escape\") {\n cleanup();\n onCancel();\n }\n }\n\n function cleanup() {\n overlay.removeEventListener(\"mousedown\", onDown);\n window.removeEventListener(\"mousemove\", onMove);\n window.removeEventListener(\"mouseup\", onUp);\n window.removeEventListener(\"keydown\", onKeyDown, true);\n overlay.remove();\n box.remove();\n }\n\n overlay.addEventListener(\"mousedown\", onDown);\n window.addEventListener(\"mousemove\", onMove);\n window.addEventListener(\"mouseup\", onUp);\n window.addEventListener(\"keydown\", onKeyDown, true);\n\n return cleanup;\n}\n","import type { AnnotationRect } from \"./types\";\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(\"Failed to load screenshot for compositing\"));\n img.src = src;\n });\n}\n\n/**\n * Burns the annotation box onto the screenshot at the moment it's made —\n * see the SDK PRD's \"annotations persist as composited screenshots, not\n * DOM references\" decision. `rect` is document-relative (see\n * AnnotationRect). `capturedSize` must be the exact width/height the\n * screenshot tool measured at capture time (captureScreenshot()'s own\n * `width`/`height`, not document.documentElement.scrollWidth/scrollHeight\n * — those can disagree substantially on a page shorter than the viewport,\n * since scrollHeight pads up to the viewport while the actual rendered/\n * captured box doesn't).\n */\nexport async function compositeAnnotation(\n screenshotDataUrl: string,\n rect: AnnotationRect,\n capturedSize: { width: number; height: number },\n): Promise<string> {\n const img = await loadImage(screenshotDataUrl);\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"2d canvas context unavailable\");\n\n ctx.drawImage(img, 0, 0);\n\n const scaleX = img.width / (capturedSize.width || img.width);\n const scaleY = img.height / (capturedSize.height || img.height);\n const x = rect.x * scaleX;\n const y = rect.y * scaleY;\n const width = rect.width * scaleX;\n const height = rect.height * scaleY;\n\n ctx.fillStyle = \"rgba(124, 58, 237, 0.15)\";\n ctx.fillRect(x, y, width, height);\n ctx.lineWidth = 3;\n ctx.strokeStyle = \"#7c3aed\";\n ctx.strokeRect(x, y, width, height);\n\n return canvas.toDataURL(\"image/png\");\n}\n","import { domToPng } from \"modern-screenshot\";\n\nexport interface Screenshot {\n dataUrl: string;\n /**\n * CSS-pixel size of document.documentElement's own render box at the\n * instant of capture — NOT scrollWidth/scrollHeight, which on some pages\n * (short content, no scrollbar) can differ substantially from what\n * getBoundingClientRect() reports and from what domToPng actually\n * rendered. Composited annotations must scale against these exact\n * numbers, measured atomically with the capture itself, or the box lands\n * in the wrong place.\n */\n width: number;\n height: number;\n}\n\n/**\n * DOM-snapshot screenshot capture — decided over getDisplayMedia (see the\n * Repros SDK PRD's Architecture Decisions): no extra permission prompt, and\n * works on iOS Safari where getDisplayMedia doesn't exist at all. Real\n * fidelity gaps versus a true screen capture (canvas/WebGL renders blank,\n * some cross-origin images get blocked) — a known, documented limitation,\n * not a bug.\n *\n * Returns null rather than throwing on failure: a failed screenshot\n * shouldn't block a note or a report submission, it should just mean no\n * image this time.\n */\nexport async function captureScreenshot(): Promise<Screenshot | null> {\n try {\n const rect = document.documentElement.getBoundingClientRect();\n const dataUrl = await domToPng(document.documentElement, {\n backgroundColor: \"#ffffff\",\n quality: 0.92,\n filter: (node) => !(node instanceof HTMLElement && node.hasAttribute(\"data-repros-toolbar\")),\n });\n return { dataUrl, width: rect.width, height: rect.height };\n } catch (err) {\n console.warn(\"[Repros SDK] screenshot capture failed:\", err);\n return null;\n }\n}\n","// Same palette and rp- class namespace as the extension's on-page toolbar\n// (apps/extension/src/content/Toolbar/toolbar.css) so a customer or tester\n// sees one consistent brand regardless of which transport put it there.\n// Injected into a shadow root, so these names can never collide with the\n// host page's own CSS — `all: initial` on :host and .rp-root stops the\n// host page's inherited styles leaking in, same reasoning as the extension.\nexport const TOOLBAR_STYLES = `\n:host { all: initial; }\n\n.rp-root {\n all: initial;\n --rp-surface: #161b1f;\n --rp-surface-2: #1f262b;\n --rp-surface-3: #262e34;\n --rp-border: #2c343b;\n --rp-border-strong: #3a4249;\n --rp-ink: #edefe9;\n --rp-ink-2: #c4cbc3;\n --rp-ink-3: #8d958e;\n --rp-accent: #7c3aed;\n --rp-accent-hover: #6d28d9;\n --rp-accent-soft: #a78bfa;\n --rp-error: #f07a5f;\n --rp-pass: #4fbe7c;\n --rp-note: #c4b5fd;\n font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-size: 13px;\n line-height: 1.4;\n color: var(--rp-ink);\n -webkit-font-smoothing: antialiased;\n}\n\n.rp-root *, .rp-root *::before, .rp-root *::after { box-sizing: border-box; }\n\n.rp-panel, .rp-pill, .rp-consent {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 2147483647;\n}\n\n.rp-panel, .rp-consent {\n width: 300px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px;\n background: var(--rp-surface);\n border: 1px solid var(--rp-border);\n border-radius: 14px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.25);\n animation: rp-in 160ms ease-out;\n}\n\n@keyframes rp-in {\n from { opacity: 0; transform: translateY(6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rp-dot, .rp-panel, .rp-consent { animation: none; }\n}\n\n.rp-mono { font-family: ui-monospace, \"SF Mono\", Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }\n\n.rp-header { display: flex; align-items: center; justify-content: space-between; }\n.rp-header-main { display: flex; align-items: center; gap: 8px; }\n\n.rp-status {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 2px 8px; border-radius: 999px;\n font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em;\n background: rgba(124, 58, 237, 0.18); color: var(--rp-accent-soft);\n}\n\n.rp-elapsed { font-size: 12px; color: var(--rp-ink-3); }\n\n.rp-dot {\n width: 7px; height: 7px; border-radius: 50%; background: var(--rp-error);\n animation: rp-pulse 1.8s ease-in-out infinite;\n}\n\n@keyframes rp-pulse {\n 0% { box-shadow: 0 0 0 0 rgba(240, 122, 95, 0.55); }\n 70% { box-shadow: 0 0 0 6px rgba(240, 122, 95, 0); }\n 100% { box-shadow: 0 0 0 0 rgba(240, 122, 95, 0); }\n}\n\n.rp-title { font-size: 14px; font-weight: 650; color: var(--rp-ink); }\n.rp-body { font-size: 12.5px; color: var(--rp-ink-2); }\n\n.rp-counts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }\n.rp-count {\n display: flex; flex-direction: column; align-items: flex-start;\n padding: 5px 7px; border-radius: 8px; background: var(--rp-surface-2);\n color: var(--rp-ink-3); font-size: 10px; line-height: 1.2; white-space: nowrap;\n}\n.rp-count-value { font-size: 15px; font-weight: 650; font-variant-numeric: tabular-nums; color: var(--rp-ink-2); }\n.rp-count-error .rp-count-value { color: var(--rp-error); }\n.rp-count-note .rp-count-value { color: var(--rp-note); }\n\n.rp-actions { display: flex; gap: 6px; }\n\n.rp-btn, .rp-icon-btn, .rp-pill {\n font: inherit; color: inherit; cursor: pointer; border: none; background: none;\n}\n.rp-btn {\n display: inline-flex; align-items: center; justify-content: center; gap: 5px;\n flex: 1 1 auto; padding: 7px 10px; border-radius: 8px;\n font-size: 12.5px; font-weight: 600; white-space: nowrap;\n transition: background-color 120ms ease, border-color 120ms ease, opacity 120ms ease;\n}\n.rp-btn:disabled { opacity: 0.45; cursor: default; }\n.rp-btn:focus-visible, .rp-icon-btn:focus-visible, .rp-pill:focus-visible {\n outline: 2px solid var(--rp-accent-soft); outline-offset: 2px;\n}\n\n.rp-btn-primary { background: var(--rp-accent); color: #fff; }\n.rp-btn-primary:hover:not(:disabled) { background: var(--rp-accent-hover); }\n\n.rp-btn-secondary { background: var(--rp-surface-2); border: 1px solid var(--rp-border); color: var(--rp-ink-2); }\n.rp-btn-secondary:hover:not(:disabled) { background: var(--rp-surface-3); border-color: var(--rp-border-strong); }\n\n.rp-btn-stop {\n background: rgba(240, 122, 95, 0.14); border: 1px solid rgba(240, 122, 95, 0.35); color: var(--rp-error);\n}\n.rp-btn-stop:hover:not(:disabled) { background: rgba(240, 122, 95, 0.22); }\n\n.rp-icon-btn {\n display: inline-flex; align-items: center; justify-content: center;\n width: 24px; height: 24px; border-radius: 6px; color: var(--rp-ink-3);\n}\n.rp-icon-btn:hover { background: var(--rp-surface-2); color: var(--rp-ink); }\n\n.rp-pill {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 7px 12px; border-radius: 999px;\n background: var(--rp-surface); border: 1px solid var(--rp-border);\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3); font-size: 12px; color: var(--rp-ink-2);\n}\n.rp-pill:hover { border-color: var(--rp-border-strong); }\n.rp-pill-count { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--rp-note); }\n.rp-pill-count.rp-tone-error { color: var(--rp-error); }\n\n.rp-error { font-size: 11.5px; color: var(--rp-error); margin: 0; }\n.rp-fine-print { font-size: 10.5px; color: var(--rp-ink-3); margin: 0; }\n`;\n","export type SubmitOutcome = \"ok\" | \"already_submitted\" | \"error\";\n\n/** Ends the session server-side. Never throws — same \"fail quiet\" stance as claim(). */\nexport async function submitSession(token: string, apiBase: string): Promise<SubmitOutcome> {\n try {\n const res = await fetch(`${apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/submit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token }),\n });\n const data = (await res.json().catch(() => null)) as { ok?: boolean; reason?: string } | null;\n if (data?.ok) return \"ok\";\n if (data?.reason === \"already_submitted\") return \"already_submitted\";\n console.warn(\"[Repros SDK] couldn't send report:\", data?.reason ?? res.status);\n return \"error\";\n } catch (err) {\n console.warn(\"[Repros SDK] couldn't reach Repros to send report:\", err);\n return \"error\";\n }\n}\n","export type AnnotationOutcome = \"ok\" | \"error\";\n\n/** Never throws — same \"fail quiet\" stance as claim/submit. */\nexport async function submitAnnotation(token: string, apiBase: string, note: string, screenshot: string): Promise<AnnotationOutcome> {\n try {\n const res = await fetch(`${apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/annotation`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token, note, screenshot }),\n });\n const data = (await res.json().catch(() => null)) as { ok?: boolean; reason?: string } | null;\n if (data?.ok) return \"ok\";\n console.warn(\"[Repros SDK] couldn't save that note:\", data?.reason ?? res.status);\n return \"error\";\n } catch (err) {\n console.warn(\"[Repros SDK] couldn't reach Repros to save that note:\", err);\n return \"error\";\n }\n}\n","import { startElementSelect, startFreeDraw, compositeAnnotation, type AnnotationRect } from \"@test-tracker/annotation-toolkit\";\nimport type { ReprosSession } from \"../claim\";\nimport { captureScreenshot } from \"../capture/screenshot\";\nimport { TOOLBAR_STYLES } from \"./styles\";\nimport { submitSession } from \"./submit\";\nimport { submitAnnotation } from \"./annotation\";\n\nexport type ToolbarMode = \"qa\" | \"customer\";\n\nexport interface MountToolbarOptions {\n mode: ToolbarMode;\n /**\n * QA mode only. Nothing calls mountToolbar with mode: \"qa\" yet — how a\n * tester starts a QA session through the SDK (identity, project/feature/\n * build selection) isn't designed. This shell renders and is exercised\n * manually with mode: \"qa\", but ships with no real Stop wiring until\n * that's decided; the button stays disabled without a callback.\n */\n onStop?: () => void;\n /** Customer mode: fires once the customer accepts the consent screen — the only point capture may start. */\n onAccepted?: () => void;\n /** Customer mode: fires once the report is actually sent, so the caller can stop capture. */\n onSubmitted?: () => void;\n /** Customer mode: fires if the customer declines the consent screen. */\n onDeclined?: () => void;\n}\n\nexport interface ToolbarCounts {\n errors: number;\n warnings: number;\n failedRequests: number;\n}\n\nexport interface ToolbarController {\n /** QA mode only — customer mode stays deliberately simple, no technical counters. */\n setCounts(counts: ToolbarCounts): void;\n destroy(): void;\n}\n\ntype AnnotateState =\n | { kind: \"idle\" }\n | { kind: \"choosing\" }\n | { kind: \"picking\"; mode: \"element\" | \"draw\" }\n | { kind: \"composing\"; rect: AnnotationRect; note: string; saving: boolean; error: string | null };\n\ntype ViewState =\n | { kind: \"consent\" }\n | { kind: \"active\"; minimized: boolean; submitting: boolean; error: string | null; annotate: AnnotateState }\n | { kind: \"sent\" };\n\nfunction formatElapsed(ms: number): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n return `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n}\n\n/**\n * Mounts the toolbar into a shadow-DOM host appended to <body>. One\n * component, two modes: customer mode gates on a consent screen before\n * anything is visible as \"shared\"; QA mode skips straight to the active\n * panel (a tester already knows they're testing).\n *\n * The Note button drives a small state machine (choose mode -> pick on the\n * page -> compose a note -> save) built on the shared\n * @test-tracker/annotation-toolkit package — the same element-select/\n * free-draw/composite primitives this SDK's toolbar uses here are meant to\n * be reusable from the extension's own React toolbar too, per the SDK\n * PRD's \"one annotation component, two hosts\" decision. That extension\n * side isn't wired up yet in this pass — its existing text-only\n * AnnotationComposer keeps working unchanged; retrofitting it to the\n * shared primitives is separate follow-up work, not done here.\n */\nexport function mountToolbar(session: ReprosSession, options: MountToolbarOptions): ToolbarController {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-repros-toolbar\", \"\");\n const shadow = host.attachShadow({ mode: \"open\" });\n\n const styleEl = document.createElement(\"style\");\n styleEl.textContent = TOOLBAR_STYLES;\n shadow.appendChild(styleEl);\n\n const root = document.createElement(\"div\");\n root.className = \"rp-root\";\n shadow.appendChild(root);\n document.body.appendChild(host);\n\n const startedAt = Date.now();\n let state: ViewState =\n options.mode === \"customer\"\n ? { kind: \"consent\" }\n : { kind: \"active\", minimized: false, submitting: false, error: null, annotate: { kind: \"idle\" } };\n let elapsedTimer: ReturnType<typeof setInterval> | undefined;\n let counts: ToolbarCounts = { errors: 0, warnings: 0, failedRequests: 0 };\n let notesCount = 0;\n let cancelPicking: (() => void) | null = null;\n\n function setState(next: ViewState) {\n state = next;\n render();\n }\n\n function ensureElapsedTimer() {\n if (elapsedTimer) return;\n elapsedTimer = setInterval(() => {\n if (state.kind === \"active\" && !state.minimized) render();\n }, 1000);\n }\n\n async function handleSubmit() {\n if (state.kind !== \"active\") return;\n setState({ ...state, submitting: true, error: null });\n const outcome = await submitSession(session.token, session.apiBase);\n if (outcome === \"ok\" || outcome === \"already_submitted\") {\n if (elapsedTimer) clearInterval(elapsedTimer);\n options.onSubmitted?.();\n setState({ kind: \"sent\" });\n return;\n }\n setState({\n kind: \"active\",\n minimized: false,\n submitting: false,\n error: \"Couldn't send that — check your connection and try again.\",\n annotate: { kind: \"idle\" },\n });\n }\n\n function startPicking(mode: \"element\" | \"draw\") {\n if (state.kind !== \"active\") return;\n setState({ ...state, annotate: { kind: \"picking\", mode } });\n host.style.display = \"none\";\n\n const onPick = ({ rect }: { rect: AnnotationRect }) => {\n cancelPicking = null;\n host.style.display = \"\";\n if (state.kind !== \"active\") return;\n setState({ ...state, annotate: { kind: \"composing\", rect, note: \"\", saving: false, error: null } });\n };\n const onCancel = () => {\n cancelPicking = null;\n host.style.display = \"\";\n if (state.kind !== \"active\") return;\n setState({ ...state, annotate: { kind: \"idle\" } });\n };\n cancelPicking = mode === \"element\" ? startElementSelect(onPick, onCancel) : startFreeDraw(onPick, onCancel);\n }\n\n async function handleSaveAnnotation() {\n if (state.kind !== \"active\" || state.annotate.kind !== \"composing\") return;\n const { rect, note } = state.annotate;\n const trimmed = note.trim();\n if (!trimmed) return;\n setState({ ...state, annotate: { ...state.annotate, saving: true, error: null } });\n\n const shot = await captureScreenshot();\n if (!shot) {\n setState({\n ...state,\n annotate: { kind: \"composing\", rect, note, saving: false, error: \"Couldn't capture a screenshot — try again.\" },\n });\n return;\n }\n const composited = await compositeAnnotation(shot.dataUrl, rect, { width: shot.width, height: shot.height });\n const outcome = await submitAnnotation(session.token, session.apiBase, trimmed, composited);\n if (outcome !== \"ok\") {\n setState({\n ...state,\n annotate: { kind: \"composing\", rect, note, saving: false, error: \"Couldn't save that note — try again.\" },\n });\n return;\n }\n notesCount++;\n if (state.kind === \"active\") setState({ ...state, annotate: { kind: \"idle\" } });\n }\n\n function render() {\n root.innerHTML = \"\";\n\n if (state.kind === \"consent\") {\n const card = document.createElement(\"div\");\n card.className = \"rp-consent\";\n card.setAttribute(\"role\", \"dialog\");\n card.setAttribute(\"aria-label\", \"Repros consent\");\n card.innerHTML = `\n <div class=\"rp-title\">Help report this problem?</div>\n <p class=\"rp-body\">Repros will note what you do on this page — clicks, errors, a note you add — so the team can see exactly what went wrong. Nothing is shared until you send it.</p>\n <div class=\"rp-actions\">\n <button class=\"rp-btn rp-btn-secondary\" data-action=\"decline\">Not now</button>\n <button class=\"rp-btn rp-btn-primary\" data-action=\"accept\">Continue</button>\n </div>\n `;\n card.querySelector('[data-action=\"decline\"]')?.addEventListener(\"click\", () => {\n host.remove();\n options.onDeclined?.();\n });\n card.querySelector('[data-action=\"accept\"]')?.addEventListener(\"click\", () => {\n ensureElapsedTimer();\n options.onAccepted?.();\n setState({ kind: \"active\", minimized: false, submitting: false, error: null, annotate: { kind: \"idle\" } });\n });\n root.appendChild(card);\n return;\n }\n\n if (state.kind === \"sent\") {\n const card = document.createElement(\"div\");\n card.className = \"rp-consent\";\n card.innerHTML = `\n <div class=\"rp-title\">Thanks — report sent</div>\n <p class=\"rp-body\">The team can now see what happened here.</p>\n `;\n root.appendChild(card);\n setTimeout(() => host.remove(), 4000);\n return;\n }\n\n ensureElapsedTimer();\n const active = state; // narrow once, locally — state is a closured `let`, so TS won't keep it narrowed past the calls below\n\n if (active.minimized) {\n const pill = document.createElement(\"button\");\n pill.className = \"rp-pill\";\n pill.title = \"Show the Repros toolbar\";\n const errorBadge =\n options.mode === \"qa\" && counts.errors > 0 ? `<span class=\"rp-pill-count rp-tone-error\">${counts.errors}</span>` : \"\";\n pill.innerHTML = `<span class=\"rp-dot\"></span><span class=\"rp-mono\">${formatElapsed(Date.now() - startedAt)}</span>${errorBadge}`;\n pill.addEventListener(\"click\", () => setState({ ...active, minimized: false }));\n root.appendChild(pill);\n return;\n }\n\n const panel = document.createElement(\"div\");\n panel.className = \"rp-panel\";\n panel.setAttribute(\"role\", \"region\");\n panel.setAttribute(\"aria-label\", options.mode === \"customer\" ? \"Repros report\" : \"Repros test session\");\n\n const header = document.createElement(\"div\");\n header.className = \"rp-header\";\n header.innerHTML = `\n <div class=\"rp-header-main\">\n <span class=\"rp-status\"><span class=\"rp-dot\"></span>${options.mode === \"customer\" ? \"RECORDING\" : \"REC\"}</span>\n <span class=\"rp-mono rp-elapsed\">${formatElapsed(Date.now() - startedAt)}</span>\n </div>\n `;\n const minimizeBtn = document.createElement(\"button\");\n minimizeBtn.className = \"rp-icon-btn\";\n minimizeBtn.title = \"Minimize\";\n minimizeBtn.setAttribute(\"aria-label\", \"Minimize toolbar\");\n minimizeBtn.textContent = \"–\";\n minimizeBtn.addEventListener(\"click\", () => setState({ ...active, minimized: true }));\n header.appendChild(minimizeBtn);\n panel.appendChild(header);\n\n if (options.mode === \"qa\") {\n const countsEl = document.createElement(\"div\");\n countsEl.className = \"rp-counts\";\n countsEl.innerHTML = `\n <span class=\"rp-count rp-count-error\"><span class=\"rp-count-value\">${counts.errors}</span>errors</span>\n <span class=\"rp-count rp-count-error\"><span class=\"rp-count-value\">${counts.failedRequests}</span>failed req</span>\n <span class=\"rp-count\"><span class=\"rp-count-value\">${counts.warnings}</span>warnings</span>\n <span class=\"rp-count rp-count-note\"><span class=\"rp-count-value\">${notesCount}</span>notes</span>\n `;\n panel.appendChild(countsEl);\n }\n\n if (active.annotate.kind === \"choosing\") {\n const chooser = document.createElement(\"div\");\n chooser.className = \"rp-actions\";\n chooser.innerHTML = `\n <button class=\"rp-btn rp-btn-secondary\" data-action=\"element\">Select element</button>\n <button class=\"rp-btn rp-btn-secondary\" data-action=\"draw\">Draw box</button>\n `;\n chooser.querySelector('[data-action=\"element\"]')?.addEventListener(\"click\", () => startPicking(\"element\"));\n chooser.querySelector('[data-action=\"draw\"]')?.addEventListener(\"click\", () => startPicking(\"draw\"));\n panel.appendChild(chooser);\n const cancel = document.createElement(\"button\");\n cancel.className = \"rp-btn rp-btn-secondary\";\n cancel.textContent = \"Cancel\";\n cancel.addEventListener(\"click\", () => setState({ ...active, annotate: { kind: \"idle\" } }));\n panel.appendChild(cancel);\n } else if (active.annotate.kind === \"picking\") {\n const hint = document.createElement(\"p\");\n hint.className = \"rp-fine-print\";\n hint.textContent = active.annotate.mode === \"element\" ? \"Click something on the page… (Esc to cancel)\" : \"Drag a box… (Esc to cancel)\";\n panel.appendChild(hint);\n } else if (active.annotate.kind === \"composing\") {\n const compose = active.annotate;\n const wrap = document.createElement(\"div\");\n wrap.innerHTML = `<textarea class=\"rp-fine-print\" style=\"width:100%;min-height:56px;background:var(--rp-surface-2);border:1px solid var(--rp-border);border-radius:8px;padding:6px 8px;color:var(--rp-ink);font:inherit;resize:vertical;\" placeholder=\"What's wrong here?\"></textarea>`;\n const textarea = wrap.querySelector(\"textarea\") as HTMLTextAreaElement;\n textarea.value = compose.note;\n panel.appendChild(wrap);\n\n const composeActions = document.createElement(\"div\");\n composeActions.className = \"rp-actions\";\n const saveBtn = document.createElement(\"button\");\n saveBtn.className = \"rp-btn rp-btn-primary\";\n saveBtn.disabled = compose.saving || !compose.note.trim();\n saveBtn.textContent = compose.saving ? \"Saving…\" : \"Save note\";\n saveBtn.addEventListener(\"click\", () => void handleSaveAnnotation());\n\n // Mutates state in place rather than going through setState — a\n // render() on every keystroke would tear down and recreate this\n // textarea (render() does root.innerHTML = \"\"), losing focus and\n // cursor position. Updates the save button's disabled state directly\n // for the same reason — it has to react to typing without a render.\n textarea.addEventListener(\"input\", () => {\n if (state.kind === \"active\" && state.annotate.kind === \"composing\") state.annotate.note = textarea.value;\n saveBtn.disabled = compose.saving || !textarea.value.trim();\n });\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.className = \"rp-btn rp-btn-secondary\";\n cancelBtn.disabled = compose.saving;\n cancelBtn.textContent = \"Cancel\";\n cancelBtn.addEventListener(\"click\", () => setState({ ...active, annotate: { kind: \"idle\" } }));\n composeActions.append(saveBtn, cancelBtn);\n panel.appendChild(composeActions);\n\n if (compose.error) {\n const err = document.createElement(\"p\");\n err.className = \"rp-error\";\n err.textContent = compose.error;\n panel.appendChild(err);\n }\n } else {\n const actions = document.createElement(\"div\");\n actions.className = \"rp-actions\";\n\n const noteBtn = document.createElement(\"button\");\n noteBtn.className = \"rp-btn rp-btn-secondary\";\n noteBtn.textContent = \"Note\";\n noteBtn.addEventListener(\"click\", () => setState({ ...active, annotate: { kind: \"choosing\" } }));\n actions.appendChild(noteBtn);\n\n if (options.mode === \"customer\") {\n const submitBtn = document.createElement(\"button\");\n submitBtn.className = \"rp-btn rp-btn-primary\";\n submitBtn.disabled = active.submitting;\n submitBtn.textContent = active.submitting ? \"Sending…\" : \"Send report\";\n submitBtn.addEventListener(\"click\", () => void handleSubmit());\n actions.appendChild(submitBtn);\n } else {\n const stopBtn = document.createElement(\"button\");\n stopBtn.className = \"rp-btn rp-btn-stop\";\n stopBtn.disabled = !options.onStop;\n stopBtn.title = options.onStop ? \"Stop recording\" : \"Not wired up yet\";\n stopBtn.textContent = \"Stop\";\n stopBtn.addEventListener(\"click\", () => options.onStop?.());\n actions.appendChild(stopBtn);\n }\n\n panel.appendChild(actions);\n\n if (active.error) {\n const err = document.createElement(\"p\");\n err.className = \"rp-error\";\n err.textContent = active.error;\n panel.appendChild(err);\n }\n }\n\n root.appendChild(panel);\n }\n\n render();\n\n return {\n setCounts(next: ToolbarCounts) {\n counts = next;\n if (state.kind === \"active\") render();\n },\n destroy() {\n if (elapsedTimer) clearInterval(elapsedTimer);\n cancelPicking?.();\n host.remove();\n },\n };\n}\n","import type { ReprosSession } from \"../claim\";\n\nexport interface CaptureCounts {\n errors: number;\n warnings: number;\n failedRequests: number;\n}\n\nexport interface LogEntryPayload {\n type: \"console_warn\" | \"console_error\" | \"window_error\" | \"unhandled_rejection\";\n message: string;\n sourceUrl?: string;\n stackTrace?: string;\n occurredAt: string;\n}\n\nexport interface NetworkRequestPayload {\n method: string;\n url: string;\n statusCode?: number;\n statusText?: string;\n errorText?: string;\n durationMs?: number;\n occurredAt: string;\n}\n\nconst FLUSH_INTERVAL_MS = 5000;\n// Matches the server's own per-batch cap (captureCustomerSessionEvents) —\n// capping client-side too means a burst that would otherwise get the whole\n// batch rejected as oversized instead just drops its tail, quietly.\nconst MAX_BUFFERED = 50;\n\nexport interface CaptureBuffer {\n addLog(entry: LogEntryPayload): void;\n addNetwork(entry: NetworkRequestPayload): void;\n stop(): void;\n}\n\n/**\n * Buffers captured events and flushes them in batches rather than one HTTP\n * call per console line or network request — the claim/submit endpoints\n * are sized for one-off human actions, this one for a live pipeline that\n * can run for as long as the session stays open. Flushes on a timer, and\n * again (via a keepalive fetch, which survives unload in every evergreen\n * browser) when the tab is hidden or unloaded, so a customer closing the\n * tab right after an error doesn't lose it.\n */\nexport function createCaptureBuffer(session: ReprosSession, onCounts: (counts: CaptureCounts) => void): CaptureBuffer {\n let logEntries: LogEntryPayload[] = [];\n let networkRequests: NetworkRequestPayload[] = [];\n const counts: CaptureCounts = { errors: 0, warnings: 0, failedRequests: 0 };\n\n // Error.stack captures the URL as it was when the page's script first\n // parsed — before readClaimToken() rewrites it via history.replaceState\n // — so a stack trace for code that ran early can still carry the raw\n // claim token. Scrub it before it ever leaves the page: this is a\n // single-use-until-submitted credential, and it has no business sitting\n // in stored evidence a developer later reads.\n function redact(text: string): string {\n return text.split(session.token).join(\"[repros-token]\");\n }\n\n function addLog(entry: LogEntryPayload) {\n if (logEntries.length >= MAX_BUFFERED) return;\n logEntries.push({\n ...entry,\n message: redact(entry.message),\n stackTrace: entry.stackTrace ? redact(entry.stackTrace) : entry.stackTrace,\n sourceUrl: entry.sourceUrl ? redact(entry.sourceUrl) : entry.sourceUrl,\n });\n if (entry.type === \"console_warn\") counts.warnings++;\n else counts.errors++;\n onCounts({ ...counts });\n }\n\n function addNetwork(entry: NetworkRequestPayload) {\n if (networkRequests.length >= MAX_BUFFERED) return;\n networkRequests.push({ ...entry, url: redact(entry.url) });\n counts.failedRequests++;\n onCounts({ ...counts });\n }\n\n async function flush() {\n if (logEntries.length === 0 && networkRequests.length === 0) return;\n const batchLog = logEntries;\n const batchNetwork = networkRequests;\n logEntries = [];\n networkRequests = [];\n const url = `${session.apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/capture`;\n try {\n await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: session.token, logEntries: batchLog, networkRequests: batchNetwork }),\n keepalive: true,\n });\n } catch (err) {\n console.warn(\"[Repros SDK] capture flush failed:\", err);\n }\n }\n\n const timer = setInterval(() => void flush(), FLUSH_INTERVAL_MS);\n const onVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") void flush();\n };\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n window.addEventListener(\"pagehide\", () => void flush());\n\n function stop() {\n clearInterval(timer);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n void flush();\n }\n\n return { addLog, addNetwork, stop };\n}\n","const MAX_MESSAGE_CHARS = 2000;\n\n/**\n * Lean version of the extension's serializeArg (apps/extension/src/injected/serialize.ts)\n * — same Error/circular-reference handling, without that one's printf-style\n * (%s/%d/%c) substitution or DOM-element description. Kept SDK-local\n * rather than shared: worth unifying later, not blocking this on an\n * extraction into packages/shared-types now.\n */\nexport function serializeArg(arg: unknown): string {\n if (typeof arg === \"string\") return arg;\n if (arg instanceof Error) return arg.stack?.startsWith(arg.name) ? arg.stack : `${arg.name}: ${arg.message}\\n${arg.stack ?? \"\"}`;\n if (typeof arg === \"undefined\") return \"undefined\";\n if (typeof arg === \"function\") return `[Function ${arg.name || \"anonymous\"}]`;\n if (typeof arg === \"symbol\" || typeof arg === \"bigint\") return String(arg);\n\n const seen = new WeakSet();\n try {\n return (\n JSON.stringify(arg, (_key, value) => {\n if (typeof value === \"object\" && value !== null) {\n if (seen.has(value)) return \"[Circular]\";\n seen.add(value);\n if (value instanceof Map) return Object.fromEntries(value);\n if (value instanceof Set) return [...value];\n }\n if (typeof value === \"bigint\") return `${value}n`;\n return value;\n }) ?? String(arg)\n );\n } catch {\n try {\n return String(arg);\n } catch {\n return \"[Unserializable]\";\n }\n }\n}\n\nexport function truncate(message: string): string {\n return message.length > MAX_MESSAGE_CHARS ? `${message.slice(0, MAX_MESSAGE_CHARS)}… [truncated]` : message;\n}\n\nexport function serializeConsoleArgs(args: unknown[]): string {\n return truncate(args.map(serializeArg).join(\" \"));\n}\n","import type { CaptureBuffer } from \"./buffer\";\nimport { serializeConsoleArgs } from \"./serialize\";\n\nconst METHOD_TO_TYPE = { warn: \"console_warn\", error: \"console_error\" } as const;\n\n/**\n * Hooks console.warn/console.error only — not .log/.info, which the\n * toolbar has no counter for and would mostly add noise to a customer's\n * report. Always calls the original first so devtools output and any page\n * code that inspects console call counts is unaffected.\n */\nexport function installConsoleCapture(buffer: CaptureBuffer): () => void {\n const originals = { warn: console.warn.bind(console), error: console.error.bind(console) };\n\n (Object.keys(METHOD_TO_TYPE) as (keyof typeof METHOD_TO_TYPE)[]).forEach((method) => {\n console[method] = (...args: unknown[]) => {\n originals[method](...args);\n try {\n const errorArg = args.find((a) => a instanceof Error) as Error | undefined;\n buffer.addLog({\n type: METHOD_TO_TYPE[method],\n message: serializeConsoleArgs(args),\n stackTrace: errorArg?.stack ?? (method === \"error\" ? new Error().stack : undefined),\n sourceUrl: window.location.href,\n occurredAt: new Date().toISOString(),\n });\n } catch {\n // Capture must never break the page's own logging.\n }\n };\n });\n\n return () => {\n console.warn = originals.warn;\n console.error = originals.error;\n };\n}\n","import type { CaptureBuffer } from \"./buffer\";\nimport { serializeArg, truncate } from \"./serialize\";\n\n/** window.onerror and unhandledrejection — mirrors the extension's errorCapture.ts. */\nexport function installErrorCapture(buffer: CaptureBuffer): () => void {\n const onError = (event: ErrorEvent) => {\n const location = event.filename ? `${event.filename}:${event.lineno}:${event.colno}` : undefined;\n buffer.addLog({\n type: \"window_error\",\n message: truncate(event.message || serializeArg(event.error)),\n stackTrace: event.error instanceof Error ? event.error.stack : location ? ` at ${location}` : undefined,\n sourceUrl: window.location.href,\n occurredAt: new Date().toISOString(),\n });\n };\n\n const onRejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n buffer.addLog({\n type: \"unhandled_rejection\",\n message: truncate(reason instanceof Error ? `${reason.name}: ${reason.message}` : serializeArg(reason)),\n stackTrace: reason instanceof Error ? reason.stack : undefined,\n sourceUrl: window.location.href,\n occurredAt: new Date().toISOString(),\n });\n };\n\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n\n return () => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n };\n}\n","import type { CaptureBuffer } from \"./buffer\";\n\n/**\n * Monkey-patches fetch and XMLHttpRequest to capture failed requests only\n * (4xx/5xx or a connection-level error) — same phase-1 scope as the\n * extension's chrome.webRequest-based capture, and deliberately no\n * headers/body (see capture.ts on the dashboard side for why).\n */\nexport function installNetworkCapture(buffer: CaptureBuffer): () => void {\n const restoreFetch = installFetchCapture(buffer);\n const restoreXhr = installXhrCapture(buffer);\n return () => {\n restoreFetch();\n restoreXhr();\n };\n}\n\nfunction requestInfo(input: RequestInfo | URL, init: RequestInit | undefined): { method: string; url: string } {\n if (typeof input === \"string\" || input instanceof URL) {\n return { method: init?.method?.toUpperCase() ?? \"GET\", url: String(input) };\n }\n return { method: (init?.method ?? input.method ?? \"GET\").toUpperCase(), url: input.url };\n}\n\nfunction installFetchCapture(buffer: CaptureBuffer): () => void {\n if (typeof window.fetch !== \"function\") return () => {};\n const originalFetch = window.fetch.bind(window);\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const { method, url } = requestInfo(input, init);\n const startedAt = performance.now();\n try {\n const response = await originalFetch(input, init);\n if (!response.ok) {\n buffer.addNetwork({\n method,\n url,\n statusCode: response.status,\n statusText: response.statusText,\n durationMs: Math.round(performance.now() - startedAt),\n occurredAt: new Date().toISOString(),\n });\n }\n return response;\n } catch (err) {\n buffer.addNetwork({\n method,\n url,\n errorText: err instanceof Error ? err.message : String(err),\n durationMs: Math.round(performance.now() - startedAt),\n occurredAt: new Date().toISOString(),\n });\n throw err;\n }\n };\n\n return () => {\n window.fetch = originalFetch;\n };\n}\n\nconst XHR_STATE = new WeakMap<XMLHttpRequest, { method: string; url: string; startedAt: number }>();\n\nfunction installXhrCapture(buffer: CaptureBuffer): () => void {\n const proto = XMLHttpRequest.prototype;\n const originalOpen = proto.open;\n const originalSend = proto.send;\n\n proto.open = function (this: XMLHttpRequest, method: string, url: string | URL, ...rest: unknown[]) {\n XHR_STATE.set(this, { method: method.toUpperCase(), url: String(url), startedAt: 0 });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalOpen as any).call(this, method, url, ...rest);\n };\n\n proto.send = function (this: XMLHttpRequest, ...args: unknown[]) {\n const state = XHR_STATE.get(this);\n if (state) state.startedAt = performance.now();\n\n const onLoadEnd = () => {\n const current = XHR_STATE.get(this);\n if (!current) return;\n // 0 means a connection-level failure (aborted, network error, CORS) — status never got set.\n if (this.status === 0 || this.status >= 400) {\n buffer.addNetwork({\n method: current.method,\n url: current.url,\n statusCode: this.status || undefined,\n statusText: this.statusText || undefined,\n errorText: this.status === 0 ? \"Network error\" : undefined,\n durationMs: Math.round(performance.now() - current.startedAt),\n occurredAt: new Date().toISOString(),\n });\n }\n this.removeEventListener(\"loadend\", onLoadEnd);\n XHR_STATE.delete(this);\n };\n this.addEventListener(\"loadend\", onLoadEnd);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalSend as any).apply(this, args);\n };\n\n return () => {\n proto.open = originalOpen;\n proto.send = originalSend;\n };\n}\n","import type { ReprosSession } from \"../claim\";\nimport { createCaptureBuffer, type CaptureCounts } from \"./buffer\";\nimport { installConsoleCapture } from \"./console\";\nimport { installErrorCapture } from \"./errors\";\nimport { installNetworkCapture } from \"./network\";\n\nexport type { CaptureCounts };\nexport { captureScreenshot } from \"./screenshot\";\n\n/**\n * Wires console/window-error/network capture into one buffer for the\n * session's lifetime. Returns a stop() that restores every patched global\n * (console.warn/error, window.fetch, XMLHttpRequest.prototype) and flushes\n * whatever's still buffered — called once the session ends (submitted),\n * since there's no point capturing a page a customer already sent.\n */\nexport function startCapture(session: ReprosSession, onCounts: (counts: CaptureCounts) => void): () => void {\n const buffer = createCaptureBuffer(session, onCounts);\n const restoreConsole = installConsoleCapture(buffer);\n const restoreErrors = installErrorCapture(buffer);\n const restoreNetwork = installNetworkCapture(buffer);\n\n return () => {\n restoreConsole();\n restoreErrors();\n restoreNetwork();\n buffer.stop();\n };\n}\n"],"mappings":"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","names":["session","session","init","session"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/claim.ts","../../annotation-toolkit/src/select.ts","../../annotation-toolkit/src/draw.ts","../../annotation-toolkit/src/composite.ts","../src/capture/screenshot.ts","../src/toolbar/styles.ts","../src/toolbar/submit.ts","../src/toolbar/annotation.ts","../src/toolbar/toolbar.ts","../src/capture/buffer.ts","../src/capture/serialize.ts","../src/capture/console.ts","../src/capture/errors.ts","../src/capture/network.ts","../src/capture/index.ts"],"sourcesContent":["import { claim, readClaimToken, type ReprosSession } from \"./claim\";\nimport { mountToolbar } from \"./toolbar/toolbar\";\nimport { startCapture } from \"./capture\";\n\nexport type { ReprosSession };\nexport type { ToolbarMode, MountToolbarOptions, ToolbarCounts } from \"./toolbar/toolbar\";\nexport { mountToolbar } from \"./toolbar/toolbar\";\nexport { captureScreenshot } from \"./capture\";\n\nexport interface InitOptions {\n /** Override for local development or self-hosted testing. Defaults to the production Repros app. */\n apiBase?: string;\n}\n\nlet session: ReprosSession | null = null;\nlet initStarted = false;\n\n/**\n * Entry point for orgs with their own build step:\n *\n * import { init } from \"@repros/sdk\";\n * init();\n *\n * Strict no-op by default: if the page wasn't opened via a Repros\n * customer-session link (no `repros_claim` query param), this does\n * nothing at all — no network call, no UI, nothing mounted. That's the\n * whole point of the claim step existing: the SDK can sit installed on\n * every page of an org's app, permanently, and stay invisible until a\n * customer arrives through an actual one-time link.\n *\n * On a successful claim this also mounts the customer toolbar (consent\n * screen, then a minimal recording panel with a \"Send report\" action).\n * Console/window-error/network capture only starts once the customer\n * actually accepts that consent screen — never before, and it stops again\n * the moment the report is sent. The note button is still a placeholder —\n * the annotation toolkit (a separate card) is what wires it up.\n */\nexport async function init(options: InitOptions = {}): Promise<ReprosSession | null> {\n if (initStarted) return session;\n initStarted = true;\n\n const token = readClaimToken();\n if (!token) return null;\n\n session = await claim(token, options.apiBase);\n if (!session) return null;\n const activeSession = session; // local const so the closures below don't depend on the mutable module-level `session`\n\n let stopCapture: (() => void) | null = null;\n const toolbar = mountToolbar(activeSession, {\n mode: \"customer\",\n onAccepted: () => {\n stopCapture = startCapture(activeSession, (counts) => toolbar.setCounts(counts));\n },\n onSubmitted: () => stopCapture?.(),\n });\n\n return session;\n}\n\nexport function getSession(): ReprosSession | null {\n return session;\n}\n","// www. explicitly — repros.dev 308-redirects to www.repros.dev, and a\n// redirect turns a simple cross-origin POST into a preflighted one for no\n// reason (same fix as apps/dashboard/app/(authed)/account/ApiTokens.tsx).\nconst DEFAULT_API_BASE = \"https://www.repros.dev\";\n\nconst QUERY_PARAM = \"repros_claim\";\n\nexport interface ReprosSession {\n sessionId: string;\n /** The claim token, kept in memory only — the toolbar's submit call is keyed on it, same as claim. */\n token: string;\n apiBase: string;\n}\n\ninterface ClaimResponse {\n ok: boolean;\n sessionId?: string;\n reason?: string;\n}\n\n/** Reads and strips the claim token from the current URL, if present. */\nexport function readClaimToken(): string | null {\n if (typeof window === \"undefined\") return null;\n const url = new URL(window.location.href);\n const token = url.searchParams.get(QUERY_PARAM);\n if (!token) return null;\n\n // The token is single-purpose and shouldn't linger in browser history or\n // leak via Referer once we've read it — same care as the claim landing\n // page's own no-referrer metadata.\n url.searchParams.delete(QUERY_PARAM);\n window.history.replaceState(window.history.state, \"\", url.toString());\n\n return token;\n}\n\n/**\n * Calls the claim endpoint. Never throws — a bad or expired token, a\n * network blip, or the endpoint being unreachable should all just mean\n * \"no session\", not a broken page for the customer.\n */\nexport async function claim(token: string, apiBase = DEFAULT_API_BASE): Promise<ReprosSession | null> {\n try {\n const res = await fetch(`${apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/claim`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token }),\n });\n const data = (await res.json().catch(() => null)) as ClaimResponse | null;\n if (!res.ok || !data?.ok || !data.sessionId) {\n console.warn(\"[Repros SDK] couldn't claim report link:\", data?.reason ?? res.status);\n return null;\n }\n return { sessionId: data.sessionId, token, apiBase };\n } catch (err) {\n console.warn(\"[Repros SDK] couldn't reach Repros to claim report link:\", err);\n return null;\n }\n}\n","import type { AnnotationTarget } from \"./types\";\n\n/**\n * DevTools-inspector-style element picking: hover highlights whatever's\n * under the cursor, click locks it in. Listens on the capture phase and\n * preventDefault/stopPropagation on the click so the host page's own click\n * handlers never fire — picking an element must not also activate it (a\n * button, a link).\n *\n * Returns a cancel function; also self-cancels on Escape or a successful\n * pick. Callers are responsible for hiding their own UI (e.g. the toolbar\n * panel) before calling this, so it doesn't get highlighted/picked itself.\n */\nexport function startElementSelect(onPick: (target: AnnotationTarget) => void, onCancel: () => void): () => void {\n const highlight = document.createElement(\"div\");\n highlight.style.cssText =\n \"position:fixed;pointer-events:none;z-index:2147483645;border:2px solid #7c3aed;\" +\n \"background:rgba(124,58,237,0.15);border-radius:2px;display:none;box-sizing:border-box;\";\n document.body.appendChild(highlight);\n\n let lastEl: Element | null = null;\n\n function updateHighlight(el: Element) {\n if (el === lastEl) return;\n lastEl = el;\n const r = el.getBoundingClientRect();\n Object.assign(highlight.style, {\n display: \"block\",\n left: `${r.left}px`,\n top: `${r.top}px`,\n width: `${r.width}px`,\n height: `${r.height}px`,\n });\n }\n\n function onMouseMove(e: MouseEvent) {\n const el = document.elementFromPoint(e.clientX, e.clientY);\n if (el && el !== highlight) updateHighlight(el);\n }\n\n function onClick(e: MouseEvent) {\n e.preventDefault();\n e.stopPropagation();\n const el = lastEl;\n cleanup();\n if (!el) {\n onCancel();\n return;\n }\n const r = el.getBoundingClientRect();\n onPick({ rect: { x: r.left + window.scrollX, y: r.top + window.scrollY, width: r.width, height: r.height } });\n }\n\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === \"Escape\") {\n cleanup();\n onCancel();\n }\n }\n\n function cleanup() {\n window.removeEventListener(\"mousemove\", onMouseMove, true);\n window.removeEventListener(\"click\", onClick, true);\n window.removeEventListener(\"keydown\", onKeyDown, true);\n highlight.remove();\n }\n\n window.addEventListener(\"mousemove\", onMouseMove, true);\n window.addEventListener(\"click\", onClick, true);\n window.addEventListener(\"keydown\", onKeyDown, true);\n\n return cleanup;\n}\n","import type { AnnotationTarget } from \"./types\";\n\nconst MIN_DRAG_PX = 4;\n\n/**\n * Free-draw: drag a rectangle anywhere on the page. A drag shorter than\n * MIN_DRAG_PX in either dimension is treated as a mis-click, not a\n * zero-size annotation, and cancels rather than picking.\n */\nexport function startFreeDraw(onPick: (target: AnnotationTarget) => void, onCancel: () => void): () => void {\n const overlay = document.createElement(\"div\");\n overlay.style.cssText = \"position:fixed;inset:0;z-index:2147483645;cursor:crosshair;background:rgba(0,0,0,0.01);\";\n\n const box = document.createElement(\"div\");\n box.style.cssText =\n \"position:fixed;pointer-events:none;z-index:2147483645;border:2px solid #7c3aed;\" +\n \"background:rgba(124,58,237,0.15);display:none;box-sizing:border-box;\";\n\n document.body.appendChild(overlay);\n document.body.appendChild(box);\n\n let dragging = false;\n let startX = 0;\n let startY = 0;\n\n function viewportRect(curX: number, curY: number) {\n return {\n x: Math.min(startX, curX),\n y: Math.min(startY, curY),\n width: Math.abs(curX - startX),\n height: Math.abs(curY - startY),\n };\n }\n\n function onDown(e: MouseEvent) {\n dragging = true;\n startX = e.clientX;\n startY = e.clientY;\n box.style.display = \"block\";\n paint(viewportRect(e.clientX, e.clientY));\n }\n\n function paint(r: { x: number; y: number; width: number; height: number }) {\n Object.assign(box.style, { left: `${r.x}px`, top: `${r.y}px`, width: `${r.width}px`, height: `${r.height}px` });\n }\n\n function onMove(e: MouseEvent) {\n if (dragging) paint(viewportRect(e.clientX, e.clientY));\n }\n\n function onUp(e: MouseEvent) {\n if (!dragging) return;\n dragging = false;\n const r = viewportRect(e.clientX, e.clientY);\n cleanup();\n if (r.width < MIN_DRAG_PX || r.height < MIN_DRAG_PX) {\n onCancel();\n return;\n }\n onPick({ rect: { x: r.x + window.scrollX, y: r.y + window.scrollY, width: r.width, height: r.height } });\n }\n\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === \"Escape\") {\n cleanup();\n onCancel();\n }\n }\n\n function cleanup() {\n overlay.removeEventListener(\"mousedown\", onDown);\n window.removeEventListener(\"mousemove\", onMove);\n window.removeEventListener(\"mouseup\", onUp);\n window.removeEventListener(\"keydown\", onKeyDown, true);\n overlay.remove();\n box.remove();\n }\n\n overlay.addEventListener(\"mousedown\", onDown);\n window.addEventListener(\"mousemove\", onMove);\n window.addEventListener(\"mouseup\", onUp);\n window.addEventListener(\"keydown\", onKeyDown, true);\n\n return cleanup;\n}\n","import type { AnnotationRect } from \"./types\";\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(\"Failed to load screenshot for compositing\"));\n img.src = src;\n });\n}\n\n/**\n * Burns the annotation box onto the screenshot at the moment it's made —\n * see the SDK PRD's \"annotations persist as composited screenshots, not\n * DOM references\" decision. `rect` is document-relative (see\n * AnnotationRect). `capturedSize` must be the exact width/height the\n * screenshot tool measured at capture time (captureScreenshot()'s own\n * `width`/`height`, not document.documentElement.scrollWidth/scrollHeight\n * — those can disagree substantially on a page shorter than the viewport,\n * since scrollHeight pads up to the viewport while the actual rendered/\n * captured box doesn't).\n */\nexport async function compositeAnnotation(\n screenshotDataUrl: string,\n rect: AnnotationRect,\n capturedSize: { width: number; height: number },\n): Promise<string> {\n const img = await loadImage(screenshotDataUrl);\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"2d canvas context unavailable\");\n\n ctx.drawImage(img, 0, 0);\n\n const scaleX = img.width / (capturedSize.width || img.width);\n const scaleY = img.height / (capturedSize.height || img.height);\n const x = rect.x * scaleX;\n const y = rect.y * scaleY;\n const width = rect.width * scaleX;\n const height = rect.height * scaleY;\n\n ctx.fillStyle = \"rgba(124, 58, 237, 0.15)\";\n ctx.fillRect(x, y, width, height);\n ctx.lineWidth = 3;\n ctx.strokeStyle = \"#7c3aed\";\n ctx.strokeRect(x, y, width, height);\n\n return canvas.toDataURL(\"image/png\");\n}\n","import { domToPng } from \"modern-screenshot\";\n\nexport interface Screenshot {\n dataUrl: string;\n /**\n * CSS-pixel size of document.documentElement's own render box at the\n * instant of capture — NOT scrollWidth/scrollHeight, which on some pages\n * (short content, no scrollbar) can differ substantially from what\n * getBoundingClientRect() reports and from what domToPng actually\n * rendered. Composited annotations must scale against these exact\n * numbers, measured atomically with the capture itself, or the box lands\n * in the wrong place.\n */\n width: number;\n height: number;\n}\n\n/**\n * DOM-snapshot screenshot capture — decided over getDisplayMedia (see the\n * Repros SDK PRD's Architecture Decisions): no extra permission prompt, and\n * works on iOS Safari where getDisplayMedia doesn't exist at all. Real\n * fidelity gaps versus a true screen capture (canvas/WebGL renders blank,\n * some cross-origin images get blocked) — a known, documented limitation,\n * not a bug.\n *\n * Returns null rather than throwing on failure: a failed screenshot\n * shouldn't block a note or a report submission, it should just mean no\n * image this time.\n */\nexport async function captureScreenshot(): Promise<Screenshot | null> {\n try {\n const rect = document.documentElement.getBoundingClientRect();\n const dataUrl = await domToPng(document.documentElement, {\n backgroundColor: \"#ffffff\",\n quality: 0.92,\n filter: (node) => !(node instanceof HTMLElement && node.hasAttribute(\"data-repros-toolbar\")),\n });\n return { dataUrl, width: rect.width, height: rect.height };\n } catch (err) {\n console.warn(\"[Repros SDK] screenshot capture failed:\", err);\n return null;\n }\n}\n","// Same palette and rp- class namespace as the extension's on-page toolbar\n// (apps/extension/src/content/Toolbar/toolbar.css) so a customer or tester\n// sees one consistent brand regardless of which transport put it there.\n// Injected into a shadow root, so these names can never collide with the\n// host page's own CSS — `all: initial` on :host and .rp-root stops the\n// host page's inherited styles leaking in, same reasoning as the extension.\nexport const TOOLBAR_STYLES = `\n:host { all: initial; }\n\n.rp-root {\n all: initial;\n --rp-surface: #161b1f;\n --rp-surface-2: #1f262b;\n --rp-surface-3: #262e34;\n --rp-border: #2c343b;\n --rp-border-strong: #3a4249;\n --rp-ink: #edefe9;\n --rp-ink-2: #c4cbc3;\n --rp-ink-3: #8d958e;\n --rp-accent: #7c3aed;\n --rp-accent-hover: #6d28d9;\n --rp-accent-soft: #a78bfa;\n --rp-error: #f07a5f;\n --rp-pass: #4fbe7c;\n --rp-note: #c4b5fd;\n font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-size: 13px;\n line-height: 1.4;\n color: var(--rp-ink);\n -webkit-font-smoothing: antialiased;\n}\n\n.rp-root *, .rp-root *::before, .rp-root *::after { box-sizing: border-box; }\n\n.rp-panel, .rp-pill, .rp-consent {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 2147483647;\n}\n\n.rp-panel, .rp-consent {\n width: 300px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px;\n background: var(--rp-surface);\n border: 1px solid var(--rp-border);\n border-radius: 14px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.25);\n animation: rp-in 160ms ease-out;\n}\n\n@keyframes rp-in {\n from { opacity: 0; transform: translateY(6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rp-dot, .rp-panel, .rp-consent { animation: none; }\n}\n\n.rp-mono { font-family: ui-monospace, \"SF Mono\", Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }\n\n.rp-header { display: flex; align-items: center; justify-content: space-between; }\n.rp-header-main { display: flex; align-items: center; gap: 8px; }\n\n.rp-status {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 2px 8px; border-radius: 999px;\n font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em;\n background: rgba(124, 58, 237, 0.18); color: var(--rp-accent-soft);\n}\n\n.rp-elapsed { font-size: 12px; color: var(--rp-ink-3); }\n\n.rp-dot {\n width: 7px; height: 7px; border-radius: 50%; background: var(--rp-error);\n animation: rp-pulse 1.8s ease-in-out infinite;\n}\n\n@keyframes rp-pulse {\n 0% { box-shadow: 0 0 0 0 rgba(240, 122, 95, 0.55); }\n 70% { box-shadow: 0 0 0 6px rgba(240, 122, 95, 0); }\n 100% { box-shadow: 0 0 0 0 rgba(240, 122, 95, 0); }\n}\n\n.rp-title { font-size: 14px; font-weight: 650; color: var(--rp-ink); }\n.rp-body { font-size: 12.5px; color: var(--rp-ink-2); }\n\n.rp-counts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }\n.rp-count {\n display: flex; flex-direction: column; align-items: flex-start;\n padding: 5px 7px; border-radius: 8px; background: var(--rp-surface-2);\n color: var(--rp-ink-3); font-size: 10px; line-height: 1.2; white-space: nowrap;\n}\n.rp-count-value { font-size: 15px; font-weight: 650; font-variant-numeric: tabular-nums; color: var(--rp-ink-2); }\n.rp-count-error .rp-count-value { color: var(--rp-error); }\n.rp-count-note .rp-count-value { color: var(--rp-note); }\n\n.rp-actions { display: flex; gap: 6px; }\n\n.rp-btn, .rp-icon-btn, .rp-pill {\n font: inherit; color: inherit; cursor: pointer; border: none; background: none;\n}\n.rp-btn {\n display: inline-flex; align-items: center; justify-content: center; gap: 5px;\n flex: 1 1 auto; padding: 7px 10px; border-radius: 8px;\n font-size: 12.5px; font-weight: 600; white-space: nowrap;\n transition: background-color 120ms ease, border-color 120ms ease, opacity 120ms ease;\n}\n.rp-btn:disabled { opacity: 0.45; cursor: default; }\n.rp-btn:focus-visible, .rp-icon-btn:focus-visible, .rp-pill:focus-visible {\n outline: 2px solid var(--rp-accent-soft); outline-offset: 2px;\n}\n\n.rp-btn-primary { background: var(--rp-accent); color: #fff; }\n.rp-btn-primary:hover:not(:disabled) { background: var(--rp-accent-hover); }\n\n.rp-btn-secondary { background: var(--rp-surface-2); border: 1px solid var(--rp-border); color: var(--rp-ink-2); }\n.rp-btn-secondary:hover:not(:disabled) { background: var(--rp-surface-3); border-color: var(--rp-border-strong); }\n\n.rp-btn-stop {\n background: rgba(240, 122, 95, 0.14); border: 1px solid rgba(240, 122, 95, 0.35); color: var(--rp-error);\n}\n.rp-btn-stop:hover:not(:disabled) { background: rgba(240, 122, 95, 0.22); }\n\n.rp-icon-btn {\n display: inline-flex; align-items: center; justify-content: center;\n width: 24px; height: 24px; border-radius: 6px; color: var(--rp-ink-3);\n}\n.rp-icon-btn:hover { background: var(--rp-surface-2); color: var(--rp-ink); }\n\n.rp-pill {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 7px 12px; border-radius: 999px;\n background: var(--rp-surface); border: 1px solid var(--rp-border);\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3); font-size: 12px; color: var(--rp-ink-2);\n}\n.rp-pill:hover { border-color: var(--rp-border-strong); }\n.rp-pill-count { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--rp-note); }\n.rp-pill-count.rp-tone-error { color: var(--rp-error); }\n\n.rp-error { font-size: 11.5px; color: var(--rp-error); margin: 0; }\n.rp-fine-print { font-size: 10.5px; color: var(--rp-ink-3); margin: 0; }\n`;\n","export type SubmitOutcome = \"ok\" | \"already_submitted\" | \"error\";\n\n/** Ends the session server-side. Never throws — same \"fail quiet\" stance as claim(). */\nexport async function submitSession(token: string, apiBase: string): Promise<SubmitOutcome> {\n try {\n const res = await fetch(`${apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/submit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token }),\n });\n const data = (await res.json().catch(() => null)) as { ok?: boolean; reason?: string } | null;\n if (data?.ok) return \"ok\";\n if (data?.reason === \"already_submitted\") return \"already_submitted\";\n console.warn(\"[Repros SDK] couldn't send report:\", data?.reason ?? res.status);\n return \"error\";\n } catch (err) {\n console.warn(\"[Repros SDK] couldn't reach Repros to send report:\", err);\n return \"error\";\n }\n}\n","export type AnnotationOutcome = \"ok\" | \"error\";\n\n/** Never throws — same \"fail quiet\" stance as claim/submit. */\nexport async function submitAnnotation(token: string, apiBase: string, note: string, screenshot: string): Promise<AnnotationOutcome> {\n try {\n const res = await fetch(`${apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/annotation`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token, note, screenshot }),\n });\n const data = (await res.json().catch(() => null)) as { ok?: boolean; reason?: string } | null;\n if (data?.ok) return \"ok\";\n console.warn(\"[Repros SDK] couldn't save that note:\", data?.reason ?? res.status);\n return \"error\";\n } catch (err) {\n console.warn(\"[Repros SDK] couldn't reach Repros to save that note:\", err);\n return \"error\";\n }\n}\n","import { startElementSelect, startFreeDraw, compositeAnnotation, type AnnotationRect } from \"@test-tracker/annotation-toolkit\";\nimport type { ReprosSession } from \"../claim\";\nimport { captureScreenshot } from \"../capture/screenshot\";\nimport { TOOLBAR_STYLES } from \"./styles\";\nimport { submitSession } from \"./submit\";\nimport { submitAnnotation } from \"./annotation\";\n\nexport type ToolbarMode = \"qa\" | \"customer\";\n\nexport interface MountToolbarOptions {\n mode: ToolbarMode;\n /**\n * QA mode only. Nothing calls mountToolbar with mode: \"qa\" yet — how a\n * tester starts a QA session through the SDK (identity, project/feature/\n * build selection) isn't designed. This shell renders and is exercised\n * manually with mode: \"qa\", but ships with no real Stop wiring until\n * that's decided; the button stays disabled without a callback.\n */\n onStop?: () => void;\n /** Customer mode: fires once the customer accepts the consent screen — the only point capture may start. */\n onAccepted?: () => void;\n /** Customer mode: fires once the report is actually sent, so the caller can stop capture. */\n onSubmitted?: () => void;\n /** Customer mode: fires if the customer declines the consent screen. */\n onDeclined?: () => void;\n}\n\nexport interface ToolbarCounts {\n errors: number;\n warnings: number;\n failedRequests: number;\n}\n\nexport interface ToolbarController {\n /** QA mode only — customer mode stays deliberately simple, no technical counters. */\n setCounts(counts: ToolbarCounts): void;\n destroy(): void;\n}\n\ntype AnnotateState =\n | { kind: \"idle\" }\n | { kind: \"choosing\" }\n | { kind: \"picking\"; mode: \"element\" | \"draw\" }\n | { kind: \"composing\"; rect: AnnotationRect; note: string; saving: boolean; error: string | null };\n\ntype ViewState =\n | { kind: \"consent\" }\n | { kind: \"active\"; minimized: boolean; submitting: boolean; error: string | null; annotate: AnnotateState }\n | { kind: \"sent\" };\n\nfunction formatElapsed(ms: number): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n return `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n}\n\n/**\n * Mounts the toolbar into a shadow-DOM host appended to <body>. One\n * component, two modes: customer mode gates on a consent screen before\n * anything is visible as \"shared\"; QA mode skips straight to the active\n * panel (a tester already knows they're testing).\n *\n * The Note button drives a small state machine (choose mode -> pick on the\n * page -> compose a note -> save) built on the shared\n * @test-tracker/annotation-toolkit package — the same element-select/\n * free-draw/composite primitives this SDK's toolbar uses here are meant to\n * be reusable from the extension's own React toolbar too, per the SDK\n * PRD's \"one annotation component, two hosts\" decision. That extension\n * side isn't wired up yet in this pass — its existing text-only\n * AnnotationComposer keeps working unchanged; retrofitting it to the\n * shared primitives is separate follow-up work, not done here.\n */\nexport function mountToolbar(session: ReprosSession, options: MountToolbarOptions): ToolbarController {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-repros-toolbar\", \"\");\n const shadow = host.attachShadow({ mode: \"open\" });\n\n const styleEl = document.createElement(\"style\");\n styleEl.textContent = TOOLBAR_STYLES;\n shadow.appendChild(styleEl);\n\n const root = document.createElement(\"div\");\n root.className = \"rp-root\";\n shadow.appendChild(root);\n document.body.appendChild(host);\n\n const startedAt = Date.now();\n let state: ViewState =\n options.mode === \"customer\"\n ? { kind: \"consent\" }\n : { kind: \"active\", minimized: false, submitting: false, error: null, annotate: { kind: \"idle\" } };\n let elapsedTimer: ReturnType<typeof setInterval> | undefined;\n let counts: ToolbarCounts = { errors: 0, warnings: 0, failedRequests: 0 };\n let notesCount = 0;\n let cancelPicking: (() => void) | null = null;\n\n function setState(next: ViewState) {\n state = next;\n render();\n }\n\n function ensureElapsedTimer() {\n if (elapsedTimer) return;\n elapsedTimer = setInterval(() => {\n // Updates the clock text node in place rather than calling render() —\n // render() does root.innerHTML = \"\" and rebuilds the whole panel, which\n // every second would tear down and recreate buttons/inputs, causing the\n // toolbar to visibly flicker.\n if (state.kind !== \"active\") return;\n const selector = state.minimized ? \".rp-pill .rp-mono\" : \".rp-elapsed\";\n const el = root.querySelector<HTMLElement>(selector);\n if (el) el.textContent = formatElapsed(Date.now() - startedAt);\n }, 1000);\n }\n\n async function handleSubmit() {\n if (state.kind !== \"active\") return;\n setState({ ...state, submitting: true, error: null });\n const outcome = await submitSession(session.token, session.apiBase);\n if (outcome === \"ok\" || outcome === \"already_submitted\") {\n if (elapsedTimer) clearInterval(elapsedTimer);\n options.onSubmitted?.();\n setState({ kind: \"sent\" });\n return;\n }\n setState({\n kind: \"active\",\n minimized: false,\n submitting: false,\n error: \"Couldn't send that — check your connection and try again.\",\n annotate: { kind: \"idle\" },\n });\n }\n\n function startPicking(mode: \"element\" | \"draw\") {\n if (state.kind !== \"active\") return;\n setState({ ...state, annotate: { kind: \"picking\", mode } });\n host.style.display = \"none\";\n\n const onPick = ({ rect }: { rect: AnnotationRect }) => {\n cancelPicking = null;\n host.style.display = \"\";\n if (state.kind !== \"active\") return;\n setState({ ...state, annotate: { kind: \"composing\", rect, note: \"\", saving: false, error: null } });\n };\n const onCancel = () => {\n cancelPicking = null;\n host.style.display = \"\";\n if (state.kind !== \"active\") return;\n setState({ ...state, annotate: { kind: \"idle\" } });\n };\n cancelPicking = mode === \"element\" ? startElementSelect(onPick, onCancel) : startFreeDraw(onPick, onCancel);\n }\n\n async function handleSaveAnnotation() {\n if (state.kind !== \"active\" || state.annotate.kind !== \"composing\") return;\n const { rect, note } = state.annotate;\n const trimmed = note.trim();\n if (!trimmed) return;\n setState({ ...state, annotate: { ...state.annotate, saving: true, error: null } });\n\n const shot = await captureScreenshot();\n if (!shot) {\n setState({\n ...state,\n annotate: { kind: \"composing\", rect, note, saving: false, error: \"Couldn't capture a screenshot — try again.\" },\n });\n return;\n }\n const composited = await compositeAnnotation(shot.dataUrl, rect, { width: shot.width, height: shot.height });\n const outcome = await submitAnnotation(session.token, session.apiBase, trimmed, composited);\n if (outcome !== \"ok\") {\n setState({\n ...state,\n annotate: { kind: \"composing\", rect, note, saving: false, error: \"Couldn't save that note — try again.\" },\n });\n return;\n }\n notesCount++;\n if (state.kind === \"active\") setState({ ...state, annotate: { kind: \"idle\" } });\n }\n\n function render() {\n root.innerHTML = \"\";\n\n if (state.kind === \"consent\") {\n const card = document.createElement(\"div\");\n card.className = \"rp-consent\";\n card.setAttribute(\"role\", \"dialog\");\n card.setAttribute(\"aria-label\", \"Repros consent\");\n card.innerHTML = `\n <div class=\"rp-title\">Help report this problem?</div>\n <p class=\"rp-body\">Repros will note what you do on this page — clicks, errors, a note you add — so the team can see exactly what went wrong. Nothing is shared until you send it.</p>\n <div class=\"rp-actions\">\n <button class=\"rp-btn rp-btn-secondary\" data-action=\"decline\">Not now</button>\n <button class=\"rp-btn rp-btn-primary\" data-action=\"accept\">Continue</button>\n </div>\n `;\n card.querySelector('[data-action=\"decline\"]')?.addEventListener(\"click\", () => {\n host.remove();\n options.onDeclined?.();\n });\n card.querySelector('[data-action=\"accept\"]')?.addEventListener(\"click\", () => {\n ensureElapsedTimer();\n options.onAccepted?.();\n setState({ kind: \"active\", minimized: false, submitting: false, error: null, annotate: { kind: \"idle\" } });\n });\n root.appendChild(card);\n return;\n }\n\n if (state.kind === \"sent\") {\n const card = document.createElement(\"div\");\n card.className = \"rp-consent\";\n card.innerHTML = `\n <div class=\"rp-title\">Thanks — report sent</div>\n <p class=\"rp-body\">The team can now see what happened here.</p>\n `;\n root.appendChild(card);\n setTimeout(() => host.remove(), 4000);\n return;\n }\n\n ensureElapsedTimer();\n const active = state; // narrow once, locally — state is a closured `let`, so TS won't keep it narrowed past the calls below\n\n if (active.minimized) {\n const pill = document.createElement(\"button\");\n pill.className = \"rp-pill\";\n pill.title = \"Show the Repros toolbar\";\n const errorBadge =\n options.mode === \"qa\" && counts.errors > 0 ? `<span class=\"rp-pill-count rp-tone-error\">${counts.errors}</span>` : \"\";\n pill.innerHTML = `<span class=\"rp-dot\"></span><span class=\"rp-mono\">${formatElapsed(Date.now() - startedAt)}</span>${errorBadge}`;\n pill.addEventListener(\"click\", () => setState({ ...active, minimized: false }));\n root.appendChild(pill);\n return;\n }\n\n const panel = document.createElement(\"div\");\n panel.className = \"rp-panel\";\n panel.setAttribute(\"role\", \"region\");\n panel.setAttribute(\"aria-label\", options.mode === \"customer\" ? \"Repros report\" : \"Repros test session\");\n\n const header = document.createElement(\"div\");\n header.className = \"rp-header\";\n header.innerHTML = `\n <div class=\"rp-header-main\">\n <span class=\"rp-status\"><span class=\"rp-dot\"></span>${options.mode === \"customer\" ? \"RECORDING\" : \"REC\"}</span>\n <span class=\"rp-mono rp-elapsed\">${formatElapsed(Date.now() - startedAt)}</span>\n </div>\n `;\n const minimizeBtn = document.createElement(\"button\");\n minimizeBtn.className = \"rp-icon-btn\";\n minimizeBtn.title = \"Minimize\";\n minimizeBtn.setAttribute(\"aria-label\", \"Minimize toolbar\");\n minimizeBtn.textContent = \"–\";\n minimizeBtn.addEventListener(\"click\", () => setState({ ...active, minimized: true }));\n header.appendChild(minimizeBtn);\n panel.appendChild(header);\n\n if (options.mode === \"qa\") {\n const countsEl = document.createElement(\"div\");\n countsEl.className = \"rp-counts\";\n countsEl.innerHTML = `\n <span class=\"rp-count rp-count-error\"><span class=\"rp-count-value\">${counts.errors}</span>errors</span>\n <span class=\"rp-count rp-count-error\"><span class=\"rp-count-value\">${counts.failedRequests}</span>failed req</span>\n <span class=\"rp-count\"><span class=\"rp-count-value\">${counts.warnings}</span>warnings</span>\n <span class=\"rp-count rp-count-note\"><span class=\"rp-count-value\">${notesCount}</span>notes</span>\n `;\n panel.appendChild(countsEl);\n }\n\n if (active.annotate.kind === \"choosing\") {\n const chooser = document.createElement(\"div\");\n chooser.className = \"rp-actions\";\n chooser.innerHTML = `\n <button class=\"rp-btn rp-btn-secondary\" data-action=\"element\">Select element</button>\n <button class=\"rp-btn rp-btn-secondary\" data-action=\"draw\">Draw box</button>\n `;\n chooser.querySelector('[data-action=\"element\"]')?.addEventListener(\"click\", () => startPicking(\"element\"));\n chooser.querySelector('[data-action=\"draw\"]')?.addEventListener(\"click\", () => startPicking(\"draw\"));\n panel.appendChild(chooser);\n const cancel = document.createElement(\"button\");\n cancel.className = \"rp-btn rp-btn-secondary\";\n cancel.textContent = \"Cancel\";\n cancel.addEventListener(\"click\", () => setState({ ...active, annotate: { kind: \"idle\" } }));\n panel.appendChild(cancel);\n } else if (active.annotate.kind === \"picking\") {\n const hint = document.createElement(\"p\");\n hint.className = \"rp-fine-print\";\n hint.textContent = active.annotate.mode === \"element\" ? \"Click something on the page… (Esc to cancel)\" : \"Drag a box… (Esc to cancel)\";\n panel.appendChild(hint);\n } else if (active.annotate.kind === \"composing\") {\n const compose = active.annotate;\n const wrap = document.createElement(\"div\");\n wrap.innerHTML = `<textarea class=\"rp-fine-print\" style=\"width:100%;min-height:56px;background:var(--rp-surface-2);border:1px solid var(--rp-border);border-radius:8px;padding:6px 8px;color:var(--rp-ink);font:inherit;resize:vertical;\" placeholder=\"What's wrong here?\"></textarea>`;\n const textarea = wrap.querySelector(\"textarea\") as HTMLTextAreaElement;\n textarea.value = compose.note;\n panel.appendChild(wrap);\n\n const composeActions = document.createElement(\"div\");\n composeActions.className = \"rp-actions\";\n const saveBtn = document.createElement(\"button\");\n saveBtn.className = \"rp-btn rp-btn-primary\";\n saveBtn.disabled = compose.saving || !compose.note.trim();\n saveBtn.textContent = compose.saving ? \"Saving…\" : \"Save note\";\n saveBtn.addEventListener(\"click\", () => void handleSaveAnnotation());\n\n // Mutates state in place rather than going through setState — a\n // render() on every keystroke would tear down and recreate this\n // textarea (render() does root.innerHTML = \"\"), losing focus and\n // cursor position. Updates the save button's disabled state directly\n // for the same reason — it has to react to typing without a render.\n textarea.addEventListener(\"input\", () => {\n if (state.kind === \"active\" && state.annotate.kind === \"composing\") state.annotate.note = textarea.value;\n saveBtn.disabled = compose.saving || !textarea.value.trim();\n });\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.className = \"rp-btn rp-btn-secondary\";\n cancelBtn.disabled = compose.saving;\n cancelBtn.textContent = \"Cancel\";\n cancelBtn.addEventListener(\"click\", () => setState({ ...active, annotate: { kind: \"idle\" } }));\n composeActions.append(saveBtn, cancelBtn);\n panel.appendChild(composeActions);\n\n if (compose.error) {\n const err = document.createElement(\"p\");\n err.className = \"rp-error\";\n err.textContent = compose.error;\n panel.appendChild(err);\n }\n } else {\n const actions = document.createElement(\"div\");\n actions.className = \"rp-actions\";\n\n const noteBtn = document.createElement(\"button\");\n noteBtn.className = \"rp-btn rp-btn-secondary\";\n noteBtn.textContent = \"Note\";\n noteBtn.addEventListener(\"click\", () => setState({ ...active, annotate: { kind: \"choosing\" } }));\n actions.appendChild(noteBtn);\n\n if (options.mode === \"customer\") {\n const submitBtn = document.createElement(\"button\");\n submitBtn.className = \"rp-btn rp-btn-primary\";\n submitBtn.disabled = active.submitting;\n submitBtn.textContent = active.submitting ? \"Sending…\" : \"Send report\";\n submitBtn.addEventListener(\"click\", () => void handleSubmit());\n actions.appendChild(submitBtn);\n } else {\n const stopBtn = document.createElement(\"button\");\n stopBtn.className = \"rp-btn rp-btn-stop\";\n stopBtn.disabled = !options.onStop;\n stopBtn.title = options.onStop ? \"Stop recording\" : \"Not wired up yet\";\n stopBtn.textContent = \"Stop\";\n stopBtn.addEventListener(\"click\", () => options.onStop?.());\n actions.appendChild(stopBtn);\n }\n\n panel.appendChild(actions);\n\n if (active.error) {\n const err = document.createElement(\"p\");\n err.className = \"rp-error\";\n err.textContent = active.error;\n panel.appendChild(err);\n }\n }\n\n root.appendChild(panel);\n }\n\n render();\n\n return {\n setCounts(next: ToolbarCounts) {\n counts = next;\n if (state.kind === \"active\") render();\n },\n destroy() {\n if (elapsedTimer) clearInterval(elapsedTimer);\n cancelPicking?.();\n host.remove();\n },\n };\n}\n","import type { ReprosSession } from \"../claim\";\n\nexport interface CaptureCounts {\n errors: number;\n warnings: number;\n failedRequests: number;\n}\n\nexport interface LogEntryPayload {\n type: \"console_warn\" | \"console_error\" | \"window_error\" | \"unhandled_rejection\";\n message: string;\n sourceUrl?: string;\n stackTrace?: string;\n occurredAt: string;\n}\n\nexport interface NetworkRequestPayload {\n method: string;\n url: string;\n statusCode?: number;\n statusText?: string;\n errorText?: string;\n durationMs?: number;\n occurredAt: string;\n}\n\nconst FLUSH_INTERVAL_MS = 5000;\n// Matches the server's own per-batch cap (captureCustomerSessionEvents) —\n// capping client-side too means a burst that would otherwise get the whole\n// batch rejected as oversized instead just drops its tail, quietly.\nconst MAX_BUFFERED = 50;\n\nexport interface CaptureBuffer {\n addLog(entry: LogEntryPayload): void;\n addNetwork(entry: NetworkRequestPayload): void;\n stop(): void;\n}\n\n/**\n * Buffers captured events and flushes them in batches rather than one HTTP\n * call per console line or network request — the claim/submit endpoints\n * are sized for one-off human actions, this one for a live pipeline that\n * can run for as long as the session stays open. Flushes on a timer, and\n * again (via a keepalive fetch, which survives unload in every evergreen\n * browser) when the tab is hidden or unloaded, so a customer closing the\n * tab right after an error doesn't lose it.\n */\nexport function createCaptureBuffer(session: ReprosSession, onCounts: (counts: CaptureCounts) => void): CaptureBuffer {\n let logEntries: LogEntryPayload[] = [];\n let networkRequests: NetworkRequestPayload[] = [];\n const counts: CaptureCounts = { errors: 0, warnings: 0, failedRequests: 0 };\n\n // Error.stack captures the URL as it was when the page's script first\n // parsed — before readClaimToken() rewrites it via history.replaceState\n // — so a stack trace for code that ran early can still carry the raw\n // claim token. Scrub it before it ever leaves the page: this is a\n // single-use-until-submitted credential, and it has no business sitting\n // in stored evidence a developer later reads.\n function redact(text: string): string {\n return text.split(session.token).join(\"[repros-token]\");\n }\n\n function addLog(entry: LogEntryPayload) {\n if (logEntries.length >= MAX_BUFFERED) return;\n logEntries.push({\n ...entry,\n message: redact(entry.message),\n stackTrace: entry.stackTrace ? redact(entry.stackTrace) : entry.stackTrace,\n sourceUrl: entry.sourceUrl ? redact(entry.sourceUrl) : entry.sourceUrl,\n });\n if (entry.type === \"console_warn\") counts.warnings++;\n else counts.errors++;\n onCounts({ ...counts });\n }\n\n function addNetwork(entry: NetworkRequestPayload) {\n if (networkRequests.length >= MAX_BUFFERED) return;\n networkRequests.push({ ...entry, url: redact(entry.url) });\n counts.failedRequests++;\n onCounts({ ...counts });\n }\n\n async function flush() {\n if (logEntries.length === 0 && networkRequests.length === 0) return;\n const batchLog = logEntries;\n const batchNetwork = networkRequests;\n logEntries = [];\n networkRequests = [];\n const url = `${session.apiBase.replace(/\\/+$/, \"\")}/api/customer-sessions/capture`;\n try {\n await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: session.token, logEntries: batchLog, networkRequests: batchNetwork }),\n keepalive: true,\n });\n } catch (err) {\n console.warn(\"[Repros SDK] capture flush failed:\", err);\n }\n }\n\n const timer = setInterval(() => void flush(), FLUSH_INTERVAL_MS);\n const onVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") void flush();\n };\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n window.addEventListener(\"pagehide\", () => void flush());\n\n function stop() {\n clearInterval(timer);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n void flush();\n }\n\n return { addLog, addNetwork, stop };\n}\n","const MAX_MESSAGE_CHARS = 2000;\n\n/**\n * Lean version of the extension's serializeArg (apps/extension/src/injected/serialize.ts)\n * — same Error/circular-reference handling, without that one's printf-style\n * (%s/%d/%c) substitution or DOM-element description. Kept SDK-local\n * rather than shared: worth unifying later, not blocking this on an\n * extraction into packages/shared-types now.\n */\nexport function serializeArg(arg: unknown): string {\n if (typeof arg === \"string\") return arg;\n if (arg instanceof Error) return arg.stack?.startsWith(arg.name) ? arg.stack : `${arg.name}: ${arg.message}\\n${arg.stack ?? \"\"}`;\n if (typeof arg === \"undefined\") return \"undefined\";\n if (typeof arg === \"function\") return `[Function ${arg.name || \"anonymous\"}]`;\n if (typeof arg === \"symbol\" || typeof arg === \"bigint\") return String(arg);\n\n const seen = new WeakSet();\n try {\n return (\n JSON.stringify(arg, (_key, value) => {\n if (typeof value === \"object\" && value !== null) {\n if (seen.has(value)) return \"[Circular]\";\n seen.add(value);\n if (value instanceof Map) return Object.fromEntries(value);\n if (value instanceof Set) return [...value];\n }\n if (typeof value === \"bigint\") return `${value}n`;\n return value;\n }) ?? String(arg)\n );\n } catch {\n try {\n return String(arg);\n } catch {\n return \"[Unserializable]\";\n }\n }\n}\n\nexport function truncate(message: string): string {\n return message.length > MAX_MESSAGE_CHARS ? `${message.slice(0, MAX_MESSAGE_CHARS)}… [truncated]` : message;\n}\n\nexport function serializeConsoleArgs(args: unknown[]): string {\n return truncate(args.map(serializeArg).join(\" \"));\n}\n","import type { CaptureBuffer } from \"./buffer\";\nimport { serializeConsoleArgs } from \"./serialize\";\n\nconst METHOD_TO_TYPE = { warn: \"console_warn\", error: \"console_error\" } as const;\n\n/**\n * Hooks console.warn/console.error only — not .log/.info, which the\n * toolbar has no counter for and would mostly add noise to a customer's\n * report. Always calls the original first so devtools output and any page\n * code that inspects console call counts is unaffected.\n */\nexport function installConsoleCapture(buffer: CaptureBuffer): () => void {\n const originals = { warn: console.warn.bind(console), error: console.error.bind(console) };\n\n (Object.keys(METHOD_TO_TYPE) as (keyof typeof METHOD_TO_TYPE)[]).forEach((method) => {\n console[method] = (...args: unknown[]) => {\n originals[method](...args);\n try {\n const errorArg = args.find((a) => a instanceof Error) as Error | undefined;\n buffer.addLog({\n type: METHOD_TO_TYPE[method],\n message: serializeConsoleArgs(args),\n stackTrace: errorArg?.stack ?? (method === \"error\" ? new Error().stack : undefined),\n sourceUrl: window.location.href,\n occurredAt: new Date().toISOString(),\n });\n } catch {\n // Capture must never break the page's own logging.\n }\n };\n });\n\n return () => {\n console.warn = originals.warn;\n console.error = originals.error;\n };\n}\n","import type { CaptureBuffer } from \"./buffer\";\nimport { serializeArg, truncate } from \"./serialize\";\n\n/** window.onerror and unhandledrejection — mirrors the extension's errorCapture.ts. */\nexport function installErrorCapture(buffer: CaptureBuffer): () => void {\n const onError = (event: ErrorEvent) => {\n const location = event.filename ? `${event.filename}:${event.lineno}:${event.colno}` : undefined;\n buffer.addLog({\n type: \"window_error\",\n message: truncate(event.message || serializeArg(event.error)),\n stackTrace: event.error instanceof Error ? event.error.stack : location ? ` at ${location}` : undefined,\n sourceUrl: window.location.href,\n occurredAt: new Date().toISOString(),\n });\n };\n\n const onRejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n buffer.addLog({\n type: \"unhandled_rejection\",\n message: truncate(reason instanceof Error ? `${reason.name}: ${reason.message}` : serializeArg(reason)),\n stackTrace: reason instanceof Error ? reason.stack : undefined,\n sourceUrl: window.location.href,\n occurredAt: new Date().toISOString(),\n });\n };\n\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n\n return () => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n };\n}\n","import type { CaptureBuffer } from \"./buffer\";\n\n/**\n * Monkey-patches fetch and XMLHttpRequest to capture failed requests only\n * (4xx/5xx or a connection-level error) — same phase-1 scope as the\n * extension's chrome.webRequest-based capture, and deliberately no\n * headers/body (see capture.ts on the dashboard side for why).\n */\nexport function installNetworkCapture(buffer: CaptureBuffer): () => void {\n const restoreFetch = installFetchCapture(buffer);\n const restoreXhr = installXhrCapture(buffer);\n return () => {\n restoreFetch();\n restoreXhr();\n };\n}\n\nfunction requestInfo(input: RequestInfo | URL, init: RequestInit | undefined): { method: string; url: string } {\n if (typeof input === \"string\" || input instanceof URL) {\n return { method: init?.method?.toUpperCase() ?? \"GET\", url: String(input) };\n }\n return { method: (init?.method ?? input.method ?? \"GET\").toUpperCase(), url: input.url };\n}\n\nfunction installFetchCapture(buffer: CaptureBuffer): () => void {\n if (typeof window.fetch !== \"function\") return () => {};\n const originalFetch = window.fetch.bind(window);\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const { method, url } = requestInfo(input, init);\n const startedAt = performance.now();\n try {\n const response = await originalFetch(input, init);\n if (!response.ok) {\n buffer.addNetwork({\n method,\n url,\n statusCode: response.status,\n statusText: response.statusText,\n durationMs: Math.round(performance.now() - startedAt),\n occurredAt: new Date().toISOString(),\n });\n }\n return response;\n } catch (err) {\n buffer.addNetwork({\n method,\n url,\n errorText: err instanceof Error ? err.message : String(err),\n durationMs: Math.round(performance.now() - startedAt),\n occurredAt: new Date().toISOString(),\n });\n throw err;\n }\n };\n\n return () => {\n window.fetch = originalFetch;\n };\n}\n\nconst XHR_STATE = new WeakMap<XMLHttpRequest, { method: string; url: string; startedAt: number }>();\n\nfunction installXhrCapture(buffer: CaptureBuffer): () => void {\n const proto = XMLHttpRequest.prototype;\n const originalOpen = proto.open;\n const originalSend = proto.send;\n\n proto.open = function (this: XMLHttpRequest, method: string, url: string | URL, ...rest: unknown[]) {\n XHR_STATE.set(this, { method: method.toUpperCase(), url: String(url), startedAt: 0 });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalOpen as any).call(this, method, url, ...rest);\n };\n\n proto.send = function (this: XMLHttpRequest, ...args: unknown[]) {\n const state = XHR_STATE.get(this);\n if (state) state.startedAt = performance.now();\n\n const onLoadEnd = () => {\n const current = XHR_STATE.get(this);\n if (!current) return;\n // 0 means a connection-level failure (aborted, network error, CORS) — status never got set.\n if (this.status === 0 || this.status >= 400) {\n buffer.addNetwork({\n method: current.method,\n url: current.url,\n statusCode: this.status || undefined,\n statusText: this.statusText || undefined,\n errorText: this.status === 0 ? \"Network error\" : undefined,\n durationMs: Math.round(performance.now() - current.startedAt),\n occurredAt: new Date().toISOString(),\n });\n }\n this.removeEventListener(\"loadend\", onLoadEnd);\n XHR_STATE.delete(this);\n };\n this.addEventListener(\"loadend\", onLoadEnd);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalSend as any).apply(this, args);\n };\n\n return () => {\n proto.open = originalOpen;\n proto.send = originalSend;\n };\n}\n","import type { ReprosSession } from \"../claim\";\nimport { createCaptureBuffer, type CaptureCounts } from \"./buffer\";\nimport { installConsoleCapture } from \"./console\";\nimport { installErrorCapture } from \"./errors\";\nimport { installNetworkCapture } from \"./network\";\n\nexport type { CaptureCounts };\nexport { captureScreenshot } from \"./screenshot\";\n\n/**\n * Wires console/window-error/network capture into one buffer for the\n * session's lifetime. Returns a stop() that restores every patched global\n * (console.warn/error, window.fetch, XMLHttpRequest.prototype) and flushes\n * whatever's still buffered — called once the session ends (submitted),\n * since there's no point capturing a page a customer already sent.\n */\nexport function startCapture(session: ReprosSession, onCounts: (counts: CaptureCounts) => void): () => void {\n const buffer = createCaptureBuffer(session, onCounts);\n const restoreConsole = installConsoleCapture(buffer);\n const restoreErrors = installErrorCapture(buffer);\n const restoreNetwork = installNetworkCapture(buffer);\n\n return () => {\n restoreConsole();\n restoreErrors();\n restoreNetwork();\n buffer.stop();\n };\n}\n"],"mappings":"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","names":["session","session","init","session"]}
package/dist/index.js CHANGED
@@ -405,7 +405,10 @@ function mountToolbar(session2, options) {
405
405
  function ensureElapsedTimer() {
406
406
  if (elapsedTimer) return;
407
407
  elapsedTimer = setInterval(() => {
408
- if (state.kind === "active" && !state.minimized) render();
408
+ if (state.kind !== "active") return;
409
+ const selector = state.minimized ? ".rp-pill .rp-mono" : ".rp-elapsed";
410
+ const el = root.querySelector(selector);
411
+ if (el) el.textContent = formatElapsed(Date.now() - startedAt);
409
412
  }, 1e3);
410
413
  }
411
414
  async function handleSubmit() {