@uncaughtdev/core 0.1.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/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/chunk-E76GW6KF.js +2 -0
- package/dist/chunk-E76GW6KF.js.map +1 -0
- package/dist/chunk-FFHQ452Q.js +2 -0
- package/dist/chunk-FFHQ452Q.js.map +1 -0
- package/dist/chunk-JALIO2BZ.mjs +2 -0
- package/dist/chunk-JALIO2BZ.mjs.map +1 -0
- package/dist/chunk-P6JRN5CN.mjs +2 -0
- package/dist/chunk-P6JRN5CN.mjs.map +1 -0
- package/dist/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +29 -0
- package/dist/index.mjs.map +1 -0
- package/dist/local-api-handler-pages.d.mts +29 -0
- package/dist/local-api-handler-pages.d.ts +29 -0
- package/dist/local-api-handler-pages.js +2 -0
- package/dist/local-api-handler-pages.js.map +1 -0
- package/dist/local-api-handler-pages.mjs +2 -0
- package/dist/local-api-handler-pages.mjs.map +1 -0
- package/dist/local-api-handler.d.mts +12 -0
- package/dist/local-api-handler.d.ts +12 -0
- package/dist/local-api-handler.js +2 -0
- package/dist/local-api-handler.js.map +1 -0
- package/dist/local-api-handler.mjs +2 -0
- package/dist/local-api-handler.mjs.map +1 -0
- package/dist/local-viewer.d.mts +1 -0
- package/dist/local-viewer.d.ts +1 -0
- package/dist/local-viewer.js +96 -0
- package/dist/local-viewer.js.map +1 -0
- package/dist/local-viewer.mjs +96 -0
- package/dist/local-viewer.mjs.map +1 -0
- package/dist/types-CjgYXVc_.d.mts +164 -0
- package/dist/types-CjgYXVc_.d.ts +164 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Uncaught Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @uncaughtdev/core
|
|
2
|
+
|
|
3
|
+
Core engine for [Uncaught](https://github.com/AjeeshDevops/uncaught) error monitoring.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx uncaught init
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or manually:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @uncaughtdev/core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What's included
|
|
18
|
+
|
|
19
|
+
- Error capture with fingerprinting and deduplication
|
|
20
|
+
- Ring buffer breadcrumb store
|
|
21
|
+
- PII sanitization
|
|
22
|
+
- AI-ready fix prompt generation
|
|
23
|
+
- Transport layer (console, local file, remote)
|
|
24
|
+
- CLI viewer (`npx uncaught`)
|
|
25
|
+
- Auto-setup command (`npx uncaught init`)
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';function c(){return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let r=Math.random()*16|0;return (t==="x"?r:r&3|8).toString(16)})}function g(t,r=10){let e=new WeakSet;function i(n,x){if(n==null||typeof n=="boolean"||typeof n=="number"||typeof n=="string")return n;if(typeof n=="bigint")return n.toString();if(!(typeof n=="function"||typeof n=="symbol")){if(x>r)return "[Max Depth]";if(n instanceof Date)return n.toISOString();if(typeof n=="object"){if(e.has(n))return "[Circular]";e.add(n);let f;if(Array.isArray(n))f=n.map(o=>i(o,x+1));else {let o={};for(let s of Object.keys(n))o[s]=i(n[s],x+1);f=o;}return e.delete(n),f}return n}}try{let n=i(t,0);return JSON.stringify(n)}catch{return '"[Unserializable]"'}}function u(){return new Date().toISOString()}function y(t,r=200){return t.length<=r?t:t.slice(0,r-3)+"..."}exports.a=c;exports.b=g;exports.c=u;exports.d=y;//# sourceMappingURL=chunk-E76GW6KF.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-E76GW6KF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"names":["generateUUID","c","safeStringify","obj","maxDepth","seen","walk","value","depth","result","item","key","safe","isoTimestamp","truncate","str","maxLen"],"mappings":"aAQO,SAASA,CAAAA,EAAuB,CAErC,OAAO,sCAAA,CAAuC,QAAQ,OAAA,CAAUC,CAAAA,EAAM,CACpE,IAAM,CAAA,CAAK,KAAK,MAAA,EAAO,CAAI,GAAM,CAAA,CAEjC,OAAA,CADUA,IAAM,GAAA,CAAM,CAAA,CAAK,CAAA,CAAI,CAAA,CAAO,CAAA,EAC7B,QAAA,CAAS,EAAE,CACtB,CAAC,CACH,CASO,SAASC,EAAcC,CAAAA,CAAcC,CAAAA,CAAmB,GAAY,CACzE,IAAMC,EAAO,IAAI,OAAA,CAEjB,SAASC,CAAAA,CAAKC,CAAAA,CAAgBC,EAAwB,CAIpD,GAFID,CAAAA,EAAU,IAAA,EACV,OAAOA,CAAAA,EAAU,WAAa,OAAOA,CAAAA,EAAU,UAC/C,OAAOA,CAAAA,EAAU,SAAU,OAAOA,CAAAA,CACtC,GAAI,OAAOA,CAAAA,EAAU,SAAU,OAAOA,CAAAA,CAAM,UAAS,CACrD,GAAI,SAAOA,CAAAA,EAAU,UAAA,EAAc,OAAOA,CAAAA,EAAU,QAAA,CAAA,CAGpD,CAAA,GAAIC,EAAQJ,CAAAA,CAAU,OAAO,cAG7B,GAAIG,CAAAA,YAAiB,KAAM,OAAOA,CAAAA,CAAM,aAAY,CAGpD,GAAI,OAAOA,CAAAA,EAAU,QAAA,CAAU,CAC7B,GAAIF,CAAAA,CAAK,IAAIE,CAAe,CAAA,CAAG,OAAO,YAAA,CACtCF,CAAAA,CAAK,GAAA,CAAIE,CAAe,CAAA,CAExB,IAAIE,EACJ,GAAI,KAAA,CAAM,QAAQF,CAAK,CAAA,CACrBE,EAASF,CAAAA,CAAM,GAAA,CAAKG,GAASJ,CAAAA,CAAKI,CAAAA,CAAMF,EAAQ,CAAC,CAAC,OAC7C,CACL,IAAML,CAAAA,CAA+B,EAAC,CACtC,IAAA,IAAWQ,KAAO,MAAA,CAAO,IAAA,CAAKJ,CAAgC,CAAA,CAC5DJ,CAAAA,CAAIQ,CAAG,CAAA,CAAIL,CAAAA,CAAMC,EAAkCI,CAAG,CAAA,CAAGH,EAAQ,CAAC,CAAA,CAEpEC,EAASN,EACX,CAEA,OAAAE,CAAAA,CAAK,MAAA,CAAOE,CAAe,CAAA,CACpBE,CACT,CAEA,OAAOF,CAAAA,CACT,CAEA,GAAI,CACF,IAAMK,EAAON,CAAAA,CAAKH,CAAAA,CAAK,CAAC,CAAA,CACxB,OAAO,KAAK,SAAA,CAAUS,CAAI,CAC5B,CAAA,KAAQ,CACN,OAAO,oBACT,CACF,CAKO,SAASC,CAAAA,EAAuB,CACrC,OAAO,IAAI,IAAA,GAAO,WAAA,EACpB,CAKO,SAASC,CAAAA,CAASC,CAAAA,CAAaC,CAAAA,CAAiB,GAAA,CAAa,CAClE,OAAID,CAAAA,CAAI,MAAA,EAAUC,EACTD,CAAAA,CAEFA,CAAAA,CAAI,MAAM,CAAA,CAAGC,CAAAA,CAAS,CAAC,CAAA,CAAI,KACpC","file":"chunk-E76GW6KF.js","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — utility helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a UUID v4 string using `Math.random`.\n * Not cryptographically secure — suitable for event IDs.\n */\nexport function generateUUID(): string {\n // RFC 4122 version 4 UUID template\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Safely serialise a value to JSON, handling circular references.\n *\n * @param obj - The value to serialise.\n * @param maxDepth - Maximum nesting depth before values are replaced with\n * `\"[Max Depth]\"`. Defaults to 10.\n */\nexport function safeStringify(obj: unknown, maxDepth: number = 10): string {\n const seen = new WeakSet();\n\n function walk(value: unknown, depth: number): unknown {\n // Primitives pass through\n if (value === null || value === undefined) return value;\n if (typeof value === 'boolean' || typeof value === 'number') return value;\n if (typeof value === 'string') return value;\n if (typeof value === 'bigint') return value.toString();\n if (typeof value === 'function' || typeof value === 'symbol') return undefined;\n\n // Depth guard\n if (depth > maxDepth) return '[Max Depth]';\n\n // Dates → ISO string\n if (value instanceof Date) return value.toISOString();\n\n // Objects and arrays — circular reference detection\n if (typeof value === 'object') {\n if (seen.has(value as object)) return '[Circular]';\n seen.add(value as object);\n\n let result: unknown;\n if (Array.isArray(value)) {\n result = value.map((item) => walk(item, depth + 1));\n } else {\n const obj: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n obj[key] = walk((value as Record<string, unknown>)[key], depth + 1);\n }\n result = obj;\n }\n\n seen.delete(value as object);\n return result;\n }\n\n return value;\n }\n\n try {\n const safe = walk(obj, 0);\n return JSON.stringify(safe);\n } catch {\n return '\"[Unserializable]\"';\n }\n}\n\n/**\n * Return the current date/time as an ISO 8601 string.\n */\nexport function isoTimestamp(): string {\n return new Date().toISOString();\n}\n\n/**\n * Truncate a string to `maxLen` characters, appending \"...\" when truncated.\n */\nexport function truncate(str: string, maxLen: number = 200): string {\n if (str.length <= maxLen) {\n return str;\n }\n return str.slice(0, maxLen - 3) + '...';\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var chunkE76GW6KF_js=require('./chunk-E76GW6KF.js');async function P(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 e;try{e=await a.json();}catch{return new Response(JSON.stringify({error:"Invalid JSON body"}),{status:400,headers:{"Content-Type":"application/json"}})}if(!e||typeof e!="object"||!Array.isArray(e.events))return new Response(JSON.stringify({error:'Payload must contain an "events" array'}),{status:400,headers:{"Content-Type":"application/json"}});let t=e.events;return t.length===0?new Response(JSON.stringify({accepted:0}),{status:202,headers:{"Content-Type":"application/json"}}):(await E(t),new Response(JSON.stringify({accepted:t.length}),{status:202,headers:{"Content-Type":"application/json"}}))}catch(e){let t=e instanceof Error?e.message:"Internal server error";return new Response(JSON.stringify({error:t}),{status:500,headers:{"Content-Type":"application/json"}})}}async function E(a){let e=await import('fs/promises'),t=await import('path'),r=t.resolve(process.cwd(),".uncaught");await e.mkdir(t.join(r,"events"),{recursive:true}),await e.mkdir(t.join(r,"fix-prompts"),{recursive:true});let p=t.join(r,"issues.json"),o=[];try{let n=await e.readFile(p,"utf-8");o=JSON.parse(n);}catch{}for(let n of a){let i=n.fingerprint;if(!i)continue;let c=t.join(r,"events",i);await e.mkdir(c,{recursive:true});let u=`event-${(n.timestamp??new Date().toISOString()).replace(/[:.]/g,"-")}.json`,w=t.join(c,u),y=w+".tmp";await e.writeFile(y,chunkE76GW6KF_js.b(n),"utf-8"),await e.rename(y,w);let g=t.join(c,"latest.json"),v=g+".tmp";await e.writeFile(v,chunkE76GW6KF_js.b(n),"utf-8"),await e.rename(v,g);let f=`${i}.md`,h=t.join(r,"fix-prompts",f),S=h+".tmp";await e.writeFile(S,n.fixPrompt??"","utf-8"),await e.rename(S,h);let m=n.user?.id??n.user?.email??"anonymous",s=o.find(j=>j.fingerprint===i);s?(s.count+=1,s.lastSeen=n.timestamp,s.latestEventFile=u,s.fixPromptFile=f,s.affectedUsers.includes(String(m))||s.affectedUsers.push(String(m)),s.status==="resolved"&&(s.status="open")):o.push({fingerprint:i,title:n.error?.message??"Unknown error",errorType:n.error?.type??"Error",count:1,affectedUsers:[String(m)],firstSeen:n.timestamp,lastSeen:n.timestamp,status:"open",fixPromptFile:f,latestEventFile:u});}let l=p+".tmp";await e.writeFile(l,JSON.stringify(o,null,2),"utf-8"),await e.rename(l,p);}exports.a=P;exports.b=E;//# sourceMappingURL=chunk-FFHQ452Q.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-FFHQ452Q.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"],"mappings":"iEAmBA,eAAsBA,CAAAA,CAAKC,CAAAA,CAAqC,CAC9D,GAAI,CAEF,GACE,OAAO,OAAA,CAAY,GAAA,EACnB,OAAA,CAAQ,GAAA,CAAI,QAAA,GAAa,YAAA,EACzB,OAAA,CAAQ,GAAA,CAAI,sBAAA,GAA2B,MAAA,CAEvC,OAAO,IAAI,QAAA,CACT,IAAA,CAAK,SAAA,CAAU,CAAE,KAAA,CAAO,yCAA0C,CAAC,CAAA,CACnE,CAAE,MAAA,CAAQ,IAAK,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAE,CACjE,CAAA,CAIF,IAAIC,CAAAA,CACJ,GAAI,CACFA,CAAAA,CAAO,MAAMD,CAAAA,CAAQ,IAAA,GACvB,CAAA,KAAQ,CACN,OAAO,IAAI,QAAA,CACT,IAAA,CAAK,SAAA,CAAU,CAAE,KAAA,CAAO,mBAAoB,CAAC,CAAA,CAC7C,CAAE,MAAA,CAAQ,IAAK,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAE,CACjE,CACF,CAEA,GACE,CAACC,CAAAA,EACD,OAAOA,CAAAA,EAAS,QAAA,EAChB,CAAC,KAAA,CAAM,OAAA,CAASA,CAAAA,CAAiC,MAAM,CAAA,CAEvD,OAAO,IAAI,QAAA,CACT,IAAA,CAAK,SAAA,CAAU,CAAE,KAAA,CAAO,wCAAyC,CAAC,CAAA,CAClE,CAAE,MAAA,CAAQ,GAAA,CAAK,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAE,CACjE,CAAA,CAGF,IAAMC,CAAAA,CAAUD,CAAAA,CAAqC,MAAA,CAErD,OAAIC,CAAAA,CAAO,MAAA,GAAW,CAAA,CACb,IAAI,QAAA,CACT,IAAA,CAAK,SAAA,CAAU,CAAE,QAAA,CAAU,CAAE,CAAC,CAAA,CAC9B,CAAE,MAAA,CAAQ,GAAA,CAAK,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAE,CACjE,CAAA,EAIF,MAAMC,CAAAA,CAAYD,CAAM,CAAA,CAEjB,IAAI,QAAA,CACT,IAAA,CAAK,SAAA,CAAU,CAAE,QAAA,CAAUA,CAAAA,CAAO,MAAO,CAAC,CAAA,CAC1C,CAAE,MAAA,CAAQ,GAAA,CAAK,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAE,CACjE,CAAA,CACF,CAAA,MAASE,EAAK,CACZ,IAAMC,CAAAA,CACJD,CAAAA,YAAe,KAAA,CAAQA,CAAAA,CAAI,OAAA,CAAU,uBAAA,CACvC,OAAO,IAAI,QAAA,CACT,IAAA,CAAK,SAAA,CAAU,CAAE,KAAA,CAAOC,CAAQ,CAAC,CAAA,CACjC,CAAE,MAAA,CAAQ,GAAA,CAAK,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAE,CACjE,CACF,CACF,CAMA,eAAsBF,CAAAA,CAAYD,CAAAA,CAAwC,CACxE,IAAMI,CAAAA,CAAK,MAAM,OAAO,aAAa,CAAA,CAC/BC,CAAAA,CAAO,MAAM,OAAO,MAAM,CAAA,CAE1BC,CAAAA,CAAUD,CAAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,CAAG,WAAW,CAAA,CACvD,MAAMD,CAAAA,CAAG,KAAA,CAAMC,CAAAA,CAAK,IAAA,CAAKC,CAAAA,CAAS,QAAQ,CAAA,CAAG,CAAE,SAAA,CAAW,IAAK,CAAC,CAAA,CAChE,MAAMF,CAAAA,CAAG,KAAA,CAAMC,CAAAA,CAAK,IAAA,CAAKC,CAAAA,CAAS,aAAa,CAAA,CAAG,CAAE,SAAA,CAAW,IAAK,CAAC,CAAA,CAGrE,IAAMC,CAAAA,CAAYF,CAAAA,CAAK,IAAA,CAAKC,CAAAA,CAAS,aAAa,CAAA,CAC9CE,CAAAA,CAAuB,EAAC,CAC5B,GAAI,CACF,IAAMC,CAAAA,CAAM,MAAML,CAAAA,CAAG,QAAA,CAASG,CAAAA,CAAW,OAAO,CAAA,CAChDC,CAAAA,CAAS,IAAA,CAAK,KAAA,CAAMC,CAAG,EACzB,CAAA,KAAQ,CAER,CAEA,IAAA,IAAWC,CAAAA,IAASV,CAAAA,CAAQ,CAC1B,IAAMW,CAAAA,CAAKD,CAAAA,CAAM,WAAA,CACjB,GAAI,CAACC,CAAAA,CAAI,SAET,IAAMC,CAAAA,CAAWP,CAAAA,CAAK,IAAA,CAAKC,CAAAA,CAAS,SAAUK,CAAE,CAAA,CAChD,MAAMP,CAAAA,CAAG,KAAA,CAAMQ,CAAAA,CAAU,CAAE,SAAA,CAAW,IAAK,CAAC,CAAA,CAO5C,IAAMC,CAAAA,CAAY,CAAA,MAAA,EAAA,CAJNH,CAAAA,CAAM,SAAA,EAAa,IAAI,IAAA,EAAK,CAAE,WAAA,EAAY,EAAG,OAAA,CACvD,OAAA,CACA,GACF,CAC6B,CAAA,KAAA,CAAA,CACvBI,CAAAA,CAAYT,CAAAA,CAAK,IAAA,CAAKO,CAAAA,CAAUC,CAAS,CAAA,CACzCE,CAAAA,CAAWD,CAAAA,CAAY,MAAA,CAC7B,MAAMV,CAAAA,CAAG,SAAA,CAAUW,CAAAA,CAAUC,kBAAAA,CAAcN,CAAK,CAAA,CAAG,OAAO,CAAA,CAC1D,MAAMN,CAAAA,CAAG,MAAA,CAAOW,CAAAA,CAAUD,CAAS,CAAA,CAGnC,IAAMG,CAAAA,CAAaZ,CAAAA,CAAK,IAAA,CAAKO,CAAAA,CAAU,aAAa,CAAA,CAC9CM,CAAAA,CAAYD,CAAAA,CAAa,MAAA,CAC/B,MAAMb,EAAG,SAAA,CAAUc,CAAAA,CAAWF,kBAAAA,CAAcN,CAAK,CAAA,CAAG,OAAO,CAAA,CAC3D,MAAMN,CAAAA,CAAG,MAAA,CAAOc,CAAAA,CAAWD,CAAU,CAAA,CAGrC,IAAME,CAAAA,CAAa,CAAA,EAAGR,CAAE,CAAA,GAAA,CAAA,CAClBS,CAAAA,CAAaf,CAAAA,CAAK,IAAA,CAAKC,CAAAA,CAAS,aAAA,CAAea,CAAU,CAAA,CACzDE,CAAAA,CAAYD,CAAAA,CAAa,MAAA,CAC/B,MAAMhB,CAAAA,CAAG,UAAUiB,CAAAA,CAAWX,CAAAA,CAAM,SAAA,EAAa,EAAA,CAAI,OAAO,CAAA,CAC5D,MAAMN,CAAAA,CAAG,MAAA,CAAOiB,CAAAA,CAAWD,CAAU,CAAA,CAGrC,IAAME,CAAAA,CACHZ,CAAAA,CAAM,IAAA,EAA8C,EAAA,EACpDA,CAAAA,CAAM,IAAA,EAA8C,KAAA,EACrD,WAAA,CAEIa,CAAAA,CAAWf,CAAAA,CAAO,IAAA,CAAMgB,CAAAA,EAAMA,CAAAA,CAAE,WAAA,GAAgBb,CAAE,CAAA,CACpDY,CAAAA,EACFA,EAAS,KAAA,EAAS,CAAA,CAClBA,CAAAA,CAAS,QAAA,CAAWb,CAAAA,CAAM,SAAA,CAC1Ba,CAAAA,CAAS,eAAA,CAAkBV,CAAAA,CAC3BU,CAAAA,CAAS,aAAA,CAAgBJ,CAAAA,CACpBI,CAAAA,CAAS,aAAA,CAAc,QAAA,CAAS,MAAA,CAAOD,CAAM,CAAC,CAAA,EACjDC,CAAAA,CAAS,aAAA,CAAc,IAAA,CAAK,MAAA,CAAOD,CAAM,CAAC,CAAA,CAExCC,CAAAA,CAAS,MAAA,GAAW,UAAA,GACtBA,CAAAA,CAAS,OAAS,MAAA,CAAA,EAGpBf,CAAAA,CAAO,IAAA,CAAK,CACV,WAAA,CAAaG,CAAAA,CACb,KAAA,CAAOD,CAAAA,CAAM,KAAA,EAAO,OAAA,EAAW,eAAA,CAC/B,SAAA,CAAWA,CAAAA,CAAM,KAAA,EAAO,IAAA,EAAQ,OAAA,CAChC,KAAA,CAAO,CAAA,CACP,aAAA,CAAe,CAAC,MAAA,CAAOY,CAAM,CAAC,CAAA,CAC9B,SAAA,CAAWZ,CAAAA,CAAM,SAAA,CACjB,QAAA,CAAUA,CAAAA,CAAM,SAAA,CAChB,MAAA,CAAQ,MAAA,CACR,aAAA,CAAeS,CAAAA,CACf,eAAA,CAAiBN,CACnB,CAAC,EAEL,CAGA,IAAMY,CAAAA,CAAWlB,CAAAA,CAAY,MAAA,CAC7B,MAAMH,CAAAA,CAAG,SAAA,CAAUqB,CAAAA,CAAU,IAAA,CAAK,SAAA,CAAUjB,CAAAA,CAAQ,IAAA,CAAM,CAAC,CAAA,CAAG,OAAO,CAAA,CACrE,MAAMJ,CAAAA,CAAG,MAAA,CAAOqB,CAAAA,CAAUlB,CAAS,EACrC","file":"chunk-FFHQ452Q.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"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {b}from'./chunk-P6JRN5CN.mjs';async function P(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 e;try{e=await a.json();}catch{return new Response(JSON.stringify({error:"Invalid JSON body"}),{status:400,headers:{"Content-Type":"application/json"}})}if(!e||typeof e!="object"||!Array.isArray(e.events))return new Response(JSON.stringify({error:'Payload must contain an "events" array'}),{status:400,headers:{"Content-Type":"application/json"}});let t=e.events;return t.length===0?new Response(JSON.stringify({accepted:0}),{status:202,headers:{"Content-Type":"application/json"}}):(await E(t),new Response(JSON.stringify({accepted:t.length}),{status:202,headers:{"Content-Type":"application/json"}}))}catch(e){let t=e instanceof Error?e.message:"Internal server error";return new Response(JSON.stringify({error:t}),{status:500,headers:{"Content-Type":"application/json"}})}}async function E(a){let e=await import('fs/promises'),t=await import('path'),r=t.resolve(process.cwd(),".uncaught");await e.mkdir(t.join(r,"events"),{recursive:true}),await e.mkdir(t.join(r,"fix-prompts"),{recursive:true});let p=t.join(r,"issues.json"),o=[];try{let n=await e.readFile(p,"utf-8");o=JSON.parse(n);}catch{}for(let n of a){let i=n.fingerprint;if(!i)continue;let c=t.join(r,"events",i);await e.mkdir(c,{recursive:true});let u=`event-${(n.timestamp??new Date().toISOString()).replace(/[:.]/g,"-")}.json`,w=t.join(c,u),y=w+".tmp";await e.writeFile(y,b(n),"utf-8"),await e.rename(y,w);let g=t.join(c,"latest.json"),v=g+".tmp";await e.writeFile(v,b(n),"utf-8"),await e.rename(v,g);let f=`${i}.md`,h=t.join(r,"fix-prompts",f),S=h+".tmp";await e.writeFile(S,n.fixPrompt??"","utf-8"),await e.rename(S,h);let m=n.user?.id??n.user?.email??"anonymous",s=o.find(j=>j.fingerprint===i);s?(s.count+=1,s.lastSeen=n.timestamp,s.latestEventFile=u,s.fixPromptFile=f,s.affectedUsers.includes(String(m))||s.affectedUsers.push(String(m)),s.status==="resolved"&&(s.status="open")):o.push({fingerprint:i,title:n.error?.message??"Unknown error",errorType:n.error?.type??"Error",count:1,affectedUsers:[String(m)],firstSeen:n.timestamp,lastSeen:n.timestamp,status:"open",fixPromptFile:f,latestEventFile:u});}let l=p+".tmp";await e.writeFile(l,JSON.stringify(o,null,2),"utf-8"),await e.rename(l,p);}export{P as a,E as b};//# sourceMappingURL=chunk-JALIO2BZ.mjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-JALIO2BZ.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"],"mappings":"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","file":"chunk-JALIO2BZ.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"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function c(){return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let r=Math.random()*16|0;return (t==="x"?r:r&3|8).toString(16)})}function g(t,r=10){let e=new WeakSet;function i(n,x){if(n==null||typeof n=="boolean"||typeof n=="number"||typeof n=="string")return n;if(typeof n=="bigint")return n.toString();if(!(typeof n=="function"||typeof n=="symbol")){if(x>r)return "[Max Depth]";if(n instanceof Date)return n.toISOString();if(typeof n=="object"){if(e.has(n))return "[Circular]";e.add(n);let f;if(Array.isArray(n))f=n.map(o=>i(o,x+1));else {let o={};for(let s of Object.keys(n))o[s]=i(n[s],x+1);f=o;}return e.delete(n),f}return n}}try{let n=i(t,0);return JSON.stringify(n)}catch{return '"[Unserializable]"'}}function u(){return new Date().toISOString()}function y(t,r=200){return t.length<=r?t:t.slice(0,r-3)+"..."}export{c as a,g as b,u as c,y as d};//# sourceMappingURL=chunk-P6JRN5CN.mjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-P6JRN5CN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"names":["generateUUID","c","safeStringify","obj","maxDepth","seen","walk","value","depth","result","item","key","safe","isoTimestamp","truncate","str","maxLen"],"mappings":"AAQO,SAASA,CAAAA,EAAuB,CAErC,OAAO,sCAAA,CAAuC,QAAQ,OAAA,CAAUC,CAAAA,EAAM,CACpE,IAAM,CAAA,CAAK,KAAK,MAAA,EAAO,CAAI,GAAM,CAAA,CAEjC,OAAA,CADUA,IAAM,GAAA,CAAM,CAAA,CAAK,CAAA,CAAI,CAAA,CAAO,CAAA,EAC7B,QAAA,CAAS,EAAE,CACtB,CAAC,CACH,CASO,SAASC,EAAcC,CAAAA,CAAcC,CAAAA,CAAmB,GAAY,CACzE,IAAMC,EAAO,IAAI,OAAA,CAEjB,SAASC,CAAAA,CAAKC,CAAAA,CAAgBC,EAAwB,CAIpD,GAFID,CAAAA,EAAU,IAAA,EACV,OAAOA,CAAAA,EAAU,WAAa,OAAOA,CAAAA,EAAU,UAC/C,OAAOA,CAAAA,EAAU,SAAU,OAAOA,CAAAA,CACtC,GAAI,OAAOA,CAAAA,EAAU,SAAU,OAAOA,CAAAA,CAAM,UAAS,CACrD,GAAI,SAAOA,CAAAA,EAAU,UAAA,EAAc,OAAOA,CAAAA,EAAU,QAAA,CAAA,CAGpD,CAAA,GAAIC,EAAQJ,CAAAA,CAAU,OAAO,cAG7B,GAAIG,CAAAA,YAAiB,KAAM,OAAOA,CAAAA,CAAM,aAAY,CAGpD,GAAI,OAAOA,CAAAA,EAAU,QAAA,CAAU,CAC7B,GAAIF,CAAAA,CAAK,IAAIE,CAAe,CAAA,CAAG,OAAO,YAAA,CACtCF,CAAAA,CAAK,GAAA,CAAIE,CAAe,CAAA,CAExB,IAAIE,EACJ,GAAI,KAAA,CAAM,QAAQF,CAAK,CAAA,CACrBE,EAASF,CAAAA,CAAM,GAAA,CAAKG,GAASJ,CAAAA,CAAKI,CAAAA,CAAMF,EAAQ,CAAC,CAAC,OAC7C,CACL,IAAML,CAAAA,CAA+B,EAAC,CACtC,IAAA,IAAWQ,KAAO,MAAA,CAAO,IAAA,CAAKJ,CAAgC,CAAA,CAC5DJ,CAAAA,CAAIQ,CAAG,CAAA,CAAIL,CAAAA,CAAMC,EAAkCI,CAAG,CAAA,CAAGH,EAAQ,CAAC,CAAA,CAEpEC,EAASN,EACX,CAEA,OAAAE,CAAAA,CAAK,MAAA,CAAOE,CAAe,CAAA,CACpBE,CACT,CAEA,OAAOF,CAAAA,CACT,CAEA,GAAI,CACF,IAAMK,EAAON,CAAAA,CAAKH,CAAAA,CAAK,CAAC,CAAA,CACxB,OAAO,KAAK,SAAA,CAAUS,CAAI,CAC5B,CAAA,KAAQ,CACN,OAAO,oBACT,CACF,CAKO,SAASC,CAAAA,EAAuB,CACrC,OAAO,IAAI,IAAA,GAAO,WAAA,EACpB,CAKO,SAASC,CAAAA,CAASC,CAAAA,CAAaC,CAAAA,CAAiB,GAAA,CAAa,CAClE,OAAID,CAAAA,CAAI,MAAA,EAAUC,EACTD,CAAAA,CAEFA,CAAAA,CAAI,MAAM,CAAA,CAAGC,CAAAA,CAAS,CAAC,CAAA,CAAI,KACpC","file":"chunk-P6JRN5CN.mjs","sourcesContent":["// ---------------------------------------------------------------------------\n// @uncaughtdev/core — utility helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a UUID v4 string using `Math.random`.\n * Not cryptographically secure — suitable for event IDs.\n */\nexport function generateUUID(): string {\n // RFC 4122 version 4 UUID template\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Safely serialise a value to JSON, handling circular references.\n *\n * @param obj - The value to serialise.\n * @param maxDepth - Maximum nesting depth before values are replaced with\n * `\"[Max Depth]\"`. Defaults to 10.\n */\nexport function safeStringify(obj: unknown, maxDepth: number = 10): string {\n const seen = new WeakSet();\n\n function walk(value: unknown, depth: number): unknown {\n // Primitives pass through\n if (value === null || value === undefined) return value;\n if (typeof value === 'boolean' || typeof value === 'number') return value;\n if (typeof value === 'string') return value;\n if (typeof value === 'bigint') return value.toString();\n if (typeof value === 'function' || typeof value === 'symbol') return undefined;\n\n // Depth guard\n if (depth > maxDepth) return '[Max Depth]';\n\n // Dates → ISO string\n if (value instanceof Date) return value.toISOString();\n\n // Objects and arrays — circular reference detection\n if (typeof value === 'object') {\n if (seen.has(value as object)) return '[Circular]';\n seen.add(value as object);\n\n let result: unknown;\n if (Array.isArray(value)) {\n result = value.map((item) => walk(item, depth + 1));\n } else {\n const obj: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n obj[key] = walk((value as Record<string, unknown>)[key], depth + 1);\n }\n result = obj;\n }\n\n seen.delete(value as object);\n return result;\n }\n\n return value;\n }\n\n try {\n const safe = walk(obj, 0);\n return JSON.stringify(safe);\n } catch {\n return '\"[Unserializable]\"';\n }\n}\n\n/**\n * Return the current date/time as an ISO 8601 string.\n */\nexport function isoTimestamp(): string {\n return new Date().toISOString();\n}\n\n/**\n * Truncate a string to `maxLen` characters, appending \"...\" when truncated.\n */\nexport function truncate(str: string, maxLen: number = 200): string {\n if (str.length <= maxLen) {\n return str;\n }\n return str.slice(0, maxLen - 3) + '...';\n}\n"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { a as UncaughtConfig, R as RequestInfo, O as OperationInfo, S as SeverityLevel, B as Breadcrumb, b as UserInfo, c as BreadcrumbStore, U as UncaughtEvent, E as EnvironmentInfo, T as Transport } from './types-CjgYXVc_.mjs';
|
|
2
|
+
export { d as BreadcrumbType, e as ErrorInfo, I as IssueEntry, f as IssueStatus, g as SdkInfo, h as TransportMode, i as TransportOptions } from './types-CjgYXVc_.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Initialise the Uncaught SDK. Calling this more than once replaces the
|
|
6
|
+
* previous client instance.
|
|
7
|
+
*/
|
|
8
|
+
declare function initUncaught(config: UncaughtConfig): UncaughtClient;
|
|
9
|
+
/**
|
|
10
|
+
* Return the current singleton client, or `undefined` if `initUncaught` has
|
|
11
|
+
* not been called.
|
|
12
|
+
*/
|
|
13
|
+
declare function getClient(): UncaughtClient | undefined;
|
|
14
|
+
declare class UncaughtClient {
|
|
15
|
+
private readonly config;
|
|
16
|
+
private readonly breadcrumbs;
|
|
17
|
+
private readonly transport;
|
|
18
|
+
private readonly rateLimiter;
|
|
19
|
+
private readonly sessionId;
|
|
20
|
+
private user;
|
|
21
|
+
constructor(config: UncaughtConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Capture an error and send it through the transport pipeline.
|
|
24
|
+
*
|
|
25
|
+
* @returns The event ID, or `undefined` if the event was dropped.
|
|
26
|
+
*/
|
|
27
|
+
captureError(error: unknown, context?: {
|
|
28
|
+
request?: RequestInfo;
|
|
29
|
+
operation?: OperationInfo;
|
|
30
|
+
componentStack?: string;
|
|
31
|
+
level?: SeverityLevel;
|
|
32
|
+
}): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Capture a plain message (not backed by an Error instance).
|
|
35
|
+
*/
|
|
36
|
+
captureMessage(message: string, level?: SeverityLevel): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Add a breadcrumb to the ring buffer.
|
|
39
|
+
*/
|
|
40
|
+
addBreadcrumb(crumb: Omit<Breadcrumb, 'timestamp'>): void;
|
|
41
|
+
/**
|
|
42
|
+
* Set user context that will be attached to subsequent events.
|
|
43
|
+
*/
|
|
44
|
+
setUser(user: UserInfo | undefined): void;
|
|
45
|
+
/**
|
|
46
|
+
* Flush all queued events to the transport.
|
|
47
|
+
*/
|
|
48
|
+
flush(): Promise<void>;
|
|
49
|
+
private shouldIgnore;
|
|
50
|
+
private debugLog;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a fixed-capacity ring-buffer store for breadcrumbs.
|
|
55
|
+
*
|
|
56
|
+
* - O(1) `add`
|
|
57
|
+
* - Oldest entries are silently overwritten when capacity is reached.
|
|
58
|
+
* - Returned arrays are always copies — callers cannot mutate internal state.
|
|
59
|
+
*
|
|
60
|
+
* @param capacity Maximum breadcrumbs retained. Defaults to 20.
|
|
61
|
+
*/
|
|
62
|
+
declare function createBreadcrumbStore(capacity?: number): BreadcrumbStore;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Deep-clone and sanitise `obj`, redacting values whose keys match
|
|
66
|
+
* sensitive patterns.
|
|
67
|
+
*
|
|
68
|
+
* - Handles circular references (returns `"[Circular]"`).
|
|
69
|
+
* - Truncates strings longer than 2 048 characters.
|
|
70
|
+
* - Never mutates the original object.
|
|
71
|
+
*
|
|
72
|
+
* @param obj - The value to sanitise.
|
|
73
|
+
* @param additionalKeys - Extra key patterns to redact on top of the defaults.
|
|
74
|
+
*/
|
|
75
|
+
declare function sanitize<T>(obj: T, additionalKeys?: string[]): T;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build a structured Markdown prompt that can be pasted into an AI assistant
|
|
79
|
+
* to diagnose and fix the production error described by `event`.
|
|
80
|
+
*
|
|
81
|
+
* Empty sections are omitted to keep the prompt concise.
|
|
82
|
+
*/
|
|
83
|
+
declare function buildFixPrompt(event: Partial<UncaughtEvent>): string;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generate a stable fingerprint for an error so that duplicate occurrences
|
|
87
|
+
* of the same bug are grouped together.
|
|
88
|
+
*
|
|
89
|
+
* The fingerprint is an 8-character hex string derived from:
|
|
90
|
+
* 1. The normalised error message (volatile parts stripped).
|
|
91
|
+
* 2. The top 3 stack frames (file + function name, no line/col numbers).
|
|
92
|
+
*
|
|
93
|
+
* @param error - An object with at least `message` and optionally `stack` and `type`.
|
|
94
|
+
*/
|
|
95
|
+
declare function generateFingerprint(error: {
|
|
96
|
+
message?: string;
|
|
97
|
+
type?: string;
|
|
98
|
+
stack?: string;
|
|
99
|
+
}): string;
|
|
100
|
+
|
|
101
|
+
interface RateLimiter {
|
|
102
|
+
/**
|
|
103
|
+
* Returns `true` if the event identified by `fingerprint` is allowed
|
|
104
|
+
* through, `false` if it should be dropped.
|
|
105
|
+
*/
|
|
106
|
+
shouldAllow(fingerprint: string): boolean;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create a rate limiter that enforces:
|
|
110
|
+
* - A global maximum of `globalMax` events per 60-second window.
|
|
111
|
+
* - A per-fingerprint maximum of `perFingerprintMax` events per 60-second window.
|
|
112
|
+
*
|
|
113
|
+
* Stale timestamps are lazily cleaned on every call to `shouldAllow`.
|
|
114
|
+
*
|
|
115
|
+
* @param globalMax Max events across all fingerprints. Defaults to 30.
|
|
116
|
+
* @param perFingerprintMax Max events for a single fingerprint. Defaults to 5.
|
|
117
|
+
*/
|
|
118
|
+
declare function createRateLimiter(globalMax?: number, perFingerprintMax?: number): RateLimiter;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Detect the current runtime environment.
|
|
122
|
+
*
|
|
123
|
+
* - SSR-safe: every global access is guarded.
|
|
124
|
+
* - Result is cached after the first invocation.
|
|
125
|
+
*/
|
|
126
|
+
declare function detectEnvironment(): EnvironmentInfo;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create the appropriate transport strategy based on config.
|
|
130
|
+
*/
|
|
131
|
+
declare function createTransport(config: UncaughtConfig): Transport;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generate a UUID v4 string using `Math.random`.
|
|
135
|
+
* Not cryptographically secure — suitable for event IDs.
|
|
136
|
+
*/
|
|
137
|
+
declare function generateUUID(): string;
|
|
138
|
+
/**
|
|
139
|
+
* Safely serialise a value to JSON, handling circular references.
|
|
140
|
+
*
|
|
141
|
+
* @param obj - The value to serialise.
|
|
142
|
+
* @param maxDepth - Maximum nesting depth before values are replaced with
|
|
143
|
+
* `"[Max Depth]"`. Defaults to 10.
|
|
144
|
+
*/
|
|
145
|
+
declare function safeStringify(obj: unknown, maxDepth?: number): string;
|
|
146
|
+
/**
|
|
147
|
+
* Return the current date/time as an ISO 8601 string.
|
|
148
|
+
*/
|
|
149
|
+
declare function isoTimestamp(): string;
|
|
150
|
+
/**
|
|
151
|
+
* Truncate a string to `maxLen` characters, appending "..." when truncated.
|
|
152
|
+
*/
|
|
153
|
+
declare function truncate(str: string, maxLen?: number): string;
|
|
154
|
+
|
|
155
|
+
export { Breadcrumb, BreadcrumbStore, EnvironmentInfo, OperationInfo, RequestInfo, SeverityLevel, Transport, UncaughtClient, UncaughtConfig, UncaughtEvent, UserInfo, buildFixPrompt, createBreadcrumbStore, createRateLimiter, createTransport, detectEnvironment, generateFingerprint, generateUUID, getClient, initUncaught, isoTimestamp, safeStringify, sanitize, truncate };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { a as UncaughtConfig, R as RequestInfo, O as OperationInfo, S as SeverityLevel, B as Breadcrumb, b as UserInfo, c as BreadcrumbStore, U as UncaughtEvent, E as EnvironmentInfo, T as Transport } from './types-CjgYXVc_.js';
|
|
2
|
+
export { d as BreadcrumbType, e as ErrorInfo, I as IssueEntry, f as IssueStatus, g as SdkInfo, h as TransportMode, i as TransportOptions } from './types-CjgYXVc_.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Initialise the Uncaught SDK. Calling this more than once replaces the
|
|
6
|
+
* previous client instance.
|
|
7
|
+
*/
|
|
8
|
+
declare function initUncaught(config: UncaughtConfig): UncaughtClient;
|
|
9
|
+
/**
|
|
10
|
+
* Return the current singleton client, or `undefined` if `initUncaught` has
|
|
11
|
+
* not been called.
|
|
12
|
+
*/
|
|
13
|
+
declare function getClient(): UncaughtClient | undefined;
|
|
14
|
+
declare class UncaughtClient {
|
|
15
|
+
private readonly config;
|
|
16
|
+
private readonly breadcrumbs;
|
|
17
|
+
private readonly transport;
|
|
18
|
+
private readonly rateLimiter;
|
|
19
|
+
private readonly sessionId;
|
|
20
|
+
private user;
|
|
21
|
+
constructor(config: UncaughtConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Capture an error and send it through the transport pipeline.
|
|
24
|
+
*
|
|
25
|
+
* @returns The event ID, or `undefined` if the event was dropped.
|
|
26
|
+
*/
|
|
27
|
+
captureError(error: unknown, context?: {
|
|
28
|
+
request?: RequestInfo;
|
|
29
|
+
operation?: OperationInfo;
|
|
30
|
+
componentStack?: string;
|
|
31
|
+
level?: SeverityLevel;
|
|
32
|
+
}): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Capture a plain message (not backed by an Error instance).
|
|
35
|
+
*/
|
|
36
|
+
captureMessage(message: string, level?: SeverityLevel): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Add a breadcrumb to the ring buffer.
|
|
39
|
+
*/
|
|
40
|
+
addBreadcrumb(crumb: Omit<Breadcrumb, 'timestamp'>): void;
|
|
41
|
+
/**
|
|
42
|
+
* Set user context that will be attached to subsequent events.
|
|
43
|
+
*/
|
|
44
|
+
setUser(user: UserInfo | undefined): void;
|
|
45
|
+
/**
|
|
46
|
+
* Flush all queued events to the transport.
|
|
47
|
+
*/
|
|
48
|
+
flush(): Promise<void>;
|
|
49
|
+
private shouldIgnore;
|
|
50
|
+
private debugLog;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a fixed-capacity ring-buffer store for breadcrumbs.
|
|
55
|
+
*
|
|
56
|
+
* - O(1) `add`
|
|
57
|
+
* - Oldest entries are silently overwritten when capacity is reached.
|
|
58
|
+
* - Returned arrays are always copies — callers cannot mutate internal state.
|
|
59
|
+
*
|
|
60
|
+
* @param capacity Maximum breadcrumbs retained. Defaults to 20.
|
|
61
|
+
*/
|
|
62
|
+
declare function createBreadcrumbStore(capacity?: number): BreadcrumbStore;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Deep-clone and sanitise `obj`, redacting values whose keys match
|
|
66
|
+
* sensitive patterns.
|
|
67
|
+
*
|
|
68
|
+
* - Handles circular references (returns `"[Circular]"`).
|
|
69
|
+
* - Truncates strings longer than 2 048 characters.
|
|
70
|
+
* - Never mutates the original object.
|
|
71
|
+
*
|
|
72
|
+
* @param obj - The value to sanitise.
|
|
73
|
+
* @param additionalKeys - Extra key patterns to redact on top of the defaults.
|
|
74
|
+
*/
|
|
75
|
+
declare function sanitize<T>(obj: T, additionalKeys?: string[]): T;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build a structured Markdown prompt that can be pasted into an AI assistant
|
|
79
|
+
* to diagnose and fix the production error described by `event`.
|
|
80
|
+
*
|
|
81
|
+
* Empty sections are omitted to keep the prompt concise.
|
|
82
|
+
*/
|
|
83
|
+
declare function buildFixPrompt(event: Partial<UncaughtEvent>): string;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generate a stable fingerprint for an error so that duplicate occurrences
|
|
87
|
+
* of the same bug are grouped together.
|
|
88
|
+
*
|
|
89
|
+
* The fingerprint is an 8-character hex string derived from:
|
|
90
|
+
* 1. The normalised error message (volatile parts stripped).
|
|
91
|
+
* 2. The top 3 stack frames (file + function name, no line/col numbers).
|
|
92
|
+
*
|
|
93
|
+
* @param error - An object with at least `message` and optionally `stack` and `type`.
|
|
94
|
+
*/
|
|
95
|
+
declare function generateFingerprint(error: {
|
|
96
|
+
message?: string;
|
|
97
|
+
type?: string;
|
|
98
|
+
stack?: string;
|
|
99
|
+
}): string;
|
|
100
|
+
|
|
101
|
+
interface RateLimiter {
|
|
102
|
+
/**
|
|
103
|
+
* Returns `true` if the event identified by `fingerprint` is allowed
|
|
104
|
+
* through, `false` if it should be dropped.
|
|
105
|
+
*/
|
|
106
|
+
shouldAllow(fingerprint: string): boolean;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create a rate limiter that enforces:
|
|
110
|
+
* - A global maximum of `globalMax` events per 60-second window.
|
|
111
|
+
* - A per-fingerprint maximum of `perFingerprintMax` events per 60-second window.
|
|
112
|
+
*
|
|
113
|
+
* Stale timestamps are lazily cleaned on every call to `shouldAllow`.
|
|
114
|
+
*
|
|
115
|
+
* @param globalMax Max events across all fingerprints. Defaults to 30.
|
|
116
|
+
* @param perFingerprintMax Max events for a single fingerprint. Defaults to 5.
|
|
117
|
+
*/
|
|
118
|
+
declare function createRateLimiter(globalMax?: number, perFingerprintMax?: number): RateLimiter;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Detect the current runtime environment.
|
|
122
|
+
*
|
|
123
|
+
* - SSR-safe: every global access is guarded.
|
|
124
|
+
* - Result is cached after the first invocation.
|
|
125
|
+
*/
|
|
126
|
+
declare function detectEnvironment(): EnvironmentInfo;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create the appropriate transport strategy based on config.
|
|
130
|
+
*/
|
|
131
|
+
declare function createTransport(config: UncaughtConfig): Transport;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generate a UUID v4 string using `Math.random`.
|
|
135
|
+
* Not cryptographically secure — suitable for event IDs.
|
|
136
|
+
*/
|
|
137
|
+
declare function generateUUID(): string;
|
|
138
|
+
/**
|
|
139
|
+
* Safely serialise a value to JSON, handling circular references.
|
|
140
|
+
*
|
|
141
|
+
* @param obj - The value to serialise.
|
|
142
|
+
* @param maxDepth - Maximum nesting depth before values are replaced with
|
|
143
|
+
* `"[Max Depth]"`. Defaults to 10.
|
|
144
|
+
*/
|
|
145
|
+
declare function safeStringify(obj: unknown, maxDepth?: number): string;
|
|
146
|
+
/**
|
|
147
|
+
* Return the current date/time as an ISO 8601 string.
|
|
148
|
+
*/
|
|
149
|
+
declare function isoTimestamp(): string;
|
|
150
|
+
/**
|
|
151
|
+
* Truncate a string to `maxLen` characters, appending "..." when truncated.
|
|
152
|
+
*/
|
|
153
|
+
declare function truncate(str: string, maxLen?: number): string;
|
|
154
|
+
|
|
155
|
+
export { Breadcrumb, BreadcrumbStore, EnvironmentInfo, OperationInfo, RequestInfo, SeverityLevel, Transport, UncaughtClient, UncaughtConfig, UncaughtEvent, UserInfo, buildFixPrompt, createBreadcrumbStore, createRateLimiter, createTransport, detectEnvironment, generateFingerprint, generateUUID, getClient, initUncaught, isoTimestamp, safeStringify, sanitize, truncate };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';var chunkE76GW6KF_js=require('./chunk-E76GW6KF.js');function S(e=20){let t=new Array(e),r=0,n=0;return {add(o){let c={...o,timestamp:chunkE76GW6KF_js.c()};t[r]=c,r=(r+1)%e,n<e&&n++;},getAll(){if(n===0)return [];let o=[],c=(r-n+e)%e;for(let i=0;i<n;i++){let s=(c+i)%e,u=t[s];u&&o.push({...u});}return o},getLast(o){if(o<=0||n===0)return [];let c=Math.min(o,n),i=[];for(let s=0;s<c;s++){let u=(r-1-s+e)%e,d=t[u];d&&i.unshift({...d});}return i},clear(){t.fill(void 0),r=0,n=0;}}}var M=["password","passwd","secret","token","apikey","api_key","authorization","credit_card","creditcard","card_number","cvv","ssn","social_security","private_key","access_token","refresh_token","session_id","cookie"],$=new Set(["authorization","cookie","set-cookie"]),C="[REDACTED]",j=2048;function D(e=[]){let r=[...M,...e].map(n=>n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(r.join("|"),"i")}function k(e,t){let r=D(t),n=new WeakSet;function a(o,c){if(c&&r.test(c))return C;if(o==null)return o;if(typeof o=="string")return o.length>j?o.slice(0,j)+"...[truncated]":o;if(typeof o=="number"||typeof o=="boolean")return o;if(typeof o=="bigint")return o.toString();if(!(typeof o=="function"||typeof o=="symbol")){if(o instanceof Date)return o.toISOString();if(Array.isArray(o)){if(n.has(o))return "[Circular]";n.add(o);let i=o.map(s=>a(s));return n.delete(o),i}if(typeof o=="object"){if(n.has(o))return "[Circular]";n.add(o);let i={};for(let s of Object.keys(o)){if($.has(s.toLowerCase())){i[s]=C;continue}i[s]=a(o[s],s);}return n.delete(o),i}return o}}return a(e)}function v(e){let t=N(e.message??""),r=V(e.stack??"",3),n=[e.type??"Error",t,...r].join(`
|
|
2
|
+
`);return z(n)}function N(e){return e.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,"<UUID>").replace(/\b[0-9a-f]{8,}\b/gi,"<HEX>").replace(/\b\d{4,}\b/g,"<NUM>").replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[.\d]*Z?/g,"<TIMESTAMP>").replace(/([/\\])[a-zA-Z0-9_-]+[-.]([a-f0-9]{6,})\.(js|ts|mjs|cjs|jsx|tsx)/g,"$1<FILE>.$3").trim()}function V(e,t){if(!e)return [];let r=e.split(`
|
|
3
|
+
`),n=[];for(let a of r){if(n.length>=t)break;let o=a.trim(),c=o.match(/at\s+(?:(.+?)\s+\()?(?:(.+?):\d+:\d+)\)?/);if(c){let s=c[1]||"<anonymous>",u=B(c[2]||"<unknown>");n.push(`${u}:${s}`);continue}let i=o.match(/^(.+?)@(.+?):\d+:\d+/);if(i){let s=i[1]||"<anonymous>",u=B(i[2]||"<unknown>");n.push(`${u}:${s}`);continue}}return n}function B(e){return e.replace(/[?#].*$/,"").replace(/^.*\/node_modules\//,"node_modules/").replace(/^(https?:\/\/[^/]+)/,"").replace(/^.*[/\\]/,"")}function z(e){let t=5381;for(let r=0;r<e.length;r++)t=(t<<5)+t+e.charCodeAt(r)|0;return (t>>>0).toString(16).padStart(8,"0")}function x(e=30,t=5){let r=new Map,n=[];function a(o,c){let i=c-6e4,s=0;for(;s<o.length&&o[s]<=i;)s++;return s>0&&o.splice(0,s),o}return {shouldAllow(o){let c=Date.now();if(n=a(n,c),n.length>=e)return false;let i=r.get(o);if(i||(i=[],r.set(o,i)),a(i,c),i.length>=t)return false;if(n.push(c),i.push(c),r.size>1e3)for(let[s,u]of r)u.length===0&&r.delete(s);return true}}}var I;function P(){if(I)return I;let e={};try{let t=typeof window<"u"&&typeof document<"u";if(typeof process<"u"&&process.versions!=null&&process.versions.node!=null)e.runtime="node",e.runtimeVersion=process.versions.node,e.platform=process.platform,e.os=W();else if(t){e.runtime="browser",e.platform="web";let n=navigator?.userAgent??"",a=K(n);e.browser=a.name,e.browserVersion=a.version,e.os=X(n),e.deviceType=q(),e.url=location?.href,e.locale=navigator?.language,e.timezone=Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone;}G(e);}catch{}return I=e,e}function W(){try{let e=process.platform;return {darwin:"macOS",win32:"Windows",linux:"Linux",freebsd:"FreeBSD",sunos:"SunOS"}[e]??e}catch{return}}function K(e){let t=[{name:"Edge",regex:/Edg(?:e|A|iOS)?\/(\d+[\d.]*)/},{name:"Opera",regex:/(?:OPR|Opera)\/(\d+[\d.]*)/},{name:"Samsung Internet",regex:/SamsungBrowser\/(\d+[\d.]*)/},{name:"UC Browser",regex:/UCBrowser\/(\d+[\d.]*)/},{name:"Firefox",regex:/Firefox\/(\d+[\d.]*)/},{name:"Chrome",regex:/Chrome\/(\d+[\d.]*)/},{name:"Safari",regex:/Version\/(\d+[\d.]*).*Safari/}];for(let{name:r,regex:n}of t){let a=e.match(n);if(a)return {name:r,version:a[1]}}return {}}function X(e){if(/Windows/i.test(e))return "Windows";if(/Mac OS X|macOS/i.test(e))return "macOS";if(/Android/i.test(e))return "Android";if(/iPhone|iPad|iPod/i.test(e))return "iOS";if(/Linux/i.test(e))return "Linux";if(/CrOS/i.test(e))return "ChromeOS"}function q(){try{if(typeof window>"u")return;let e=window.screen?.width??window.innerWidth;return e<=480?"mobile":e<=1024?"tablet":"desktop"}catch{return}}function G(e){try{typeof window<"u"&&(window.__NEXT_DATA__?(e.framework="next",e.frameworkVersion=window.__NEXT_DATA__?.buildId??void 0):window.__remixContext?e.framework="remix":window.__NUXT__&&(e.framework="nuxt")),typeof process<"u"&&process.env!=null&&(e.framework||(process.env.__NEXT_PRIVATE_ORIGIN!==void 0||process.env.NEXT_RUNTIME?e.framework="next":process.env.REMIX_DEV_ORIGIN!==void 0&&(e.framework="remix")),process.env.VERCEL?e.platform=e.platform??"vercel":process.env.RAILWAY_PROJECT_ID?e.platform=e.platform??"railway":process.env.FLY_APP_NAME?e.platform=e.platform??"fly":process.env.AWS_LAMBDA_FUNCTION_NAME?e.platform=e.platform??"aws-lambda":process.env.GOOGLE_CLOUD_PROJECT&&(e.platform=e.platform??"gcp"));try{typeof undefined?.VITE_USER_NODE_ENV<"u"&&(e.framework||(e.framework="vite"));}catch{}}catch{}}function T(e){let t=[];if(t.push(`I have a production bug in my application that I need help diagnosing and fixing.
|
|
4
|
+
`),e.error){let r=J(e.error.stack),n=["## Error",""];n.push(`- **Type:** ${e.error.type||"Error"}`),n.push(`- **Message:** ${e.error.message||"(no message)"}`),r&&n.push(`- **Location:** ${r}`),t.push(n.join(`
|
|
5
|
+
`));}if(e.error?.stack){let r=e.error.stack.split(`
|
|
6
|
+
`).slice(0,15).map(n=>n.trimEnd()).join(`
|
|
7
|
+
`);t.push(`## Stack Trace
|
|
8
|
+
|
|
9
|
+
\`\`\`
|
|
10
|
+
${r}
|
|
11
|
+
\`\`\``);}return e.operation&&t.push(H(e.operation)),e.request&&t.push(Y(e.request)),e.breadcrumbs&&e.breadcrumbs.length>0&&t.push(Z(e.breadcrumbs)),e.environment&&t.push(ee(e.environment)),e.error?.componentStack&&t.push(`## React Component Stack
|
|
12
|
+
|
|
13
|
+
\`\`\`
|
|
14
|
+
${e.error.componentStack.trim()}
|
|
15
|
+
\`\`\``),t.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(`
|
|
16
|
+
`)),t.join(`
|
|
17
|
+
|
|
18
|
+
`)+`
|
|
19
|
+
`}function J(e){if(e)for(let t of e.split(`
|
|
20
|
+
`)){let r=t.trim(),n=r.match(/at\s+(?:.+?\s+\()?(.+?:\d+:\d+)\)?/);if(n)return n[1];let a=r.match(/@(.+?:\d+:\d+)/);if(a)return a[1]}}function H(e){let t=["## Failed Operation",""];return t.push(`- **Provider:** ${e.provider}`),t.push(`- **Type:** ${e.type}`),t.push(`- **Method:** ${e.method}`),e.params&&(t.push("- **Params:**"),t.push("```json"),t.push(JSON.stringify(e.params,null,2)),t.push("```")),e.errorCode&&t.push(`- **Error Code:** ${e.errorCode}`),e.errorDetails&&t.push(`- **Error Details:** ${e.errorDetails}`),t.join(`
|
|
21
|
+
`)}function Y(e){let t=["## HTTP Request Context",""];return e.method&&t.push(`- **Method:** ${e.method}`),e.url&&t.push(`- **URL:** ${e.url}`),e.body&&(t.push("- **Body:**"),t.push("```json"),t.push(typeof e.body=="string"?e.body:JSON.stringify(e.body,null,2)),t.push("```")),t.join(`
|
|
22
|
+
`)}function Z(e){let t=e.slice(-5),r=["## User Session",""];for(let n of t){let a=Q(n.timestamp);r.push(`- \`${a}\` **[${n.type}]** ${n.message}`);}return r.join(`
|
|
23
|
+
`)}function Q(e){try{let t=new Date(e),r=String(t.getHours()).padStart(2,"0"),n=String(t.getMinutes()).padStart(2,"0"),a=String(t.getSeconds()).padStart(2,"0");return `${r}:${n}:${a}`}catch{return e}}function ee(e){let t=["## Environment",""],r=[["Framework",e.framework],["Framework Version",e.frameworkVersion],["Runtime",e.runtime],["Runtime Version",e.runtimeVersion],["Platform",e.platform],["Browser",e.browser?`${e.browser} ${e.browserVersion??""}`.trim():void 0],["OS",e.os],["Device",e.deviceType],["Locale",e.locale],["Timezone",e.timezone],["URL",e.url]];for(let[n,a]of r)a&&t.push(`- **${n}:** ${a}`);return t.join(`
|
|
24
|
+
`)}function _(e){switch(e.transport??"local"){case "console":return F();case "remote":return ie(e);default:return te(e)}}function F(e){return {send(t){try{let r=`[uncaught] ${t.error.type}: ${t.error.message}`;typeof console.group=="function"?console.group(r):console.log(`--- ${r} ---`),console.error("Error:",t.error.message),t.error.stack&&console.log("Stack:",t.error.stack),console.log("Event ID:",t.eventId),console.log("Fingerprint:",t.fingerprint),console.log("Breadcrumbs:",t.breadcrumbs),t.fixPrompt&&console.log(`Fix Prompt:
|
|
25
|
+
`,t.fixPrompt),typeof console.groupEnd=="function"&&console.groupEnd();}catch{}},async flush(){}}}function te(e){return typeof process<"u"&&process.versions?.node!=null?ne(e):oe()}function ne(e){let t,r,n="",a=false;async function o(){if(a)return;let i=await import('fs/promises'),s=await import('path');t=i,r=s,n=e.localOutputDir??r.resolve(process.cwd(),".uncaught"),await t.mkdir(r.join(n,"events"),{recursive:true}),await t.mkdir(r.join(n,"fix-prompts"),{recursive:true}),await c(t,r),a=true;}async function c(i,s){try{let u=s.resolve(process.cwd(),".gitignore"),d="";try{d=await i.readFile(u,"utf-8");}catch{}d.includes(".uncaught")||await i.appendFile(u,`
|
|
26
|
+
# Uncaught local error store
|
|
27
|
+
.uncaught/
|
|
28
|
+
`);}catch{}}return {async send(i){try{if(await o(),!t||!r)return;let s=i.fingerprint,u=r.join(n,"events",s);await t.mkdir(u,{recursive:!0});let m=`event-${i.timestamp.replace(/[:.]/g,"-")}.json`,y=r.join(u,m),f=y+".tmp";await t.writeFile(f,chunkE76GW6KF_js.b(i),"utf-8"),await t.rename(f,y);let p=r.join(u,"latest.json"),l=p+".tmp";await t.writeFile(l,chunkE76GW6KF_js.b(i),"utf-8"),await t.rename(l,p);let b=`${s}.md`,L=r.join(n,"fix-prompts",b),O=L+".tmp";await t.writeFile(O,i.fixPrompt,"utf-8"),await t.rename(O,L),await re(t,r,n,i,m,b);}catch{}},async flush(){}}}async function re(e,t,r,n,a,o){let c=t.join(r,"issues.json"),i=[];try{let m=await e.readFile(c,"utf-8");i=JSON.parse(m);}catch{}let s=i.find(m=>m.fingerprint===n.fingerprint),u=n.user?.id??n.user?.email??"anonymous";s?(s.count+=1,s.lastSeen=n.timestamp,s.latestEventFile=a,s.fixPromptFile=o,s.affectedUsers.includes(u)||s.affectedUsers.push(u),s.status==="resolved"&&(s.status="open")):i.push({fingerprint:n.fingerprint,title:n.error.message,errorType:n.error.type,count:1,affectedUsers:[u],firstSeen:n.timestamp,lastSeen:n.timestamp,status:"open",fixPromptFile:o,latestEventFile:a});let d=c+".tmp";await e.writeFile(d,JSON.stringify(i,null,2),"utf-8"),await e.rename(d,c);}function oe(e){let t=[],r=F();async function n(a){try{let o=await fetch("/api/uncaught/local",{method:"POST",headers:{"Content-Type":"application/json"},body:chunkE76GW6KF_js.b({events:a})});return o.ok||o.status===202}catch{return false}}return {send(a){t.push(a),n([a]).then(o=>{o||r.send(a);}).catch(()=>{r.send(a);});},async flush(){if(t.length===0)return;let a=t.splice(0,t.length);if(!await n(a))for(let c of a)r.send(c);}}}function ie(e){let t=e.endpoint??"",r=e.projectKey??"",n=3,a=10,o=5e3,c=[],i,s=false,u=[1e3,2e3,4e3];async function d(f){if(!(f.length===0||s))for(let p=0;p<=n;p++)try{let l=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",...r?{"X-Project-Key":r}:{}},body:chunkE76GW6KF_js.b({events:f})});if(l.ok||l.status===202)return;if(l.status===401){s=!0,i&&clearInterval(i);return}if(l.status===429){let b=parseInt(l.headers?.get?.("Retry-After")??"10",10);await U(b*1e3);continue}p<n&&await U(u[p]??4e3);}catch{p<n&&await U(u[p]??4e3);}}function m(){i||(i=setInterval(()=>{if(c.length>0){let f=c.splice(0,a);d(f).catch(()=>{});}},o),typeof i=="object"&&"unref"in i&&i.unref());}function y(){try{typeof window<"u"&&typeof navigator?.sendBeacon=="function"&&(window.addEventListener("visibilitychange",()=>{if(document.visibilityState==="hidden"&&c.length>0){let f=chunkE76GW6KF_js.b({events:c.splice(0,c.length)});navigator.sendBeacon(t,f);}}),window.addEventListener("pagehide",()=>{if(c.length>0){let f=chunkE76GW6KF_js.b({events:c.splice(0,c.length)});navigator.sendBeacon(t,f);}}));}catch{}}return m(),y(),{send(f){if(!s&&(c.push(f),c.length>=a)){let p=c.splice(0,a);d(p).catch(()=>{});}},async flush(){for(i&&(clearInterval(i),i=void 0);c.length>0;){let f=c.splice(0,a);await d(f);}}}}function U(e){return new Promise(t=>setTimeout(t,e))}var se="@uncaughtdev/core",ae="0.1.0",R;function ce(e){return R=new E(e),R}function ue(){return R}var E=class{constructor(t){this.config={enabled:true,debug:false,maxBreadcrumbs:20,maxEventsPerMinute:30,...t},this.breadcrumbs=S(this.config.maxBreadcrumbs),this.transport=_(this.config),this.rateLimiter=x(this.config.maxEventsPerMinute),this.sessionId=chunkE76GW6KF_js.a();}captureError(t,r){try{if(!this.config.enabled)return;let n=fe(t);if(r?.componentStack&&(n.componentStack=r.componentStack),this.shouldIgnore(n.message)){this.debugLog("Event ignored by ignoreErrors filter");return}let a=v(n);if(!this.rateLimiter.shouldAllow(a)){this.debugLog(`Rate-limited: ${a}`);return}let o=this.breadcrumbs.getAll(),c=P(),i=chunkE76GW6KF_js.a(),s={eventId:i,timestamp:chunkE76GW6KF_js.c(),projectKey:this.config.projectKey,level:r?.level??"error",fingerprint:a,error:n,breadcrumbs:o,request:r?.request,operation:r?.operation,environment:c,user:this.user?{...this.user,sessionId:this.sessionId}:{sessionId:this.sessionId},fixPrompt:"",sdk:{name:se,version:ae}};if(s=k(s,this.config.sanitizeKeys),s.fixPrompt=T(s),this.config.beforeSend){let u=this.config.beforeSend(s);if(u===null){this.debugLog("Event dropped by beforeSend");return}s=u;}return this.transport.send(s),this.debugLog(`Captured event: ${i} (${a})`),i}catch(n){this.debugLog("captureError failed:",n);return}}captureMessage(t,r="info"){try{return this.captureError(new Error(t),{level:r})}catch(n){this.debugLog("captureMessage failed:",n);return}}addBreadcrumb(t){try{if(!this.config.enabled)return;this.breadcrumbs.add(t);}catch(r){this.debugLog("addBreadcrumb failed:",r);}}setUser(t){try{this.user=t?{...t}:void 0;}catch(r){this.debugLog("setUser failed:",r);}}async flush(){try{await this.transport.flush();}catch(t){this.debugLog("flush failed:",t);}}shouldIgnore(t){let r=this.config.ignoreErrors;if(!r||r.length===0)return false;for(let n of r)if(typeof n=="string"){if(t.includes(n))return true}else if(n instanceof RegExp&&n.test(t))return true;return false}debugLog(...t){if(this.config.debug)try{console.debug("[uncaught]",...t);}catch{}}};function fe(e){if(e instanceof Error)return {message:e.message||String(e),type:e.constructor?.name||e.name||"Error",stack:e.stack,raw:e};if(typeof e=="string")return {message:e,type:"StringError",stack:new Error(e).stack,raw:e};if(e!==null&&typeof e=="object"){let t=e;return {message:String(t.message??t.reason??JSON.stringify(e)),type:String(t.name??t.type??"ObjectError"),stack:typeof t.stack=="string"?t.stack:void 0,raw:e}}return {message:String(e),type:"UnknownError",raw:e}}Object.defineProperty(exports,"generateUUID",{enumerable:true,get:function(){return chunkE76GW6KF_js.a}});Object.defineProperty(exports,"isoTimestamp",{enumerable:true,get:function(){return chunkE76GW6KF_js.c}});Object.defineProperty(exports,"safeStringify",{enumerable:true,get:function(){return chunkE76GW6KF_js.b}});Object.defineProperty(exports,"truncate",{enumerable:true,get:function(){return chunkE76GW6KF_js.d}});exports.UncaughtClient=E;exports.buildFixPrompt=T;exports.createBreadcrumbStore=S;exports.createRateLimiter=x;exports.createTransport=_;exports.detectEnvironment=P;exports.generateFingerprint=v;exports.getClient=ue;exports.initUncaught=ce;exports.sanitize=k;//# sourceMappingURL=index.js.map
|
|
29
|
+
//# sourceMappingURL=index.js.map
|