@stacksjs/bun-router 0.0.17 → 0.0.19

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 (153) hide show
  1. package/dist/chunk-2mx7fq49.js +4 -0
  2. package/dist/chunk-a8p83068.js +103 -0
  3. package/dist/{chunk-cgptvjdf.js → chunk-y3xq6wq6.js} +1 -1
  4. package/dist/cli.js +4 -3
  5. package/dist/container/index.js +1149 -211
  6. package/dist/container/service-provider.d.ts +3 -3
  7. package/dist/development/performance-profiler.d.ts +3 -3
  8. package/dist/development/route-debugger.d.ts +1 -1
  9. package/dist/development/route-inspector.d.ts +2 -2
  10. package/dist/index.js +369 -2209
  11. package/dist/observability/integration.d.ts +1 -1
  12. package/dist/response/macros.d.ts +1 -1
  13. package/dist/router/index.d.ts +7 -7
  14. package/dist/router/router.d.ts +0 -32
  15. package/dist/testing/performance-testing.d.ts +2 -2
  16. package/dist/testing/test-client.d.ts +1 -1
  17. package/dist/testing/websocket-testing.d.ts +1 -1
  18. package/package.json +7 -7
  19. package/dist/chunk-1ahs68ys.js +0 -18
  20. package/dist/chunk-g3ybefhg.js +0 -1923
  21. package/src/auth.ts +0 -489
  22. package/src/cache/lru-cache.ts +0 -457
  23. package/src/cache/middleware-memoization.ts +0 -531
  24. package/src/cache/route-cache-warmer.ts +0 -486
  25. package/src/cache/sqlite-cache.ts +0 -783
  26. package/src/cache/streaming-cache.ts +0 -572
  27. package/src/cli/colors.ts +0 -29
  28. package/src/cli/index.ts +0 -287
  29. package/src/cli/middleware.ts +0 -291
  30. package/src/cli/openapi.ts +0 -407
  31. package/src/cli/router.ts +0 -188
  32. package/src/cli/routes.ts +0 -265
  33. package/src/cli/utils.ts +0 -531
  34. package/src/cli.ts +0 -5
  35. package/src/config.ts +0 -322
  36. package/src/container/container.ts +0 -786
  37. package/src/container/contextual-binding.ts +0 -606
  38. package/src/container/decorators.ts +0 -368
  39. package/src/container/index.ts +0 -49
  40. package/src/container/service-provider.ts +0 -596
  41. package/src/development/hot-reload.ts +0 -673
  42. package/src/development/index.ts +0 -499
  43. package/src/development/performance-profiler.ts +0 -717
  44. package/src/development/route-debugger.ts +0 -527
  45. package/src/development/route-inspector.ts +0 -749
  46. package/src/development/typescript-utilities.ts +0 -682
  47. package/src/docs.ts +0 -397
  48. package/src/errors/circuit-breaker.ts +0 -733
  49. package/src/errors/error-handler.ts +0 -569
  50. package/src/errors/error-reporting.ts +0 -672
  51. package/src/errors/exceptions.ts +0 -536
  52. package/src/errors/graceful-degradation.ts +0 -623
  53. package/src/errors/index.ts +0 -21
  54. package/src/errors/router-errors.ts +0 -632
  55. package/src/file-serving/static-files.ts +0 -718
  56. package/src/index.ts +0 -66
  57. package/src/middleware/auth.ts +0 -220
  58. package/src/middleware/content_security_policy.ts +0 -215
  59. package/src/middleware/cors.ts +0 -142
  60. package/src/middleware/csrf.ts +0 -166
  61. package/src/middleware/ddos_protection.ts +0 -279
  62. package/src/middleware/file_security.ts +0 -191
  63. package/src/middleware/file_upload.ts +0 -275
  64. package/src/middleware/helmet.ts +0 -268
  65. package/src/middleware/index.ts +0 -117
  66. package/src/middleware/input_validation.ts +0 -453
  67. package/src/middleware/json_body.ts +0 -37
  68. package/src/middleware/performance_alerting.ts +0 -540
  69. package/src/middleware/performance_dashboard.ts +0 -661
  70. package/src/middleware/performance_monitor.ts +0 -945
  71. package/src/middleware/pipeline.ts +0 -489
  72. package/src/middleware/rate_limit.ts +0 -251
  73. package/src/middleware/request_id.ts +0 -36
  74. package/src/middleware/request_signing.ts +0 -638
  75. package/src/middleware/request_tracer.ts +0 -653
  76. package/src/middleware/response_cache.ts +0 -778
  77. package/src/middleware/security.ts +0 -482
  78. package/src/middleware/security_suite.ts +0 -257
  79. package/src/middleware/session.ts +0 -91
  80. package/src/model-binding/index.ts +0 -18
  81. package/src/model-binding/model-middleware.ts +0 -425
  82. package/src/model-binding/model-registry.ts +0 -550
  83. package/src/model-binding.ts +0 -370
  84. package/src/model-resolver-factory.ts +0 -106
  85. package/src/observability/correlation.ts +0 -691
  86. package/src/observability/health-checks.ts +0 -729
  87. package/src/observability/index.ts +0 -184
  88. package/src/observability/integration.ts +0 -548
  89. package/src/observability/metrics.ts +0 -753
  90. package/src/observability/tracing.ts +0 -638
  91. package/src/optimization/bun-utilities.ts +0 -778
  92. package/src/query-builder-integration.ts +0 -137
  93. package/src/request/context.ts +0 -109
  94. package/src/request/enhanced-request.ts +0 -876
  95. package/src/request/macros.ts +0 -822
  96. package/src/response/macros.ts +0 -665
  97. package/src/response/response-factory.ts +0 -596
  98. package/src/router/api-routes.ts +0 -243
  99. package/src/router/file-based-routing.ts +0 -697
  100. package/src/router/file-streaming.ts +0 -383
  101. package/src/router/fluent-routing.ts +0 -866
  102. package/src/router/group-organization.ts +0 -229
  103. package/src/router/handler-resolver.ts +0 -327
  104. package/src/router/http-methods.ts +0 -407
  105. package/src/router/index.ts +0 -193
  106. package/src/router/middleware-groups.ts +0 -222
  107. package/src/router/middleware-integration.ts +0 -399
  108. package/src/router/middleware.ts +0 -234
  109. package/src/router/model-binding.ts +0 -215
  110. package/src/router/optimized-route-matching.ts +0 -302
  111. package/src/router/route-building.ts +0 -221
  112. package/src/router/route-compiler.ts +0 -691
  113. package/src/router/route-matching.ts +0 -368
  114. package/src/router/route-trie.ts +0 -450
  115. package/src/router/router.ts +0 -1616
  116. package/src/router/server.ts +0 -684
  117. package/src/router/validation-integration.ts +0 -449
  118. package/src/router/view-rendering.ts +0 -233
  119. package/src/router/websocket.ts +0 -100
  120. package/src/routing/route-caching.ts +0 -402
  121. package/src/routing/route-throttling.ts +0 -469
  122. package/src/routing/subdomain-routing.ts +0 -492
  123. package/src/session/database-store.ts +0 -109
  124. package/src/session/file-store.ts +0 -148
  125. package/src/session/index.ts +0 -244
  126. package/src/session/memory-store.ts +0 -88
  127. package/src/session/redis-store.ts +0 -93
  128. package/src/streaming/index.ts +0 -17
  129. package/src/streaming/sse-handler.ts +0 -482
  130. package/src/streaming/stream-handler.ts +0 -552
  131. package/src/testing/auth-testing.ts +0 -448
  132. package/src/testing/file-upload-testing.ts +0 -543
  133. package/src/testing/index.ts +0 -10
  134. package/src/testing/middleware-testing.ts +0 -322
  135. package/src/testing/model-binding-testing.ts +0 -645
  136. package/src/testing/performance-testing.ts +0 -738
  137. package/src/testing/test-client.ts +0 -310
  138. package/src/testing/test-request.ts +0 -315
  139. package/src/testing/test-response.ts +0 -332
  140. package/src/testing/types.ts +0 -224
  141. package/src/testing/websocket-testing.ts +0 -590
  142. package/src/types/controller-types.ts +0 -385
  143. package/src/types/core.ts +0 -683
  144. package/src/types/middleware-types.ts +0 -422
  145. package/src/types/request-response-augmentation.ts +0 -489
  146. package/src/types/route-inference.ts +0 -372
  147. package/src/types.ts +0 -2123
  148. package/src/url.ts +0 -131
  149. package/src/utils/index.ts +0 -1
  150. package/src/utils/query-preservation.ts +0 -201
  151. package/src/utils.ts +0 -395
  152. package/src/validation/validator.ts +0 -688
  153. package/src/websocket/clustering.ts +0 -769
