- {running ? (
-
-
retrieving · reranking · generating
-
-
- ) : null}
-
-
- {data.flags.map((f, i) => (
-
- {f.kind === "ok" ? "✓" : "!"} {f.label}
-
- ))}
-
-
- {data.body.map((sec, si) => (
-
-
{sec.h}
-
- {sec.p.map((seg, i) => {
- const content = seg.code
- ? {seg.t}
- : seg.t;
- if (seg.c) {
- return { setHovered(id); if (id) setActiveEv(id); }}
- onClick={(id) => setActiveEv(id)}>{content} ;
- }
- return {content} ;
- })}
-
-
- ))}
-
-
-
Suggested next actions
-
- ∂ Derive achievability
- ▢ Quiz me on this section
- ◉ Oral-exam follow-ups
-
-
-
-
-
-
- ↺ Re-run
- + Add to notebook
-
-
grounded · {data.tokensOut} tokens
-
-
- );
-}
-
-// ---------- Evidence drawer ----------
-function EvidenceDrawer({ items, active, setActive, hovered, setHovered }) {
- return (
-
-
-
-
-
-
- {items.map((ev, i) => (
-
setActive(ev.id)}
- onMouseEnter={() => setHovered(ev.id)}
- onMouseLeave={() => setHovered(null)}
- className={"b-thin-1 rounded-md p-3 cursor-pointer transition-all " +
- (active === ev.id ? "bg-blue-dark/8 dark:bg-white/10" : "hover:bg-blue-dark/5 dark:hover:bg-white/5")}
- style={{border:"1px solid currentColor"}}>
-
- [{i+1}]
- {ev.docName}
- {ev.kind}
- {ev.lang === "it" ? IT → EN : null}
-
-
- {ev.anchor} · score {ev.score.toFixed(3)} · rerank {ev.rerank.toFixed(3)}
-
-
{ev.preview}
-
- → open in viewer
- ⌖ pin
- ⊘ exclude
- id · {ev.id}
-
-
- ))}
-
-
-
-
- );
-}
-
-function ScoreBars({ items }) {
- return (
-
- {items.map((ev, i) => (
-
-
[{i+1}]
-
-
{ev.rerank.toFixed(3)}
-
- ))}
-
- retrieval
- rerank
-
-
- );
-}
-
-function Bar({ label, pct }) {
- return (
-
-
- {label}
- {pct}%
-
-
-
- );
-}
-
-function InspectDrawer() {
- return (
-
- );
-}
-
-function Pre({ title, lines }) {
- return (
-
-
{title}
-
- {lines.join("\n")}
-
-
- );
-}
-
-Object.assign(window, { StudyScreen });
diff --git a/BitPolito-Academy-UI/src/screens-workspace.jsx b/BitPolito-Academy-UI/src/screens-workspace.jsx
deleted file mode 100644
index 35d48ff..0000000
--- a/BitPolito-Academy-UI/src/screens-workspace.jsx
+++ /dev/null
@@ -1,460 +0,0 @@
-// Course workspace + Source viewer.
-
-const { useState: _u2 } = React;
-
-function WorkspaceScreen({ onOpenDoc, onStartStudy }) {
- const D = window.ACADEMY_DATA;
- const [docs, setDocs] = _u2(D.documents);
- const [selected, setSelected] = _u2(docs[0].id);
- const [filter, setFilter] = _u2("all");
- const [dragOver, setDragOver] = _u2(false);
-
- // Light simulation of upload progress on load
- React.useEffect(() => {
- const t = setInterval(() => {
- setDocs(prev => prev.map(d => {
- if (d.state === "uploading" && d.progress < 1) {
- const next = Math.min(1, (d.progress || 0) + 0.07);
- return next >= 1 ? { ...d, state: "processing", progress: 0.05, stage: "parse → chunk" } : { ...d, progress: next };
- }
- if (d.state === "processing" && d.progress < 1) {
- const next = Math.min(1, (d.progress || 0) + 0.04);
- return next >= 1
- ? { ...d, state: "indexed", progress: 1, chunks: 200 + Math.round(Math.random() * 200), parser: { ok: 0.95, ocr: false, lang: "en" } }
- : { ...d, progress: next };
- }
- return d;
- }));
- }, 700);
- return () => clearInterval(t);
- }, []);
-
- const filtered = docs.filter(d => {
- if (filter === "all") return true;
- if (filter === "open") return d.state !== "indexed";
- return d.state === filter;
- });
- const sel = docs.find(d => d.id === selected) || docs[0];
-
- const handleRetry = (id) => setDocs(prev => prev.map(d => d.id === id ? { ...d, state: "uploading", progress: 0.1, error: null } : d));
- const handleClear = (id) => setDocs(prev => prev.filter(d => d.id !== id));
-
- // Course header summary
- const c = D.courses[0];
- const counts = {
- indexed: docs.filter(d => d.state === "indexed").length,
- processing: docs.filter(d => d.state === "processing" || d.state === "uploading").length,
- failed: docs.filter(d => d.state === "failed").length,
- };
-
- return (
-
- {/* Course header */}
-
-
-
-
- {c.code}
-
{c.title}
-
-
{c.term} · {c.lecturer}
-
-
-
-
- 0} />
- 0} />
-
-
- onOpenDoc(sel.id)}>Open viewer
- onStartStudy()}>
- Study →
-
-
-
-
- {/* Two columns */}
-
- {/* LEFT — upload + document list */}
-
-
{ e.preventDefault(); setDragOver(true); }}
- onDragLeave={() => setDragOver(false)}
- onDrop={(e) => {
- e.preventDefault(); setDragOver(false);
- const fakeName = "Drop_" + (docs.length+1) + ".pdf";
- setDocs(prev => [...prev, {
- id: "doc-" + Date.now(), name: fakeName, kind: "slides",
- pages: 20, sizeKB: 1800, uploadedAt: "now", state: "uploading", progress: 0.05,
- }]);
- }}>
-
-
-
- ↑
-
-
-
Drop files here, or paste a URL
-
- Files are parsed locally · structure-preserving · embedded with qvac
-
-
-
{
- setDocs(prev => [...prev, {
- id: "doc-" + Date.now(), name: "Lecture-supplement-" + (prev.length+1) + ".pdf", kind: "slides",
- pages: 20, sizeKB: 1800, uploadedAt: "now", state: "uploading", progress: 0.1
- }]);
- }}>Choose files
-
-
-
- {/* Filter bar */}
-
- {[
- { id: "all", label: "All" },
- { id: "indexed", label: "Indexed" },
- { id: "processing", label: "Processing" },
- { id: "failed", label: "Failed" },
- { id: "open", label: "Open · needs attention" },
- ].map(f => (
- setFilter(f.id)}
- className={"font-mono text-[11px] tracking-[0.18em] uppercase px-3 h-8 rounded-md transition-all " +
- (filter === f.id ? "bg-blue-dark text-white dark:bg-white dark:text-blue-dark" : "b-thin")}>
- {f.label}
-
- ))}
- {filtered.length} shown
-
-
- {/* Document list */}
-
-
-
Document
-
Kind
-
Size · pages
-
Status
-
-
- {filtered.map(d => (
-
setSelected(d.id)}
- onOpen={() => onOpenDoc(d.id)}
- onRetry={() => handleRetry(d.id)}
- onClear={() => handleClear(d.id)} />
- ))}
- {filtered.length === 0 ? (
- No documents in this view.
- ) : null}
-
-
-
- {/* RIGHT — document detail panel */}
-
-
-
-
Document detail
-
{sel.name}
-
- {sel.kind} · {sel.pages || "—"} pages · {(sel.sizeKB/1024).toFixed(1)} MB · {sel.uploadedAt}
-
-
-
-
-
-
-
- {sel.stage ?
stage · {sel.stage}
: null}
-
-
- {sel.state === "failed" ? (
-
-
Error
-
{sel.error}
-
- handleRetry(sel.id)}>Retry parse
- handleClear(sel.id)}>Clear
-
-
- ) : null}
-
- {sel.parser ? (
-
- ) : null}
-
-
-
-
- onOpenDoc(sel.id)}>Open in viewer →
- Use in study
- id · {sel.id}
-
-
-
-
-
-
- );
-}
-
-function Stat2({ n, k, warn, err }) {
- const cls = err ? "text-err" : warn ? "text-warn" : "";
- return (
-
- );
-}
-
-function KV({ k, v }) {
- return (
-
- );
-}
-
-function DocRow({ d, selected, onSelect, onOpen, onRetry, onClear }) {
- return (
-
-
-
-
-
{d.name}
-
- {d.uploadedAt} · {d.parser ? `${d.parser.lang} · ${d.parser.ocr ? "OCR" : "native"}` : "queued"}
-
-
-
-
{d.kind}
-
- {(d.sizeKB/1024).toFixed(1)} MB · {d.pages || "—"}p
-
-
-
- {(d.state === "uploading" || d.state === "processing") && d.progress != null
- ?
: null}
-
-
{ e.stopPropagation(); onOpen(); }}
- className="font-mono text-sm opacity-60 hover:opacity-100">→
-
- );
-}
-
-function Lifecycle({ state, progress }) {
- const stages = ["queued", "uploading", "processing", "indexed"];
- const failed = state === "failed";
- const idx = failed ? -1 : stages.indexOf(state === "uploaded" ? "processing" : state);
- return (
-
- {stages.map((s, i) => {
- const done = !failed && i < idx;
- const here = !failed && i === idx;
- return (
-
-
- {here && progress != null
- ?
- : null}
-
{s}
-
- {i < stages.length - 1 ? › : null}
-
- );
- })}
- {failed ?
FAILED : null}
-
- );
-}
-
-function ParsedPreview() {
- const D = window.ACADEMY_DATA;
- const p = D.openDoc.parsed;
- return (
-
-
{p.title}
-
- {p.bullets.slice(0, 2).map((b, i) => {b} )}
-
-
{p.formula}
-
- );
-}
-
-// =================== Source Viewer ===================
-
-function SourceViewer({ onStartStudy }) {
- const D = window.ACADEMY_DATA;
- const doc = D.openDoc;
- const [page, setPage] = _u2(doc.activePage);
- const [hovered, setHovered] = _u2(null);
-
- return (
-
- {/* Doc header */}
-
-
-
-
{doc.name}
-
{doc.pages} pages · indexed · 318 chunks
-
-
- setPage(p => Math.max(1, p-1))} className="px-3 h-8 mono">‹
- page {page} / {doc.pages}
- setPage(p => Math.min(doc.pages, p+1))} className="px-3 h-8 mono">›
-
-
Use in study →
-
-
- {/* 3-pane: outline · viewer · meta */}
-
- {/* Outline */}
-
-
-
- {doc.outline.map(o => (
-
- setPage(o.page)}
- className={"w-full flex items-baseline gap-2 px-2 py-1.5 rounded-sm text-left text-[13px] " +
- (page >= o.page && (o.page === doc.outline[doc.outline.length-1].page || page < (doc.outline[doc.outline.indexOf(o)+1]?.page ?? 999))
- ? "bg-blue-dark text-white dark:bg-white dark:text-blue-dark" : "hover:bg-blue-dark/5 dark:hover:bg-white/10")}>
- p.{o.page}
- {o.label}
-
-
- ))}
-
-
-
-
-
- {Array.from({length: 9}).map((_, i) => {
- const n = page - 4 + i;
- if (n < 1 || n > doc.pages) return
;
- return (
-
setPage(n)}
- className={"b-thin-1 rounded-sm aspect-[3/4] stripes flex items-end justify-center pb-1 transition-all " +
- (n === page ? "ring-2 ring-blue-dark dark:ring-white" : "")}
- style={{border:"1px solid currentColor"}}>
- {n}
-
- );
- })}
-
-
-
-
- {/* Slide canvas */}
-
-
-
-
Slide {page} · §4
-
{doc.parsed.title}
-
-
parser · 97% · native
-
-
-
-
-
{doc.parsed.title}
-
- {doc.parsed.bullets.map((b, i) => (
-
- ▸
- {b}
-
- ))}
-
-
- {doc.parsed.formula}
-
-
{doc.parsed.cap}
-
-
-
-
-
-
- {/* Meta / structure */}
-
-
-
- );
-}
-
-function Anchor({ n, caption, active }) {
- return (
-
- );
-}
-
-function Tree({ label, v }) {
- return (
-
- └
- {label}
- {v}
-
- );
-}
-
-Object.assign(window, { WorkspaceScreen, SourceViewer });
diff --git a/BitPolito-Academy-UI/tweaks-panel.jsx b/BitPolito-Academy-UI/tweaks-panel.jsx
deleted file mode 100644
index 5f8f95a..0000000
--- a/BitPolito-Academy-UI/tweaks-panel.jsx
+++ /dev/null
@@ -1,425 +0,0 @@
-
-// tweaks-panel.jsx
-// Reusable Tweaks shell + form-control helpers.
-//
-// Owns the host protocol (listens for __activate_edit_mode / __deactivate_edit_mode,
-// posts __edit_mode_available / __edit_mode_set_keys / __edit_mode_dismissed) so
-// individual prototypes don't re-roll it. Ships a consistent set of controls so you
-// don't hand-draw
, segmented radios, steppers, etc.
-//
-// Usage (in an HTML file that loads React + Babel):
-//
-// const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
-// "primaryColor": "#D97757",
-// "fontSize": 16,
-// "density": "regular",
-// "dark": false
-// }/*EDITMODE-END*/;
-//
-// function App() {
-// const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
-// return (
-//
-// Hello
-//
-//
-// setTweak('fontSize', v)} />
-// setTweak('density', v)} />
-//
-// setTweak('primaryColor', v)} />
-// setTweak('dark', v)} />
-//
-//
-// );
-// }
-//
-// ─────────────────────────────────────────────────────────────────────────────
-
-const __TWEAKS_STYLE = `
- .twk-panel{position:fixed;right:16px;bottom:16px;z-index:2147483646;width:280px;
- max-height:calc(100vh - 32px);display:flex;flex-direction:column;
- background:rgba(250,249,247,.78);color:#29261b;
- -webkit-backdrop-filter:blur(24px) saturate(160%);backdrop-filter:blur(24px) saturate(160%);
- border:.5px solid rgba(255,255,255,.6);border-radius:14px;
- box-shadow:0 1px 0 rgba(255,255,255,.5) inset,0 12px 40px rgba(0,0,0,.18);
- font:11.5px/1.4 ui-sans-serif,system-ui,-apple-system,sans-serif;overflow:hidden}
- .twk-hd{display:flex;align-items:center;justify-content:space-between;
- padding:10px 8px 10px 14px;cursor:move;user-select:none}
- .twk-hd b{font-size:12px;font-weight:600;letter-spacing:.01em}
- .twk-x{appearance:none;border:0;background:transparent;color:rgba(41,38,27,.55);
- width:22px;height:22px;border-radius:6px;cursor:default;font-size:13px;line-height:1}
- .twk-x:hover{background:rgba(0,0,0,.06);color:#29261b}
- .twk-body{padding:2px 14px 14px;display:flex;flex-direction:column;gap:10px;
- overflow-y:auto;overflow-x:hidden;min-height:0;
- scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.15) transparent}
- .twk-body::-webkit-scrollbar{width:8px}
- .twk-body::-webkit-scrollbar-track{background:transparent;margin:2px}
- .twk-body::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:4px;
- border:2px solid transparent;background-clip:content-box}
- .twk-body::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25);
- border:2px solid transparent;background-clip:content-box}
- .twk-row{display:flex;flex-direction:column;gap:5px}
- .twk-row-h{flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
- .twk-lbl{display:flex;justify-content:space-between;align-items:baseline;
- color:rgba(41,38,27,.72)}
- .twk-lbl>span:first-child{font-weight:500}
- .twk-val{color:rgba(41,38,27,.5);font-variant-numeric:tabular-nums}
-
- .twk-sect{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
- color:rgba(41,38,27,.45);padding:10px 0 0}
- .twk-sect:first-child{padding-top:0}
-
- .twk-field{appearance:none;width:100%;height:26px;padding:0 8px;
- border:.5px solid rgba(0,0,0,.1);border-radius:7px;
- background:rgba(255,255,255,.6);color:inherit;font:inherit;outline:none}
- .twk-field:focus{border-color:rgba(0,0,0,.25);background:rgba(255,255,255,.85)}
- select.twk-field{padding-right:22px;
- background-image:url("data:image/svg+xml;utf8,
");
- background-repeat:no-repeat;background-position:right 8px center}
-
- .twk-slider{appearance:none;-webkit-appearance:none;width:100%;height:4px;margin:6px 0;
- border-radius:999px;background:rgba(0,0,0,.12);outline:none}
- .twk-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
- width:14px;height:14px;border-radius:50%;background:#fff;
- border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
- .twk-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;
- background:#fff;border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
-
- .twk-seg{position:relative;display:flex;padding:2px;border-radius:8px;
- background:rgba(0,0,0,.06);user-select:none}
- .twk-seg-thumb{position:absolute;top:2px;bottom:2px;border-radius:6px;
- background:rgba(255,255,255,.9);box-shadow:0 1px 2px rgba(0,0,0,.12);
- transition:left .15s cubic-bezier(.3,.7,.4,1),width .15s}
- .twk-seg.dragging .twk-seg-thumb{transition:none}
- .twk-seg button{appearance:none;position:relative;z-index:1;flex:1;border:0;
- background:transparent;color:inherit;font:inherit;font-weight:500;min-height:22px;
- border-radius:6px;cursor:default;padding:4px 6px;line-height:1.2;
- overflow-wrap:anywhere}
-
- .twk-toggle{position:relative;width:32px;height:18px;border:0;border-radius:999px;
- background:rgba(0,0,0,.15);transition:background .15s;cursor:default;padding:0}
- .twk-toggle[data-on="1"]{background:#34c759}
- .twk-toggle i{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
- background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .15s}
- .twk-toggle[data-on="1"] i{transform:translateX(14px)}
-
- .twk-num{display:flex;align-items:center;height:26px;padding:0 0 0 8px;
- border:.5px solid rgba(0,0,0,.1);border-radius:7px;background:rgba(255,255,255,.6)}
- .twk-num-lbl{font-weight:500;color:rgba(41,38,27,.6);cursor:ew-resize;
- user-select:none;padding-right:8px}
- .twk-num input{flex:1;min-width:0;height:100%;border:0;background:transparent;
- font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0 8px 0 0;
- outline:none;color:inherit;-moz-appearance:textfield}
- .twk-num input::-webkit-inner-spin-button,.twk-num input::-webkit-outer-spin-button{
- -webkit-appearance:none;margin:0}
- .twk-num-unit{padding-right:8px;color:rgba(41,38,27,.45)}
-
- .twk-btn{appearance:none;height:26px;padding:0 12px;border:0;border-radius:7px;
- background:rgba(0,0,0,.78);color:#fff;font:inherit;font-weight:500;cursor:default}
- .twk-btn:hover{background:rgba(0,0,0,.88)}
- .twk-btn.secondary{background:rgba(0,0,0,.06);color:inherit}
- .twk-btn.secondary:hover{background:rgba(0,0,0,.1)}
-
- .twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:22px;
- border:.5px solid rgba(0,0,0,.1);border-radius:6px;padding:0;cursor:default;
- background:transparent;flex-shrink:0}
- .twk-swatch::-webkit-color-swatch-wrapper{padding:0}
- .twk-swatch::-webkit-color-swatch{border:0;border-radius:5.5px}
- .twk-swatch::-moz-color-swatch{border:0;border-radius:5.5px}
-`;
-
-// ── useTweaks ───────────────────────────────────────────────────────────────
-// Single source of truth for tweak values. setTweak persists via the host
-// (__edit_mode_set_keys → host rewrites the EDITMODE block on disk).
-function useTweaks(defaults) {
- const [values, setValues] = React.useState(defaults);
- // Accepts either setTweak('key', value) or setTweak({ key: value, ... }) so a
- // useState-style call doesn't write a "[object Object]" key into the persisted
- // JSON block.
- const setTweak = React.useCallback((keyOrEdits, val) => {
- const edits = typeof keyOrEdits === 'object' && keyOrEdits !== null
- ? keyOrEdits : { [keyOrEdits]: val };
- setValues((prev) => ({ ...prev, ...edits }));
- window.parent.postMessage({ type: '__edit_mode_set_keys', edits }, '*');
- }, []);
- return [values, setTweak];
-}
-
-// ── TweaksPanel ─────────────────────────────────────────────────────────────
-// Floating shell. Registers the protocol listener BEFORE announcing
-// availability — if the announce ran first, the host's activate could land
-// before our handler exists and the toolbar toggle would silently no-op.
-// The close button posts __edit_mode_dismissed so the host's toolbar toggle
-// flips off in lockstep; the host echoes __deactivate_edit_mode back which
-// is what actually hides the panel.
-function TweaksPanel({ title = 'Tweaks', children }) {
- const [open, setOpen] = React.useState(false);
- const dragRef = React.useRef(null);
- const offsetRef = React.useRef({ x: 16, y: 16 });
- const PAD = 16;
-
- const clampToViewport = React.useCallback(() => {
- const panel = dragRef.current;
- if (!panel) return;
- const w = panel.offsetWidth, h = panel.offsetHeight;
- const maxRight = Math.max(PAD, window.innerWidth - w - PAD);
- const maxBottom = Math.max(PAD, window.innerHeight - h - PAD);
- offsetRef.current = {
- x: Math.min(maxRight, Math.max(PAD, offsetRef.current.x)),
- y: Math.min(maxBottom, Math.max(PAD, offsetRef.current.y)),
- };
- panel.style.right = offsetRef.current.x + 'px';
- panel.style.bottom = offsetRef.current.y + 'px';
- }, []);
-
- React.useEffect(() => {
- if (!open) return;
- clampToViewport();
- if (typeof ResizeObserver === 'undefined') {
- window.addEventListener('resize', clampToViewport);
- return () => window.removeEventListener('resize', clampToViewport);
- }
- const ro = new ResizeObserver(clampToViewport);
- ro.observe(document.documentElement);
- return () => ro.disconnect();
- }, [open, clampToViewport]);
-
- React.useEffect(() => {
- const onMsg = (e) => {
- const t = e?.data?.type;
- if (t === '__activate_edit_mode') setOpen(true);
- else if (t === '__deactivate_edit_mode') setOpen(false);
- };
- window.addEventListener('message', onMsg);
- window.parent.postMessage({ type: '__edit_mode_available' }, '*');
- return () => window.removeEventListener('message', onMsg);
- }, []);
-
- const dismiss = () => {
- setOpen(false);
- window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*');
- };
-
- const onDragStart = (e) => {
- const panel = dragRef.current;
- if (!panel) return;
- const r = panel.getBoundingClientRect();
- const sx = e.clientX, sy = e.clientY;
- const startRight = window.innerWidth - r.right;
- const startBottom = window.innerHeight - r.bottom;
- const move = (ev) => {
- offsetRef.current = {
- x: startRight - (ev.clientX - sx),
- y: startBottom - (ev.clientY - sy),
- };
- clampToViewport();
- };
- const up = () => {
- window.removeEventListener('mousemove', move);
- window.removeEventListener('mouseup', up);
- };
- window.addEventListener('mousemove', move);
- window.addEventListener('mouseup', up);
- };
-
- if (!open) return null;
- return (
- <>
-
-
-
- {title}
- e.stopPropagation()}
- onClick={dismiss}>✕
-
-
{children}
-
- >
- );
-}
-
-// ── Layout helpers ──────────────────────────────────────────────────────────
-
-function TweakSection({ label, children }) {
- return (
- <>
-
{label}
- {children}
- >
- );
-}
-
-function TweakRow({ label, value, children, inline = false }) {
- return (
-
-
- {label}
- {value != null && {value} }
-
- {children}
-
- );
-}
-
-// ── Controls ────────────────────────────────────────────────────────────────
-
-function TweakSlider({ label, value, min = 0, max = 100, step = 1, unit = '', onChange }) {
- return (
-
- onChange(Number(e.target.value))} />
-
- );
-}
-
-function TweakToggle({ label, value, onChange }) {
- return (
-
-
{label}
-
onChange(!value)}>
-
- );
-}
-
-function TweakRadio({ label, value, options, onChange }) {
- const trackRef = React.useRef(null);
- const [dragging, setDragging] = React.useState(false);
- const opts = options.map((o) => (typeof o === 'object' ? o : { value: o, label: o }));
- const idx = Math.max(0, opts.findIndex((o) => o.value === value));
- const n = opts.length;
-
- // The active value is read by pointer-move handlers attached for the lifetime
- // of a drag — ref it so a stale closure doesn't fire onChange for every move.
- const valueRef = React.useRef(value);
- valueRef.current = value;
-
- const segAt = (clientX) => {
- const r = trackRef.current.getBoundingClientRect();
- const inner = r.width - 4;
- const i = Math.floor(((clientX - r.left - 2) / inner) * n);
- return opts[Math.max(0, Math.min(n - 1, i))].value;
- };
-
- const onPointerDown = (e) => {
- setDragging(true);
- const v0 = segAt(e.clientX);
- if (v0 !== valueRef.current) onChange(v0);
- const move = (ev) => {
- if (!trackRef.current) return;
- const v = segAt(ev.clientX);
- if (v !== valueRef.current) onChange(v);
- };
- const up = () => {
- setDragging(false);
- window.removeEventListener('pointermove', move);
- window.removeEventListener('pointerup', up);
- };
- window.addEventListener('pointermove', move);
- window.addEventListener('pointerup', up);
- };
-
- return (
-
-
-
- {opts.map((o) => (
-
- {o.label}
-
- ))}
-
-
- );
-}
-
-function TweakSelect({ label, value, options, onChange }) {
- return (
-
- onChange(e.target.value)}>
- {options.map((o) => {
- const v = typeof o === 'object' ? o.value : o;
- const l = typeof o === 'object' ? o.label : o;
- return {l} ;
- })}
-
-
- );
-}
-
-function TweakText({ label, value, placeholder, onChange }) {
- return (
-
- onChange(e.target.value)} />
-
- );
-}
-
-function TweakNumber({ label, value, min, max, step = 1, unit = '', onChange }) {
- const clamp = (n) => {
- if (min != null && n < min) return min;
- if (max != null && n > max) return max;
- return n;
- };
- const startRef = React.useRef({ x: 0, val: 0 });
- const onScrubStart = (e) => {
- e.preventDefault();
- startRef.current = { x: e.clientX, val: value };
- const decimals = (String(step).split('.')[1] || '').length;
- const move = (ev) => {
- const dx = ev.clientX - startRef.current.x;
- const raw = startRef.current.val + dx * step;
- const snapped = Math.round(raw / step) * step;
- onChange(clamp(Number(snapped.toFixed(decimals))));
- };
- const up = () => {
- window.removeEventListener('pointermove', move);
- window.removeEventListener('pointerup', up);
- };
- window.addEventListener('pointermove', move);
- window.addEventListener('pointerup', up);
- };
- return (
-
- {label}
- onChange(clamp(Number(e.target.value)))} />
- {unit && {unit} }
-
- );
-}
-
-function TweakColor({ label, value, onChange }) {
- return (
-
-
{label}
-
onChange(e.target.value)} />
-
- );
-}
-
-function TweakButton({ label, onClick, secondary = false }) {
- return (
-
{label}
- );
-}
-
-Object.assign(window, {
- useTweaks, TweaksPanel, TweakSection, TweakRow,
- TweakSlider, TweakToggle, TweakRadio, TweakSelect,
- TweakText, TweakNumber, TweakColor, TweakButton,
-});
diff --git a/Caddyfile b/Caddyfile
new file mode 100644
index 0000000..59721cc
--- /dev/null
+++ b/Caddyfile
@@ -0,0 +1,15 @@
+# Reverse proxy for Bitcoin Academy.
+# Routes /api/* to the FastAPI backend and everything else to Next.js.
+#
+# For production with automatic TLS, replace ":80" with your domain:
+# academy.example.com {
+# ...
+# }
+# Caddy will obtain and renew Let's Encrypt certificates automatically.
+
+:80 {
+ @api path /api/*
+ reverse_proxy @api api:8000
+
+ reverse_proxy web:3000
+}
diff --git a/README.md b/README.md
index ad3fcca..bff40a6 100644
--- a/README.md
+++ b/README.md
@@ -1,256 +1,175 @@
# BitPolito Academy
-Open-source educational platform for Bitcoin study. Turns course materials (slides, textbooks, past exams) into an AI workspace with RAG tutoring, source-anchored citations, and 8 study actions.
+[](https://github.com/BitPolito/bitcoin-academy/actions/workflows/ci.yml)
+
+Educational platform for Bitcoin study built at BitPolito. Upload slides, PDFs, or textbooks and interact with them through eight study actions: **explain**, **summarize**, **retrieve**, **open\_questions**, **quiz**, **oral**, **derive**, **compare**.
+
+Everything runs locally — no external API keys needed. The retrieval pipeline uses QVAC dense search (GTE-Large FP16) combined with BM25, cross-encoder reranking, MMR diversity, and optional Qwen3-4B for answer generation. A semantic cache (fastembed + Redis) avoids recomputing identical or near-identical queries.
---
-## Quick Start
+## Requirements
+
+| Dependency | Version |
+|---|---|
+| Node.js | ≥ 22.17 |
+| Python | 3.11 |
+| uv | latest |
+| Redis | ≥ 7 |
-### Prerequisites
+Redis is optional in development but required in production for background ingestion, semantic cache, token blacklist, and account lockout. SQLite is used in development — no PostgreSQL setup needed.
-| Requirement | Version | Notes |
-|---|---|---|
-| Node.js | **≥ 22.17** | Required by `@qvac/sdk` (bare runtime shims) |
-| Python | **3.11** | FastAPI backend and ingestion pipeline |
-| uv | latest | Recommended package manager — [install](https://docs.astral.sh/uv/getting-started/installation/) |
-| Redis | **≥ 7** | Optional — enables async ARQ task queue (`brew install redis`) |
-| Disk | ~2 GB | QVAC embedding model (~670 MB, downloaded on first run) |
-| RAM | ≥ 8 GB | For local LLM inference (optional) |
+**Disk and RAM:** plan for ~4 GB of disk (embedding model ~670 MB + Qwen3-4B ~2.5 GB, downloaded on first run) and at least 8 GB RAM (~5 GB at runtime with the LLM loaded). 16 GB is more comfortable.
-> No PostgreSQL needed in development: the backend uses **SQLite** (`services/ai/bitcoin_academy.db`).
+If you're on a machine with less than 8 GB free, set `QVAC_LLM_ENABLED=false`. The system will run in retrieval-only mode (~670 MB total): all study actions still return source passages, but there's no prose generation.
-### Start
+---
+
+## Quick Start (Docker)
```bash
-chmod +x start-dev.sh
-./start-dev.sh
+# 1. Create root .env with database credentials
+echo "DATABASE_URL=postgresql://bitcoin_academy:bitcoin_academy@postgres:5432/bitcoin_academy" > .env
+
+# 2. Copy and configure service env files
+cp services/ai/.env.example services/ai/.env
+cp apps/web/.env.example apps/web/.env.local
+
+# 3. Start everything
+docker compose up --build
```
-The script:
-- **With uv** (recommended): runs `uv sync` — near-instant when the lockfile is unchanged
-- **Without uv**: uses pip with a hash-check to skip installs when `requirements.txt` hasn't changed
-- Auto-starts Redis if `redis-server` is found, then launches the ARQ worker
-- Backend health check is synchronous (30 s); QVAC health check runs in background (up to 300 s — first model load takes 2-5 min)
-- Seeds the database with test users, then starts the Next.js frontend (Turbopack)
+`docker compose up` automatically merges `docker-compose.yml` with `docker-compose.override.yml`, which adds source mounts, hot reload, and exposed ports. To run the production base without the dev overrides:
-| Service | URL |
+```bash
+docker compose -f docker-compose.yml up --build
+```
+
+| Service | Dev URL |
|---|---|
-| Frontend |
|
-| Backend API | |
-| QVAC service | |
-| Swagger UI | |
+| Frontend | http://localhost:3000 (through Caddy on :80 in prod) |
+| Backend API | http://localhost:8000 (through Caddy on /api/* in prod) |
+| Reverse proxy | http://localhost:80 |
+| QVAC service | http://localhost:3001 |
+| Interactive API docs | http://localhost:8000/docs (dev only) |
-**Development credentials (seeded automatically):**
+Default development accounts created automatically:
| Role | Email | Password |
|---|---|---|
| Admin | `admin@bitpolito.it` | `DevAdmin@2024!Secure` |
| Student | `student@bitpolito.it` | `DevStudent@2024!Learn` |
-### Manual start
+---
+
+## Manual Start (Development)
```bash
# Frontend
cd apps/web && npm install && npm run dev
-# Backend (with uv — recommended)
+# Backend — run setup once, then start the server
cd services/ai
-uv sync # creates .venv and installs deps from uv.lock
-uv run python -m app.db.init_db # create DB and seed users
+cp .env.example .env # fill in SECRET_KEY at minimum
+bash setup-dev.sh # installs deps, initialises DB, creates dev accounts
uv run uvicorn app.main:app --reload --port 8000
-# Backend (with pip)
-cd services/ai
-python3 -m venv venv && source venv/bin/activate
-pip install -r requirements.txt
-python -m app.db.init_db
-python -m uvicorn app.main:app --reload --port 8000
-
-# ARQ worker (optional — requires Redis)
+# Background worker (optional — requires Redis)
redis-server --daemonize yes
-cd services/ai && REDIS_URL=redis://localhost:6379/0 arq app.workers.arq_worker.WorkerSettings
+cd services/ai
+uv run arq app.workers.arq_worker.WorkerSettings
-# QVAC service
+# QVAC service (downloads models on first run — 2–5 minutes)
cd workers/qvac-service && npm install && node src/server.js
```
---
-## Project structure
+## Configuration
-```
-bitcoin-academy/
-├── apps/web/ # Next.js 14 — App Router
-│ └── src/
-│ ├── app/
-│ │ ├── (auth)/ # Login / signup
-│ │ ├── dashboard/ # Student dashboard (progress, completed courses)
-│ │ └── courses/
-│ │ ├── page.tsx # Courses home — hero, stats, card grid
-│ │ ├── layout.tsx # TopBar + ToastProvider for all /courses/*
-│ │ └── [courseId]/
-│ │ ├── page.tsx # Workspace — upload, doc list, detail panel
-│ │ ├── study/ # Study — split-pane, 8 AI actions, evidence drawer
-│ │ ├── debug/ # Pipeline visibility (dev only)
-│ │ └── documents/[documentId]/preview/ # SourceViewer 3-pane
-│ ├── components/
-│ │ ├── ui/ # BrandMark, TopBar, Toast, BadgeDisplay, ProgressBar
-│ │ ├── courses/ # CourseCard, CreateCourseModal
-│ │ ├── documents/ # DocumentUpload, DocumentRow
-│ │ └── study/ # OutputPane, SourcePane, StudyActionBar, StudyOutput,
-│ │ # CitationCard, LessonNav, ContentChunks, SplitPane
-│ └── lib/
-│ ├── api/ # apiFetch, documents API, types, adapters
-│ └── services/ # courses, chat, study, progress
-├── services/ai/ # FastAPI backend
-│ └── app/
-│ ├── api/ # auth, chat, courses, documents, study, debug, progress
-│ ├── workers/pipeline.py # 4-stage: parse_pdf_pages → clean/boilerplate → structure-aware chunk → QVAC /ingest
-│ ├── workers/arq_worker.py # ARQ job definitions (ingest_document, reindex_document_qvac) + WorkerSettings
-│ ├── services/
-│ │ ├── study_service.py # dispatch 8 actions, DispatchTrace, QVAC /query
-│ │ └── chat_service.py # free chat → QVAC /query, ChromaDB fallback
-│ ├── schemas/study_schemas.py # StudyAction enum (8), ActionMeta, STUDY_ACTION_REGISTRY
-│ ├── core/rate_limit.py # slowapi Limiter singleton
-│ └── db/ # SQLAlchemy models, session, init_db
-├── workers/
-│ ├── python-ingester/src/ # legacy — RamSafeIngestor, StructuralParser, Chunker (no longer used by pipeline.py)
-│ └── qvac-service/src/ # Node.js — POST /ingest, POST /query, GET /health
-├── docs/
-│ ├── qvac-integration.md
-│ ├── mvp-issues.md # Open issues and gaps (P1/P2/post-MVP)
-│ └── src/ # Sample documents for testing (PDF, PPTX)
-├── start-dev.sh # Full dev start with health check loop
-└── docker-compose.yml
+```bash
+cp services/ai/.env.example services/ai/.env
+cp apps/web/.env.example apps/web/.env.local
```
----
+Docker Compose also needs a root-level `.env` with `DATABASE_URL` (used in variable substitution — see the Docker quick start above).
-## Stack
+Set `ENVIRONMENT=development` to enable Swagger UI and relaxed CORS.
-| Layer | Technology |
-|---|---|
-| Frontend | Next.js 14 · TypeScript · Tailwind CSS · NextAuth.js 4 |
-| Design system | BitPolito blue `#001CE0` · JetBrains Mono · `darkMode: 'class'` |
-| Backend | FastAPI · SQLAlchemy 2 · Pydantic v2 · python-jose · slowapi · uv |
-| Parsing | `pymupdf4llm` (PDF, page-level) · `python-pptx` (PPTX) · `python-docx` (DOCX) |
-| Chunking | Structure-aware: heading → atomic table → sentence-split paragraph (≤ 400 words, no overlap) |
-| Task queue | `arq` + Redis 7 (async ingestion jobs; falls back to FastAPI `BackgroundTasks` without Redis) |
-| Vector store | QVAC HyperDB (primary) · ChromaDB (passive fallback at query time) |
-| Embedding | QVAC `GTE_LARGE_FP16` 1024-dim (ingestion + query) · fastembed `all-MiniLM-L6-v2` (ChromaDB fallback only) |
-| LLM | LangChain + OpenAI (optional) · QVAC raw answer as fallback |
-| QVAC service | Node.js 22.17+ · `@qvac/sdk` |
-| Database | SQLite (dev) · PostgreSQL (prod) |
+### RAG variables
+
+| Variable | Default | Description |
+|---|---|---|
+| `QVAC_SERVICE_URL` | `http://localhost:3001` | URL of the QVAC Node.js service |
+| `QVAC_INGEST_DIR` | `./qvac_ingest` | Where the pipeline writes JSONL files for QVAC |
+| `QVAC_INGEST_TIMEOUT` | `300` | Timeout (s) for the QVAC `/ingest` call |
+| `RAG_TOP_K` | `5` | Chunks passed to the LLM after reranking |
+| `RAG_RETRIEVE_K` | `20` | Candidates fetched from the dense + sparse pool |
+| `RAG_MAX_CONTEXT_TOKENS` | `6000` | Token budget for context blocks |
+| `RAG_MAX_EVIDENCE` | `6` | Max evidence chunks returned by the study endpoint |
+| `RAG_HYDE` | `true` | Hypothetical Document Embedding query expansion |
+| `RAG_QUERY_REWRITE` | `false` | Rewrite the raw question into a dense retrieval query |
+| `RAG_COMPRESS_CONTEXT` | `true` | Trim each passage to relevant sentences before the LLM |
+| `RAG_CONTEXTUAL_CHUNKS` | `false` | Prepend an AI-generated context prefix at ingest time |
+| `RAG_SEMANTIC_CACHE` | `true` | Enable semantic cache (requires Redis) |
+| `RAG_CACHE_THRESHOLD` | `0.92` | Cosine similarity threshold for a cache hit |
+| `RAG_CACHE_TTL_SECONDS` | `86400` | Cache entry lifetime (24 h) |
+| `USE_DOCLING` | `false` | Use Docling for PDF parsing instead of pymupdf4llm |
+| `SKIP_CHROMA_INDEX` | `true` | Skip ChromaDB write during ingestion (QVAC-only mode) |
+
+Full list: [`docs/configuration.md`](docs/configuration.md).
---
-## Ingestion flow
+## Testing
-```
-Upload PDF / PPTX / DOCX via UI
- │
- ▼
-documents_api.py
- ├─ [Redis available] → arq_pool.enqueue_job("ingest_document", ...) → ARQ worker
- └─ [no Redis] → BackgroundTasks.add_task(pipeline.run, ...)
-
-pipeline.py — 4 stages:
- │
- ├─ Stage 1 – parse_pdf_pages()
- │ pymupdf4llm.to_markdown(page_chunks=True) → [{page, text}, …] per page
- │ (PPTX / DOCX: parse_pptx / parse_docx → same page-dict format)
- │
- ├─ Stage 2 – clean / boilerplate detection
- │ detect_boilerplate() → lines on ≥ 30% of pages → boilerplate set
- │ clean_page() → strip watermarks, zero-width chars, boilerplate lines
- │
- ├─ Stage 3 – chunk_pages() — structure-aware
- │ headings → section marker (not a chunk)
- │ tables → atomic chunk (≥ 4 words, no split)
- │ paragraphs → sentence-split at ≤ 400 words
- │ chunk id: "{doc_id}_{page:04d}_{idx:04d}" · citation_label: "p. {page}"
- │
- ├─ Stage 4 – filter_chunks()
- │ drop paragraphs < 25 words, tables < 4 words, chunks > 60% figure captions
- │
- ├─ _write_jsonl() → {doc_id}_contingency.jsonl → QVAC_INGEST_DIR
- └─ POST :3001/ingest (timeout 300 s) → QVAC GTE_LARGE_FP16 → HyperDB workspace
-```
+```bash
+# Backend (pytest)
+cd services/ai
+uv run pytest # all tests
+uv run pytest tests/unit/
+uv run pytest tests/integration/
-**ChromaDB** is not written during ingestion (`SKIP_CHROMA_INDEX=true`).
-It remains as a passive fallback in `chat_service.py` if QVAC is unreachable.
+# RAG end-to-end suite
+uv run python test_rag.py # 35 curated queries
+uv run python test_rag.py --query "What is Bitcoin?" # single query
+uv run python test_rag.py --output results.json # save JSON report
-## Study flow
+# Frontend
+cd apps/web && npm test
-```
-Student: query + action (explain / summarize / retrieve / open_questions /
- quiz / oral / derive / compare)
- → POST /api/courses/{id}/study [rate limit: 20/min, JWT required]
- → study_service.dispatch()
- ├─ _retrieve() → POST :3001/query → QVAC (embedding + HyperDB search)
- └─ _generate() → LangChain + OpenAI (if OPENAI_API_KEY is set)
- fallback: QVAC raw answer
- → StudyDispatchResponse { answer, citations, retrieval_used, action }
- + DispatchTrace JSON in logs (request_id, duration_ms, chunks_found, …)
+# QVAC service
+cd workers/qvac-service && npm test
```
----
+The RAG suite runs 35 queries across 7 categories (basic, chapter, conceptual, comparative, synthesis, adversarial, stress) through the full retrieval pipeline, scoring each PASS / WARN / FAIL by retrieval confidence. Results are saved as JSON for baseline comparisons.
-## API
-
-| Endpoint | Description |
-|---|---|
-| `POST /api/auth/register` | Register a new user |
-| `POST /api/auth/login` | Login → JWT |
-| `GET /api/courses` | List courses |
-| `POST /api/courses` | Create a course workspace |
-| `POST /api/courses/{id}/documents` | Upload a document (starts pipeline) |
-| `POST /api/courses/{id}/study` | RAG study action — 20 req/min |
-| `POST /api/courses/{id}/chat` | Free RAG chat |
-| `GET /api/courses/{id}/documents/{doc_id}/preview` | SourceViewer data |
-| `GET /api/debug/*` | Pipeline visibility endpoints (dev only) |
-| `GET /api/health` | Health check |
-
-Interactive docs: `http://localhost:8000/docs`
+CI runs on every push and pull request to `main` and `rag` via GitHub Actions (`.github/workflows/ci.yml`).
---
-## Environment variables
-
-**Backend** (`services/ai/.env`):
-
-```env
-DATABASE_URL=sqlite:///./bitcoin_academy.db
-SECRET_KEY=
-ENVIRONMENT=development
-CORS_ORIGINS=http://localhost:3000
-
-QVAC_SERVICE_URL=http://localhost:3001
-QVAC_INGEST_DIR=./qvac_ingest
-QVAC_INGEST_TIMEOUT=300 # seconds to wait for QVAC embedding (large PDFs need ~3-5 min)
-UPLOADS_DIR=./uploads
-CHROMA_DB_PATH=./chroma_db
-CHROMA_COLLECTION_NAME=bitpolito_course
-SKIP_CHROMA_INDEX=true # skip in-process embedding; QVAC is the sole index
+## Docs
-REDIS_URL=redis://localhost:6379/0 # optional — enables ARQ async ingestion queue
+| Document | Contents |
+|---|---|
+| [`docs/architecture.md`](docs/architecture.md) | Project layout, tech stack, component overview |
+| [`docs/api.md`](docs/api.md) | Full REST API reference |
+| [`docs/configuration.md`](docs/configuration.md) | All environment variables |
-RAG_TOP_K=5
-RAG_MAX_EVIDENCE=6
-LLM_TIMEOUT_SECONDS=30
+> `docs/` is in `.gitignore` and not committed to the repo.
-OPENAI_API_KEY= # optional — enables LLM generation
-DEBUG_MODE=false
-LOG_LEVEL=INFO # DEBUG for verbose output (sqlalchemy, httpx, etc.)
-```
+---
-**Frontend** (`apps/web/.env.local`):
+## Troubleshooting
-```env
-NEXT_PUBLIC_API_BASE_URL=http://localhost:8000
-NEXTAUTH_SECRET=dev-secret-key
-NEXTAUTH_URL=http://localhost:3000
-```
+| Symptom | Likely cause | Fix |
+|---|---|---|
+| QVAC service fails to start | Model download timed out on first run | Re-run `node src/server.js` — models are cached after the first successful download |
+| `/health` returns `database: disconnected` | `DATABASE_URL` missing or wrong | Check `services/ai/.env`; confirm PostgreSQL is running (or use the SQLite default for dev) |
+| Document stuck in `processing` forever | Redis not running → ARQ worker not started | `redis-server --daemonize yes`, then start the ARQ worker |
+| Frontend CORS error | `CORS_ORIGINS` missing the frontend origin | Add the frontend URL to `CORS_ORIGINS` in `services/ai/.env` |
+| Chat returns "Il servizio di ricerca non è disponibile" | QVAC service not running | `cd workers/qvac-service && node src/server.js` |
+| SSR API calls fail in Docker (`ECONNREFUSED localhost:8000`) | Next.js server-side calls resolve to the wrong host | `docker-compose.yml` sets `API_BASE_URL=http://api:8000/api` for SSR; make sure the web container env is current |
---
diff --git a/apps/.gitignore b/apps/.gitignore
index f934749..9b63f37 100644
--- a/apps/.gitignore
+++ b/apps/.gitignore
@@ -1,6 +1,7 @@
# Ignore node modules and lock files
node_modules/
package-lock.json
+!web/package-lock.json
# Ignore build and dist folders
dist/
build/
diff --git a/apps/web/.eslintrc.json b/apps/web/.eslintrc.json
index 1e3aed6..0c8c9ad 100644
--- a/apps/web/.eslintrc.json
+++ b/apps/web/.eslintrc.json
@@ -1,4 +1,5 @@
{
+ "root": true,
"extends": "next/core-web-vitals",
"rules": {
"react/react-in-jsx-scope": "off",
diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile
index 8fd641a..4d6d4ca 100644
--- a/apps/web/Dockerfile
+++ b/apps/web/Dockerfile
@@ -1,11 +1,17 @@
-FROM node:20-alpine
-
+FROM node:20-alpine AS builder
WORKDIR /app
-
COPY package*.json ./
RUN npm ci
-
COPY . .
+RUN npm run build
+FROM node:20-alpine AS runner
+WORKDIR /app
+ENV NODE_ENV=production
+COPY package*.json ./
+RUN npm ci --omit=dev
+COPY --from=builder /app/public ./public
+COPY --from=builder /app/.next ./.next
+COPY --from=builder /app/next.config.js ./
EXPOSE 3000
-CMD ["npm", "run", "dev"]
+CMD ["npm", "start"]
diff --git a/apps/web/__tests__/integration/auth-flow.test.tsx b/apps/web/__tests__/integration/auth-flow.test.tsx
index 8e156c1..d234b3c 100644
--- a/apps/web/__tests__/integration/auth-flow.test.tsx
+++ b/apps/web/__tests__/integration/auth-flow.test.tsx
@@ -78,8 +78,8 @@ describe('Authentication Flow Integration', () => {
// Fill out registration form
await userEvent.type(screen.getByLabelText(/display name/i), 'New User');
await userEvent.type(screen.getByLabelText(/email/i), 'newuser@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123!');
// Submit form
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -101,14 +101,14 @@ describe('Authentication Flow Integration', () => {
'credentials',
expect.objectContaining({
email: 'newuser@example.com',
- password: 'SecurePass123',
+ password: 'SecurePass123!',
})
);
});
// Verify redirect
await waitFor(() => {
- expect(mockRouter.push).toHaveBeenCalledWith('/dashboard');
+ expect(mockRouter.push).toHaveBeenCalledWith('/courses');
});
});
@@ -138,7 +138,7 @@ describe('Authentication Flow Integration', () => {
// Verify redirect
await waitFor(() => {
- expect(mockRouter.push).toHaveBeenCalledWith('/dashboard');
+ expect(mockRouter.push).toHaveBeenCalledWith('/courses');
});
});
});
@@ -147,7 +147,7 @@ describe('Authentication Flow Integration', () => {
it('handles login failure gracefully', async () => {
(signIn as jest.Mock).mockResolvedValue({
ok: false,
- error: 'Invalid email or password',
+ error: 'CredentialsSignin',
});
render( );
@@ -177,8 +177,8 @@ describe('Authentication Flow Integration', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'duplicate@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -196,8 +196,8 @@ describe('Authentication Flow Integration', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
diff --git a/apps/web/__tests__/integration/study-flow.test.tsx b/apps/web/__tests__/integration/study-flow.test.tsx
index 7c1ef0a..dd0a380 100644
--- a/apps/web/__tests__/integration/study-flow.test.tsx
+++ b/apps/web/__tests__/integration/study-flow.test.tsx
@@ -4,9 +4,18 @@ import userEvent from '@testing-library/user-event';
import { useSession } from 'next-auth/react';
import { useParams } from 'next/navigation';
-// scrollIntoView not implemented in jsdom
+// scrollIntoView and matchMedia not implemented in jsdom
beforeAll(() => {
window.HTMLElement.prototype.scrollIntoView = jest.fn();
+ Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation((query: string) => ({
+ matches: false,
+ media: query,
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ })),
+ });
});
// ── Module mocks ────────────────────────────────────────────────────────────
@@ -18,6 +27,7 @@ jest.mock('next-auth/react', () => ({
jest.mock('next/navigation', () => ({
useParams: jest.fn(),
useRouter: jest.fn(() => ({ push: jest.fn() })),
+ useSearchParams: jest.fn(() => ({ get: jest.fn().mockReturnValue(null) })),
}));
jest.mock('@/lib/services/courses', () => ({
@@ -36,10 +46,12 @@ jest.mock('@/lib/services/documents', () => ({
jest.mock('@/lib/api/documents', () => ({
getDocumentPreviewView: jest.fn(),
+ getDocumentListRows: jest.fn(),
}));
jest.mock('@/lib/services/chat', () => ({
- sendChatMessage: jest.fn(),
+ sendChatMessageStream: jest.fn(),
+ submitFeedback: jest.fn(),
}));
// ── Imports after mocks ─────────────────────────────────────────────────────
@@ -47,8 +59,8 @@ jest.mock('@/lib/services/chat', () => ({
import { getCourse, getCourseLessons } from '@/lib/services/courses';
import { getCourseProgress, markLessonComplete } from '@/lib/services/progress';
import { getDocuments } from '@/lib/services/documents';
-import { getDocumentPreviewView } from '@/lib/api/documents';
-import { sendChatMessage } from '@/lib/services/chat';
+import { getDocumentPreviewView, getDocumentListRows } from '@/lib/api/documents';
+import { sendChatMessageStream } from '@/lib/services/chat';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const StudyPage = require('../../src/app/courses/[courseId]/study/page').default;
@@ -79,6 +91,7 @@ function setupMocks() {
(getCourseLessons as jest.Mock).mockResolvedValue(LESSONS);
(getCourseProgress as jest.Mock).mockResolvedValue(PROGRESS);
(getDocuments as jest.Mock).mockResolvedValue([]);
+ (getDocumentListRows as jest.Mock).mockResolvedValue([]);
(getDocumentPreviewView as jest.Mock).mockResolvedValue({
id: 'doc-1',
filename: 'guide.pdf',
@@ -191,7 +204,7 @@ describe('Study Flow Integration', () => {
filename: 'bitcoin-guide.pdf',
extractedTextPreview: null,
pageCount: 5,
- sections: [{ title: 'What is Bitcoin?' }],
+ sections: ['What is Bitcoin?'],
sampleChunks: [
{ text: 'Bitcoin is a decentralized digital currency.', section: 'What is Bitcoin?' },
{ text: 'Transactions are verified by network nodes.', section: 'What is Bitcoin?' },
@@ -218,7 +231,7 @@ describe('Study Flow Integration', () => {
filename: 'guide.pdf',
extractedTextPreview: null,
pageCount: 2,
- sections: [{ title: 'Overview' }, { title: 'Key Concepts' }],
+ sections: ['Overview', 'Key Concepts'],
sampleChunks: [{ text: 'Some chunk text.' }],
});
@@ -245,11 +258,7 @@ describe('Study Flow Integration', () => {
describe('chat integration', () => {
it('sends a message to the chat service with correct courseId', async () => {
- (sendChatMessage as jest.Mock).mockResolvedValue({
- answer: 'Bitcoin was created in 2009.',
- citations: [],
- retrievalUsed: false,
- });
+ (sendChatMessageStream as jest.Mock).mockImplementation(async () => {});
render( );
@@ -262,19 +271,20 @@ describe('Study Flow Integration', () => {
fireEvent.click(screen.getByRole('button', { name: /send message/i }));
await waitFor(() => {
- expect(sendChatMessage).toHaveBeenCalledWith(
+ expect(sendChatMessageStream).toHaveBeenCalledWith(
'course-123',
'When was Bitcoin created?',
- 'test-token'
+ expect.any(Function),
+ expect.any(Function),
+ 'test-token',
+ expect.any(Array),
);
});
});
it('displays the AI response in the chat thread', async () => {
- (sendChatMessage as jest.Mock).mockResolvedValue({
- answer: 'Satoshi Nakamoto created Bitcoin.',
- citations: [],
- retrievalUsed: false,
+ (sendChatMessageStream as jest.Mock).mockImplementation(async (_c: string, _m: string, onToken: (t: string) => void) => {
+ onToken('Satoshi Nakamoto created Bitcoin.');
});
render( );
@@ -293,10 +303,13 @@ describe('Study Flow Integration', () => {
});
it('shows citations returned with the AI response', async () => {
- (sendChatMessage as jest.Mock).mockResolvedValue({
- answer: 'Bitcoin uses proof of work.',
- citations: [{ snippet: 'Proof of work is the consensus mechanism.', score: 0.88 }],
- retrievalUsed: true,
+ (sendChatMessageStream as jest.Mock).mockImplementation(async (
+ _c: string, _m: string,
+ onToken: (t: string) => void,
+ onCitations: (c: unknown[]) => void,
+ ) => {
+ onToken('Bitcoin uses proof of work.');
+ onCitations([{ snippet: 'Proof of work is the consensus mechanism.', score: 0.88 }]);
});
render( );
@@ -311,9 +324,12 @@ describe('Study Flow Integration', () => {
);
fireEvent.click(screen.getByRole('button', { name: /send message/i }));
+ await waitFor(() => {
+ fireEvent.click(screen.getByRole('button', { name: /show 1 source/i }));
+ });
await waitFor(() => {
expect(screen.getByText(/proof of work is the consensus mechanism/i)).toBeInTheDocument();
- expect(screen.getByText('Relevance: 88%')).toBeInTheDocument();
+ expect(screen.getByText(/88%/)).toBeInTheDocument();
});
});
});
@@ -383,7 +399,7 @@ describe('Study Flow Integration', () => {
fireEvent.click(screen.getByRole('button', { name: /mark as complete/i }));
await waitFor(() => {
- expect(screen.getByText(/new badge earned/i)).toBeInTheDocument();
+ expect(screen.getByText(/badge earned/i)).toBeInTheDocument();
expect(screen.getByText('First Steps')).toBeInTheDocument();
});
});
diff --git a/apps/web/__tests__/unit/CitationCard.test.tsx b/apps/web/__tests__/unit/CitationCard.test.tsx
new file mode 100644
index 0000000..e94d844
--- /dev/null
+++ b/apps/web/__tests__/unit/CitationCard.test.tsx
@@ -0,0 +1,85 @@
+import '@testing-library/jest-dom';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { CitationCard } from '../../src/components/study/CitationCard';
+
+const mockPush = jest.fn();
+jest.mock('next/navigation', () => ({
+ useRouter: () => ({ push: mockPush }),
+}));
+
+const baseCitation = {
+ snippet: 'A UTXO is an unspent transaction output.',
+ score: 0.88,
+ label: 'lecture01.pdf',
+ page: 7,
+ slide: 0,
+ section: 'Bitcoin Basics',
+ doc_id: 'doc-abc',
+};
+
+describe('CitationCard', () => {
+ beforeEach(() => jest.clearAllMocks());
+
+ it('renders the snippet text', () => {
+ render( );
+ expect(screen.getByText(/UTXO is an unspent/)).toBeInTheDocument();
+ });
+
+ it('renders the relevance percentage', () => {
+ render( );
+ expect(screen.getByText('88%')).toBeInTheDocument();
+ });
+
+ it('renders the document label and page', () => {
+ render( );
+ expect(screen.getByText(/lecture01\.pdf/)).toBeInTheDocument();
+ expect(screen.getByText(/p\.7/)).toBeInTheDocument();
+ });
+
+ it('renders the section when present', () => {
+ render( );
+ expect(screen.getByText('Bitcoin Basics')).toBeInTheDocument();
+ });
+
+ it('shows "View in source →" link when doc_id is present', () => {
+ render( );
+ expect(screen.getByText(/View in source/)).toBeInTheDocument();
+ });
+
+ it('navigates to the document preview on click', () => {
+ render( );
+ fireEvent.click(screen.getByText(/View in source/).closest('div')!);
+ expect(mockPush).toHaveBeenCalledWith(
+ '/courses/c1/documents/doc-abc/preview?page=7'
+ );
+ });
+
+ it('uses slide parameter in URL when only slide is set', () => {
+ const citation = { ...baseCitation, page: 0, slide: 3 };
+ render( );
+ fireEvent.click(screen.getByText(/View in source/).closest('div')!);
+ expect(mockPush).toHaveBeenCalledWith(
+ '/courses/c2/documents/doc-abc/preview?slide=3'
+ );
+ });
+
+ it('does not navigate when doc_id is empty', () => {
+ const citation = { ...baseCitation, doc_id: '' };
+ render( );
+ // No "View in source" link and no onClick — clicking the snippet p does nothing
+ fireEvent.click(screen.getByText(/UTXO/).closest('p')!);
+ expect(mockPush).not.toHaveBeenCalled();
+ });
+
+ it('truncates long snippets to 180 chars', () => {
+ const longSnippet = 'x'.repeat(200);
+ render( );
+ expect(screen.getByText(/x{1,180}…/)).toBeInTheDocument();
+ });
+
+ it('renders "Source" as fallback label when label and location are empty', () => {
+ const citation = { ...baseCitation, label: '', page: 0, slide: 0 };
+ render( );
+ expect(screen.getByText(/\[1\] Source/i)).toBeInTheDocument();
+ });
+});
diff --git a/apps/web/__tests__/unit/CourseCard.test.tsx b/apps/web/__tests__/unit/CourseCard.test.tsx
new file mode 100644
index 0000000..a953951
--- /dev/null
+++ b/apps/web/__tests__/unit/CourseCard.test.tsx
@@ -0,0 +1,67 @@
+import '@testing-library/jest-dom';
+import { render, screen } from '@testing-library/react';
+import { CourseCard } from '../../src/components/courses/CourseCard';
+
+jest.mock('next/link', () => {
+ const MockLink = ({ href, children }: { href: string; children: React.ReactNode }) => (
+ {children}
+ );
+ MockLink.displayName = 'MockLink';
+ return MockLink;
+});
+
+const baseCourse = {
+ id: 'btc-101',
+ title: 'Bitcoin 101',
+ description: 'Foundations of Bitcoin',
+};
+
+describe('CourseCard', () => {
+ it('renders the course title in the heading', () => {
+ render( );
+ expect(screen.getByRole('heading', { name: 'Bitcoin 101' })).toBeInTheDocument();
+ });
+
+ it('renders the course description', () => {
+ render( );
+ expect(screen.getByText('Foundations of Bitcoin')).toBeInTheDocument();
+ });
+
+ it('links to the correct course URL', () => {
+ render( );
+ expect(screen.getByRole('link')).toHaveAttribute('href', '/courses/btc-101');
+ });
+
+ it('shows "all indexed" status dot when all docs are indexed', () => {
+ render( );
+ expect(screen.getByText('all indexed')).toBeInTheDocument();
+ });
+
+ it('shows processing count when docs are processing', () => {
+ render( );
+ expect(screen.getByText('2 processing')).toBeInTheDocument();
+ });
+
+ it('shows failed count when docs have errors', () => {
+ render( );
+ expect(screen.getByText('2 failed')).toBeInTheDocument();
+ });
+
+ it('renders doc stats grid when stats are provided', () => {
+ render( );
+ expect(screen.getByText('10')).toBeInTheDocument();
+ expect(screen.getByText('8')).toBeInTheDocument();
+ });
+
+ it('does not render stats grid when stats are null', () => {
+ render( );
+ expect(screen.queryByText('docs')).not.toBeInTheDocument();
+ });
+
+ it('snapshot: renders consistently', () => {
+ const { container } = render(
+
+ );
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/apps/web/__tests__/unit/DocumentUpload.test.tsx b/apps/web/__tests__/unit/DocumentUpload.test.tsx
new file mode 100644
index 0000000..c07574f
--- /dev/null
+++ b/apps/web/__tests__/unit/DocumentUpload.test.tsx
@@ -0,0 +1,96 @@
+import '@testing-library/jest-dom';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { DocumentUpload } from '../../src/components/documents/DocumentUpload';
+
+// jsdom does not implement crypto.randomUUID
+let uuidCounter = 0;
+Object.defineProperty(global, 'crypto', {
+ value: { randomUUID: () => `mock-uuid-${++uuidCounter}` },
+ configurable: true,
+});
+
+const mockShowToast = jest.fn();
+jest.mock('@/components/ui/Toast', () => ({
+ useToast: () => ({ showToast: mockShowToast }),
+}));
+
+const mockUpload = jest.fn();
+const mockFetchStatus = jest.fn();
+jest.mock('@/lib/api/documents', () => ({
+ uploadDocumentWithProgress: (...args: unknown[]) => mockUpload(...args),
+ fetchDocumentStatus: (...args: unknown[]) => mockFetchStatus(...args),
+ retryDocument: jest.fn(),
+}));
+
+function makeFile(name: string, type: string, sizeBytes = 1024): File {
+ const file = new File(['x'], name, { type });
+ Object.defineProperty(file, 'size', { value: sizeBytes });
+ return file;
+}
+
+describe('DocumentUpload', () => {
+ beforeEach(() => jest.clearAllMocks());
+
+ it('renders the drop zone and type selector', () => {
+ render( );
+ expect(screen.getByText(/Click to upload/)).toBeInTheDocument();
+ expect(screen.getByText('Lecture')).toBeInTheDocument();
+ expect(screen.getByText('Past Exam')).toBeInTheDocument();
+ expect(screen.getByText('Supplement')).toBeInTheDocument();
+ });
+
+ it('rejects unsupported file types with a toast', () => {
+ render( );
+ const input = document.querySelector('input[type="file"]') as HTMLInputElement;
+ const file = makeFile('notes.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
+ fireEvent.change(input, { target: { files: [file] } });
+ expect(mockShowToast).toHaveBeenCalledWith('Unsupported format. Use PDF or PPTX.', 'err');
+ expect(screen.queryByText('notes.docx')).not.toBeInTheDocument();
+ });
+
+ it('rejects files over 50 MB with a validation error row', () => {
+ render( );
+ const input = document.querySelector('input[type="file"]') as HTMLInputElement;
+ const bigFile = makeFile('huge.pdf', 'application/pdf', 51 * 1024 * 1024);
+ fireEvent.change(input, { target: { files: [bigFile] } });
+ expect(screen.getByText('huge.pdf')).toBeInTheDocument();
+ expect(screen.getByText('File too large (max 50 MB)')).toBeInTheDocument();
+ });
+
+ it('starts upload for valid PDF and shows uploading state', async () => {
+ mockUpload.mockImplementation((_courseId, _file, _token, _type, onProgress) => {
+ onProgress(50);
+ return new Promise(() => {});
+ });
+
+ render( );
+ const input = document.querySelector('input[type="file"]') as HTMLInputElement;
+ const file = makeFile('slides.pdf', 'application/pdf');
+ fireEvent.change(input, { target: { files: [file] } });
+
+ await waitFor(() => {
+ expect(screen.getByText('slides.pdf')).toBeInTheDocument();
+ });
+ expect(mockUpload).toHaveBeenCalled();
+ });
+
+ it('shows "Indexed" status after successful upload and processing', async () => {
+ mockUpload.mockResolvedValue({ id: 'doc-1' });
+ mockFetchStatus.mockResolvedValue({ status: 'ready', processing_stage: 'done' });
+
+ render( );
+ const input = document.querySelector('input[type="file"]') as HTMLInputElement;
+ fireEvent.change(input, { target: { files: [makeFile('deck.pdf', 'application/pdf')] } });
+
+ await waitFor(() => {
+ expect(screen.getByText('Indexed')).toBeInTheDocument();
+ });
+ });
+
+ it('changes selected type when type button is clicked', () => {
+ render( );
+ fireEvent.click(screen.getByText('Past Exam'));
+ const pastExamBtn = screen.getByText('Past Exam').closest('button')!;
+ expect(pastExamBtn.className).toMatch(/bg-blue-dark/);
+ });
+});
diff --git a/apps/web/__tests__/unit/OutputPane.test.tsx b/apps/web/__tests__/unit/OutputPane.test.tsx
index de1a91e..81a8457 100644
--- a/apps/web/__tests__/unit/OutputPane.test.tsx
+++ b/apps/web/__tests__/unit/OutputPane.test.tsx
@@ -10,11 +10,12 @@ beforeAll(() => {
// Mock the chat service so no real HTTP calls are made
jest.mock('../../src/lib/services/chat', () => ({
- sendChatMessage: jest.fn(),
+ sendChatMessageStream: jest.fn(),
+ submitFeedback: jest.fn(),
}));
-import { sendChatMessage } from '../../src/lib/services/chat';
-const mockSend = sendChatMessage as jest.MockedFunction;
+import { sendChatMessageStream } from '../../src/lib/services/chat';
+const mockSend = sendChatMessageStream as jest.MockedFunction;
describe('OutputPane', () => {
const defaultProps = { courseId: 'course-123', accessToken: 'tok' };
@@ -46,7 +47,7 @@ describe('OutputPane', () => {
it('shows the empty-state prompt when no messages', () => {
render( );
- expect(screen.getByText(/ask me anything/i)).toBeInTheDocument();
+ expect(screen.getByText(/type a topic/i)).toBeInTheDocument();
});
it('shows lesson-specific placeholder when a lesson is selected', () => {
@@ -57,7 +58,7 @@ describe('OutputPane', () => {
/>
);
const textarea = screen.getByRole('textbox', { name: /message input/i });
- expect(textarea).toHaveAttribute('placeholder', 'Ask about "How Mining Works"…');
+ expect(textarea).toHaveAttribute('placeholder', 'Ask about "How Mining Works" or pick an action above…');
});
});
@@ -70,7 +71,7 @@ describe('OutputPane', () => {
});
it('shows user message in the thread after send', async () => {
- mockSend.mockResolvedValue({ answer: 'A UTXO is an unspent output.', citations: [], retrievalUsed: false });
+ mockSend.mockImplementation(async () => {});
render( );
const textarea = screen.getByRole('textbox', { name: /message input/i });
@@ -81,7 +82,7 @@ describe('OutputPane', () => {
});
it('clears the input after send', async () => {
- mockSend.mockResolvedValue({ answer: 'Answer.', citations: [], retrievalUsed: false });
+ mockSend.mockImplementation(async () => {});
render( );
const textarea = screen.getByRole('textbox', { name: /message input/i });
@@ -103,10 +104,8 @@ describe('OutputPane', () => {
});
it('shows assistant reply after response arrives', async () => {
- mockSend.mockResolvedValue({
- answer: 'Bitcoin is a peer-to-peer currency.',
- citations: [],
- retrievalUsed: false,
+ mockSend.mockImplementation(async (_c, _m, onToken) => {
+ onToken('Bitcoin is a peer-to-peer currency.');
});
render( );
@@ -120,36 +119,49 @@ describe('OutputPane', () => {
});
it('sends via Enter key (without Shift)', async () => {
- mockSend.mockResolvedValue({ answer: 'Answer.', citations: [], retrievalUsed: false });
+ mockSend.mockImplementation(async () => {});
render( );
const textarea = screen.getByRole('textbox', { name: /message input/i });
await userEvent.type(textarea, 'Question?{Enter}');
- expect(mockSend).toHaveBeenCalledWith('course-123', 'Question?', 'tok');
+ expect(mockSend).toHaveBeenCalledWith(
+ 'course-123',
+ 'Question?',
+ expect.any(Function),
+ expect.any(Function),
+ 'tok',
+ expect.any(Array),
+ );
});
- it('calls sendChatMessage with correct courseId and accessToken', async () => {
- mockSend.mockResolvedValue({ answer: 'Ok.', citations: [], retrievalUsed: false });
+ it('calls sendChatMessageStream with correct courseId and accessToken', async () => {
+ mockSend.mockImplementation(async () => {});
render( );
const textarea = screen.getByRole('textbox', { name: /message input/i });
await userEvent.type(textarea, 'Test question');
fireEvent.click(screen.getByRole('button', { name: /send message/i }));
- expect(mockSend).toHaveBeenCalledWith('my-course', 'Test question', 'my-token');
+ expect(mockSend).toHaveBeenCalledWith(
+ 'my-course',
+ 'Test question',
+ expect.any(Function),
+ expect.any(Function),
+ 'my-token',
+ expect.any(Array),
+ );
});
});
describe('citations', () => {
it('renders citation snippets when the response includes sources', async () => {
- mockSend.mockResolvedValue({
- answer: 'Answer with sources.',
- citations: [
+ mockSend.mockImplementation(async (_c, _m, onToken, onCitations) => {
+ onToken('Answer with sources.');
+ onCitations([
{ snippet: 'The first source text.', score: 0.95 },
{ snippet: 'The second source text.', score: 0.80 },
- ],
- retrievalUsed: true,
+ ]);
});
render( );
@@ -157,6 +169,9 @@ describe('OutputPane', () => {
await userEvent.type(textarea, 'Question?');
fireEvent.click(screen.getByRole('button', { name: /send message/i }));
+ await waitFor(() => {
+ fireEvent.click(screen.getByRole('button', { name: /show 2 sources/i }));
+ });
await waitFor(() => {
expect(screen.getByText(/the first source text/i)).toBeInTheDocument();
expect(screen.getByText(/the second source text/i)).toBeInTheDocument();
@@ -164,10 +179,9 @@ describe('OutputPane', () => {
});
it('shows relevance percentage for each citation', async () => {
- mockSend.mockResolvedValue({
- answer: 'Answer.',
- citations: [{ snippet: 'Snippet.', score: 0.92 }],
- retrievalUsed: true,
+ mockSend.mockImplementation(async (_c, _m, onToken, onCitations) => {
+ onToken('Answer.');
+ onCitations([{ snippet: 'Snippet.', score: 0.92 }]);
});
render( );
@@ -178,15 +192,17 @@ describe('OutputPane', () => {
fireEvent.click(screen.getByRole('button', { name: /send message/i }));
await waitFor(() => {
- expect(screen.getByText('Relevance: 92%')).toBeInTheDocument();
+ fireEvent.click(screen.getByRole('button', { name: /show 1 source/i }));
+ });
+ await waitFor(() => {
+ expect(screen.getByText(/92%/)).toBeInTheDocument();
});
});
- it('shows "Sources" heading when citations are present', async () => {
- mockSend.mockResolvedValue({
- answer: 'Answer.',
- citations: [{ snippet: 'Source.', score: 0.9 }],
- retrievalUsed: true,
+ it('shows source toggle button when citations are present', async () => {
+ mockSend.mockImplementation(async (_c, _m, onToken, onCitations) => {
+ onToken('Answer.');
+ onCitations([{ snippet: 'Source.', score: 0.9 }]);
});
render( );
@@ -197,12 +213,12 @@ describe('OutputPane', () => {
fireEvent.click(screen.getByRole('button', { name: /send message/i }));
await waitFor(() => {
- expect(screen.getByText('Sources')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: /show 1 source/i })).toBeInTheDocument();
});
});
it('does not render Sources section when citations array is empty', async () => {
- mockSend.mockResolvedValue({ answer: 'No sources.', citations: [], retrievalUsed: false });
+ mockSend.mockImplementation(async () => {});
render( );
await userEvent.type(
@@ -212,7 +228,7 @@ describe('OutputPane', () => {
fireEvent.click(screen.getByRole('button', { name: /send message/i }));
await waitFor(() => {
- expect(screen.queryByText('Sources')).not.toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: /show.*source/i })).not.toBeInTheDocument();
});
});
});
diff --git a/apps/web/__tests__/unit/StudyActionBar.test.tsx b/apps/web/__tests__/unit/StudyActionBar.test.tsx
new file mode 100644
index 0000000..5226f3e
--- /dev/null
+++ b/apps/web/__tests__/unit/StudyActionBar.test.tsx
@@ -0,0 +1,67 @@
+import '@testing-library/jest-dom';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { StudyActionBar } from '../../src/components/study/StudyActionBar';
+
+describe('StudyActionBar', () => {
+ const noop = jest.fn();
+
+ beforeEach(() => jest.clearAllMocks());
+
+ it('renders all 8 action buttons', () => {
+ render( );
+ const labels = ['Explain', 'Summarize', 'Retrieve', 'Questions', 'Quiz', 'Oral Exam', 'Derive', 'Compare'];
+ for (const label of labels) {
+ expect(screen.getByText(label)).toBeInTheDocument();
+ }
+ });
+
+ it('calls onAction with the correct id when a button is clicked', () => {
+ render( );
+ fireEvent.click(screen.getByText('Explain').closest('button')!);
+ expect(noop).toHaveBeenCalledWith('explain');
+ });
+
+ it('disables all buttons while loading', () => {
+ render( );
+ const buttons = screen.getAllByRole('button');
+ for (const btn of buttons) {
+ expect(btn).toBeDisabled();
+ }
+ });
+
+ it('disables all buttons when disabled prop is true', () => {
+ render( );
+ const buttons = screen.getAllByRole('button');
+ for (const btn of buttons) {
+ expect(btn).toBeDisabled();
+ }
+ });
+
+ it('disables all buttons when hasIndexedDocs is false', () => {
+ render( );
+ const buttons = screen.getAllByRole('button');
+ for (const btn of buttons) {
+ expect(btn).toBeDisabled();
+ }
+ });
+
+ it('shows "Upload documents first" tooltip when hasIndexedDocs is false', () => {
+ render( );
+ const buttons = screen.getAllByRole('button');
+ for (const btn of buttons) {
+ expect(btn).toHaveAttribute('title', 'Upload documents first');
+ }
+ });
+
+ it('applies active styling to the active action button', () => {
+ render( );
+ const quizBtn = screen.getByText('Quiz').closest('button')!;
+ expect(quizBtn.className).toMatch(/bg-blue-dark/);
+ });
+
+ it('does not apply active styling to inactive buttons', () => {
+ render( );
+ const explainBtn = screen.getByText('Explain').closest('button')!;
+ expect(explainBtn.className).not.toMatch(/bg-blue-dark text-white/);
+ });
+});
diff --git a/apps/web/__tests__/unit/__snapshots__/CourseCard.test.tsx.snap b/apps/web/__tests__/unit/__snapshots__/CourseCard.test.tsx.snap
new file mode 100644
index 0000000..b0e36f1
--- /dev/null
+++ b/apps/web/__tests__/unit/__snapshots__/CourseCard.test.tsx.snap
@@ -0,0 +1,113 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CourseCard snapshot: renders consistently 1`] = `
+
+`;
diff --git a/apps/web/__tests__/unit/login.test.tsx b/apps/web/__tests__/unit/login.test.tsx
index 3749091..2331c4c 100644
--- a/apps/web/__tests__/unit/login.test.tsx
+++ b/apps/web/__tests__/unit/login.test.tsx
@@ -122,7 +122,7 @@ describe('LoginPage', () => {
email: 'test@example.com',
password: 'Password123',
redirect: false,
- callbackUrl: '/dashboard',
+ callbackUrl: '/courses',
});
});
});
@@ -137,7 +137,7 @@ describe('LoginPage', () => {
fireEvent.click(screen.getByRole('button', { name: /sign in/i }));
await waitFor(() => {
- expect(mockRouter.push).toHaveBeenCalledWith('/dashboard');
+ expect(mockRouter.push).toHaveBeenCalledWith('/courses');
expect(mockRouter.refresh).toHaveBeenCalled();
});
});
@@ -167,7 +167,7 @@ describe('LoginPage', () => {
});
it('displays error message on failed login', async () => {
- (signIn as jest.Mock).mockResolvedValue({ ok: false, error: 'Invalid credentials' });
+ (signIn as jest.Mock).mockResolvedValue({ ok: false, error: 'CredentialsSignin' });
render( );
@@ -176,7 +176,7 @@ describe('LoginPage', () => {
fireEvent.click(screen.getByRole('button', { name: /sign in/i }));
await waitFor(() => {
- expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument();
+ expect(screen.getByText(/invalid email or password/i)).toBeInTheDocument();
});
});
diff --git a/apps/web/__tests__/unit/signup.test.tsx b/apps/web/__tests__/unit/signup.test.tsx
index 9d8fc2f..d626ed7 100644
--- a/apps/web/__tests__/unit/signup.test.tsx
+++ b/apps/web/__tests__/unit/signup.test.tsx
@@ -39,7 +39,7 @@ describe('SignupPage', () => {
it('renders signup form with all elements', () => {
render( );
- expect(screen.getByRole('heading', { name: /create your account/i })).toBeInTheDocument();
+ expect(screen.getByRole('heading', { name: /create account/i })).toBeInTheDocument();
expect(screen.getByLabelText(/display name/i)).toBeInTheDocument();
expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument();
@@ -51,7 +51,7 @@ describe('SignupPage', () => {
it('shows password requirements hint', () => {
render( );
- expect(screen.getByText(/min 8 characters/i)).toBeInTheDocument();
+ expect(screen.getByText(/min 12/i)).toBeInTheDocument();
});
});
@@ -59,8 +59,8 @@ describe('SignupPage', () => {
it('shows error when email is empty', async () => {
render( );
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -73,8 +73,8 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'invalid-email');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -93,7 +93,7 @@ describe('SignupPage', () => {
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
await waitFor(() => {
- expect(screen.getByText(/at least 8 characters/i)).toBeInTheDocument();
+ expect(screen.getByText(/at least 12 characters/i)).toBeInTheDocument();
});
});
@@ -143,7 +143,7 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
await userEvent.type(screen.getByLabelText(/confirm password/i), 'DifferentPass456');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -157,7 +157,7 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -179,8 +179,8 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -205,8 +205,8 @@ describe('SignupPage', () => {
await userEvent.type(screen.getByLabelText(/display name/i), 'Test User');
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -218,7 +218,7 @@ describe('SignupPage', () => {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'test@example.com',
- password: 'ValidPass123',
+ password: 'ValidPass123!',
display_name: 'Test User',
}),
})
@@ -239,8 +239,8 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -249,7 +249,7 @@ describe('SignupPage', () => {
'credentials',
expect.objectContaining({
email: 'test@example.com',
- password: 'ValidPass123',
+ password: 'ValidPass123!',
})
);
});
@@ -268,13 +268,13 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
await waitFor(() => {
- expect(mockRouter.push).toHaveBeenCalledWith('/dashboard');
+ expect(mockRouter.push).toHaveBeenCalledWith('/courses');
});
});
@@ -288,8 +288,8 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'existing@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -308,8 +308,8 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
@@ -326,8 +326,8 @@ describe('SignupPage', () => {
render( );
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
- await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123');
- await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123');
+ await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!');
+ await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!');
fireEvent.click(screen.getByRole('button', { name: /create account/i }));
diff --git a/apps/web/coverage/clover.xml b/apps/web/coverage/clover.xml
deleted file mode 100644
index 6224420..0000000
--- a/apps/web/coverage/clover.xml
+++ /dev/null
@@ -1,760 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/web/coverage/coverage-final.json b/apps/web/coverage/coverage-final.json
deleted file mode 100644
index 1bcce70..0000000
--- a/apps/web/coverage/coverage-final.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/middleware.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/middleware.ts","statementMap":{"0":{"start":{"line":5,"column":36},"end":{"line":5,"column":50}},"1":{"start":{"line":5,"column":9},"end":{"line":5,"column":27}},"2":{"start":{"line":5,"column":50},"end":{"line":5,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0},"f":{},"b":{}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/layout.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/layout.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}},"1":{"start":{"line":5,"column":13},"end":{"line":5,"column":21}},"2":{"start":{"line":2,"column":7},"end":{"line":2,"column":null}},"3":{"start":{"line":3,"column":29},"end":{"line":3,"column":null}},"4":{"start":{"line":5,"column":34},"end":{"line":9,"column":null}}},"fnMap":{"0":{"name":"RootLayout","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}},"loc":{"start":{"line":11,"column":78},"end":{"line":19,"column":1}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{"0":0},"b":{}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/page.tsx","statementMap":{"0":{"start":{"line":7,"column":24},"end":{"line":7,"column":null}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":8,"column":17},"end":{"line":8,"column":null}},"5":{"start":{"line":9,"column":21},"end":{"line":9,"column":null}},"6":{"start":{"line":11,"column":2},"end":{"line":17,"column":null}},"7":{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},"8":{"start":{"line":13,"column":6},"end":{"line":13,"column":null}},"9":{"start":{"line":14,"column":11},"end":{"line":16,"column":null}},"10":{"start":{"line":15,"column":6},"end":{"line":15,"column":null}},"11":{"start":{"line":19,"column":2},"end":{"line":27,"column":null}},"12":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}}},"fnMap":{"0":{"name":"Home","decl":{"start":{"line":7,"column":24},"end":{"line":7,"column":null}},"loc":{"start":{"line":7,"column":24},"end":{"line":30,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":11,"column":12},"end":{"line":11,"column":null}},"loc":{"start":{"line":11,"column":12},"end":{"line":17,"column":5}}}},"branchMap":{"0":{"loc":{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},{"start":{"line":14,"column":11},"end":{"line":16,"column":null}}]},"1":{"loc":{"start":{"line":14,"column":11},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":11},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":19,"column":2},"end":{"line":27,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":2},"end":{"line":27,"column":null}}]},"3":{"loc":{"start":{"line":19,"column":6},"end":{"line":19,"column":60}},"type":"binary-expr","locations":[{"start":{"line":19,"column":6},"end":{"line":19,"column":30}},{"start":{"line":19,"column":30},"end":{"line":19,"column":60}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/layout.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/layout.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}}},"fnMap":{"0":{"name":"AuthLayout","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}},"loc":{"start":{"line":11,"column":64},"end":{"line":23,"column":null}}}},"branchMap":{},"s":{"0":0},"f":{"0":0},"b":{}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/login/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/login/page.tsx","statementMap":{"0":{"start":{"line":18,"column":24},"end":{"line":18,"column":null}},"1":{"start":{"line":7,"column":23},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":17},"end":{"line":8,"column":null}},"3":{"start":{"line":9,"column":43},"end":{"line":9,"column":null}},"4":{"start":{"line":10,"column":36},"end":{"line":10,"column":null}},"5":{"start":{"line":19,"column":17},"end":{"line":19,"column":null}},"6":{"start":{"line":20,"column":23},"end":{"line":20,"column":null}},"7":{"start":{"line":21,"column":22},"end":{"line":21,"column":null}},"8":{"start":{"line":22,"column":21},"end":{"line":22,"column":null}},"9":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}},"10":{"start":{"line":25,"column":34},"end":{"line":25,"column":null}},"11":{"start":{"line":26,"column":36},"end":{"line":26,"column":null}},"12":{"start":{"line":27,"column":30},"end":{"line":27,"column":null}},"13":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"14":{"start":{"line":36,"column":23},"end":{"line":53,"column":null}},"15":{"start":{"line":37,"column":34},"end":{"line":37,"column":null}},"16":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"17":{"start":{"line":41,"column":6},"end":{"line":41,"column":null}},"18":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"19":{"start":{"line":43,"column":6},"end":{"line":43,"column":null}},"20":{"start":{"line":47,"column":4},"end":{"line":49,"column":null}},"21":{"start":{"line":48,"column":6},"end":{"line":48,"column":null}},"22":{"start":{"line":51,"column":4},"end":{"line":51,"column":null}},"23":{"start":{"line":52,"column":4},"end":{"line":52,"column":null}},"24":{"start":{"line":58,"column":23},"end":{"line":90,"column":null}},"25":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"26":{"start":{"line":62,"column":4},"end":{"line":62,"column":null}},"27":{"start":{"line":65,"column":4},"end":{"line":67,"column":null}},"28":{"start":{"line":66,"column":6},"end":{"line":66,"column":null}},"29":{"start":{"line":69,"column":4},"end":{"line":69,"column":null}},"30":{"start":{"line":71,"column":4},"end":{"line":89,"column":null}},"31":{"start":{"line":72,"column":21},"end":{"line":77,"column":null}},"32":{"start":{"line":79,"column":6},"end":{"line":84,"column":null}},"33":{"start":{"line":80,"column":8},"end":{"line":80,"column":null}},"34":{"start":{"line":81,"column":13},"end":{"line":84,"column":null}},"35":{"start":{"line":82,"column":8},"end":{"line":82,"column":null}},"36":{"start":{"line":83,"column":8},"end":{"line":83,"column":null}},"37":{"start":{"line":86,"column":6},"end":{"line":86,"column":null}},"38":{"start":{"line":88,"column":6},"end":{"line":88,"column":null}},"39":{"start":{"line":124,"column":31},"end":{"line":124,"column":null}},"40":{"start":{"line":153,"column":31},"end":{"line":153,"column":null}}},"fnMap":{"0":{"name":"LoginPage","decl":{"start":{"line":18,"column":24},"end":{"line":18,"column":null}},"loc":{"start":{"line":18,"column":24},"end":{"line":229,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":36,"column":23},"end":{"line":36,"column":null}},"loc":{"start":{"line":36,"column":23},"end":{"line":53,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":58,"column":23},"end":{"line":58,"column":30}},"loc":{"start":{"line":58,"column":30},"end":{"line":90,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":124,"column":24},"end":{"line":124,"column":25}},"loc":{"start":{"line":124,"column":31},"end":{"line":124,"column":null}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":153,"column":24},"end":{"line":153,"column":25}},"loc":{"start":{"line":153,"column":31},"end":{"line":153,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":22},"end":{"line":21,"column":null}},"type":"binary-expr","locations":[{"start":{"line":21,"column":22},"end":{"line":21,"column":57}},{"start":{"line":21,"column":57},"end":{"line":21,"column":null}}]},"1":{"loc":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"type":"cond-expr","locations":[{"start":{"line":31,"column":38},"end":{"line":31,"column":89}},{"start":{"line":31,"column":89},"end":{"line":31,"column":null}}]},"2":{"loc":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"3":{"loc":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"4":{"loc":{"start":{"line":47,"column":4},"end":{"line":49,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":49,"column":null}}]},"5":{"loc":{"start":{"line":65,"column":4},"end":{"line":67,"column":null}},"type":"if","locations":[{"start":{"line":65,"column":4},"end":{"line":67,"column":null}}]},"6":{"loc":{"start":{"line":79,"column":6},"end":{"line":84,"column":null}},"type":"if","locations":[{"start":{"line":79,"column":6},"end":{"line":84,"column":null}},{"start":{"line":81,"column":13},"end":{"line":84,"column":null}}]},"7":{"loc":{"start":{"line":81,"column":13},"end":{"line":84,"column":null}},"type":"if","locations":[{"start":{"line":81,"column":13},"end":{"line":84,"column":null}}]},"8":{"loc":{"start":{"line":97,"column":7},"end":{"line":97,"column":null}},"type":"binary-expr","locations":[{"start":{"line":97,"column":7},"end":{"line":97,"column":null}}]},"9":{"loc":{"start":{"line":104,"column":7},"end":{"line":104,"column":21}},"type":"binary-expr","locations":[{"start":{"line":104,"column":7},"end":{"line":104,"column":21}}]},"10":{"loc":{"start":{"line":126,"column":16},"end":{"line":126,"column":null}},"type":"cond-expr","locations":[{"start":{"line":126,"column":31},"end":{"line":126,"column":50}},{"start":{"line":126,"column":50},"end":{"line":126,"column":null}}]},"11":{"loc":{"start":{"line":129,"column":28},"end":{"line":129,"column":null}},"type":"cond-expr","locations":[{"start":{"line":129,"column":43},"end":{"line":129,"column":52}},{"start":{"line":129,"column":52},"end":{"line":129,"column":null}}]},"12":{"loc":{"start":{"line":130,"column":32},"end":{"line":130,"column":null}},"type":"cond-expr","locations":[{"start":{"line":130,"column":47},"end":{"line":130,"column":63}},{"start":{"line":130,"column":63},"end":{"line":130,"column":null}}]},"13":{"loc":{"start":{"line":133,"column":11},"end":{"line":133,"column":23}},"type":"binary-expr","locations":[{"start":{"line":133,"column":11},"end":{"line":133,"column":23}}]},"14":{"loc":{"start":{"line":155,"column":16},"end":{"line":155,"column":null}},"type":"cond-expr","locations":[{"start":{"line":155,"column":34},"end":{"line":155,"column":53}},{"start":{"line":155,"column":53},"end":{"line":155,"column":null}}]},"15":{"loc":{"start":{"line":158,"column":28},"end":{"line":158,"column":null}},"type":"cond-expr","locations":[{"start":{"line":158,"column":46},"end":{"line":158,"column":55}},{"start":{"line":158,"column":55},"end":{"line":158,"column":null}}]},"16":{"loc":{"start":{"line":159,"column":32},"end":{"line":159,"column":null}},"type":"cond-expr","locations":[{"start":{"line":159,"column":50},"end":{"line":159,"column":69}},{"start":{"line":159,"column":69},"end":{"line":159,"column":null}}]},"17":{"loc":{"start":{"line":162,"column":11},"end":{"line":162,"column":26}},"type":"binary-expr","locations":[{"start":{"line":162,"column":11},"end":{"line":162,"column":26}}]},"18":{"loc":{"start":{"line":176,"column":13},"end":{"line":201,"column":null}},"type":"cond-expr","locations":[{"start":{"line":177,"column":14},"end":{"line":201,"column":null}},{"start":{"line":201,"column":14},"end":{"line":201,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":327,"6":327,"7":327,"8":327,"9":327,"10":325,"11":325,"12":325,"13":325,"14":325,"15":15,"16":15,"17":3,"18":12,"19":2,"20":15,"21":6,"22":15,"23":15,"24":325,"25":15,"26":15,"27":15,"28":6,"29":9,"30":9,"31":9,"32":8,"33":2,"34":6,"35":6,"36":6,"37":0,"38":8,"39":179,"40":106},"f":{"0":327,"1":15,"2":15,"3":179,"4":106},"b":{"0":[327,262],"1":[36,289],"2":[3,12],"3":[2],"4":[6],"5":[6],"6":[2,6],"7":[6],"8":[325],"9":[325],"10":[6,319],"11":[6,319],"12":[6,319],"13":[325],"14":[7,318],"15":[7,318],"16":[7,318],"17":[325],"18":[9,316]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/signup/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/signup/page.tsx","statementMap":{"0":{"start":{"line":23,"column":24},"end":{"line":23,"column":null}},"1":{"start":{"line":7,"column":17},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":26},"end":{"line":8,"column":null}},"3":{"start":{"line":9,"column":23},"end":{"line":9,"column":null}},"4":{"start":{"line":10,"column":36},"end":{"line":10,"column":null}},"5":{"start":{"line":13,"column":16},"end":{"line":13,"column":null}},"6":{"start":{"line":24,"column":17},"end":{"line":24,"column":null}},"7":{"start":{"line":26,"column":28},"end":{"line":26,"column":null}},"8":{"start":{"line":27,"column":34},"end":{"line":27,"column":null}},"9":{"start":{"line":28,"column":48},"end":{"line":28,"column":null}},"10":{"start":{"line":29,"column":40},"end":{"line":29,"column":null}},"11":{"start":{"line":30,"column":36},"end":{"line":30,"column":null}},"12":{"start":{"line":31,"column":30},"end":{"line":31,"column":null}},"13":{"start":{"line":36,"column":23},"end":{"line":73,"column":null}},"14":{"start":{"line":37,"column":34},"end":{"line":37,"column":null}},"15":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"16":{"start":{"line":41,"column":6},"end":{"line":41,"column":null}},"17":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"18":{"start":{"line":43,"column":6},"end":{"line":43,"column":null}},"19":{"start":{"line":47,"column":4},"end":{"line":57,"column":null}},"20":{"start":{"line":48,"column":6},"end":{"line":48,"column":null}},"21":{"start":{"line":49,"column":11},"end":{"line":57,"column":null}},"22":{"start":{"line":50,"column":6},"end":{"line":50,"column":null}},"23":{"start":{"line":51,"column":11},"end":{"line":57,"column":null}},"24":{"start":{"line":52,"column":6},"end":{"line":52,"column":null}},"25":{"start":{"line":53,"column":11},"end":{"line":57,"column":null}},"26":{"start":{"line":54,"column":6},"end":{"line":54,"column":null}},"27":{"start":{"line":55,"column":11},"end":{"line":57,"column":null}},"28":{"start":{"line":56,"column":6},"end":{"line":56,"column":null}},"29":{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},"30":{"start":{"line":61,"column":6},"end":{"line":61,"column":null}},"31":{"start":{"line":62,"column":11},"end":{"line":64,"column":null}},"32":{"start":{"line":63,"column":6},"end":{"line":63,"column":null}},"33":{"start":{"line":67,"column":4},"end":{"line":69,"column":null}},"34":{"start":{"line":68,"column":6},"end":{"line":68,"column":null}},"35":{"start":{"line":71,"column":4},"end":{"line":71,"column":null}},"36":{"start":{"line":72,"column":4},"end":{"line":72,"column":null}},"37":{"start":{"line":78,"column":23},"end":{"line":138,"column":null}},"38":{"start":{"line":79,"column":4},"end":{"line":79,"column":null}},"39":{"start":{"line":82,"column":4},"end":{"line":82,"column":null}},"40":{"start":{"line":85,"column":4},"end":{"line":87,"column":null}},"41":{"start":{"line":86,"column":6},"end":{"line":86,"column":null}},"42":{"start":{"line":89,"column":4},"end":{"line":89,"column":null}},"43":{"start":{"line":91,"column":4},"end":{"line":137,"column":null}},"44":{"start":{"line":93,"column":31},"end":{"line":103,"column":null}},"45":{"start":{"line":105,"column":6},"end":{"line":116,"column":null}},"46":{"start":{"line":106,"column":22},"end":{"line":106,"column":null}},"47":{"start":{"line":108,"column":8},"end":{"line":114,"column":null}},"48":{"start":{"line":109,"column":10},"end":{"line":109,"column":null}},"49":{"start":{"line":110,"column":15},"end":{"line":114,"column":null}},"50":{"start":{"line":111,"column":10},"end":{"line":111,"column":null}},"51":{"start":{"line":113,"column":10},"end":{"line":113,"column":null}},"52":{"start":{"line":115,"column":8},"end":{"line":115,"column":null}},"53":{"start":{"line":119,"column":21},"end":{"line":124,"column":null}},"54":{"start":{"line":126,"column":6},"end":{"line":132,"column":null}},"55":{"start":{"line":128,"column":8},"end":{"line":128,"column":null}},"56":{"start":{"line":129,"column":13},"end":{"line":132,"column":null}},"57":{"start":{"line":130,"column":8},"end":{"line":130,"column":null}},"58":{"start":{"line":131,"column":8},"end":{"line":131,"column":null}},"59":{"start":{"line":134,"column":6},"end":{"line":134,"column":null}},"60":{"start":{"line":136,"column":6},"end":{"line":136,"column":null}},"61":{"start":{"line":164,"column":31},"end":{"line":164,"column":null}},"62":{"start":{"line":193,"column":31},"end":{"line":193,"column":null}},"63":{"start":{"line":222,"column":31},"end":{"line":222,"column":null}},"64":{"start":{"line":255,"column":31},"end":{"line":255,"column":null}}},"fnMap":{"0":{"name":"SignupPage","decl":{"start":{"line":23,"column":24},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":24},"end":{"line":331,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":36,"column":23},"end":{"line":36,"column":null}},"loc":{"start":{"line":36,"column":23},"end":{"line":73,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":78,"column":23},"end":{"line":78,"column":30}},"loc":{"start":{"line":78,"column":30},"end":{"line":138,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":164,"column":24},"end":{"line":164,"column":25}},"loc":{"start":{"line":164,"column":31},"end":{"line":164,"column":null}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":193,"column":24},"end":{"line":193,"column":25}},"loc":{"start":{"line":193,"column":31},"end":{"line":193,"column":null}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":222,"column":24},"end":{"line":222,"column":25}},"loc":{"start":{"line":222,"column":31},"end":{"line":222,"column":null}}},"6":{"name":"(anonymous_8)","decl":{"start":{"line":255,"column":24},"end":{"line":255,"column":25}},"loc":{"start":{"line":255,"column":31},"end":{"line":255,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":16},"end":{"line":13,"column":null}},"type":"binary-expr","locations":[{"start":{"line":13,"column":16},"end":{"line":13,"column":47}},{"start":{"line":13,"column":51},"end":{"line":13,"column":null}}]},"1":{"loc":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"2":{"loc":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"3":{"loc":{"start":{"line":47,"column":4},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":57,"column":null}},{"start":{"line":49,"column":11},"end":{"line":57,"column":null}}]},"4":{"loc":{"start":{"line":49,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":11},"end":{"line":57,"column":null}},{"start":{"line":51,"column":11},"end":{"line":57,"column":null}}]},"5":{"loc":{"start":{"line":51,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":51,"column":11},"end":{"line":57,"column":null}},{"start":{"line":53,"column":11},"end":{"line":57,"column":null}}]},"6":{"loc":{"start":{"line":53,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":53,"column":11},"end":{"line":57,"column":null}},{"start":{"line":55,"column":11},"end":{"line":57,"column":null}}]},"7":{"loc":{"start":{"line":55,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":55,"column":11},"end":{"line":57,"column":null}}]},"8":{"loc":{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},{"start":{"line":62,"column":11},"end":{"line":64,"column":null}}]},"9":{"loc":{"start":{"line":62,"column":11},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":62,"column":11},"end":{"line":64,"column":null}}]},"10":{"loc":{"start":{"line":67,"column":4},"end":{"line":69,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":4},"end":{"line":69,"column":null}}]},"11":{"loc":{"start":{"line":67,"column":8},"end":{"line":67,"column":47}},"type":"binary-expr","locations":[{"start":{"line":67,"column":8},"end":{"line":67,"column":23}},{"start":{"line":67,"column":23},"end":{"line":67,"column":47}}]},"12":{"loc":{"start":{"line":85,"column":4},"end":{"line":87,"column":null}},"type":"if","locations":[{"start":{"line":85,"column":4},"end":{"line":87,"column":null}}]},"13":{"loc":{"start":{"line":101,"column":24},"end":{"line":101,"column":null}},"type":"binary-expr","locations":[{"start":{"line":101,"column":24},"end":{"line":101,"column":39}},{"start":{"line":101,"column":39},"end":{"line":101,"column":null}}]},"14":{"loc":{"start":{"line":105,"column":6},"end":{"line":116,"column":null}},"type":"if","locations":[{"start":{"line":105,"column":6},"end":{"line":116,"column":null}}]},"15":{"loc":{"start":{"line":108,"column":8},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":108,"column":8},"end":{"line":114,"column":null}},{"start":{"line":110,"column":15},"end":{"line":114,"column":null}}]},"16":{"loc":{"start":{"line":110,"column":15},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":110,"column":15},"end":{"line":114,"column":null}},{"start":{"line":112,"column":15},"end":{"line":114,"column":null}}]},"17":{"loc":{"start":{"line":111,"column":31},"end":{"line":111,"column":68}},"type":"binary-expr","locations":[{"start":{"line":111,"column":31},"end":{"line":111,"column":43}},{"start":{"line":111,"column":47},"end":{"line":111,"column":68}}]},"18":{"loc":{"start":{"line":113,"column":31},"end":{"line":113,"column":69}},"type":"binary-expr","locations":[{"start":{"line":113,"column":31},"end":{"line":113,"column":43}},{"start":{"line":113,"column":47},"end":{"line":113,"column":69}}]},"19":{"loc":{"start":{"line":126,"column":6},"end":{"line":132,"column":null}},"type":"if","locations":[{"start":{"line":126,"column":6},"end":{"line":132,"column":null}},{"start":{"line":129,"column":13},"end":{"line":132,"column":null}}]},"20":{"loc":{"start":{"line":129,"column":13},"end":{"line":132,"column":null}},"type":"if","locations":[{"start":{"line":129,"column":13},"end":{"line":132,"column":null}}]},"21":{"loc":{"start":{"line":145,"column":7},"end":{"line":145,"column":21}},"type":"binary-expr","locations":[{"start":{"line":145,"column":7},"end":{"line":145,"column":21}}]},"22":{"loc":{"start":{"line":166,"column":16},"end":{"line":166,"column":null}},"type":"cond-expr","locations":[{"start":{"line":166,"column":37},"end":{"line":166,"column":56}},{"start":{"line":166,"column":56},"end":{"line":166,"column":null}}]},"23":{"loc":{"start":{"line":169,"column":28},"end":{"line":169,"column":null}},"type":"cond-expr","locations":[{"start":{"line":169,"column":49},"end":{"line":169,"column":58}},{"start":{"line":169,"column":58},"end":{"line":169,"column":null}}]},"24":{"loc":{"start":{"line":170,"column":32},"end":{"line":170,"column":null}},"type":"cond-expr","locations":[{"start":{"line":170,"column":53},"end":{"line":170,"column":75}},{"start":{"line":170,"column":75},"end":{"line":170,"column":null}}]},"25":{"loc":{"start":{"line":173,"column":11},"end":{"line":173,"column":29}},"type":"binary-expr","locations":[{"start":{"line":173,"column":11},"end":{"line":173,"column":29}}]},"26":{"loc":{"start":{"line":195,"column":16},"end":{"line":195,"column":null}},"type":"cond-expr","locations":[{"start":{"line":195,"column":31},"end":{"line":195,"column":50}},{"start":{"line":195,"column":50},"end":{"line":195,"column":null}}]},"27":{"loc":{"start":{"line":198,"column":28},"end":{"line":198,"column":null}},"type":"cond-expr","locations":[{"start":{"line":198,"column":43},"end":{"line":198,"column":52}},{"start":{"line":198,"column":52},"end":{"line":198,"column":null}}]},"28":{"loc":{"start":{"line":199,"column":32},"end":{"line":199,"column":null}},"type":"cond-expr","locations":[{"start":{"line":199,"column":47},"end":{"line":199,"column":63}},{"start":{"line":199,"column":63},"end":{"line":199,"column":null}}]},"29":{"loc":{"start":{"line":202,"column":11},"end":{"line":202,"column":23}},"type":"binary-expr","locations":[{"start":{"line":202,"column":11},"end":{"line":202,"column":23}}]},"30":{"loc":{"start":{"line":224,"column":16},"end":{"line":224,"column":null}},"type":"cond-expr","locations":[{"start":{"line":224,"column":34},"end":{"line":224,"column":53}},{"start":{"line":224,"column":53},"end":{"line":224,"column":null}}]},"31":{"loc":{"start":{"line":227,"column":28},"end":{"line":227,"column":null}},"type":"cond-expr","locations":[{"start":{"line":227,"column":46},"end":{"line":227,"column":55}},{"start":{"line":227,"column":55},"end":{"line":227,"column":null}}]},"32":{"loc":{"start":{"line":228,"column":32},"end":{"line":228,"column":null}},"type":"cond-expr","locations":[{"start":{"line":228,"column":50},"end":{"line":228,"column":69}},{"start":{"line":228,"column":69},"end":{"line":228,"column":null}}]},"33":{"loc":{"start":{"line":232,"column":12},"end":{"line":236,"column":13}},"type":"cond-expr","locations":[{"start":{"line":232,"column":12},"end":{"line":236,"column":13}}]},"34":{"loc":{"start":{"line":257,"column":16},"end":{"line":257,"column":null}},"type":"cond-expr","locations":[{"start":{"line":257,"column":41},"end":{"line":257,"column":60}},{"start":{"line":257,"column":60},"end":{"line":257,"column":null}}]},"35":{"loc":{"start":{"line":260,"column":28},"end":{"line":260,"column":null}},"type":"cond-expr","locations":[{"start":{"line":260,"column":53},"end":{"line":260,"column":62}},{"start":{"line":260,"column":62},"end":{"line":260,"column":null}}]},"36":{"loc":{"start":{"line":261,"column":32},"end":{"line":261,"column":null}},"type":"cond-expr","locations":[{"start":{"line":261,"column":57},"end":{"line":261,"column":83}},{"start":{"line":261,"column":83},"end":{"line":261,"column":null}}]},"37":{"loc":{"start":{"line":264,"column":11},"end":{"line":264,"column":33}},"type":"binary-expr","locations":[{"start":{"line":264,"column":11},"end":{"line":264,"column":33}}]},"38":{"loc":{"start":{"line":278,"column":13},"end":{"line":303,"column":null}},"type":"cond-expr","locations":[{"start":{"line":279,"column":14},"end":{"line":303,"column":null}},{"start":{"line":303,"column":14},"end":{"line":303,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":774,"7":774,"8":772,"9":772,"10":772,"11":772,"12":772,"13":772,"14":18,"15":18,"16":1,"17":17,"18":1,"19":18,"20":0,"21":18,"22":1,"23":17,"24":1,"25":16,"26":1,"27":15,"28":1,"29":18,"30":1,"31":17,"32":1,"33":18,"34":0,"35":18,"36":18,"37":772,"38":18,"39":18,"40":18,"41":8,"42":10,"43":10,"44":10,"45":8,"46":3,"47":3,"48":2,"49":1,"50":0,"51":1,"52":3,"53":5,"54":5,"55":0,"56":5,"57":5,"58":5,"59":1,"60":9,"61":17,"62":281,"63":217,"64":209},"f":{"0":774,"1":18,"2":18,"3":17,"4":281,"5":217,"6":209},"b":{"0":[2,2],"1":[1,17],"2":[1],"3":[0,18],"4":[1,17],"5":[1,16],"6":[1,15],"7":[1],"8":[1,17],"9":[1],"10":[0],"11":[18,2],"12":[8],"13":[10,8],"14":[3],"15":[2,1],"16":[0,1],"17":[0,0],"18":[1,0],"19":[0,5],"20":[5],"21":[772],"22":[0,772],"23":[0,772],"24":[0,772],"25":[772],"26":[4,768],"27":[4,768],"28":[4,768],"29":[772],"30":[4,768],"31":[4,768],"32":[4,768],"33":[4],"34":[2,770],"35":[2,770],"36":[2,770],"37":[772],"38":[10,762]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/api/auth/[...nextauth]/route.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/api/auth/[...nextauth]/route.ts","statementMap":{"0":{"start":{"line":9,"column":9},"end":{"line":9,"column":20}},"1":{"start":{"line":9,"column":25},"end":{"line":9,"column":36}},"2":{"start":{"line":4,"column":21},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":28},"end":{"line":5,"column":null}},"4":{"start":{"line":7,"column":16},"end":{"line":7,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{},"b":{}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/page.tsx","statementMap":{"0":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":27},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":26},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":17},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":40},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":27},"end":{"line":8,"column":null}},"7":{"start":{"line":11,"column":36},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":32},"end":{"line":13,"column":null}},"10":{"start":{"line":14,"column":32},"end":{"line":14,"column":null}},"11":{"start":{"line":15,"column":28},"end":{"line":15,"column":null}},"12":{"start":{"line":17,"column":2},"end":{"line":36,"column":null}},"13":{"start":{"line":18,"column":4},"end":{"line":21,"column":null}},"14":{"start":{"line":19,"column":6},"end":{"line":19,"column":null}},"15":{"start":{"line":20,"column":6},"end":{"line":20,"column":null}},"16":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"17":{"start":{"line":22,"column":36},"end":{"line":22,"column":null}},"18":{"start":{"line":25,"column":6},"end":{"line":32,"column":null}},"19":{"start":{"line":26,"column":21},"end":{"line":26,"column":null}},"20":{"start":{"line":27,"column":8},"end":{"line":27,"column":null}},"21":{"start":{"line":29,"column":8},"end":{"line":29,"column":null}},"22":{"start":{"line":31,"column":8},"end":{"line":31,"column":null}},"23":{"start":{"line":35,"column":4},"end":{"line":35,"column":null}},"24":{"start":{"line":38,"column":2},"end":{"line":60,"column":null}},"25":{"start":{"line":49,"column":14},"end":{"line":49,"column":27}},"26":{"start":{"line":80,"column":29},"end":{"line":80,"column":null}},"27":{"start":{"line":97,"column":14},"end":{"line":97,"column":42}}},"fnMap":{"0":{"name":"CoursesPage","decl":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"loc":{"start":{"line":10,"column":24},"end":{"line":104,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"loc":{"start":{"line":17,"column":12},"end":{"line":36,"column":5}}},"2":{"name":"fetchCourses","decl":{"start":{"line":24,"column":19},"end":{"line":24,"column":null}},"loc":{"start":{"line":24,"column":19},"end":{"line":33,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":48,"column":27},"end":{"line":48,"column":28}},"loc":{"start":{"line":49,"column":14},"end":{"line":49,"column":27}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":80,"column":23},"end":{"line":80,"column":29}},"loc":{"start":{"line":80,"column":29},"end":{"line":80,"column":null}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":96,"column":25},"end":{"line":96,"column":26}},"loc":{"start":{"line":97,"column":14},"end":{"line":97,"column":42}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":4},"end":{"line":21,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":21,"column":null}}]},"1":{"loc":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"type":"if","locations":[{"start":{"line":22,"column":4},"end":{"line":22,"column":null}}]},"2":{"loc":{"start":{"line":29,"column":17},"end":{"line":29,"column":null}},"type":"cond-expr","locations":[{"start":{"line":29,"column":40},"end":{"line":29,"column":51}},{"start":{"line":29,"column":54},"end":{"line":29,"column":null}}]},"3":{"loc":{"start":{"line":38,"column":2},"end":{"line":60,"column":null}},"type":"if","locations":[{"start":{"line":38,"column":2},"end":{"line":60,"column":null}}]},"4":{"loc":{"start":{"line":38,"column":6},"end":{"line":38,"column":71}},"type":"binary-expr","locations":[{"start":{"line":38,"column":6},"end":{"line":38,"column":31}},{"start":{"line":38,"column":31},"end":{"line":38,"column":61}},{"start":{"line":38,"column":61},"end":{"line":38,"column":71}}]},"5":{"loc":{"start":{"line":77,"column":10},"end":{"line":86,"column":20}},"type":"cond-expr","locations":[{"start":{"line":77,"column":10},"end":{"line":86,"column":20}}]},"6":{"loc":{"start":{"line":87,"column":10},"end":{"line":95,"column":11}},"type":"cond-expr","locations":[{"start":{"line":87,"column":10},"end":{"line":95,"column":11}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0],"1":[0],"2":[0,0],"3":[0],"4":[0,0,0],"5":[0],"6":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/layout.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/layout.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":37}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":39},"end":{"line":4,"column":null}},"3":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"4":{"start":{"line":13,"column":19},"end":{"line":13,"column":null}},"5":{"start":{"line":14,"column":19},"end":{"line":14,"column":34}},"6":{"start":{"line":16,"column":19},"end":{"line":19,"column":null}},"7":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"8":{"start":{"line":44,"column":14},"end":{"line":45,"column":null}}},"fnMap":{"0":{"name":"CourseLayout","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":37}},"loc":{"start":{"line":11,"column":68},"end":{"line":62,"column":null}}},"1":{"name":"isActive","decl":{"start":{"line":21,"column":11},"end":{"line":21,"column":20}},"loc":{"start":{"line":21,"column":49},"end":{"line":23,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":43,"column":26},"end":{"line":43,"column":27}},"loc":{"start":{"line":44,"column":14},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":22,"column":11},"end":{"line":22,"column":null}},"type":"cond-expr","locations":[{"start":{"line":22,"column":19},"end":{"line":22,"column":39}},{"start":{"line":22,"column":39},"end":{"line":22,"column":null}}]},"1":{"loc":{"start":{"line":48,"column":18},"end":{"line":50,"column":null}},"type":"cond-expr","locations":[{"start":{"line":49,"column":22},"end":{"line":49,"column":null}},{"start":{"line":50,"column":22},"end":{"line":50,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/page.tsx","statementMap":{"0":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":37},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":70},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":29},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":31},"end":{"line":8,"column":null}},"7":{"start":{"line":11,"column":17},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":19},"end":{"line":13,"column":34}},"10":{"start":{"line":14,"column":28},"end":{"line":14,"column":null}},"11":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"12":{"start":{"line":17,"column":30},"end":{"line":17,"column":null}},"13":{"start":{"line":18,"column":32},"end":{"line":18,"column":null}},"14":{"start":{"line":19,"column":32},"end":{"line":19,"column":null}},"15":{"start":{"line":20,"column":28},"end":{"line":20,"column":null}},"16":{"start":{"line":21,"column":44},"end":{"line":21,"column":null}},"17":{"start":{"line":23,"column":27},"end":{"line":25,"column":null}},"18":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"19":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}},"20":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"21":{"start":{"line":31,"column":2},"end":{"line":48,"column":null}},"22":{"start":{"line":33,"column":6},"end":{"line":44,"column":null}},"23":{"start":{"line":34,"column":42},"end":{"line":37,"column":null}},"24":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"25":{"start":{"line":39,"column":8},"end":{"line":39,"column":null}},"26":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"27":{"start":{"line":43,"column":8},"end":{"line":43,"column":null}},"28":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}},"29":{"start":{"line":47,"column":18},"end":{"line":47,"column":null}},"30":{"start":{"line":50,"column":2},"end":{"line":71,"column":null}},"31":{"start":{"line":60,"column":16},"end":{"line":60,"column":29}},"32":{"start":{"line":73,"column":2},"end":{"line":87,"column":null}},"33":{"start":{"line":79,"column":27},"end":{"line":79,"column":null}},"34":{"start":{"line":116,"column":20},"end":{"line":116,"column":40}}},"fnMap":{"0":{"name":"CourseWorkspacePage","decl":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"loc":{"start":{"line":10,"column":24},"end":{"line":157,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":23,"column":39},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":39},"end":{"line":25,"column":5}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":24,"column":21},"end":{"line":24,"column":22}},"loc":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}}},"3":{"name":"handleViewPreview","decl":{"start":{"line":27,"column":11},"end":{"line":27,"column":29}},"loc":{"start":{"line":27,"column":47},"end":{"line":29,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":31,"column":12},"end":{"line":31,"column":null}},"loc":{"start":{"line":31,"column":12},"end":{"line":48,"column":5}}},"5":{"name":"load","decl":{"start":{"line":32,"column":19},"end":{"line":32,"column":null}},"loc":{"start":{"line":32,"column":19},"end":{"line":45,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":59,"column":29},"end":{"line":59,"column":30}},"loc":{"start":{"line":60,"column":16},"end":{"line":60,"column":29}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":79,"column":21},"end":{"line":79,"column":27}},"loc":{"start":{"line":79,"column":27},"end":{"line":79,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":115,"column":31},"end":{"line":115,"column":32}},"loc":{"start":{"line":116,"column":20},"end":{"line":116,"column":40}}}},"branchMap":{"0":{"loc":{"start":{"line":41,"column":17},"end":{"line":41,"column":null}},"type":"cond-expr","locations":[{"start":{"line":41,"column":40},"end":{"line":41,"column":51}},{"start":{"line":41,"column":54},"end":{"line":41,"column":null}}]},"1":{"loc":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":47,"column":null}}]},"2":{"loc":{"start":{"line":50,"column":2},"end":{"line":71,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":2},"end":{"line":71,"column":null}}]},"3":{"loc":{"start":{"line":73,"column":2},"end":{"line":87,"column":null}},"type":"if","locations":[{"start":{"line":73,"column":2},"end":{"line":87,"column":null}}]},"4":{"loc":{"start":{"line":73,"column":6},"end":{"line":73,"column":24}},"type":"binary-expr","locations":[{"start":{"line":73,"column":6},"end":{"line":73,"column":15}},{"start":{"line":73,"column":15},"end":{"line":73,"column":24}}]},"5":{"loc":{"start":{"line":77,"column":47},"end":{"line":77,"column":null}},"type":"binary-expr","locations":[{"start":{"line":77,"column":47},"end":{"line":77,"column":56}},{"start":{"line":77,"column":56},"end":{"line":77,"column":null}}]},"6":{"loc":{"start":{"line":93,"column":9},"end":{"line":93,"column":27}},"type":"binary-expr","locations":[{"start":{"line":93,"column":9},"end":{"line":93,"column":27}}]},"7":{"loc":{"start":{"line":107,"column":16},"end":{"line":114,"column":17}},"type":"cond-expr","locations":[{"start":{"line":107,"column":16},"end":{"line":114,"column":17}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0],"4":[0,0],"5":[0,0],"6":[0],"7":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx","statementMap":{"0":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":37},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":39},"end":{"line":6,"column":null}},"5":{"start":{"line":10,"column":17},"end":{"line":10,"column":null}},"6":{"start":{"line":11,"column":17},"end":{"line":11,"column":null}},"7":{"start":{"line":12,"column":19},"end":{"line":12,"column":34}},"8":{"start":{"line":13,"column":21},"end":{"line":13,"column":38}},"9":{"start":{"line":14,"column":28},"end":{"line":14,"column":null}},"10":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"11":{"start":{"line":17,"column":32},"end":{"line":17,"column":null}},"12":{"start":{"line":18,"column":32},"end":{"line":18,"column":null}},"13":{"start":{"line":19,"column":28},"end":{"line":19,"column":null}},"14":{"start":{"line":21,"column":15},"end":{"line":31,"column":null}},"15":{"start":{"line":22,"column":4},"end":{"line":30,"column":null}},"16":{"start":{"line":23,"column":6},"end":{"line":23,"column":null}},"17":{"start":{"line":24,"column":19},"end":{"line":24,"column":null}},"18":{"start":{"line":25,"column":6},"end":{"line":25,"column":null}},"19":{"start":{"line":27,"column":6},"end":{"line":27,"column":null}},"20":{"start":{"line":29,"column":6},"end":{"line":29,"column":null}},"21":{"start":{"line":33,"column":2},"end":{"line":35,"column":null}},"22":{"start":{"line":34,"column":4},"end":{"line":34,"column":null}},"23":{"start":{"line":37,"column":2},"end":{"line":47,"column":null}},"24":{"start":{"line":49,"column":2},"end":{"line":71,"column":null}},"25":{"start":{"line":62,"column":29},"end":{"line":62,"column":null}},"26":{"start":{"line":78,"column":25},"end":{"line":78,"column":null}},"27":{"start":{"line":120,"column":18},"end":{"line":120,"column":31}},"28":{"start":{"line":143,"column":18},"end":{"line":143,"column":31}}},"fnMap":{"0":{"name":"DocumentPreviewPage","decl":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"loc":{"start":{"line":9,"column":24},"end":{"line":159,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":21,"column":27},"end":{"line":21,"column":null}},"loc":{"start":{"line":21,"column":27},"end":{"line":31,"column":5}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":33,"column":12},"end":{"line":33,"column":null}},"loc":{"start":{"line":33,"column":12},"end":{"line":35,"column":5}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":62,"column":23},"end":{"line":62,"column":29}},"loc":{"start":{"line":62,"column":29},"end":{"line":62,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":78,"column":19},"end":{"line":78,"column":25}},"loc":{"start":{"line":78,"column":25},"end":{"line":78,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":119,"column":38},"end":{"line":119,"column":39}},"loc":{"start":{"line":120,"column":18},"end":{"line":120,"column":31}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":142,"column":42},"end":{"line":142,"column":43}},"loc":{"start":{"line":143,"column":18},"end":{"line":143,"column":31}}}},"branchMap":{"0":{"loc":{"start":{"line":27,"column":15},"end":{"line":27,"column":null}},"type":"cond-expr","locations":[{"start":{"line":27,"column":38},"end":{"line":27,"column":49}},{"start":{"line":27,"column":52},"end":{"line":27,"column":null}}]},"1":{"loc":{"start":{"line":37,"column":2},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":2},"end":{"line":47,"column":null}}]},"2":{"loc":{"start":{"line":49,"column":2},"end":{"line":71,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":2},"end":{"line":71,"column":null}}]},"3":{"loc":{"start":{"line":49,"column":6},"end":{"line":49,"column":25}},"type":"binary-expr","locations":[{"start":{"line":49,"column":6},"end":{"line":49,"column":15}},{"start":{"line":49,"column":15},"end":{"line":49,"column":25}}]},"4":{"loc":{"start":{"line":53,"column":47},"end":{"line":53,"column":null}},"type":"binary-expr","locations":[{"start":{"line":53,"column":47},"end":{"line":53,"column":56}},{"start":{"line":53,"column":56},"end":{"line":53,"column":null}}]},"5":{"loc":{"start":{"line":88,"column":11},"end":{"line":88,"column":null}},"type":"binary-expr","locations":[{"start":{"line":88,"column":11},"end":{"line":88,"column":null}}]},"6":{"loc":{"start":{"line":89,"column":74},"end":{"line":89,"column":null}},"type":"cond-expr","locations":[{"start":{"line":89,"column":100},"end":{"line":89,"column":106}},{"start":{"line":89,"column":106},"end":{"line":89,"column":null}}]},"7":{"loc":{"start":{"line":102,"column":14},"end":{"line":106,"column":15}},"type":"cond-expr","locations":[{"start":{"line":102,"column":14},"end":{"line":106,"column":15}}]},"8":{"loc":{"start":{"line":118,"column":14},"end":{"line":129,"column":15}},"type":"cond-expr","locations":[{"start":{"line":118,"column":14},"end":{"line":129,"column":15}}]},"9":{"loc":{"start":{"line":117,"column":13},"end":{"line":117,"column":null}},"type":"binary-expr","locations":[{"start":{"line":117,"column":13},"end":{"line":117,"column":29}},{"start":{"line":117,"column":33},"end":{"line":117,"column":null}}]},"10":{"loc":{"start":{"line":141,"column":14},"end":{"line":152,"column":15}},"type":"cond-expr","locations":[{"start":{"line":141,"column":14},"end":{"line":152,"column":15}}]},"11":{"loc":{"start":{"line":140,"column":13},"end":{"line":140,"column":null}},"type":"binary-expr","locations":[{"start":{"line":140,"column":13},"end":{"line":140,"column":33}},{"start":{"line":140,"column":37},"end":{"line":140,"column":null}}]},"12":{"loc":{"start":{"line":146,"column":23},"end":{"line":146,"column":null}},"type":"cond-expr","locations":[{"start":{"line":146,"column":51},"end":{"line":146,"column":59}},{"start":{"line":146,"column":59},"end":{"line":146,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0],"6":[0,0],"7":[0],"8":[0],"9":[0,0],"10":[0],"11":[0,0],"12":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/study/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/study/page.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":39},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":26},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":27},"end":{"line":8,"column":null}},"7":{"start":{"line":9,"column":27},"end":{"line":9,"column":null}},"8":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":19},"end":{"line":13,"column":34}},"10":{"start":{"line":14,"column":28},"end":{"line":14,"column":null}},"11":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"12":{"start":{"line":17,"column":30},"end":{"line":17,"column":null}},"13":{"start":{"line":18,"column":32},"end":{"line":18,"column":null}},"14":{"start":{"line":20,"column":2},"end":{"line":32,"column":null}},"15":{"start":{"line":22,"column":6},"end":{"line":29,"column":null}},"16":{"start":{"line":23,"column":21},"end":{"line":23,"column":null}},"17":{"start":{"line":24,"column":8},"end":{"line":24,"column":null}},"18":{"start":{"line":28,"column":8},"end":{"line":28,"column":null}},"19":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"20":{"start":{"line":31,"column":18},"end":{"line":31,"column":null}},"21":{"start":{"line":34,"column":2},"end":{"line":40,"column":null}}},"fnMap":{"0":{"name":"StudyPage","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":null}},"loc":{"start":{"line":11,"column":24},"end":{"line":51,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"loc":{"start":{"line":20,"column":12},"end":{"line":32,"column":5}}},"2":{"name":"load","decl":{"start":{"line":21,"column":19},"end":{"line":21,"column":null}},"loc":{"start":{"line":21,"column":19},"end":{"line":30,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"type":"if","locations":[{"start":{"line":31,"column":4},"end":{"line":31,"column":null}}]},"1":{"loc":{"start":{"line":34,"column":2},"end":{"line":40,"column":null}},"type":"if","locations":[{"start":{"line":34,"column":2},"end":{"line":40,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0],"1":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/dashboard/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/dashboard/page.tsx","statementMap":{"0":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":36},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":26},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":17},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":40},"end":{"line":7,"column":null}},"6":{"start":{"line":10,"column":36},"end":{"line":10,"column":null}},"7":{"start":{"line":11,"column":17},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":32},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":46},"end":{"line":13,"column":null}},"10":{"start":{"line":15,"column":2},"end":{"line":30,"column":null}},"11":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"12":{"start":{"line":16,"column":36},"end":{"line":16,"column":null}},"13":{"start":{"line":19,"column":6},"end":{"line":26,"column":null}},"14":{"start":{"line":20,"column":21},"end":{"line":20,"column":null}},"15":{"start":{"line":21,"column":8},"end":{"line":21,"column":null}},"16":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"17":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"18":{"start":{"line":32,"column":2},"end":{"line":41,"column":null}},"19":{"start":{"line":43,"column":2},"end":{"line":46,"column":null}},"20":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"21":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"22":{"start":{"line":48,"column":24},"end":{"line":50,"column":null}},"23":{"start":{"line":49,"column":4},"end":{"line":49,"column":null}}},"fnMap":{"0":{"name":"DashboardPage","decl":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"loc":{"start":{"line":9,"column":24},"end":{"line":141,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":15,"column":12},"end":{"line":15,"column":null}},"loc":{"start":{"line":15,"column":12},"end":{"line":30,"column":5}}},"2":{"name":"fetchCourses","decl":{"start":{"line":18,"column":19},"end":{"line":18,"column":null}},"loc":{"start":{"line":18,"column":19},"end":{"line":27,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":48,"column":24},"end":{"line":48,"column":null}},"loc":{"start":{"line":48,"column":24},"end":{"line":50,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":4},"end":{"line":16,"column":null}}]},"1":{"loc":{"start":{"line":32,"column":2},"end":{"line":41,"column":null}},"type":"if","locations":[{"start":{"line":32,"column":2},"end":{"line":41,"column":null}}]},"2":{"loc":{"start":{"line":43,"column":2},"end":{"line":46,"column":null}},"type":"if","locations":[{"start":{"line":43,"column":2},"end":{"line":46,"column":null}}]},"3":{"loc":{"start":{"line":72,"column":23},"end":{"line":72,"column":82}},"type":"binary-expr","locations":[{"start":{"line":72,"column":23},"end":{"line":72,"column":61}},{"start":{"line":72,"column":61},"end":{"line":72,"column":82}}]},"4":{"loc":{"start":{"line":87,"column":21},"end":{"line":87,"column":null}},"type":"binary-expr","locations":[{"start":{"line":87,"column":21},"end":{"line":87,"column":60}},{"start":{"line":87,"column":60},"end":{"line":87,"column":null}}]},"5":{"loc":{"start":{"line":93,"column":21},"end":{"line":93,"column":null}},"type":"binary-expr","locations":[{"start":{"line":93,"column":21},"end":{"line":93,"column":53}},{"start":{"line":93,"column":53},"end":{"line":93,"column":null}}]},"6":{"loc":{"start":{"line":106,"column":21},"end":{"line":109,"column":36}},"type":"cond-expr","locations":[{"start":{"line":107,"column":22},"end":{"line":109,"column":30}},{"start":{"line":109,"column":22},"end":{"line":109,"column":36}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/CourseCard.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/CourseCard.tsx","statementMap":{"0":{"start":{"line":10,"column":16},"end":{"line":10,"column":27}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}}},"fnMap":{"0":{"name":"CourseCard","decl":{"start":{"line":10,"column":16},"end":{"line":10,"column":27}},"loc":{"start":{"line":10,"column":54},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":22,"column":13},"end":{"line":22,"column":31}},"type":"binary-expr","locations":[{"start":{"line":22,"column":13},"end":{"line":22,"column":31}}]}},"s":{"0":0,"1":0},"f":{"0":0},"b":{"0":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/DocumentList.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/DocumentList.tsx","statementMap":{"0":{"start":{"line":37,"column":16},"end":{"line":37,"column":29}},"1":{"start":{"line":3,"column":57},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":36},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":36},"end":{"line":6,"column":null}},"4":{"start":{"line":7,"column":40},"end":{"line":7,"column":null}},"5":{"start":{"line":16,"column":30},"end":{"line":16,"column":null}},"6":{"start":{"line":19,"column":2},"end":{"line":19,"column":null}},"7":{"start":{"line":19,"column":20},"end":{"line":19,"column":null}},"8":{"start":{"line":20,"column":2},"end":{"line":20,"column":null}},"9":{"start":{"line":20,"column":27},"end":{"line":20,"column":null}},"10":{"start":{"line":21,"column":2},"end":{"line":21,"column":null}},"11":{"start":{"line":25,"column":2},"end":{"line":34,"column":null}},"12":{"start":{"line":26,"column":4},"end":{"line":31,"column":null}},"13":{"start":{"line":33,"column":4},"end":{"line":33,"column":null}},"14":{"start":{"line":43,"column":36},"end":{"line":43,"column":null}},"15":{"start":{"line":44,"column":32},"end":{"line":44,"column":null}},"16":{"start":{"line":45,"column":28},"end":{"line":45,"column":null}},"17":{"start":{"line":46,"column":38},"end":{"line":46,"column":null}},"18":{"start":{"line":47,"column":38},"end":{"line":47,"column":null}},"19":{"start":{"line":48,"column":18},"end":{"line":48,"column":null}},"20":{"start":{"line":50,"column":25},"end":{"line":69,"column":null}},"21":{"start":{"line":52,"column":6},"end":{"line":67,"column":null}},"22":{"start":{"line":53,"column":8},"end":{"line":53,"column":null}},"23":{"start":{"line":53,"column":21},"end":{"line":53,"column":null}},"24":{"start":{"line":54,"column":21},"end":{"line":54,"column":null}},"25":{"start":{"line":55,"column":8},"end":{"line":55,"column":null}},"26":{"start":{"line":57,"column":24},"end":{"line":57,"column":null}},"27":{"start":{"line":58,"column":8},"end":{"line":63,"column":null}},"28":{"start":{"line":59,"column":10},"end":{"line":59,"column":null}},"29":{"start":{"line":60,"column":10},"end":{"line":60,"column":null}},"30":{"start":{"line":61,"column":15},"end":{"line":63,"column":null}},"31":{"start":{"line":62,"column":10},"end":{"line":62,"column":null}},"32":{"start":{"line":65,"column":8},"end":{"line":65,"column":null}},"33":{"start":{"line":66,"column":8},"end":{"line":66,"column":null}},"34":{"start":{"line":72,"column":2},"end":{"line":74,"column":null}},"35":{"start":{"line":73,"column":4},"end":{"line":73,"column":null}},"36":{"start":{"line":76,"column":2},"end":{"line":84,"column":null}},"37":{"start":{"line":77,"column":26},"end":{"line":77,"column":null}},"38":{"start":{"line":77,"column":48},"end":{"line":77,"column":61}},"39":{"start":{"line":78,"column":4},"end":{"line":80,"column":null}},"40":{"start":{"line":79,"column":6},"end":{"line":79,"column":null}},"41":{"start":{"line":79,"column":42},"end":{"line":79,"column":64}},"42":{"start":{"line":81,"column":4},"end":{"line":83,"column":null}},"43":{"start":{"line":82,"column":6},"end":{"line":82,"column":null}},"44":{"start":{"line":82,"column":27},"end":{"line":82,"column":null}},"45":{"start":{"line":87,"column":4},"end":{"line":87,"column":null}},"46":{"start":{"line":88,"column":4},"end":{"line":88,"column":null}},"47":{"start":{"line":92,"column":4},"end":{"line":92,"column":null}},"48":{"start":{"line":92,"column":29},"end":{"line":92,"column":null}},"49":{"start":{"line":95,"column":2},"end":{"line":109,"column":null}},"50":{"start":{"line":99,"column":10},"end":{"line":99,"column":23}},"51":{"start":{"line":111,"column":2},"end":{"line":123,"column":null}},"52":{"start":{"line":116,"column":25},"end":{"line":116,"column":null}},"53":{"start":{"line":125,"column":2},"end":{"line":145,"column":null}},"54":{"start":{"line":178,"column":29},"end":{"line":178,"column":50}},"55":{"start":{"line":179,"column":10},"end":{"line":180,"column":29}},"56":{"start":{"line":183,"column":31},"end":{"line":183,"column":null}}},"fnMap":{"0":{"name":"formatFileSize","decl":{"start":{"line":18,"column":9},"end":{"line":18,"column":24}},"loc":{"start":{"line":18,"column":37},"end":{"line":22,"column":null}}},"1":{"name":"formatTime","decl":{"start":{"line":24,"column":9},"end":{"line":24,"column":20}},"loc":{"start":{"line":24,"column":31},"end":{"line":35,"column":null}}},"2":{"name":"DocumentList","decl":{"start":{"line":37,"column":16},"end":{"line":37,"column":29}},"loc":{"start":{"line":42,"column":20},"end":{"line":241,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":51,"column":4},"end":{"line":51,"column":11}},"loc":{"start":{"line":51,"column":25},"end":{"line":68,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":72,"column":12},"end":{"line":72,"column":null}},"loc":{"start":{"line":72,"column":12},"end":{"line":74,"column":5}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":76,"column":12},"end":{"line":76,"column":null}},"loc":{"start":{"line":76,"column":12},"end":{"line":84,"column":5}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":77,"column":41},"end":{"line":77,"column":42}},"loc":{"start":{"line":77,"column":48},"end":{"line":77,"column":61}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":79,"column":36},"end":{"line":79,"column":42}},"loc":{"start":{"line":79,"column":42},"end":{"line":79,"column":64}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":81,"column":11},"end":{"line":81,"column":null}},"loc":{"start":{"line":81,"column":11},"end":{"line":83,"column":null}}},"9":{"name":"handleRefresh","decl":{"start":{"line":86,"column":11},"end":{"line":86,"column":null}},"loc":{"start":{"line":86,"column":11},"end":{"line":89,"column":null}}},"10":{"name":"toggleExpand","decl":{"start":{"line":91,"column":11},"end":{"line":91,"column":24}},"loc":{"start":{"line":91,"column":34},"end":{"line":93,"column":null}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":92,"column":18},"end":{"line":92,"column":19}},"loc":{"start":{"line":92,"column":29},"end":{"line":92,"column":null}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":98,"column":23},"end":{"line":98,"column":24}},"loc":{"start":{"line":99,"column":10},"end":{"line":99,"column":23}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":116,"column":19},"end":{"line":116,"column":25}},"loc":{"start":{"line":116,"column":25},"end":{"line":116,"column":null}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":177,"column":23},"end":{"line":177,"column":24}},"loc":{"start":{"line":177,"column":24},"end":{"line":237,"column":null}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":183,"column":25},"end":{"line":183,"column":31}},"loc":{"start":{"line":183,"column":31},"end":{"line":183,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":19,"column":2},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":2},"end":{"line":19,"column":null}}]},"1":{"loc":{"start":{"line":20,"column":2},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":20,"column":2},"end":{"line":20,"column":null}}]},"2":{"loc":{"start":{"line":40,"column":2},"end":{"line":40,"column":16}},"type":"default-arg","locations":[{"start":{"line":40,"column":15},"end":{"line":40,"column":16}}]},"3":{"loc":{"start":{"line":51,"column":11},"end":{"line":51,"column":25}},"type":"default-arg","locations":[{"start":{"line":51,"column":20},"end":{"line":51,"column":25}}]},"4":{"loc":{"start":{"line":53,"column":8},"end":{"line":53,"column":null}},"type":"if","locations":[{"start":{"line":53,"column":8},"end":{"line":53,"column":null}}]},"5":{"loc":{"start":{"line":57,"column":24},"end":{"line":57,"column":null}},"type":"cond-expr","locations":[{"start":{"line":57,"column":47},"end":{"line":57,"column":58}},{"start":{"line":57,"column":61},"end":{"line":57,"column":null}}]},"6":{"loc":{"start":{"line":58,"column":8},"end":{"line":63,"column":null}},"type":"if","locations":[{"start":{"line":58,"column":8},"end":{"line":63,"column":null}},{"start":{"line":61,"column":15},"end":{"line":63,"column":null}}]},"7":{"loc":{"start":{"line":58,"column":12},"end":{"line":58,"column":96}},"type":"binary-expr","locations":[{"start":{"line":58,"column":12},"end":{"line":58,"column":56}},{"start":{"line":58,"column":56},"end":{"line":58,"column":96}}]},"8":{"loc":{"start":{"line":61,"column":15},"end":{"line":63,"column":null}},"type":"if","locations":[{"start":{"line":61,"column":15},"end":{"line":63,"column":null}}]},"9":{"loc":{"start":{"line":78,"column":4},"end":{"line":80,"column":null}},"type":"if","locations":[{"start":{"line":78,"column":4},"end":{"line":80,"column":null}}]},"10":{"loc":{"start":{"line":82,"column":6},"end":{"line":82,"column":null}},"type":"if","locations":[{"start":{"line":82,"column":6},"end":{"line":82,"column":null}}]},"11":{"loc":{"start":{"line":92,"column":29},"end":{"line":92,"column":null}},"type":"cond-expr","locations":[{"start":{"line":92,"column":43},"end":{"line":92,"column":50}},{"start":{"line":92,"column":50},"end":{"line":92,"column":null}}]},"12":{"loc":{"start":{"line":95,"column":2},"end":{"line":109,"column":null}},"type":"if","locations":[{"start":{"line":95,"column":2},"end":{"line":109,"column":null}}]},"13":{"loc":{"start":{"line":111,"column":2},"end":{"line":123,"column":null}},"type":"if","locations":[{"start":{"line":111,"column":2},"end":{"line":123,"column":null}}]},"14":{"loc":{"start":{"line":125,"column":2},"end":{"line":145,"column":null}},"type":"if","locations":[{"start":{"line":125,"column":2},"end":{"line":145,"column":null}}]},"15":{"loc":{"start":{"line":151,"column":38},"end":{"line":151,"column":null}},"type":"cond-expr","locations":[{"start":{"line":151,"column":63},"end":{"line":151,"column":69}},{"start":{"line":151,"column":69},"end":{"line":151,"column":null}}]},"16":{"loc":{"start":{"line":160,"column":38},"end":{"line":160,"column":71}},"type":"cond-expr","locations":[{"start":{"line":160,"column":51},"end":{"line":160,"column":68}},{"start":{"line":160,"column":68},"end":{"line":160,"column":71}}]},"17":{"loc":{"start":{"line":172,"column":11},"end":{"line":172,"column":null}},"type":"cond-expr","locations":[{"start":{"line":172,"column":24},"end":{"line":172,"column":42}},{"start":{"line":172,"column":42},"end":{"line":172,"column":null}}]},"18":{"loc":{"start":{"line":190,"column":96},"end":{"line":190,"column":126}},"type":"cond-expr","locations":[{"start":{"line":190,"column":109},"end":{"line":190,"column":123}},{"start":{"line":190,"column":123},"end":{"line":190,"column":126}}]},"19":{"loc":{"start":{"line":216,"column":21},"end":{"line":216,"column":47}},"type":"binary-expr","locations":[{"start":{"line":216,"column":21},"end":{"line":216,"column":47}},{"start":{"line":216,"column":47},"end":{"line":216,"column":63}}]},"20":{"loc":{"start":{"line":226,"column":15},"end":{"line":226,"column":null}},"type":"binary-expr","locations":[{"start":{"line":226,"column":15},"end":{"line":226,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0],"9":[0],"10":[0],"11":[0,0],"12":[0],"13":[0],"14":[0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/ProcessingIndicator.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/ProcessingIndicator.tsx","statementMap":{"0":{"start":{"line":49,"column":16},"end":{"line":49,"column":36}},"1":{"start":{"line":11,"column":104},"end":{"line":36,"column":null}},"2":{"start":{"line":38,"column":54},"end":{"line":47,"column":null}},"3":{"start":{"line":50,"column":17},"end":{"line":50,"column":38}},"4":{"start":{"line":51,"column":21},"end":{"line":51,"column":null}}},"fnMap":{"0":{"name":"ProcessingIndicator","decl":{"start":{"line":49,"column":16},"end":{"line":49,"column":36}},"loc":{"start":{"line":49,"column":95},"end":{"line":64,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":49,"column":53},"end":{"line":49,"column":67}},"type":"default-arg","locations":[{"start":{"line":49,"column":65},"end":{"line":49,"column":67}}]},"1":{"loc":{"start":{"line":51,"column":21},"end":{"line":51,"column":null}},"type":"cond-expr","locations":[{"start":{"line":51,"column":70},"end":{"line":51,"column":89}},{"start":{"line":51,"column":92},"end":{"line":51,"column":null}}]},"2":{"loc":{"start":{"line":51,"column":21},"end":{"line":51,"column":70}},"type":"binary-expr","locations":[{"start":{"line":51,"column":21},"end":{"line":51,"column":30}},{"start":{"line":51,"column":30},"end":{"line":51,"column":50}},{"start":{"line":51,"column":50},"end":{"line":51,"column":70}}]},"3":{"loc":{"start":{"line":59,"column":7},"end":{"line":59,"column":21}},"type":"binary-expr","locations":[{"start":{"line":59,"column":7},"end":{"line":59,"column":21}},{"start":{"line":59,"column":21},"end":{"line":59,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{"0":0},"b":{"0":[0],"1":[0,0],"2":[0,0,0],"3":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentProcessingPanel.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentProcessingPanel.tsx","statementMap":{"0":{"start":{"line":22,"column":16},"end":{"line":22,"column":40}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":38},"end":{"line":4,"column":null}},"3":{"start":{"line":27,"column":30},"end":{"line":27,"column":null}},"4":{"start":{"line":28,"column":32},"end":{"line":28,"column":null}},"5":{"start":{"line":29,"column":28},"end":{"line":29,"column":null}},"6":{"start":{"line":31,"column":15},"end":{"line":41,"column":null}},"7":{"start":{"line":32,"column":4},"end":{"line":40,"column":null}},"8":{"start":{"line":33,"column":6},"end":{"line":33,"column":null}},"9":{"start":{"line":34,"column":19},"end":{"line":34,"column":null}},"10":{"start":{"line":35,"column":6},"end":{"line":35,"column":null}},"11":{"start":{"line":37,"column":6},"end":{"line":37,"column":null}},"12":{"start":{"line":39,"column":6},"end":{"line":39,"column":null}},"13":{"start":{"line":43,"column":2},"end":{"line":45,"column":null}},"14":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"15":{"start":{"line":47,"column":2},"end":{"line":55,"column":null}},"16":{"start":{"line":57,"column":2},"end":{"line":66,"column":null}},"17":{"start":{"line":68,"column":2},"end":{"line":68,"column":null}},"18":{"start":{"line":68,"column":15},"end":{"line":68,"column":null}},"19":{"start":{"line":97,"column":25},"end":{"line":97,"column":null}}},"fnMap":{"0":{"name":"DetailRow","decl":{"start":{"line":13,"column":9},"end":{"line":13,"column":19}},"loc":{"start":{"line":13,"column":78},"end":{"line":20,"column":null}}},"1":{"name":"DocumentProcessingPanel","decl":{"start":{"line":22,"column":16},"end":{"line":22,"column":40}},"loc":{"start":{"line":26,"column":31},"end":{"line":108,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":31,"column":27},"end":{"line":31,"column":null}},"loc":{"start":{"line":31,"column":27},"end":{"line":41,"column":5}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"loc":{"start":{"line":43,"column":12},"end":{"line":45,"column":5}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":97,"column":19},"end":{"line":97,"column":25}},"loc":{"start":{"line":97,"column":25},"end":{"line":97,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":17,"column":56},"end":{"line":17,"column":65}},"type":"binary-expr","locations":[{"start":{"line":17,"column":56},"end":{"line":17,"column":65}}]},"1":{"loc":{"start":{"line":37,"column":15},"end":{"line":37,"column":null}},"type":"cond-expr","locations":[{"start":{"line":37,"column":38},"end":{"line":37,"column":49}},{"start":{"line":37,"column":52},"end":{"line":37,"column":null}}]},"2":{"loc":{"start":{"line":47,"column":2},"end":{"line":55,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":2},"end":{"line":55,"column":null}}]},"3":{"loc":{"start":{"line":57,"column":2},"end":{"line":66,"column":null}},"type":"if","locations":[{"start":{"line":57,"column":2},"end":{"line":66,"column":null}}]},"4":{"loc":{"start":{"line":68,"column":2},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":68,"column":2},"end":{"line":68,"column":null}}]},"5":{"loc":{"start":{"line":79,"column":9},"end":{"line":79,"column":28}},"type":"binary-expr","locations":[{"start":{"line":79,"column":9},"end":{"line":79,"column":28}}]},"6":{"loc":{"start":{"line":85,"column":9},"end":{"line":85,"column":34}},"type":"binary-expr","locations":[{"start":{"line":85,"column":9},"end":{"line":85,"column":34}}]},"7":{"loc":{"start":{"line":95,"column":7},"end":{"line":95,"column":24}},"type":"binary-expr","locations":[{"start":{"line":95,"column":7},"end":{"line":95,"column":24}},{"start":{"line":95,"column":24},"end":{"line":95,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0],"1":[0,0],"2":[0],"3":[0],"4":[0],"5":[0],"6":[0],"7":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentRow.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentRow.tsx","statementMap":{"0":{"start":{"line":20,"column":16},"end":{"line":20,"column":28}},"1":{"start":{"line":3,"column":25},"end":{"line":3,"column":null}},"2":{"start":{"line":5,"column":31},"end":{"line":5,"column":null}},"3":{"start":{"line":6,"column":36},"end":{"line":6,"column":null}},"4":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"5":{"start":{"line":15,"column":20},"end":{"line":15,"column":null}},"6":{"start":{"line":16,"column":2},"end":{"line":16,"column":null}},"7":{"start":{"line":16,"column":27},"end":{"line":16,"column":null}},"8":{"start":{"line":17,"column":2},"end":{"line":17,"column":null}},"9":{"start":{"line":21,"column":34},"end":{"line":21,"column":null}},"10":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"11":{"start":{"line":24,"column":47},"end":{"line":24,"column":null}},"12":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"13":{"start":{"line":26,"column":4},"end":{"line":31,"column":null}},"14":{"start":{"line":27,"column":6},"end":{"line":27,"column":null}},"15":{"start":{"line":28,"column":6},"end":{"line":28,"column":null}},"16":{"start":{"line":30,"column":6},"end":{"line":30,"column":null}}},"fnMap":{"0":{"name":"formatFileSize","decl":{"start":{"line":14,"column":9},"end":{"line":14,"column":24}},"loc":{"start":{"line":14,"column":37},"end":{"line":18,"column":null}}},"1":{"name":"DocumentRow","decl":{"start":{"line":20,"column":16},"end":{"line":20,"column":28}},"loc":{"start":{"line":20,"column":87},"end":{"line":58,"column":null}}},"2":{"name":"handleDelete","decl":{"start":{"line":23,"column":17},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":17},"end":{"line":32,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":2},"end":{"line":15,"column":null}}]},"1":{"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":2},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":4},"end":{"line":24,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0],"1":[0],"2":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentUpload.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentUpload.tsx","statementMap":{"0":{"start":{"line":18,"column":16},"end":{"line":18,"column":31}},"1":{"start":{"line":3,"column":46},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":58},"end":{"line":4,"column":null}},"3":{"start":{"line":19,"column":26},"end":{"line":19,"column":null}},"4":{"start":{"line":20,"column":38},"end":{"line":20,"column":null}},"5":{"start":{"line":21,"column":19},"end":{"line":21,"column":null}},"6":{"start":{"line":23,"column":22},"end":{"line":69,"column":null}},"7":{"start":{"line":25,"column":24},"end":{"line":25,"column":null}},"8":{"start":{"line":26,"column":6},"end":{"line":26,"column":null}},"9":{"start":{"line":26,"column":34},"end":{"line":26,"column":null}},"10":{"start":{"line":28,"column":35},"end":{"line":31,"column":null}},"11":{"start":{"line":28,"column":60},"end":{"line":31,"column":null}},"12":{"start":{"line":33,"column":6},"end":{"line":33,"column":null}},"13":{"start":{"line":33,"column":24},"end":{"line":33,"column":null}},"14":{"start":{"line":35,"column":6},"end":{"line":67,"column":null}},"15":{"start":{"line":35,"column":19},"end":{"line":35,"column":22}},"16":{"start":{"line":36,"column":21},"end":{"line":36,"column":33}},"17":{"start":{"line":37,"column":25},"end":{"line":37,"column":null}},"18":{"start":{"line":39,"column":8},"end":{"line":66,"column":null}},"19":{"start":{"line":40,"column":22},"end":{"line":40,"column":null}},"20":{"start":{"line":42,"column":10},"end":{"line":46,"column":null}},"21":{"start":{"line":43,"column":28},"end":{"line":43,"column":null}},"22":{"start":{"line":44,"column":12},"end":{"line":44,"column":null}},"23":{"start":{"line":44,"column":35},"end":{"line":44,"column":null}},"24":{"start":{"line":45,"column":12},"end":{"line":45,"column":null}},"25":{"start":{"line":48,"column":10},"end":{"line":48,"column":null}},"26":{"start":{"line":50,"column":10},"end":{"line":54,"column":null}},"27":{"start":{"line":51,"column":28},"end":{"line":51,"column":null}},"28":{"start":{"line":52,"column":12},"end":{"line":52,"column":null}},"29":{"start":{"line":52,"column":35},"end":{"line":52,"column":null}},"30":{"start":{"line":53,"column":12},"end":{"line":53,"column":null}},"31":{"start":{"line":56,"column":10},"end":{"line":56,"column":null}},"32":{"start":{"line":58,"column":26},"end":{"line":58,"column":null}},"33":{"start":{"line":59,"column":10},"end":{"line":64,"column":null}},"34":{"start":{"line":60,"column":28},"end":{"line":60,"column":null}},"35":{"start":{"line":61,"column":12},"end":{"line":62,"column":null}},"36":{"start":{"line":62,"column":14},"end":{"line":62,"column":null}},"37":{"start":{"line":63,"column":12},"end":{"line":63,"column":null}},"38":{"start":{"line":65,"column":10},"end":{"line":65,"column":null}},"39":{"start":{"line":72,"column":17},"end":{"line":80,"column":null}},"40":{"start":{"line":74,"column":6},"end":{"line":74,"column":null}},"41":{"start":{"line":75,"column":6},"end":{"line":75,"column":null}},"42":{"start":{"line":76,"column":6},"end":{"line":78,"column":null}},"43":{"start":{"line":77,"column":8},"end":{"line":77,"column":null}},"44":{"start":{"line":83,"column":21},"end":{"line":83,"column":null}},"45":{"start":{"line":83,"column":40},"end":{"line":83,"column":null}},"46":{"start":{"line":84,"column":20},"end":{"line":84,"column":null}},"47":{"start":{"line":84,"column":39},"end":{"line":84,"column":null}},"48":{"start":{"line":90,"column":10},"end":{"line":90,"column":null}},"49":{"start":{"line":91,"column":10},"end":{"line":91,"column":null}},"50":{"start":{"line":93,"column":27},"end":{"line":93,"column":null}},"51":{"start":{"line":95,"column":23},"end":{"line":95,"column":null}},"52":{"start":{"line":109,"column":12},"end":{"line":109,"column":null}},"53":{"start":{"line":109,"column":32},"end":{"line":109,"column":null}},"54":{"start":{"line":110,"column":12},"end":{"line":110,"column":null}},"55":{"start":{"line":125,"column":12},"end":{"line":125,"column":25}},"56":{"start":{"line":140,"column":12},"end":{"line":140,"column":25}}},"fnMap":{"0":{"name":"DocumentUpload","decl":{"start":{"line":18,"column":16},"end":{"line":18,"column":31}},"loc":{"start":{"line":18,"column":95},"end":{"line":152,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":24,"column":4},"end":{"line":24,"column":11}},"loc":{"start":{"line":24,"column":11},"end":{"line":68,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":28,"column":49},"end":{"line":28,"column":50}},"loc":{"start":{"line":28,"column":60},"end":{"line":31,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":33,"column":14},"end":{"line":33,"column":15}},"loc":{"start":{"line":33,"column":24},"end":{"line":33,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":42,"column":18},"end":{"line":42,"column":19}},"loc":{"start":{"line":42,"column":19},"end":{"line":46,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":50,"column":18},"end":{"line":50,"column":19}},"loc":{"start":{"line":50,"column":19},"end":{"line":54,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":59,"column":18},"end":{"line":59,"column":19}},"loc":{"start":{"line":59,"column":19},"end":{"line":64,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":73,"column":4},"end":{"line":73,"column":5}},"loc":{"start":{"line":73,"column":5},"end":{"line":79,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":83,"column":33},"end":{"line":83,"column":34}},"loc":{"start":{"line":83,"column":40},"end":{"line":83,"column":null}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":84,"column":32},"end":{"line":84,"column":33}},"loc":{"start":{"line":84,"column":39},"end":{"line":84,"column":null}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":89,"column":20},"end":{"line":89,"column":21}},"loc":{"start":{"line":89,"column":21},"end":{"line":92,"column":null}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":93,"column":21},"end":{"line":93,"column":27}},"loc":{"start":{"line":93,"column":27},"end":{"line":93,"column":null}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":95,"column":17},"end":{"line":95,"column":23}},"loc":{"start":{"line":95,"column":23},"end":{"line":95,"column":null}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":108,"column":20},"end":{"line":108,"column":21}},"loc":{"start":{"line":108,"column":21},"end":{"line":111,"column":null}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":124,"column":26},"end":{"line":124,"column":27}},"loc":{"start":{"line":125,"column":12},"end":{"line":125,"column":25}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":139,"column":25},"end":{"line":139,"column":26}},"loc":{"start":{"line":140,"column":12},"end":{"line":140,"column":25}}}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":6},"end":{"line":26,"column":null}},"type":"if","locations":[{"start":{"line":26,"column":6},"end":{"line":26,"column":null}}]},"1":{"loc":{"start":{"line":44,"column":12},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":44,"column":12},"end":{"line":44,"column":null}}]},"2":{"loc":{"start":{"line":52,"column":12},"end":{"line":52,"column":null}},"type":"if","locations":[{"start":{"line":52,"column":12},"end":{"line":52,"column":null}}]},"3":{"loc":{"start":{"line":58,"column":26},"end":{"line":58,"column":null}},"type":"cond-expr","locations":[{"start":{"line":58,"column":49},"end":{"line":58,"column":60}},{"start":{"line":58,"column":63},"end":{"line":58,"column":null}}]},"4":{"loc":{"start":{"line":61,"column":12},"end":{"line":62,"column":null}},"type":"if","locations":[{"start":{"line":61,"column":12},"end":{"line":62,"column":null}}]},"5":{"loc":{"start":{"line":76,"column":6},"end":{"line":78,"column":null}},"type":"if","locations":[{"start":{"line":76,"column":6},"end":{"line":78,"column":null}}]},"6":{"loc":{"start":{"line":83,"column":40},"end":{"line":83,"column":null}},"type":"binary-expr","locations":[{"start":{"line":83,"column":40},"end":{"line":83,"column":70}},{"start":{"line":83,"column":70},"end":{"line":83,"column":null}}]},"7":{"loc":{"start":{"line":97,"column":10},"end":{"line":99,"column":null}},"type":"cond-expr","locations":[{"start":{"line":98,"column":14},"end":{"line":98,"column":null}},{"start":{"line":99,"column":14},"end":{"line":99,"column":null}}]},"8":{"loc":{"start":{"line":109,"column":12},"end":{"line":109,"column":null}},"type":"if","locations":[{"start":{"line":109,"column":12},"end":{"line":109,"column":null}}]},"9":{"loc":{"start":{"line":122,"column":7},"end":{"line":122,"column":null}},"type":"binary-expr","locations":[{"start":{"line":122,"column":7},"end":{"line":122,"column":null}}]},"10":{"loc":{"start":{"line":137,"column":7},"end":{"line":137,"column":null}},"type":"binary-expr","locations":[{"start":{"line":137,"column":7},"end":{"line":137,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0],"5":[0],"6":[0,0],"7":[0,0],"8":[0],"9":[0],"10":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/providers/AuthProvider.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/providers/AuthProvider.tsx","statementMap":{"0":{"start":{"line":18,"column":16},"end":{"line":18,"column":28}},"1":{"start":{"line":22,"column":0},"end":{"line":22,"column":15}},"2":{"start":{"line":7,"column":32},"end":{"line":7,"column":null}},"3":{"start":{"line":22,"column":15},"end":{"line":22,"column":28}}},"fnMap":{"0":{"name":"AuthProvider","decl":{"start":{"line":18,"column":16},"end":{"line":18,"column":28}},"loc":{"start":{"line":18,"column":60},"end":{"line":20,"column":null}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0},"b":{}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/OutputPane.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/OutputPane.tsx","statementMap":{"0":{"start":{"line":3,"column":16},"end":{"line":3,"column":null}}},"fnMap":{"0":{"name":"OutputPane","decl":{"start":{"line":3,"column":16},"end":{"line":3,"column":null}},"loc":{"start":{"line":3,"column":16},"end":{"line":38,"column":null}}}},"branchMap":{},"s":{"0":0},"f":{"0":0},"b":{}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SourcePane.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SourcePane.tsx","statementMap":{"0":{"start":{"line":7,"column":16},"end":{"line":7,"column":27}}},"fnMap":{"0":{"name":"SourcePane","decl":{"start":{"line":7,"column":16},"end":{"line":7,"column":27}},"loc":{"start":{"line":7,"column":59},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":9},"end":{"line":14,"column":null}},"type":"binary-expr","locations":[{"start":{"line":14,"column":9},"end":{"line":14,"column":null}}]}},"s":{"0":0},"f":{"0":0},"b":{"0":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SplitPane.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SplitPane.tsx","statementMap":{"0":{"start":{"line":13,"column":16},"end":{"line":13,"column":26}},"1":{"start":{"line":3,"column":62},"end":{"line":3,"column":null}},"2":{"start":{"line":20,"column":40},"end":{"line":20,"column":null}},"3":{"start":{"line":21,"column":23},"end":{"line":21,"column":null}},"4":{"start":{"line":22,"column":19},"end":{"line":22,"column":null}},"5":{"start":{"line":24,"column":22},"end":{"line":49,"column":null}},"6":{"start":{"line":26,"column":6},"end":{"line":26,"column":null}},"7":{"start":{"line":27,"column":6},"end":{"line":27,"column":null}},"8":{"start":{"line":29,"column":26},"end":{"line":34,"column":null}},"9":{"start":{"line":30,"column":8},"end":{"line":30,"column":null}},"10":{"start":{"line":30,"column":56},"end":{"line":30,"column":null}},"11":{"start":{"line":31,"column":21},"end":{"line":31,"column":null}},"12":{"start":{"line":32,"column":24},"end":{"line":32,"column":null}},"13":{"start":{"line":33,"column":8},"end":{"line":33,"column":null}},"14":{"start":{"line":36,"column":24},"end":{"line":42,"column":null}},"15":{"start":{"line":37,"column":8},"end":{"line":37,"column":null}},"16":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"17":{"start":{"line":39,"column":8},"end":{"line":39,"column":null}},"18":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"19":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"20":{"start":{"line":44,"column":6},"end":{"line":44,"column":null}},"21":{"start":{"line":45,"column":6},"end":{"line":45,"column":null}},"22":{"start":{"line":46,"column":6},"end":{"line":46,"column":null}},"23":{"start":{"line":47,"column":6},"end":{"line":47,"column":null}}},"fnMap":{"0":{"name":"SplitPane","decl":{"start":{"line":13,"column":16},"end":{"line":13,"column":26}},"loc":{"start":{"line":19,"column":17},"end":{"line":71,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":25,"column":4},"end":{"line":25,"column":5}},"loc":{"start":{"line":25,"column":5},"end":{"line":48,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":29,"column":26},"end":{"line":29,"column":27}},"loc":{"start":{"line":29,"column":27},"end":{"line":34,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":36,"column":24},"end":{"line":36,"column":null}},"loc":{"start":{"line":36,"column":24},"end":{"line":42,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":25}},"type":"default-arg","locations":[{"start":{"line":16,"column":23},"end":{"line":16,"column":25}}]},"1":{"loc":{"start":{"line":17,"column":2},"end":{"line":17,"column":21}},"type":"default-arg","locations":[{"start":{"line":17,"column":19},"end":{"line":17,"column":21}}]},"2":{"loc":{"start":{"line":18,"column":2},"end":{"line":18,"column":21}},"type":"default-arg","locations":[{"start":{"line":18,"column":19},"end":{"line":18,"column":21}}]},"3":{"loc":{"start":{"line":30,"column":8},"end":{"line":30,"column":null}},"type":"if","locations":[{"start":{"line":30,"column":8},"end":{"line":30,"column":null}}]},"4":{"loc":{"start":{"line":30,"column":12},"end":{"line":30,"column":54}},"type":"binary-expr","locations":[{"start":{"line":30,"column":12},"end":{"line":30,"column":29}},{"start":{"line":30,"column":33},"end":{"line":30,"column":54}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api.ts","statementMap":{"0":{"start":{"line":7,"column":13},"end":{"line":7,"column":21}},"1":{"start":{"line":35,"column":22},"end":{"line":35,"column":30}},"2":{"start":{"line":2,"column":2},"end":{"line":5,"column":null}},"3":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"4":{"start":{"line":9,"column":11},"end":{"line":9,"column":25}},"5":{"start":{"line":11,"column":11},"end":{"line":11,"column":44}},"6":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"7":{"start":{"line":24,"column":2},"end":{"line":31,"column":null}},"8":{"start":{"line":25,"column":22},"end":{"line":25,"column":null}},"9":{"start":{"line":25,"column":57},"end":{"line":25,"column":null}},"10":{"start":{"line":26,"column":4},"end":{"line":29,"column":null}},"11":{"start":{"line":32,"column":2},"end":{"line":32,"column":null}},"12":{"start":{"line":39,"column":65},"end":{"line":39,"column":null}},"13":{"start":{"line":41,"column":42},"end":{"line":43,"column":null}},"14":{"start":{"line":45,"column":2},"end":{"line":47,"column":null}},"15":{"start":{"line":46,"column":4},"end":{"line":46,"column":null}},"16":{"start":{"line":49,"column":2},"end":{"line":51,"column":null}},"17":{"start":{"line":50,"column":4},"end":{"line":50,"column":null}},"18":{"start":{"line":53,"column":19},"end":{"line":57,"column":null}},"19":{"start":{"line":59,"column":2},"end":{"line":59,"column":null}}},"fnMap":{"0":{"name":"(anonymous_3)","decl":{"start":{"line":8,"column":2},"end":{"line":8,"column":null}},"loc":{"start":{"line":12,"column":4},"end":{"line":15,"column":null}}},"1":{"name":"handleResponse","decl":{"start":{"line":23,"column":15},"end":{"line":23,"column":33}},"loc":{"start":{"line":23,"column":51},"end":{"line":33,"column":null}}},"2":{"name":"(anonymous_5)","decl":{"start":{"line":25,"column":50},"end":{"line":25,"column":57}},"loc":{"start":{"line":25,"column":57},"end":{"line":25,"column":null}}},"3":{"name":"apiFetch","decl":{"start":{"line":35,"column":22},"end":{"line":35,"column":30}},"loc":{"start":{"line":37,"column":28},"end":{"line":60,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":2},"end":{"line":5,"column":null}},"type":"binary-expr","locations":[{"start":{"line":2,"column":2},"end":{"line":2,"column":38}},{"start":{"line":3,"column":3},"end":{"line":5,"column":32}}]},"1":{"loc":{"start":{"line":3,"column":3},"end":{"line":5,"column":32}},"type":"cond-expr","locations":[{"start":{"line":4,"column":6},"end":{"line":4,"column":46}},{"start":{"line":5,"column":6},"end":{"line":5,"column":32}}]},"2":{"loc":{"start":{"line":24,"column":2},"end":{"line":31,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":2},"end":{"line":31,"column":null}}]},"3":{"loc":{"start":{"line":28,"column":6},"end":{"line":28,"column":84}},"type":"binary-expr","locations":[{"start":{"line":28,"column":6},"end":{"line":28,"column":22}},{"start":{"line":28,"column":26},"end":{"line":28,"column":43}},{"start":{"line":28,"column":47},"end":{"line":28,"column":84}}]},"4":{"loc":{"start":{"line":37,"column":2},"end":{"line":37,"column":28}},"type":"default-arg","locations":[{"start":{"line":37,"column":26},"end":{"line":37,"column":28}}]},"5":{"loc":{"start":{"line":45,"column":2},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":2},"end":{"line":47,"column":null}}]},"6":{"loc":{"start":{"line":49,"column":2},"end":{"line":51,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":2},"end":{"line":51,"column":null}}]},"7":{"loc":{"start":{"line":49,"column":6},"end":{"line":49,"column":57}},"type":"binary-expr","locations":[{"start":{"line":49,"column":6},"end":{"line":49,"column":28}},{"start":{"line":49,"column":28},"end":{"line":49,"column":57}}]},"8":{"loc":{"start":{"line":56,"column":10},"end":{"line":56,"column":null}},"type":"cond-expr","locations":[{"start":{"line":56,"column":37},"end":{"line":56,"column":44}},{"start":{"line":56,"column":44},"end":{"line":56,"column":null}}]},"9":{"loc":{"start":{"line":56,"column":44},"end":{"line":56,"column":null}},"type":"cond-expr","locations":[{"start":{"line":56,"column":65},"end":{"line":56,"column":88}},{"start":{"line":56,"column":88},"end":{"line":56,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0,0],"1":[0,0],"2":[0],"3":[0,0,0],"4":[0],"5":[0],"6":[0],"7":[0,0],"8":[0,0],"9":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/adapters.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/adapters.ts","statementMap":{"0":{"start":{"line":43,"column":16},"end":{"line":43,"column":36}},"1":{"start":{"line":27,"column":16},"end":{"line":27,"column":33}},"2":{"start":{"line":72,"column":16},"end":{"line":72,"column":37}},"3":{"start":{"line":13,"column":2},"end":{"line":20,"column":null}},"4":{"start":{"line":14,"column":16},"end":{"line":14,"column":34}},"5":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"6":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"7":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"8":{"start":{"line":16,"column":81},"end":{"line":16,"column":null}},"9":{"start":{"line":17,"column":4},"end":{"line":17,"column":null}},"10":{"start":{"line":17,"column":79},"end":{"line":17,"column":null}},"11":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"12":{"start":{"line":18,"column":25},"end":{"line":18,"column":null}},"13":{"start":{"line":19,"column":4},"end":{"line":19,"column":null}},"14":{"start":{"line":19,"column":13},"end":{"line":19,"column":null}},"15":{"start":{"line":21,"column":14},"end":{"line":21,"column":null}},"16":{"start":{"line":22,"column":2},"end":{"line":22,"column":null}},"17":{"start":{"line":25,"column":55},"end":{"line":25,"column":null}},"18":{"start":{"line":28,"column":2},"end":{"line":40,"column":null}},"19":{"start":{"line":44,"column":59},"end":{"line":44,"column":null}},"20":{"start":{"line":45,"column":2},"end":{"line":51,"column":null}},"21":{"start":{"line":46,"column":4},"end":{"line":50,"column":null}},"22":{"start":{"line":47,"column":6},"end":{"line":47,"column":null}},"23":{"start":{"line":49,"column":6},"end":{"line":49,"column":null}},"24":{"start":{"line":53,"column":2},"end":{"line":69,"column":null}},"25":{"start":{"line":73,"column":2},"end":{"line":80,"column":null}}},"fnMap":{"0":{"name":"mimeToLabel","decl":{"start":{"line":12,"column":9},"end":{"line":12,"column":21}},"loc":{"start":{"line":12,"column":58},"end":{"line":23,"column":null}}},"1":{"name":"toDocumentListRow","decl":{"start":{"line":27,"column":16},"end":{"line":27,"column":33}},"loc":{"start":{"line":27,"column":59},"end":{"line":41,"column":null}}},"2":{"name":"toDocumentDetailView","decl":{"start":{"line":43,"column":16},"end":{"line":43,"column":36}},"loc":{"start":{"line":43,"column":60},"end":{"line":70,"column":null}}},"3":{"name":"toDocumentPreviewView","decl":{"start":{"line":72,"column":16},"end":{"line":72,"column":37}},"loc":{"start":{"line":72,"column":62},"end":{"line":81,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":2},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":13,"column":2},"end":{"line":20,"column":null}}]},"1":{"loc":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":4},"end":{"line":15,"column":null}}]},"2":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":4},"end":{"line":16,"column":null}}]},"3":{"loc":{"start":{"line":17,"column":4},"end":{"line":17,"column":null}},"type":"if","locations":[{"start":{"line":17,"column":4},"end":{"line":17,"column":null}}]},"4":{"loc":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":18,"column":null}}]},"5":{"loc":{"start":{"line":19,"column":4},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":4},"end":{"line":19,"column":null}}]},"6":{"loc":{"start":{"line":22,"column":9},"end":{"line":22,"column":null}},"type":"binary-expr","locations":[{"start":{"line":22,"column":9},"end":{"line":22,"column":16}},{"start":{"line":22,"column":16},"end":{"line":22,"column":null}}]},"7":{"loc":{"start":{"line":45,"column":2},"end":{"line":51,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":2},"end":{"line":51,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0],"5":[0],"6":[0,0],"7":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/courses.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/courses.ts","statementMap":{"0":{"start":{"line":28,"column":22},"end":{"line":28,"column":34}},"1":{"start":{"line":14,"column":22},"end":{"line":14,"column":33}},"2":{"start":{"line":21,"column":22},"end":{"line":21,"column":40}},"3":{"start":{"line":4,"column":22},"end":{"line":4,"column":34}},"4":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"5":{"start":{"line":9,"column":2},"end":{"line":11,"column":null}},"6":{"start":{"line":18,"column":2},"end":{"line":18,"column":null}},"7":{"start":{"line":25,"column":2},"end":{"line":25,"column":null}},"8":{"start":{"line":32,"column":2},"end":{"line":36,"column":null}}},"fnMap":{"0":{"name":"fetchCourses","decl":{"start":{"line":4,"column":22},"end":{"line":4,"column":34}},"loc":{"start":{"line":7,"column":22},"end":{"line":12,"column":null}}},"1":{"name":"fetchCourse","decl":{"start":{"line":14,"column":22},"end":{"line":14,"column":33}},"loc":{"start":{"line":16,"column":22},"end":{"line":19,"column":null}}},"2":{"name":"fetchCourseLessons","decl":{"start":{"line":21,"column":22},"end":{"line":21,"column":40}},"loc":{"start":{"line":23,"column":22},"end":{"line":26,"column":null}}},"3":{"name":"createCourse","decl":{"start":{"line":28,"column":22},"end":{"line":28,"column":34}},"loc":{"start":{"line":30,"column":22},"end":{"line":37,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":5,"column":2},"end":{"line":5,"column":10}},"type":"default-arg","locations":[{"start":{"line":5,"column":9},"end":{"line":5,"column":10}}]},"1":{"loc":{"start":{"line":6,"column":2},"end":{"line":6,"column":13}},"type":"default-arg","locations":[{"start":{"line":6,"column":10},"end":{"line":6,"column":13}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/documents.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/documents.ts","statementMap":{"0":{"start":{"line":65,"column":22},"end":{"line":65,"column":36}},"1":{"start":{"line":33,"column":22},"end":{"line":33,"column":41}},"2":{"start":{"line":42,"column":22},"end":{"line":42,"column":42}},"3":{"start":{"line":24,"column":22},"end":{"line":24,"column":41}},"4":{"start":{"line":15,"column":22},"end":{"line":15,"column":40}},"5":{"start":{"line":85,"column":22},"end":{"line":85,"column":43}},"6":{"start":{"line":77,"column":22},"end":{"line":77,"column":41}},"7":{"start":{"line":93,"column":22},"end":{"line":93,"column":44}},"8":{"start":{"line":103,"column":22},"end":{"line":103,"column":47}},"9":{"start":{"line":51,"column":22},"end":{"line":51,"column":36}},"10":{"start":{"line":1,"column":35},"end":{"line":1,"column":null}},"11":{"start":{"line":11,"column":79},"end":{"line":11,"column":null}},"12":{"start":{"line":19,"column":2},"end":{"line":21,"column":null}},"13":{"start":{"line":28,"column":2},"end":{"line":30,"column":null}},"14":{"start":{"line":37,"column":2},"end":{"line":39,"column":null}},"15":{"start":{"line":46,"column":2},"end":{"line":48,"column":null}},"16":{"start":{"line":56,"column":19},"end":{"line":56,"column":null}},"17":{"start":{"line":57,"column":2},"end":{"line":57,"column":null}},"18":{"start":{"line":58,"column":2},"end":{"line":62,"column":null}},"19":{"start":{"line":69,"column":2},"end":{"line":72,"column":null}},"20":{"start":{"line":81,"column":16},"end":{"line":81,"column":null}},"21":{"start":{"line":82,"column":2},"end":{"line":82,"column":null}},"22":{"start":{"line":89,"column":15},"end":{"line":89,"column":null}},"23":{"start":{"line":90,"column":2},"end":{"line":90,"column":null}},"24":{"start":{"line":97,"column":15},"end":{"line":97,"column":null}},"25":{"start":{"line":98,"column":2},"end":{"line":98,"column":null}},"26":{"start":{"line":109,"column":2},"end":{"line":123,"column":null}},"27":{"start":{"line":109,"column":15},"end":{"line":109,"column":18}},"28":{"start":{"line":110,"column":4},"end":{"line":121,"column":null}},"29":{"start":{"line":111,"column":21},"end":{"line":111,"column":null}},"30":{"start":{"line":112,"column":6},"end":{"line":114,"column":null}},"31":{"start":{"line":113,"column":8},"end":{"line":113,"column":null}},"32":{"start":{"line":116,"column":6},"end":{"line":120,"column":null}},"33":{"start":{"line":119,"column":8},"end":{"line":119,"column":null}},"34":{"start":{"line":122,"column":4},"end":{"line":122,"column":null}},"35":{"start":{"line":122,"column":35},"end":{"line":122,"column":null}},"36":{"start":{"line":124,"column":2},"end":{"line":124,"column":null}}},"fnMap":{"0":{"name":"fetchDocumentsList","decl":{"start":{"line":15,"column":22},"end":{"line":15,"column":40}},"loc":{"start":{"line":17,"column":22},"end":{"line":22,"column":null}}},"1":{"name":"fetchDocumentStatus","decl":{"start":{"line":24,"column":22},"end":{"line":24,"column":41}},"loc":{"start":{"line":26,"column":22},"end":{"line":31,"column":null}}},"2":{"name":"fetchDocumentDetail","decl":{"start":{"line":33,"column":22},"end":{"line":33,"column":41}},"loc":{"start":{"line":35,"column":22},"end":{"line":40,"column":null}}},"3":{"name":"fetchDocumentPreview","decl":{"start":{"line":42,"column":22},"end":{"line":42,"column":42}},"loc":{"start":{"line":44,"column":22},"end":{"line":49,"column":null}}},"4":{"name":"uploadDocument","decl":{"start":{"line":51,"column":22},"end":{"line":51,"column":36}},"loc":{"start":{"line":54,"column":22},"end":{"line":63,"column":null}}},"5":{"name":"deleteDocument","decl":{"start":{"line":65,"column":22},"end":{"line":65,"column":36}},"loc":{"start":{"line":67,"column":22},"end":{"line":73,"column":null}}},"6":{"name":"getDocumentListRows","decl":{"start":{"line":77,"column":22},"end":{"line":77,"column":41}},"loc":{"start":{"line":79,"column":22},"end":{"line":83,"column":null}}},"7":{"name":"getDocumentDetailView","decl":{"start":{"line":85,"column":22},"end":{"line":85,"column":43}},"loc":{"start":{"line":87,"column":22},"end":{"line":91,"column":null}}},"8":{"name":"getDocumentPreviewView","decl":{"start":{"line":93,"column":22},"end":{"line":93,"column":44}},"loc":{"start":{"line":95,"column":22},"end":{"line":99,"column":null}}},"9":{"name":"pollDocumentUntilTerminal","decl":{"start":{"line":103,"column":22},"end":{"line":103,"column":47}},"loc":{"start":{"line":107,"column":18},"end":{"line":125,"column":null}}},"10":{"name":"(anonymous_21)","decl":{"start":{"line":122,"column":22},"end":{"line":122,"column":23}},"loc":{"start":{"line":122,"column":35},"end":{"line":122,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":106,"column":2},"end":{"line":106,"column":19}},"type":"default-arg","locations":[{"start":{"line":106,"column":15},"end":{"line":106,"column":19}}]},"1":{"loc":{"start":{"line":107,"column":2},"end":{"line":107,"column":18}},"type":"default-arg","locations":[{"start":{"line":107,"column":16},"end":{"line":107,"column":18}}]},"2":{"loc":{"start":{"line":112,"column":6},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":112,"column":6},"end":{"line":114,"column":null}}]},"3":{"loc":{"start":{"line":112,"column":10},"end":{"line":112,"column":66}},"type":"binary-expr","locations":[{"start":{"line":112,"column":10},"end":{"line":112,"column":39}},{"start":{"line":112,"column":39},"end":{"line":112,"column":66}}]},"4":{"loc":{"start":{"line":116,"column":6},"end":{"line":120,"column":null}},"type":"if","locations":[{"start":{"line":116,"column":6},"end":{"line":120,"column":null}},{"start":{"line":118,"column":13},"end":{"line":120,"column":null}}]},"5":{"loc":{"start":{"line":116,"column":10},"end":{"line":116,"column":56}},"type":"binary-expr","locations":[{"start":{"line":116,"column":10},"end":{"line":116,"column":33}},{"start":{"line":116,"column":37},"end":{"line":116,"column":56}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/index.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/index.ts","statementMap":{"0":{"start":{"line":1,"column":14},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":14},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":14},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":14},"end":{"line":4,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{},"b":{}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/auth/config.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/auth/config.ts","statementMap":{"0":{"start":{"line":6,"column":13},"end":{"line":6,"column":40}},"1":{"start":{"line":2,"column":32},"end":{"line":2,"column":null}},"2":{"start":{"line":4,"column":16},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":40},"end":{"line":83,"column":null}},"4":{"start":{"line":15,"column":8},"end":{"line":17,"column":null}},"5":{"start":{"line":16,"column":10},"end":{"line":16,"column":null}},"6":{"start":{"line":19,"column":8},"end":{"line":50,"column":null}},"7":{"start":{"line":20,"column":22},"end":{"line":27,"column":null}},"8":{"start":{"line":29,"column":10},"end":{"line":32,"column":null}},"9":{"start":{"line":30,"column":26},"end":{"line":30,"column":null}},"10":{"start":{"line":30,"column":56},"end":{"line":30,"column":null}},"11":{"start":{"line":31,"column":12},"end":{"line":31,"column":null}},"12":{"start":{"line":34,"column":23},"end":{"line":34,"column":null}},"13":{"start":{"line":36,"column":10},"end":{"line":44,"column":null}},"14":{"start":{"line":46,"column":10},"end":{"line":48,"column":null}},"15":{"start":{"line":47,"column":12},"end":{"line":47,"column":null}},"16":{"start":{"line":49,"column":10},"end":{"line":49,"column":null}},"17":{"start":{"line":56,"column":6},"end":{"line":61,"column":null}},"18":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"19":{"start":{"line":58,"column":8},"end":{"line":58,"column":null}},"20":{"start":{"line":59,"column":8},"end":{"line":59,"column":null}},"21":{"start":{"line":60,"column":8},"end":{"line":60,"column":null}},"22":{"start":{"line":62,"column":6},"end":{"line":62,"column":null}},"23":{"start":{"line":65,"column":6},"end":{"line":70,"column":null}},"24":{"start":{"line":66,"column":9},"end":{"line":66,"column":null}},"25":{"start":{"line":67,"column":9},"end":{"line":67,"column":null}},"26":{"start":{"line":68,"column":9},"end":{"line":68,"column":null}},"27":{"start":{"line":69,"column":9},"end":{"line":69,"column":null}},"28":{"start":{"line":71,"column":6},"end":{"line":71,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":14,"column":6},"end":{"line":14,"column":12}},"loc":{"start":{"line":14,"column":33},"end":{"line":51,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":30,"column":49},"end":{"line":30,"column":56}},"loc":{"start":{"line":30,"column":56},"end":{"line":30,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":55,"column":4},"end":{"line":55,"column":10}},"loc":{"start":{"line":55,"column":29},"end":{"line":63,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":64,"column":4},"end":{"line":64,"column":10}},"loc":{"start":{"line":64,"column":36},"end":{"line":72,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":4,"column":16},"end":{"line":4,"column":null}},"type":"binary-expr","locations":[{"start":{"line":4,"column":16},"end":{"line":4,"column":47}},{"start":{"line":4,"column":51},"end":{"line":4,"column":null}}]},"1":{"loc":{"start":{"line":15,"column":8},"end":{"line":17,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":8},"end":{"line":17,"column":null}}]},"2":{"loc":{"start":{"line":15,"column":12},"end":{"line":15,"column":59}},"type":"binary-expr","locations":[{"start":{"line":15,"column":12},"end":{"line":15,"column":35}},{"start":{"line":15,"column":35},"end":{"line":15,"column":59}}]},"3":{"loc":{"start":{"line":29,"column":10},"end":{"line":32,"column":null}},"type":"if","locations":[{"start":{"line":29,"column":10},"end":{"line":32,"column":null}}]},"4":{"loc":{"start":{"line":31,"column":28},"end":{"line":31,"column":null}},"type":"binary-expr","locations":[{"start":{"line":31,"column":28},"end":{"line":31,"column":40}},{"start":{"line":31,"column":44},"end":{"line":31,"column":null}}]},"5":{"loc":{"start":{"line":37,"column":16},"end":{"line":37,"column":40}},"type":"binary-expr","locations":[{"start":{"line":37,"column":16},"end":{"line":37,"column":33}},{"start":{"line":37,"column":33},"end":{"line":37,"column":40}}]},"6":{"loc":{"start":{"line":38,"column":19},"end":{"line":38,"column":56}},"type":"binary-expr","locations":[{"start":{"line":38,"column":19},"end":{"line":38,"column":39}},{"start":{"line":38,"column":39},"end":{"line":38,"column":56}}]},"7":{"loc":{"start":{"line":39,"column":18},"end":{"line":39,"column":null}},"type":"binary-expr","locations":[{"start":{"line":39,"column":18},"end":{"line":39,"column":45}},{"start":{"line":39,"column":45},"end":{"line":39,"column":null}}]},"8":{"loc":{"start":{"line":40,"column":25},"end":{"line":40,"column":null}},"type":"binary-expr","locations":[{"start":{"line":40,"column":25},"end":{"line":40,"column":42}},{"start":{"line":40,"column":46},"end":{"line":40,"column":null}}]},"9":{"loc":{"start":{"line":41,"column":26},"end":{"line":41,"column":null}},"type":"binary-expr","locations":[{"start":{"line":41,"column":26},"end":{"line":41,"column":44}},{"start":{"line":41,"column":48},"end":{"line":41,"column":null}}]},"10":{"loc":{"start":{"line":42,"column":18},"end":{"line":42,"column":null}},"type":"binary-expr","locations":[{"start":{"line":42,"column":18},"end":{"line":42,"column":37}},{"start":{"line":42,"column":37},"end":{"line":42,"column":null}}]},"11":{"loc":{"start":{"line":43,"column":25},"end":{"line":43,"column":null}},"type":"binary-expr","locations":[{"start":{"line":43,"column":25},"end":{"line":43,"column":52}},{"start":{"line":43,"column":52},"end":{"line":43,"column":null}}]},"12":{"loc":{"start":{"line":46,"column":10},"end":{"line":48,"column":null}},"type":"if","locations":[{"start":{"line":46,"column":10},"end":{"line":48,"column":null}}]},"13":{"loc":{"start":{"line":56,"column":6},"end":{"line":61,"column":null}},"type":"if","locations":[{"start":{"line":56,"column":6},"end":{"line":61,"column":null}}]},"14":{"loc":{"start":{"line":65,"column":6},"end":{"line":70,"column":null}},"type":"if","locations":[{"start":{"line":65,"column":6},"end":{"line":70,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0,0],"1":[0],"2":[0,0],"3":[0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0],"13":[0],"14":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/middleware/auth-guard.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/middleware/auth-guard.ts","statementMap":{"0":{"start":{"line":7,"column":22},"end":{"line":7,"column":36}},"1":{"start":{"line":32,"column":13},"end":{"line":32,"column":19}},"2":{"start":{"line":1,"column":29},"end":{"line":1,"column":null}},"3":{"start":{"line":2,"column":25},"end":{"line":2,"column":null}},"4":{"start":{"line":5,"column":21},"end":{"line":5,"column":null}},"5":{"start":{"line":8,"column":23},"end":{"line":8,"column":38}},"6":{"start":{"line":10,"column":19},"end":{"line":11,"column":null}},"7":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}},"8":{"start":{"line":14,"column":2},"end":{"line":16,"column":null}},"9":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"10":{"start":{"line":18,"column":16},"end":{"line":21,"column":null}},"11":{"start":{"line":23,"column":2},"end":{"line":27,"column":null}},"12":{"start":{"line":24,"column":21},"end":{"line":24,"column":null}},"13":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"14":{"start":{"line":26,"column":4},"end":{"line":26,"column":null}},"15":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}},"16":{"start":{"line":32,"column":22},"end":{"line":34,"column":null}}},"fnMap":{"0":{"name":"authMiddleware","decl":{"start":{"line":7,"column":22},"end":{"line":7,"column":36}},"loc":{"start":{"line":7,"column":57},"end":{"line":30,"column":null}}},"1":{"name":"(anonymous_4)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":5}},"loc":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}},"type":"binary-expr","locations":[{"start":{"line":11,"column":14},"end":{"line":11,"column":35}},{"start":{"line":11,"column":35},"end":{"line":11,"column":null}}]},"1":{"loc":{"start":{"line":14,"column":2},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":2},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":14,"column":6},"end":{"line":14,"column":84}},"type":"binary-expr","locations":[{"start":{"line":14,"column":6},"end":{"line":14,"column":18}},{"start":{"line":14,"column":18},"end":{"line":14,"column":51}},{"start":{"line":14,"column":51},"end":{"line":14,"column":84}}]},"3":{"loc":{"start":{"line":23,"column":2},"end":{"line":27,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":27,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0],"2":[0,0,0],"3":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/courses.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/courses.ts","statementMap":{"0":{"start":{"line":29,"column":22},"end":{"line":29,"column":31}},"1":{"start":{"line":36,"column":22},"end":{"line":36,"column":38}},"2":{"start":{"line":19,"column":22},"end":{"line":19,"column":32}},"3":{"start":{"line":43,"column":22},"end":{"line":43,"column":31}},"4":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"5":{"start":{"line":24,"column":2},"end":{"line":26,"column":null}},"6":{"start":{"line":33,"column":2},"end":{"line":33,"column":null}},"7":{"start":{"line":40,"column":2},"end":{"line":40,"column":null}},"8":{"start":{"line":47,"column":2},"end":{"line":47,"column":null}}},"fnMap":{"0":{"name":"getCourses","decl":{"start":{"line":19,"column":22},"end":{"line":19,"column":32}},"loc":{"start":{"line":22,"column":22},"end":{"line":27,"column":null}}},"1":{"name":"getCourse","decl":{"start":{"line":29,"column":22},"end":{"line":29,"column":31}},"loc":{"start":{"line":31,"column":22},"end":{"line":34,"column":null}}},"2":{"name":"getCourseLessons","decl":{"start":{"line":36,"column":22},"end":{"line":36,"column":38}},"loc":{"start":{"line":38,"column":22},"end":{"line":41,"column":null}}},"3":{"name":"getLesson","decl":{"start":{"line":43,"column":22},"end":{"line":43,"column":31}},"loc":{"start":{"line":45,"column":22},"end":{"line":48,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":20,"column":2},"end":{"line":20,"column":10}},"type":"default-arg","locations":[{"start":{"line":20,"column":9},"end":{"line":20,"column":10}}]},"1":{"loc":{"start":{"line":21,"column":2},"end":{"line":21,"column":13}},"type":"default-arg","locations":[{"start":{"line":21,"column":10},"end":{"line":21,"column":13}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0]}}
-,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/documents.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/documents.ts","statementMap":{"0":{"start":{"line":54,"column":22},"end":{"line":54,"column":36}},"1":{"start":{"line":45,"column":22},"end":{"line":45,"column":39}},"2":{"start":{"line":21,"column":22},"end":{"line":21,"column":34}},"3":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"4":{"start":{"line":30,"column":22},"end":{"line":30,"column":36}},"5":{"start":{"line":1,"column":35},"end":{"line":1,"column":null}},"6":{"start":{"line":25,"column":2},"end":{"line":27,"column":null}},"7":{"start":{"line":35,"column":19},"end":{"line":35,"column":null}},"8":{"start":{"line":36,"column":2},"end":{"line":36,"column":null}},"9":{"start":{"line":38,"column":2},"end":{"line":42,"column":null}},"10":{"start":{"line":49,"column":2},"end":{"line":51,"column":null}},"11":{"start":{"line":58,"column":2},"end":{"line":61,"column":null}},"12":{"start":{"line":74,"column":2},"end":{"line":88,"column":null}},"13":{"start":{"line":74,"column":15},"end":{"line":74,"column":18}},"14":{"start":{"line":75,"column":4},"end":{"line":86,"column":null}},"15":{"start":{"line":76,"column":21},"end":{"line":76,"column":null}},"16":{"start":{"line":77,"column":6},"end":{"line":79,"column":null}},"17":{"start":{"line":78,"column":8},"end":{"line":78,"column":null}},"18":{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},"19":{"start":{"line":84,"column":8},"end":{"line":84,"column":null}},"20":{"start":{"line":87,"column":4},"end":{"line":87,"column":null}},"21":{"start":{"line":87,"column":35},"end":{"line":87,"column":null}},"22":{"start":{"line":89,"column":2},"end":{"line":89,"column":null}}},"fnMap":{"0":{"name":"getDocuments","decl":{"start":{"line":21,"column":22},"end":{"line":21,"column":34}},"loc":{"start":{"line":23,"column":22},"end":{"line":28,"column":null}}},"1":{"name":"uploadDocument","decl":{"start":{"line":30,"column":22},"end":{"line":30,"column":36}},"loc":{"start":{"line":33,"column":22},"end":{"line":43,"column":null}}},"2":{"name":"getDocumentStatus","decl":{"start":{"line":45,"column":22},"end":{"line":45,"column":39}},"loc":{"start":{"line":47,"column":22},"end":{"line":52,"column":null}}},"3":{"name":"deleteDocument","decl":{"start":{"line":54,"column":22},"end":{"line":54,"column":36}},"loc":{"start":{"line":56,"column":22},"end":{"line":62,"column":null}}},"4":{"name":"pollDocumentStatus","decl":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"loc":{"start":{"line":72,"column":18},"end":{"line":90,"column":null}}},"5":{"name":"(anonymous_11)","decl":{"start":{"line":87,"column":22},"end":{"line":87,"column":23}},"loc":{"start":{"line":87,"column":35},"end":{"line":87,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":71,"column":2},"end":{"line":71,"column":19}},"type":"default-arg","locations":[{"start":{"line":71,"column":15},"end":{"line":71,"column":19}}]},"1":{"loc":{"start":{"line":72,"column":2},"end":{"line":72,"column":18}},"type":"default-arg","locations":[{"start":{"line":72,"column":16},"end":{"line":72,"column":18}}]},"2":{"loc":{"start":{"line":77,"column":6},"end":{"line":79,"column":null}},"type":"if","locations":[{"start":{"line":77,"column":6},"end":{"line":79,"column":null}}]},"3":{"loc":{"start":{"line":77,"column":10},"end":{"line":77,"column":66}},"type":"binary-expr","locations":[{"start":{"line":77,"column":10},"end":{"line":77,"column":39}},{"start":{"line":77,"column":39},"end":{"line":77,"column":66}}]},"4":{"loc":{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},"type":"if","locations":[{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},{"start":{"line":83,"column":13},"end":{"line":85,"column":null}}]},"5":{"loc":{"start":{"line":81,"column":10},"end":{"line":81,"column":56}},"type":"binary-expr","locations":[{"start":{"line":81,"column":10},"end":{"line":81,"column":33}},{"start":{"line":81,"column":37},"end":{"line":81,"column":56}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0]}}
-}
diff --git a/apps/web/coverage/lcov-report/base.css b/apps/web/coverage/lcov-report/base.css
deleted file mode 100644
index f418035..0000000
--- a/apps/web/coverage/lcov-report/base.css
+++ /dev/null
@@ -1,224 +0,0 @@
-body, html {
- margin:0; padding: 0;
- height: 100%;
-}
-body {
- font-family: Helvetica Neue, Helvetica, Arial;
- font-size: 14px;
- color:#333;
-}
-.small { font-size: 12px; }
-*, *:after, *:before {
- -webkit-box-sizing:border-box;
- -moz-box-sizing:border-box;
- box-sizing:border-box;
- }
-h1 { font-size: 20px; margin: 0;}
-h2 { font-size: 14px; }
-pre {
- font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
- margin: 0;
- padding: 0;
- -moz-tab-size: 2;
- -o-tab-size: 2;
- tab-size: 2;
-}
-a { color:#0074D9; text-decoration:none; }
-a:hover { text-decoration:underline; }
-.strong { font-weight: bold; }
-.space-top1 { padding: 10px 0 0 0; }
-.pad2y { padding: 20px 0; }
-.pad1y { padding: 10px 0; }
-.pad2x { padding: 0 20px; }
-.pad2 { padding: 20px; }
-.pad1 { padding: 10px; }
-.space-left2 { padding-left:55px; }
-.space-right2 { padding-right:20px; }
-.center { text-align:center; }
-.clearfix { display:block; }
-.clearfix:after {
- content:'';
- display:block;
- height:0;
- clear:both;
- visibility:hidden;
- }
-.fl { float: left; }
-@media only screen and (max-width:640px) {
- .col3 { width:100%; max-width:100%; }
- .hide-mobile { display:none!important; }
-}
-
-.quiet {
- color: #7f7f7f;
- color: rgba(0,0,0,0.5);
-}
-.quiet a { opacity: 0.7; }
-
-.fraction {
- font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
- font-size: 10px;
- color: #555;
- background: #E8E8E8;
- padding: 4px 5px;
- border-radius: 3px;
- vertical-align: middle;
-}
-
-div.path a:link, div.path a:visited { color: #333; }
-table.coverage {
- border-collapse: collapse;
- margin: 10px 0 0 0;
- padding: 0;
-}
-
-table.coverage td {
- margin: 0;
- padding: 0;
- vertical-align: top;
-}
-table.coverage td.line-count {
- text-align: right;
- padding: 0 5px 0 20px;
-}
-table.coverage td.line-coverage {
- text-align: right;
- padding-right: 10px;
- min-width:20px;
-}
-
-table.coverage td span.cline-any {
- display: inline-block;
- padding: 0 5px;
- width: 100%;
-}
-.missing-if-branch {
- display: inline-block;
- margin-right: 5px;
- border-radius: 3px;
- position: relative;
- padding: 0 4px;
- background: #333;
- color: yellow;
-}
-
-.skip-if-branch {
- display: none;
- margin-right: 10px;
- position: relative;
- padding: 0 4px;
- background: #ccc;
- color: white;
-}
-.missing-if-branch .typ, .skip-if-branch .typ {
- color: inherit !important;
-}
-.coverage-summary {
- border-collapse: collapse;
- width: 100%;
-}
-.coverage-summary tr { border-bottom: 1px solid #bbb; }
-.keyline-all { border: 1px solid #ddd; }
-.coverage-summary td, .coverage-summary th { padding: 10px; }
-.coverage-summary tbody { border: 1px solid #bbb; }
-.coverage-summary td { border-right: 1px solid #bbb; }
-.coverage-summary td:last-child { border-right: none; }
-.coverage-summary th {
- text-align: left;
- font-weight: normal;
- white-space: nowrap;
-}
-.coverage-summary th.file { border-right: none !important; }
-.coverage-summary th.pct { }
-.coverage-summary th.pic,
-.coverage-summary th.abs,
-.coverage-summary td.pct,
-.coverage-summary td.abs { text-align: right; }
-.coverage-summary td.file { white-space: nowrap; }
-.coverage-summary td.pic { min-width: 120px !important; }
-.coverage-summary tfoot td { }
-
-.coverage-summary .sorter {
- height: 10px;
- width: 7px;
- display: inline-block;
- margin-left: 0.5em;
- background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
-}
-.coverage-summary .sorted .sorter {
- background-position: 0 -20px;
-}
-.coverage-summary .sorted-desc .sorter {
- background-position: 0 -10px;
-}
-.status-line { height: 10px; }
-/* yellow */
-.cbranch-no { background: yellow !important; color: #111; }
-/* dark red */
-.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
-.low .chart { border:1px solid #C21F39 }
-.highlighted,
-.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
- background: #C21F39 !important;
-}
-/* medium red */
-.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
-/* light red */
-.low, .cline-no { background:#FCE1E5 }
-/* light green */
-.high, .cline-yes { background:rgb(230,245,208) }
-/* medium green */
-.cstat-yes { background:rgb(161,215,106) }
-/* dark green */
-.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
-.high .chart { border:1px solid rgb(77,146,33) }
-/* dark yellow (gold) */
-.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
-.medium .chart { border:1px solid #f9cd0b; }
-/* light yellow */
-.medium { background: #fff4c2; }
-
-.cstat-skip { background: #ddd; color: #111; }
-.fstat-skip { background: #ddd; color: #111 !important; }
-.cbranch-skip { background: #ddd !important; color: #111; }
-
-span.cline-neutral { background: #eaeaea; }
-
-.coverage-summary td.empty {
- opacity: .5;
- padding-top: 4px;
- padding-bottom: 4px;
- line-height: 1;
- color: #888;
-}
-
-.cover-fill, .cover-empty {
- display:inline-block;
- height: 12px;
-}
-.chart {
- line-height: 0;
-}
-.cover-empty {
- background: white;
-}
-.cover-full {
- border-right: none !important;
-}
-pre.prettyprint {
- border: none !important;
- padding: 0 !important;
- margin: 0 !important;
-}
-.com { color: #999 !important; }
-.ignore-none { color: #999; font-weight: normal; }
-
-.wrapper {
- min-height: 100%;
- height: auto !important;
- height: 100%;
- margin: 0 auto -48px;
-}
-.footer, .push {
- height: 48px;
-}
diff --git a/apps/web/coverage/lcov-report/block-navigation.js b/apps/web/coverage/lcov-report/block-navigation.js
deleted file mode 100644
index 530d1ed..0000000
--- a/apps/web/coverage/lcov-report/block-navigation.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/* eslint-disable */
-var jumpToCode = (function init() {
- // Classes of code we would like to highlight in the file view
- var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
-
- // Elements to highlight in the file listing view
- var fileListingElements = ['td.pct.low'];
-
- // We don't want to select elements that are direct descendants of another match
- var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
-
- // Selector that finds elements on the page to which we can jump
- var selector =
- fileListingElements.join(', ') +
- ', ' +
- notSelector +
- missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
-
- // The NodeList of matching elements
- var missingCoverageElements = document.querySelectorAll(selector);
-
- var currentIndex;
-
- function toggleClass(index) {
- missingCoverageElements
- .item(currentIndex)
- .classList.remove('highlighted');
- missingCoverageElements.item(index).classList.add('highlighted');
- }
-
- function makeCurrent(index) {
- toggleClass(index);
- currentIndex = index;
- missingCoverageElements.item(index).scrollIntoView({
- behavior: 'smooth',
- block: 'center',
- inline: 'center'
- });
- }
-
- function goToPrevious() {
- var nextIndex = 0;
- if (typeof currentIndex !== 'number' || currentIndex === 0) {
- nextIndex = missingCoverageElements.length - 1;
- } else if (missingCoverageElements.length > 1) {
- nextIndex = currentIndex - 1;
- }
-
- makeCurrent(nextIndex);
- }
-
- function goToNext() {
- var nextIndex = 0;
-
- if (
- typeof currentIndex === 'number' &&
- currentIndex < missingCoverageElements.length - 1
- ) {
- nextIndex = currentIndex + 1;
- }
-
- makeCurrent(nextIndex);
- }
-
- return function jump(event) {
- if (
- document.getElementById('fileSearch') === document.activeElement &&
- document.activeElement != null
- ) {
- // if we're currently focused on the search input, we don't want to navigate
- return;
- }
-
- switch (event.which) {
- case 78: // n
- case 74: // j
- goToNext();
- break;
- case 66: // b
- case 75: // k
- case 80: // p
- goToPrevious();
- break;
- }
- };
-})();
-window.addEventListener('keydown', jumpToCode);
diff --git a/apps/web/coverage/lcov-report/favicon.png b/apps/web/coverage/lcov-report/favicon.png
deleted file mode 100644
index c1525b8..0000000
Binary files a/apps/web/coverage/lcov-report/favicon.png and /dev/null differ
diff --git a/apps/web/coverage/lcov-report/index.html b/apps/web/coverage/lcov-report/index.html
deleted file mode 100644
index 31ce679..0000000
--- a/apps/web/coverage/lcov-report/index.html
+++ /dev/null
@@ -1,401 +0,0 @@
-
-
-
-
-
- Code coverage report for All files
-
-
-
-
-
-
-
-
-
-
-
-
All files
-
-
-
- 15.73%
- Statements
- 101/642
-
-
-
-
- 27.47%
- Branches
- 86/313
-
-
-
-
- 8.82%
- Functions
- 12/136
-
-
-
-
- 16.88%
- Lines
- 101/598
-
-
-
-
-
- Press n or j to go to the next uncovered block, b , p or k for the previous block.
-
-
-
- Filter:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/apps/web/coverage/lcov-report/prettify.css b/apps/web/coverage/lcov-report/prettify.css
deleted file mode 100644
index b317a7c..0000000
--- a/apps/web/coverage/lcov-report/prettify.css
+++ /dev/null
@@ -1 +0,0 @@
-.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
diff --git a/apps/web/coverage/lcov-report/prettify.js b/apps/web/coverage/lcov-report/prettify.js
deleted file mode 100644
index b322523..0000000
--- a/apps/web/coverage/lcov-report/prettify.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/* eslint-disable */
-window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^
-
-
-
-