@uncaughtdev/core 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -3
- package/dist/chunk-2YXXFGBV.js +2 -0
- package/dist/chunk-2YXXFGBV.js.map +1 -0
- package/dist/chunk-3FCDO7OR.mjs +23 -0
- package/dist/chunk-3FCDO7OR.mjs.map +1 -0
- package/dist/chunk-A6GKDPT3.mjs +2 -0
- package/dist/chunk-A6GKDPT3.mjs.map +1 -0
- package/dist/chunk-BXMN7NW4.mjs +2 -0
- package/dist/chunk-BXMN7NW4.mjs.map +1 -0
- package/dist/chunk-HANXURHX.mjs +59 -0
- package/dist/chunk-HANXURHX.mjs.map +1 -0
- package/dist/chunk-MSUAXLMV.js +2 -0
- package/dist/chunk-MSUAXLMV.js.map +1 -0
- package/dist/chunk-VQXSHR3C.js +59 -0
- package/dist/chunk-VQXSHR3C.js.map +1 -0
- package/dist/chunk-WZBG5VLB.js +23 -0
- package/dist/chunk-WZBG5VLB.js.map +1 -0
- package/dist/index.d.mts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +7 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -26
- package/dist/index.mjs.map +1 -1
- package/dist/local-api-handler-pages.js +1 -1
- package/dist/local-api-handler-pages.js.map +1 -1
- package/dist/local-api-handler-pages.mjs +1 -1
- package/dist/local-api-handler-pages.mjs.map +1 -1
- package/dist/local-api-handler.d.mts +1 -1
- package/dist/local-api-handler.d.ts +1 -1
- package/dist/local-api-handler.js +1 -1
- package/dist/local-api-handler.mjs +1 -1
- package/dist/local-viewer.js +432 -46
- package/dist/local-viewer.js.map +1 -1
- package/dist/local-viewer.mjs +432 -46
- package/dist/local-viewer.mjs.map +1 -1
- package/dist/mcp-server.d.mts +1 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +22 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-server.mjs +22 -0
- package/dist/mcp-server.mjs.map +1 -0
- package/dist/sqlite-store-4FTNST7O.js +2 -0
- package/dist/sqlite-store-4FTNST7O.js.map +1 -0
- package/dist/sqlite-store-TEXDAAOM.mjs +2 -0
- package/dist/sqlite-store-TEXDAAOM.mjs.map +1 -0
- package/dist/{types-CjgYXVc_.d.mts → types-D1Fw4k-D.d.mts} +12 -1
- package/dist/{types-CjgYXVc_.d.ts → types-D1Fw4k-D.d.ts} +12 -1
- package/package.json +9 -2
- package/dist/chunk-FFHQ452Q.js +0 -2
- package/dist/chunk-FFHQ452Q.js.map +0 -1
- package/dist/chunk-JALIO2BZ.mjs +0 -2
- package/dist/chunk-JALIO2BZ.mjs.map +0 -1
package/dist/local-viewer.js
CHANGED
|
@@ -1,96 +1,482 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict';var
|
|
2
|
+
'use strict';var chunkVQXSHR3C_js=require('./chunk-VQXSHR3C.js');require('./chunk-2YXXFGBV.js');var l=require('fs/promises'),i=require('path'),L=require('http'),child_process=require('child_process');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var l__namespace=/*#__PURE__*/_interopNamespace(l);var i__namespace=/*#__PURE__*/_interopNamespace(i);var L__namespace=/*#__PURE__*/_interopNamespace(L);var O="\x1B[0m",$="\x1B[1m",h="\x1B[2m",k="\x1B[31m",x="\x1B[32m",v="\x1B[33m",b="\x1B[36m",I="\x1B[37m",w="\x1B[90m",N="\x1B[41m",z="\x1B[42m",B="\x1B[43m";function t(e,n){return `${e}${n}${O}`}function P(){return i__namespace.resolve(process.cwd(),".uncaught")}function M(){return i__namespace.join(P(),"issues.json")}function J(){return i__namespace.join(P(),"uncaught.db")}function E(){let e=chunkVQXSHR3C_js.a(J());return e.importFromFiles(P()),e}async function D(){try{let e=E(),n=e.getIssues();return e.close(),n}catch{try{let e=await l__namespace.readFile(M(),"utf-8");return JSON.parse(e)}catch{return []}}}async function G(e){let n=M(),o=n+".tmp";await l__namespace.writeFile(o,JSON.stringify(e,null,2),"utf-8"),await l__namespace.rename(o,n);try{let s=E();for(let a of e)s.upsertIssue(a);s.close();}catch{}}async function _(){let e=await D();if(e.length===0){console.log(t(h,`
|
|
3
3
|
No issues found in .uncaught/
|
|
4
|
-
`)),console.log(
|
|
5
|
-
`));return}console.log(""),console.log(
|
|
6
|
-
Invalid issue number: ${e}`)),console.error(
|
|
7
|
-
`)),process.exitCode=1;return}let
|
|
8
|
-
Fix prompt file not found: ${
|
|
9
|
-
`)),process.exitCode=1;return}if(
|
|
4
|
+
`)),console.log(t(w,` Capture errors with initUncaught() to see them here.
|
|
5
|
+
`));return}console.log(""),console.log(t($," Uncaught Issues")),console.log(t(h," \u2500".repeat(35))),console.log("");let n=K("#","Status","Count","Error","Last Seen");console.log(t(h,` ${n}`)),console.log(t(h," "+"\u2500".repeat(90)));for(let o=0;o<e.length;o++){let s=e[o],a=String(o+1).padStart(3),r=Q(s.status),u=String(s.count).padStart(5),d=F(s.title,45),g=F(s.errorType,15),f=X(s.lastSeen),c=`${t(k,g)} ${t(I,d)}`;console.log(` ${t(b,a)} ${r} ${t(v,u)} ${c} ${t(w,f)}`);}console.log(""),console.log(t(h,` ${e.length} issue(s) total`)),console.log(t(w," Run: uncaught show <n> to view fix prompt, --open to open in editor")),console.log(t(b," Run: npx @uncaughtdev/core dashboard \u2014 for a web UI")),console.log("");}async function V(e,n){let o=await D(),s=parseInt(e,10)-1;if(isNaN(s)||s<0||s>=o.length){console.error(t(k,`
|
|
6
|
+
Invalid issue number: ${e}`)),console.error(t(w,` Valid range: 1-${o.length}
|
|
7
|
+
`)),process.exitCode=1;return}let a=o[s],r=i__namespace.join(P(),"fix-prompts",a.fixPromptFile),u;try{u=await l__namespace.readFile(r,"utf-8");}catch{console.error(t(k,`
|
|
8
|
+
Fix prompt file not found: ${r}
|
|
9
|
+
`)),process.exitCode=1;return}if(n){let d=process.env.EDITOR||"code";console.log(t(h,` Opening ${r} in ${d}...`));try{child_process.spawn(d,[r],{detached:!0,stdio:"ignore"}).unref();}catch{let g=process.platform==="darwin"?"open":"xdg-open";try{child_process.spawn(g,[r],{detached:!0,stdio:"ignore"}).unref();}catch{console.error(t(k,` Could not open editor. File is at: ${r}`));}}return}console.log(""),console.log(t($,` Fix Prompt for Issue #${s+1}`)),console.log(t(h,` Fingerprint: ${a.fingerprint}`)),console.log(t(h,` Count: ${a.count} | Users: ${a.affectedUsers.length}`)),console.log(t(h," \u2500".repeat(35))),console.log(""),console.log(u),console.log(""),console.log(t(w,` File: ${r}`)),console.log(t(w," Tip: run with --open to open in your editor")),console.log("");}async function W(){let e=P();try{await l__namespace.access(e);}catch{console.log(t(h,`
|
|
10
10
|
Nothing to clear \u2014 .uncaught/ does not exist.
|
|
11
|
-
`));return}let
|
|
11
|
+
`));return}try{let o=E();o.deleteAllIssues(),o.close();}catch{}let n=await l__namespace.readdir(e);for(let o of n){let s=i__namespace.join(e,o);await l__namespace.rm(s,{recursive:true,force:true});}console.log(t(x,`
|
|
12
12
|
Cleared all issues in .uncaught/
|
|
13
|
-
`));}async function
|
|
14
|
-
Invalid issue number: ${e}`)),console.error(
|
|
15
|
-
`)),process.exitCode=1;return}let s=
|
|
13
|
+
`));}async function Y(e){let n=await D(),o=parseInt(e,10)-1;if(isNaN(o)||o<0||o>=n.length){console.error(t(k,`
|
|
14
|
+
Invalid issue number: ${e}`)),console.error(t(w,` Valid range: 1-${n.length}
|
|
15
|
+
`)),process.exitCode=1;return}let s=n[o];if(s.status==="resolved"){console.log(t(v,`
|
|
16
16
|
Issue #${o+1} is already resolved.
|
|
17
|
-
`));return}s.status="resolved",await
|
|
18
|
-
Issue #${o+1} marked as resolved.`)),console.log(
|
|
19
|
-
`));}function
|
|
20
|
-
${
|
|
17
|
+
`));return}s.status="resolved",await G(n);try{let a=E();a.updateIssueStatus(s.fingerprint,"resolved"),a.close();}catch{}console.log(t(x,`
|
|
18
|
+
Issue #${o+1} marked as resolved.`)),console.log(t(h,` ${s.errorType}: ${F(s.title,60)}
|
|
19
|
+
`));}function K(e,n,o,s,a){return `${e.padStart(3)} ${n.padEnd(10)} ${o.padStart(5)} ${s.padEnd(60)} ${a}`}function Q(e){switch(e){case "open":return t(`${N}${I}${$}`," OPEN ");case "resolved":return t(`${z}${I}${$}`," DONE ");case "ignored":return t(`${B}${I}${$}`," SKIP ");default:return t(h,String(e).padEnd(6))}}function X(e){try{let n=new Date(e).getTime(),s=Date.now()-n;if(s<0)return "just now";let a=Math.floor(s/1e3);if(a<60)return `${a}s ago`;let r=Math.floor(a/60);if(r<60)return `${r}m ago`;let u=Math.floor(r/60);if(u<24)return `${u}h ago`;let d=Math.floor(u/24);return d<30?`${d}d ago`:new Date(e).toLocaleDateString()}catch{return e}}function F(e,n){return e.length<=n?e:e.slice(0,n-3)+"..."}function U(){console.log(`
|
|
20
|
+
${t($," uncaught")} \u2014 error monitoring for vibe coders
|
|
21
21
|
|
|
22
|
-
${
|
|
23
|
-
${
|
|
22
|
+
${t($," Setup:")}
|
|
23
|
+
${t(b,"npx uncaughtdev init")} Auto-detect framework, install, and patch \u2014 one command
|
|
24
24
|
|
|
25
|
-
${
|
|
25
|
+
${t($," Viewer:")}
|
|
26
26
|
npx uncaughtdev List all captured issues
|
|
27
27
|
npx uncaughtdev list List all captured issues
|
|
28
28
|
npx uncaughtdev show <n> Display fix prompt for issue #n
|
|
29
29
|
npx uncaughtdev show <n> --open Open fix prompt in $EDITOR
|
|
30
30
|
npx uncaughtdev resolve <n> Mark issue #n as resolved
|
|
31
31
|
npx uncaughtdev clear Remove all captured issues
|
|
32
|
+
npx uncaughtdev dashboard Open web dashboard (--port 3300)
|
|
32
33
|
|
|
33
|
-
${
|
|
34
|
+
${t($," Examples:")}
|
|
34
35
|
npx uncaughtdev init Setup Uncaught in your project
|
|
35
36
|
npx uncaughtdev show 1 Print fix prompt for issue #1
|
|
36
37
|
npx uncaughtdev show 3 --open Open issue #3's prompt in editor
|
|
37
|
-
`);}async function
|
|
38
|
-
`,o)+1;return e.slice(0,
|
|
38
|
+
`);}async function y(e){try{return await l__namespace.access(e),!0}catch{return false}}async function S(e){try{return (await l__namespace.stat(e)).isDirectory()}catch{return false}}function Z(e,n){return new Promise(o=>{let s=child_process.spawn(e,n,{stdio:"inherit",shell:true});s.on("close",a=>o(a===0)),s.on("error",()=>o(false));})}async function q(){let e=process.cwd(),n={framework:"unknown",packageManager:"npm",hasSupabase:false,hasTypescript:false,rootDir:e};await y(i__namespace.join(e,"pnpm-lock.yaml"))?n.packageManager="pnpm":await y(i__namespace.join(e,"yarn.lock"))?n.packageManager="yarn":(await y(i__namespace.join(e,"bun.lockb"))||await y(i__namespace.join(e,"bun.lock")))&&(n.packageManager="bun");let o={};try{let a=await l__namespace.readFile(i__namespace.join(e,"package.json"),"utf-8");o=JSON.parse(a);}catch{return n}let s={...o.dependencies??{},...o.devDependencies??{}};return n.hasTypescript="typescript"in s||await y(i__namespace.join(e,"tsconfig.json")),n.hasSupabase="@supabase/supabase-js"in s,"next"in s?await S(i__namespace.join(e,"app"))||await S(i__namespace.join(e,"src","app"))?n.framework="nextjs-app":await S(i__namespace.join(e,"pages"))||await S(i__namespace.join(e,"src","pages"))?n.framework="nextjs-pages":n.framework="nextjs-app":"vite"in s&&("react"in s||"react-dom"in s)?n.framework="vite-react":"react-scripts"in s&&(n.framework="cra"),n}function ee(e,n){switch(e){case "pnpm":return {cmd:"pnpm",args:["add",...n]};case "yarn":return {cmd:"yarn",args:["add",...n]};case "bun":return {cmd:"bun",args:["add",...n]};default:return {cmd:"npm",args:["install",...n]}}}function te(e){let n=-1,o=/^import\s/gm,s;for(;(s=o.exec(e))!==null;)n=s.index;return n}function T(e,n){let o=te(e);if(o>=0){let a=e.indexOf(`
|
|
39
|
+
`,o)+1;return e.slice(0,a)+n+e.slice(a)}let s=e.indexOf(`
|
|
39
40
|
`);return s>=0&&e.slice(0,s).includes("use client")?e.slice(0,s+1)+`
|
|
40
|
-
`+
|
|
41
|
-
\u26A0 Install failed \u2014 packages may not be published yet.`)),console.log(
|
|
42
|
-
`),console.log(
|
|
43
|
-
`),console.log(
|
|
41
|
+
`+n+e.slice(s+1):n+e}async function ne(){let e=process.cwd();console.log(""),console.log(t($," \u{1F9EA} uncaught init")),console.log(t(h," \u2500".repeat(35))),console.log(""),console.log(t(b," \u25B8 Detecting framework..."));let n=await q();if(console.log(` ${t(x,{"nextjs-app":"Next.js (App Router)","nextjs-pages":"Next.js (Pages Router)","vite-react":"Vite + React",cra:"Create React App",unknown:"Unknown"}[n.framework])} \xB7 ${n.packageManager} \xB7 TS=${n.hasTypescript} \xB7 Supabase=${n.hasSupabase}`),console.log(""),n.framework==="unknown"){console.log(t(v," \u26A0 Could not detect framework. Supported: Next.js, Vite+React, CRA")),process.exitCode=1;return}console.log(t(b," \u25B8 Installing packages..."));let s=["@uncaughtdev/core","@uncaughtdev/react"];n.hasSupabase&&s.push("@uncaughtdev/supabase");let a=ee(n.packageManager,s);console.log(t(w,` ${a.cmd} ${a.args.join(" ")}`)),await Z(a.cmd,a.args)?(console.log(t(x," \u2713 Packages installed")),console.log("")):(console.log(t(v,`
|
|
42
|
+
\u26A0 Install failed \u2014 packages may not be published yet.`)),console.log(t(w," Continuing with file patching. Install manually if needed.")),console.log(""));let u=n.hasTypescript?"tsx":"jsx",d=n.hasTypescript?"ts":"js",g=i__namespace.basename(e);if(n.framework==="nextjs-app"){let f=await S(i__namespace.join(e,"app"))?i__namespace.join(e,"app"):i__namespace.join(e,"src","app");console.log(t(b," \u25B8 Setting up providers..."));let c=i__namespace.join(f,`layout.${u}`);await oe(f,c,g,u),console.log(t(b," \u25B8 Creating API route..."));let m=i__namespace.join(f,"api","uncaught","local");await l__namespace.mkdir(m,{recursive:true});let p=i__namespace.join(m,`route.${d}`);await y(p)?console.log(t(v," \u2298 Already exists, skipping")):(await l__namespace.writeFile(p,`export { POST } from '@uncaughtdev/core/local-api-handler';
|
|
43
|
+
`),console.log(t(x,` \u2713 Created ${i__namespace.relative(e,p)}`))),console.log(t(b," \u25B8 Patching next.config...")),await A(e);}else if(n.framework==="nextjs-pages"){let f=await S(i__namespace.join(e,"pages"))?i__namespace.join(e,"pages"):i__namespace.join(e,"src","pages");console.log(t(b," \u25B8 Patching _app..."));let c=i__namespace.join(f,`_app.${u}`);await R(c,g,"pages-app"),console.log(t(b," \u25B8 Creating API route..."));let m=i__namespace.join(f,"api","uncaught");await l__namespace.mkdir(m,{recursive:true});let p=i__namespace.join(m,`local.${d}`);await y(p)?console.log(t(v," \u2298 Already exists, skipping")):(await l__namespace.writeFile(p,`export { default } from '@uncaughtdev/core/local-api-handler/pages';
|
|
44
|
+
`),console.log(t(x,` \u2713 Created ${i__namespace.relative(e,p)}`))),console.log(t(b," \u25B8 Patching next.config...")),await A(e);}else if(n.framework==="vite-react"||n.framework==="cra"){let f=n.framework==="vite-react"?["src/main.tsx","src/main.jsx","main.tsx","main.jsx"]:["src/index.tsx","src/index.jsx"],c=null;for(let m of f){let p=i__namespace.join(e,m);if(await y(p)){c=p;break}}c?(console.log(t(b," \u25B8 Patching entry file...")),await R(c,g,"entry")):console.log(t(v," \u26A0 Could not find entry file. Wrap your root with <UncaughtProvider> manually."));}n.hasSupabase&&(console.log(""),console.log(t(b," \u25B8 Supabase detected! Wrap your client:")),console.log(t(w," import { wrapSupabase } from '@uncaughtdev/supabase';")),console.log(t(w," const supabase = wrapSupabase(createClient(url, key));"))),console.log(""),console.log(t(x," \u2713 Done! Uncaught is now tracking errors.")),console.log(""),console.log(t(I," Start your dev server, trigger an error, then:")),console.log(t(b," npx uncaughtdev")),console.log("");}async function oe(e,n,o,s){let a=process.cwd(),r=i__namespace.join(e,`providers.${s}`),u=i__namespace.relative(a,r),d=i__namespace.relative(a,n);if(await y(r)){let m=await l__namespace.readFile(r,"utf-8");if(m.includes("UncaughtProvider")||m.includes("@uncaughtdev/react"))console.log(t(v,` \u2298 ${u} already has UncaughtProvider, skipping`));else {let p=m;!p.includes("'use client'")&&!p.includes('"use client"')&&(p=`'use client';
|
|
45
|
+
|
|
46
|
+
${p}`),p=T(p,`import { UncaughtProvider } from '@uncaughtdev/react';
|
|
47
|
+
`),await l__namespace.writeFile(r,p),console.log(t(v,` \u26A0 ${u} exists \u2014 added import but you may need to manually wrap with <UncaughtProvider>.`));}}else {let m=`'use client';
|
|
44
48
|
|
|
45
49
|
import { UncaughtProvider } from '@uncaughtdev/react';
|
|
46
50
|
|
|
51
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
52
|
+
return (
|
|
53
|
+
<UncaughtProvider projectKey="${o}" transport="local">
|
|
54
|
+
{children}
|
|
55
|
+
</UncaughtProvider>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
`;await l__namespace.writeFile(r,m),console.log(t(x,` \u2713 Created ${u}`));}if(!await y(n)){await l__namespace.writeFile(n,`import { Providers } from './providers';
|
|
59
|
+
|
|
47
60
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
48
61
|
return (
|
|
49
62
|
<html lang="en">
|
|
50
63
|
<body>
|
|
51
|
-
<
|
|
52
|
-
{children}
|
|
53
|
-
</UncaughtProvider>
|
|
64
|
+
<Providers>{children}</Providers>
|
|
54
65
|
</body>
|
|
55
66
|
</html>
|
|
56
67
|
);
|
|
57
68
|
}
|
|
58
|
-
|
|
69
|
+
`),console.log(t(x,` \u2713 Created ${d}`));return}let g=await l__namespace.readFile(n,"utf-8");if(g.includes("UncaughtProvider")||g.includes("@uncaughtdev/react")||g.includes("./providers")){console.log(t(v,` \u2298 ${d} already has providers, skipping`));return}g=T(g,`import { Providers } from './providers';
|
|
70
|
+
`);let f=false,c=g.match(/(\n(\s*))\{children\}/);c&&(g=g.replace(`${c[1]}{children}`,`${c[1]}<Providers>{children}</Providers>`),f=true),f?(await l__namespace.writeFile(n,g),console.log(t(x,` \u2713 Patched ${d}`))):console.log(t(v,` \u26A0 Could not auto-patch ${d}. Import Providers from './providers' and wrap {children}.`));}async function R(e,n,o){let s=process.cwd(),a=i__namespace.relative(s,e);if(!await y(e)){let c="";o==="pages-app"&&(c=`import { UncaughtProvider } from '@uncaughtdev/react';
|
|
59
71
|
import type { AppProps } from 'next/app';
|
|
60
72
|
|
|
61
73
|
export default function App({ Component, pageProps }: AppProps) {
|
|
62
74
|
return (
|
|
63
|
-
<UncaughtProvider projectKey="${
|
|
75
|
+
<UncaughtProvider projectKey="${n}" transport="local">
|
|
64
76
|
<Component {...pageProps} />
|
|
65
77
|
</UncaughtProvider>
|
|
66
78
|
);
|
|
67
79
|
}
|
|
68
|
-
`),await
|
|
69
|
-
|
|
70
|
-
${
|
|
71
|
-
|
|
72
|
-
${u}
|
|
73
|
-
{children}
|
|
74
|
-
${d}
|
|
75
|
-
`);g=true;}else if(o==="pages-app"){let l=a.match(/<Component\s[^>]*\/>/);l&&(a=a.replace(l[0],`${u}
|
|
76
|
-
${l[0]}
|
|
77
|
-
${d}`),g=true);}else if(o==="entry"){let l=a.match(/<App\s*\/>/);l&&(a=a.replace(l[0],`${u}
|
|
80
|
+
`),await l__namespace.writeFile(e,c),console.log(t(x,` \u2713 Created ${a}`));return}let r=await l__namespace.readFile(e,"utf-8");if(r.includes("UncaughtProvider")||r.includes("@uncaughtdev/react")){console.log(t(v,` \u2298 ${a} already has UncaughtProvider, skipping`));return}r=T(r,`import { UncaughtProvider } from '@uncaughtdev/react';
|
|
81
|
+
`);let d=`<UncaughtProvider projectKey="${n}" transport="${o==="entry"?"console":"local"}">`,g="</UncaughtProvider>",f=false;if(o==="pages-app"){let c=r.match(/<Component\s[^>]*\/>/);c&&(r=r.replace(c[0],`${d}
|
|
82
|
+
${c[0]}
|
|
83
|
+
${g}`),f=true);}else if(o==="entry"){let c=r.match(/<App\s*\/>/);c&&(r=r.replace(c[0],`${d}
|
|
78
84
|
<App />
|
|
79
|
-
${
|
|
85
|
+
${g}`),f=true);}f?(await l__namespace.writeFile(e,r),console.log(t(x,` \u2713 Patched ${a}`))):(console.log(t(v,` \u26A0 Could not auto-patch ${a}. Wrap your root component with:`)),console.log(t(w,` <UncaughtProvider projectKey="${n}" transport="local">{children}</UncaughtProvider>`)));}async function A(e){let n=["next.config.ts","next.config.mjs","next.config.js"],o=null;for(let u of n){let d=i__namespace.join(e,u);if(await y(d)){o=d;break}}let s=`
|
|
80
86
|
webpack: (config, { isServer }) => {
|
|
81
87
|
if (!isServer) {
|
|
82
88
|
config.resolve.fallback = { ...config.resolve.fallback, fs: false, path: false, child_process: false };
|
|
83
89
|
}
|
|
84
90
|
return config;
|
|
85
|
-
},`;if(!o){o=
|
|
91
|
+
},`;if(!o){o=i__namespace.join(e,"next.config.js"),await l__namespace.writeFile(o,`/** @type {import('next').NextConfig} */
|
|
86
92
|
const nextConfig = {${s}
|
|
87
93
|
};
|
|
88
94
|
module.exports = nextConfig;
|
|
89
|
-
`),console.log(
|
|
95
|
+
`),console.log(t(x," \u2713 Created next.config.js"));return}let a=await l__namespace.readFile(o,"utf-8");if(a.includes("fs: false")){console.log(t(v," \u2298 Already has webpack fallback, skipping"));return}if(a.includes("webpack")){console.log(t(v," \u2298 Has custom webpack \u2014 add manually: config.resolve.fallback = { fs: false, path: false, child_process: false }"));return}let r=a.match(/(const\s+\w+\s*=\s*\{|module\.exports\s*=\s*\{|export\s+default\s*\{)/);if(r&&r.index!==void 0){let u=r.index+r[0].length;a=a.slice(0,u)+s+a.slice(u),await l__namespace.writeFile(o,a),console.log(t(x,` \u2713 Patched ${i__namespace.relative(e,o)}`));}else console.log(t(v," \u26A0 Could not auto-patch. Add webpack fallback for fs/path/child_process manually."));}var se=`<!DOCTYPE html>
|
|
96
|
+
<html lang="en">
|
|
97
|
+
<head>
|
|
98
|
+
<meta charset="utf-8">
|
|
99
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
100
|
+
<title>Uncaught Dashboard</title>
|
|
101
|
+
<style>
|
|
102
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
103
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; background: #0a0a0a; color: #e0e0e0; min-height: 100vh; }
|
|
104
|
+
a { color: #60a5fa; text-decoration: none; }
|
|
105
|
+
code { background: #1e1e2e; padding: 2px 6px; border-radius: 4px; font-size: 0.85em; }
|
|
106
|
+
pre { background: #1e1e2e; padding: 16px; border-radius: 8px; overflow-x: auto; font-size: 0.85em; line-height: 1.5; }
|
|
107
|
+
header { background: #111; border-bottom: 1px solid #222; padding: 16px 24px; display: flex; align-items: center; gap: 16px; }
|
|
108
|
+
header h1 { font-size: 1.2rem; font-weight: 600; }
|
|
109
|
+
header h1 span { color: #f87171; }
|
|
110
|
+
.stats { display: flex; gap: 24px; margin-left: auto; font-size: 0.85rem; color: #888; }
|
|
111
|
+
.stats .stat-val { color: #e0e0e0; font-weight: 600; }
|
|
112
|
+
.filters { display: flex; gap: 8px; padding: 16px 24px; border-bottom: 1px solid #1a1a1a; }
|
|
113
|
+
.filter-btn { background: #1a1a1a; border: 1px solid #333; color: #aaa; padding: 6px 16px; border-radius: 6px; cursor: pointer; font-size: 0.85rem; transition: all 0.15s; }
|
|
114
|
+
.filter-btn:hover { border-color: #555; color: #e0e0e0; }
|
|
115
|
+
.filter-btn.active { background: #1e3a5f; border-color: #60a5fa; color: #60a5fa; }
|
|
116
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
|
|
117
|
+
table { width: 100%; border-collapse: collapse; margin-top: 8px; }
|
|
118
|
+
th { text-align: left; padding: 12px 16px; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: #666; border-bottom: 1px solid #222; }
|
|
119
|
+
td { padding: 12px 16px; border-bottom: 1px solid #1a1a1a; font-size: 0.9rem; vertical-align: top; }
|
|
120
|
+
tr { cursor: pointer; transition: background 0.1s; }
|
|
121
|
+
tr:hover { background: #141414; }
|
|
122
|
+
tr.selected { background: #1a1a2e; }
|
|
123
|
+
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; }
|
|
124
|
+
.badge-open { background: #3b1111; color: #f87171; }
|
|
125
|
+
.badge-resolved { background: #0f2f1a; color: #4ade80; }
|
|
126
|
+
.badge-ignored { background: #2d2305; color: #facc15; }
|
|
127
|
+
.badge-release { background: #1e1e3e; color: #a78bfa; font-size: 0.7rem; margin-left: 6px; }
|
|
128
|
+
.badge-env { background: #1a2e1a; color: #86efac; font-size: 0.7rem; margin-left: 4px; }
|
|
129
|
+
.env-filter { background: #1a1a1a; border: 1px solid #333; color: #aaa; padding: 6px 12px; border-radius: 6px; font-size: 0.85rem; margin-left: 8px; }
|
|
130
|
+
.feedback-box { background: #1a1a2e; border: 1px solid #2e2e5c; border-radius: 8px; padding: 12px 16px; margin-top: 8px; font-style: italic; color: #c4b5fd; }
|
|
131
|
+
.count { background: #1e1e2e; padding: 2px 8px; border-radius: 10px; font-size: 0.8rem; font-weight: 600; }
|
|
132
|
+
.error-type { color: #f87171; font-weight: 500; font-size: 0.8rem; }
|
|
133
|
+
.error-title { color: #e0e0e0; }
|
|
134
|
+
.time-ago { color: #666; font-size: 0.8rem; }
|
|
135
|
+
.empty { text-align: center; padding: 80px 24px; color: #555; }
|
|
136
|
+
.empty h2 { font-size: 1.2rem; margin-bottom: 8px; color: #666; }
|
|
137
|
+
|
|
138
|
+
/* Detail panel */
|
|
139
|
+
.detail-overlay { display: none; position: fixed; top: 0; right: 0; bottom: 0; width: 55%; background: #111; border-left: 1px solid #222; overflow-y: auto; z-index: 100; box-shadow: -4px 0 24px rgba(0,0,0,0.5); }
|
|
140
|
+
.detail-overlay.open { display: block; }
|
|
141
|
+
.detail-header { padding: 20px 24px; border-bottom: 1px solid #222; display: flex; align-items: center; gap: 12px; }
|
|
142
|
+
.detail-header h2 { font-size: 1rem; flex: 1; }
|
|
143
|
+
.close-btn { background: none; border: 1px solid #333; color: #aaa; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 0.85rem; }
|
|
144
|
+
.close-btn:hover { border-color: #666; color: #fff; }
|
|
145
|
+
.detail-body { padding: 24px; }
|
|
146
|
+
.detail-section { margin-bottom: 24px; }
|
|
147
|
+
.detail-section h3 { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; color: #666; margin-bottom: 8px; }
|
|
148
|
+
.detail-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
|
|
149
|
+
.meta-item { background: #1a1a1a; padding: 12px; border-radius: 6px; }
|
|
150
|
+
.meta-label { font-size: 0.7rem; text-transform: uppercase; color: #666; margin-bottom: 4px; }
|
|
151
|
+
.meta-value { font-size: 0.9rem; }
|
|
152
|
+
.breadcrumbs { list-style: none; }
|
|
153
|
+
.breadcrumbs li { padding: 6px 0; border-bottom: 1px solid #1a1a1a; font-size: 0.85rem; display: flex; gap: 8px; }
|
|
154
|
+
.bc-time { color: #666; font-family: monospace; font-size: 0.8rem; min-width: 60px; }
|
|
155
|
+
.bc-type { color: #60a5fa; font-size: 0.75rem; font-weight: 600; min-width: 70px; }
|
|
156
|
+
.action-bar { display: flex; gap: 8px; padding: 16px 24px; border-top: 1px solid #222; position: sticky; bottom: 0; background: #111; }
|
|
157
|
+
.action-btn { padding: 8px 16px; border-radius: 6px; border: 1px solid #333; cursor: pointer; font-size: 0.85rem; transition: all 0.15s; }
|
|
158
|
+
.btn-resolve { background: #0f2f1a; color: #4ade80; border-color: #1a5c2e; }
|
|
159
|
+
.btn-resolve:hover { background: #1a5c2e; }
|
|
160
|
+
.btn-ignore { background: #2d2305; color: #facc15; border-color: #5c4a0a; }
|
|
161
|
+
.btn-ignore:hover { background: #5c4a0a; }
|
|
162
|
+
.btn-copy { background: #1a1a2e; color: #818cf8; border-color: #2e2e5c; }
|
|
163
|
+
.btn-copy:hover { background: #2e2e5c; }
|
|
164
|
+
.btn-open { background: #1e3a5f; color: #60a5fa; border-color: #2563eb; }
|
|
165
|
+
.btn-open:hover { background: #2563eb; }
|
|
166
|
+
|
|
167
|
+
/* Fix prompt box */
|
|
168
|
+
.prompt-box { position: relative; background: #0d0d0d; border: 1px solid #222; border-radius: 8px; margin-bottom: 24px; }
|
|
169
|
+
.prompt-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid #222; }
|
|
170
|
+
.prompt-header h3 { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; color: #666; margin: 0; }
|
|
171
|
+
.prompt-copy-btn { background: #1a1a2e; border: 1px solid #2e2e5c; color: #818cf8; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 0.75rem; display: flex; align-items: center; gap: 4px; transition: all 0.15s; }
|
|
172
|
+
.prompt-copy-btn:hover { background: #2e2e5c; }
|
|
173
|
+
.prompt-copy-btn svg { width: 14px; height: 14px; }
|
|
174
|
+
.prompt-content { padding: 16px; max-height: 300px; overflow-y: auto; font-size: 0.85rem; line-height: 1.5; white-space: pre-wrap; font-family: 'SF Mono', 'Fira Code', monospace; color: #ccc; }
|
|
175
|
+
|
|
176
|
+
/* Fix prompt markdown (expanded view) */
|
|
177
|
+
.fix-prompt { background: #0d0d0d; border: 1px solid #222; border-radius: 8px; padding: 20px; line-height: 1.6; }
|
|
178
|
+
.fix-prompt h1, .fix-prompt h2, .fix-prompt h3 { color: #e0e0e0; margin: 16px 0 8px; }
|
|
179
|
+
.fix-prompt h1 { font-size: 1.2rem; }
|
|
180
|
+
.fix-prompt h2 { font-size: 1rem; border-bottom: 1px solid #222; padding-bottom: 6px; }
|
|
181
|
+
.fix-prompt h3 { font-size: 0.9rem; }
|
|
182
|
+
.fix-prompt ul, .fix-prompt ol { padding-left: 20px; margin: 8px 0; }
|
|
183
|
+
.fix-prompt li { margin: 4px 0; }
|
|
184
|
+
.fix-prompt strong { color: #f0f0f0; }
|
|
185
|
+
.fix-prompt p { margin: 8px 0; }
|
|
186
|
+
</style>
|
|
187
|
+
</head>
|
|
188
|
+
<body>
|
|
189
|
+
<header>
|
|
190
|
+
<h1><span>uncaught</span> dashboard</h1>
|
|
191
|
+
<div class="stats" id="stats"></div>
|
|
192
|
+
</header>
|
|
193
|
+
<div class="filters" id="filters">
|
|
194
|
+
<button class="filter-btn active" data-filter="all">All</button>
|
|
195
|
+
<button class="filter-btn" data-filter="open">Open</button>
|
|
196
|
+
<button class="filter-btn" data-filter="resolved">Resolved</button>
|
|
197
|
+
<button class="filter-btn" data-filter="ignored">Ignored</button>
|
|
198
|
+
<select class="env-filter" id="env-filter">
|
|
199
|
+
<option value="">All Environments</option>
|
|
200
|
+
</select>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="container">
|
|
203
|
+
<table>
|
|
204
|
+
<thead>
|
|
205
|
+
<tr><th>#</th><th>Status</th><th>Error</th><th>Release</th><th>Count</th><th>Users</th><th>Last Seen</th></tr>
|
|
206
|
+
</thead>
|
|
207
|
+
<tbody id="issues-body"></tbody>
|
|
208
|
+
</table>
|
|
209
|
+
<div class="empty" id="empty" style="display:none;">
|
|
210
|
+
<h2>No issues found</h2>
|
|
211
|
+
<p>Trigger some errors in your app and they will appear here.</p>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
<div class="detail-overlay" id="detail">
|
|
215
|
+
<div class="detail-header">
|
|
216
|
+
<h2 id="detail-title"></h2>
|
|
217
|
+
<button class="close-btn" id="close-detail">Close</button>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="detail-body" id="detail-body"></div>
|
|
220
|
+
<div class="action-bar" id="action-bar"></div>
|
|
221
|
+
</div>
|
|
222
|
+
<script>
|
|
223
|
+
let allIssues = [];
|
|
224
|
+
let currentFilter = 'all';
|
|
225
|
+
let currentEnv = '';
|
|
226
|
+
let selectedFp = null;
|
|
227
|
+
|
|
228
|
+
async function fetchIssues() {
|
|
229
|
+
try {
|
|
230
|
+
var params = [];
|
|
231
|
+
if (currentFilter !== 'all') params.push('status=' + currentFilter);
|
|
232
|
+
if (currentEnv) params.push('environment=' + encodeURIComponent(currentEnv));
|
|
233
|
+
var url = '/api/issues' + (params.length ? '?' + params.join('&') : '');
|
|
234
|
+
const res = await fetch(url);
|
|
235
|
+
allIssues = await res.json();
|
|
236
|
+
renderIssues();
|
|
237
|
+
fetchStats();
|
|
238
|
+
updateEnvFilter();
|
|
239
|
+
} catch(e) { console.error('Failed to fetch issues', e); }
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function updateEnvFilter() {
|
|
243
|
+
var sel = document.getElementById('env-filter');
|
|
244
|
+
var envs = new Set();
|
|
245
|
+
allIssues.forEach(function(i) { if (i.environment) envs.add(i.environment); });
|
|
246
|
+
var opts = '<option value="">All Environments</option>';
|
|
247
|
+
envs.forEach(function(e) { opts += '<option value="' + escHtml(e) + '"' + (e === currentEnv ? ' selected' : '') + '>' + escHtml(e) + '</option>'; });
|
|
248
|
+
sel.innerHTML = opts;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function fetchStats() {
|
|
252
|
+
try {
|
|
253
|
+
const res = await fetch('/api/stats');
|
|
254
|
+
const s = await res.json();
|
|
255
|
+
document.getElementById('stats').innerHTML =
|
|
256
|
+
'<div><span class="stat-val">' + s.total + '</span> issues</div>' +
|
|
257
|
+
'<div><span class="stat-val">' + s.open + '</span> open</div>' +
|
|
258
|
+
'<div><span class="stat-val">' + s.totalEvents + '</span> events</div>';
|
|
259
|
+
} catch(e) {}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function timeAgo(iso) {
|
|
263
|
+
const ms = Date.now() - new Date(iso).getTime();
|
|
264
|
+
if (ms < 0) return 'just now';
|
|
265
|
+
const s = Math.floor(ms/1000);
|
|
266
|
+
if (s < 60) return s + 's ago';
|
|
267
|
+
const m = Math.floor(s/60);
|
|
268
|
+
if (m < 60) return m + 'm ago';
|
|
269
|
+
const h = Math.floor(m/60);
|
|
270
|
+
if (h < 24) return h + 'h ago';
|
|
271
|
+
const d = Math.floor(h/24);
|
|
272
|
+
if (d < 30) return d + 'd ago';
|
|
273
|
+
return new Date(iso).toLocaleDateString();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function escHtml(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
|
277
|
+
|
|
278
|
+
function renderIssues() {
|
|
279
|
+
const body = document.getElementById('issues-body');
|
|
280
|
+
const empty = document.getElementById('empty');
|
|
281
|
+
if (allIssues.length === 0) { body.innerHTML = ''; empty.style.display = 'block'; return; }
|
|
282
|
+
empty.style.display = 'none';
|
|
283
|
+
body.innerHTML = allIssues.map(function(issue, i) {
|
|
284
|
+
const cls = issue.status === 'open' ? 'badge-open' : issue.status === 'resolved' ? 'badge-resolved' : 'badge-ignored';
|
|
285
|
+
var relBadge = issue.release ? '<span class="badge badge-release">' + escHtml(issue.release) + '</span>' : '<span style="color:#555">\u2014</span>';
|
|
286
|
+
var envBadge = issue.environment ? '<span class="badge badge-env">' + escHtml(issue.environment) + '</span>' : '';
|
|
287
|
+
return '<tr data-fp="' + issue.fingerprint + '" class="' + (issue.fingerprint === selectedFp ? 'selected' : '') + '">' +
|
|
288
|
+
'<td>' + (i+1) + '</td>' +
|
|
289
|
+
'<td><span class="badge ' + cls + '">' + issue.status + '</span>' + envBadge + '</td>' +
|
|
290
|
+
'<td><span class="error-type">' + escHtml(issue.errorType) + '</span> <span class="error-title">' + escHtml(issue.title.length > 60 ? issue.title.slice(0,57) + '...' : issue.title) + '</span></td>' +
|
|
291
|
+
'<td>' + relBadge + '</td>' +
|
|
292
|
+
'<td><span class="count">' + issue.count + '</span></td>' +
|
|
293
|
+
'<td>' + issue.affectedUsers.length + '</td>' +
|
|
294
|
+
'<td class="time-ago">' + timeAgo(issue.lastSeen) + '</td>' +
|
|
295
|
+
'</tr>';
|
|
296
|
+
}).join('');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function renderMd(md) {
|
|
300
|
+
return md
|
|
301
|
+
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
|
302
|
+
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
|
303
|
+
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
|
304
|
+
.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')
|
|
305
|
+
.replace(/\`\`\`[\\s\\S]*?\`\`\`/g, function(m) {
|
|
306
|
+
var code = m.replace(/\`\`\`\\w*\\n?/g, '').replace(/\`\`\`$/g, '');
|
|
307
|
+
return '<pre><code>' + escHtml(code) + '</code></pre>';
|
|
308
|
+
})
|
|
309
|
+
.replace(/\`([^\`]+)\`/g, '<code>$1</code>')
|
|
310
|
+
.replace(/^- \\*\\*(.+?)\\*\\* ?\u2014 ?(.+)$/gm, '<li><strong>$1</strong> \u2014 $2</li>')
|
|
311
|
+
.replace(/^- (.+)$/gm, '<li>$1</li>')
|
|
312
|
+
.replace(/^(\\d+)\\. (.+)$/gm, '<li>$2</li>')
|
|
313
|
+
.replace(/(<li>.*<\\/li>)/s, '<ul>$1</ul>')
|
|
314
|
+
.replace(/\\n\\n/g, '</p><p>')
|
|
315
|
+
.replace(/\\n/g, '<br>');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function showDetail(fp) {
|
|
319
|
+
selectedFp = fp;
|
|
320
|
+
renderIssues();
|
|
321
|
+
try {
|
|
322
|
+
const res = await fetch('/api/issues/' + fp);
|
|
323
|
+
const data = await res.json();
|
|
324
|
+
const issue = data.issue;
|
|
325
|
+
const event = data.event;
|
|
326
|
+
|
|
327
|
+
document.getElementById('detail-title').textContent = issue.errorType + ': ' + issue.title;
|
|
328
|
+
|
|
329
|
+
let html = '';
|
|
330
|
+
|
|
331
|
+
if (event) {
|
|
332
|
+
// Fix prompt \u2014 first, compact box with copy icon
|
|
333
|
+
if (event.fixPrompt) {
|
|
334
|
+
html += '<div class="prompt-box">';
|
|
335
|
+
html += '<div class="prompt-header"><h3>Fix Prompt</h3>';
|
|
336
|
+
html += '<button class="prompt-copy-btn" onclick="copyPrompt()">';
|
|
337
|
+
html += '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
|
|
338
|
+
html += '<span id="copy-label">Copy</span></button></div>';
|
|
339
|
+
html += '<div class="prompt-content">' + escHtml(event.fixPrompt) + '</div>';
|
|
340
|
+
html += '</div>';
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Meta
|
|
345
|
+
html += '<div class="detail-section"><h3>Details</h3><div class="detail-meta">';
|
|
346
|
+
html += '<div class="meta-item"><div class="meta-label">Fingerprint</div><div class="meta-value"><code>' + issue.fingerprint + '</code></div></div>';
|
|
347
|
+
html += '<div class="meta-item"><div class="meta-label">Occurrences</div><div class="meta-value">' + issue.count + '</div></div>';
|
|
348
|
+
html += '<div class="meta-item"><div class="meta-label">Users Affected</div><div class="meta-value">' + issue.affectedUsers.length + '</div></div>';
|
|
349
|
+
html += '<div class="meta-item"><div class="meta-label">First Seen</div><div class="meta-value">' + timeAgo(issue.firstSeen) + '</div></div>';
|
|
350
|
+
html += '<div class="meta-item"><div class="meta-label">Last Seen</div><div class="meta-value">' + timeAgo(issue.lastSeen) + '</div></div>';
|
|
351
|
+
html += '<div class="meta-item"><div class="meta-label">Status</div><div class="meta-value"><span class="badge badge-' + issue.status + '">' + issue.status + '</span></div></div>';
|
|
352
|
+
if (issue.release) html += '<div class="meta-item"><div class="meta-label">Release</div><div class="meta-value"><span class="badge badge-release">' + escHtml(issue.release) + '</span></div></div>';
|
|
353
|
+
if (issue.environment) html += '<div class="meta-item"><div class="meta-label">Environment</div><div class="meta-value"><span class="badge badge-env">' + escHtml(issue.environment) + '</span></div></div>';
|
|
354
|
+
html += '</div></div>';
|
|
355
|
+
|
|
356
|
+
if (event) {
|
|
357
|
+
// User feedback
|
|
358
|
+
if (event.userFeedback) {
|
|
359
|
+
html += '<div class="detail-section"><h3>User Feedback</h3><div class="feedback-box">"' + escHtml(event.userFeedback) + '"</div></div>';
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Stack trace (prefer resolved)
|
|
363
|
+
var stackLabel = 'Stack Trace';
|
|
364
|
+
var stackContent = '';
|
|
365
|
+
if (event.error && event.error.resolvedStack) {
|
|
366
|
+
stackLabel = 'Stack Trace (Source Mapped)';
|
|
367
|
+
stackContent = event.error.resolvedStack;
|
|
368
|
+
} else if (event.error && event.error.stack) {
|
|
369
|
+
stackContent = event.error.stack;
|
|
370
|
+
}
|
|
371
|
+
if (stackContent) {
|
|
372
|
+
html += '<div class="detail-section"><h3>' + stackLabel + '</h3><pre><code>' + escHtml(stackContent) + '</code></pre></div>';
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Environment
|
|
376
|
+
if (event.environment) {
|
|
377
|
+
const env = event.environment;
|
|
378
|
+
html += '<div class="detail-section"><h3>Environment</h3><div class="detail-meta">';
|
|
379
|
+
if (env.deploy) html += '<div class="meta-item"><div class="meta-label">Deploy Env</div><div class="meta-value">' + escHtml(env.deploy) + '</div></div>';
|
|
380
|
+
if (env.browser) html += '<div class="meta-item"><div class="meta-label">Browser</div><div class="meta-value">' + escHtml(env.browser + (env.browserVersion ? ' ' + env.browserVersion : '')) + '</div></div>';
|
|
381
|
+
if (env.os) html += '<div class="meta-item"><div class="meta-label">OS</div><div class="meta-value">' + escHtml(env.os) + '</div></div>';
|
|
382
|
+
if (env.runtime) html += '<div class="meta-item"><div class="meta-label">Runtime</div><div class="meta-value">' + escHtml(env.runtime) + '</div></div>';
|
|
383
|
+
if (env.url) html += '<div class="meta-item"><div class="meta-label">URL</div><div class="meta-value">' + escHtml(env.url) + '</div></div>';
|
|
384
|
+
if (env.deviceType) html += '<div class="meta-item"><div class="meta-label">Device</div><div class="meta-value">' + escHtml(env.deviceType) + '</div></div>';
|
|
385
|
+
html += '</div></div>';
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Breadcrumbs
|
|
389
|
+
if (event.breadcrumbs && event.breadcrumbs.length > 0) {
|
|
390
|
+
html += '<div class="detail-section"><h3>Breadcrumbs</h3><ul class="breadcrumbs">';
|
|
391
|
+
event.breadcrumbs.forEach(function(bc) {
|
|
392
|
+
var t = bc.timestamp ? new Date(bc.timestamp).toLocaleTimeString() : '';
|
|
393
|
+
html += '<li><span class="bc-time">' + t + '</span><span class="bc-type">[' + escHtml(bc.type) + ']</span><span>' + escHtml(bc.message) + '</span></li>';
|
|
394
|
+
});
|
|
395
|
+
html += '</ul></div>';
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
document.getElementById('detail-body').innerHTML = html;
|
|
400
|
+
|
|
401
|
+
// Action bar
|
|
402
|
+
let actions = '';
|
|
403
|
+
if (issue.status !== 'resolved') actions += '<button class="action-btn btn-resolve" onclick="updateStatus(\\'' + fp + '\\', \\'resolved\\')">Mark Resolved</button>';
|
|
404
|
+
if (issue.status !== 'ignored') actions += '<button class="action-btn btn-ignore" onclick="updateStatus(\\'' + fp + '\\', \\'ignored\\')">Mark Ignored</button>';
|
|
405
|
+
if (issue.status !== 'open') actions += '<button class="action-btn btn-open" onclick="updateStatus(\\'' + fp + '\\', \\'open\\')">Reopen</button>';
|
|
406
|
+
document.getElementById('action-bar').innerHTML = actions;
|
|
407
|
+
|
|
408
|
+
document.getElementById('detail').classList.add('open');
|
|
409
|
+
window._currentFixPrompt = event ? event.fixPrompt : '';
|
|
410
|
+
} catch(e) { console.error('Failed to load detail', e); }
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async function updateStatus(fp, status) {
|
|
414
|
+
try {
|
|
415
|
+
await fetch('/api/issues/' + fp, { method: 'PATCH', headers: {'Content-Type':'application/json'}, body: JSON.stringify({status:status}) });
|
|
416
|
+
fetchIssues();
|
|
417
|
+
showDetail(fp);
|
|
418
|
+
} catch(e) { console.error('Failed to update', e); }
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function copyPrompt() {
|
|
422
|
+
if (window._currentFixPrompt) {
|
|
423
|
+
navigator.clipboard.writeText(window._currentFixPrompt).then(function() {
|
|
424
|
+
var label = document.getElementById('copy-label');
|
|
425
|
+
if (label) { label.textContent = 'Copied!'; setTimeout(function(){ label.textContent = 'Copy'; }, 2000); }
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Event delegation
|
|
431
|
+
document.getElementById('issues-body').addEventListener('click', function(e) {
|
|
432
|
+
var tr = e.target.closest('tr');
|
|
433
|
+
if (tr && tr.dataset.fp) showDetail(tr.dataset.fp);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
document.getElementById('close-detail').addEventListener('click', function() {
|
|
437
|
+
document.getElementById('detail').classList.remove('open');
|
|
438
|
+
selectedFp = null;
|
|
439
|
+
renderIssues();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
document.getElementById('filters').addEventListener('click', function(e) {
|
|
443
|
+
var btn = e.target.closest('.filter-btn');
|
|
444
|
+
if (!btn) return;
|
|
445
|
+
document.querySelectorAll('.filter-btn').forEach(function(b){ b.classList.remove('active'); });
|
|
446
|
+
btn.classList.add('active');
|
|
447
|
+
currentFilter = btn.dataset.filter;
|
|
448
|
+
fetchIssues();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
document.getElementById('env-filter').addEventListener('change', function(e) {
|
|
452
|
+
currentEnv = e.target.value;
|
|
453
|
+
fetchIssues();
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Close on Escape
|
|
457
|
+
document.addEventListener('keydown', function(e) {
|
|
458
|
+
if (e.key === 'Escape') {
|
|
459
|
+
document.getElementById('detail').classList.remove('open');
|
|
460
|
+
selectedFp = null;
|
|
461
|
+
renderIssues();
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Initial load + auto-refresh
|
|
466
|
+
fetchIssues();
|
|
467
|
+
setInterval(fetchIssues, 5000);
|
|
468
|
+
</script>
|
|
469
|
+
</body>
|
|
470
|
+
</html>`;async function ae(e){let n=P();await l__namespace.mkdir(n,{recursive:true});let o;try{o=E();}catch(a){console.error(t(k,`
|
|
471
|
+
Failed to open database:`),a),process.exitCode=1;return}let s=L__namespace.createServer(async(a,r)=>{let u=new URL(a.url,`http://localhost:${e}`),d=u.pathname;if(r.setHeader("Access-Control-Allow-Origin","*"),r.setHeader("Access-Control-Allow-Methods","GET, PATCH, DELETE, OPTIONS"),r.setHeader("Access-Control-Allow-Headers","Content-Type"),a.method==="OPTIONS"){r.writeHead(204),r.end();return}if(d==="/"||d==="/index.html"){r.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),r.end(se);return}try{if(d==="/api/issues"&&a.method==="GET"){let c=u.searchParams.get("status"),m=u.searchParams.get("environment")||void 0,p={};c&&(p.status=c),m&&(p.environment=m);let j=Object.keys(p).length>0?o.getIssues(p):o.getIssues();r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify(j));return}if(d==="/api/stats"&&a.method==="GET"){let c=o.getStats();r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify(c));return}let g=d.match(/^\/api\/issues\/([a-f0-9]+)$/),f=d.match(/^\/api\/issues\/([a-f0-9]+)\/events$/);if(f&&a.method==="GET"){let c=f[1],m=parseInt(u.searchParams.get("limit")??"20",10),p=parseInt(u.searchParams.get("offset")??"0",10),j=o.getEvents(c,{limit:m,offset:p});r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify(j));return}if(g&&a.method==="GET"){let c=g[1],m=o.getIssue(c);if(!m){r.writeHead(404,{"Content-Type":"application/json"}),r.end(JSON.stringify({error:"Issue not found"}));return}let p=o.getLatestEvent(c);r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({issue:m,event:p??null}));return}if(g&&a.method==="PATCH"){let c=g[1],m=await re(a),{status:p}=JSON.parse(m);o.updateIssueStatus(c,p),r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({ok:!0}));return}if(d==="/api/issues"&&a.method==="DELETE"){o.deleteAllIssues(),r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({ok:!0}));return}}catch(g){r.writeHead(500,{"Content-Type":"application/json"}),r.end(JSON.stringify({error:String(g)}));return}r.writeHead(404),r.end("Not found");});s.on("error",a=>{a.code==="EADDRINUSE"?(console.error(t(k,`
|
|
472
|
+
Port ${e} is already in use. Try: npx @uncaughtdev/core dashboard --port ${e+1}
|
|
473
|
+
`)),process.exitCode=1):(console.error(t(k,`
|
|
474
|
+
Server error:`),a),process.exitCode=1);}),s.listen(e,()=>{console.log(""),console.log(t($," uncaught dashboard")),console.log(t(h," \u2500".repeat(35))),console.log(""),console.log(` ${t(x,"\u25CF")} Running at ${t(b,`http://localhost:${e}`)}`),console.log(""),console.log(t(h," Press Ctrl+C to stop")),console.log("");let a=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";try{child_process.spawn(a,[`http://localhost:${e}`],{detached:!0,stdio:"ignore"}).unref();}catch{}}),process.on("SIGINT",()=>{console.log(t(h,`
|
|
475
|
+
Shutting down...`)),o.close(),s.close(),process.exit(0);});}function re(e){return new Promise((n,o)=>{let s="";e.on("data",a=>{s+=a;}),e.on("end",()=>n(s)),e.on("error",o);})}async function ie(){let e=process.argv.slice(2),n=e[0]??"list";switch(n){case "init":case "setup":await ne();break;case "list":case "ls":await _();break;case "show":case "view":{let o=e[1];if(!o){console.error(t(k,`
|
|
90
476
|
Missing issue number. Usage: uncaught show <n>
|
|
91
|
-
`)),process.exitCode=1;return}let s=e.includes("--open")||e.includes("-o");await
|
|
477
|
+
`)),process.exitCode=1;return}let s=e.includes("--open")||e.includes("-o");await V(o,s);break}case "resolve":{let o=e[1];if(!o){console.error(t(k,`
|
|
92
478
|
Missing issue number. Usage: uncaught resolve <n>
|
|
93
|
-
`)),process.exitCode=1;return}await
|
|
94
|
-
Unknown command: ${
|
|
479
|
+
`)),process.exitCode=1;return}await Y(o);break}case "clear":case "clean":await W();break;case "dashboard":case "dash":case "ui":{let o=e.indexOf("--port"),s=o>=0&&parseInt(e[o+1],10)||3300;await ae(s);break}case "help":case "--help":case "-h":U();break;default:console.error(t(k,`
|
|
480
|
+
Unknown command: ${n}`)),U(),process.exitCode=1;break}}ie().catch(e=>{console.error(t(k,`
|
|
95
481
|
Unexpected error:`),e),process.exitCode=1;});//# sourceMappingURL=local-viewer.js.map
|
|
96
482
|
//# sourceMappingURL=local-viewer.js.map
|