@@ -1,672 +0,0 @@
1
- /**
2
- * Advanced Error Handling - Error Reporting Integration
3
- *
4
- * Integration with external error reporting services (Sentry, Bugsnag, etc.)
5
- */
6
-
7
- import type { ErrorContext, RouterException } from './exceptions'
8
-
9
- export interface ErrorReportingConfig {
10
- enabled: boolean
11
- service: 'sentry' | 'bugsnag' | 'custom'
12
- dsn?: string
13
- apiKey?: string
14
- environment?: string
15
- release?: string
16
- sampleRate?: number
17
- beforeSend?: (error: Error, context: ErrorContext) => Error | null
18
- filters?: {
19
- ignoreErrors?: (string | RegExp)[]
20
- ignoreCodes?: string[]
21
- ignoreUrls?: (string | RegExp)[]
22
- allowUrls?: (string | RegExp)[]
23
- }
24
- tags?: Record<string, string>
25
- user?: {
26
- id?: string
27
- email?: string
28
- username?: string
29
- }
30
- extra?: Record<string, any>
31
- breadcrumbs?: {
32
- enabled: boolean
33
- maxBreadcrumbs: number
34
- }
35
- performance?: {
36
- enabled: boolean
37
- tracesSampleRate: number
38
- }
39
- }
40
-
41
- export interface ErrorReport {
42
- error: RouterException
43
- context: ErrorContext
44
- fingerprint?: string[]
45
- tags?: Record<string, string>
46
- extra?: Record<string, any>
47
- user?: Record<string, any>
48
- level?: 'debug' | 'info' | 'warning' | 'error' | 'fatal'
49
- }
50
-
51
- export interface ErrorReporter {
52
- report: (error: RouterException, context?: ErrorContext) => Promise<string | null>
53
- addBreadcrumb: (message: string, category?: string, level?: string, data?: Record<string, any>) => void
54
- setUser: (user: Record<string, any>) => void
55
- setTag: (key: string, value: string) => void
56
- setExtra: (key: string, value: any) => void
57
- setContext: (key: string, context: Record<string, any>) => void
58
- close: (timeout?: number) => Promise<boolean>
59
- }
60
-
61
- /**
62
- * Sentry error reporter implementation
63
- */
64
- export class SentryReporter implements ErrorReporter {
65
- private config: ErrorReportingConfig
66
- private sentry: any
67
-
68
- constructor(config: ErrorReportingConfig) {
69
- this.config = config
70
- this.initializeSentry()
71
- }
72
-
73
- private async initializeSentry(): Promise<void> {
74
- try {
75
- // Dynamic import to avoid bundling if not used
76
- // @ts-expect-error - Optional peer dependency
77
- const Sentry = await import('@sentry/node') as any
78
-
79
- Sentry.init({
80
- dsn: this.config.dsn,
81
- environment: this.config.environment || 'production',
82
- release: this.config.release,
83
- sampleRate: this.config.sampleRate || 1.0,
84
- beforeSend: (event: any, hint: any) => {
85
- const error = hint.originalException as RouterException
86
- if (error && this.config.beforeSend) {
87
- const filteredError = this.config.beforeSend(error, error.context)
88
- return filteredError ? event : null
89
- }
90
- return event
91
- },
92
- ignoreErrors: this.config.filters?.ignoreErrors,
93
- denyUrls: this.config.filters?.ignoreUrls,
94
- allowUrls: this.config.filters?.allowUrls,
95
- initialScope: {
96
- tags: this.config.tags,
97
- extra: this.config.extra,
98
- user: this.config.user,
99
- },
100
- maxBreadcrumbs: this.config.breadcrumbs?.maxBreadcrumbs || 100,
101
- tracesSampleRate: this.config.performance?.tracesSampleRate || 0.1,
102
- })
103
-
104
- this.sentry = Sentry
105
- }
106
- catch (error) {
107
- console.warn('Failed to initialize Sentry:', error)
108
- }
109
- }
110
-
111
- async report(error: RouterException, context?: ErrorContext): Promise<string | null> {
112
- if (!this.sentry || !this.config.enabled) {
113
- return null
114
- }
115
-
116
- try {
117
- // Check if error should be ignored
118
- if (this.shouldIgnoreError(error)) {
119
- return null
120
- }
121
-
122
- const mergedContext = { ...error.context, ...context }
123
-
124
- return this.sentry.withScope((scope: any) => {
125
- // Set error context
126
- if (mergedContext.requestId) {
127
- scope.setTag('requestId', mergedContext.requestId)
128
- }
129
- if (mergedContext.userId) {
130
- scope.setUser({ id: mergedContext.userId })
131
- }
132
- if (mergedContext.traceId) {
133
- scope.setTag('traceId', mergedContext.traceId)
134
- }
135
- if (mergedContext.route) {
136
- scope.setTag('route', mergedContext.route)
137
- }
138
- if (mergedContext.method) {
139
- scope.setTag('method', mergedContext.method)
140
- }
141
-
142
- // Set error metadata
143
- scope.setTag('errorCode', error.code)
144
- scope.setTag('severity', error.severity)
145
- scope.setTag('category', error.category)
146
- scope.setLevel(this.mapSeverityToLevel(error.severity))
147
-
148
- // Set extra context
149
- scope.setExtra('statusCode', error.statusCode)
150
- scope.setExtra('retryable', error.retryable)
151
- scope.setExtra('timestamp', error.timestamp)
152
- scope.setExtra('context', mergedContext)
153
-
154
- // Set fingerprint for grouping
155
- const fingerprint = this.generateFingerprint(error)
156
- if (fingerprint.length > 0) {
157
- scope.setFingerprint(fingerprint)
158
- }
159
-
160
- return this.sentry.captureException(error)
161
- })
162
- }
163
- catch (reportingError) {
164
- console.error('Failed to report error to Sentry:', reportingError)
165
- return null
166
- }
167
- }
168
-
169
- addBreadcrumb(message: string, category = 'default', level = 'info', data?: Record<string, any>): void {
170
- if (!this.sentry || !this.config.breadcrumbs?.enabled) {
171
- return
172
- }
173
-
174
- this.sentry.addBreadcrumb({
175
- message,
176
- category,
177
- level,
178
- data,
179
- timestamp: Date.now() / 1000,
180
- })
181
- }
182
-
183
- setUser(user: Record<string, any>): void {
184
- if (!this.sentry)
185
- return
186
- this.sentry.setUser(user)
187
- }
188
-
189
- setTag(key: string, value: string): void {
190
- if (!this.sentry)
191
- return
192
- this.sentry.setTag(key, value)
193
- }
194
-
195
- setExtra(key: string, value: any): void {
196
- if (!this.sentry)
197
- return
198
- this.sentry.setExtra(key, value)
199
- }
200
-
201
- setContext(key: string, context: Record<string, any>): void {
202
- if (!this.sentry)
203
- return
204
- this.sentry.setContext(key, context)
205
- }
206
-
207
- async close(timeout = 2000): Promise<boolean> {
208
- if (!this.sentry)
209
- return true
210
- return this.sentry.close(timeout)
211
- }
212
-
213
- private shouldIgnoreError(error: RouterException): boolean {
214
- const filters = this.config.filters
215
- if (!filters)
216
- return false
217
-
218
- // Check ignore codes
219
- if (filters.ignoreCodes?.includes(error.code)) {
220
- return true
221
- }
222
-
223
- // Check ignore errors (message patterns)
224
- if (filters.ignoreErrors?.some((pattern) => {
225
- if (typeof pattern === 'string') {
226
- return error.message.includes(pattern)
227
- }
228
- return pattern.test(error.message)
229
- })) {
230
- return true
231
- }
232
-
233
- return false
234
- }
235
-
236
- private mapSeverityToLevel(severity: string): string {
237
- switch (severity) {
238
- case 'low': return 'info'
239
- case 'medium': return 'warning'
240
- case 'high': return 'error'
241
- case 'critical': return 'fatal'
242
- default: return 'error'
243
- }
244
- }
245
-
246
- private generateFingerprint(error: RouterException): string[] {
247
- const fingerprint: string[] = []
248
-
249
- // Group by error code and category
250
- fingerprint.push(error.code)
251
- fingerprint.push(error.category)
252
-
253
- // Add route if available for better grouping
254
- if (error.context.route) {
255
- fingerprint.push(error.context.route)
256
- }
257
-
258
- return fingerprint
259
- }
260
- }
261
-
262
- /**
263
- * Bugsnag error reporter implementation
264
- */
265
- export class BugsnagReporter implements ErrorReporter {
266
- private config: ErrorReportingConfig
267
- private bugsnag: any
268
-
269
- constructor(config: ErrorReportingConfig) {
270
- this.config = config
271
- this.initializeBugsnag()
272
- }
273
-
274
- private async initializeBugsnag(): Promise<void> {
275
- try {
276
- // Dynamic import to avoid bundling if not used
277
- // @ts-expect-error - Optional peer dependency
278
- const Bugsnag = await import('@bugsnag/js') as any
279
-
280
- Bugsnag.start({
281
- apiKey: this.config.apiKey!,
282
- appVersion: this.config.release,
283
- releaseStage: this.config.environment || 'production',
284
- enabledReleaseStages: ['production', 'staging'],
285
- maxBreadcrumbs: this.config.breadcrumbs?.maxBreadcrumbs || 25,
286
- onError: (event: any) => {
287
- const error = event.originalError as RouterException
288
- if (error && this.config.beforeSend) {
289
- const filteredError = this.config.beforeSend(error, error.context)
290
- return filteredError !== null
291
- }
292
- return !this.shouldIgnoreError(error)
293
- },
294
- })
295
-
296
- this.bugsnag = Bugsnag
297
- }
298
- catch (error) {
299
- console.warn('Failed to initialize Bugsnag:', error)
300
- }
301
- }
302
-
303
- async report(error: RouterException, context?: ErrorContext): Promise<string | null> {
304
- if (!this.bugsnag || !this.config.enabled) {
305
- return null
306
- }
307
-
308
- try {
309
- if (this.shouldIgnoreError(error)) {
310
- return null
311
- }
312
-
313
- const mergedContext = { ...error.context, ...context }
314
-
315
- this.bugsnag.notify(error, (event: any) => {
316
- // Set severity
317
- event.severity = this.mapSeverityToBugsnag(error.severity)
318
-
319
- // Set context
320
- event.context = mergedContext.route || mergedContext.url
321
-
322
- // Set user
323
- if (mergedContext.userId) {
324
- event.setUser(mergedContext.userId, undefined, undefined)
325
- }
326
-
327
- // Add metadata
328
- event.addMetadata('error', {
329
- code: error.code,
330
- category: error.category,
331
- statusCode: error.statusCode,
332
- retryable: error.retryable,
333
- timestamp: error.timestamp,
334
- })
335
-
336
- event.addMetadata('request', {
337
- requestId: mergedContext.requestId,
338
- method: mergedContext.method,
339
- url: mergedContext.url,
340
- userAgent: mergedContext.userAgent,
341
- ip: mergedContext.ip,
342
- })
343
-
344
- // Set grouping hash
345
- event.groupingHash = this.generateGroupingHash(error)
346
- })
347
-
348
- return 'reported' // Bugsnag doesn't return event ID
349
- }
350
- catch (reportingError) {
351
- console.error('Failed to report error to Bugsnag:', reportingError)
352
- return null
353
- }
354
- }
355
-
356
- addBreadcrumb(message: string, category = 'manual', _level = 'info', data?: Record<string, any>): void {
357
- if (!this.bugsnag || !this.config.breadcrumbs?.enabled) {
358
- return
359
- }
360
-
361
- this.bugsnag.leaveBreadcrumb(message, data, category)
362
- }
363
-
364
- setUser(user: Record<string, any>): void {
365
- if (!this.bugsnag)
366
- return
367
- this.bugsnag.setUser(user.id, user.email, user.username)
368
- }
369
-
370
- setTag(key: string, value: string): void {
371
- // Bugsnag doesn't have tags, use metadata instead
372
- if (!this.bugsnag)
373
- return
374
- this.bugsnag.addMetadata('tags', { [key]: value })
375
- }
376
-
377
- setExtra(key: string, value: any): void {
378
- if (!this.bugsnag)
379
- return
380
- this.bugsnag.addMetadata('extra', { [key]: value })
381
- }
382
-
383
- setContext(key: string, context: Record<string, any>): void {
384
- if (!this.bugsnag)
385
- return
386
- this.bugsnag.addMetadata(key, context)
387
- }
388
-
389
- async close(): Promise<boolean> {
390
- // Bugsnag doesn't have a close method
391
- return true
392
- }
393
-
394
- private shouldIgnoreError(error: RouterException): boolean {
395
- const filters = this.config.filters
396
- if (!filters)
397
- return false
398
-
399
- if (filters.ignoreCodes?.includes(error.code)) {
400
- return true
401
- }
402
-
403
- if (filters.ignoreErrors?.some((pattern) => {
404
- if (typeof pattern === 'string') {
405
- return error.message.includes(pattern)
406
- }
407
- return pattern.test(error.message)
408
- })) {
409
- return true
410
- }
411
-
412
- return false
413
- }
414
-
415
- private mapSeverityToBugsnag(severity: string): string {
416
- switch (severity) {
417
- case 'low': return 'info'
418
- case 'medium': return 'warning'
419
- case 'high': return 'error'
420
- case 'critical': return 'error'
421
- default: return 'warning'
422
- }
423
- }
424
-
425
- private generateGroupingHash(error: RouterException): string {
426
- return `${error.code}-${error.category}-${error.context.route || 'unknown'}`
427
- }
428
- }
429
-
430
- /**
431
- * Custom error reporter for custom implementations
432
- */
433
- export class CustomReporter implements ErrorReporter {
434
- private config: ErrorReportingConfig
435
- private reportHandler: (report: ErrorReport) => Promise<string | null>
436
-
437
- constructor(
438
- config: ErrorReportingConfig,
439
- reportHandler: (report: ErrorReport) => Promise<string | null>,
440
- ) {
441
- this.config = config
442
- this.reportHandler = reportHandler
443
- }
444
-
445
- async report(error: RouterException, context?: ErrorContext): Promise<string | null> {
446
- if (!this.config.enabled) {
447
- return null
448
- }
449
-
450
- try {
451
- const mergedContext = { ...error.context, ...context }
452
-
453
- if (this.config.beforeSend) {
454
- const filteredError = this.config.beforeSend(error, mergedContext)
455
- if (!filteredError) {
456
- return null
457
- }
458
- }
459
-
460
- const report: ErrorReport = {
461
- error,
462
- context: mergedContext,
463
- level: this.mapSeverityToLevel(error.severity),
464
- tags: this.config.tags,
465
- extra: this.config.extra,
466
- user: this.config.user,
467
- }
468
-
469
- return await this.reportHandler(report)
470
- }
471
- catch (reportingError) {
472
- console.error('Failed to report error with custom handler:', reportingError)
473
- return null
474
- }
475
- }
476
-
477
- addBreadcrumb(): void {
478
- // Custom implementation would handle this
479
- }
480
-
481
- setUser(): void {
482
- // Custom implementation would handle this
483
- }
484
-
485
- setTag(): void {
486
- // Custom implementation would handle this
487
- }
488
-
489
- setExtra(): void {
490
- // Custom implementation would handle this
491
- }
492
-
493
- setContext(): void {
494
- // Custom implementation would handle this
495
- }
496
-
497
- async close(): Promise<boolean> {
498
- return true
499
- }
500
-
501
- private mapSeverityToLevel(severity: string): 'debug' | 'info' | 'warning' | 'error' | 'fatal' {
502
- switch (severity) {
503
- case 'low': return 'info'
504
- case 'medium': return 'warning'
505
- case 'high': return 'error'
506
- case 'critical': return 'fatal'
507
- default: return 'error'
508
- }
509
- }
510
- }
511
-
512
- /**
513
- * Error reporting manager
514
- */
515
- export class ErrorReportingManager {
516
- private reporters: ErrorReporter[] = []
517
- private breadcrumbs: Array<{ message: string, timestamp: Date, data?: any }> = []
518
- private maxBreadcrumbs = 50
519
-
520
- constructor(configs: ErrorReportingConfig[]) {
521
- this.initializeReporters(configs)
522
- }
523
-
524
- private initializeReporters(configs: ErrorReportingConfig[]): void {
525
- for (const config of configs) {
526
- if (!config.enabled)
527
- continue
528
-
529
- switch (config.service) {
530
- case 'sentry':
531
- this.reporters.push(new SentryReporter(config))
532
- break
533
- case 'bugsnag':
534
- this.reporters.push(new BugsnagReporter(config))
535
- break
536
- case 'custom':
537
- // Custom reporter would be added separately
538
- break
539
- }
540
- }
541
- }
542
-
543
- addReporter(reporter: ErrorReporter): void {
544
- this.reporters.push(reporter)
545
- }
546
-
547
- async report(error: RouterException, context?: ErrorContext): Promise<string[]> {
548
- const reportIds: string[] = []
549
-
550
- // Add breadcrumb for this error
551
- this.addBreadcrumb(`Error occurred: ${error.message}`, 'error', {
552
- code: error.code,
553
- severity: error.severity,
554
- })
555
-
556
- // Report to all configured reporters
557
- const promises = this.reporters.map(async (reporter) => {
558
- try {
559
- const reportId = await reporter.report(error, context)
560
- if (reportId) {
561
- reportIds.push(reportId)
562
- }
563
- }
564
- catch (reportingError) {
565
- console.error('Reporter failed:', reportingError)
566
- }
567
- })
568
-
569
- await Promise.allSettled(promises)
570
- return reportIds
571
- }
572
-
573
- addBreadcrumb(message: string, category = 'default', data?: any): void {
574
- // Add to internal breadcrumbs
575
- this.breadcrumbs.push({
576
- message,
577
- timestamp: new Date(),
578
- data: { category, ...data },
579
- })
580
-
581
- // Keep only recent breadcrumbs
582
- if (this.breadcrumbs.length > this.maxBreadcrumbs) {
583
- this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs)
584
- }
585
-
586
- // Add to all reporters
587
- this.reporters.forEach((reporter) => {
588
- reporter.addBreadcrumb(message, category, 'info', data)
589
- })
590
- }
591
-
592
- setUser(user: Record<string, any>): void {
593
- this.reporters.forEach(reporter => reporter.setUser(user))
594
- }
595
-
596
- setTag(key: string, value: string): void {
597
- this.reporters.forEach(reporter => reporter.setTag(key, value))
598
- }
599
-
600
- setExtra(key: string, value: any): void {
601
- this.reporters.forEach(reporter => reporter.setExtra(key, value))
602
- }
603
-
604
- setContext(key: string, context: Record<string, any>): void {
605
- this.reporters.forEach(reporter => reporter.setContext(key, context))
606
- }
607
-
608
- async close(timeout = 5000): Promise<boolean> {
609
- const promises = this.reporters.map(reporter => reporter.close(timeout))
610
- const results = await Promise.allSettled(promises)
611
- return results.every(result => result.status === 'fulfilled' && result.value)
612
- }
613
-
614
- getBreadcrumbs(): Array<{ message: string, timestamp: Date, data?: any }> {
615
- return [...this.breadcrumbs]
616
- }
617
- }
618
-
619
- /**
620
- * Factory function to create error reporting manager
621
- */
622
- export function createErrorReporting(configs: ErrorReportingConfig[]): ErrorReportingManager {
623
- return new ErrorReportingManager(configs)
624
- }
625
-
626
- /**
627
- * Default configuration presets
628
- */
629
- export const ErrorReportingPresets = {
630
- sentry: (dsn: string, environment = 'production'): ErrorReportingConfig => ({
631
- enabled: true,
632
- service: 'sentry',
633
- dsn,
634
- environment,
635
- sampleRate: 1.0,
636
- breadcrumbs: {
637
- enabled: true,
638
- maxBreadcrumbs: 100,
639
- },
640
- performance: {
641
- enabled: true,
642
- tracesSampleRate: 0.1,
643
- },
644
- filters: {
645
- ignoreErrors: [
646
- 'Non-Error promise rejection captured',
647
- 'ResizeObserver loop limit exceeded',
648
- ],
649
- ignoreCodes: ['VALIDATION_ERROR'],
650
- },
651
- }),
652
-
653
- bugsnag: (apiKey: string, environment = 'production'): ErrorReportingConfig => ({
654
- enabled: true,
655
- service: 'bugsnag',
656
- apiKey,
657
- environment,
658
- breadcrumbs: {
659
- enabled: true,
660
- maxBreadcrumbs: 25,
661
- },
662
- filters: {
663
- ignoreCodes: ['VALIDATION_ERROR'],
664
- },
665
- }),
666
-
667
- development: (): ErrorReportingConfig => ({
668
- enabled: false,
669
- service: 'custom',
670
- environment: 'development',
671
- }),
672
- }