@devlens/vue 1.0.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/README.md +76 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +139 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +133 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @devlens/vue
|
|
2
|
+
|
|
3
|
+
DevLens Vue 3 integration -- automatic runtime error detection for Vue applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @devlens/core @devlens/vue
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Peer dependencies:** Vue >= 3.3.0
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createApp } from 'vue';
|
|
17
|
+
import { createDevLensPlugin } from '@devlens/vue';
|
|
18
|
+
|
|
19
|
+
const app = createApp(App);
|
|
20
|
+
|
|
21
|
+
app.use(createDevLensPlugin({
|
|
22
|
+
enabled: true,
|
|
23
|
+
minSeverity: 'warn',
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
app.mount('#app');
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The plugin automatically installs `app.config.errorHandler` and `app.config.warnHandler` to capture Vue errors and warnings.
|
|
30
|
+
|
|
31
|
+
## Guarded Ref
|
|
32
|
+
|
|
33
|
+
Reactive ref with null/undefined detection:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { useGuardedRef } from '@devlens/vue';
|
|
37
|
+
|
|
38
|
+
const user = useGuardedRef(initialUser, 'UserProfile');
|
|
39
|
+
|
|
40
|
+
// Accessing null/undefined properties on user.value triggers detection
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Watch Data
|
|
44
|
+
|
|
45
|
+
Monitor data for null/undefined values:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { useGuardedWatch } from '@devlens/vue';
|
|
49
|
+
|
|
50
|
+
useGuardedWatch({ user, posts, settings }, 'Dashboard');
|
|
51
|
+
|
|
52
|
+
// [RENDER] DevLens [WARN] render-data: "posts" is undefined in Dashboard
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## API Reference
|
|
56
|
+
|
|
57
|
+
| Export | Description |
|
|
58
|
+
|--------|-------------|
|
|
59
|
+
| `createDevLensPlugin(options?)` | Vue 3 plugin with auto error/warn handlers |
|
|
60
|
+
| `useDevLens()` | Inject the engine instance |
|
|
61
|
+
| `useGuardedRef(initialValue, label?)` | Reactive ref with null/undefined detection |
|
|
62
|
+
| `useGuardedWatch(data, label?)` | Watch data for null/undefined |
|
|
63
|
+
|
|
64
|
+
## Details
|
|
65
|
+
|
|
66
|
+
- ~5KB ESM bundle
|
|
67
|
+
- Auto error handler: `app.config.errorHandler`
|
|
68
|
+
- Auto warn handler: `app.config.warnHandler`
|
|
69
|
+
- Network interceptor and global catcher auto-installed
|
|
70
|
+
- Dual ESM + CJS output
|
|
71
|
+
- Full TypeScript declarations
|
|
72
|
+
- Production-safe -- auto-disabled in production
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
MIT -- [github.com/crashsense/devlens](https://github.com/crashsense/devlens)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { InjectionKey, App, Ref } from 'vue';
|
|
2
|
+
import { DevLensEngine, DevLensConfig } from '@devlens/core';
|
|
3
|
+
export { DetectedIssue, DevLensConfig, IssueCategory, Severity } from '@devlens/core';
|
|
4
|
+
|
|
5
|
+
declare const DevLensKey: InjectionKey<DevLensEngine>;
|
|
6
|
+
interface DevLensPluginOptions extends DevLensConfig {
|
|
7
|
+
}
|
|
8
|
+
declare function createDevLensPlugin(options?: DevLensPluginOptions): {
|
|
9
|
+
install(app: App): void;
|
|
10
|
+
uninstall(): void;
|
|
11
|
+
getEngine(): DevLensEngine | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare function useDevLens(): DevLensEngine | null;
|
|
15
|
+
declare function useGuardedRef<T extends object>(initialValue: T, label?: string): Ref<T>;
|
|
16
|
+
declare function useGuardedWatch(data: Record<string, unknown>, label?: string): void;
|
|
17
|
+
|
|
18
|
+
export { DevLensKey, type DevLensPluginOptions, createDevLensPlugin, useDevLens, useGuardedRef, useGuardedWatch };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { InjectionKey, App, Ref } from 'vue';
|
|
2
|
+
import { DevLensEngine, DevLensConfig } from '@devlens/core';
|
|
3
|
+
export { DetectedIssue, DevLensConfig, IssueCategory, Severity } from '@devlens/core';
|
|
4
|
+
|
|
5
|
+
declare const DevLensKey: InjectionKey<DevLensEngine>;
|
|
6
|
+
interface DevLensPluginOptions extends DevLensConfig {
|
|
7
|
+
}
|
|
8
|
+
declare function createDevLensPlugin(options?: DevLensPluginOptions): {
|
|
9
|
+
install(app: App): void;
|
|
10
|
+
uninstall(): void;
|
|
11
|
+
getEngine(): DevLensEngine | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare function useDevLens(): DevLensEngine | null;
|
|
15
|
+
declare function useGuardedRef<T extends object>(initialValue: T, label?: string): Ref<T>;
|
|
16
|
+
declare function useGuardedWatch(data: Record<string, unknown>, label?: string): void;
|
|
17
|
+
|
|
18
|
+
export { DevLensKey, type DevLensPluginOptions, createDevLensPlugin, useDevLens, useGuardedRef, useGuardedWatch };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@devlens/core');
|
|
4
|
+
var vue = require('vue');
|
|
5
|
+
|
|
6
|
+
// src/plugin.ts
|
|
7
|
+
var DevLensKey = /* @__PURE__ */ Symbol("devlens");
|
|
8
|
+
function isProductionEnv() {
|
|
9
|
+
try {
|
|
10
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function createDevLensPlugin(options = {}) {
|
|
16
|
+
let engine = null;
|
|
17
|
+
const cleanups = [];
|
|
18
|
+
return {
|
|
19
|
+
install(app) {
|
|
20
|
+
if (options.enabled === false || isProductionEnv()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
engine = core.createDetectionEngine(options);
|
|
24
|
+
app.provide(DevLensKey, engine);
|
|
25
|
+
const networkConfig = options.modules?.network === false ? void 0 : options.modules?.network;
|
|
26
|
+
const catcherConfig = options.modules?.catcher === false ? void 0 : options.modules?.catcher;
|
|
27
|
+
if (networkConfig !== void 0 || options.modules?.network !== false) {
|
|
28
|
+
const network = core.createNetworkInterceptor(engine, networkConfig);
|
|
29
|
+
network.install();
|
|
30
|
+
cleanups.push(network);
|
|
31
|
+
}
|
|
32
|
+
if (catcherConfig !== void 0 || options.modules?.catcher !== false) {
|
|
33
|
+
const catcher = core.createGlobalCatcher(engine, catcherConfig);
|
|
34
|
+
catcher.install();
|
|
35
|
+
cleanups.push(catcher);
|
|
36
|
+
}
|
|
37
|
+
app.config.errorHandler = (err, instance, info) => {
|
|
38
|
+
if (!engine?.isEnabled()) return;
|
|
39
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
40
|
+
const componentName = instance?.$options?.name ?? instance?.$options?.__name ?? "AnonymousComponent";
|
|
41
|
+
engine.report({
|
|
42
|
+
id: `unhandled-error:vue:${error.message}`,
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
severity: "error",
|
|
45
|
+
category: "unhandled-error",
|
|
46
|
+
message: `Vue error in ${componentName}: ${error.message}`,
|
|
47
|
+
details: {
|
|
48
|
+
component: componentName,
|
|
49
|
+
lifecycleHook: info
|
|
50
|
+
},
|
|
51
|
+
stack: error.stack,
|
|
52
|
+
source: `Vue:${componentName}`,
|
|
53
|
+
suggestion: `Error in ${info} of ${componentName} \u2014 check the component logic`
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
app.config.warnHandler = (msg, instance, trace) => {
|
|
57
|
+
if (!engine?.isEnabled()) return;
|
|
58
|
+
const componentName = instance?.$options?.name ?? instance?.$options?.__name ?? "AnonymousComponent";
|
|
59
|
+
engine.report({
|
|
60
|
+
id: `unhandled-error:vue-warn:${msg.slice(0, 80)}`,
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
severity: "warn",
|
|
63
|
+
category: "unhandled-error",
|
|
64
|
+
message: `Vue warning in ${componentName}: ${msg}`,
|
|
65
|
+
details: {
|
|
66
|
+
component: componentName,
|
|
67
|
+
trace
|
|
68
|
+
},
|
|
69
|
+
source: `Vue:${componentName}`,
|
|
70
|
+
suggestion: "Check the Vue warning above \u2014 it may indicate a potential issue"
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
uninstall() {
|
|
75
|
+
for (const cleanup of cleanups) {
|
|
76
|
+
cleanup.uninstall();
|
|
77
|
+
}
|
|
78
|
+
cleanups.length = 0;
|
|
79
|
+
engine = null;
|
|
80
|
+
},
|
|
81
|
+
getEngine() {
|
|
82
|
+
return engine;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function useDevLens() {
|
|
87
|
+
return vue.inject(DevLensKey, null);
|
|
88
|
+
}
|
|
89
|
+
function useGuardedRef(initialValue, label) {
|
|
90
|
+
const engine = useDevLens();
|
|
91
|
+
const source = vue.ref(initialValue);
|
|
92
|
+
if (!engine || !engine.isEnabled()) {
|
|
93
|
+
return source;
|
|
94
|
+
}
|
|
95
|
+
const guardian = core.createDataGuardian(engine);
|
|
96
|
+
const guarded = vue.ref(guardian.guard(source.value, label));
|
|
97
|
+
vue.watch(source, (newVal) => {
|
|
98
|
+
if (newVal === null || newVal === void 0 || typeof newVal !== "object") {
|
|
99
|
+
guarded.value = newVal;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const freshGuardian = core.createDataGuardian(engine);
|
|
103
|
+
guarded.value = freshGuardian.guard(newVal, label);
|
|
104
|
+
}, { deep: true });
|
|
105
|
+
return guarded;
|
|
106
|
+
}
|
|
107
|
+
function useGuardedWatch(data, label) {
|
|
108
|
+
const engine = useDevLens();
|
|
109
|
+
if (!engine || !engine.isEnabled()) return;
|
|
110
|
+
const resolvedLabel = label ?? "useGuardedWatch";
|
|
111
|
+
const dataRef = vue.ref(data);
|
|
112
|
+
vue.watch(dataRef, (current) => {
|
|
113
|
+
for (const [key, value] of Object.entries(current)) {
|
|
114
|
+
if (value === null || value === void 0) {
|
|
115
|
+
const issue = {
|
|
116
|
+
id: `render-data:${resolvedLabel}:${key}`,
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
severity: "warn",
|
|
119
|
+
category: "render-data",
|
|
120
|
+
message: `"${key}" is ${value === null ? "null" : "undefined"} in ${resolvedLabel}`,
|
|
121
|
+
path: `${resolvedLabel}.${key}`,
|
|
122
|
+
foundValue: value,
|
|
123
|
+
expectedType: "non-nullish value",
|
|
124
|
+
source: resolvedLabel,
|
|
125
|
+
suggestion: `"${key}" is ${value === null ? "null" : "undefined"} \u2014 check data loading in ${resolvedLabel}`
|
|
126
|
+
};
|
|
127
|
+
engine.report(issue);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, { immediate: true, deep: true });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
exports.DevLensKey = DevLensKey;
|
|
134
|
+
exports.createDevLensPlugin = createDevLensPlugin;
|
|
135
|
+
exports.useDevLens = useDevLens;
|
|
136
|
+
exports.useGuardedRef = useGuardedRef;
|
|
137
|
+
exports.useGuardedWatch = useGuardedWatch;
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts","../src/composables.ts"],"names":["createDetectionEngine","createNetworkInterceptor","createGlobalCatcher","inject","ref","createDataGuardian","watch"],"mappings":";;;;;;AAQO,IAAM,UAAA,0BAAiD,SAAS;AAEvE,SAAS,eAAA,GAA2B;AAClC,EAAA,IAAI;AACF,IAAA,OACE,OAAO,OAAA,KAAY,WAAA,IACnB,OAAA,CAAQ,KAAK,QAAA,KAAa,YAAA;AAAA,EAE9B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAIO,SAAS,mBAAA,CAAoB,OAAA,GAAgC,EAAC,EAAG;AACtE,EAAA,IAAI,MAAA,GAA+B,IAAA;AACnC,EAAA,MAAM,WAAyC,EAAC;AAEhD,EAAA,OAAO;AAAA,IACL,QAAQ,GAAA,EAAgB;AACtB,MAAA,IAAI,OAAA,CAAQ,OAAA,KAAY,KAAA,IAAS,eAAA,EAAgB,EAAG;AAClD,QAAA;AAAA,MACF;AAEA,MAAA,MAAA,GAASA,2BAAsB,OAAO,CAAA;AACtC,MAAA,GAAA,CAAI,OAAA,CAAQ,YAAY,MAAM,CAAA;AAE9B,MAAA,MAAM,gBACJ,OAAA,CAAQ,OAAA,EAAS,YAAY,KAAA,GAAQ,MAAA,GAAY,QAAQ,OAAA,EAAS,OAAA;AACpE,MAAA,MAAM,gBACJ,OAAA,CAAQ,OAAA,EAAS,YAAY,KAAA,GAAQ,MAAA,GAAY,QAAQ,OAAA,EAAS,OAAA;AAEpE,MAAA,IAAI,aAAA,KAAkB,MAAA,IAAa,OAAA,CAAQ,OAAA,EAAS,YAAY,KAAA,EAAO;AACrE,QAAA,MAAM,OAAA,GAAUC,6BAAA,CAAyB,MAAA,EAAQ,aAAa,CAAA;AAC9D,QAAA,OAAA,CAAQ,OAAA,EAAQ;AAChB,QAAA,QAAA,CAAS,KAAK,OAAO,CAAA;AAAA,MACvB;AAEA,MAAA,IAAI,aAAA,KAAkB,MAAA,IAAa,OAAA,CAAQ,OAAA,EAAS,YAAY,KAAA,EAAO;AACrE,QAAA,MAAM,OAAA,GAAUC,wBAAA,CAAoB,MAAA,EAAQ,aAAa,CAAA;AACzD,QAAA,OAAA,CAAQ,OAAA,EAAQ;AAChB,QAAA,QAAA,CAAS,KAAK,OAAO,CAAA;AAAA,MACvB;AAEA,MAAA,GAAA,CAAI,MAAA,CAAO,YAAA,GAAe,CAAC,GAAA,EAAK,UAAU,IAAA,KAAS;AACjD,QAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,EAAU,EAAG;AAE1B,QAAA,MAAM,KAAA,GAAQ,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AAChE,QAAA,MAAM,gBACH,QAAA,EAAU,QAAA,EAAU,IAAA,IACpB,QAAA,EAAU,UAAU,MAAA,IACrB,oBAAA;AAEF,QAAA,MAAA,CAAO,MAAA,CAAO;AAAA,UACZ,EAAA,EAAI,CAAA,oBAAA,EAAuB,KAAA,CAAM,OAAO,CAAA,CAAA;AAAA,UACxC,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,UACpB,QAAA,EAAU,OAAA;AAAA,UACV,QAAA,EAAU,iBAAA;AAAA,UACV,OAAA,EAAS,CAAA,aAAA,EAAgB,aAAa,CAAA,EAAA,EAAK,MAAM,OAAO,CAAA,CAAA;AAAA,UACxD,OAAA,EAAS;AAAA,YACP,SAAA,EAAW,aAAA;AAAA,YACX,aAAA,EAAe;AAAA,WACjB;AAAA,UACA,OAAO,KAAA,CAAM,KAAA;AAAA,UACb,MAAA,EAAQ,OAAO,aAAa,CAAA,CAAA;AAAA,UAC5B,UAAA,EAAY,CAAA,SAAA,EAAY,IAAI,CAAA,IAAA,EAAO,aAAa,CAAA,iCAAA;AAAA,SACjD,CAAA;AAAA,MACH,CAAA;AAEA,MAAA,GAAA,CAAI,MAAA,CAAO,WAAA,GAAc,CAAC,GAAA,EAAK,UAAU,KAAA,KAAU;AACjD,QAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,EAAU,EAAG;AAE1B,QAAA,MAAM,gBACH,QAAA,EAAU,QAAA,EAAU,IAAA,IACpB,QAAA,EAAU,UAAU,MAAA,IACrB,oBAAA;AAEF,QAAA,MAAA,CAAO,MAAA,CAAO;AAAA,UACZ,IAAI,CAAA,yBAAA,EAA4B,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,CAAA;AAAA,UAChD,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,UACpB,QAAA,EAAU,MAAA;AAAA,UACV,QAAA,EAAU,iBAAA;AAAA,UACV,OAAA,EAAS,CAAA,eAAA,EAAkB,aAAa,CAAA,EAAA,EAAK,GAAG,CAAA,CAAA;AAAA,UAChD,OAAA,EAAS;AAAA,YACP,SAAA,EAAW,aAAA;AAAA,YACX;AAAA,WACF;AAAA,UACA,MAAA,EAAQ,OAAO,aAAa,CAAA,CAAA;AAAA,UAC5B,UAAA,EAAY;AAAA,SACb,CAAA;AAAA,MACH,CAAA;AAAA,IACF,CAAA;AAAA,IAEA,SAAA,GAAkB;AAChB,MAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,QAAA,OAAA,CAAQ,SAAA,EAAU;AAAA,MACpB;AACA,MAAA,QAAA,CAAS,MAAA,GAAS,CAAA;AAClB,MAAA,MAAA,GAAS,IAAA;AAAA,IACX,CAAA;AAAA,IAEA,SAAA,GAAkC;AAChC,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AACF;AC7GO,SAAS,UAAA,GAAmC;AACjD,EAAA,OAAOC,UAAA,CAAO,YAAY,IAAI,CAAA;AAChC;AAEO,SAAS,aAAA,CACd,cACA,KAAA,EACQ;AACR,EAAA,MAAM,SAAS,UAAA,EAAW;AAC1B,EAAA,MAAM,MAAA,GAASC,QAAI,YAAY,CAAA;AAE/B,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,CAAO,WAAU,EAAG;AAClC,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAA,GAAWC,wBAAmB,MAAM,CAAA;AAC1C,EAAA,MAAM,UAAUD,OAAA,CAAI,QAAA,CAAS,MAAM,MAAA,CAAO,KAAA,EAAO,KAAK,CAAC,CAAA;AAEvD,EAAAE,SAAA,CAAM,MAAA,EAAQ,CAAC,MAAA,KAAW;AACxB,IAAA,IAAI,WAAW,IAAA,IAAQ,MAAA,KAAW,MAAA,IAAa,OAAO,WAAW,QAAA,EAAU;AACzE,MAAA,OAAA,CAAQ,KAAA,GAAQ,MAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,aAAA,GAAgBD,wBAAmB,MAAM,CAAA;AAC/C,IAAA,OAAA,CAAQ,KAAA,GAAQ,aAAA,CAAc,KAAA,CAAM,MAAA,EAAQ,KAAK,CAAA;AAAA,EACnD,CAAA,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAEjB,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,eAAA,CACd,MACA,KAAA,EACM;AACN,EAAA,MAAM,SAAS,UAAA,EAAW;AAC1B,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,CAAO,WAAU,EAAG;AAEpC,EAAA,MAAM,gBAAgB,KAAA,IAAS,iBAAA;AAE/B,EAAA,MAAM,OAAA,GAAUD,QAAI,IAAI,CAAA;AAExB,EAAAE,SAAA,CAAM,OAAA,EAAS,CAAC,OAAA,KAAY;AAC1B,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAClD,MAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,MAAA,EAAW;AACzC,QAAA,MAAM,KAAA,GAAuB;AAAA,UAC3B,EAAA,EAAI,CAAA,YAAA,EAAe,aAAa,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AAAA,UACvC,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,UACpB,QAAA,EAAU,MAAA;AAAA,UACV,QAAA,EAAU,aAAA;AAAA,UACV,OAAA,EAAS,IAAI,GAAG,CAAA,KAAA,EAAQ,UAAU,IAAA,GAAO,MAAA,GAAS,WAAW,CAAA,IAAA,EAAO,aAAa,CAAA,CAAA;AAAA,UACjF,IAAA,EAAM,CAAA,EAAG,aAAa,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AAAA,UAC7B,UAAA,EAAY,KAAA;AAAA,UACZ,YAAA,EAAc,mBAAA;AAAA,UACd,MAAA,EAAQ,aAAA;AAAA,UACR,UAAA,EAAY,IAAI,GAAG,CAAA,KAAA,EAAQ,UAAU,IAAA,GAAO,MAAA,GAAS,WAAW,CAAA,8BAAA,EAA4B,aAAa,CAAA;AAAA,SAC3G;AACA,QAAA,MAAA,CAAO,OAAO,KAAK,CAAA;AAAA,MACrB;AAAA,IACF;AAAA,EACF,GAAG,EAAE,SAAA,EAAW,IAAA,EAAM,IAAA,EAAM,MAAM,CAAA;AACpC","file":"index.js","sourcesContent":["import type { App, InjectionKey } from 'vue';\nimport type { DevLensConfig, DevLensEngine } from '@devlens/core';\nimport {\n createDetectionEngine,\n createNetworkInterceptor,\n createGlobalCatcher,\n} from '@devlens/core';\n\nexport const DevLensKey: InjectionKey<DevLensEngine> = Symbol('devlens');\n\nfunction isProductionEnv(): boolean {\n try {\n return (\n typeof process !== 'undefined' &&\n process.env?.NODE_ENV === 'production'\n );\n } catch {\n return false;\n }\n}\n\nexport interface DevLensPluginOptions extends DevLensConfig {}\n\nexport function createDevLensPlugin(options: DevLensPluginOptions = {}) {\n let engine: DevLensEngine | null = null;\n const cleanups: Array<{ uninstall(): void }> = [];\n\n return {\n install(app: App): void {\n if (options.enabled === false || isProductionEnv()) {\n return;\n }\n\n engine = createDetectionEngine(options);\n app.provide(DevLensKey, engine);\n\n const networkConfig =\n options.modules?.network === false ? undefined : options.modules?.network;\n const catcherConfig =\n options.modules?.catcher === false ? undefined : options.modules?.catcher;\n\n if (networkConfig !== undefined || options.modules?.network !== false) {\n const network = createNetworkInterceptor(engine, networkConfig);\n network.install();\n cleanups.push(network);\n }\n\n if (catcherConfig !== undefined || options.modules?.catcher !== false) {\n const catcher = createGlobalCatcher(engine, catcherConfig);\n catcher.install();\n cleanups.push(catcher);\n }\n\n app.config.errorHandler = (err, instance, info) => {\n if (!engine?.isEnabled()) return;\n\n const error = err instanceof Error ? err : new Error(String(err));\n const componentName =\n (instance?.$options?.name) ??\n (instance?.$options?.__name) ??\n 'AnonymousComponent';\n\n engine.report({\n id: `unhandled-error:vue:${error.message}`,\n timestamp: Date.now(),\n severity: 'error',\n category: 'unhandled-error',\n message: `Vue error in ${componentName}: ${error.message}`,\n details: {\n component: componentName,\n lifecycleHook: info,\n },\n stack: error.stack,\n source: `Vue:${componentName}`,\n suggestion: `Error in ${info} of ${componentName} — check the component logic`,\n });\n };\n\n app.config.warnHandler = (msg, instance, trace) => {\n if (!engine?.isEnabled()) return;\n\n const componentName =\n (instance?.$options?.name) ??\n (instance?.$options?.__name) ??\n 'AnonymousComponent';\n\n engine.report({\n id: `unhandled-error:vue-warn:${msg.slice(0, 80)}`,\n timestamp: Date.now(),\n severity: 'warn',\n category: 'unhandled-error',\n message: `Vue warning in ${componentName}: ${msg}`,\n details: {\n component: componentName,\n trace,\n },\n source: `Vue:${componentName}`,\n suggestion: 'Check the Vue warning above — it may indicate a potential issue',\n });\n };\n },\n\n uninstall(): void {\n for (const cleanup of cleanups) {\n cleanup.uninstall();\n }\n cleanups.length = 0;\n engine = null;\n },\n\n getEngine(): DevLensEngine | null {\n return engine;\n },\n };\n}\n","import { inject, ref, watch, type Ref } from 'vue';\nimport type { DevLensEngine, DetectedIssue } from '@devlens/core';\nimport { createDataGuardian } from '@devlens/core';\nimport { DevLensKey } from './plugin';\n\nexport function useDevLens(): DevLensEngine | null {\n return inject(DevLensKey, null);\n}\n\nexport function useGuardedRef<T extends object>(\n initialValue: T,\n label?: string,\n): Ref<T> {\n const engine = useDevLens();\n const source = ref(initialValue) as Ref<T>;\n\n if (!engine || !engine.isEnabled()) {\n return source;\n }\n\n const guardian = createDataGuardian(engine);\n const guarded = ref(guardian.guard(source.value, label)) as Ref<T>;\n\n watch(source, (newVal) => {\n if (newVal === null || newVal === undefined || typeof newVal !== 'object') {\n guarded.value = newVal;\n return;\n }\n const freshGuardian = createDataGuardian(engine);\n guarded.value = freshGuardian.guard(newVal, label);\n }, { deep: true });\n\n return guarded;\n}\n\nexport function useGuardedWatch(\n data: Record<string, unknown>,\n label?: string,\n): void {\n const engine = useDevLens();\n if (!engine || !engine.isEnabled()) return;\n\n const resolvedLabel = label ?? 'useGuardedWatch';\n\n const dataRef = ref(data);\n\n watch(dataRef, (current) => {\n for (const [key, value] of Object.entries(current)) {\n if (value === null || value === undefined) {\n const issue: DetectedIssue = {\n id: `render-data:${resolvedLabel}:${key}`,\n timestamp: Date.now(),\n severity: 'warn',\n category: 'render-data',\n message: `\"${key}\" is ${value === null ? 'null' : 'undefined'} in ${resolvedLabel}`,\n path: `${resolvedLabel}.${key}`,\n foundValue: value,\n expectedType: 'non-nullish value',\n source: resolvedLabel,\n suggestion: `\"${key}\" is ${value === null ? 'null' : 'undefined'} — check data loading in ${resolvedLabel}`,\n };\n engine.report(issue);\n }\n }\n }, { immediate: true, deep: true });\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createDetectionEngine, createNetworkInterceptor, createGlobalCatcher, createDataGuardian } from '@devlens/core';
|
|
2
|
+
import { inject, ref, watch } from 'vue';
|
|
3
|
+
|
|
4
|
+
// src/plugin.ts
|
|
5
|
+
var DevLensKey = /* @__PURE__ */ Symbol("devlens");
|
|
6
|
+
function isProductionEnv() {
|
|
7
|
+
try {
|
|
8
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function createDevLensPlugin(options = {}) {
|
|
14
|
+
let engine = null;
|
|
15
|
+
const cleanups = [];
|
|
16
|
+
return {
|
|
17
|
+
install(app) {
|
|
18
|
+
if (options.enabled === false || isProductionEnv()) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
engine = createDetectionEngine(options);
|
|
22
|
+
app.provide(DevLensKey, engine);
|
|
23
|
+
const networkConfig = options.modules?.network === false ? void 0 : options.modules?.network;
|
|
24
|
+
const catcherConfig = options.modules?.catcher === false ? void 0 : options.modules?.catcher;
|
|
25
|
+
if (networkConfig !== void 0 || options.modules?.network !== false) {
|
|
26
|
+
const network = createNetworkInterceptor(engine, networkConfig);
|
|
27
|
+
network.install();
|
|
28
|
+
cleanups.push(network);
|
|
29
|
+
}
|
|
30
|
+
if (catcherConfig !== void 0 || options.modules?.catcher !== false) {
|
|
31
|
+
const catcher = createGlobalCatcher(engine, catcherConfig);
|
|
32
|
+
catcher.install();
|
|
33
|
+
cleanups.push(catcher);
|
|
34
|
+
}
|
|
35
|
+
app.config.errorHandler = (err, instance, info) => {
|
|
36
|
+
if (!engine?.isEnabled()) return;
|
|
37
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
38
|
+
const componentName = instance?.$options?.name ?? instance?.$options?.__name ?? "AnonymousComponent";
|
|
39
|
+
engine.report({
|
|
40
|
+
id: `unhandled-error:vue:${error.message}`,
|
|
41
|
+
timestamp: Date.now(),
|
|
42
|
+
severity: "error",
|
|
43
|
+
category: "unhandled-error",
|
|
44
|
+
message: `Vue error in ${componentName}: ${error.message}`,
|
|
45
|
+
details: {
|
|
46
|
+
component: componentName,
|
|
47
|
+
lifecycleHook: info
|
|
48
|
+
},
|
|
49
|
+
stack: error.stack,
|
|
50
|
+
source: `Vue:${componentName}`,
|
|
51
|
+
suggestion: `Error in ${info} of ${componentName} \u2014 check the component logic`
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
app.config.warnHandler = (msg, instance, trace) => {
|
|
55
|
+
if (!engine?.isEnabled()) return;
|
|
56
|
+
const componentName = instance?.$options?.name ?? instance?.$options?.__name ?? "AnonymousComponent";
|
|
57
|
+
engine.report({
|
|
58
|
+
id: `unhandled-error:vue-warn:${msg.slice(0, 80)}`,
|
|
59
|
+
timestamp: Date.now(),
|
|
60
|
+
severity: "warn",
|
|
61
|
+
category: "unhandled-error",
|
|
62
|
+
message: `Vue warning in ${componentName}: ${msg}`,
|
|
63
|
+
details: {
|
|
64
|
+
component: componentName,
|
|
65
|
+
trace
|
|
66
|
+
},
|
|
67
|
+
source: `Vue:${componentName}`,
|
|
68
|
+
suggestion: "Check the Vue warning above \u2014 it may indicate a potential issue"
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
uninstall() {
|
|
73
|
+
for (const cleanup of cleanups) {
|
|
74
|
+
cleanup.uninstall();
|
|
75
|
+
}
|
|
76
|
+
cleanups.length = 0;
|
|
77
|
+
engine = null;
|
|
78
|
+
},
|
|
79
|
+
getEngine() {
|
|
80
|
+
return engine;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function useDevLens() {
|
|
85
|
+
return inject(DevLensKey, null);
|
|
86
|
+
}
|
|
87
|
+
function useGuardedRef(initialValue, label) {
|
|
88
|
+
const engine = useDevLens();
|
|
89
|
+
const source = ref(initialValue);
|
|
90
|
+
if (!engine || !engine.isEnabled()) {
|
|
91
|
+
return source;
|
|
92
|
+
}
|
|
93
|
+
const guardian = createDataGuardian(engine);
|
|
94
|
+
const guarded = ref(guardian.guard(source.value, label));
|
|
95
|
+
watch(source, (newVal) => {
|
|
96
|
+
if (newVal === null || newVal === void 0 || typeof newVal !== "object") {
|
|
97
|
+
guarded.value = newVal;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const freshGuardian = createDataGuardian(engine);
|
|
101
|
+
guarded.value = freshGuardian.guard(newVal, label);
|
|
102
|
+
}, { deep: true });
|
|
103
|
+
return guarded;
|
|
104
|
+
}
|
|
105
|
+
function useGuardedWatch(data, label) {
|
|
106
|
+
const engine = useDevLens();
|
|
107
|
+
if (!engine || !engine.isEnabled()) return;
|
|
108
|
+
const resolvedLabel = label ?? "useGuardedWatch";
|
|
109
|
+
const dataRef = ref(data);
|
|
110
|
+
watch(dataRef, (current) => {
|
|
111
|
+
for (const [key, value] of Object.entries(current)) {
|
|
112
|
+
if (value === null || value === void 0) {
|
|
113
|
+
const issue = {
|
|
114
|
+
id: `render-data:${resolvedLabel}:${key}`,
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
severity: "warn",
|
|
117
|
+
category: "render-data",
|
|
118
|
+
message: `"${key}" is ${value === null ? "null" : "undefined"} in ${resolvedLabel}`,
|
|
119
|
+
path: `${resolvedLabel}.${key}`,
|
|
120
|
+
foundValue: value,
|
|
121
|
+
expectedType: "non-nullish value",
|
|
122
|
+
source: resolvedLabel,
|
|
123
|
+
suggestion: `"${key}" is ${value === null ? "null" : "undefined"} \u2014 check data loading in ${resolvedLabel}`
|
|
124
|
+
};
|
|
125
|
+
engine.report(issue);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}, { immediate: true, deep: true });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { DevLensKey, createDevLensPlugin, useDevLens, useGuardedRef, useGuardedWatch };
|
|
132
|
+
//# sourceMappingURL=index.mjs.map
|
|
133
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts","../src/composables.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { App, InjectionKey } from 'vue';\nimport type { DevLensConfig, DevLensEngine } from '@devlens/core';\nimport {\n createDetectionEngine,\n createNetworkInterceptor,\n createGlobalCatcher,\n} from '@devlens/core';\n\nexport const DevLensKey: InjectionKey<DevLensEngine> = Symbol('devlens');\n\nfunction isProductionEnv(): boolean {\n try {\n return (\n typeof process !== 'undefined' &&\n process.env?.NODE_ENV === 'production'\n );\n } catch {\n return false;\n }\n}\n\nexport interface DevLensPluginOptions extends DevLensConfig {}\n\nexport function createDevLensPlugin(options: DevLensPluginOptions = {}) {\n let engine: DevLensEngine | null = null;\n const cleanups: Array<{ uninstall(): void }> = [];\n\n return {\n install(app: App): void {\n if (options.enabled === false || isProductionEnv()) {\n return;\n }\n\n engine = createDetectionEngine(options);\n app.provide(DevLensKey, engine);\n\n const networkConfig =\n options.modules?.network === false ? undefined : options.modules?.network;\n const catcherConfig =\n options.modules?.catcher === false ? undefined : options.modules?.catcher;\n\n if (networkConfig !== undefined || options.modules?.network !== false) {\n const network = createNetworkInterceptor(engine, networkConfig);\n network.install();\n cleanups.push(network);\n }\n\n if (catcherConfig !== undefined || options.modules?.catcher !== false) {\n const catcher = createGlobalCatcher(engine, catcherConfig);\n catcher.install();\n cleanups.push(catcher);\n }\n\n app.config.errorHandler = (err, instance, info) => {\n if (!engine?.isEnabled()) return;\n\n const error = err instanceof Error ? err : new Error(String(err));\n const componentName =\n (instance?.$options?.name) ??\n (instance?.$options?.__name) ??\n 'AnonymousComponent';\n\n engine.report({\n id: `unhandled-error:vue:${error.message}`,\n timestamp: Date.now(),\n severity: 'error',\n category: 'unhandled-error',\n message: `Vue error in ${componentName}: ${error.message}`,\n details: {\n component: componentName,\n lifecycleHook: info,\n },\n stack: error.stack,\n source: `Vue:${componentName}`,\n suggestion: `Error in ${info} of ${componentName} — check the component logic`,\n });\n };\n\n app.config.warnHandler = (msg, instance, trace) => {\n if (!engine?.isEnabled()) return;\n\n const componentName =\n (instance?.$options?.name) ??\n (instance?.$options?.__name) ??\n 'AnonymousComponent';\n\n engine.report({\n id: `unhandled-error:vue-warn:${msg.slice(0, 80)}`,\n timestamp: Date.now(),\n severity: 'warn',\n category: 'unhandled-error',\n message: `Vue warning in ${componentName}: ${msg}`,\n details: {\n component: componentName,\n trace,\n },\n source: `Vue:${componentName}`,\n suggestion: 'Check the Vue warning above — it may indicate a potential issue',\n });\n };\n },\n\n uninstall(): void {\n for (const cleanup of cleanups) {\n cleanup.uninstall();\n }\n cleanups.length = 0;\n engine = null;\n },\n\n getEngine(): DevLensEngine | null {\n return engine;\n },\n };\n}\n","import { inject, ref, watch, type Ref } from 'vue';\nimport type { DevLensEngine, DetectedIssue } from '@devlens/core';\nimport { createDataGuardian } from '@devlens/core';\nimport { DevLensKey } from './plugin';\n\nexport function useDevLens(): DevLensEngine | null {\n return inject(DevLensKey, null);\n}\n\nexport function useGuardedRef<T extends object>(\n initialValue: T,\n label?: string,\n): Ref<T> {\n const engine = useDevLens();\n const source = ref(initialValue) as Ref<T>;\n\n if (!engine || !engine.isEnabled()) {\n return source;\n }\n\n const guardian = createDataGuardian(engine);\n const guarded = ref(guardian.guard(source.value, label)) as Ref<T>;\n\n watch(source, (newVal) => {\n if (newVal === null || newVal === undefined || typeof newVal !== 'object') {\n guarded.value = newVal;\n return;\n }\n const freshGuardian = createDataGuardian(engine);\n guarded.value = freshGuardian.guard(newVal, label);\n }, { deep: true });\n\n return guarded;\n}\n\nexport function useGuardedWatch(\n data: Record<string, unknown>,\n label?: string,\n): void {\n const engine = useDevLens();\n if (!engine || !engine.isEnabled()) return;\n\n const resolvedLabel = label ?? 'useGuardedWatch';\n\n const dataRef = ref(data);\n\n watch(dataRef, (current) => {\n for (const [key, value] of Object.entries(current)) {\n if (value === null || value === undefined) {\n const issue: DetectedIssue = {\n id: `render-data:${resolvedLabel}:${key}`,\n timestamp: Date.now(),\n severity: 'warn',\n category: 'render-data',\n message: `\"${key}\" is ${value === null ? 'null' : 'undefined'} in ${resolvedLabel}`,\n path: `${resolvedLabel}.${key}`,\n foundValue: value,\n expectedType: 'non-nullish value',\n source: resolvedLabel,\n suggestion: `\"${key}\" is ${value === null ? 'null' : 'undefined'} — check data loading in ${resolvedLabel}`,\n };\n engine.report(issue);\n }\n }\n }, { immediate: true, deep: true });\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@devlens/vue",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DevLens Vue integration - automatic error detection for Vue apps",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"typecheck": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"devlens",
|
|
33
|
+
"crashsense",
|
|
34
|
+
"vue",
|
|
35
|
+
"debug",
|
|
36
|
+
"error-detection",
|
|
37
|
+
"developer-tools"
|
|
38
|
+
],
|
|
39
|
+
"author": "crashsense",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/crashsense/devlens.git",
|
|
43
|
+
"directory": "packages/vue"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/crashsense/devlens#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/crashsense/devlens/issues"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"vue": ">=3.3.0"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@devlens/core": "2.0.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "^25.3.0",
|
|
61
|
+
"tsup": "^8.4.0",
|
|
62
|
+
"vitest": "^3.0.0",
|
|
63
|
+
"vue": "^3.5.0"
|
|
64
|
+
}
|
|
65
|
+
}
|