@crashsense/vue 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.
@@ -0,0 +1,18 @@
1
+ import { Plugin, WatchSource } from 'vue';
2
+ import { CrashSeverity, Breadcrumb, CrashSenseCore } from '@crashsense/types';
3
+ export { CrashAnalysis, CrashEvent, CrashReport, CrashSenseConfig } from '@crashsense/types';
4
+ export { createCrashSense } from '@crashsense/core';
5
+
6
+ declare const CRASHSENSE_INJECTION_KEY: unique symbol;
7
+ declare const crashSensePlugin: Plugin;
8
+
9
+ declare function useCrashSense(): {
10
+ captureException: (error: unknown, context?: Record<string, unknown>) => void;
11
+ captureMessage: (message: string, severity?: CrashSeverity) => void;
12
+ addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => void;
13
+ core: CrashSenseCore;
14
+ };
15
+
16
+ declare function useReactivityTracker(watchTargets: Record<string, WatchSource>, threshold?: number): void;
17
+
18
+ export { CRASHSENSE_INJECTION_KEY, crashSensePlugin, useCrashSense, useReactivityTracker };
@@ -0,0 +1,18 @@
1
+ import { Plugin, WatchSource } from 'vue';
2
+ import { CrashSeverity, Breadcrumb, CrashSenseCore } from '@crashsense/types';
3
+ export { CrashAnalysis, CrashEvent, CrashReport, CrashSenseConfig } from '@crashsense/types';
4
+ export { createCrashSense } from '@crashsense/core';
5
+
6
+ declare const CRASHSENSE_INJECTION_KEY: unique symbol;
7
+ declare const crashSensePlugin: Plugin;
8
+
9
+ declare function useCrashSense(): {
10
+ captureException: (error: unknown, context?: Record<string, unknown>) => void;
11
+ captureMessage: (message: string, severity?: CrashSeverity) => void;
12
+ addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => void;
13
+ core: CrashSenseCore;
14
+ };
15
+
16
+ declare function useReactivityTracker(watchTargets: Record<string, WatchSource>, threshold?: number): void;
17
+
18
+ export { CRASHSENSE_INJECTION_KEY, crashSensePlugin, useCrashSense, useReactivityTracker };
package/dist/index.js ADDED
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ var core = require('@crashsense/core');
4
+ var vue = require('vue');
5
+
6
+ // src/plugin.ts
7
+ var CRASHSENSE_INJECTION_KEY = /* @__PURE__ */ Symbol("crashsense");
8
+ var coreInstance = null;
9
+ var crashSensePlugin = {
10
+ install(app, options) {
11
+ coreInstance = core.createCrashSense(options);
12
+ const previousErrorHandler = app.config.errorHandler;
13
+ app.config.errorHandler = (err, instance, info) => {
14
+ const error = err instanceof Error ? err : new Error(String(err));
15
+ let componentName = "Unknown";
16
+ if (instance && "$options" in instance) {
17
+ componentName = instance.$options.name ?? instance.$options.__name ?? "Anonymous";
18
+ }
19
+ coreInstance.captureException(error, {
20
+ framework: "vue",
21
+ componentName,
22
+ lifecycleInfo: info,
23
+ lifecycleStage: info
24
+ });
25
+ if (previousErrorHandler) {
26
+ previousErrorHandler(err, instance, info);
27
+ }
28
+ };
29
+ app.provide(CRASHSENSE_INJECTION_KEY, coreInstance);
30
+ app.config.globalProperties.$crashsense = coreInstance;
31
+ }
32
+ };
33
+ function useCrashSense() {
34
+ const core = vue.inject(CRASHSENSE_INJECTION_KEY);
35
+ if (!core) {
36
+ throw new Error(
37
+ "[CrashSense] useCrashSense() called outside of crashSensePlugin. Make sure to call app.use(crashSensePlugin, config) before using this composable."
38
+ );
39
+ }
40
+ return {
41
+ captureException: (error, context) => {
42
+ core.captureException(error, context);
43
+ },
44
+ captureMessage: (message, severity) => {
45
+ core.captureMessage(message, severity);
46
+ },
47
+ addBreadcrumb: (breadcrumb) => {
48
+ core.addBreadcrumb(breadcrumb);
49
+ },
50
+ core
51
+ };
52
+ }
53
+ var DEFAULT_THRESHOLD = 100;
54
+ var WINDOW_MS = 1e3;
55
+ function useReactivityTracker(watchTargets, threshold = DEFAULT_THRESHOLD) {
56
+ const { captureMessage } = useCrashSense();
57
+ const changeCounts = /* @__PURE__ */ new Map();
58
+ const stopHandles = [];
59
+ for (const [name, source] of Object.entries(watchTargets)) {
60
+ changeCounts.set(name, []);
61
+ const stop = vue.watch(source, () => {
62
+ const now = Date.now();
63
+ const timestamps = changeCounts.get(name);
64
+ timestamps.push(now);
65
+ const cutoff = now - WINDOW_MS;
66
+ const filtered = timestamps.filter((t) => t > cutoff);
67
+ changeCounts.set(name, filtered);
68
+ if (filtered.length > threshold) {
69
+ captureMessage(
70
+ `Potential reactivity loop detected for "${name}": ${filtered.length} changes in ${WINDOW_MS}ms`,
71
+ "warning"
72
+ );
73
+ changeCounts.set(name, []);
74
+ }
75
+ }, { deep: true });
76
+ stopHandles.push(stop);
77
+ }
78
+ vue.onUnmounted(() => {
79
+ for (const stop of stopHandles) {
80
+ stop();
81
+ }
82
+ changeCounts.clear();
83
+ });
84
+ }
85
+
86
+ Object.defineProperty(exports, "createCrashSense", {
87
+ enumerable: true,
88
+ get: function () { return core.createCrashSense; }
89
+ });
90
+ exports.CRASHSENSE_INJECTION_KEY = CRASHSENSE_INJECTION_KEY;
91
+ exports.crashSensePlugin = crashSensePlugin;
92
+ exports.useCrashSense = useCrashSense;
93
+ exports.useReactivityTracker = useReactivityTracker;
94
+ //# sourceMappingURL=index.js.map
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts","../src/composables.ts","../src/reactivity-tracker.ts"],"names":["createCrashSense","inject","watch","onUnmounted"],"mappings":"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","file":"index.js","sourcesContent":["import type { App, Plugin } from 'vue';\nimport type { CrashSenseConfig, CrashSenseCore } from '@crashsense/types';\nimport { createCrashSense } from '@crashsense/core';\n\nexport const CRASHSENSE_INJECTION_KEY = Symbol('crashsense');\n\nlet coreInstance: CrashSenseCore | null = null;\n\nexport const crashSensePlugin: Plugin = {\n install(app: App, options: CrashSenseConfig) {\n coreInstance = createCrashSense(options);\n\n const previousErrorHandler = app.config.errorHandler;\n\n app.config.errorHandler = (err, instance, info) => {\n const error = err instanceof Error ? err : new Error(String(err));\n\n let componentName = 'Unknown';\n if (instance && '$options' in instance) {\n componentName = (instance.$options as { name?: string }).name ?? (instance.$options as { __name?: string }).__name ?? 'Anonymous';\n }\n\n coreInstance!.captureException(error, {\n framework: 'vue',\n componentName,\n lifecycleInfo: info,\n lifecycleStage: info,\n });\n\n if (previousErrorHandler) {\n previousErrorHandler(err, instance, info);\n }\n };\n\n app.provide(CRASHSENSE_INJECTION_KEY, coreInstance);\n\n app.config.globalProperties.$crashsense = coreInstance;\n },\n};\n\nexport function getCoreInstance(): CrashSenseCore | null {\n return coreInstance;\n}\n","import { inject } from 'vue';\nimport type { CrashSenseCore, Breadcrumb, CrashSeverity } from '@crashsense/types';\nimport { CRASHSENSE_INJECTION_KEY } from './plugin';\n\nexport function useCrashSense(): {\n captureException: (error: unknown, context?: Record<string, unknown>) => void;\n captureMessage: (message: string, severity?: CrashSeverity) => void;\n addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => void;\n core: CrashSenseCore;\n} {\n const core = inject<CrashSenseCore>(CRASHSENSE_INJECTION_KEY);\n\n if (!core) {\n throw new Error(\n '[CrashSense] useCrashSense() called outside of crashSensePlugin. ' +\n 'Make sure to call app.use(crashSensePlugin, config) before using this composable.',\n );\n }\n\n return {\n captureException: (error: unknown, context?: Record<string, unknown>) => {\n core.captureException(error, context);\n },\n captureMessage: (message: string, severity?: CrashSeverity) => {\n core.captureMessage(message, severity);\n },\n addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => {\n core.addBreadcrumb(breadcrumb);\n },\n core,\n };\n}\n","import { watch, onUnmounted, type WatchSource } from 'vue';\nimport { useCrashSense } from './composables';\n\nconst DEFAULT_THRESHOLD = 100;\nconst WINDOW_MS = 1000;\n\nexport function useReactivityTracker(\n watchTargets: Record<string, WatchSource>,\n threshold: number = DEFAULT_THRESHOLD,\n): void {\n const { captureMessage } = useCrashSense();\n const changeCounts = new Map<string, number[]>();\n const stopHandles: Array<() => void> = [];\n\n for (const [name, source] of Object.entries(watchTargets)) {\n changeCounts.set(name, []);\n\n const stop = watch(source, () => {\n const now = Date.now();\n const timestamps = changeCounts.get(name)!;\n timestamps.push(now);\n\n const cutoff = now - WINDOW_MS;\n const filtered = timestamps.filter((t) => t > cutoff);\n changeCounts.set(name, filtered);\n\n if (filtered.length > threshold) {\n captureMessage(\n `Potential reactivity loop detected for \"${name}\": ${filtered.length} changes in ${WINDOW_MS}ms`,\n 'warning',\n );\n changeCounts.set(name, []);\n }\n }, { deep: true });\n\n stopHandles.push(stop);\n }\n\n onUnmounted(() => {\n for (const stop of stopHandles) {\n stop();\n }\n changeCounts.clear();\n });\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,87 @@
1
+ import { createCrashSense } from '@crashsense/core';
2
+ export { createCrashSense } from '@crashsense/core';
3
+ import { inject, watch, onUnmounted } from 'vue';
4
+
5
+ // src/plugin.ts
6
+ var CRASHSENSE_INJECTION_KEY = /* @__PURE__ */ Symbol("crashsense");
7
+ var coreInstance = null;
8
+ var crashSensePlugin = {
9
+ install(app, options) {
10
+ coreInstance = createCrashSense(options);
11
+ const previousErrorHandler = app.config.errorHandler;
12
+ app.config.errorHandler = (err, instance, info) => {
13
+ const error = err instanceof Error ? err : new Error(String(err));
14
+ let componentName = "Unknown";
15
+ if (instance && "$options" in instance) {
16
+ componentName = instance.$options.name ?? instance.$options.__name ?? "Anonymous";
17
+ }
18
+ coreInstance.captureException(error, {
19
+ framework: "vue",
20
+ componentName,
21
+ lifecycleInfo: info,
22
+ lifecycleStage: info
23
+ });
24
+ if (previousErrorHandler) {
25
+ previousErrorHandler(err, instance, info);
26
+ }
27
+ };
28
+ app.provide(CRASHSENSE_INJECTION_KEY, coreInstance);
29
+ app.config.globalProperties.$crashsense = coreInstance;
30
+ }
31
+ };
32
+ function useCrashSense() {
33
+ const core = inject(CRASHSENSE_INJECTION_KEY);
34
+ if (!core) {
35
+ throw new Error(
36
+ "[CrashSense] useCrashSense() called outside of crashSensePlugin. Make sure to call app.use(crashSensePlugin, config) before using this composable."
37
+ );
38
+ }
39
+ return {
40
+ captureException: (error, context) => {
41
+ core.captureException(error, context);
42
+ },
43
+ captureMessage: (message, severity) => {
44
+ core.captureMessage(message, severity);
45
+ },
46
+ addBreadcrumb: (breadcrumb) => {
47
+ core.addBreadcrumb(breadcrumb);
48
+ },
49
+ core
50
+ };
51
+ }
52
+ var DEFAULT_THRESHOLD = 100;
53
+ var WINDOW_MS = 1e3;
54
+ function useReactivityTracker(watchTargets, threshold = DEFAULT_THRESHOLD) {
55
+ const { captureMessage } = useCrashSense();
56
+ const changeCounts = /* @__PURE__ */ new Map();
57
+ const stopHandles = [];
58
+ for (const [name, source] of Object.entries(watchTargets)) {
59
+ changeCounts.set(name, []);
60
+ const stop = watch(source, () => {
61
+ const now = Date.now();
62
+ const timestamps = changeCounts.get(name);
63
+ timestamps.push(now);
64
+ const cutoff = now - WINDOW_MS;
65
+ const filtered = timestamps.filter((t) => t > cutoff);
66
+ changeCounts.set(name, filtered);
67
+ if (filtered.length > threshold) {
68
+ captureMessage(
69
+ `Potential reactivity loop detected for "${name}": ${filtered.length} changes in ${WINDOW_MS}ms`,
70
+ "warning"
71
+ );
72
+ changeCounts.set(name, []);
73
+ }
74
+ }, { deep: true });
75
+ stopHandles.push(stop);
76
+ }
77
+ onUnmounted(() => {
78
+ for (const stop of stopHandles) {
79
+ stop();
80
+ }
81
+ changeCounts.clear();
82
+ });
83
+ }
84
+
85
+ export { CRASHSENSE_INJECTION_KEY, crashSensePlugin, useCrashSense, useReactivityTracker };
86
+ //# sourceMappingURL=index.mjs.map
87
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts","../src/composables.ts","../src/reactivity-tracker.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { App, Plugin } from 'vue';\nimport type { CrashSenseConfig, CrashSenseCore } from '@crashsense/types';\nimport { createCrashSense } from '@crashsense/core';\n\nexport const CRASHSENSE_INJECTION_KEY = Symbol('crashsense');\n\nlet coreInstance: CrashSenseCore | null = null;\n\nexport const crashSensePlugin: Plugin = {\n install(app: App, options: CrashSenseConfig) {\n coreInstance = createCrashSense(options);\n\n const previousErrorHandler = app.config.errorHandler;\n\n app.config.errorHandler = (err, instance, info) => {\n const error = err instanceof Error ? err : new Error(String(err));\n\n let componentName = 'Unknown';\n if (instance && '$options' in instance) {\n componentName = (instance.$options as { name?: string }).name ?? (instance.$options as { __name?: string }).__name ?? 'Anonymous';\n }\n\n coreInstance!.captureException(error, {\n framework: 'vue',\n componentName,\n lifecycleInfo: info,\n lifecycleStage: info,\n });\n\n if (previousErrorHandler) {\n previousErrorHandler(err, instance, info);\n }\n };\n\n app.provide(CRASHSENSE_INJECTION_KEY, coreInstance);\n\n app.config.globalProperties.$crashsense = coreInstance;\n },\n};\n\nexport function getCoreInstance(): CrashSenseCore | null {\n return coreInstance;\n}\n","import { inject } from 'vue';\nimport type { CrashSenseCore, Breadcrumb, CrashSeverity } from '@crashsense/types';\nimport { CRASHSENSE_INJECTION_KEY } from './plugin';\n\nexport function useCrashSense(): {\n captureException: (error: unknown, context?: Record<string, unknown>) => void;\n captureMessage: (message: string, severity?: CrashSeverity) => void;\n addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => void;\n core: CrashSenseCore;\n} {\n const core = inject<CrashSenseCore>(CRASHSENSE_INJECTION_KEY);\n\n if (!core) {\n throw new Error(\n '[CrashSense] useCrashSense() called outside of crashSensePlugin. ' +\n 'Make sure to call app.use(crashSensePlugin, config) before using this composable.',\n );\n }\n\n return {\n captureException: (error: unknown, context?: Record<string, unknown>) => {\n core.captureException(error, context);\n },\n captureMessage: (message: string, severity?: CrashSeverity) => {\n core.captureMessage(message, severity);\n },\n addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => {\n core.addBreadcrumb(breadcrumb);\n },\n core,\n };\n}\n","import { watch, onUnmounted, type WatchSource } from 'vue';\nimport { useCrashSense } from './composables';\n\nconst DEFAULT_THRESHOLD = 100;\nconst WINDOW_MS = 1000;\n\nexport function useReactivityTracker(\n watchTargets: Record<string, WatchSource>,\n threshold: number = DEFAULT_THRESHOLD,\n): void {\n const { captureMessage } = useCrashSense();\n const changeCounts = new Map<string, number[]>();\n const stopHandles: Array<() => void> = [];\n\n for (const [name, source] of Object.entries(watchTargets)) {\n changeCounts.set(name, []);\n\n const stop = watch(source, () => {\n const now = Date.now();\n const timestamps = changeCounts.get(name)!;\n timestamps.push(now);\n\n const cutoff = now - WINDOW_MS;\n const filtered = timestamps.filter((t) => t > cutoff);\n changeCounts.set(name, filtered);\n\n if (filtered.length > threshold) {\n captureMessage(\n `Potential reactivity loop detected for \"${name}\": ${filtered.length} changes in ${WINDOW_MS}ms`,\n 'warning',\n );\n changeCounts.set(name, []);\n }\n }, { deep: true });\n\n stopHandles.push(stop);\n }\n\n onUnmounted(() => {\n for (const stop of stopHandles) {\n stop();\n }\n changeCounts.clear();\n });\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@crashsense/vue",
3
+ "version": "0.1.0",
4
+ "description": "CrashSense Vue adapter — errorHandler integration, reactivity tracking",
5
+ "author": "hoainho <nhoxtvt@gmail.com>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/hoainho/crashsense.git",
10
+ "directory": "packages/vue"
11
+ },
12
+ "homepage": "https://github.com/hoainho/crashsense/tree/main/packages/vue",
13
+ "bugs": {
14
+ "url": "https://github.com/hoainho/crashsense/issues"
15
+ },
16
+ "keywords": [
17
+ "crashsense",
18
+ "vue",
19
+ "vue3",
20
+ "crash-detection",
21
+ "error-handler",
22
+ "reactivity-tracking"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.mjs",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.mjs",
34
+ "require": "./dist/index.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "dev": "tsup --watch",
43
+ "test": "vitest run"
44
+ },
45
+ "dependencies": {
46
+ "@crashsense/types": "*",
47
+ "@crashsense/core": "*"
48
+ },
49
+ "peerDependencies": {
50
+ "vue": ">=3.0.0"
51
+ },
52
+ "sideEffects": false
53
+ }