@devlens/react 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/dist/index.d.mts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +162 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +155 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +71 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode, Component, ErrorInfo } from 'react';
|
|
4
|
+
import { DevLensConfig, DevLensEngine } from '@devlens/core';
|
|
5
|
+
export { DetectedIssue, DevLensConfig, IssueCategory, Severity } from '@devlens/core';
|
|
6
|
+
|
|
7
|
+
interface DevLensProviderProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
config?: DevLensConfig;
|
|
10
|
+
}
|
|
11
|
+
declare function DevLensProvider({ children, config, }: DevLensProviderProps): react_jsx_runtime.JSX.Element;
|
|
12
|
+
|
|
13
|
+
interface DevLensErrorBoundaryProps {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
16
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
17
|
+
}
|
|
18
|
+
interface ErrorBoundaryState {
|
|
19
|
+
error: Error | null;
|
|
20
|
+
}
|
|
21
|
+
declare class DevLensErrorBoundary extends Component<DevLensErrorBoundaryProps, ErrorBoundaryState> {
|
|
22
|
+
static contextType: react.Context<DevLensEngine | null>;
|
|
23
|
+
context: DevLensEngine | null;
|
|
24
|
+
constructor(props: DevLensErrorBoundaryProps);
|
|
25
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
26
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
27
|
+
reset: () => void;
|
|
28
|
+
render(): ReactNode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare function useDevLens(): DevLensEngine | null;
|
|
32
|
+
declare function useGuardedState<T extends object>(initialState: T | (() => T), label?: string): [T, React.Dispatch<React.SetStateAction<T>>];
|
|
33
|
+
declare function useGuardedEffect(data: Record<string, unknown>, label?: string): void;
|
|
34
|
+
|
|
35
|
+
declare const DevLensContext: react.Context<DevLensEngine | null>;
|
|
36
|
+
|
|
37
|
+
export { DevLensContext, DevLensErrorBoundary, type DevLensErrorBoundaryProps, DevLensProvider, type DevLensProviderProps, useDevLens, useGuardedEffect, useGuardedState };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode, Component, ErrorInfo } from 'react';
|
|
4
|
+
import { DevLensConfig, DevLensEngine } from '@devlens/core';
|
|
5
|
+
export { DetectedIssue, DevLensConfig, IssueCategory, Severity } from '@devlens/core';
|
|
6
|
+
|
|
7
|
+
interface DevLensProviderProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
config?: DevLensConfig;
|
|
10
|
+
}
|
|
11
|
+
declare function DevLensProvider({ children, config, }: DevLensProviderProps): react_jsx_runtime.JSX.Element;
|
|
12
|
+
|
|
13
|
+
interface DevLensErrorBoundaryProps {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
16
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
17
|
+
}
|
|
18
|
+
interface ErrorBoundaryState {
|
|
19
|
+
error: Error | null;
|
|
20
|
+
}
|
|
21
|
+
declare class DevLensErrorBoundary extends Component<DevLensErrorBoundaryProps, ErrorBoundaryState> {
|
|
22
|
+
static contextType: react.Context<DevLensEngine | null>;
|
|
23
|
+
context: DevLensEngine | null;
|
|
24
|
+
constructor(props: DevLensErrorBoundaryProps);
|
|
25
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
26
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
27
|
+
reset: () => void;
|
|
28
|
+
render(): ReactNode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare function useDevLens(): DevLensEngine | null;
|
|
32
|
+
declare function useGuardedState<T extends object>(initialState: T | (() => T), label?: string): [T, React.Dispatch<React.SetStateAction<T>>];
|
|
33
|
+
declare function useGuardedEffect(data: Record<string, unknown>, label?: string): void;
|
|
34
|
+
|
|
35
|
+
declare const DevLensContext: react.Context<DevLensEngine | null>;
|
|
36
|
+
|
|
37
|
+
export { DevLensContext, DevLensErrorBoundary, type DevLensErrorBoundaryProps, DevLensProvider, type DevLensProviderProps, useDevLens, useGuardedEffect, useGuardedState };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var core = require('@devlens/core');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
// src/provider.tsx
|
|
8
|
+
var DevLensContext = react.createContext(null);
|
|
9
|
+
function isProductionEnv() {
|
|
10
|
+
try {
|
|
11
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function DevLensProvider({
|
|
17
|
+
children,
|
|
18
|
+
config = {}
|
|
19
|
+
}) {
|
|
20
|
+
const engineRef = react.useRef(
|
|
21
|
+
null
|
|
22
|
+
);
|
|
23
|
+
const cleanupRef = react.useRef(null);
|
|
24
|
+
const installedRef = react.useRef(false);
|
|
25
|
+
if (config.enabled === false || isProductionEnv()) {
|
|
26
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
27
|
+
}
|
|
28
|
+
if (!engineRef.current) {
|
|
29
|
+
engineRef.current = core.createDetectionEngine(config);
|
|
30
|
+
}
|
|
31
|
+
react.useEffect(() => {
|
|
32
|
+
const engine = engineRef.current;
|
|
33
|
+
if (!engine || installedRef.current) return;
|
|
34
|
+
installedRef.current = true;
|
|
35
|
+
const networkConfig = config.modules?.network === false ? void 0 : config.modules?.network;
|
|
36
|
+
const catcherConfig = config.modules?.catcher === false ? void 0 : config.modules?.catcher;
|
|
37
|
+
const interceptors = [];
|
|
38
|
+
if (networkConfig !== void 0 || config.modules?.network !== false) {
|
|
39
|
+
const network = core.createNetworkInterceptor(engine, networkConfig);
|
|
40
|
+
network.install();
|
|
41
|
+
interceptors.push(network);
|
|
42
|
+
}
|
|
43
|
+
if (catcherConfig !== void 0 || config.modules?.catcher !== false) {
|
|
44
|
+
const catcher = core.createGlobalCatcher(engine, catcherConfig);
|
|
45
|
+
catcher.install();
|
|
46
|
+
interceptors.push(catcher);
|
|
47
|
+
}
|
|
48
|
+
cleanupRef.current = () => {
|
|
49
|
+
for (const interceptor of interceptors) {
|
|
50
|
+
interceptor.uninstall();
|
|
51
|
+
}
|
|
52
|
+
installedRef.current = false;
|
|
53
|
+
};
|
|
54
|
+
return () => {
|
|
55
|
+
cleanupRef.current?.();
|
|
56
|
+
cleanupRef.current = null;
|
|
57
|
+
};
|
|
58
|
+
}, []);
|
|
59
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DevLensContext.Provider, { value: engineRef.current, children });
|
|
60
|
+
}
|
|
61
|
+
var DevLensErrorBoundary = class extends react.Component {
|
|
62
|
+
constructor(props) {
|
|
63
|
+
super(props);
|
|
64
|
+
this.reset = () => {
|
|
65
|
+
this.setState({ error: null });
|
|
66
|
+
};
|
|
67
|
+
this.state = { error: null };
|
|
68
|
+
}
|
|
69
|
+
static getDerivedStateFromError(error) {
|
|
70
|
+
return { error };
|
|
71
|
+
}
|
|
72
|
+
componentDidCatch(error, errorInfo) {
|
|
73
|
+
const engine = this.context;
|
|
74
|
+
if (engine?.isEnabled()) {
|
|
75
|
+
const issue = {
|
|
76
|
+
id: `unhandled-error:render:${error.message}`,
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
severity: "error",
|
|
79
|
+
category: "unhandled-error",
|
|
80
|
+
message: `React render error: ${error.message}`,
|
|
81
|
+
details: {
|
|
82
|
+
componentStack: errorInfo.componentStack ?? "unavailable"
|
|
83
|
+
},
|
|
84
|
+
stack: error.stack,
|
|
85
|
+
source: "DevLensErrorBoundary",
|
|
86
|
+
suggestion: "A React component threw during render. Check the component stack above."
|
|
87
|
+
};
|
|
88
|
+
engine.report(issue);
|
|
89
|
+
}
|
|
90
|
+
this.props.onError?.(error, errorInfo);
|
|
91
|
+
}
|
|
92
|
+
render() {
|
|
93
|
+
const { error } = this.state;
|
|
94
|
+
const { children, fallback } = this.props;
|
|
95
|
+
if (error) {
|
|
96
|
+
if (typeof fallback === "function") {
|
|
97
|
+
return fallback(error, this.reset);
|
|
98
|
+
}
|
|
99
|
+
if (fallback !== void 0) {
|
|
100
|
+
return fallback;
|
|
101
|
+
}
|
|
102
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", children: [
|
|
103
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "Something went wrong:" }),
|
|
104
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { children: error.message })
|
|
105
|
+
] });
|
|
106
|
+
}
|
|
107
|
+
return children;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
DevLensErrorBoundary.contextType = DevLensContext;
|
|
111
|
+
function useDevLens() {
|
|
112
|
+
return react.useContext(DevLensContext);
|
|
113
|
+
}
|
|
114
|
+
function useGuardedState(initialState, label) {
|
|
115
|
+
const engine = useDevLens();
|
|
116
|
+
const [state, setState] = react.useState(initialState);
|
|
117
|
+
const guardedState = react.useMemo(() => {
|
|
118
|
+
if (!engine || !engine.isEnabled()) return state;
|
|
119
|
+
if (state === null || state === void 0) return state;
|
|
120
|
+
if (typeof state !== "object") return state;
|
|
121
|
+
const guardian = core.createDataGuardian(engine);
|
|
122
|
+
return guardian.guard(state, label);
|
|
123
|
+
}, [state, engine, label]);
|
|
124
|
+
return [guardedState, setState];
|
|
125
|
+
}
|
|
126
|
+
function useGuardedEffect(data, label) {
|
|
127
|
+
const engine = useDevLens();
|
|
128
|
+
const dataRef = react.useRef(data);
|
|
129
|
+
dataRef.current = data;
|
|
130
|
+
react.useEffect(() => {
|
|
131
|
+
if (!engine || !engine.isEnabled()) return;
|
|
132
|
+
const currentData = dataRef.current;
|
|
133
|
+
const resolvedLabel = label ?? "useGuardedEffect";
|
|
134
|
+
const entries = Object.entries(currentData);
|
|
135
|
+
for (const [key, value] of entries) {
|
|
136
|
+
if (value === null || value === void 0) {
|
|
137
|
+
const issue = {
|
|
138
|
+
id: `render-data:${resolvedLabel}:${key}`,
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
severity: "warn",
|
|
141
|
+
category: "render-data",
|
|
142
|
+
message: `Render data "${key}" is ${value === null ? "null" : "undefined"} in ${resolvedLabel}`,
|
|
143
|
+
path: `${resolvedLabel}.${key}`,
|
|
144
|
+
foundValue: value,
|
|
145
|
+
expectedType: "non-nullish value",
|
|
146
|
+
source: resolvedLabel,
|
|
147
|
+
suggestion: `"${key}" is ${value === null ? "null" : "undefined"} \u2014 this may cause the UI to not render correctly. Check data loading.`
|
|
148
|
+
};
|
|
149
|
+
engine.report(issue);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
exports.DevLensContext = DevLensContext;
|
|
156
|
+
exports.DevLensErrorBoundary = DevLensErrorBoundary;
|
|
157
|
+
exports.DevLensProvider = DevLensProvider;
|
|
158
|
+
exports.useDevLens = useDevLens;
|
|
159
|
+
exports.useGuardedEffect = useGuardedEffect;
|
|
160
|
+
exports.useGuardedState = useGuardedState;
|
|
161
|
+
//# sourceMappingURL=index.js.map
|
|
162
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/error-boundary.tsx","../src/hooks.ts"],"names":["createContext","useRef","createDetectionEngine","useEffect","createNetworkInterceptor","createGlobalCatcher","Component","jsxs","jsx","useContext","useState","useMemo","createDataGuardian"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext } from 'react';\nimport type { DevLensEngine } from '@devlens/core';\n\nexport const DevLensContext = createContext<DevLensEngine | null>(null);\n","import { useEffect, useRef, type ReactNode } from 'react';\nimport type { DevLensConfig } from '@devlens/core';\nimport {\n createDetectionEngine,\n createNetworkInterceptor,\n createGlobalCatcher,\n} from '@devlens/core';\nimport { DevLensContext } from './context';\n\nexport interface DevLensProviderProps {\n children: ReactNode;\n config?: DevLensConfig;\n}\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 function DevLensProvider({\n children,\n config = {},\n}: DevLensProviderProps) {\n const engineRef = useRef<ReturnType<typeof createDetectionEngine> | null>(\n null,\n );\n const cleanupRef = useRef<(() => void) | null>(null);\n const installedRef = useRef(false);\n\n if (config.enabled === false || isProductionEnv()) {\n return <>{children}</>;\n }\n\n if (!engineRef.current) {\n engineRef.current = createDetectionEngine(config);\n }\n\n useEffect(() => {\n const engine = engineRef.current;\n if (!engine || installedRef.current) return;\n\n installedRef.current = true;\n\n const networkConfig =\n config.modules?.network === false ? undefined : config.modules?.network;\n const catcherConfig =\n config.modules?.catcher === false ? undefined : config.modules?.catcher;\n\n const interceptors: Array<{ uninstall(): void }> = [];\n\n if (networkConfig !== undefined || config.modules?.network !== false) {\n const network = createNetworkInterceptor(engine, networkConfig);\n network.install();\n interceptors.push(network);\n }\n\n if (catcherConfig !== undefined || config.modules?.catcher !== false) {\n const catcher = createGlobalCatcher(engine, catcherConfig);\n catcher.install();\n interceptors.push(catcher);\n }\n\n cleanupRef.current = () => {\n for (const interceptor of interceptors) {\n interceptor.uninstall();\n }\n installedRef.current = false;\n };\n\n return () => {\n cleanupRef.current?.();\n cleanupRef.current = null;\n };\n }, []);\n\n return (\n <DevLensContext.Provider value={engineRef.current}>\n {children}\n </DevLensContext.Provider>\n );\n}\n","import { Component, type ReactNode, type ErrorInfo } from 'react';\nimport type { DevLensEngine, DetectedIssue } from '@devlens/core';\nimport { DevLensContext } from './context';\n\nexport interface DevLensErrorBoundaryProps {\n children: ReactNode;\n fallback?:\n | ReactNode\n | ((error: Error, reset: () => void) => ReactNode);\n onError?: (error: Error, errorInfo: ErrorInfo) => void;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\nexport class DevLensErrorBoundary extends Component<\n DevLensErrorBoundaryProps,\n ErrorBoundaryState\n> {\n static contextType = DevLensContext;\n declare context: DevLensEngine | null;\n\n constructor(props: DevLensErrorBoundaryProps) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n const engine = this.context;\n\n if (engine?.isEnabled()) {\n const issue: DetectedIssue = {\n id: `unhandled-error:render:${error.message}`,\n timestamp: Date.now(),\n severity: 'error',\n category: 'unhandled-error',\n message: `React render error: ${error.message}`,\n details: {\n componentStack: errorInfo.componentStack ?? 'unavailable',\n },\n stack: error.stack,\n source: 'DevLensErrorBoundary',\n suggestion:\n 'A React component threw during render. Check the component stack above.',\n };\n engine.report(issue);\n }\n\n this.props.onError?.(error, errorInfo);\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n render(): ReactNode {\n const { error } = this.state;\n const { children, fallback } = this.props;\n\n if (error) {\n if (typeof fallback === 'function') {\n return fallback(error, this.reset);\n }\n if (fallback !== undefined) {\n return fallback;\n }\n return (\n <div role=\"alert\">\n <p>Something went wrong:</p>\n <pre>{error.message}</pre>\n </div>\n );\n }\n\n return children;\n }\n}\n","import { useContext, useState, useMemo, useEffect, useRef } from 'react';\nimport type { DevLensEngine, DetectedIssue } from '@devlens/core';\nimport { createDataGuardian } from '@devlens/core';\nimport { DevLensContext } from './context';\n\nexport function useDevLens(): DevLensEngine | null {\n return useContext(DevLensContext);\n}\n\nexport function useGuardedState<T extends object>(\n initialState: T | (() => T),\n label?: string,\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n const engine = useDevLens();\n const [state, setState] = useState<T>(initialState);\n\n const guardedState = useMemo(() => {\n if (!engine || !engine.isEnabled()) return state;\n if (state === null || state === undefined) return state;\n if (typeof state !== 'object') return state;\n\n const guardian = createDataGuardian(engine);\n return guardian.guard(state, label);\n }, [state, engine, label]);\n\n return [guardedState, setState];\n}\n\nexport function useGuardedEffect(\n data: Record<string, unknown>,\n label?: string,\n): void {\n const engine = useDevLens();\n const dataRef = useRef(data);\n dataRef.current = data;\n\n useEffect(() => {\n if (!engine || !engine.isEnabled()) return;\n\n const currentData = dataRef.current;\n const resolvedLabel = label ?? 'useGuardedEffect';\n\n const entries = Object.entries(currentData);\n for (const [key, value] of entries) {\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: `Render data \"${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'} — this may cause the UI to not render correctly. Check data loading.`,\n };\n engine.report(issue);\n }\n }\n });\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { createContext, Component, useRef, useEffect, useContext, useState, useMemo } from 'react';
|
|
2
|
+
import { createDetectionEngine, createNetworkInterceptor, createGlobalCatcher, createDataGuardian } from '@devlens/core';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// src/provider.tsx
|
|
6
|
+
var DevLensContext = createContext(null);
|
|
7
|
+
function isProductionEnv() {
|
|
8
|
+
try {
|
|
9
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function DevLensProvider({
|
|
15
|
+
children,
|
|
16
|
+
config = {}
|
|
17
|
+
}) {
|
|
18
|
+
const engineRef = useRef(
|
|
19
|
+
null
|
|
20
|
+
);
|
|
21
|
+
const cleanupRef = useRef(null);
|
|
22
|
+
const installedRef = useRef(false);
|
|
23
|
+
if (config.enabled === false || isProductionEnv()) {
|
|
24
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
25
|
+
}
|
|
26
|
+
if (!engineRef.current) {
|
|
27
|
+
engineRef.current = createDetectionEngine(config);
|
|
28
|
+
}
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const engine = engineRef.current;
|
|
31
|
+
if (!engine || installedRef.current) return;
|
|
32
|
+
installedRef.current = true;
|
|
33
|
+
const networkConfig = config.modules?.network === false ? void 0 : config.modules?.network;
|
|
34
|
+
const catcherConfig = config.modules?.catcher === false ? void 0 : config.modules?.catcher;
|
|
35
|
+
const interceptors = [];
|
|
36
|
+
if (networkConfig !== void 0 || config.modules?.network !== false) {
|
|
37
|
+
const network = createNetworkInterceptor(engine, networkConfig);
|
|
38
|
+
network.install();
|
|
39
|
+
interceptors.push(network);
|
|
40
|
+
}
|
|
41
|
+
if (catcherConfig !== void 0 || config.modules?.catcher !== false) {
|
|
42
|
+
const catcher = createGlobalCatcher(engine, catcherConfig);
|
|
43
|
+
catcher.install();
|
|
44
|
+
interceptors.push(catcher);
|
|
45
|
+
}
|
|
46
|
+
cleanupRef.current = () => {
|
|
47
|
+
for (const interceptor of interceptors) {
|
|
48
|
+
interceptor.uninstall();
|
|
49
|
+
}
|
|
50
|
+
installedRef.current = false;
|
|
51
|
+
};
|
|
52
|
+
return () => {
|
|
53
|
+
cleanupRef.current?.();
|
|
54
|
+
cleanupRef.current = null;
|
|
55
|
+
};
|
|
56
|
+
}, []);
|
|
57
|
+
return /* @__PURE__ */ jsx(DevLensContext.Provider, { value: engineRef.current, children });
|
|
58
|
+
}
|
|
59
|
+
var DevLensErrorBoundary = class extends Component {
|
|
60
|
+
constructor(props) {
|
|
61
|
+
super(props);
|
|
62
|
+
this.reset = () => {
|
|
63
|
+
this.setState({ error: null });
|
|
64
|
+
};
|
|
65
|
+
this.state = { error: null };
|
|
66
|
+
}
|
|
67
|
+
static getDerivedStateFromError(error) {
|
|
68
|
+
return { error };
|
|
69
|
+
}
|
|
70
|
+
componentDidCatch(error, errorInfo) {
|
|
71
|
+
const engine = this.context;
|
|
72
|
+
if (engine?.isEnabled()) {
|
|
73
|
+
const issue = {
|
|
74
|
+
id: `unhandled-error:render:${error.message}`,
|
|
75
|
+
timestamp: Date.now(),
|
|
76
|
+
severity: "error",
|
|
77
|
+
category: "unhandled-error",
|
|
78
|
+
message: `React render error: ${error.message}`,
|
|
79
|
+
details: {
|
|
80
|
+
componentStack: errorInfo.componentStack ?? "unavailable"
|
|
81
|
+
},
|
|
82
|
+
stack: error.stack,
|
|
83
|
+
source: "DevLensErrorBoundary",
|
|
84
|
+
suggestion: "A React component threw during render. Check the component stack above."
|
|
85
|
+
};
|
|
86
|
+
engine.report(issue);
|
|
87
|
+
}
|
|
88
|
+
this.props.onError?.(error, errorInfo);
|
|
89
|
+
}
|
|
90
|
+
render() {
|
|
91
|
+
const { error } = this.state;
|
|
92
|
+
const { children, fallback } = this.props;
|
|
93
|
+
if (error) {
|
|
94
|
+
if (typeof fallback === "function") {
|
|
95
|
+
return fallback(error, this.reset);
|
|
96
|
+
}
|
|
97
|
+
if (fallback !== void 0) {
|
|
98
|
+
return fallback;
|
|
99
|
+
}
|
|
100
|
+
return /* @__PURE__ */ jsxs("div", { role: "alert", children: [
|
|
101
|
+
/* @__PURE__ */ jsx("p", { children: "Something went wrong:" }),
|
|
102
|
+
/* @__PURE__ */ jsx("pre", { children: error.message })
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
return children;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
DevLensErrorBoundary.contextType = DevLensContext;
|
|
109
|
+
function useDevLens() {
|
|
110
|
+
return useContext(DevLensContext);
|
|
111
|
+
}
|
|
112
|
+
function useGuardedState(initialState, label) {
|
|
113
|
+
const engine = useDevLens();
|
|
114
|
+
const [state, setState] = useState(initialState);
|
|
115
|
+
const guardedState = useMemo(() => {
|
|
116
|
+
if (!engine || !engine.isEnabled()) return state;
|
|
117
|
+
if (state === null || state === void 0) return state;
|
|
118
|
+
if (typeof state !== "object") return state;
|
|
119
|
+
const guardian = createDataGuardian(engine);
|
|
120
|
+
return guardian.guard(state, label);
|
|
121
|
+
}, [state, engine, label]);
|
|
122
|
+
return [guardedState, setState];
|
|
123
|
+
}
|
|
124
|
+
function useGuardedEffect(data, label) {
|
|
125
|
+
const engine = useDevLens();
|
|
126
|
+
const dataRef = useRef(data);
|
|
127
|
+
dataRef.current = data;
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (!engine || !engine.isEnabled()) return;
|
|
130
|
+
const currentData = dataRef.current;
|
|
131
|
+
const resolvedLabel = label ?? "useGuardedEffect";
|
|
132
|
+
const entries = Object.entries(currentData);
|
|
133
|
+
for (const [key, value] of entries) {
|
|
134
|
+
if (value === null || value === void 0) {
|
|
135
|
+
const issue = {
|
|
136
|
+
id: `render-data:${resolvedLabel}:${key}`,
|
|
137
|
+
timestamp: Date.now(),
|
|
138
|
+
severity: "warn",
|
|
139
|
+
category: "render-data",
|
|
140
|
+
message: `Render data "${key}" is ${value === null ? "null" : "undefined"} in ${resolvedLabel}`,
|
|
141
|
+
path: `${resolvedLabel}.${key}`,
|
|
142
|
+
foundValue: value,
|
|
143
|
+
expectedType: "non-nullish value",
|
|
144
|
+
source: resolvedLabel,
|
|
145
|
+
suggestion: `"${key}" is ${value === null ? "null" : "undefined"} \u2014 this may cause the UI to not render correctly. Check data loading.`
|
|
146
|
+
};
|
|
147
|
+
engine.report(issue);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { DevLensContext, DevLensErrorBoundary, DevLensProvider, useDevLens, useGuardedEffect, useGuardedState };
|
|
154
|
+
//# sourceMappingURL=index.mjs.map
|
|
155
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/error-boundary.tsx","../src/hooks.ts"],"names":["jsx","useRef","useEffect"],"mappings":"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","file":"index.mjs","sourcesContent":["import { createContext } from 'react';\nimport type { DevLensEngine } from '@devlens/core';\n\nexport const DevLensContext = createContext<DevLensEngine | null>(null);\n","import { useEffect, useRef, type ReactNode } from 'react';\nimport type { DevLensConfig } from '@devlens/core';\nimport {\n createDetectionEngine,\n createNetworkInterceptor,\n createGlobalCatcher,\n} from '@devlens/core';\nimport { DevLensContext } from './context';\n\nexport interface DevLensProviderProps {\n children: ReactNode;\n config?: DevLensConfig;\n}\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 function DevLensProvider({\n children,\n config = {},\n}: DevLensProviderProps) {\n const engineRef = useRef<ReturnType<typeof createDetectionEngine> | null>(\n null,\n );\n const cleanupRef = useRef<(() => void) | null>(null);\n const installedRef = useRef(false);\n\n if (config.enabled === false || isProductionEnv()) {\n return <>{children}</>;\n }\n\n if (!engineRef.current) {\n engineRef.current = createDetectionEngine(config);\n }\n\n useEffect(() => {\n const engine = engineRef.current;\n if (!engine || installedRef.current) return;\n\n installedRef.current = true;\n\n const networkConfig =\n config.modules?.network === false ? undefined : config.modules?.network;\n const catcherConfig =\n config.modules?.catcher === false ? undefined : config.modules?.catcher;\n\n const interceptors: Array<{ uninstall(): void }> = [];\n\n if (networkConfig !== undefined || config.modules?.network !== false) {\n const network = createNetworkInterceptor(engine, networkConfig);\n network.install();\n interceptors.push(network);\n }\n\n if (catcherConfig !== undefined || config.modules?.catcher !== false) {\n const catcher = createGlobalCatcher(engine, catcherConfig);\n catcher.install();\n interceptors.push(catcher);\n }\n\n cleanupRef.current = () => {\n for (const interceptor of interceptors) {\n interceptor.uninstall();\n }\n installedRef.current = false;\n };\n\n return () => {\n cleanupRef.current?.();\n cleanupRef.current = null;\n };\n }, []);\n\n return (\n <DevLensContext.Provider value={engineRef.current}>\n {children}\n </DevLensContext.Provider>\n );\n}\n","import { Component, type ReactNode, type ErrorInfo } from 'react';\nimport type { DevLensEngine, DetectedIssue } from '@devlens/core';\nimport { DevLensContext } from './context';\n\nexport interface DevLensErrorBoundaryProps {\n children: ReactNode;\n fallback?:\n | ReactNode\n | ((error: Error, reset: () => void) => ReactNode);\n onError?: (error: Error, errorInfo: ErrorInfo) => void;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\nexport class DevLensErrorBoundary extends Component<\n DevLensErrorBoundaryProps,\n ErrorBoundaryState\n> {\n static contextType = DevLensContext;\n declare context: DevLensEngine | null;\n\n constructor(props: DevLensErrorBoundaryProps) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n const engine = this.context;\n\n if (engine?.isEnabled()) {\n const issue: DetectedIssue = {\n id: `unhandled-error:render:${error.message}`,\n timestamp: Date.now(),\n severity: 'error',\n category: 'unhandled-error',\n message: `React render error: ${error.message}`,\n details: {\n componentStack: errorInfo.componentStack ?? 'unavailable',\n },\n stack: error.stack,\n source: 'DevLensErrorBoundary',\n suggestion:\n 'A React component threw during render. Check the component stack above.',\n };\n engine.report(issue);\n }\n\n this.props.onError?.(error, errorInfo);\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n render(): ReactNode {\n const { error } = this.state;\n const { children, fallback } = this.props;\n\n if (error) {\n if (typeof fallback === 'function') {\n return fallback(error, this.reset);\n }\n if (fallback !== undefined) {\n return fallback;\n }\n return (\n <div role=\"alert\">\n <p>Something went wrong:</p>\n <pre>{error.message}</pre>\n </div>\n );\n }\n\n return children;\n }\n}\n","import { useContext, useState, useMemo, useEffect, useRef } from 'react';\nimport type { DevLensEngine, DetectedIssue } from '@devlens/core';\nimport { createDataGuardian } from '@devlens/core';\nimport { DevLensContext } from './context';\n\nexport function useDevLens(): DevLensEngine | null {\n return useContext(DevLensContext);\n}\n\nexport function useGuardedState<T extends object>(\n initialState: T | (() => T),\n label?: string,\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n const engine = useDevLens();\n const [state, setState] = useState<T>(initialState);\n\n const guardedState = useMemo(() => {\n if (!engine || !engine.isEnabled()) return state;\n if (state === null || state === undefined) return state;\n if (typeof state !== 'object') return state;\n\n const guardian = createDataGuardian(engine);\n return guardian.guard(state, label);\n }, [state, engine, label]);\n\n return [guardedState, setState];\n}\n\nexport function useGuardedEffect(\n data: Record<string, unknown>,\n label?: string,\n): void {\n const engine = useDevLens();\n const dataRef = useRef(data);\n dataRef.current = data;\n\n useEffect(() => {\n if (!engine || !engine.isEnabled()) return;\n\n const currentData = dataRef.current;\n const resolvedLabel = label ?? 'useGuardedEffect';\n\n const entries = Object.entries(currentData);\n for (const [key, value] of entries) {\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: `Render data \"${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'} — this may cause the UI to not render correctly. Check data loading.`,\n };\n engine.report(issue);\n }\n }\n });\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@devlens/react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DevLens React integration - automatic error detection for React apps",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.cts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
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
|
+
"test:watch": "vitest",
|
|
29
|
+
"clean": "rm -rf dist",
|
|
30
|
+
"typecheck": "tsc --noEmit"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"devlens",
|
|
34
|
+
"crashsense",
|
|
35
|
+
"react",
|
|
36
|
+
"debug",
|
|
37
|
+
"error-boundary",
|
|
38
|
+
"error-detection",
|
|
39
|
+
"developer-tools"
|
|
40
|
+
],
|
|
41
|
+
"author": "crashsense",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/crashsense/devlens.git",
|
|
45
|
+
"directory": "packages/react"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/crashsense/devlens#readme",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/crashsense/devlens/issues"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"react": ">=17.0.0",
|
|
57
|
+
"react-dom": ">=17.0.0"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@devlens/core": "1.0.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^25.3.0",
|
|
64
|
+
"@types/react": "^19.0.0",
|
|
65
|
+
"@types/react-dom": "^19.0.0",
|
|
66
|
+
"react": "^19.0.0",
|
|
67
|
+
"react-dom": "^19.0.0",
|
|
68
|
+
"tsup": "^8.4.0",
|
|
69
|
+
"vitest": "^3.0.0"
|
|
70
|
+
}
|
|
71
|
+
}
|