@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.
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +87 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -0
package/dist/index.d.mts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|