workers-sentinel 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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +341 -0
  3. package/dist/README.md +1 -0
  4. package/dist/dashboard/assets/EventDetail-DgqYLyiF.js +1 -0
  5. package/dist/dashboard/assets/IssueDetail-CjG-J6cD.js +1 -0
  6. package/dist/dashboard/assets/Issues-Cfnqb_a0.js +33 -0
  7. package/dist/dashboard/assets/Layout-Edli6x7p.js +1 -0
  8. package/dist/dashboard/assets/Login-DHYJjZXJ.js +1 -0
  9. package/dist/dashboard/assets/ProjectCreate-D5GeKxHe.js +33 -0
  10. package/dist/dashboard/assets/ProjectSettings-6OwGPcir.js +1 -0
  11. package/dist/dashboard/assets/Projects-Bth0OyBs.js +1 -0
  12. package/dist/dashboard/assets/Register-C3zVYtDV.js +1 -0
  13. package/dist/dashboard/assets/index-7dRN2NZv.js +30 -0
  14. package/dist/dashboard/assets/index-JMAa_1Q2.css +1 -0
  15. package/dist/dashboard/assets/projects-BUj8O17i.js +1 -0
  16. package/dist/dashboard/index.html +14 -0
  17. package/dist/durable-objects/auth-state.d.ts +28 -0
  18. package/dist/durable-objects/auth-state.d.ts.map +1 -0
  19. package/dist/durable-objects/project-state.d.ts +23 -0
  20. package/dist/durable-objects/project-state.d.ts.map +1 -0
  21. package/dist/index.d.ts +13 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +4774 -0
  24. package/dist/index.js.map +8 -0
  25. package/dist/lib/envelope-parser.d.ts +38 -0
  26. package/dist/lib/envelope-parser.d.ts.map +1 -0
  27. package/dist/lib/fingerprint.d.ts +23 -0
  28. package/dist/lib/fingerprint.d.ts.map +1 -0
  29. package/dist/middleware/auth.d.ts +10 -0
  30. package/dist/middleware/auth.d.ts.map +1 -0
  31. package/dist/routes/auth.d.ts +6 -0
  32. package/dist/routes/auth.d.ts.map +1 -0
  33. package/dist/routes/events.d.ts +11 -0
  34. package/dist/routes/events.d.ts.map +1 -0
  35. package/dist/routes/ingestion.d.ts +6 -0
  36. package/dist/routes/ingestion.d.ts.map +1 -0
  37. package/dist/routes/issues.d.ts +11 -0
  38. package/dist/routes/issues.d.ts.map +1 -0
  39. package/dist/routes/projects.d.ts +11 -0
  40. package/dist/routes/projects.d.ts.map +1 -0
  41. package/dist/rpc.d.ts +59 -0
  42. package/dist/rpc.d.ts.map +1 -0
  43. package/dist/types.d.ts +166 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/package.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gabriel Massadas
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,341 @@
1
+ <div align="center">
2
+ <a href="#">
3
+ <h1 style="font-size: 4rem;">🛡️</h1>
4
+ <h1>Workers Sentinel</h1>
5
+ </a>
6
+ </div>
7
+
8
+ <p align="center">
9
+ <em>A self-hosted, Sentry-compatible error tracking system running entirely on Cloudflare Workers</em>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://github.com/G4brym/workers-sentinel/commits/main" target="_blank">
14
+ <img src="https://img.shields.io/github/commit-activity/m/G4brym/workers-sentinel?label=Commits&style=social" alt="Workers Sentinel Commits">
15
+ </a>
16
+ <a href="https://github.com/G4brym/workers-sentinel/issues" target="_blank">
17
+ <img src="https://img.shields.io/github/issues/G4brym/workers-sentinel?style=social" alt="Issues">
18
+ </a>
19
+ <a href="https://github.com/G4brym/workers-sentinel/blob/main/LICENSE" target="_blank">
20
+ <img src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=social" alt="Software License">
21
+ </a>
22
+ </p>
23
+
24
+ # Workers Sentinel
25
+
26
+ Workers Sentinel is a lightweight, self-hosted error tracking and monitoring solution that runs entirely on your Cloudflare account. It accepts events using the Sentry SDK wire format, allowing you to use existing Sentry SDKs by simply changing the DSN endpoint. All your error data is stored securely in SQLite-backed Durable Objects, giving you full control over your information.
27
+
28
+ [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/G4brym/workers-sentinel/tree/main/template)
29
+
30
+ ## Table of Contents
31
+
32
+ - [Overview](#overview)
33
+ - [Why Workers Sentinel?](#why-workers-sentinel)
34
+ - [Key Features](#key-features)
35
+ - [Prerequisites](#prerequisites)
36
+ - [Getting Started](#getting-started)
37
+ - [SDK Configuration](#sdk-configuration)
38
+ - [Architecture](#architecture)
39
+ - [Roadmap & Future Enhancements](#roadmap--future-enhancements)
40
+ - [Known Limitations](#known-limitations)
41
+ - [Contributing](#contributing)
42
+ - [License](#license)
43
+
44
+ ## Overview
45
+
46
+ Workers Sentinel gives you a private, self-hosted error tracking solution with a modern web dashboard. By leveraging the Cloudflare ecosystem, it offers a cost-effective and scalable alternative to hosted error tracking services. All your data is stored in your own Durable Objects with SQLite storage, giving you complete control and privacy.
47
+
48
+ ## Why Workers Sentinel?
49
+
50
+ **🔒 Privacy First**
51
+ - All data stays in YOUR Cloudflare account
52
+ - No third-party tracking or data sharing
53
+ - You control your error data completely
54
+
55
+ **💰 Cost-Effective**
56
+ - Runs on Cloudflare's generous free tier
57
+ - Pay only for what you use beyond free limits
58
+ - No monthly subscription fees
59
+
60
+ **⚡ Performance**
61
+ - Built on Cloudflare's global edge network
62
+ - Fast error ingestion worldwide
63
+ - Serverless architecture scales automatically
64
+
65
+ **🔌 SDK Compatible**
66
+ - Works with existing Sentry SDKs
67
+ - Just change your DSN endpoint
68
+ - Supports JavaScript, Python, Go, and more
69
+
70
+ **🎨 Modern Dashboard**
71
+ - Clean, intuitive interface
72
+ - Stack trace visualization
73
+ - Issue grouping and management
74
+
75
+ **🛠️ Easy Setup**
76
+ - Deploy with one click
77
+ - Automatic project creation
78
+ - Smart authentication setup
79
+
80
+ ## Key Features
81
+
82
+ - **🔌 Sentry SDK Compatible**: Use existing Sentry SDKs by changing only the DSN endpoint
83
+ - **🔒 Secure & Private**: Self-hosted on your Cloudflare account with no third-party data access
84
+ - **🔐 Smart Authentication**: Automatic first-user admin setup with session-based auth
85
+ - **📊 Issue Grouping**: Automatic fingerprinting groups similar errors into issues
86
+ - **📈 Event Statistics**: Track error frequency with hourly aggregations
87
+ - **🔍 Stack Traces**: Full stack trace visualization with code context
88
+ - **👥 User Tracking**: See how many users are affected by each issue
89
+ - **🏷️ Tags & Context**: View tags, breadcrumbs, and contextual data
90
+ - **✅ Issue Management**: Mark issues as resolved or ignored
91
+ - **🌐 Multi-Project**: Create multiple projects with isolated data storage
92
+
93
+ ## Prerequisites
94
+
95
+ Before deploying Workers Sentinel, make sure you have:
96
+
97
+ - **Cloudflare Account** - [Sign up for free](https://dash.cloudflare.com/sign-up)
98
+ - **Node.js 20+** - For local development (not required for one-click deployment)
99
+
100
+ **Cloudflare Services Used:**
101
+ - Workers (Compute)
102
+ - Durable Objects with SQLite (State management)
103
+ - Workers Assets (Dashboard hosting)
104
+
105
+ All these services have generous free tiers sufficient for most use cases.
106
+
107
+ ## Getting Started
108
+
109
+ ### One-Click Deploy
110
+
111
+ Use the "Deploy to Cloudflare" button above, or run:
112
+
113
+ ```bash
114
+ npm create cloudflare@latest -- --template=https://github.com/G4brym/workers-sentinel/tree/main/template
115
+ ```
116
+
117
+ ### Manual Deployment
118
+
119
+ ```bash
120
+ # Clone the repository
121
+ git clone https://github.com/G4brym/workers-sentinel.git
122
+ cd workers-sentinel
123
+
124
+ # Install dependencies
125
+ pnpm install
126
+
127
+ # Build and deploy
128
+ pnpm deploy
129
+ ```
130
+
131
+ ### First-Time Setup
132
+
133
+ 1. **Deploy your worker** to Cloudflare
134
+ 2. **Visit your worker URL** in a browser
135
+ 3. **Register the first user** - this becomes your admin account
136
+ 4. **Create your first project** - you'll receive a DSN
137
+ 5. **Configure your Sentry SDK** with the DSN
138
+
139
+ ## SDK Configuration
140
+
141
+ Workers Sentinel is compatible with official Sentry SDKs. Simply use your Sentinel DSN instead of a Sentry DSN.
142
+
143
+ ### Cloudflare Workers (Service Binding with RPC)
144
+
145
+ For Cloudflare Workers, you can use [service bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) with RPC for optimal performance. This routes requests internally within Cloudflare's network, avoiding external HTTP roundtrips and reducing latency.
146
+
147
+ **Step 1:** Add a service binding to your worker's `wrangler.jsonc`:
148
+
149
+ ```jsonc
150
+ {
151
+ "name": "my-worker",
152
+ "main": "src/index.ts",
153
+ "compatibility_flags": ["nodejs_als"],
154
+ "services": [
155
+ { "binding": "SENTINEL", "service": "workers-sentinel", "entrypoint": "SentinelRpc" }
156
+ ]
157
+ }
158
+ ```
159
+
160
+ **Step 2:** Initialize Sentry with the RPC transport:
161
+
162
+ ```typescript
163
+ import * as Sentry from '@sentry/cloudflare';
164
+ import { waitUntil } from 'cloudflare:workers';
165
+
166
+ const DSN = 'https://<public_key>@<your-worker>.workers.dev/<project_id>';
167
+
168
+ export default Sentry.withSentry(
169
+ (env: Env) => ({
170
+ dsn: DSN,
171
+ transport: () => ({
172
+ send: async (envelope) => {
173
+ const rpcPromise = env.SENTINEL.captureEnvelope(DSN, envelope);
174
+ waitUntil(rpcPromise);
175
+ const result = await rpcPromise;
176
+ return { statusCode: result.status };
177
+ },
178
+ flush: async () => true,
179
+ }),
180
+ }),
181
+ {
182
+ async fetch(request, env, ctx) {
183
+ // Your worker code here
184
+ return new Response('Hello!');
185
+ },
186
+ }
187
+ );
188
+ ```
189
+
190
+ The `waitUntil` call ensures the RPC completes even after the HTTP response returns. The `captureEnvelope` method takes the DSN and envelope, handling authentication and ingestion internally.
191
+
192
+ ### JavaScript / Browser
193
+
194
+ ```javascript
195
+ import * as Sentry from '@sentry/browser';
196
+
197
+ Sentry.init({
198
+ dsn: 'https://<public_key>@<your-worker>.workers.dev/<project_id>',
199
+ });
200
+ ```
201
+
202
+ ### Node.js
203
+
204
+ ```javascript
205
+ const Sentry = require('@sentry/node');
206
+
207
+ Sentry.init({
208
+ dsn: 'https://<public_key>@<your-worker>.workers.dev/<project_id>',
209
+ });
210
+ ```
211
+
212
+ ### Python
213
+
214
+ ```python
215
+ import sentry_sdk
216
+
217
+ sentry_sdk.init(
218
+ dsn="https://<public_key>@<your-worker>.workers.dev/<project_id>",
219
+ )
220
+ ```
221
+
222
+ ### Go
223
+
224
+ ```go
225
+ import "github.com/getsentry/sentry-go"
226
+
227
+ sentry.Init(sentry.ClientOptions{
228
+ Dsn: "https://<public_key>@<your-worker>.workers.dev/<project_id>",
229
+ })
230
+ ```
231
+
232
+ The DSN is displayed when you create a project in the dashboard, or you can find it in the project settings.
233
+
234
+ ## Architecture
235
+
236
+ Workers Sentinel is built with modern web technologies:
237
+
238
+ **Backend (Worker):**
239
+ - **Hono** - Fast, lightweight web framework
240
+ - **Cloudflare Durable Objects** - Distributed state with SQLite storage
241
+ - **Sentry Envelope Parser** - Compatible with Sentry SDK wire format
242
+
243
+ **Frontend (Dashboard):**
244
+ - **Vue.js 3** - Progressive JavaScript framework
245
+ - **TypeScript** - Type-safe development
246
+ - **Tailwind CSS** - Utility-first styling
247
+ - **Pinia** - State management
248
+ - **Vite** - Fast build tooling
249
+
250
+ **Data Architecture:**
251
+ - **AuthState DO** (singleton) - Users, sessions, project registry
252
+ - **ProjectState DO** (per-project) - Issues, events, statistics
253
+
254
+ Each project has its own isolated Durable Object with SQLite storage, ensuring data isolation and scalability.
255
+
256
+ ## Roadmap & Future Enhancements
257
+
258
+ Planned features for future releases:
259
+
260
+ - [ ] Source map support for JavaScript errors
261
+ - [ ] Release tracking and deployment correlation
262
+ - [ ] Performance monitoring (transactions, spans)
263
+ - [ ] Alerting integrations (webhook, email)
264
+ - [ ] Session replay support
265
+ - [ ] Team/organization support
266
+ - [ ] Issue assignment
267
+ - [ ] Search functionality
268
+ - [ ] Time-series charts
269
+ - [ ] Rate limiting per project
270
+ - [ ] Event retention policies
271
+
272
+ ## Known Limitations
273
+
274
+ **Current Limitations:**
275
+ - No source map support yet (stack traces show minified code)
276
+ - No performance monitoring (error tracking only)
277
+ - No alerting/notifications
278
+ - Single-user admin (no team management yet)
279
+
280
+ **Sentry Feature Parity:**
281
+ Workers Sentinel focuses on core error tracking. Advanced Sentry features like:
282
+ - Session replay
283
+ - Performance monitoring
284
+ - Profiling
285
+ - Crons monitoring
286
+
287
+ Are not currently supported but may be added in future versions.
288
+
289
+ **Browser Compatibility:**
290
+ - Modern browsers required (Chrome 90+, Firefox 88+, Safari 14+)
291
+ - JavaScript must be enabled
292
+ - Cookies must be enabled for authentication
293
+
294
+ ## Contributing
295
+
296
+ We welcome contributions from the community!
297
+
298
+ **🐛 Bug Reports**
299
+ - Use the [GitHub Issues](https://github.com/G4brym/workers-sentinel/issues) page
300
+ - Include reproduction steps
301
+ - Specify your environment
302
+
303
+ **✨ Feature Requests**
304
+ - Check existing issues first
305
+ - Explain the use case and benefit
306
+
307
+ **💻 Code Contributions**
308
+ 1. Fork the repository
309
+ 2. Create a feature branch
310
+ 3. Make your changes
311
+ 4. Submit a Pull Request
312
+
313
+ **Development Setup:**
314
+ ```bash
315
+ # Clone and install
316
+ git clone https://github.com/G4brym/workers-sentinel.git
317
+ cd workers-sentinel
318
+ pnpm install
319
+
320
+ # Start development (runs wrangler dev)
321
+ pnpm dev
322
+
323
+ # Build dashboard
324
+ pnpm --filter @workers-sentinel/dashboard build
325
+
326
+ # Typecheck
327
+ pnpm typecheck
328
+
329
+ # Lint
330
+ pnpm lint
331
+ ```
332
+
333
+ ## License
334
+
335
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
336
+
337
+ ---
338
+
339
+ **Made with ❤️ for the self-hosted community**
340
+
341
+ If you find Workers Sentinel useful, please consider giving it a ⭐ on GitHub!
package/dist/README.md ADDED
@@ -0,0 +1 @@
1
+ This folder contains the built output assets for the worker "workers-sentinel" generated at 2026-01-18T21:56:03.666Z.
@@ -0,0 +1 @@
1
+ import{d as b,k as x,l as w,r,x as h,c as l,h as m,a as t,t as v,e as E,n as I,w as f,f as y,R as g,y as N,b as _,o as a}from"./index-7dRN2NZv.js";const $={key:0,class:"text-center py-12"},B={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},V={key:2},j={class:"mb-6"},C={class:"flex items-center space-x-2 text-sm text-gray-500 mb-2"},R={class:"text-xl font-bold text-gray-900 dark:text-white"},D={class:"card p-4"},S={class:"text-xs font-mono overflow-x-auto whitespace-pre-wrap text-gray-900 dark:text-gray-100"},L=b({__name:"EventDetail",setup(F){const c=w(),n=x(()=>c.params.slug),p=x(()=>c.params.eventId),u=r(null),d=r(null),i=r(!0),o=r(null);async function k(){i.value=!0,o.value=null;try{const s=await N.get(`/api/projects/${n.value}/events/${p.value}`);u.value=s.event,d.value=s.issueId}catch(s){o.value=s instanceof Error?s.message:"Failed to load event"}finally{i.value=!1}}return h(()=>k()),(s,e)=>(a(),l("div",null,[i.value?(a(),l("div",$,[...e[0]||(e[0]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1)])])):o.value?(a(),l("div",B,v(o.value),1)):u.value?(a(),l("div",V,[t("div",j,[t("div",C,[E(y(g),{to:`/projects/${n.value}/issues`,class:"hover:text-gray-700"},{default:f(()=>[...e[1]||(e[1]=[_("Issues",-1)])]),_:1},8,["to"]),e[3]||(e[3]=t("span",null,"/",-1)),d.value?(a(),I(y(g),{key:0,to:`/projects/${n.value}/issues/${d.value}`,class:"hover:text-gray-700"},{default:f(()=>[...e[2]||(e[2]=[_(" Issue ",-1)])]),_:1},8,["to"])):m("",!0),e[4]||(e[4]=t("span",null,"/",-1)),e[5]||(e[5]=t("span",null,"Event",-1))]),t("h1",R," Event "+v(p.value),1)]),t("div",D,[t("pre",S,v(JSON.stringify(u.value,null,2)),1)])])):m("",!0)]))}});export{L as default};
@@ -0,0 +1 @@
1
+ import{d as T,k as E,l as B,r as v,x as N,c as a,h as l,a as t,t as n,e as V,w as M,f as P,R as q,b as c,q as F,F as x,p as y,y as $,o}from"./index-7dRN2NZv.js";const z={key:0,class:"text-center py-12"},A={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},O={key:2},U={class:"mb-6"},G={class:"flex items-center space-x-2 text-sm text-gray-500 mb-2"},H={class:"text-xl font-bold text-gray-900 dark:text-white mb-2"},J={key:0,class:"text-sm text-gray-500"},K={class:"flex items-center justify-between mb-6"},Q={class:"flex items-center space-x-4"},W={class:"flex items-center space-x-6 text-sm text-gray-500"},X={class:"font-medium text-gray-900 dark:text-white"},Y={key:0},Z={class:"font-medium text-gray-900 dark:text-white"},tt={class:"font-medium text-gray-900 dark:text-white"},et={key:0,class:"mb-4"},st=["value"],at=["value"],ot={key:0},nt={class:"grid grid-cols-1 lg:grid-cols-3 gap-6"},rt={class:"lg:col-span-2 space-y-4"},lt={class:"card"},it={key:0,class:"p-4 text-gray-500"},dt={key:1,class:"divide-y divide-gray-200 dark:divide-gray-700"},ut=["onClick"],ct={class:"flex items-start justify-between"},vt={class:"flex-1 min-w-0"},xt={class:"flex items-center space-x-2"},yt={key:0,class:"w-2 h-2 bg-primary-500 rounded-full",title:"In-app frame"},pt={key:1,class:"w-2 h-2 bg-gray-300 rounded-full",title:"System frame"},gt={class:"text-sm font-medium text-gray-900 dark:text-white truncate"},_t={class:"text-xs text-gray-500 mt-1 truncate"},kt={key:0},mt={key:1},ht={key:0,class:"bg-gray-900 p-4"},bt={class:"text-xs font-mono overflow-x-auto"},ft={key:1,class:"block text-white bg-error-900/50 -mx-4 px-4"},wt={key:0,class:"card"},Ct={class:"divide-y divide-gray-200 dark:divide-gray-700 max-h-96 overflow-y-auto"},It={class:"flex items-center justify-between"},$t={class:"flex items-center space-x-2"},St={class:"badge badge-info"},jt={class:"text-gray-900 dark:text-white"},Et={key:0,class:"text-xs text-gray-400"},Ft={class:"space-y-4"},Rt={class:"card p-4"},Lt={class:"space-y-2 text-sm"},Dt={class:"text-gray-900 dark:text-white font-mono text-xs"},Tt={class:"text-gray-900 dark:text-white"},Bt={key:0},Nt={class:"text-gray-900 dark:text-white"},Vt={key:1},Mt={class:"text-gray-900 dark:text-white font-mono text-xs"},Pt={key:0,class:"card p-4"},qt={class:"space-y-2 text-sm"},zt={key:0},At={class:"text-gray-900 dark:text-white font-mono text-xs"},Ot={key:1},Ut={class:"text-gray-900 dark:text-white"},Gt={key:2},Ht={class:"text-gray-900 dark:text-white font-mono"},Jt={key:1,class:"card p-4"},Kt={class:"flex flex-wrap gap-2"},Qt={class:"text-gray-500"},Wt={class:"ml-1 text-gray-900 dark:text-white"},Zt=T({__name:"IssueDetail",setup(Xt){const S=B(),k=E(()=>S.params.slug),f=E(()=>S.params.issueId),d=v(null),m=v([]),r=v(null),w=v(!0),h=v(null),p=v(new Set);async function R(){w.value=!0,h.value=null;try{const[i,e]=await Promise.all([$.get(`/api/projects/${k.value}/issues/${f.value}`),$.get(`/api/projects/${k.value}/issues/${f.value}/events?limit=10`)]);d.value=i.issue,m.value=e.events,r.value=e.events[0]||null}catch(i){h.value=i instanceof Error?i.message:"Failed to load issue"}finally{w.value=!1}}async function C(i){if(d.value)try{await $.patch(`/api/projects/${k.value}/issues/${f.value}`,{status:i}),d.value.status=i}catch(e){console.error("Failed to update status:",e)}}function I(i){return new Date(i).toLocaleString()}function L(i){p.value.has(i)?p.value.delete(i):p.value.add(i)}function j(){var e,g,s,u;const i=(s=(g=(e=r.value)==null?void 0:e.exception)==null?void 0:g.values)==null?void 0:s[0];return(u=i==null?void 0:i.stacktrace)!=null&&u.frames?[...i.stacktrace.frames].reverse():[]}function D(i){switch(i){case"fatal":case"error":return"text-error-600 dark:text-error-400";case"warning":return"text-warning-600 dark:text-warning-400";default:return"text-primary-600 dark:text-primary-400"}}return N(()=>R()),(i,e)=>{var g;return o(),a("div",null,[w.value?(o(),a("div",z,[...e[4]||(e[4]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1)])])):h.value?(o(),a("div",A,n(h.value),1)):d.value&&r.value?(o(),a("div",O,[t("div",U,[t("div",G,[V(P(q),{to:`/projects/${k.value}/issues`,class:"hover:text-gray-700"},{default:M(()=>[...e[5]||(e[5]=[c(" Issues ",-1)])]),_:1},8,["to"]),e[6]||(e[6]=t("span",null,"/",-1)),t("span",null,n(d.value.metadata.type),1)]),t("h1",H,[t("span",{class:F(D(d.value.level))},n(d.value.metadata.type)+":",3),c(" "+n(d.value.metadata.value),1)]),d.value.culprit?(o(),a("p",J,n(d.value.culprit),1)):l("",!0)]),t("div",K,[t("div",Q,[d.value.status!=="resolved"?(o(),a("button",{key:0,class:"btn btn-secondary",onClick:e[0]||(e[0]=s=>C("resolved"))}," Resolve ")):l("",!0),d.value.status!=="ignored"?(o(),a("button",{key:1,class:"btn btn-secondary",onClick:e[1]||(e[1]=s=>C("ignored"))}," Ignore ")):l("",!0),d.value.status!=="unresolved"?(o(),a("button",{key:2,class:"btn btn-secondary",onClick:e[2]||(e[2]=s=>C("unresolved"))}," Reopen ")):l("",!0)]),t("div",W,[t("div",null,[t("span",X,n(d.value.count.toLocaleString()),1),e[7]||(e[7]=c(" events ",-1))]),d.value.userCount>0?(o(),a("div",Y,[t("span",Z,n(d.value.userCount.toLocaleString()),1),e[8]||(e[8]=c(" users ",-1))])):l("",!0),t("div",null,[e[9]||(e[9]=c(" First seen ",-1)),t("span",tt,n(I(d.value.firstSeen)),1)])])]),m.value.length>1?(o(),a("div",et,[e[10]||(e[10]=t("label",{class:"label"},"Event",-1)),t("select",{class:"input w-auto",value:r.value.event_id,onChange:e[3]||(e[3]=s=>r.value=m.value.find(u=>u.event_id===s.target.value)||null)},[(o(!0),a(x,null,y(m.value,s=>(o(),a("option",{key:s.event_id,value:s.event_id},[c(n(I(s.timestamp))+" ",1),s.environment?(o(),a("span",ot," - "+n(s.environment),1)):l("",!0)],8,at))),128))],40,st)])):l("",!0),t("div",nt,[t("div",rt,[t("div",lt,[e[12]||(e[12]=t("div",{class:"p-4 border-b border-gray-200 dark:border-gray-700"},[t("h2",{class:"font-semibold text-gray-900 dark:text-white"},"Stack Trace")],-1)),j().length===0?(o(),a("div",it," No stack trace available ")):(o(),a("div",dt,[(o(!0),a(x,null,y(j(),(s,u)=>(o(),a("div",{key:u,class:"group"},[t("button",{class:"w-full p-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors",onClick:b=>L(u)},[t("div",ct,[t("div",vt,[t("div",xt,[s.in_app!==!1?(o(),a("span",yt)):(o(),a("span",pt)),t("code",gt,n(s.function||"(anonymous)"),1)]),t("p",_t,[c(n(s.filename)+" ",1),s.lineno?(o(),a("span",kt,":"+n(s.lineno),1)):l("",!0),s.colno?(o(),a("span",mt,":"+n(s.colno),1)):l("",!0)])]),(o(),a("svg",{class:F(["w-5 h-5 text-gray-400 transition-transform",{"rotate-180":p.value.has(u)}]),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...e[11]||(e[11]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 9l-7 7-7-7"},null,-1)])],2))])],8,ut),p.value.has(u)&&(s.context_line||s.pre_context||s.post_context)?(o(),a("div",ht,[t("pre",bt,[s.pre_context?(o(!0),a(x,{key:0},y(s.pre_context,(b,_)=>(o(),a("code",{key:"pre-"+_,class:"block text-gray-500"},n((s.lineno||0)-s.pre_context.length+_)+" "+n(b),1))),128)):l("",!0),s.context_line?(o(),a("code",ft,n(s.lineno)+" "+n(s.context_line),1)):l("",!0),s.post_context?(o(!0),a(x,{key:2},y(s.post_context,(b,_)=>(o(),a("code",{key:"post-"+_,class:"block text-gray-500"},n((s.lineno||0)+_+1)+" "+n(b),1))),128)):l("",!0)])])):l("",!0)]))),128))]))]),(g=r.value.breadcrumbs)!=null&&g.length?(o(),a("div",wt,[e[13]||(e[13]=t("div",{class:"p-4 border-b border-gray-200 dark:border-gray-700"},[t("h2",{class:"font-semibold text-gray-900 dark:text-white"},"Breadcrumbs")],-1)),t("div",Ct,[(o(!0),a(x,null,y(r.value.breadcrumbs,(s,u)=>(o(),a("div",{key:u,class:"p-3 text-sm"},[t("div",It,[t("div",$t,[t("span",St,n(s.category||s.type||"default"),1),t("span",jt,n(s.message),1)]),s.timestamp?(o(),a("span",Et,n(new Date(s.timestamp).toLocaleTimeString()),1)):l("",!0)])]))),128))])])):l("",!0)]),t("div",Ft,[t("div",Rt,[e[18]||(e[18]=t("h3",{class:"font-semibold text-gray-900 dark:text-white mb-3"},"Event Info",-1)),t("dl",Lt,[t("div",null,[e[14]||(e[14]=t("dt",{class:"text-gray-500"},"Event ID",-1)),t("dd",Dt,n(r.value.event_id),1)]),t("div",null,[e[15]||(e[15]=t("dt",{class:"text-gray-500"},"Timestamp",-1)),t("dd",Tt,n(I(r.value.timestamp)),1)]),r.value.environment?(o(),a("div",Bt,[e[16]||(e[16]=t("dt",{class:"text-gray-500"},"Environment",-1)),t("dd",Nt,n(r.value.environment),1)])):l("",!0),r.value.release?(o(),a("div",Vt,[e[17]||(e[17]=t("dt",{class:"text-gray-500"},"Release",-1)),t("dd",Mt,n(r.value.release),1)])):l("",!0)])]),r.value.user?(o(),a("div",Pt,[e[22]||(e[22]=t("h3",{class:"font-semibold text-gray-900 dark:text-white mb-3"},"User",-1)),t("dl",qt,[r.value.user.id?(o(),a("div",zt,[e[19]||(e[19]=t("dt",{class:"text-gray-500"},"ID",-1)),t("dd",At,n(r.value.user.id),1)])):l("",!0),r.value.user.email?(o(),a("div",Ot,[e[20]||(e[20]=t("dt",{class:"text-gray-500"},"Email",-1)),t("dd",Ut,n(r.value.user.email),1)])):l("",!0),r.value.user.ip_address?(o(),a("div",Gt,[e[21]||(e[21]=t("dt",{class:"text-gray-500"},"IP Address",-1)),t("dd",Ht,n(r.value.user.ip_address),1)])):l("",!0)])])):l("",!0),r.value.tags&&Object.keys(r.value.tags).length>0?(o(),a("div",Jt,[e[23]||(e[23]=t("h3",{class:"font-semibold text-gray-900 dark:text-white mb-3"},"Tags",-1)),t("div",Kt,[(o(!0),a(x,null,y(r.value.tags,(s,u)=>(o(),a("span",{key:u,class:"inline-flex items-center px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-xs"},[t("span",Qt,n(u)+":",1),t("span",Wt,n(s),1)]))),128))])])):l("",!0)])])])):l("",!0)])}}});export{Zt as default};
@@ -0,0 +1,33 @@
1
+ import{d as N,k as h,l as R,r as i,x as E,m as b,c as a,a as s,h as c,i as I,A as P,t as n,z as S,F as C,p as T,y as k,n as U,w as B,q as F,g as z,f as A,R as V,o as r}from"./index-7dRN2NZv.js";import{u as W}from"./projects-BUj8O17i.js";const H={class:"flex items-center space-x-4 mb-6"},K={key:0,class:"text-center py-12"},Q={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},G={key:2,class:"max-w-2xl mx-auto"},J={class:"bg-gray-50 dark:bg-gray-700 rounded-lg p-6 mt-6"},O={class:"font-medium text-gray-900 dark:text-white mb-4"},X={class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},Y={key:1,class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},Z={key:3,class:"card divide-y divide-gray-200 dark:divide-gray-700"},tt={class:"flex items-start justify-between"},et={class:"flex-1 min-w-0"},st={class:"flex items-center space-x-2"},ot={class:"text-sm font-medium text-gray-900 dark:text-white truncate"},rt={class:"mt-1 text-sm text-gray-600 dark:text-gray-300 truncate"},at={key:0,class:"mt-1 text-xs text-gray-400 truncate"},nt={class:"ml-4 flex flex-col items-end text-sm"},lt={class:"flex items-center space-x-4 text-gray-500"},it={title:"Events"},ut={key:0,title:"Users"},dt={class:"text-xs text-gray-400 mt-1"},ct=["onClick"],gt=["onClick"],vt=["onClick"],pt={key:4,class:"mt-4 text-center"},xt=["disabled"],yt={key:0},mt={key:1},_t=N({__name:"Issues",setup(ft){const q=R(),$=W(),u=h(()=>q.params.slug),j=h(()=>$.projects.find(o=>o.slug===u.value)),_=h(()=>{var o;return((o=j.value)==null?void 0:o.platform)==="cloudflare-workers"}),d=i([]),g=i(!0),p=i(null),v=i("unresolved"),w=i(!1),m=i(),x=i("");async function y(o=!1){o||(g.value=!0),p.value=null;try{const e=new URLSearchParams;v.value&&e.set("status",v.value),o&&m.value&&e.set("cursor",m.value);const t=await k.get(`/api/projects/${u.value}/issues?${e}`);o?d.value=[...d.value,...t.issues]:d.value=t.issues,w.value=t.hasMore,m.value=t.nextCursor,t.issues.length===0&&!x.value&&L()}catch(e){p.value=e instanceof Error?e.message:"Failed to load issues"}finally{g.value=!1}}async function L(){try{const o=await k.get(`/api/projects/${u.value}`);x.value=o.dsn}catch{}}async function f(o,e){try{await k.patch(`/api/projects/${u.value}/issues/${o.id}`,{status:e}),o.status=e}catch(t){console.error("Failed to update status:",t)}}function D(o){const e=new Date(o),l=Math.floor((new Date().getTime()-e.getTime())/1e3);return l<60?"just now":l<3600?`${Math.floor(l/60)}m ago`:l<86400?`${Math.floor(l/3600)}h ago`:l<604800?`${Math.floor(l/86400)}d ago`:e.toLocaleDateString()}function M(o){switch(o){case"fatal":case"error":return"badge-error";case"warning":return"badge-warning";default:return"badge-info"}}return E(()=>y()),b(v,()=>y()),b(u,()=>y()),(o,e)=>(r(),a("div",null,[s("div",H,[I(s("select",{"onUpdate:modelValue":e[0]||(e[0]=t=>v.value=t),class:"input w-auto"},[...e[3]||(e[3]=[s("option",{value:""},"All Issues",-1),s("option",{value:"unresolved"},"Unresolved",-1),s("option",{value:"resolved"},"Resolved",-1),s("option",{value:"ignored"},"Ignored",-1)])],512),[[P,v.value]])]),g.value&&d.value.length===0?(r(),a("div",K,[...e[4]||(e[4]=[s("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1),s("p",{class:"mt-4 text-gray-500"},"Loading issues...",-1)])])):p.value?(r(),a("div",Q,n(p.value),1)):d.value.length===0?(r(),a("div",G,[e[7]||(e[7]=S('<div class="text-center py-8"><svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg><h3 class="mt-2 text-lg font-medium text-gray-900 dark:text-white">No issues yet</h3><p class="mt-1 text-sm text-gray-500"> Configure your SDK to start capturing errors. </p></div>',1)),s("div",J,[s("h3",O,n(_.value?"Quick Setup (Cloudflare Workers)":"Quick Setup"),1),_.value?(r(),a(C,{key:0},[e[5]||(e[5]=S(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 1. Install the SDK: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>npm install @sentry/cloudflare --save</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 2. Add a service binding to Sentinel and the compatibility flag in <code class="text-xs bg-gray-200 dark:bg-gray-600 px-1 rounded">wrangler.jsonc</code>: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>{
2
+ &quot;compatibility_flags&quot;: [&quot;nodejs_als&quot;],
3
+ &quot;services&quot;: [
4
+ { &quot;binding&quot;: &quot;SENTINEL&quot;, &quot;service&quot;: &quot;workers-sentinel&quot;, &quot;entrypoint&quot;: &quot;SentinelRpc&quot; }
5
+ ]
6
+ }</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 3. Initialize Sentry with the RPC transport: </p>`,5)),s("pre",X,[s("code",null,`import * as Sentry from "@sentry/cloudflare";
7
+ import { waitUntil } from "cloudflare:workers";
8
+
9
+ const DSN = "`+n(x.value)+`";
10
+
11
+ export default Sentry.withSentry(
12
+ (env: Env) => ({
13
+ dsn: DSN,
14
+ transport: () => ({
15
+ send: async (envelope) => {
16
+ const rpcPromise = env.SENTINEL.captureEnvelope(DSN, envelope);
17
+ waitUntil(rpcPromise);
18
+ const result = await rpcPromise;
19
+ return { statusCode: result.status };
20
+ },
21
+ flush: async () => true,
22
+ }),
23
+ }),
24
+ {
25
+ async fetch(request, env, ctx) {
26
+ return new Response("Hello World!");
27
+ },
28
+ }
29
+ );`,1)]),e[6]||(e[6]=s("p",{class:"text-xs text-gray-500 dark:text-gray-400 mt-3"}," Using a service binding with RPC routes requests internally within Cloudflare's network, avoiding external HTTP roundtrips and reducing latency. ",-1))],64)):(r(),a("pre",Y,[s("code",null,`import * as Sentry from '@sentry/browser';
30
+
31
+ Sentry.init({
32
+ dsn: '`+n(x.value)+`',
33
+ });`,1)]))])])):(r(),a("div",Z,[(r(!0),a(C,null,T(d.value,t=>(r(),U(A(V),{key:t.id,to:`/projects/${u.value}/issues/${t.id}`,class:"block p-4 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"},{default:B(()=>[s("div",tt,[s("div",et,[s("div",st,[s("span",{class:F(["badge",M(t.level)])},n(t.level),3),s("h3",ot,n(t.metadata.type),1)]),s("p",rt,n(t.metadata.value||t.title),1),t.culprit?(r(),a("p",at,n(t.culprit),1)):c("",!0)]),s("div",nt,[s("div",lt,[s("span",it,n(t.count.toLocaleString()),1),t.userCount>0?(r(),a("span",ut,n(t.userCount.toLocaleString())+" users ",1)):c("",!0)]),s("p",dt,n(D(t.lastSeen)),1)])]),s("div",{class:"mt-3 flex items-center space-x-2",onClick:e[1]||(e[1]=z(()=>{},["prevent"]))},[t.status!=="resolved"?(r(),a("button",{key:0,class:"text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",onClick:l=>f(t,"resolved")}," Resolve ",8,ct)):c("",!0),t.status!=="ignored"?(r(),a("button",{key:1,class:"text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",onClick:l=>f(t,"ignored")}," Ignore ",8,gt)):c("",!0),t.status!=="unresolved"?(r(),a("button",{key:2,class:"text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",onClick:l=>f(t,"unresolved")}," Reopen ",8,vt)):c("",!0)])]),_:2},1032,["to"]))),128))])),w.value?(r(),a("div",pt,[s("button",{class:"btn btn-secondary",disabled:g.value,onClick:e[2]||(e[2]=t=>y(!0))},[g.value?(r(),a("span",yt,"Loading...")):(r(),a("span",mt,"Load More"))],8,xt)])):c("",!0)]))}});export{_t as default};
@@ -0,0 +1 @@
1
+ import{d as S,u as B,k as w,l as V,m as L,c as a,a as e,e as n,w as l,f as t,R as i,n as R,h as k,F as N,p as P,t as d,q as p,s as A,j as H,b as y,o as r}from"./index-7dRN2NZv.js";import{u as $}from"./projects-BUj8O17i.js";const z={class:"flex h-screen"},F={class:"w-64 bg-gray-900 text-white flex flex-col"},M={class:"p-4 border-b border-gray-800"},q={class:"flex-1 overflow-y-auto p-4"},D={class:"flex items-center justify-between mb-4"},E={key:0,class:"text-gray-400 text-sm"},I={key:1,class:"space-y-1"},T={class:"truncate"},U={class:"p-4 border-t border-gray-800"},G={class:"flex items-center justify-between"},J={class:"flex items-center min-w-0"},K={class:"w-8 h-8 bg-gray-700 rounded-full flex items-center justify-center text-sm font-medium"},O={class:"ml-3 min-w-0"},Q={class:"text-sm font-medium truncate"},W={class:"text-xs text-gray-400 truncate"},X={class:"flex-1 overflow-y-auto"},Y={key:0,class:"bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-4"},Z={class:"flex items-center justify-between"},ee={class:"flex items-center space-x-4"},te={class:"text-xl font-semibold text-gray-900 dark:text-white"},se={class:"badge badge-info"},oe={class:"flex items-center space-x-2"},re={class:"p-6"},ie=S({__name:"Layout",setup(ae){const m=V(),j=H(),c=B(),o=$(),h=w(()=>m.params.slug),u=w(()=>o.projects.find(g=>g.slug===h.value));async function C(){await c.logout(),o.reset(),j.push("/login")}return L(()=>c.isAuthenticated,g=>{g&&!o.initialized&&o.loadProjects()},{immediate:!0}),(g,s)=>{var f,_,v,b;return r(),a("div",z,[e("aside",F,[e("div",M,[n(t(i),{to:"/",class:"flex items-center space-x-2"},{default:l(()=>[...s[0]||(s[0]=[e("div",{class:"w-8 h-8 bg-primary-500 rounded-lg flex items-center justify-center"},[e("span",{class:"text-lg font-bold"},"S")],-1),e("span",{class:"font-semibold"},"Sentinel",-1)])]),_:1})]),e("nav",q,[e("div",D,[s[2]||(s[2]=e("h2",{class:"text-xs font-semibold text-gray-400 uppercase tracking-wider"},"Projects",-1)),n(t(i),{to:"/projects/new",class:"text-gray-400 hover:text-white"},{default:l(()=>[...s[1]||(s[1]=[e("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1)])]),_:1})]),t(o).loading?(r(),a("div",E,"Loading...")):(r(),a("ul",I,[(r(!0),a(N,null,P(t(o).projects,x=>(r(),a("li",{key:x.id},[n(t(i),{to:`/projects/${x.slug}/issues`,class:p(["flex items-center px-3 py-2 rounded-lg text-sm",h.value===x.slug?"bg-gray-800 text-white":"text-gray-300 hover:bg-gray-800 hover:text-white"])},{default:l(()=>[e("span",T,d(x.name),1)]),_:2},1032,["to","class"])]))),128))])),!t(o).loading&&t(o).projects.length===0?(r(),R(t(i),{key:2,to:"/projects/new",class:"block text-center py-8 text-gray-400 hover:text-white"},{default:l(()=>[...s[3]||(s[3]=[y(" Create your first project ",-1)])]),_:1})):k("",!0)]),e("div",U,[e("div",G,[e("div",J,[e("div",K,d((_=(f=t(c).user)==null?void 0:f.name)==null?void 0:_.charAt(0).toUpperCase()),1),e("div",O,[e("p",Q,d((v=t(c).user)==null?void 0:v.name),1),e("p",W,d((b=t(c).user)==null?void 0:b.email),1)])]),e("button",{onClick:C,class:"text-gray-400 hover:text-white",title:"Logout"},[...s[4]||(s[4]=[e("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"})],-1)])])])])]),e("main",X,[u.value?(r(),a("header",Y,[e("div",Z,[e("div",ee,[e("h1",te,d(u.value.name),1),e("span",se,d(u.value.platform),1)]),e("div",oe,[n(t(i),{to:`/projects/${u.value.slug}/issues`,class:p(["px-3 py-1.5 text-sm rounded-lg",t(m).name==="issues"?"bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white":"text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"])},{default:l(()=>[...s[5]||(s[5]=[y(" Issues ",-1)])]),_:1},8,["to","class"]),n(t(i),{to:`/projects/${u.value.slug}/settings`,class:p(["px-3 py-1.5 text-sm rounded-lg",t(m).name==="project-settings"?"bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white":"text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"])},{default:l(()=>[...s[6]||(s[6]=[y(" Settings ",-1)])]),_:1},8,["to","class"])])])])):k("",!0),e("div",re,[n(t(A))])])])}}});export{ie as default};
@@ -0,0 +1 @@
1
+ import{d as f,u as g,r as d,c as r,a as t,b as u,e as b,w as v,f as a,R as w,g as k,h as S,t as h,i as p,v as m,j as _,o}from"./index-7dRN2NZv.js";const V={class:"min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 py-12 px-4 sm:px-6 lg:px-8"},N={class:"max-w-md w-full space-y-8"},j={class:"mt-2 text-center text-sm text-gray-600 dark:text-gray-400"},B={key:0,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg text-sm"},C={class:"space-y-4"},R=["disabled"],q={key:0},D={key:1},M=f({__name:"Login",setup(E){const c=_(),s=g(),l=d(""),n=d("");async function x(){await s.login(l.value,n.value)&&c.push("/")}return(y,e)=>(o(),r("div",V,[t("div",N,[t("div",null,[e[4]||(e[4]=t("div",{class:"mx-auto w-12 h-12 bg-primary-500 rounded-xl flex items-center justify-center"},[t("span",{class:"text-2xl font-bold text-white"},"S")],-1)),e[5]||(e[5]=t("h2",{class:"mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-white"}," Sign in to Sentinel ",-1)),t("p",j,[e[3]||(e[3]=u(" Or ",-1)),b(a(w),{to:"/register",class:"font-medium text-primary-600 hover:text-primary-500"},{default:v(()=>[...e[2]||(e[2]=[u(" create a new account ",-1)])]),_:1})])]),t("form",{class:"mt-8 space-y-6",onSubmit:k(x,["prevent"])},[a(s).error?(o(),r("div",B,h(a(s).error),1)):S("",!0),t("div",C,[t("div",null,[e[6]||(e[6]=t("label",{for:"email",class:"label"},"Email address",-1)),p(t("input",{id:"email","onUpdate:modelValue":e[0]||(e[0]=i=>l.value=i),type:"email",autocomplete:"email",required:"",class:"input",placeholder:"you@example.com"},null,512),[[m,l.value]])]),t("div",null,[e[7]||(e[7]=t("label",{for:"password",class:"label"},"Password",-1)),p(t("input",{id:"password","onUpdate:modelValue":e[1]||(e[1]=i=>n.value=i),type:"password",autocomplete:"current-password",required:"",class:"input",placeholder:"••••••••"},null,512),[[m,n.value]])])]),t("button",{type:"submit",disabled:a(s).loading,class:"btn btn-primary w-full"},[a(s).loading?(o(),r("span",q,"Signing in...")):(o(),r("span",D,"Sign in"))],8,R)],32)])]))}});export{M as default};
@@ -0,0 +1,33 @@
1
+ import{d as S,r as u,k as j,c as o,a as e,t as n,F as y,z as q,g as C,h as P,i as g,v as _,A as N,p as D,f as E,j as T,y as R,o as a}from"./index-7dRN2NZv.js";import{u as M}from"./projects-BUj8O17i.js";const U={class:"max-w-2xl mx-auto"},V={key:0,class:"card p-6"},B={class:"text-center mb-6"},A={class:"text-gray-500 mt-1"},H={class:"mb-6"},I={class:"flex items-center space-x-2"},L={class:"flex-1 bg-gray-100 dark:bg-gray-700 px-3 py-2 rounded-lg text-sm font-mono break-all text-gray-900 dark:text-gray-100"},z={class:"bg-gray-50 dark:bg-gray-700 rounded-lg p-4 mb-6"},F={class:"font-medium text-gray-900 dark:text-white mb-2"},J={class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},W={key:1,class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},G={key:0,class:"mb-4 bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg text-sm"},K={class:"space-y-4"},Q=["value"],$={class:"mt-6 flex justify-end space-x-3"},O=["disabled"],Y={key:0},X={key:1},re=S({__name:"ProjectCreate",setup(Z){const m=T(),b=M(),p=u(""),i=u("cloudflare-workers"),v=u("cloudflare-workers"),d=u(!1),c=u(null),r=u(null),x=[{value:"cloudflare-workers",label:"Cloudflare Workers"},{value:"javascript",label:"JavaScript"},{value:"node",label:"Node.js"},{value:"python",label:"Python"},{value:"go",label:"Go"},{value:"rust",label:"Rust"},{value:"java",label:"Java"},{value:"php",label:"PHP"},{value:"ruby",label:"Ruby"},{value:"other",label:"Other"}];async function f(){d.value=!0,c.value=null;try{v.value=i.value;const s=await R.post("/api/projects",{name:p.value,platform:i.value});r.value=s,b.addProject({id:s.project.id,name:s.project.name,slug:s.project.slug,platform:i.value})}catch(s){c.value=s instanceof Error?s.message:"Failed to create project"}finally{d.value=!1}}const k=j(()=>v.value==="cloudflare-workers"?"Quick Setup (Cloudflare Workers)":"Quick Setup (JavaScript)");function w(){r.value&&navigator.clipboard.writeText(r.value.dsn)}function h(){r.value&&m.push(`/projects/${r.value.project.slug}/issues`)}return(s,t)=>(a(),o("div",U,[t[12]||(t[12]=e("h1",{class:"text-2xl font-bold text-gray-900 dark:text-white mb-6"},"Create New Project",-1)),r.value?(a(),o("div",V,[e("div",B,[t[3]||(t[3]=e("div",{class:"mx-auto w-12 h-12 bg-green-100 dark:bg-green-900 rounded-full flex items-center justify-center mb-4"},[e("svg",{class:"w-6 h-6 text-green-600 dark:text-green-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M5 13l4 4L19 7"})])],-1)),t[4]||(t[4]=e("h2",{class:"text-xl font-semibold text-gray-900 dark:text-white"},"Project Created!",-1)),e("p",A,n(r.value.project.name),1)]),e("div",H,[t[6]||(t[6]=e("label",{class:"label"},"Your DSN",-1)),e("div",I,[e("code",L,n(r.value.dsn),1),e("button",{onClick:w,class:"btn btn-secondary",title:"Copy to clipboard"},[...t[5]||(t[5]=[e("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"})],-1)])])]),t[7]||(t[7]=e("p",{class:"mt-2 text-sm text-gray-500"}," Use this DSN to configure your Sentry SDK. ",-1))]),e("div",z,[e("h3",F,n(k.value),1),v.value==="cloudflare-workers"?(a(),o(y,{key:0},[t[8]||(t[8]=q(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 1. Install the SDK: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>npm install @sentry/cloudflare --save</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 2. Add a service binding to Sentinel and the compatibility flag in <code class="text-xs bg-gray-200 dark:bg-gray-600 px-1 rounded">wrangler.jsonc</code>: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>{
2
+ &quot;compatibility_flags&quot;: [&quot;nodejs_als&quot;],
3
+ &quot;services&quot;: [
4
+ { &quot;binding&quot;: &quot;SENTINEL&quot;, &quot;service&quot;: &quot;workers-sentinel&quot;, &quot;entrypoint&quot;: &quot;SentinelRpc&quot; }
5
+ ]
6
+ }</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 3. Initialize Sentry with the RPC transport: </p>`,5)),e("pre",J,[e("code",null,`import * as Sentry from "@sentry/cloudflare";
7
+ import { waitUntil } from "cloudflare:workers";
8
+
9
+ const DSN = "`+n(r.value.dsn)+`";
10
+
11
+ export default Sentry.withSentry(
12
+ (env: Env) => ({
13
+ dsn: DSN,
14
+ transport: () => ({
15
+ send: async (envelope) => {
16
+ const rpcPromise = env.SENTINEL.captureEnvelope(DSN, envelope);
17
+ waitUntil(rpcPromise);
18
+ const result = await rpcPromise;
19
+ return { statusCode: result.status };
20
+ },
21
+ flush: async () => true,
22
+ }),
23
+ }),
24
+ {
25
+ async fetch(request, env, ctx) {
26
+ return new Response("Hello World!");
27
+ },
28
+ }
29
+ );`,1)]),t[9]||(t[9]=e("p",{class:"text-xs text-gray-500 dark:text-gray-400 mt-3"}," Using a service binding with RPC routes requests internally within Cloudflare's network, avoiding external HTTP roundtrips and reducing latency. ",-1))],64)):(a(),o("pre",W,[e("code",null,`import * as Sentry from '@sentry/browser';
30
+
31
+ Sentry.init({
32
+ dsn: '`+n(r.value.dsn)+`',
33
+ });`,1)]))]),e("button",{onClick:h,class:"btn btn-primary w-full"}," Go to Project ")])):(a(),o("form",{key:1,class:"card p-6",onSubmit:C(f,["prevent"])},[c.value?(a(),o("div",G,n(c.value),1)):P("",!0),e("div",K,[e("div",null,[t[10]||(t[10]=e("label",{for:"name",class:"label"},"Project Name",-1)),g(e("input",{id:"name","onUpdate:modelValue":t[0]||(t[0]=l=>p.value=l),type:"text",required:"",class:"input",placeholder:"My Application"},null,512),[[_,p.value]])]),e("div",null,[t[11]||(t[11]=e("label",{for:"platform",class:"label"},"Platform",-1)),g(e("select",{id:"platform","onUpdate:modelValue":t[1]||(t[1]=l=>i.value=l),class:"input"},[(a(),o(y,null,D(x,l=>e("option",{key:l.value,value:l.value},n(l.label),9,Q)),64))],512),[[N,i.value]])])]),e("div",$,[e("button",{type:"button",class:"btn btn-secondary",onClick:t[2]||(t[2]=l=>E(m).back())}," Cancel "),e("button",{type:"submit",disabled:d.value,class:"btn btn-primary"},[d.value?(a(),o("span",Y,"Creating...")):(a(),o("span",X,"Create Project"))],8,O)])],32))]))}});export{re as default};
@@ -0,0 +1 @@
1
+ import{d as _,k as D,l as C,r as n,x as S,c as a,h as v,a as t,t as r,g as P,y as p,j as N,o as l}from"./index-7dRN2NZv.js";import{u as $}from"./projects-BUj8O17i.js";const B={class:"max-w-2xl"},A={key:0,class:"text-center py-12"},E={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},M={key:2,class:"space-y-6"},V={class:"card p-6"},z={class:"flex items-center space-x-2"},F={class:"flex-1 bg-gray-100 dark:bg-gray-700 px-3 py-2 rounded-lg text-sm font-mono break-all text-gray-900 dark:text-gray-100"},I={class:"card p-6"},K={class:"space-y-4"},R={class:"text-gray-900 dark:text-white"},T={class:"text-gray-900 dark:text-white font-mono"},U={class:"text-gray-900 dark:text-white"},H={class:"text-gray-900 dark:text-white"},L={class:"text-gray-900 dark:text-white font-mono text-sm"},O={class:"card border-error-200 dark:border-error-800"},Z={class:"p-6"},q={class:"flex items-center justify-between"},G={class:"bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md mx-4"},J={class:"text-lg font-semibold text-gray-900 dark:text-white mb-2"},Q={class:"flex justify-end space-x-3"},W=["disabled"],X={key:0},Y={key:1},ot=_({__name:"ProjectSettings",setup(tt){const y=C(),b=N(),f=$(),c=D(()=>y.params.slug),o=n(null),x=n(""),m=n(!0),d=n(null),i=n(!1),u=n(!1);async function k(){m.value=!0,d.value=null;try{const s=await p.get(`/api/projects/${c.value}`);o.value=s.project,x.value=s.dsn}catch(s){d.value=s instanceof Error?s.message:"Failed to load project"}finally{m.value=!1}}async function h(){if(o.value){i.value=!0;try{await p.delete(`/api/projects/${c.value}`),f.removeProject(c.value),b.push("/projects")}catch(s){d.value=s instanceof Error?s.message:"Failed to delete project",i.value=!1}}}function j(){navigator.clipboard.writeText(x.value)}function w(s){return new Date(s).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric",hour:"2-digit",minute:"2-digit"})}return S(()=>k()),(s,e)=>(l(),a("div",B,[m.value?(l(),a("div",A,[...e[3]||(e[3]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1)])])):d.value?(l(),a("div",E,r(d.value),1)):o.value?(l(),a("div",M,[e[16]||(e[16]=t("h1",{class:"text-2xl font-bold text-gray-900 dark:text-white"},"Project Settings",-1)),t("div",V,[e[5]||(e[5]=t("h2",{class:"text-lg font-semibold text-gray-900 dark:text-white mb-4"},"Client Keys (DSN)",-1)),e[6]||(e[6]=t("p",{class:"text-sm text-gray-500 mb-4"}," Use this DSN to configure your Sentry SDK. ",-1)),t("div",z,[t("code",F,r(x.value),1),t("button",{onClick:j,class:"btn btn-secondary",title:"Copy to clipboard"},[...e[4]||(e[4]=[t("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"})],-1)])])])]),t("div",I,[e[12]||(e[12]=t("h2",{class:"text-lg font-semibold text-gray-900 dark:text-white mb-4"},"Project Information",-1)),t("dl",K,[t("div",null,[e[7]||(e[7]=t("dt",{class:"text-sm text-gray-500"},"Name",-1)),t("dd",R,r(o.value.name),1)]),t("div",null,[e[8]||(e[8]=t("dt",{class:"text-sm text-gray-500"},"Slug",-1)),t("dd",T,r(o.value.slug),1)]),t("div",null,[e[9]||(e[9]=t("dt",{class:"text-sm text-gray-500"},"Platform",-1)),t("dd",U,r(o.value.platform),1)]),t("div",null,[e[10]||(e[10]=t("dt",{class:"text-sm text-gray-500"},"Created",-1)),t("dd",H,r(w(o.value.createdAt)),1)]),t("div",null,[e[11]||(e[11]=t("dt",{class:"text-sm text-gray-500"},"Project ID",-1)),t("dd",L,r(o.value.id),1)])])]),t("div",O,[e[14]||(e[14]=t("div",{class:"p-6 border-b border-error-200 dark:border-error-800"},[t("h2",{class:"text-lg font-semibold text-error-600 dark:text-error-400"},"Danger Zone")],-1)),t("div",Z,[t("div",q,[e[13]||(e[13]=t("div",null,[t("h3",{class:"text-sm font-medium text-gray-900 dark:text-white"},"Delete this project"),t("p",{class:"text-sm text-gray-500"}," Once you delete a project, there is no going back. All data will be permanently deleted. ")],-1)),t("button",{class:"btn btn-danger",onClick:e[0]||(e[0]=g=>u.value=!0)}," Delete Project ")])])]),u.value?(l(),a("div",{key:0,class:"fixed inset-0 bg-black/50 flex items-center justify-center z-50",onClick:e[2]||(e[2]=P(g=>u.value=!1,["self"]))},[t("div",G,[t("h3",J,' Delete "'+r(o.value.name)+'"? ',1),e[15]||(e[15]=t("p",{class:"text-sm text-gray-500 mb-6"}," This action cannot be undone. All issues, events, and settings for this project will be permanently deleted. ",-1)),t("div",Q,[t("button",{class:"btn btn-secondary",onClick:e[1]||(e[1]=g=>u.value=!1)}," Cancel "),t("button",{class:"btn btn-danger",disabled:i.value,onClick:h},[i.value?(l(),a("span",X,"Deleting...")):(l(),a("span",Y,"Delete Project"))],8,W)])])])):v("",!0)])):v("",!0)]))}});export{ot as default};
@@ -0,0 +1 @@
1
+ import{d as y,r as d,x as f,y as k,c as a,a as t,e as x,w as c,f as u,R as m,t as n,F as w,p as h,b as g,o as r,n as _}from"./index-7dRN2NZv.js";const b={class:"flex items-center justify-between mb-6"},j={key:0,class:"text-center py-12"},B={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},C={key:2,class:"text-center py-12"},M={class:"mt-6"},N={key:3,class:"grid gap-4 md:grid-cols-2 lg:grid-cols-3"},V={class:"flex items-start justify-between"},D={class:"font-semibold text-gray-900 dark:text-white"},H={class:"text-sm text-gray-500 mt-1"},L={class:"badge badge-info"},P={class:"text-xs text-gray-400 mt-4"},E=y({__name:"Projects",setup(F){const l=d([]),p=d(!0),i=d(null);f(async()=>{try{const s=await k.get("/api/projects");l.value=s.projects}catch(s){i.value=s instanceof Error?s.message:"Failed to load projects"}finally{p.value=!1}});function v(s){return new Date(s).toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"})}return(s,e)=>(r(),a("div",null,[t("div",b,[e[1]||(e[1]=t("h1",{class:"text-2xl font-bold text-gray-900 dark:text-white"},"Projects",-1)),x(u(m),{to:"/projects/new",class:"btn btn-primary"},{default:c(()=>[...e[0]||(e[0]=[t("svg",{class:"w-5 h-5 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1),g(" New Project ",-1)])]),_:1})]),p.value?(r(),a("div",j,[...e[2]||(e[2]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1),t("p",{class:"mt-4 text-gray-500"},"Loading projects...",-1)])])):i.value?(r(),a("div",B,n(i.value),1)):l.value.length===0?(r(),a("div",C,[e[4]||(e[4]=t("svg",{class:"mx-auto h-12 w-12 text-gray-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"})],-1)),e[5]||(e[5]=t("h3",{class:"mt-2 text-sm font-medium text-gray-900 dark:text-white"},"No projects",-1)),e[6]||(e[6]=t("p",{class:"mt-1 text-sm text-gray-500"},"Get started by creating a new project.",-1)),t("div",M,[x(u(m),{to:"/projects/new",class:"btn btn-primary"},{default:c(()=>[...e[3]||(e[3]=[t("svg",{class:"w-5 h-5 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1),g(" New Project ",-1)])]),_:1})])])):(r(),a("div",N,[(r(!0),a(w,null,h(l.value,o=>(r(),_(u(m),{key:o.id,to:`/projects/${o.slug}/issues`,class:"card p-6 hover:shadow-md transition-shadow"},{default:c(()=>[t("div",V,[t("div",null,[t("h3",D,n(o.name),1),t("p",H,n(o.slug),1)]),t("span",L,n(o.platform),1)]),t("p",P,"Created "+n(v(o.createdAt)),1)]),_:2},1032,["to"]))),128))]))]))}});export{E as default};
@@ -0,0 +1 @@
1
+ import{d as v,u as g,r as u,c as l,a as t,b as x,e as b,w,f as r,R as k,g as h,h as S,t as V,i as p,v as m,j as C,o}from"./index-7dRN2NZv.js";const N={class:"min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 py-12 px-4 sm:px-6 lg:px-8"},R={class:"max-w-md w-full space-y-8"},j={class:"mt-2 text-center text-sm text-gray-600 dark:text-gray-400"},q={key:0,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg text-sm"},A={class:"space-y-4"},B=["disabled"],D={key:0},U={key:1},M=v({__name:"Register",setup(_){const c=C(),s=g(),n=u(""),i=u(""),d=u("");async function y(){await s.register(i.value,d.value,n.value)&&c.push("/")}return(f,e)=>(o(),l("div",N,[t("div",R,[t("div",null,[e[5]||(e[5]=t("div",{class:"mx-auto w-12 h-12 bg-primary-500 rounded-xl flex items-center justify-center"},[t("span",{class:"text-2xl font-bold text-white"},"S")],-1)),e[6]||(e[6]=t("h2",{class:"mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-white"}," Create your account ",-1)),t("p",j,[e[4]||(e[4]=x(" Already have an account? ",-1)),b(r(k),{to:"/login",class:"font-medium text-primary-600 hover:text-primary-500"},{default:w(()=>[...e[3]||(e[3]=[x(" Sign in ",-1)])]),_:1})])]),t("form",{class:"mt-8 space-y-6",onSubmit:h(y,["prevent"])},[r(s).error?(o(),l("div",q,V(r(s).error),1)):S("",!0),t("div",A,[t("div",null,[e[7]||(e[7]=t("label",{for:"name",class:"label"},"Name",-1)),p(t("input",{id:"name","onUpdate:modelValue":e[0]||(e[0]=a=>n.value=a),type:"text",autocomplete:"name",required:"",class:"input",placeholder:"John Doe"},null,512),[[m,n.value]])]),t("div",null,[e[8]||(e[8]=t("label",{for:"email",class:"label"},"Email address",-1)),p(t("input",{id:"email","onUpdate:modelValue":e[1]||(e[1]=a=>i.value=a),type:"email",autocomplete:"email",required:"",class:"input",placeholder:"you@example.com"},null,512),[[m,i.value]])]),t("div",null,[e[9]||(e[9]=t("label",{for:"password",class:"label"},"Password",-1)),p(t("input",{id:"password","onUpdate:modelValue":e[2]||(e[2]=a=>d.value=a),type:"password",autocomplete:"new-password",required:"",minlength:"8",class:"input",placeholder:"••••••••"},null,512),[[m,d.value]]),e[10]||(e[10]=t("p",{class:"mt-1 text-xs text-gray-500"},"At least 8 characters",-1))])]),t("button",{type:"submit",disabled:r(s).loading,class:"btn btn-primary w-full"},[r(s).loading?(o(),l("span",D,"Creating account...")):(o(),l("span",U,"Create account"))],8,B)],32)])]))}});export{M as default};