@error-explorer/vue 1.1.1

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/dist/index.cjs ADDED
@@ -0,0 +1,658 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var browser = require('@error-explorer/browser');
6
+ var vue = require('vue');
7
+
8
+ // src/plugin.ts
9
+ function getComponentName(instance) {
10
+ if (!instance) return void 0;
11
+ const options = instance.$.type;
12
+ return options.name || options.__name || extractNameFromFile(options.__file);
13
+ }
14
+ function extractNameFromFile(file) {
15
+ if (!file) return void 0;
16
+ const match = file.match(/([^/\\]+)\.vue$/);
17
+ return match ? match[1] : void 0;
18
+ }
19
+ function getComponentTrace(instance) {
20
+ const trace = [];
21
+ let current = instance;
22
+ while (current) {
23
+ const name = getComponentName(current);
24
+ if (name) {
25
+ trace.push(name);
26
+ }
27
+ current = current.$.parent?.proxy ?? null;
28
+ }
29
+ return trace;
30
+ }
31
+ function serializeProps(props, maxDepth) {
32
+ const serialize = (value, depth) => {
33
+ if (depth > maxDepth) {
34
+ return "[Max depth reached]";
35
+ }
36
+ if (value === null || value === void 0) {
37
+ return value;
38
+ }
39
+ if (typeof value === "function") {
40
+ return "[Function]";
41
+ }
42
+ if (typeof value === "symbol") {
43
+ return value.toString();
44
+ }
45
+ if (value instanceof Date) {
46
+ return value.toISOString();
47
+ }
48
+ if (value instanceof Error) {
49
+ return { name: value.name, message: value.message };
50
+ }
51
+ if (Array.isArray(value)) {
52
+ return value.slice(0, 10).map((item) => serialize(item, depth + 1));
53
+ }
54
+ if (typeof value === "object") {
55
+ const serialized = {};
56
+ const keys = Object.keys(value).slice(0, 20);
57
+ for (const key of keys) {
58
+ serialized[key] = serialize(value[key], depth + 1);
59
+ }
60
+ return serialized;
61
+ }
62
+ return value;
63
+ };
64
+ return serialize(props, 0);
65
+ }
66
+ function buildVueContext(instance, info, options) {
67
+ const context = {
68
+ info
69
+ };
70
+ if (options.captureComponentName !== false && instance) {
71
+ context.name = getComponentName(instance);
72
+ const type = instance.$.type;
73
+ if (type.__file) {
74
+ context.file = type.__file;
75
+ }
76
+ context.trace = getComponentTrace(instance);
77
+ }
78
+ if (options.captureComponentProps && instance) {
79
+ const props = instance.$.props;
80
+ if (props && Object.keys(props).length > 0) {
81
+ context.props = serializeProps(props, options.propsDepth ?? 2);
82
+ }
83
+ }
84
+ return context;
85
+ }
86
+ var originalErrorHandler;
87
+ var originalWarnHandler;
88
+ function setupErrorHandler(app, options) {
89
+ if (options.vueErrorHandler === false) {
90
+ return;
91
+ }
92
+ originalErrorHandler = app.config.errorHandler;
93
+ app.config.errorHandler = (err, instance, info) => {
94
+ if (options.beforeVueCapture) {
95
+ const error2 = err instanceof Error ? err : new Error(String(err));
96
+ if (!options.beforeVueCapture(error2, instance, info)) {
97
+ if (originalErrorHandler) {
98
+ originalErrorHandler(err, instance, info);
99
+ }
100
+ return;
101
+ }
102
+ }
103
+ const vueContext = buildVueContext(instance, info, options);
104
+ browser.ErrorExplorer.addBreadcrumb({
105
+ type: "error",
106
+ category: "vue.error",
107
+ message: err instanceof Error ? err.message : String(err),
108
+ level: "error",
109
+ data: {
110
+ component: vueContext.name,
111
+ info: vueContext.info
112
+ }
113
+ });
114
+ const error = err instanceof Error ? err : new Error(String(err));
115
+ browser.ErrorExplorer.captureException(error, {
116
+ tags: {
117
+ "vue.component": vueContext.name || "unknown",
118
+ "vue.info": info
119
+ },
120
+ extra: {
121
+ vue: vueContext
122
+ }
123
+ });
124
+ if (originalErrorHandler) {
125
+ originalErrorHandler(err, instance, info);
126
+ }
127
+ if (options.environment === "development" || undefined?.DEV) {
128
+ console.error("[Vue Error]", err);
129
+ }
130
+ };
131
+ }
132
+ function setupWarnHandler(app, options) {
133
+ const enableWarn = options.vueWarnHandler ?? (options.environment === "development" || undefined?.DEV);
134
+ if (!enableWarn) {
135
+ return;
136
+ }
137
+ originalWarnHandler = app.config.warnHandler;
138
+ app.config.warnHandler = (msg, instance, trace) => {
139
+ const componentName = getComponentName(instance);
140
+ browser.ErrorExplorer.addBreadcrumb({
141
+ type: "debug",
142
+ category: "vue.warn",
143
+ message: msg,
144
+ level: "warning",
145
+ data: {
146
+ component: componentName,
147
+ trace: trace.split("\n").slice(0, 5)
148
+ }
149
+ });
150
+ if (originalWarnHandler) {
151
+ originalWarnHandler(msg, instance, trace);
152
+ }
153
+ if (undefined?.DEV) {
154
+ console.warn("[Vue Warn]", msg);
155
+ if (trace) {
156
+ console.warn(trace);
157
+ }
158
+ }
159
+ };
160
+ }
161
+ function restoreHandlers(app) {
162
+ if (originalErrorHandler !== void 0) {
163
+ app.config.errorHandler = originalErrorHandler;
164
+ originalErrorHandler = void 0;
165
+ }
166
+ if (originalWarnHandler !== void 0) {
167
+ app.config.warnHandler = originalWarnHandler;
168
+ originalWarnHandler = void 0;
169
+ }
170
+ }
171
+ function createErrorExplorerPlugin(options = {}) {
172
+ return {
173
+ install(app) {
174
+ if (!browser.ErrorExplorer.isInitialized()) {
175
+ browser.ErrorExplorer.init(options);
176
+ }
177
+ setupErrorHandler(app, options);
178
+ setupWarnHandler(app, options);
179
+ app.provide("errorExplorer", browser.ErrorExplorer);
180
+ app.config.globalProperties.$errorExplorer = browser.ErrorExplorer;
181
+ browser.ErrorExplorer.addBreadcrumb({
182
+ type: "navigation",
183
+ category: "vue.lifecycle",
184
+ message: "Vue app mounted",
185
+ level: "info"
186
+ });
187
+ }
188
+ };
189
+ }
190
+ function defaultGetRouteName(route) {
191
+ if (route.name && typeof route.name === "string") {
192
+ return route.name;
193
+ }
194
+ return route.path || "/";
195
+ }
196
+ function buildRouteData(route, options) {
197
+ const data = {
198
+ path: route.path,
199
+ name: route.name,
200
+ fullPath: route.fullPath
201
+ };
202
+ if (options.trackParams && Object.keys(route.params).length > 0) {
203
+ data.params = { ...route.params };
204
+ }
205
+ if (options.trackQuery && Object.keys(route.query).length > 0) {
206
+ data.query = { ...route.query };
207
+ }
208
+ if (route.meta && Object.keys(route.meta).length > 0) {
209
+ const safeMeta = {};
210
+ for (const [key, value] of Object.entries(route.meta)) {
211
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
212
+ safeMeta[key] = value;
213
+ }
214
+ }
215
+ if (Object.keys(safeMeta).length > 0) {
216
+ data.meta = safeMeta;
217
+ }
218
+ }
219
+ return data;
220
+ }
221
+ function setupRouterIntegration(router, options = {}) {
222
+ if (options.trackNavigation === false) {
223
+ return () => {
224
+ };
225
+ }
226
+ const getRouteName = options.getRouteName || defaultGetRouteName;
227
+ let navigationStartTime = null;
228
+ const beforeEachGuard = router.beforeEach((to, from) => {
229
+ navigationStartTime = performance.now();
230
+ if (from.name || from.path !== "/") {
231
+ const shouldAdd = options.beforeNavigationBreadcrumb ? options.beforeNavigationBreadcrumb(from, to) : true;
232
+ if (shouldAdd) {
233
+ browser.ErrorExplorer.addBreadcrumb({
234
+ type: "navigation",
235
+ category: "router",
236
+ message: `Navigating from ${getRouteName(from)} to ${getRouteName(to)}`,
237
+ level: "info",
238
+ data: {
239
+ from: buildRouteData(from, options),
240
+ to: buildRouteData(to, options)
241
+ }
242
+ });
243
+ }
244
+ }
245
+ return true;
246
+ });
247
+ const afterEachGuard = router.afterEach((to, from, failure) => {
248
+ const duration = navigationStartTime ? performance.now() - navigationStartTime : void 0;
249
+ navigationStartTime = null;
250
+ if (failure) {
251
+ browser.ErrorExplorer.addBreadcrumb({
252
+ type: "navigation",
253
+ category: "router.error",
254
+ message: `Navigation failed to ${getRouteName(to)}`,
255
+ level: "error",
256
+ data: {
257
+ to: buildRouteData(to, options),
258
+ error: failure.message,
259
+ type: failure.type,
260
+ duration
261
+ }
262
+ });
263
+ if (failure.type !== 4) {
264
+ browser.ErrorExplorer.captureException(failure, {
265
+ tags: {
266
+ "router.error": "navigation_failure",
267
+ "router.to": getRouteName(to)
268
+ }
269
+ });
270
+ }
271
+ } else {
272
+ browser.ErrorExplorer.addBreadcrumb({
273
+ type: "navigation",
274
+ category: "router",
275
+ message: `Navigated to ${getRouteName(to)}`,
276
+ level: "info",
277
+ data: {
278
+ route: buildRouteData(to, options),
279
+ duration
280
+ }
281
+ });
282
+ }
283
+ });
284
+ const errorHandler = router.onError((error) => {
285
+ browser.ErrorExplorer.addBreadcrumb({
286
+ type: "error",
287
+ category: "router.error",
288
+ message: error.message,
289
+ level: "error"
290
+ });
291
+ browser.ErrorExplorer.captureException(error, {
292
+ tags: {
293
+ "router.error": "unhandled"
294
+ }
295
+ });
296
+ });
297
+ return () => {
298
+ beforeEachGuard();
299
+ afterEachGuard();
300
+ errorHandler();
301
+ };
302
+ }
303
+ function createRouterIntegration(options = {}) {
304
+ let cleanup = null;
305
+ return {
306
+ /**
307
+ * Install the router integration
308
+ */
309
+ install(router) {
310
+ cleanup = setupRouterIntegration(router, options);
311
+ },
312
+ /**
313
+ * Uninstall the router integration
314
+ */
315
+ uninstall() {
316
+ if (cleanup) {
317
+ cleanup();
318
+ cleanup = null;
319
+ }
320
+ }
321
+ };
322
+ }
323
+ var ErrorBoundary = vue.defineComponent({
324
+ name: "ErrorBoundary",
325
+ props: {
326
+ /**
327
+ * Whether to capture the error to Error Explorer
328
+ */
329
+ capture: {
330
+ type: Boolean,
331
+ default: true
332
+ },
333
+ /**
334
+ * Additional tags to add when capturing
335
+ */
336
+ tags: {
337
+ type: Object,
338
+ default: () => ({})
339
+ },
340
+ /**
341
+ * Additional context to add when capturing
342
+ */
343
+ context: {
344
+ type: Object,
345
+ default: () => ({})
346
+ },
347
+ /**
348
+ * Fallback component to render when an error occurs
349
+ */
350
+ fallback: {
351
+ type: [Object, Function, null],
352
+ default: null
353
+ },
354
+ /**
355
+ * Whether to stop error propagation
356
+ */
357
+ stopPropagation: {
358
+ type: Boolean,
359
+ default: true
360
+ }
361
+ },
362
+ emits: {
363
+ /**
364
+ * Emitted when an error is caught
365
+ */
366
+ error: (error, info) => true,
367
+ /**
368
+ * Emitted when the error state is reset
369
+ */
370
+ reset: () => true
371
+ },
372
+ setup(props, { slots, emit, expose }) {
373
+ const error = vue.ref(null);
374
+ const errorInfo = vue.ref("");
375
+ const reset = () => {
376
+ error.value = null;
377
+ errorInfo.value = "";
378
+ emit("reset");
379
+ };
380
+ vue.onErrorCaptured((err, instance, info) => {
381
+ const capturedError = err instanceof Error ? err : new Error(String(err));
382
+ error.value = capturedError;
383
+ errorInfo.value = info;
384
+ emit("error", capturedError, info);
385
+ browser.ErrorExplorer.addBreadcrumb({
386
+ type: "error",
387
+ category: "vue.errorBoundary",
388
+ message: capturedError.message,
389
+ level: "error",
390
+ data: {
391
+ info,
392
+ component: instance?.$.type ? instance.$.type.name : void 0
393
+ }
394
+ });
395
+ if (props.capture) {
396
+ browser.ErrorExplorer.captureException(capturedError, {
397
+ tags: {
398
+ "errorBoundary": "true",
399
+ "vue.info": info,
400
+ ...props.tags
401
+ },
402
+ extra: {
403
+ errorBoundary: {
404
+ info,
405
+ context: props.context
406
+ }
407
+ }
408
+ });
409
+ }
410
+ return props.stopPropagation;
411
+ });
412
+ expose({
413
+ reset,
414
+ error
415
+ });
416
+ return () => {
417
+ if (error.value) {
418
+ if (slots.fallback) {
419
+ return slots.fallback({
420
+ error: error.value,
421
+ info: errorInfo.value,
422
+ reset
423
+ });
424
+ }
425
+ if (props.fallback) {
426
+ if (typeof props.fallback === "function") {
427
+ return props.fallback();
428
+ }
429
+ return props.fallback;
430
+ }
431
+ return vue.h("div", { class: "error-boundary-fallback" }, [
432
+ vue.h("p", { style: "color: red;" }, `Error: ${error.value.message}`),
433
+ vue.h(
434
+ "button",
435
+ {
436
+ onClick: reset,
437
+ style: "margin-top: 8px; padding: 4px 12px; cursor: pointer;"
438
+ },
439
+ "Try Again"
440
+ )
441
+ ]);
442
+ }
443
+ return slots.default?.();
444
+ };
445
+ }
446
+ });
447
+ function withErrorBoundary(Component, options = {}) {
448
+ return vue.defineComponent({
449
+ name: `WithErrorBoundary(${Component.name || "Component"})`,
450
+ setup(_, { attrs, slots }) {
451
+ return () => vue.h(
452
+ ErrorBoundary,
453
+ {
454
+ capture: options.capture ?? true,
455
+ tags: options.tags ?? {},
456
+ context: options.context ?? {},
457
+ fallback: options.fallback ?? null,
458
+ onError: options.onError
459
+ },
460
+ {
461
+ default: () => vue.h(Component, attrs, slots)
462
+ }
463
+ );
464
+ }
465
+ });
466
+ }
467
+ var ErrorExplorerKey = /* @__PURE__ */ Symbol("errorExplorer");
468
+ function useErrorExplorer() {
469
+ const injected = vue.inject(ErrorExplorerKey, null) || vue.inject("errorExplorer", null);
470
+ const errorExplorer = injected || browser.ErrorExplorer;
471
+ return {
472
+ /**
473
+ * Check if Error Explorer is initialized
474
+ */
475
+ isInitialized: () => errorExplorer.isInitialized(),
476
+ /**
477
+ * Capture an exception
478
+ */
479
+ captureException: (error, context) => errorExplorer.captureException(error, context),
480
+ /**
481
+ * Capture a message
482
+ */
483
+ captureMessage: (message, level) => errorExplorer.captureMessage(message, level),
484
+ /**
485
+ * Add a breadcrumb
486
+ */
487
+ addBreadcrumb: (breadcrumb) => errorExplorer.addBreadcrumb(breadcrumb),
488
+ /**
489
+ * Set user context
490
+ */
491
+ setUser: (user) => errorExplorer.setUser(user),
492
+ /**
493
+ * Clear user context
494
+ */
495
+ clearUser: () => errorExplorer.clearUser(),
496
+ /**
497
+ * Set a tag
498
+ */
499
+ setTag: (key, value) => errorExplorer.setTag(key, value),
500
+ /**
501
+ * Set multiple tags
502
+ */
503
+ setTags: (tags) => errorExplorer.setTags(tags),
504
+ /**
505
+ * Set extra context
506
+ */
507
+ setExtra: (extra) => errorExplorer.setExtra(extra),
508
+ /**
509
+ * Set named context
510
+ */
511
+ setContext: (name, context) => errorExplorer.setContext(name, context),
512
+ /**
513
+ * Flush pending events
514
+ */
515
+ flush: (timeout) => errorExplorer.flush(timeout),
516
+ /**
517
+ * Close the SDK
518
+ */
519
+ close: (timeout) => errorExplorer.close(timeout)
520
+ };
521
+ }
522
+ function useComponentBreadcrumbs() {
523
+ const instance = vue.getCurrentInstance();
524
+ const componentName = instance?.type ? instance.type.name || instance.type.__name || "Unknown" : "Unknown";
525
+ vue.onMounted(() => {
526
+ browser.ErrorExplorer.addBreadcrumb({
527
+ type: "debug",
528
+ category: "vue.lifecycle",
529
+ message: `${componentName} mounted`,
530
+ level: "debug"
531
+ });
532
+ });
533
+ vue.onUnmounted(() => {
534
+ browser.ErrorExplorer.addBreadcrumb({
535
+ type: "debug",
536
+ category: "vue.lifecycle",
537
+ message: `${componentName} unmounted`,
538
+ level: "debug"
539
+ });
540
+ });
541
+ }
542
+ function useActionTracker() {
543
+ const instance = vue.getCurrentInstance();
544
+ const componentName = instance?.type ? instance.type.name || instance.type.__name || "Unknown" : "Unknown";
545
+ return {
546
+ /**
547
+ * Track a user action
548
+ */
549
+ trackAction: (action, data) => {
550
+ browser.ErrorExplorer.addBreadcrumb({
551
+ type: "user-action",
552
+ category: "action",
553
+ message: action,
554
+ level: "info",
555
+ data: {
556
+ component: componentName,
557
+ ...data
558
+ }
559
+ });
560
+ },
561
+ /**
562
+ * Track a UI interaction
563
+ */
564
+ trackInteraction: (element, action, data) => {
565
+ browser.ErrorExplorer.addBreadcrumb({
566
+ type: "user-action",
567
+ category: `ui.${action}`,
568
+ message: `${action} on ${element}`,
569
+ level: "info",
570
+ data: {
571
+ component: componentName,
572
+ element,
573
+ ...data
574
+ }
575
+ });
576
+ }
577
+ };
578
+ }
579
+ function useErrorHandler(defaultContext) {
580
+ const instance = vue.getCurrentInstance();
581
+ const componentName = instance?.type ? instance.type.name || instance.type.__name || "Unknown" : "Unknown";
582
+ const handleError = (error, context) => {
583
+ const err = error instanceof Error ? error : new Error(String(error));
584
+ browser.ErrorExplorer.captureException(err, {
585
+ ...defaultContext,
586
+ ...context,
587
+ tags: {
588
+ "vue.component": componentName,
589
+ ...defaultContext?.tags,
590
+ ...context?.tags
591
+ }
592
+ });
593
+ return err;
594
+ };
595
+ const wrapAsync = (fn, context) => {
596
+ return async (...args) => {
597
+ try {
598
+ return await fn(...args);
599
+ } catch (error) {
600
+ handleError(error, context);
601
+ return void 0;
602
+ }
603
+ };
604
+ };
605
+ const tryCatch = (fn, context) => {
606
+ try {
607
+ return fn();
608
+ } catch (error) {
609
+ handleError(error, context);
610
+ return void 0;
611
+ }
612
+ };
613
+ return {
614
+ handleError,
615
+ wrapAsync,
616
+ tryCatch
617
+ };
618
+ }
619
+ function useUserContext(user) {
620
+ const setUserFromValue = (value) => {
621
+ if (value) {
622
+ browser.ErrorExplorer.setUser(value);
623
+ } else {
624
+ browser.ErrorExplorer.clearUser();
625
+ }
626
+ };
627
+ if (user && typeof user === "object" && "value" in user) {
628
+ const refValue = user.value;
629
+ setUserFromValue(refValue);
630
+ } else {
631
+ setUserFromValue(user);
632
+ }
633
+ return {
634
+ setUser: (newUser) => browser.ErrorExplorer.setUser(newUser),
635
+ clearUser: () => browser.ErrorExplorer.clearUser()
636
+ };
637
+ }
638
+ var src_default = createErrorExplorerPlugin;
639
+
640
+ Object.defineProperty(exports, "ErrorExplorer", {
641
+ enumerable: true,
642
+ get: function () { return browser.ErrorExplorer; }
643
+ });
644
+ exports.ErrorBoundary = ErrorBoundary;
645
+ exports.ErrorExplorerKey = ErrorExplorerKey;
646
+ exports.createErrorExplorerPlugin = createErrorExplorerPlugin;
647
+ exports.createRouterIntegration = createRouterIntegration;
648
+ exports.default = src_default;
649
+ exports.restoreHandlers = restoreHandlers;
650
+ exports.setupRouterIntegration = setupRouterIntegration;
651
+ exports.useActionTracker = useActionTracker;
652
+ exports.useComponentBreadcrumbs = useComponentBreadcrumbs;
653
+ exports.useErrorExplorer = useErrorExplorer;
654
+ exports.useErrorHandler = useErrorHandler;
655
+ exports.useUserContext = useUserContext;
656
+ exports.withErrorBoundary = withErrorBoundary;
657
+ //# sourceMappingURL=index.cjs.map
658
+ //# sourceMappingURL=index.cjs.map