@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/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Core engine for [Uncaught](https://github.com/AjeeshDevops/uncaught) error monit
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx
|
|
8
|
+
npx uncaughtdev init
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Or manually:
|
|
@@ -21,8 +21,49 @@ npm install @uncaughtdev/core
|
|
|
21
21
|
- PII sanitization
|
|
22
22
|
- AI-ready fix prompt generation
|
|
23
23
|
- Transport layer (console, local file, remote)
|
|
24
|
-
-
|
|
25
|
-
-
|
|
24
|
+
- SQLite storage backend with web dashboard
|
|
25
|
+
- Source map resolution for production stack traces
|
|
26
|
+
- Node.js process-level error handlers (`setupNodeHandlers`)
|
|
27
|
+
- Express error middleware (`expressErrorHandler`)
|
|
28
|
+
- Fastify error handler plugin (`fastifyErrorPlugin`)
|
|
29
|
+
- Webhook notifications for new error fingerprints
|
|
30
|
+
- Release tracking and environment filtering
|
|
31
|
+
- User feedback API (`submitFeedback`)
|
|
32
|
+
- MCP server for AI coding assistants (Cursor, Claude Code, Windsurf)
|
|
33
|
+
- CLI viewer (`npx uncaughtdev`)
|
|
34
|
+
- Auto-setup command (`npx uncaughtdev init`)
|
|
35
|
+
|
|
36
|
+
## MCP Server
|
|
37
|
+
|
|
38
|
+
Exposes error data to AI coding assistants via Model Context Protocol:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Add to Cursor (~/.cursor/mcp.json):
|
|
42
|
+
{ "mcpServers": { "uncaught": { "command": "npx", "args": ["-y", "@uncaughtdev/core", "uncaught-mcp"] } } }
|
|
43
|
+
|
|
44
|
+
# Add to Claude Code:
|
|
45
|
+
claude mcp add uncaught -- npx -y @uncaughtdev/core uncaught-mcp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Tools: `setup_uncaught`, `list_errors`, `get_error`, `get_fix_prompt`, `get_stats`, `resolve_error`, `search_errors`
|
|
49
|
+
|
|
50
|
+
## Server-Side Usage
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { initUncaught, setupNodeHandlers, expressErrorHandler } from '@uncaughtdev/core';
|
|
54
|
+
|
|
55
|
+
const client = initUncaught({
|
|
56
|
+
projectKey: 'my-api',
|
|
57
|
+
environment: 'production',
|
|
58
|
+
release: '1.2.0',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Capture uncaughtException and unhandledRejection
|
|
62
|
+
setupNodeHandlers(client);
|
|
63
|
+
|
|
64
|
+
// Express error middleware (register after all routes)
|
|
65
|
+
app.use(expressErrorHandler(client));
|
|
66
|
+
```
|
|
26
67
|
|
|
27
68
|
## License
|
|
28
69
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var d=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});exports.a=d;//# sourceMappingURL=chunk-2YXXFGBV.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-2YXXFGBV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-2YXXFGBV.js"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function f(r){let n=[];if(n.push(`I have a production bug in my application that I need help diagnosing and fixing.
|
|
2
|
+
`),r.error){let o=i(r.error.stack),t=["## Error",""];t.push(`- **Type:** ${r.error.type||"Error"}`),t.push(`- **Message:** ${r.error.message||"(no message)"}`),o&&t.push(`- **Location:** ${o}`),n.push(t.join(`
|
|
3
|
+
`));}let s=r.error?.resolvedStack??r.error?.stack;if(s){let o=s.split(`
|
|
4
|
+
`).slice(0,15).map(e=>e.trimEnd()).join(`
|
|
5
|
+
`),t=r.error?.resolvedStack?"Stack Trace (source-mapped)":"Stack Trace";n.push(`## ${t}
|
|
6
|
+
|
|
7
|
+
\`\`\`
|
|
8
|
+
${o}
|
|
9
|
+
\`\`\``);}return r.operation&&n.push(a(r.operation)),r.request&&n.push(u(r.request)),r.breadcrumbs&&r.breadcrumbs.length>0&&n.push(c(r.breadcrumbs)),r.environment&&n.push(m(r.environment)),r.error?.componentStack&&n.push(`## React Component Stack
|
|
10
|
+
|
|
11
|
+
\`\`\`
|
|
12
|
+
${r.error.componentStack.trim()}
|
|
13
|
+
\`\`\``),n.push(["## What I need","","1. **Root cause analysis** \u2014 explain why this error is occurring.","2. **A fix** \u2014 provide the corrected code with an explanation of the changes.","3. **Prevention** \u2014 suggest any guards or tests to prevent this from happening again."].join(`
|
|
14
|
+
`)),n.join(`
|
|
15
|
+
|
|
16
|
+
`)+`
|
|
17
|
+
`}function i(r){if(r)for(let n of r.split(`
|
|
18
|
+
`)){let s=n.trim(),o=s.match(/at\s+(?:.+?\s+\()?(.+?:\d+:\d+)\)?/);if(o)return o[1];let t=s.match(/@(.+?:\d+:\d+)/);if(t)return t[1]}}function a(r){let n=["## Failed Operation",""];return n.push(`- **Provider:** ${r.provider}`),n.push(`- **Type:** ${r.type}`),n.push(`- **Method:** ${r.method}`),r.params&&(n.push("- **Params:**"),n.push("```json"),n.push(JSON.stringify(r.params,null,2)),n.push("```")),r.errorCode&&n.push(`- **Error Code:** ${r.errorCode}`),r.errorDetails&&n.push(`- **Error Details:** ${r.errorDetails}`),n.join(`
|
|
19
|
+
`)}function u(r){let n=["## HTTP Request Context",""];return r.method&&n.push(`- **Method:** ${r.method}`),r.url&&n.push(`- **URL:** ${r.url}`),r.body&&(n.push("- **Body:**"),n.push("```json"),n.push(typeof r.body=="string"?r.body:JSON.stringify(r.body,null,2)),n.push("```")),n.join(`
|
|
20
|
+
`)}function c(r){let n=r.slice(-5),s=["## User Session",""];for(let o of n){let t=p(o.timestamp);s.push(`- \`${t}\` **[${o.type}]** ${o.message}`);}return s.join(`
|
|
21
|
+
`)}function p(r){try{let n=new Date(r),s=String(n.getHours()).padStart(2,"0"),o=String(n.getMinutes()).padStart(2,"0"),t=String(n.getSeconds()).padStart(2,"0");return `${s}:${o}:${t}`}catch{return r}}function m(r){let n=["## Environment",""],s=[["Deploy Environment",r.deploy],["Framework",r.framework],["Framework Version",r.frameworkVersion],["Runtime",r.runtime],["Runtime Version",r.runtimeVersion],["Platform",r.platform],["Browser",r.browser?`${r.browser} ${r.browserVersion??""}`.trim():void 0],["OS",r.os],["Device",r.deviceType],["Locale",r.locale],["Timezone",r.timezone],["URL",r.url]];for(let[o,t]of s)t&&n.push(`- **${o}:** ${t}`);return n.join(`
|
|
22
|
+
`)}export{f as a};//# sourceMappingURL=chunk-3FCDO7OR.mjs.map
|
|
23
|
+
//# sourceMappingURL=chunk-3FCDO7OR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/prompt-builder.ts"],"names":["buildFixPrompt","event","sections","location","extractLocation","lines","stackSource","frames","l","label","formatOperation","formatRequest","formatBreadcrumbs","formatEnvironment","stack","line","trimmed","v8","sm","op","req","crumbs","recent","crumb","time","formatTime","iso","d","h","m","s","env","entries","value"],"mappings":"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","file":"chunk-3FCDO7OR.mjs","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — fix-prompt builder\n// ---------------------------------------------------------------------------\n\nimport type { UncaughtEvent, Breadcrumb, EnvironmentInfo, OperationInfo, RequestInfo } from './types';\n\n/**\n * Build a structured Markdown prompt that can be pasted into an AI assistant\n * to diagnose and fix the production error described by `event`.\n *\n * Empty sections are omitted to keep the prompt concise.\n */\nexport function buildFixPrompt(event: Partial<UncaughtEvent>): string {\n const sections: string[] = [];\n\n // ----- Intro -------------------------------------------------------------\n sections.push(\n 'I have a production bug in my application that I need help diagnosing and fixing.\\n'\n );\n\n // ----- Error -------------------------------------------------------------\n if (event.error) {\n const location = extractLocation(event.error.stack);\n const lines: string[] = ['## Error', ''];\n lines.push(`- **Type:** ${event.error.type || 'Error'}`);\n lines.push(`- **Message:** ${event.error.message || '(no message)'}`);\n if (location) {\n lines.push(`- **Location:** ${location}`);\n }\n sections.push(lines.join('\\n'));\n }\n\n // ----- Stack Trace -------------------------------------------------------\n const stackSource = event.error?.resolvedStack ?? event.error?.stack;\n if (stackSource) {\n const frames = stackSource\n .split('\\n')\n .slice(0, 15)\n .map((l) => l.trimEnd())\n .join('\\n');\n const label = event.error?.resolvedStack ? 'Stack Trace (source-mapped)' : 'Stack Trace';\n sections.push(`## ${label}\\n\\n\\`\\`\\`\\n${frames}\\n\\`\\`\\``);\n }\n\n // ----- Failed Operation --------------------------------------------------\n if (event.operation) {\n sections.push(formatOperation(event.operation));\n }\n\n // ----- HTTP Request Context ----------------------------------------------\n if (event.request) {\n sections.push(formatRequest(event.request));\n }\n\n // ----- User Session (last 5 breadcrumbs) ---------------------------------\n if (event.breadcrumbs && event.breadcrumbs.length > 0) {\n sections.push(formatBreadcrumbs(event.breadcrumbs));\n }\n\n // ----- Environment -------------------------------------------------------\n if (event.environment) {\n sections.push(formatEnvironment(event.environment));\n }\n\n // ----- React Component Stack ---------------------------------------------\n if (event.error?.componentStack) {\n sections.push(\n `## React Component Stack\\n\\n\\`\\`\\`\\n${event.error.componentStack.trim()}\\n\\`\\`\\``\n );\n }\n\n // ----- What I need -------------------------------------------------------\n sections.push(\n [\n '## What I need',\n '',\n '1. **Root cause analysis** — explain why this error is occurring.',\n '2. **A fix** — provide the corrected code with an explanation of the changes.',\n '3. **Prevention** — suggest any guards or tests to prevent this from happening again.',\n ].join('\\n')\n );\n\n return sections.join('\\n\\n') + '\\n';\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the top-most location (file:line:col) from a stack trace string.\n */\nfunction extractLocation(stack?: string): string | undefined {\n if (!stack) return undefined;\n\n for (const line of stack.split('\\n')) {\n const trimmed = line.trim();\n\n // V8: \" at fn (file:line:col)\"\n const v8 = trimmed.match(/at\\s+(?:.+?\\s+\\()?(.+?:\\d+:\\d+)\\)?/);\n if (v8) return v8[1];\n\n // SpiderMonkey / JSC: \"fn@file:line:col\"\n const sm = trimmed.match(/@(.+?:\\d+:\\d+)/);\n if (sm) return sm[1];\n }\n\n return undefined;\n}\n\nfunction formatOperation(op: OperationInfo): string {\n const lines: string[] = ['## Failed Operation', ''];\n lines.push(`- **Provider:** ${op.provider}`);\n lines.push(`- **Type:** ${op.type}`);\n lines.push(`- **Method:** ${op.method}`);\n if (op.params) {\n lines.push(`- **Params:**`);\n lines.push('```json');\n lines.push(JSON.stringify(op.params, null, 2));\n lines.push('```');\n }\n if (op.errorCode) {\n lines.push(`- **Error Code:** ${op.errorCode}`);\n }\n if (op.errorDetails) {\n lines.push(`- **Error Details:** ${op.errorDetails}`);\n }\n return lines.join('\\n');\n}\n\nfunction formatRequest(req: RequestInfo): string {\n const lines: string[] = ['## HTTP Request Context', ''];\n if (req.method) lines.push(`- **Method:** ${req.method}`);\n if (req.url) lines.push(`- **URL:** ${req.url}`);\n if (req.body) {\n lines.push(`- **Body:**`);\n lines.push('```json');\n lines.push(\n typeof req.body === 'string'\n ? req.body\n : JSON.stringify(req.body, null, 2)\n );\n lines.push('```');\n }\n return lines.join('\\n');\n}\n\nfunction formatBreadcrumbs(crumbs: Breadcrumb[]): string {\n // Take the last 5 breadcrumbs\n const recent = crumbs.slice(-5);\n const lines: string[] = ['## User Session', ''];\n\n for (const crumb of recent) {\n const time = formatTime(crumb.timestamp);\n lines.push(`- \\`${time}\\` **[${crumb.type}]** ${crumb.message}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Extract HH:MM:SS from an ISO timestamp.\n */\nfunction formatTime(iso: string): string {\n try {\n const d = new Date(iso);\n const h = String(d.getHours()).padStart(2, '0');\n const m = String(d.getMinutes()).padStart(2, '0');\n const s = String(d.getSeconds()).padStart(2, '0');\n return `${h}:${m}:${s}`;\n } catch {\n return iso;\n }\n}\n\nfunction formatEnvironment(env: EnvironmentInfo): string {\n const lines: string[] = ['## Environment', ''];\n const entries: Array<[string, string | undefined]> = [\n ['Deploy Environment', env.deploy],\n ['Framework', env.framework],\n ['Framework Version', env.frameworkVersion],\n ['Runtime', env.runtime],\n ['Runtime Version', env.runtimeVersion],\n ['Platform', env.platform],\n ['Browser', env.browser ? `${env.browser} ${env.browserVersion ?? ''}`.trim() : undefined],\n ['OS', env.os],\n ['Device', env.deviceType],\n ['Locale', env.locale],\n ['Timezone', env.timezone],\n ['URL', env.url],\n ];\n\n for (const [label, value] of entries) {\n if (value) {\n lines.push(`- **${label}:** ${value}`);\n }\n }\n\n return lines.join('\\n');\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var d=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});export{d as a};//# sourceMappingURL=chunk-A6GKDPT3.mjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-A6GKDPT3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-A6GKDPT3.mjs"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {b}from'./chunk-P6JRN5CN.mjs';async function N(a){try{if(typeof process<"u"&&process.env.NODE_ENV==="production"&&process.env.UNCAUGHT_LOCAL_IN_PROD!=="true")return new Response(JSON.stringify({error:"Local handler is disabled in production"}),{status:403,headers:{"Content-Type":"application/json"}});let t;try{t=await a.json();}catch{return new Response(JSON.stringify({error:"Invalid JSON body"}),{status:400,headers:{"Content-Type":"application/json"}})}if(!t||typeof t!="object"||!Array.isArray(t.events))return new Response(JSON.stringify({error:'Payload must contain an "events" array'}),{status:400,headers:{"Content-Type":"application/json"}});let n=t.events;return n.length===0?new Response(JSON.stringify({accepted:0}),{status:202,headers:{"Content-Type":"application/json"}}):(await O(n),new Response(JSON.stringify({accepted:n.length}),{status:202,headers:{"Content-Type":"application/json"}}))}catch(t){let n=t instanceof Error?t.message:"Internal server error";return new Response(JSON.stringify({error:n}),{status:500,headers:{"Content-Type":"application/json"}})}}async function O(a){let t=await import('fs/promises'),n=await import('path'),i=n.resolve(process.cwd(),".uncaught");await t.mkdir(n.join(i,"events"),{recursive:true}),await t.mkdir(n.join(i,"fix-prompts"),{recursive:true});let c=n.join(i,"issues.json"),p=[];try{let e=await t.readFile(c,"utf-8");p=JSON.parse(e);}catch{}for(let e of a){let r=e.fingerprint;if(!r)continue;let o=n.join(i,"events",r);await t.mkdir(o,{recursive:true});let f=`event-${(e.timestamp??new Date().toISOString()).replace(/[:.]/g,"-")}.json`,y=n.join(o,f),g=y+".tmp";await t.writeFile(g,b(e),"utf-8"),await t.rename(g,y);let v=n.join(o,"latest.json"),h=v+".tmp";await t.writeFile(h,b(e),"utf-8"),await t.rename(h,v);let m=`${r}.md`,S=n.join(i,"fix-prompts",m),j=S+".tmp";await t.writeFile(j,e.fixPrompt??"","utf-8"),await t.rename(j,S);let d=e.user?.id??e.user?.email??"anonymous",s=p.find(E=>E.fingerprint===r);s?(s.count+=1,s.lastSeen=e.timestamp,s.latestEventFile=f,s.fixPromptFile=m,s.affectedUsers.includes(String(d))||s.affectedUsers.push(String(d)),s.status==="resolved"&&(s.status="open")):p.push({fingerprint:r,title:e.error?.message??"Unknown error",errorType:e.error?.type??"Error",count:1,affectedUsers:[String(d)],firstSeen:e.timestamp,lastSeen:e.timestamp,status:"open",fixPromptFile:m,latestEventFile:f});}let w=c+".tmp";await t.writeFile(w,JSON.stringify(p,null,2),"utf-8"),await t.rename(w,c);try{let{openStore:e}=await import('./sqlite-store-TEXDAAOM.mjs'),r=n.join(i,"uncaught.db"),o=e(r);for(let u of a)u.fingerprint&&o.insertEvent(u);o.close();}catch{}}export{N as a,O as b};//# sourceMappingURL=chunk-BXMN7NW4.mjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-BXMN7NW4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/local-api-handler.ts"],"names":["POST","request","body","events","writeEvents","err","message","fs","path","baseDir","indexPath","issues","raw","event","fp","eventDir","eventFile","eventPath","tmpEvent","safeStringify","latestPath","tmpLatest","promptFile","promptPath","tmpPrompt","userId","existing","i","tmpIndex","openStore","dbPath","store"],"mappings":"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","file":"chunk-BXMN7NW4.mjs","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — Next.js App Router local API handler\n// ---------------------------------------------------------------------------\n//\n// Usage:\n// // app/api/uncaught/local/route.ts\n// export { POST } from '@uncaughtdev/core/local-api-handler';\n//\n// ---------------------------------------------------------------------------\n\nimport type { UncaughtEvent, IssueEntry } from './types';\nimport { safeStringify } from './utils';\n\n/**\n * Next.js App Router POST handler.\n * Accepts `{ events: UncaughtEvent[] }` and writes them to `.uncaught/`.\n *\n * Blocked in production unless `UNCAUGHT_LOCAL_IN_PROD=true` is set.\n */\nexport async function POST(request: Request): Promise<Response> {\n try {\n // --- Production guard --------------------------------------------------\n if (\n typeof process !== 'undefined' &&\n process.env.NODE_ENV === 'production' &&\n process.env.UNCAUGHT_LOCAL_IN_PROD !== 'true'\n ) {\n return new Response(\n JSON.stringify({ error: 'Local handler is disabled in production' }),\n { status: 403, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // --- Parse body --------------------------------------------------------\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return new Response(\n JSON.stringify({ error: 'Invalid JSON body' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (\n !body ||\n typeof body !== 'object' ||\n !Array.isArray((body as Record<string, unknown>).events)\n ) {\n return new Response(\n JSON.stringify({ error: 'Payload must contain an \"events\" array' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const events = (body as { events: UncaughtEvent[] }).events;\n\n if (events.length === 0) {\n return new Response(\n JSON.stringify({ accepted: 0 }),\n { status: 202, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // --- Write events to disk ----------------------------------------------\n await writeEvents(events);\n\n return new Response(\n JSON.stringify({ accepted: events.length }),\n { status: 202, headers: { 'Content-Type': 'application/json' } }\n );\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Internal server error';\n return new Response(\n JSON.stringify({ error: message }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared disk-writing logic\n// ---------------------------------------------------------------------------\n\nexport async function writeEvents(events: UncaughtEvent[]): Promise<void> {\n const fs = await import('fs/promises');\n const path = await import('path');\n\n const baseDir = path.resolve(process.cwd(), '.uncaught');\n await fs.mkdir(path.join(baseDir, 'events'), { recursive: true });\n await fs.mkdir(path.join(baseDir, 'fix-prompts'), { recursive: true });\n\n // Load existing issues index\n const indexPath = path.join(baseDir, 'issues.json');\n let issues: IssueEntry[] = [];\n try {\n const raw = await fs.readFile(indexPath, 'utf-8');\n issues = JSON.parse(raw) as IssueEntry[];\n } catch {\n // Start fresh.\n }\n\n for (const event of events) {\n const fp = event.fingerprint;\n if (!fp) continue;\n\n const eventDir = path.join(baseDir, 'events', fp);\n await fs.mkdir(eventDir, { recursive: true });\n\n // Timestamped event file\n const ts = (event.timestamp ?? new Date().toISOString()).replace(\n /[:.]/g,\n '-'\n );\n const eventFile = `event-${ts}.json`;\n const eventPath = path.join(eventDir, eventFile);\n const tmpEvent = eventPath + '.tmp';\n await fs.writeFile(tmpEvent, safeStringify(event), 'utf-8');\n await fs.rename(tmpEvent, eventPath);\n\n // latest.json\n const latestPath = path.join(eventDir, 'latest.json');\n const tmpLatest = latestPath + '.tmp';\n await fs.writeFile(tmpLatest, safeStringify(event), 'utf-8');\n await fs.rename(tmpLatest, latestPath);\n\n // Fix-prompt markdown\n const promptFile = `${fp}.md`;\n const promptPath = path.join(baseDir, 'fix-prompts', promptFile);\n const tmpPrompt = promptPath + '.tmp';\n await fs.writeFile(tmpPrompt, event.fixPrompt ?? '', 'utf-8');\n await fs.rename(tmpPrompt, promptPath);\n\n // Update issues index\n const userId =\n (event.user as Record<string, unknown> | undefined)?.id ??\n (event.user as Record<string, unknown> | undefined)?.email ??\n 'anonymous';\n\n const existing = issues.find((i) => i.fingerprint === fp);\n if (existing) {\n existing.count += 1;\n existing.lastSeen = event.timestamp;\n existing.latestEventFile = eventFile;\n existing.fixPromptFile = promptFile;\n if (!existing.affectedUsers.includes(String(userId))) {\n existing.affectedUsers.push(String(userId));\n }\n if (existing.status === 'resolved') {\n existing.status = 'open';\n }\n } else {\n issues.push({\n fingerprint: fp,\n title: event.error?.message ?? 'Unknown error',\n errorType: event.error?.type ?? 'Error',\n count: 1,\n affectedUsers: [String(userId)],\n firstSeen: event.timestamp,\n lastSeen: event.timestamp,\n status: 'open',\n fixPromptFile: promptFile,\n latestEventFile: eventFile,\n });\n }\n }\n\n // Write updated index atomically\n const tmpIndex = indexPath + '.tmp';\n await fs.writeFile(tmpIndex, JSON.stringify(issues, null, 2), 'utf-8');\n await fs.rename(tmpIndex, indexPath);\n\n // Also write to SQLite\n try {\n const { openStore } = await import('./sqlite-store');\n const dbPath = path.join(baseDir, 'uncaught.db');\n const store = openStore(dbPath);\n for (const event of events) {\n if (!event.fingerprint) continue;\n store.insertEvent(event);\n }\n store.close();\n } catch {\n // SQLite is best-effort — flat files are the primary store\n }\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {a}from'./chunk-A6GKDPT3.mjs';import G from'better-sqlite3';var W=`
|
|
2
|
+
CREATE TABLE IF NOT EXISTS issues (
|
|
3
|
+
fingerprint TEXT PRIMARY KEY,
|
|
4
|
+
title TEXT NOT NULL,
|
|
5
|
+
error_type TEXT NOT NULL,
|
|
6
|
+
count INTEGER NOT NULL DEFAULT 1,
|
|
7
|
+
affected_users TEXT NOT NULL DEFAULT '[]',
|
|
8
|
+
first_seen TEXT NOT NULL,
|
|
9
|
+
last_seen TEXT NOT NULL,
|
|
10
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
11
|
+
fix_prompt_file TEXT NOT NULL DEFAULT '',
|
|
12
|
+
latest_event_file TEXT NOT NULL DEFAULT '',
|
|
13
|
+
release TEXT NOT NULL DEFAULT '',
|
|
14
|
+
environment TEXT NOT NULL DEFAULT ''
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_issues_status ON issues(status);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_issues_last_seen ON issues(last_seen);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
21
|
+
event_id TEXT PRIMARY KEY,
|
|
22
|
+
fingerprint TEXT NOT NULL,
|
|
23
|
+
timestamp TEXT NOT NULL,
|
|
24
|
+
level TEXT NOT NULL DEFAULT 'error',
|
|
25
|
+
fix_prompt TEXT NOT NULL DEFAULT '',
|
|
26
|
+
payload TEXT NOT NULL,
|
|
27
|
+
FOREIGN KEY (fingerprint) REFERENCES issues(fingerprint)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_events_fingerprint ON events(fingerprint);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
32
|
+
|
|
33
|
+
CREATE TABLE IF NOT EXISTS _meta (
|
|
34
|
+
key TEXT PRIMARY KEY,
|
|
35
|
+
value TEXT NOT NULL
|
|
36
|
+
);
|
|
37
|
+
`,Y=`
|
|
38
|
+
ALTER TABLE issues ADD COLUMN release TEXT NOT NULL DEFAULT '';
|
|
39
|
+
ALTER TABLE issues ADD COLUMN environment TEXT NOT NULL DEFAULT '';
|
|
40
|
+
`;function H(I){let t=new G(I);t.pragma("journal_mode = WAL"),t.pragma("foreign_keys = ON"),t.exec(W);try{for(let e of Y.trim().split(";").filter(Boolean))try{t.exec(e+";");}catch{}}catch{}let p=t.prepare(`
|
|
41
|
+
INSERT INTO issues (fingerprint, title, error_type, count, affected_users, first_seen, last_seen, status, fix_prompt_file, latest_event_file, release, environment)
|
|
42
|
+
VALUES (@fingerprint, @title, @error_type, @count, @affected_users, @first_seen, @last_seen, @status, @fix_prompt_file, @latest_event_file, @release, @environment)
|
|
43
|
+
ON CONFLICT(fingerprint) DO UPDATE SET
|
|
44
|
+
title = @title,
|
|
45
|
+
error_type = @error_type,
|
|
46
|
+
count = @count,
|
|
47
|
+
affected_users = @affected_users,
|
|
48
|
+
first_seen = @first_seen,
|
|
49
|
+
last_seen = @last_seen,
|
|
50
|
+
status = @status,
|
|
51
|
+
fix_prompt_file = @fix_prompt_file,
|
|
52
|
+
latest_event_file = @latest_event_file,
|
|
53
|
+
release = @release,
|
|
54
|
+
environment = @environment
|
|
55
|
+
`),O=t.prepare("SELECT * FROM issues ORDER BY last_seen DESC"),R=t.prepare("SELECT * FROM issues WHERE status = ? ORDER BY last_seen DESC"),U=t.prepare("SELECT * FROM issues WHERE environment = ? ORDER BY last_seen DESC"),F=t.prepare("SELECT * FROM issues WHERE status = ? AND environment = ? ORDER BY last_seen DESC"),d=t.prepare("SELECT * FROM issues WHERE fingerprint = ?"),A=t.prepare("UPDATE issues SET status = ? WHERE fingerprint = ?"),y=t.prepare("DELETE FROM issues"),C=t.prepare("DELETE FROM events"),S=t.prepare(`
|
|
56
|
+
INSERT OR IGNORE INTO events (event_id, fingerprint, timestamp, level, fix_prompt, payload)
|
|
57
|
+
VALUES (@event_id, @fingerprint, @timestamp, @level, @fix_prompt, @payload)
|
|
58
|
+
`),D=t.prepare("SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT ? OFFSET ?"),X=t.prepare("SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT 1"),x=t.prepare("SELECT COUNT(*) as cnt FROM events WHERE fingerprint = ?"),M=t.prepare("SELECT value FROM _meta WHERE key = ?"),b=t.prepare("INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)"),h=t.prepare("SELECT COUNT(*) as cnt FROM issues"),m=t.prepare("SELECT COUNT(*) as cnt FROM issues WHERE status = ?"),B=t.prepare("SELECT COUNT(*) as cnt FROM events");function T(e){return {fingerprint:e.fingerprint,title:e.title,errorType:e.error_type,count:e.count,affectedUsers:JSON.parse(e.affected_users),firstSeen:e.first_seen,lastSeen:e.last_seen,status:e.status,fixPromptFile:e.fix_prompt_file,latestEventFile:e.latest_event_file,release:e.release||void 0,environment:e.environment||void 0}}function l(e){return {fingerprint:e.fingerprint,title:e.title,error_type:e.errorType,count:e.count,affected_users:JSON.stringify(e.affectedUsers),first_seen:e.firstSeen,last_seen:e.lastSeen,status:e.status,fix_prompt_file:e.fixPromptFile,latest_event_file:e.latestEventFile,release:e.release??"",environment:e.environment??""}}function _(e){return JSON.parse(e.payload)}let P=t.transaction((e,s,a)=>{let i=e.fingerprint,r=e.user?.id??e.user?.email??"anonymous";S.run({event_id:e.eventId,fingerprint:i,timestamp:e.timestamp,level:e.level,fix_prompt:e.fixPrompt??"",payload:JSON.stringify(e)});let o=d.get(i);if(o){let n=T(o);n.count+=1,n.lastSeen=e.timestamp,n.latestEventFile=s,n.fixPromptFile=a,n.release=e.release??n.release,n.environment=e.environment?.deploy??n.environment,n.affectedUsers.includes(String(r))||n.affectedUsers.push(String(r)),n.status==="resolved"&&(n.status="open"),p.run(l(n));}else p.run(l({fingerprint:i,title:e.error?.message??"Unknown error",errorType:e.error?.type??"Error",count:1,affectedUsers:[String(r)],firstSeen:e.timestamp,lastSeen:e.timestamp,status:"open",fixPromptFile:a,latestEventFile:s,release:e.release,environment:e.environment?.deploy}));});return {upsertIssue(e){p.run(l(e));},getIssues(e){let s;return e?.status&&e?.environment?s=F.all(e.status,e.environment):e?.status?s=R.all(e.status):e?.environment?s=U.all(e.environment):s=O.all(),s.map(T)},getIssue(e){let s=d.get(e);return s?T(s):void 0},updateIssueStatus(e,s){A.run(s,e);},deleteAllIssues(){C.run(),y.run();},insertEvent(e){let a=`event-${(e.timestamp??new Date().toISOString()).replace(/[:.]/g,"-")}.json`,i=`${e.fingerprint}.md`;P(e,a,i);},getEvents(e,s){let a=s?.limit??50,i=s?.offset??0;return D.all(e,a,i).map(_)},getLatestEvent(e){let s=X.get(e);return s?_(s):void 0},getEventCount(e){return x.get(e).cnt},getStats(){return {total:h.get().cnt,open:m.get("open").cnt,resolved:m.get("resolved").cnt,ignored:m.get("ignored").cnt,totalEvents:B.get().cnt}},importFromFiles(e){if(M.get("migrated")?.value==="true")return {issues:0,events:0};let a$1=0,i=0;try{let r=a("fs"),o=a("path"),n=o.join(e,"issues.json");if(r.existsSync(n)){let g=r.readFileSync(n,"utf-8"),v=JSON.parse(g);for(let u of v)p.run(l(u)),a$1++;}let c=o.join(e,"events");if(r.existsSync(c)){let g=r.readdirSync(c);for(let v of g){let u=o.join(c,v);if(!r.statSync(u).isDirectory())continue;let k=r.readdirSync(u).filter(f=>f.startsWith("event-")&&f.endsWith(".json"));for(let f of k)try{let L=r.readFileSync(o.join(u,f),"utf-8"),E=JSON.parse(L);S.run({event_id:E.eventId,fingerprint:E.fingerprint,timestamp:E.timestamp,level:E.level,fix_prompt:E.fixPrompt??"",payload:L}),i++;}catch{}}}}catch{}return b.run("migrated","true"),{issues:a$1,events:i}},close(){t.close();}}}export{H as a};//# sourceMappingURL=chunk-HANXURHX.mjs.map
|
|
59
|
+
//# sourceMappingURL=chunk-HANXURHX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sqlite-store.ts"],"names":["SCHEMA_SQL","MIGRATION_SQL","openStore","dbPath","db","Database","stmt","stmtUpsertIssue","stmtGetIssues","stmtGetIssuesByStatus","stmtGetIssuesByEnv","stmtGetIssuesByStatusAndEnv","stmtGetIssue","stmtUpdateStatus","stmtDeleteAllIssues","stmtDeleteAllEvents","stmtInsertEvent","stmtGetEvents","stmtGetLatestEvent","stmtGetEventCount","stmtGetMeta","stmtSetMeta","stmtCountAll","stmtCountByStatus","stmtCountEvents","rowToIssue","row","issueToParams","entry","rowToEvent","insertEventAndUpsert","event","eventFile","promptFile","fp","userId","existingRow","existing","filter","rows","fingerprint","status","opts","limit","offset","baseDir","issueCount","eventCount","fsSync","N","pathMod","issuesPath","raw","issues","issue","eventsDir","fps","fpDir","files","file","eventRaw"],"mappings":"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","file":"chunk-HANXURHX.mjs","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — SQLite storage layer\n// ---------------------------------------------------------------------------\n\nimport Database from 'better-sqlite3';\nimport type { IssueEntry, IssueStatus, UncaughtEvent } from './types';\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS issues (\n fingerprint TEXT PRIMARY KEY,\n title TEXT NOT NULL,\n error_type TEXT NOT NULL,\n count INTEGER NOT NULL DEFAULT 1,\n affected_users TEXT NOT NULL DEFAULT '[]',\n first_seen TEXT NOT NULL,\n last_seen TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'open',\n fix_prompt_file TEXT NOT NULL DEFAULT '',\n latest_event_file TEXT NOT NULL DEFAULT '',\n release TEXT NOT NULL DEFAULT '',\n environment TEXT NOT NULL DEFAULT ''\n);\n\nCREATE INDEX IF NOT EXISTS idx_issues_status ON issues(status);\nCREATE INDEX IF NOT EXISTS idx_issues_last_seen ON issues(last_seen);\n\nCREATE TABLE IF NOT EXISTS events (\n event_id TEXT PRIMARY KEY,\n fingerprint TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n level TEXT NOT NULL DEFAULT 'error',\n fix_prompt TEXT NOT NULL DEFAULT '',\n payload TEXT NOT NULL,\n FOREIGN KEY (fingerprint) REFERENCES issues(fingerprint)\n);\n\nCREATE INDEX IF NOT EXISTS idx_events_fingerprint ON events(fingerprint);\nCREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);\n\nCREATE TABLE IF NOT EXISTS _meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n`;\n\n// Migrate existing databases that lack new columns\nconst MIGRATION_SQL = `\nALTER TABLE issues ADD COLUMN release TEXT NOT NULL DEFAULT '';\nALTER TABLE issues ADD COLUMN environment TEXT NOT NULL DEFAULT '';\n`;\n\n// ---------------------------------------------------------------------------\n// Public interface\n// ---------------------------------------------------------------------------\n\nexport interface SqliteStore {\n upsertIssue(entry: IssueEntry): void;\n getIssues(filter?: { status?: IssueStatus; environment?: string }): IssueEntry[];\n getIssue(fingerprint: string): IssueEntry | undefined;\n updateIssueStatus(fingerprint: string, status: IssueStatus): void;\n deleteAllIssues(): void;\n\n insertEvent(event: UncaughtEvent): void;\n getEvents(fingerprint: string, opts?: { limit?: number; offset?: number }): UncaughtEvent[];\n getLatestEvent(fingerprint: string): UncaughtEvent | undefined;\n getEventCount(fingerprint: string): number;\n\n getStats(): { total: number; open: number; resolved: number; ignored: number; totalEvents: number };\n\n importFromFiles(baseDir: string): { issues: number; events: number };\n\n close(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport function openStore(dbPath: string): SqliteStore {\n const db = new Database(dbPath);\n db.pragma('journal_mode = WAL');\n db.pragma('foreign_keys = ON');\n db.exec(SCHEMA_SQL);\n\n // Run migrations for existing databases\n try {\n for (const stmt of MIGRATION_SQL.trim().split(';').filter(Boolean)) {\n try { db.exec(stmt + ';'); } catch { /* column already exists */ }\n }\n } catch { /* migration best-effort */ }\n\n // Prepared statements\n const stmtUpsertIssue = db.prepare(`\n INSERT INTO issues (fingerprint, title, error_type, count, affected_users, first_seen, last_seen, status, fix_prompt_file, latest_event_file, release, environment)\n VALUES (@fingerprint, @title, @error_type, @count, @affected_users, @first_seen, @last_seen, @status, @fix_prompt_file, @latest_event_file, @release, @environment)\n ON CONFLICT(fingerprint) DO UPDATE SET\n title = @title,\n error_type = @error_type,\n count = @count,\n affected_users = @affected_users,\n first_seen = @first_seen,\n last_seen = @last_seen,\n status = @status,\n fix_prompt_file = @fix_prompt_file,\n latest_event_file = @latest_event_file,\n release = @release,\n environment = @environment\n `);\n\n const stmtGetIssues = db.prepare('SELECT * FROM issues ORDER BY last_seen DESC');\n const stmtGetIssuesByStatus = db.prepare('SELECT * FROM issues WHERE status = ? ORDER BY last_seen DESC');\n const stmtGetIssuesByEnv = db.prepare('SELECT * FROM issues WHERE environment = ? ORDER BY last_seen DESC');\n const stmtGetIssuesByStatusAndEnv = db.prepare('SELECT * FROM issues WHERE status = ? AND environment = ? ORDER BY last_seen DESC');\n const stmtGetIssue = db.prepare('SELECT * FROM issues WHERE fingerprint = ?');\n const stmtUpdateStatus = db.prepare('UPDATE issues SET status = ? WHERE fingerprint = ?');\n const stmtDeleteAllIssues = db.prepare('DELETE FROM issues');\n const stmtDeleteAllEvents = db.prepare('DELETE FROM events');\n\n const stmtInsertEvent = db.prepare(`\n INSERT OR IGNORE INTO events (event_id, fingerprint, timestamp, level, fix_prompt, payload)\n VALUES (@event_id, @fingerprint, @timestamp, @level, @fix_prompt, @payload)\n `);\n\n const stmtGetEvents = db.prepare('SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT ? OFFSET ?');\n const stmtGetLatestEvent = db.prepare('SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT 1');\n const stmtGetEventCount = db.prepare('SELECT COUNT(*) as cnt FROM events WHERE fingerprint = ?');\n\n const stmtGetMeta = db.prepare('SELECT value FROM _meta WHERE key = ?');\n const stmtSetMeta = db.prepare('INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)');\n\n const stmtCountAll = db.prepare('SELECT COUNT(*) as cnt FROM issues');\n const stmtCountByStatus = db.prepare('SELECT COUNT(*) as cnt FROM issues WHERE status = ?');\n const stmtCountEvents = db.prepare('SELECT COUNT(*) as cnt FROM events');\n\n // Row → IssueEntry mapper\n function rowToIssue(row: Record<string, unknown>): IssueEntry {\n return {\n fingerprint: row.fingerprint as string,\n title: row.title as string,\n errorType: row.error_type as string,\n count: row.count as number,\n affectedUsers: JSON.parse(row.affected_users as string),\n firstSeen: row.first_seen as string,\n lastSeen: row.last_seen as string,\n status: row.status as IssueStatus,\n fixPromptFile: row.fix_prompt_file as string,\n latestEventFile: row.latest_event_file as string,\n release: (row.release as string) || undefined,\n environment: (row.environment as string) || undefined,\n };\n }\n\n // IssueEntry → row params mapper\n function issueToParams(entry: IssueEntry) {\n return {\n fingerprint: entry.fingerprint,\n title: entry.title,\n error_type: entry.errorType,\n count: entry.count,\n affected_users: JSON.stringify(entry.affectedUsers),\n first_seen: entry.firstSeen,\n last_seen: entry.lastSeen,\n status: entry.status,\n fix_prompt_file: entry.fixPromptFile,\n latest_event_file: entry.latestEventFile,\n release: entry.release ?? '',\n environment: entry.environment ?? '',\n };\n }\n\n // Row → UncaughtEvent mapper\n function rowToEvent(row: Record<string, unknown>): UncaughtEvent {\n return JSON.parse(row.payload as string) as UncaughtEvent;\n }\n\n // Insert event + upsert issue atomically\n const insertEventAndUpsert = db.transaction((event: UncaughtEvent, eventFile: string, promptFile: string) => {\n const fp = event.fingerprint;\n const userId = event.user?.id ?? event.user?.email ?? 'anonymous';\n\n // Insert event row\n stmtInsertEvent.run({\n event_id: event.eventId,\n fingerprint: fp,\n timestamp: event.timestamp,\n level: event.level,\n fix_prompt: event.fixPrompt ?? '',\n payload: JSON.stringify(event),\n });\n\n // Upsert issue\n const existingRow = stmtGetIssue.get(fp) as Record<string, unknown> | undefined;\n if (existingRow) {\n const existing = rowToIssue(existingRow);\n existing.count += 1;\n existing.lastSeen = event.timestamp;\n existing.latestEventFile = eventFile;\n existing.fixPromptFile = promptFile;\n existing.release = event.release ?? existing.release;\n existing.environment = event.environment?.deploy ?? existing.environment;\n if (!existing.affectedUsers.includes(String(userId))) {\n existing.affectedUsers.push(String(userId));\n }\n if (existing.status === 'resolved') {\n existing.status = 'open';\n }\n stmtUpsertIssue.run(issueToParams(existing));\n } else {\n stmtUpsertIssue.run(issueToParams({\n fingerprint: fp,\n title: event.error?.message ?? 'Unknown error',\n errorType: event.error?.type ?? 'Error',\n count: 1,\n affectedUsers: [String(userId)],\n firstSeen: event.timestamp,\n lastSeen: event.timestamp,\n status: 'open',\n fixPromptFile: promptFile,\n latestEventFile: eventFile,\n release: event.release,\n environment: event.environment?.deploy,\n }));\n }\n });\n\n return {\n upsertIssue(entry: IssueEntry): void {\n stmtUpsertIssue.run(issueToParams(entry));\n },\n\n getIssues(filter?: { status?: IssueStatus; environment?: string }): IssueEntry[] {\n let rows: unknown[];\n if (filter?.status && filter?.environment) {\n rows = stmtGetIssuesByStatusAndEnv.all(filter.status, filter.environment);\n } else if (filter?.status) {\n rows = stmtGetIssuesByStatus.all(filter.status);\n } else if (filter?.environment) {\n rows = stmtGetIssuesByEnv.all(filter.environment);\n } else {\n rows = stmtGetIssues.all();\n }\n return (rows as Record<string, unknown>[]).map(rowToIssue);\n },\n\n getIssue(fingerprint: string): IssueEntry | undefined {\n const row = stmtGetIssue.get(fingerprint) as Record<string, unknown> | undefined;\n return row ? rowToIssue(row) : undefined;\n },\n\n updateIssueStatus(fingerprint: string, status: IssueStatus): void {\n stmtUpdateStatus.run(status, fingerprint);\n },\n\n deleteAllIssues(): void {\n stmtDeleteAllEvents.run();\n stmtDeleteAllIssues.run();\n },\n\n insertEvent(event: UncaughtEvent): void {\n const ts = (event.timestamp ?? new Date().toISOString()).replace(/[:.]/g, '-');\n const eventFile = `event-${ts}.json`;\n const promptFile = `${event.fingerprint}.md`;\n insertEventAndUpsert(event, eventFile, promptFile);\n },\n\n getEvents(fingerprint: string, opts?: { limit?: number; offset?: number }): UncaughtEvent[] {\n const limit = opts?.limit ?? 50;\n const offset = opts?.offset ?? 0;\n const rows = stmtGetEvents.all(fingerprint, limit, offset);\n return (rows as Record<string, unknown>[]).map(rowToEvent);\n },\n\n getLatestEvent(fingerprint: string): UncaughtEvent | undefined {\n const row = stmtGetLatestEvent.get(fingerprint) as Record<string, unknown> | undefined;\n return row ? rowToEvent(row) : undefined;\n },\n\n getEventCount(fingerprint: string): number {\n const row = stmtGetEventCount.get(fingerprint) as { cnt: number };\n return row.cnt;\n },\n\n getStats() {\n return {\n total: (stmtCountAll.get() as { cnt: number }).cnt,\n open: (stmtCountByStatus.get('open') as { cnt: number }).cnt,\n resolved: (stmtCountByStatus.get('resolved') as { cnt: number }).cnt,\n ignored: (stmtCountByStatus.get('ignored') as { cnt: number }).cnt,\n totalEvents: (stmtCountEvents.get() as { cnt: number }).cnt,\n };\n },\n\n importFromFiles(baseDir: string): { issues: number; events: number } {\n const meta = stmtGetMeta.get('migrated') as { value: string } | undefined;\n if (meta?.value === 'true') return { issues: 0, events: 0 };\n\n let issueCount = 0;\n let eventCount = 0;\n\n try {\n const fsSync = require('fs');\n const pathMod = require('path');\n\n // Import issues.json\n const issuesPath = pathMod.join(baseDir, 'issues.json');\n if (fsSync.existsSync(issuesPath)) {\n const raw = fsSync.readFileSync(issuesPath, 'utf-8');\n const issues = JSON.parse(raw) as IssueEntry[];\n for (const issue of issues) {\n stmtUpsertIssue.run(issueToParams(issue));\n issueCount++;\n }\n }\n\n // Import event files\n const eventsDir = pathMod.join(baseDir, 'events');\n if (fsSync.existsSync(eventsDir)) {\n const fps = fsSync.readdirSync(eventsDir);\n for (const fp of fps) {\n const fpDir = pathMod.join(eventsDir, fp);\n if (!fsSync.statSync(fpDir).isDirectory()) continue;\n\n const files = fsSync.readdirSync(fpDir).filter((f: string) => f.startsWith('event-') && f.endsWith('.json'));\n for (const file of files) {\n try {\n const eventRaw = fsSync.readFileSync(pathMod.join(fpDir, file), 'utf-8');\n const event = JSON.parse(eventRaw) as UncaughtEvent;\n stmtInsertEvent.run({\n event_id: event.eventId,\n fingerprint: event.fingerprint,\n timestamp: event.timestamp,\n level: event.level,\n fix_prompt: event.fixPrompt ?? '',\n payload: eventRaw,\n });\n eventCount++;\n } catch {\n // Skip malformed event files\n }\n }\n }\n }\n } catch {\n // Migration is best-effort\n }\n\n stmtSetMeta.run('migrated', 'true');\n return { issues: issueCount, events: eventCount };\n },\n\n close(): void {\n db.close();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var chunkE76GW6KF_js=require('./chunk-E76GW6KF.js');async function N(a){try{if(typeof process<"u"&&process.env.NODE_ENV==="production"&&process.env.UNCAUGHT_LOCAL_IN_PROD!=="true")return new Response(JSON.stringify({error:"Local handler is disabled in production"}),{status:403,headers:{"Content-Type":"application/json"}});let t;try{t=await a.json();}catch{return new Response(JSON.stringify({error:"Invalid JSON body"}),{status:400,headers:{"Content-Type":"application/json"}})}if(!t||typeof t!="object"||!Array.isArray(t.events))return new Response(JSON.stringify({error:'Payload must contain an "events" array'}),{status:400,headers:{"Content-Type":"application/json"}});let n=t.events;return n.length===0?new Response(JSON.stringify({accepted:0}),{status:202,headers:{"Content-Type":"application/json"}}):(await O(n),new Response(JSON.stringify({accepted:n.length}),{status:202,headers:{"Content-Type":"application/json"}}))}catch(t){let n=t instanceof Error?t.message:"Internal server error";return new Response(JSON.stringify({error:n}),{status:500,headers:{"Content-Type":"application/json"}})}}async function O(a){let t=await import('fs/promises'),n=await import('path'),i=n.resolve(process.cwd(),".uncaught");await t.mkdir(n.join(i,"events"),{recursive:true}),await t.mkdir(n.join(i,"fix-prompts"),{recursive:true});let c=n.join(i,"issues.json"),p=[];try{let e=await t.readFile(c,"utf-8");p=JSON.parse(e);}catch{}for(let e of a){let r=e.fingerprint;if(!r)continue;let o=n.join(i,"events",r);await t.mkdir(o,{recursive:true});let f=`event-${(e.timestamp??new Date().toISOString()).replace(/[:.]/g,"-")}.json`,y=n.join(o,f),g=y+".tmp";await t.writeFile(g,chunkE76GW6KF_js.b(e),"utf-8"),await t.rename(g,y);let v=n.join(o,"latest.json"),h=v+".tmp";await t.writeFile(h,chunkE76GW6KF_js.b(e),"utf-8"),await t.rename(h,v);let m=`${r}.md`,S=n.join(i,"fix-prompts",m),j=S+".tmp";await t.writeFile(j,e.fixPrompt??"","utf-8"),await t.rename(j,S);let d=e.user?.id??e.user?.email??"anonymous",s=p.find(E=>E.fingerprint===r);s?(s.count+=1,s.lastSeen=e.timestamp,s.latestEventFile=f,s.fixPromptFile=m,s.affectedUsers.includes(String(d))||s.affectedUsers.push(String(d)),s.status==="resolved"&&(s.status="open")):p.push({fingerprint:r,title:e.error?.message??"Unknown error",errorType:e.error?.type??"Error",count:1,affectedUsers:[String(d)],firstSeen:e.timestamp,lastSeen:e.timestamp,status:"open",fixPromptFile:m,latestEventFile:f});}let w=c+".tmp";await t.writeFile(w,JSON.stringify(p,null,2),"utf-8"),await t.rename(w,c);try{let{openStore:e}=await import('./sqlite-store-4FTNST7O.js'),r=n.join(i,"uncaught.db"),o=e(r);for(let u of a)u.fingerprint&&o.insertEvent(u);o.close();}catch{}}exports.a=N;exports.b=O;//# sourceMappingURL=chunk-MSUAXLMV.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-MSUAXLMV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/local-api-handler.ts"],"names":["POST","request","body","events","writeEvents","err","message","fs","path","baseDir","indexPath","issues","raw","event","fp","eventDir","eventFile","eventPath","tmpEvent","safeStringify","latestPath","tmpLatest","promptFile","promptPath","tmpPrompt","userId","existing","i","tmpIndex","openStore","dbPath","store"],"mappings":"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","file":"chunk-MSUAXLMV.js","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — Next.js App Router local API handler\n// ---------------------------------------------------------------------------\n//\n// Usage:\n// // app/api/uncaught/local/route.ts\n// export { POST } from '@uncaughtdev/core/local-api-handler';\n//\n// ---------------------------------------------------------------------------\n\nimport type { UncaughtEvent, IssueEntry } from './types';\nimport { safeStringify } from './utils';\n\n/**\n * Next.js App Router POST handler.\n * Accepts `{ events: UncaughtEvent[] }` and writes them to `.uncaught/`.\n *\n * Blocked in production unless `UNCAUGHT_LOCAL_IN_PROD=true` is set.\n */\nexport async function POST(request: Request): Promise<Response> {\n try {\n // --- Production guard --------------------------------------------------\n if (\n typeof process !== 'undefined' &&\n process.env.NODE_ENV === 'production' &&\n process.env.UNCAUGHT_LOCAL_IN_PROD !== 'true'\n ) {\n return new Response(\n JSON.stringify({ error: 'Local handler is disabled in production' }),\n { status: 403, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // --- Parse body --------------------------------------------------------\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return new Response(\n JSON.stringify({ error: 'Invalid JSON body' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (\n !body ||\n typeof body !== 'object' ||\n !Array.isArray((body as Record<string, unknown>).events)\n ) {\n return new Response(\n JSON.stringify({ error: 'Payload must contain an \"events\" array' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const events = (body as { events: UncaughtEvent[] }).events;\n\n if (events.length === 0) {\n return new Response(\n JSON.stringify({ accepted: 0 }),\n { status: 202, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // --- Write events to disk ----------------------------------------------\n await writeEvents(events);\n\n return new Response(\n JSON.stringify({ accepted: events.length }),\n { status: 202, headers: { 'Content-Type': 'application/json' } }\n );\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Internal server error';\n return new Response(\n JSON.stringify({ error: message }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared disk-writing logic\n// ---------------------------------------------------------------------------\n\nexport async function writeEvents(events: UncaughtEvent[]): Promise<void> {\n const fs = await import('fs/promises');\n const path = await import('path');\n\n const baseDir = path.resolve(process.cwd(), '.uncaught');\n await fs.mkdir(path.join(baseDir, 'events'), { recursive: true });\n await fs.mkdir(path.join(baseDir, 'fix-prompts'), { recursive: true });\n\n // Load existing issues index\n const indexPath = path.join(baseDir, 'issues.json');\n let issues: IssueEntry[] = [];\n try {\n const raw = await fs.readFile(indexPath, 'utf-8');\n issues = JSON.parse(raw) as IssueEntry[];\n } catch {\n // Start fresh.\n }\n\n for (const event of events) {\n const fp = event.fingerprint;\n if (!fp) continue;\n\n const eventDir = path.join(baseDir, 'events', fp);\n await fs.mkdir(eventDir, { recursive: true });\n\n // Timestamped event file\n const ts = (event.timestamp ?? new Date().toISOString()).replace(\n /[:.]/g,\n '-'\n );\n const eventFile = `event-${ts}.json`;\n const eventPath = path.join(eventDir, eventFile);\n const tmpEvent = eventPath + '.tmp';\n await fs.writeFile(tmpEvent, safeStringify(event), 'utf-8');\n await fs.rename(tmpEvent, eventPath);\n\n // latest.json\n const latestPath = path.join(eventDir, 'latest.json');\n const tmpLatest = latestPath + '.tmp';\n await fs.writeFile(tmpLatest, safeStringify(event), 'utf-8');\n await fs.rename(tmpLatest, latestPath);\n\n // Fix-prompt markdown\n const promptFile = `${fp}.md`;\n const promptPath = path.join(baseDir, 'fix-prompts', promptFile);\n const tmpPrompt = promptPath + '.tmp';\n await fs.writeFile(tmpPrompt, event.fixPrompt ?? '', 'utf-8');\n await fs.rename(tmpPrompt, promptPath);\n\n // Update issues index\n const userId =\n (event.user as Record<string, unknown> | undefined)?.id ??\n (event.user as Record<string, unknown> | undefined)?.email ??\n 'anonymous';\n\n const existing = issues.find((i) => i.fingerprint === fp);\n if (existing) {\n existing.count += 1;\n existing.lastSeen = event.timestamp;\n existing.latestEventFile = eventFile;\n existing.fixPromptFile = promptFile;\n if (!existing.affectedUsers.includes(String(userId))) {\n existing.affectedUsers.push(String(userId));\n }\n if (existing.status === 'resolved') {\n existing.status = 'open';\n }\n } else {\n issues.push({\n fingerprint: fp,\n title: event.error?.message ?? 'Unknown error',\n errorType: event.error?.type ?? 'Error',\n count: 1,\n affectedUsers: [String(userId)],\n firstSeen: event.timestamp,\n lastSeen: event.timestamp,\n status: 'open',\n fixPromptFile: promptFile,\n latestEventFile: eventFile,\n });\n }\n }\n\n // Write updated index atomically\n const tmpIndex = indexPath + '.tmp';\n await fs.writeFile(tmpIndex, JSON.stringify(issues, null, 2), 'utf-8');\n await fs.rename(tmpIndex, indexPath);\n\n // Also write to SQLite\n try {\n const { openStore } = await import('./sqlite-store');\n const dbPath = path.join(baseDir, 'uncaught.db');\n const store = openStore(dbPath);\n for (const event of events) {\n if (!event.fingerprint) continue;\n store.insertEvent(event);\n }\n store.close();\n } catch {\n // SQLite is best-effort — flat files are the primary store\n }\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';var chunk2YXXFGBV_js=require('./chunk-2YXXFGBV.js'),G=require('better-sqlite3');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var G__default=/*#__PURE__*/_interopDefault(G);var W=`
|
|
2
|
+
CREATE TABLE IF NOT EXISTS issues (
|
|
3
|
+
fingerprint TEXT PRIMARY KEY,
|
|
4
|
+
title TEXT NOT NULL,
|
|
5
|
+
error_type TEXT NOT NULL,
|
|
6
|
+
count INTEGER NOT NULL DEFAULT 1,
|
|
7
|
+
affected_users TEXT NOT NULL DEFAULT '[]',
|
|
8
|
+
first_seen TEXT NOT NULL,
|
|
9
|
+
last_seen TEXT NOT NULL,
|
|
10
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
11
|
+
fix_prompt_file TEXT NOT NULL DEFAULT '',
|
|
12
|
+
latest_event_file TEXT NOT NULL DEFAULT '',
|
|
13
|
+
release TEXT NOT NULL DEFAULT '',
|
|
14
|
+
environment TEXT NOT NULL DEFAULT ''
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_issues_status ON issues(status);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_issues_last_seen ON issues(last_seen);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
21
|
+
event_id TEXT PRIMARY KEY,
|
|
22
|
+
fingerprint TEXT NOT NULL,
|
|
23
|
+
timestamp TEXT NOT NULL,
|
|
24
|
+
level TEXT NOT NULL DEFAULT 'error',
|
|
25
|
+
fix_prompt TEXT NOT NULL DEFAULT '',
|
|
26
|
+
payload TEXT NOT NULL,
|
|
27
|
+
FOREIGN KEY (fingerprint) REFERENCES issues(fingerprint)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_events_fingerprint ON events(fingerprint);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
32
|
+
|
|
33
|
+
CREATE TABLE IF NOT EXISTS _meta (
|
|
34
|
+
key TEXT PRIMARY KEY,
|
|
35
|
+
value TEXT NOT NULL
|
|
36
|
+
);
|
|
37
|
+
`,Y=`
|
|
38
|
+
ALTER TABLE issues ADD COLUMN release TEXT NOT NULL DEFAULT '';
|
|
39
|
+
ALTER TABLE issues ADD COLUMN environment TEXT NOT NULL DEFAULT '';
|
|
40
|
+
`;function H(I){let t=new G__default.default(I);t.pragma("journal_mode = WAL"),t.pragma("foreign_keys = ON"),t.exec(W);try{for(let e of Y.trim().split(";").filter(Boolean))try{t.exec(e+";");}catch{}}catch{}let p=t.prepare(`
|
|
41
|
+
INSERT INTO issues (fingerprint, title, error_type, count, affected_users, first_seen, last_seen, status, fix_prompt_file, latest_event_file, release, environment)
|
|
42
|
+
VALUES (@fingerprint, @title, @error_type, @count, @affected_users, @first_seen, @last_seen, @status, @fix_prompt_file, @latest_event_file, @release, @environment)
|
|
43
|
+
ON CONFLICT(fingerprint) DO UPDATE SET
|
|
44
|
+
title = @title,
|
|
45
|
+
error_type = @error_type,
|
|
46
|
+
count = @count,
|
|
47
|
+
affected_users = @affected_users,
|
|
48
|
+
first_seen = @first_seen,
|
|
49
|
+
last_seen = @last_seen,
|
|
50
|
+
status = @status,
|
|
51
|
+
fix_prompt_file = @fix_prompt_file,
|
|
52
|
+
latest_event_file = @latest_event_file,
|
|
53
|
+
release = @release,
|
|
54
|
+
environment = @environment
|
|
55
|
+
`),O=t.prepare("SELECT * FROM issues ORDER BY last_seen DESC"),R=t.prepare("SELECT * FROM issues WHERE status = ? ORDER BY last_seen DESC"),U=t.prepare("SELECT * FROM issues WHERE environment = ? ORDER BY last_seen DESC"),F=t.prepare("SELECT * FROM issues WHERE status = ? AND environment = ? ORDER BY last_seen DESC"),d=t.prepare("SELECT * FROM issues WHERE fingerprint = ?"),A=t.prepare("UPDATE issues SET status = ? WHERE fingerprint = ?"),y=t.prepare("DELETE FROM issues"),C=t.prepare("DELETE FROM events"),S=t.prepare(`
|
|
56
|
+
INSERT OR IGNORE INTO events (event_id, fingerprint, timestamp, level, fix_prompt, payload)
|
|
57
|
+
VALUES (@event_id, @fingerprint, @timestamp, @level, @fix_prompt, @payload)
|
|
58
|
+
`),D=t.prepare("SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT ? OFFSET ?"),X=t.prepare("SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT 1"),x=t.prepare("SELECT COUNT(*) as cnt FROM events WHERE fingerprint = ?"),M=t.prepare("SELECT value FROM _meta WHERE key = ?"),b=t.prepare("INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)"),h=t.prepare("SELECT COUNT(*) as cnt FROM issues"),m=t.prepare("SELECT COUNT(*) as cnt FROM issues WHERE status = ?"),B=t.prepare("SELECT COUNT(*) as cnt FROM events");function T(e){return {fingerprint:e.fingerprint,title:e.title,errorType:e.error_type,count:e.count,affectedUsers:JSON.parse(e.affected_users),firstSeen:e.first_seen,lastSeen:e.last_seen,status:e.status,fixPromptFile:e.fix_prompt_file,latestEventFile:e.latest_event_file,release:e.release||void 0,environment:e.environment||void 0}}function l(e){return {fingerprint:e.fingerprint,title:e.title,error_type:e.errorType,count:e.count,affected_users:JSON.stringify(e.affectedUsers),first_seen:e.firstSeen,last_seen:e.lastSeen,status:e.status,fix_prompt_file:e.fixPromptFile,latest_event_file:e.latestEventFile,release:e.release??"",environment:e.environment??""}}function _(e){return JSON.parse(e.payload)}let P=t.transaction((e,s,a)=>{let i=e.fingerprint,r=e.user?.id??e.user?.email??"anonymous";S.run({event_id:e.eventId,fingerprint:i,timestamp:e.timestamp,level:e.level,fix_prompt:e.fixPrompt??"",payload:JSON.stringify(e)});let o=d.get(i);if(o){let n=T(o);n.count+=1,n.lastSeen=e.timestamp,n.latestEventFile=s,n.fixPromptFile=a,n.release=e.release??n.release,n.environment=e.environment?.deploy??n.environment,n.affectedUsers.includes(String(r))||n.affectedUsers.push(String(r)),n.status==="resolved"&&(n.status="open"),p.run(l(n));}else p.run(l({fingerprint:i,title:e.error?.message??"Unknown error",errorType:e.error?.type??"Error",count:1,affectedUsers:[String(r)],firstSeen:e.timestamp,lastSeen:e.timestamp,status:"open",fixPromptFile:a,latestEventFile:s,release:e.release,environment:e.environment?.deploy}));});return {upsertIssue(e){p.run(l(e));},getIssues(e){let s;return e?.status&&e?.environment?s=F.all(e.status,e.environment):e?.status?s=R.all(e.status):e?.environment?s=U.all(e.environment):s=O.all(),s.map(T)},getIssue(e){let s=d.get(e);return s?T(s):void 0},updateIssueStatus(e,s){A.run(s,e);},deleteAllIssues(){C.run(),y.run();},insertEvent(e){let a=`event-${(e.timestamp??new Date().toISOString()).replace(/[:.]/g,"-")}.json`,i=`${e.fingerprint}.md`;P(e,a,i);},getEvents(e,s){let a=s?.limit??50,i=s?.offset??0;return D.all(e,a,i).map(_)},getLatestEvent(e){let s=X.get(e);return s?_(s):void 0},getEventCount(e){return x.get(e).cnt},getStats(){return {total:h.get().cnt,open:m.get("open").cnt,resolved:m.get("resolved").cnt,ignored:m.get("ignored").cnt,totalEvents:B.get().cnt}},importFromFiles(e){if(M.get("migrated")?.value==="true")return {issues:0,events:0};let a=0,i=0;try{let r=chunk2YXXFGBV_js.a("fs"),o=chunk2YXXFGBV_js.a("path"),n=o.join(e,"issues.json");if(r.existsSync(n)){let g=r.readFileSync(n,"utf-8"),v=JSON.parse(g);for(let u of v)p.run(l(u)),a++;}let c=o.join(e,"events");if(r.existsSync(c)){let g=r.readdirSync(c);for(let v of g){let u=o.join(c,v);if(!r.statSync(u).isDirectory())continue;let k=r.readdirSync(u).filter(f=>f.startsWith("event-")&&f.endsWith(".json"));for(let f of k)try{let L=r.readFileSync(o.join(u,f),"utf-8"),E=JSON.parse(L);S.run({event_id:E.eventId,fingerprint:E.fingerprint,timestamp:E.timestamp,level:E.level,fix_prompt:E.fixPrompt??"",payload:L}),i++;}catch{}}}}catch{}return b.run("migrated","true"),{issues:a,events:i}},close(){t.close();}}}exports.a=H;//# sourceMappingURL=chunk-VQXSHR3C.js.map
|
|
59
|
+
//# sourceMappingURL=chunk-VQXSHR3C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sqlite-store.ts"],"names":["SCHEMA_SQL","MIGRATION_SQL","openStore","dbPath","db","Database","stmt","stmtUpsertIssue","stmtGetIssues","stmtGetIssuesByStatus","stmtGetIssuesByEnv","stmtGetIssuesByStatusAndEnv","stmtGetIssue","stmtUpdateStatus","stmtDeleteAllIssues","stmtDeleteAllEvents","stmtInsertEvent","stmtGetEvents","stmtGetLatestEvent","stmtGetEventCount","stmtGetMeta","stmtSetMeta","stmtCountAll","stmtCountByStatus","stmtCountEvents","rowToIssue","row","issueToParams","entry","rowToEvent","insertEventAndUpsert","event","eventFile","promptFile","fp","userId","existingRow","existing","filter","rows","fingerprint","status","opts","limit","offset","baseDir","issueCount","eventCount","fsSync","N","pathMod","issuesPath","raw","issues","issue","eventsDir","fps","fpDir","files","file","eventRaw"],"mappings":"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","file":"chunk-VQXSHR3C.js","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — SQLite storage layer\n// ---------------------------------------------------------------------------\n\nimport Database from 'better-sqlite3';\nimport type { IssueEntry, IssueStatus, UncaughtEvent } from './types';\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS issues (\n fingerprint TEXT PRIMARY KEY,\n title TEXT NOT NULL,\n error_type TEXT NOT NULL,\n count INTEGER NOT NULL DEFAULT 1,\n affected_users TEXT NOT NULL DEFAULT '[]',\n first_seen TEXT NOT NULL,\n last_seen TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'open',\n fix_prompt_file TEXT NOT NULL DEFAULT '',\n latest_event_file TEXT NOT NULL DEFAULT '',\n release TEXT NOT NULL DEFAULT '',\n environment TEXT NOT NULL DEFAULT ''\n);\n\nCREATE INDEX IF NOT EXISTS idx_issues_status ON issues(status);\nCREATE INDEX IF NOT EXISTS idx_issues_last_seen ON issues(last_seen);\n\nCREATE TABLE IF NOT EXISTS events (\n event_id TEXT PRIMARY KEY,\n fingerprint TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n level TEXT NOT NULL DEFAULT 'error',\n fix_prompt TEXT NOT NULL DEFAULT '',\n payload TEXT NOT NULL,\n FOREIGN KEY (fingerprint) REFERENCES issues(fingerprint)\n);\n\nCREATE INDEX IF NOT EXISTS idx_events_fingerprint ON events(fingerprint);\nCREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);\n\nCREATE TABLE IF NOT EXISTS _meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n`;\n\n// Migrate existing databases that lack new columns\nconst MIGRATION_SQL = `\nALTER TABLE issues ADD COLUMN release TEXT NOT NULL DEFAULT '';\nALTER TABLE issues ADD COLUMN environment TEXT NOT NULL DEFAULT '';\n`;\n\n// ---------------------------------------------------------------------------\n// Public interface\n// ---------------------------------------------------------------------------\n\nexport interface SqliteStore {\n upsertIssue(entry: IssueEntry): void;\n getIssues(filter?: { status?: IssueStatus; environment?: string }): IssueEntry[];\n getIssue(fingerprint: string): IssueEntry | undefined;\n updateIssueStatus(fingerprint: string, status: IssueStatus): void;\n deleteAllIssues(): void;\n\n insertEvent(event: UncaughtEvent): void;\n getEvents(fingerprint: string, opts?: { limit?: number; offset?: number }): UncaughtEvent[];\n getLatestEvent(fingerprint: string): UncaughtEvent | undefined;\n getEventCount(fingerprint: string): number;\n\n getStats(): { total: number; open: number; resolved: number; ignored: number; totalEvents: number };\n\n importFromFiles(baseDir: string): { issues: number; events: number };\n\n close(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport function openStore(dbPath: string): SqliteStore {\n const db = new Database(dbPath);\n db.pragma('journal_mode = WAL');\n db.pragma('foreign_keys = ON');\n db.exec(SCHEMA_SQL);\n\n // Run migrations for existing databases\n try {\n for (const stmt of MIGRATION_SQL.trim().split(';').filter(Boolean)) {\n try { db.exec(stmt + ';'); } catch { /* column already exists */ }\n }\n } catch { /* migration best-effort */ }\n\n // Prepared statements\n const stmtUpsertIssue = db.prepare(`\n INSERT INTO issues (fingerprint, title, error_type, count, affected_users, first_seen, last_seen, status, fix_prompt_file, latest_event_file, release, environment)\n VALUES (@fingerprint, @title, @error_type, @count, @affected_users, @first_seen, @last_seen, @status, @fix_prompt_file, @latest_event_file, @release, @environment)\n ON CONFLICT(fingerprint) DO UPDATE SET\n title = @title,\n error_type = @error_type,\n count = @count,\n affected_users = @affected_users,\n first_seen = @first_seen,\n last_seen = @last_seen,\n status = @status,\n fix_prompt_file = @fix_prompt_file,\n latest_event_file = @latest_event_file,\n release = @release,\n environment = @environment\n `);\n\n const stmtGetIssues = db.prepare('SELECT * FROM issues ORDER BY last_seen DESC');\n const stmtGetIssuesByStatus = db.prepare('SELECT * FROM issues WHERE status = ? ORDER BY last_seen DESC');\n const stmtGetIssuesByEnv = db.prepare('SELECT * FROM issues WHERE environment = ? ORDER BY last_seen DESC');\n const stmtGetIssuesByStatusAndEnv = db.prepare('SELECT * FROM issues WHERE status = ? AND environment = ? ORDER BY last_seen DESC');\n const stmtGetIssue = db.prepare('SELECT * FROM issues WHERE fingerprint = ?');\n const stmtUpdateStatus = db.prepare('UPDATE issues SET status = ? WHERE fingerprint = ?');\n const stmtDeleteAllIssues = db.prepare('DELETE FROM issues');\n const stmtDeleteAllEvents = db.prepare('DELETE FROM events');\n\n const stmtInsertEvent = db.prepare(`\n INSERT OR IGNORE INTO events (event_id, fingerprint, timestamp, level, fix_prompt, payload)\n VALUES (@event_id, @fingerprint, @timestamp, @level, @fix_prompt, @payload)\n `);\n\n const stmtGetEvents = db.prepare('SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT ? OFFSET ?');\n const stmtGetLatestEvent = db.prepare('SELECT * FROM events WHERE fingerprint = ? ORDER BY timestamp DESC LIMIT 1');\n const stmtGetEventCount = db.prepare('SELECT COUNT(*) as cnt FROM events WHERE fingerprint = ?');\n\n const stmtGetMeta = db.prepare('SELECT value FROM _meta WHERE key = ?');\n const stmtSetMeta = db.prepare('INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)');\n\n const stmtCountAll = db.prepare('SELECT COUNT(*) as cnt FROM issues');\n const stmtCountByStatus = db.prepare('SELECT COUNT(*) as cnt FROM issues WHERE status = ?');\n const stmtCountEvents = db.prepare('SELECT COUNT(*) as cnt FROM events');\n\n // Row → IssueEntry mapper\n function rowToIssue(row: Record<string, unknown>): IssueEntry {\n return {\n fingerprint: row.fingerprint as string,\n title: row.title as string,\n errorType: row.error_type as string,\n count: row.count as number,\n affectedUsers: JSON.parse(row.affected_users as string),\n firstSeen: row.first_seen as string,\n lastSeen: row.last_seen as string,\n status: row.status as IssueStatus,\n fixPromptFile: row.fix_prompt_file as string,\n latestEventFile: row.latest_event_file as string,\n release: (row.release as string) || undefined,\n environment: (row.environment as string) || undefined,\n };\n }\n\n // IssueEntry → row params mapper\n function issueToParams(entry: IssueEntry) {\n return {\n fingerprint: entry.fingerprint,\n title: entry.title,\n error_type: entry.errorType,\n count: entry.count,\n affected_users: JSON.stringify(entry.affectedUsers),\n first_seen: entry.firstSeen,\n last_seen: entry.lastSeen,\n status: entry.status,\n fix_prompt_file: entry.fixPromptFile,\n latest_event_file: entry.latestEventFile,\n release: entry.release ?? '',\n environment: entry.environment ?? '',\n };\n }\n\n // Row → UncaughtEvent mapper\n function rowToEvent(row: Record<string, unknown>): UncaughtEvent {\n return JSON.parse(row.payload as string) as UncaughtEvent;\n }\n\n // Insert event + upsert issue atomically\n const insertEventAndUpsert = db.transaction((event: UncaughtEvent, eventFile: string, promptFile: string) => {\n const fp = event.fingerprint;\n const userId = event.user?.id ?? event.user?.email ?? 'anonymous';\n\n // Insert event row\n stmtInsertEvent.run({\n event_id: event.eventId,\n fingerprint: fp,\n timestamp: event.timestamp,\n level: event.level,\n fix_prompt: event.fixPrompt ?? '',\n payload: JSON.stringify(event),\n });\n\n // Upsert issue\n const existingRow = stmtGetIssue.get(fp) as Record<string, unknown> | undefined;\n if (existingRow) {\n const existing = rowToIssue(existingRow);\n existing.count += 1;\n existing.lastSeen = event.timestamp;\n existing.latestEventFile = eventFile;\n existing.fixPromptFile = promptFile;\n existing.release = event.release ?? existing.release;\n existing.environment = event.environment?.deploy ?? existing.environment;\n if (!existing.affectedUsers.includes(String(userId))) {\n existing.affectedUsers.push(String(userId));\n }\n if (existing.status === 'resolved') {\n existing.status = 'open';\n }\n stmtUpsertIssue.run(issueToParams(existing));\n } else {\n stmtUpsertIssue.run(issueToParams({\n fingerprint: fp,\n title: event.error?.message ?? 'Unknown error',\n errorType: event.error?.type ?? 'Error',\n count: 1,\n affectedUsers: [String(userId)],\n firstSeen: event.timestamp,\n lastSeen: event.timestamp,\n status: 'open',\n fixPromptFile: promptFile,\n latestEventFile: eventFile,\n release: event.release,\n environment: event.environment?.deploy,\n }));\n }\n });\n\n return {\n upsertIssue(entry: IssueEntry): void {\n stmtUpsertIssue.run(issueToParams(entry));\n },\n\n getIssues(filter?: { status?: IssueStatus; environment?: string }): IssueEntry[] {\n let rows: unknown[];\n if (filter?.status && filter?.environment) {\n rows = stmtGetIssuesByStatusAndEnv.all(filter.status, filter.environment);\n } else if (filter?.status) {\n rows = stmtGetIssuesByStatus.all(filter.status);\n } else if (filter?.environment) {\n rows = stmtGetIssuesByEnv.all(filter.environment);\n } else {\n rows = stmtGetIssues.all();\n }\n return (rows as Record<string, unknown>[]).map(rowToIssue);\n },\n\n getIssue(fingerprint: string): IssueEntry | undefined {\n const row = stmtGetIssue.get(fingerprint) as Record<string, unknown> | undefined;\n return row ? rowToIssue(row) : undefined;\n },\n\n updateIssueStatus(fingerprint: string, status: IssueStatus): void {\n stmtUpdateStatus.run(status, fingerprint);\n },\n\n deleteAllIssues(): void {\n stmtDeleteAllEvents.run();\n stmtDeleteAllIssues.run();\n },\n\n insertEvent(event: UncaughtEvent): void {\n const ts = (event.timestamp ?? new Date().toISOString()).replace(/[:.]/g, '-');\n const eventFile = `event-${ts}.json`;\n const promptFile = `${event.fingerprint}.md`;\n insertEventAndUpsert(event, eventFile, promptFile);\n },\n\n getEvents(fingerprint: string, opts?: { limit?: number; offset?: number }): UncaughtEvent[] {\n const limit = opts?.limit ?? 50;\n const offset = opts?.offset ?? 0;\n const rows = stmtGetEvents.all(fingerprint, limit, offset);\n return (rows as Record<string, unknown>[]).map(rowToEvent);\n },\n\n getLatestEvent(fingerprint: string): UncaughtEvent | undefined {\n const row = stmtGetLatestEvent.get(fingerprint) as Record<string, unknown> | undefined;\n return row ? rowToEvent(row) : undefined;\n },\n\n getEventCount(fingerprint: string): number {\n const row = stmtGetEventCount.get(fingerprint) as { cnt: number };\n return row.cnt;\n },\n\n getStats() {\n return {\n total: (stmtCountAll.get() as { cnt: number }).cnt,\n open: (stmtCountByStatus.get('open') as { cnt: number }).cnt,\n resolved: (stmtCountByStatus.get('resolved') as { cnt: number }).cnt,\n ignored: (stmtCountByStatus.get('ignored') as { cnt: number }).cnt,\n totalEvents: (stmtCountEvents.get() as { cnt: number }).cnt,\n };\n },\n\n importFromFiles(baseDir: string): { issues: number; events: number } {\n const meta = stmtGetMeta.get('migrated') as { value: string } | undefined;\n if (meta?.value === 'true') return { issues: 0, events: 0 };\n\n let issueCount = 0;\n let eventCount = 0;\n\n try {\n const fsSync = require('fs');\n const pathMod = require('path');\n\n // Import issues.json\n const issuesPath = pathMod.join(baseDir, 'issues.json');\n if (fsSync.existsSync(issuesPath)) {\n const raw = fsSync.readFileSync(issuesPath, 'utf-8');\n const issues = JSON.parse(raw) as IssueEntry[];\n for (const issue of issues) {\n stmtUpsertIssue.run(issueToParams(issue));\n issueCount++;\n }\n }\n\n // Import event files\n const eventsDir = pathMod.join(baseDir, 'events');\n if (fsSync.existsSync(eventsDir)) {\n const fps = fsSync.readdirSync(eventsDir);\n for (const fp of fps) {\n const fpDir = pathMod.join(eventsDir, fp);\n if (!fsSync.statSync(fpDir).isDirectory()) continue;\n\n const files = fsSync.readdirSync(fpDir).filter((f: string) => f.startsWith('event-') && f.endsWith('.json'));\n for (const file of files) {\n try {\n const eventRaw = fsSync.readFileSync(pathMod.join(fpDir, file), 'utf-8');\n const event = JSON.parse(eventRaw) as UncaughtEvent;\n stmtInsertEvent.run({\n event_id: event.eventId,\n fingerprint: event.fingerprint,\n timestamp: event.timestamp,\n level: event.level,\n fix_prompt: event.fixPrompt ?? '',\n payload: eventRaw,\n });\n eventCount++;\n } catch {\n // Skip malformed event files\n }\n }\n }\n }\n } catch {\n // Migration is best-effort\n }\n\n stmtSetMeta.run('migrated', 'true');\n return { issues: issueCount, events: eventCount };\n },\n\n close(): void {\n db.close();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';function f(r){let n=[];if(n.push(`I have a production bug in my application that I need help diagnosing and fixing.
|
|
2
|
+
`),r.error){let o=i(r.error.stack),t=["## Error",""];t.push(`- **Type:** ${r.error.type||"Error"}`),t.push(`- **Message:** ${r.error.message||"(no message)"}`),o&&t.push(`- **Location:** ${o}`),n.push(t.join(`
|
|
3
|
+
`));}let s=r.error?.resolvedStack??r.error?.stack;if(s){let o=s.split(`
|
|
4
|
+
`).slice(0,15).map(e=>e.trimEnd()).join(`
|
|
5
|
+
`),t=r.error?.resolvedStack?"Stack Trace (source-mapped)":"Stack Trace";n.push(`## ${t}
|
|
6
|
+
|
|
7
|
+
\`\`\`
|
|
8
|
+
${o}
|
|
9
|
+
\`\`\``);}return r.operation&&n.push(a(r.operation)),r.request&&n.push(u(r.request)),r.breadcrumbs&&r.breadcrumbs.length>0&&n.push(c(r.breadcrumbs)),r.environment&&n.push(m(r.environment)),r.error?.componentStack&&n.push(`## React Component Stack
|
|
10
|
+
|
|
11
|
+
\`\`\`
|
|
12
|
+
${r.error.componentStack.trim()}
|
|
13
|
+
\`\`\``),n.push(["## What I need","","1. **Root cause analysis** \u2014 explain why this error is occurring.","2. **A fix** \u2014 provide the corrected code with an explanation of the changes.","3. **Prevention** \u2014 suggest any guards or tests to prevent this from happening again."].join(`
|
|
14
|
+
`)),n.join(`
|
|
15
|
+
|
|
16
|
+
`)+`
|
|
17
|
+
`}function i(r){if(r)for(let n of r.split(`
|
|
18
|
+
`)){let s=n.trim(),o=s.match(/at\s+(?:.+?\s+\()?(.+?:\d+:\d+)\)?/);if(o)return o[1];let t=s.match(/@(.+?:\d+:\d+)/);if(t)return t[1]}}function a(r){let n=["## Failed Operation",""];return n.push(`- **Provider:** ${r.provider}`),n.push(`- **Type:** ${r.type}`),n.push(`- **Method:** ${r.method}`),r.params&&(n.push("- **Params:**"),n.push("```json"),n.push(JSON.stringify(r.params,null,2)),n.push("```")),r.errorCode&&n.push(`- **Error Code:** ${r.errorCode}`),r.errorDetails&&n.push(`- **Error Details:** ${r.errorDetails}`),n.join(`
|
|
19
|
+
`)}function u(r){let n=["## HTTP Request Context",""];return r.method&&n.push(`- **Method:** ${r.method}`),r.url&&n.push(`- **URL:** ${r.url}`),r.body&&(n.push("- **Body:**"),n.push("```json"),n.push(typeof r.body=="string"?r.body:JSON.stringify(r.body,null,2)),n.push("```")),n.join(`
|
|
20
|
+
`)}function c(r){let n=r.slice(-5),s=["## User Session",""];for(let o of n){let t=p(o.timestamp);s.push(`- \`${t}\` **[${o.type}]** ${o.message}`);}return s.join(`
|
|
21
|
+
`)}function p(r){try{let n=new Date(r),s=String(n.getHours()).padStart(2,"0"),o=String(n.getMinutes()).padStart(2,"0"),t=String(n.getSeconds()).padStart(2,"0");return `${s}:${o}:${t}`}catch{return r}}function m(r){let n=["## Environment",""],s=[["Deploy Environment",r.deploy],["Framework",r.framework],["Framework Version",r.frameworkVersion],["Runtime",r.runtime],["Runtime Version",r.runtimeVersion],["Platform",r.platform],["Browser",r.browser?`${r.browser} ${r.browserVersion??""}`.trim():void 0],["OS",r.os],["Device",r.deviceType],["Locale",r.locale],["Timezone",r.timezone],["URL",r.url]];for(let[o,t]of s)t&&n.push(`- **${o}:** ${t}`);return n.join(`
|
|
22
|
+
`)}exports.a=f;//# sourceMappingURL=chunk-WZBG5VLB.js.map
|
|
23
|
+
//# sourceMappingURL=chunk-WZBG5VLB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/prompt-builder.ts"],"names":["buildFixPrompt","event","sections","location","extractLocation","lines","stackSource","frames","l","label","formatOperation","formatRequest","formatBreadcrumbs","formatEnvironment","stack","line","trimmed","v8","sm","op","req","crumbs","recent","crumb","time","formatTime","iso","d","h","m","s","env","entries","value"],"mappings":"aAYO,SAASA,CAAAA,CAAeC,EAAuC,CACpE,IAAMC,EAAqB,EAAC,CAQ5B,GALAA,CAAAA,CAAS,IAAA,CACP,CAAA;AAAA,CACF,CAAA,CAGID,EAAM,KAAA,CAAO,CACf,IAAME,CAAAA,CAAWC,CAAAA,CAAgBH,EAAM,KAAA,CAAM,KAAK,EAC5CI,CAAAA,CAAkB,CAAC,WAAY,EAAE,CAAA,CACvCA,EAAM,IAAA,CAAK,CAAA,YAAA,EAAeJ,EAAM,KAAA,CAAM,IAAA,EAAQ,OAAO,CAAA,CAAE,CAAA,CACvDI,EAAM,IAAA,CAAK,CAAA,eAAA,EAAkBJ,EAAM,KAAA,CAAM,OAAA,EAAW,cAAc,CAAA,CAAE,CAAA,CAChEE,GACFE,CAAAA,CAAM,IAAA,CAAK,mBAAmBF,CAAQ,CAAA,CAAE,EAE1CD,CAAAA,CAAS,IAAA,CAAKG,EAAM,IAAA,CAAK;AAAA,CAAI,CAAC,EAChC,CAGA,IAAMC,CAAAA,CAAcL,EAAM,KAAA,EAAO,aAAA,EAAiBA,CAAAA,CAAM,KAAA,EAAO,MAC/D,GAAIK,CAAAA,CAAa,CACf,IAAMC,CAAAA,CAASD,EACZ,KAAA,CAAM;AAAA,CAAI,CAAA,CACV,KAAA,CAAM,CAAA,CAAG,EAAE,CAAA,CACX,GAAA,CAAKE,CAAAA,EAAMA,CAAAA,CAAE,OAAA,EAAS,CAAA,CACtB,IAAA,CAAK;AAAA,CAAI,CAAA,CACNC,CAAAA,CAAQR,CAAAA,CAAM,KAAA,EAAO,aAAA,CAAgB,8BAAgC,aAAA,CAC3EC,CAAAA,CAAS,IAAA,CAAK,CAAA,GAAA,EAAMO,CAAK;;AAAA;AAAA,EAAeF,CAAM;AAAA,MAAA,CAAU,EAC1D,CAGA,OAAIN,CAAAA,CAAM,WACRC,CAAAA,CAAS,IAAA,CAAKQ,EAAgBT,CAAAA,CAAM,SAAS,CAAC,CAAA,CAI5CA,CAAAA,CAAM,SACRC,CAAAA,CAAS,IAAA,CAAKS,EAAcV,CAAAA,CAAM,OAAO,CAAC,CAAA,CAIxCA,CAAAA,CAAM,WAAA,EAAeA,EAAM,WAAA,CAAY,MAAA,CAAS,GAClDC,CAAAA,CAAS,IAAA,CAAKU,EAAkBX,CAAAA,CAAM,WAAW,CAAC,CAAA,CAIhDA,CAAAA,CAAM,WAAA,EACRC,EAAS,IAAA,CAAKW,CAAAA,CAAkBZ,EAAM,WAAW,CAAC,EAIhDA,CAAAA,CAAM,KAAA,EAAO,cAAA,EACfC,CAAAA,CAAS,IAAA,CACP,CAAA;;AAAA;AAAA,EAAuCD,CAAAA,CAAM,KAAA,CAAM,cAAA,CAAe,IAAA,EAAM;AAAA,MAAA,CAC1E,CAAA,CAIFC,CAAAA,CAAS,IAAA,CACP,CACE,gBAAA,CACA,GACA,wEAAA,CACA,oFAAA,CACA,4FACF,CAAA,CAAE,IAAA,CAAK;AAAA,CAAI,CACb,CAAA,CAEOA,CAAAA,CAAS,IAAA,CAAK;;AAAA,CAAM,CAAA,CAAI;AAAA,CACjC,CASA,SAASE,CAAAA,CAAgBU,CAAAA,CAAoC,CAC3D,GAAKA,CAAAA,CAEL,IAAA,IAAWC,CAAAA,IAAQD,CAAAA,CAAM,KAAA,CAAM;AAAA,CAAI,CAAA,CAAG,CACpC,IAAME,CAAAA,CAAUD,EAAK,IAAA,EAAK,CAGpBE,CAAAA,CAAKD,CAAAA,CAAQ,KAAA,CAAM,oCAAoC,EAC7D,GAAIC,CAAAA,CAAI,OAAOA,CAAAA,CAAG,CAAC,CAAA,CAGnB,IAAMC,CAAAA,CAAKF,CAAAA,CAAQ,KAAA,CAAM,gBAAgB,CAAA,CACzC,GAAIE,EAAI,OAAOA,CAAAA,CAAG,CAAC,CACrB,CAGF,CAEA,SAASR,CAAAA,CAAgBS,CAAAA,CAA2B,CAClD,IAAMd,CAAAA,CAAkB,CAAC,sBAAuB,EAAE,CAAA,CAClD,OAAAA,CAAAA,CAAM,IAAA,CAAK,CAAA,gBAAA,EAAmBc,EAAG,QAAQ,CAAA,CAAE,CAAA,CAC3Cd,CAAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAec,EAAG,IAAI,CAAA,CAAE,CAAA,CACnCd,CAAAA,CAAM,IAAA,CAAK,CAAA,cAAA,EAAiBc,EAAG,MAAM,CAAA,CAAE,CAAA,CACnCA,CAAAA,CAAG,MAAA,GACLd,CAAAA,CAAM,KAAK,eAAe,CAAA,CAC1BA,CAAAA,CAAM,IAAA,CAAK,SAAS,CAAA,CACpBA,EAAM,IAAA,CAAK,IAAA,CAAK,SAAA,CAAUc,CAAAA,CAAG,MAAA,CAAQ,IAAA,CAAM,CAAC,CAAC,CAAA,CAC7Cd,CAAAA,CAAM,IAAA,CAAK,KAAK,CAAA,CAAA,CAEdc,EAAG,SAAA,EACLd,CAAAA,CAAM,IAAA,CAAK,CAAA,kBAAA,EAAqBc,CAAAA,CAAG,SAAS,EAAE,CAAA,CAE5CA,CAAAA,CAAG,YAAA,EACLd,CAAAA,CAAM,IAAA,CAAK,CAAA,qBAAA,EAAwBc,EAAG,YAAY,CAAA,CAAE,CAAA,CAE/Cd,CAAAA,CAAM,IAAA,CAAK;AAAA,CAAI,CACxB,CAEA,SAASM,CAAAA,CAAcS,CAAAA,CAA0B,CAC/C,IAAMf,CAAAA,CAAkB,CAAC,yBAAA,CAA2B,EAAE,CAAA,CACtD,OAAIe,CAAAA,CAAI,MAAA,EAAQf,CAAAA,CAAM,IAAA,CAAK,CAAA,cAAA,EAAiBe,CAAAA,CAAI,MAAM,CAAA,CAAE,CAAA,CACpDA,CAAAA,CAAI,GAAA,EAAKf,CAAAA,CAAM,IAAA,CAAK,CAAA,WAAA,EAAce,CAAAA,CAAI,GAAG,CAAA,CAAE,CAAA,CAC3CA,CAAAA,CAAI,IAAA,GACNf,CAAAA,CAAM,IAAA,CAAK,aAAa,CAAA,CACxBA,CAAAA,CAAM,IAAA,CAAK,SAAS,CAAA,CACpBA,CAAAA,CAAM,IAAA,CACJ,OAAOe,CAAAA,CAAI,IAAA,EAAS,QAAA,CAChBA,CAAAA,CAAI,IAAA,CACJ,IAAA,CAAK,SAAA,CAAUA,CAAAA,CAAI,IAAA,CAAM,IAAA,CAAM,CAAC,CACtC,CAAA,CACAf,CAAAA,CAAM,IAAA,CAAK,KAAK,CAAA,CAAA,CAEXA,EAAM,IAAA,CAAK;AAAA,CAAI,CACxB,CAEA,SAASO,EAAkBS,CAAAA,CAA8B,CAEvD,IAAMC,CAAAA,CAASD,CAAAA,CAAO,MAAM,EAAE,CAAA,CACxBhB,EAAkB,CAAC,iBAAA,CAAmB,EAAE,CAAA,CAE9C,IAAA,IAAWkB,KAASD,CAAAA,CAAQ,CAC1B,IAAME,CAAAA,CAAOC,EAAWF,CAAAA,CAAM,SAAS,EACvClB,CAAAA,CAAM,IAAA,CAAK,OAAOmB,CAAI,CAAA,MAAA,EAASD,EAAM,IAAI,CAAA,IAAA,EAAOA,EAAM,OAAO,CAAA,CAAE,EACjE,CAEA,OAAOlB,EAAM,IAAA,CAAK;AAAA,CAAI,CACxB,CAKA,SAASoB,CAAAA,CAAWC,CAAAA,CAAqB,CACvC,GAAI,CACF,IAAMC,CAAAA,CAAI,IAAI,IAAA,CAAKD,CAAG,CAAA,CAChBE,CAAAA,CAAI,OAAOD,CAAAA,CAAE,QAAA,EAAU,CAAA,CAAE,SAAS,CAAA,CAAG,GAAG,CAAA,CACxCE,CAAAA,CAAI,OAAOF,CAAAA,CAAE,UAAA,EAAY,CAAA,CAAE,SAAS,CAAA,CAAG,GAAG,CAAA,CAC1CG,CAAAA,CAAI,OAAOH,CAAAA,CAAE,UAAA,EAAY,CAAA,CAAE,SAAS,CAAA,CAAG,GAAG,CAAA,CAChD,OAAO,GAAGC,CAAC,CAAA,CAAA,EAAIC,CAAC,CAAA,CAAA,EAAIC,CAAC,CAAA,CACvB,CAAA,KAAQ,CACN,OAAOJ,CACT,CACF,CAEA,SAASb,CAAAA,CAAkBkB,EAA8B,CACvD,IAAM1B,CAAAA,CAAkB,CAAC,iBAAkB,EAAE,CAAA,CACvC2B,CAAAA,CAA+C,CACnD,CAAC,oBAAA,CAAsBD,CAAAA,CAAI,MAAM,CAAA,CACjC,CAAC,YAAaA,CAAAA,CAAI,SAAS,CAAA,CAC3B,CAAC,oBAAqBA,CAAAA,CAAI,gBAAgB,CAAA,CAC1C,CAAC,UAAWA,CAAAA,CAAI,OAAO,CAAA,CACvB,CAAC,kBAAmBA,CAAAA,CAAI,cAAc,CAAA,CACtC,CAAC,WAAYA,CAAAA,CAAI,QAAQ,CAAA,CACzB,CAAC,UAAWA,CAAAA,CAAI,OAAA,CAAU,CAAA,EAAGA,CAAAA,CAAI,OAAO,CAAA,CAAA,EAAIA,CAAAA,CAAI,cAAA,EAAkB,EAAE,GAAG,IAAA,EAAK,CAAI,MAAS,CAAA,CACzF,CAAC,KAAMA,CAAAA,CAAI,EAAE,CAAA,CACb,CAAC,SAAUA,CAAAA,CAAI,UAAU,CAAA,CACzB,CAAC,SAAUA,CAAAA,CAAI,MAAM,CAAA,CACrB,CAAC,WAAYA,CAAAA,CAAI,QAAQ,CAAA,CACzB,CAAC,MAAOA,CAAAA,CAAI,GAAG,CACjB,CAAA,CAEA,OAAW,CAACtB,CAAAA,CAAOwB,CAAK,CAAA,GAAKD,EACvBC,CAAAA,EACF5B,CAAAA,CAAM,IAAA,CAAK,CAAA,IAAA,EAAOI,CAAK,CAAA,IAAA,EAAOwB,CAAK,EAAE,CAAA,CAIzC,OAAO5B,EAAM,IAAA,CAAK;AAAA,CAAI,CACxB","file":"chunk-WZBG5VLB.js","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — fix-prompt builder\n// ---------------------------------------------------------------------------\n\nimport type { UncaughtEvent, Breadcrumb, EnvironmentInfo, OperationInfo, RequestInfo } from './types';\n\n/**\n * Build a structured Markdown prompt that can be pasted into an AI assistant\n * to diagnose and fix the production error described by `event`.\n *\n * Empty sections are omitted to keep the prompt concise.\n */\nexport function buildFixPrompt(event: Partial<UncaughtEvent>): string {\n const sections: string[] = [];\n\n // ----- Intro -------------------------------------------------------------\n sections.push(\n 'I have a production bug in my application that I need help diagnosing and fixing.\\n'\n );\n\n // ----- Error -------------------------------------------------------------\n if (event.error) {\n const location = extractLocation(event.error.stack);\n const lines: string[] = ['## Error', ''];\n lines.push(`- **Type:** ${event.error.type || 'Error'}`);\n lines.push(`- **Message:** ${event.error.message || '(no message)'}`);\n if (location) {\n lines.push(`- **Location:** ${location}`);\n }\n sections.push(lines.join('\\n'));\n }\n\n // ----- Stack Trace -------------------------------------------------------\n const stackSource = event.error?.resolvedStack ?? event.error?.stack;\n if (stackSource) {\n const frames = stackSource\n .split('\\n')\n .slice(0, 15)\n .map((l) => l.trimEnd())\n .join('\\n');\n const label = event.error?.resolvedStack ? 'Stack Trace (source-mapped)' : 'Stack Trace';\n sections.push(`## ${label}\\n\\n\\`\\`\\`\\n${frames}\\n\\`\\`\\``);\n }\n\n // ----- Failed Operation --------------------------------------------------\n if (event.operation) {\n sections.push(formatOperation(event.operation));\n }\n\n // ----- HTTP Request Context ----------------------------------------------\n if (event.request) {\n sections.push(formatRequest(event.request));\n }\n\n // ----- User Session (last 5 breadcrumbs) ---------------------------------\n if (event.breadcrumbs && event.breadcrumbs.length > 0) {\n sections.push(formatBreadcrumbs(event.breadcrumbs));\n }\n\n // ----- Environment -------------------------------------------------------\n if (event.environment) {\n sections.push(formatEnvironment(event.environment));\n }\n\n // ----- React Component Stack ---------------------------------------------\n if (event.error?.componentStack) {\n sections.push(\n `## React Component Stack\\n\\n\\`\\`\\`\\n${event.error.componentStack.trim()}\\n\\`\\`\\``\n );\n }\n\n // ----- What I need -------------------------------------------------------\n sections.push(\n [\n '## What I need',\n '',\n '1. **Root cause analysis** — explain why this error is occurring.',\n '2. **A fix** — provide the corrected code with an explanation of the changes.',\n '3. **Prevention** — suggest any guards or tests to prevent this from happening again.',\n ].join('\\n')\n );\n\n return sections.join('\\n\\n') + '\\n';\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the top-most location (file:line:col) from a stack trace string.\n */\nfunction extractLocation(stack?: string): string | undefined {\n if (!stack) return undefined;\n\n for (const line of stack.split('\\n')) {\n const trimmed = line.trim();\n\n // V8: \" at fn (file:line:col)\"\n const v8 = trimmed.match(/at\\s+(?:.+?\\s+\\()?(.+?:\\d+:\\d+)\\)?/);\n if (v8) return v8[1];\n\n // SpiderMonkey / JSC: \"fn@file:line:col\"\n const sm = trimmed.match(/@(.+?:\\d+:\\d+)/);\n if (sm) return sm[1];\n }\n\n return undefined;\n}\n\nfunction formatOperation(op: OperationInfo): string {\n const lines: string[] = ['## Failed Operation', ''];\n lines.push(`- **Provider:** ${op.provider}`);\n lines.push(`- **Type:** ${op.type}`);\n lines.push(`- **Method:** ${op.method}`);\n if (op.params) {\n lines.push(`- **Params:**`);\n lines.push('```json');\n lines.push(JSON.stringify(op.params, null, 2));\n lines.push('```');\n }\n if (op.errorCode) {\n lines.push(`- **Error Code:** ${op.errorCode}`);\n }\n if (op.errorDetails) {\n lines.push(`- **Error Details:** ${op.errorDetails}`);\n }\n return lines.join('\\n');\n}\n\nfunction formatRequest(req: RequestInfo): string {\n const lines: string[] = ['## HTTP Request Context', ''];\n if (req.method) lines.push(`- **Method:** ${req.method}`);\n if (req.url) lines.push(`- **URL:** ${req.url}`);\n if (req.body) {\n lines.push(`- **Body:**`);\n lines.push('```json');\n lines.push(\n typeof req.body === 'string'\n ? req.body\n : JSON.stringify(req.body, null, 2)\n );\n lines.push('```');\n }\n return lines.join('\\n');\n}\n\nfunction formatBreadcrumbs(crumbs: Breadcrumb[]): string {\n // Take the last 5 breadcrumbs\n const recent = crumbs.slice(-5);\n const lines: string[] = ['## User Session', ''];\n\n for (const crumb of recent) {\n const time = formatTime(crumb.timestamp);\n lines.push(`- \\`${time}\\` **[${crumb.type}]** ${crumb.message}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Extract HH:MM:SS from an ISO timestamp.\n */\nfunction formatTime(iso: string): string {\n try {\n const d = new Date(iso);\n const h = String(d.getHours()).padStart(2, '0');\n const m = String(d.getMinutes()).padStart(2, '0');\n const s = String(d.getSeconds()).padStart(2, '0');\n return `${h}:${m}:${s}`;\n } catch {\n return iso;\n }\n}\n\nfunction formatEnvironment(env: EnvironmentInfo): string {\n const lines: string[] = ['## Environment', ''];\n const entries: Array<[string, string | undefined]> = [\n ['Deploy Environment', env.deploy],\n ['Framework', env.framework],\n ['Framework Version', env.frameworkVersion],\n ['Runtime', env.runtime],\n ['Runtime Version', env.runtimeVersion],\n ['Platform', env.platform],\n ['Browser', env.browser ? `${env.browser} ${env.browserVersion ?? ''}`.trim() : undefined],\n ['OS', env.os],\n ['Device', env.deviceType],\n ['Locale', env.locale],\n ['Timezone', env.timezone],\n ['URL', env.url],\n ];\n\n for (const [label, value] of entries) {\n if (value) {\n lines.push(`- **${label}:** ${value}`);\n }\n }\n\n return lines.join('\\n');\n}\n"]}
|