@crashsense/react 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,40 @@
1
+ import React from 'react';
2
+ import { CrashSenseCore, CrashSenseConfig, CrashReport, CrashSeverity, Breadcrumb } from '@crashsense/types';
3
+ export { CrashAnalysis, CrashEvent, CrashReport, CrashSenseConfig } from '@crashsense/types';
4
+ export { createCrashSense } from '@crashsense/core';
5
+
6
+ declare const CrashSenseContext: React.Context<CrashSenseCore | null>;
7
+ interface CrashSenseProviderProps {
8
+ config: CrashSenseConfig;
9
+ children: React.ReactNode;
10
+ fallback?: React.ReactNode;
11
+ onCrash?: (report: CrashReport) => void;
12
+ }
13
+ interface CrashSenseProviderState {
14
+ hasError: boolean;
15
+ }
16
+ declare class CrashSenseProvider extends React.Component<CrashSenseProviderProps, CrashSenseProviderState> {
17
+ private core;
18
+ constructor(props: CrashSenseProviderProps);
19
+ static getDerivedStateFromError(_error: Error): CrashSenseProviderState;
20
+ componentDidMount(): void;
21
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
22
+ componentWillUnmount(): void;
23
+ render(): React.ReactNode;
24
+ }
25
+
26
+ declare function useCrashSense(): {
27
+ captureException: (error: unknown, context?: Record<string, unknown>) => void;
28
+ captureMessage: (message: string, severity?: CrashSeverity) => void;
29
+ addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => void;
30
+ core: CrashSenseCore | null;
31
+ };
32
+
33
+ declare function useRenderTracker(componentName: string, threshold?: number): void;
34
+
35
+ declare function createHydrationDetector(core: CrashSenseCore): {
36
+ install(): void;
37
+ uninstall(): void;
38
+ };
39
+
40
+ export { CrashSenseContext, CrashSenseProvider, createHydrationDetector, useCrashSense, useRenderTracker };
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { CrashSenseCore, CrashSenseConfig, CrashReport, CrashSeverity, Breadcrumb } from '@crashsense/types';
3
+ export { CrashAnalysis, CrashEvent, CrashReport, CrashSenseConfig } from '@crashsense/types';
4
+ export { createCrashSense } from '@crashsense/core';
5
+
6
+ declare const CrashSenseContext: React.Context<CrashSenseCore | null>;
7
+ interface CrashSenseProviderProps {
8
+ config: CrashSenseConfig;
9
+ children: React.ReactNode;
10
+ fallback?: React.ReactNode;
11
+ onCrash?: (report: CrashReport) => void;
12
+ }
13
+ interface CrashSenseProviderState {
14
+ hasError: boolean;
15
+ }
16
+ declare class CrashSenseProvider extends React.Component<CrashSenseProviderProps, CrashSenseProviderState> {
17
+ private core;
18
+ constructor(props: CrashSenseProviderProps);
19
+ static getDerivedStateFromError(_error: Error): CrashSenseProviderState;
20
+ componentDidMount(): void;
21
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
22
+ componentWillUnmount(): void;
23
+ render(): React.ReactNode;
24
+ }
25
+
26
+ declare function useCrashSense(): {
27
+ captureException: (error: unknown, context?: Record<string, unknown>) => void;
28
+ captureMessage: (message: string, severity?: CrashSeverity) => void;
29
+ addBreadcrumb: (breadcrumb: Omit<Breadcrumb, 'timestamp'>) => void;
30
+ core: CrashSenseCore | null;
31
+ };
32
+
33
+ declare function useRenderTracker(componentName: string, threshold?: number): void;
34
+
35
+ declare function createHydrationDetector(core: CrashSenseCore): {
36
+ install(): void;
37
+ uninstall(): void;
38
+ };
39
+
40
+ export { CrashSenseContext, CrashSenseProvider, createHydrationDetector, useCrashSense, useRenderTracker };
package/dist/index.js ADDED
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var core = require('@crashsense/core');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var React__default = /*#__PURE__*/_interopDefault(React);
9
+
10
+ // src/provider.ts
11
+ var CrashSenseContext = React__default.default.createContext(null);
12
+ var CrashSenseProvider = class extends React__default.default.Component {
13
+ constructor(props) {
14
+ super(props);
15
+ this.core = null;
16
+ this.state = { hasError: false };
17
+ }
18
+ static getDerivedStateFromError(_error) {
19
+ return { hasError: true };
20
+ }
21
+ componentDidMount() {
22
+ const enrichedConfig = {
23
+ ...this.props.config,
24
+ onCrash: (report) => {
25
+ this.props.onCrash?.(report);
26
+ this.props.config.onCrash?.(report);
27
+ }
28
+ };
29
+ this.core = core.createCrashSense(enrichedConfig);
30
+ }
31
+ componentDidCatch(error, errorInfo) {
32
+ if (!this.core) return;
33
+ this.core.captureException(error, {
34
+ componentStack: errorInfo.componentStack ?? "",
35
+ framework: "react",
36
+ lifecycleStage: "rendering"
37
+ });
38
+ }
39
+ componentWillUnmount() {
40
+ this.core?.destroy();
41
+ this.core = null;
42
+ }
43
+ render() {
44
+ if (this.state.hasError && this.props.fallback !== void 0) {
45
+ return React__default.default.createElement(
46
+ CrashSenseContext.Provider,
47
+ { value: this.core },
48
+ this.props.fallback
49
+ );
50
+ }
51
+ return React__default.default.createElement(
52
+ CrashSenseContext.Provider,
53
+ { value: this.core },
54
+ this.props.children
55
+ );
56
+ }
57
+ };
58
+ function useCrashSense() {
59
+ const core = React.useContext(CrashSenseContext);
60
+ return {
61
+ captureException: (error, context) => {
62
+ core?.captureException(error, context);
63
+ },
64
+ captureMessage: (message, severity) => {
65
+ core?.captureMessage(message, severity);
66
+ },
67
+ addBreadcrumb: (breadcrumb) => {
68
+ core?.addBreadcrumb(breadcrumb);
69
+ },
70
+ core
71
+ };
72
+ }
73
+ var DEFAULT_THRESHOLD = 50;
74
+ var WINDOW_MS = 1e3;
75
+ function useRenderTracker(componentName, threshold = DEFAULT_THRESHOLD) {
76
+ const renderTimestamps = React.useRef([]);
77
+ const warned = React.useRef(false);
78
+ const { captureMessage } = useCrashSense();
79
+ const now = Date.now();
80
+ renderTimestamps.current.push(now);
81
+ const cutoff = now - WINDOW_MS;
82
+ renderTimestamps.current = renderTimestamps.current.filter((t) => t > cutoff);
83
+ if (renderTimestamps.current.length > threshold && !warned.current) {
84
+ warned.current = true;
85
+ captureMessage(
86
+ `Potential infinite re-render detected in ${componentName}: ${renderTimestamps.current.length} renders in ${WINDOW_MS}ms`,
87
+ "warning"
88
+ );
89
+ }
90
+ React.useEffect(() => {
91
+ return () => {
92
+ renderTimestamps.current = [];
93
+ warned.current = false;
94
+ };
95
+ }, []);
96
+ }
97
+
98
+ // src/hydration-detector.ts
99
+ function createHydrationDetector(core) {
100
+ let originalConsoleError = null;
101
+ return {
102
+ install() {
103
+ if (typeof console === "undefined") return;
104
+ originalConsoleError = console.error;
105
+ console.error = (...args) => {
106
+ originalConsoleError.apply(console, args);
107
+ const message = args.map(String).join(" ");
108
+ if (/hydrat/i.test(message)) {
109
+ core.captureException(
110
+ new Error(`Hydration mismatch: ${message.slice(0, 200)}`),
111
+ {
112
+ source: "hydration_detector",
113
+ framework: "react",
114
+ lifecycleStage: "hydrating"
115
+ }
116
+ );
117
+ }
118
+ };
119
+ },
120
+ uninstall() {
121
+ if (originalConsoleError) {
122
+ console.error = originalConsoleError;
123
+ originalConsoleError = null;
124
+ }
125
+ }
126
+ };
127
+ }
128
+
129
+ Object.defineProperty(exports, "createCrashSense", {
130
+ enumerable: true,
131
+ get: function () { return core.createCrashSense; }
132
+ });
133
+ exports.CrashSenseContext = CrashSenseContext;
134
+ exports.CrashSenseProvider = CrashSenseProvider;
135
+ exports.createHydrationDetector = createHydrationDetector;
136
+ exports.useCrashSense = useCrashSense;
137
+ exports.useRenderTracker = useRenderTracker;
138
+ //# sourceMappingURL=index.js.map
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.ts","../src/hooks.ts","../src/render-tracker.ts","../src/hydration-detector.ts"],"names":["React","createCrashSense","useContext","useRef","useEffect"],"mappings":";;;;;;;;;;AAIO,IAAM,iBAAA,GAAoBA,sBAAA,CAAM,aAAA,CAAqC,IAAI;AAazE,IAAM,kBAAA,GAAN,cAAiCA,sBAAA,CAAM,SAAA,CAG5C;AAAA,EAGA,YAAY,KAAA,EAAgC;AAC1C,IAAA,KAAA,CAAM,KAAK,CAAA;AAHb,IAAA,IAAA,CAAQ,IAAA,GAA8B,IAAA;AAIpC,IAAA,IAAA,CAAK,KAAA,GAAQ,EAAE,QAAA,EAAU,KAAA,EAAM;AAAA,EACjC;AAAA,EAEA,OAAO,yBAAyB,MAAA,EAAwC;AACtE,IAAA,OAAO,EAAE,UAAU,IAAA,EAAK;AAAA,EAC1B;AAAA,EAEA,iBAAA,GAA0B;AACxB,IAAA,MAAM,cAAA,GAAmC;AAAA,MACvC,GAAG,KAAK,KAAA,CAAM,MAAA;AAAA,MACd,OAAA,EAAS,CAAC,MAAA,KAAwB;AAChC,QAAA,IAAA,CAAK,KAAA,CAAM,UAAU,MAAM,CAAA;AAC3B,QAAA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,OAAA,GAAU,MAAM,CAAA;AAAA,MACpC;AAAA,KACF;AAEA,IAAA,IAAA,CAAK,IAAA,GAAOC,sBAAiB,cAAc,CAAA;AAAA,EAC7C;AAAA,EAEA,iBAAA,CAAkB,OAAc,SAAA,EAAkC;AAChE,IAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AAEhB,IAAA,IAAA,CAAK,IAAA,CAAK,iBAAiB,KAAA,EAAO;AAAA,MAChC,cAAA,EAAgB,UAAU,cAAA,IAAkB,EAAA;AAAA,MAC5C,SAAA,EAAW,OAAA;AAAA,MACX,cAAA,EAAgB;AAAA,KACjB,CAAA;AAAA,EACH;AAAA,EAEA,oBAAA,GAA6B;AAC3B,IAAA,IAAA,CAAK,MAAM,OAAA,EAAQ;AACnB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AAAA,EAEA,MAAA,GAA0B;AACxB,IAAA,IAAI,KAAK,KAAA,CAAM,QAAA,IAAY,IAAA,CAAK,KAAA,CAAM,aAAa,MAAA,EAAW;AAC5D,MAAA,OAAOD,sBAAA,CAAM,aAAA;AAAA,QACX,iBAAA,CAAkB,QAAA;AAAA,QAClB,EAAE,KAAA,EAAO,IAAA,CAAK,IAAA,EAAK;AAAA,QACnB,KAAK,KAAA,CAAM;AAAA,OACb;AAAA,IACF;AAEA,IAAA,OAAOA,sBAAA,CAAM,aAAA;AAAA,MACX,iBAAA,CAAkB,QAAA;AAAA,MAClB,EAAE,KAAA,EAAO,IAAA,CAAK,IAAA,EAAK;AAAA,MACnB,KAAK,KAAA,CAAM;AAAA,KACb;AAAA,EACF;AACF;ACtEO,SAAS,aAAA,GAKd;AACA,EAAA,MAAM,IAAA,GAAOE,iBAAW,iBAAiB,CAAA;AAEzC,EAAA,OAAO;AAAA,IACL,gBAAA,EAAkB,CAAC,KAAA,EAAgB,OAAA,KAAsC;AACvE,MAAA,IAAA,EAAM,gBAAA,CAAiB,OAAO,OAAO,CAAA;AAAA,IACvC,CAAA;AAAA,IACA,cAAA,EAAgB,CAAC,OAAA,EAAiB,QAAA,KAA6B;AAC7D,MAAA,IAAA,EAAM,cAAA,CAAe,SAAS,QAAQ,CAAA;AAAA,IACxC,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,UAAA,KAA8C;AAC5D,MAAA,IAAA,EAAM,cAAc,UAAU,CAAA;AAAA,IAChC,CAAA;AAAA,IACA;AAAA,GACF;AACF;ACrBA,IAAM,iBAAA,GAAoB,EAAA;AAC1B,IAAM,SAAA,GAAY,GAAA;AAEX,SAAS,gBAAA,CACd,aAAA,EACA,SAAA,GAAoB,iBAAA,EACd;AACN,EAAA,MAAM,gBAAA,GAAmBC,YAAA,CAAiB,EAAE,CAAA;AAC5C,EAAA,MAAM,MAAA,GAASA,aAAO,KAAK,CAAA;AAC3B,EAAA,MAAM,EAAE,cAAA,EAAe,GAAI,aAAA,EAAc;AAEzC,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,gBAAA,CAAiB,OAAA,CAAQ,KAAK,GAAG,CAAA;AAEjC,EAAA,MAAM,SAAS,GAAA,GAAM,SAAA;AACrB,EAAA,gBAAA,CAAiB,UAAU,gBAAA,CAAiB,OAAA,CAAQ,OAAO,CAAC,CAAA,KAAM,IAAI,MAAM,CAAA;AAE5E,EAAA,IAAI,iBAAiB,OAAA,CAAQ,MAAA,GAAS,SAAA,IAAa,CAAC,OAAO,OAAA,EAAS;AAClE,IAAA,MAAA,CAAO,OAAA,GAAU,IAAA;AACjB,IAAA,cAAA;AAAA,MACE,4CAA4C,aAAa,CAAA,EAAA,EAAK,iBAAiB,OAAA,CAAQ,MAAM,eAAe,SAAS,CAAA,EAAA,CAAA;AAAA,MACrH;AAAA,KACF;AAAA,EACF;AAEA,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,OAAO,MAAM;AACX,MAAA,gBAAA,CAAiB,UAAU,EAAC;AAC5B,MAAA,MAAA,CAAO,OAAA,GAAU,KAAA;AAAA,IACnB,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AACP;;;AChCO,SAAS,wBAAwB,IAAA,EAAsB;AAC5D,EAAA,IAAI,oBAAA,GAAoD,IAAA;AAExD,EAAA,OAAO;AAAA,IACL,OAAA,GAAgB;AACd,MAAA,IAAI,OAAO,YAAY,WAAA,EAAa;AAEpC,MAAA,oBAAA,GAAuB,OAAA,CAAQ,KAAA;AAE/B,MAAA,OAAA,CAAQ,KAAA,GAAQ,IAAI,IAAA,KAAoB;AACtC,QAAA,oBAAA,CAAsB,KAAA,CAAM,SAAS,IAAI,CAAA;AAEzC,QAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,CAAE,KAAK,GAAG,CAAA;AACzC,QAAA,IAAI,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA,EAAG;AAC3B,UAAA,IAAA,CAAK,gBAAA;AAAA,YACH,IAAI,MAAM,CAAA,oBAAA,EAAuB,OAAA,CAAQ,MAAM,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,YACxD;AAAA,cACE,MAAA,EAAQ,oBAAA;AAAA,cACR,SAAA,EAAW,OAAA;AAAA,cACX,cAAA,EAAgB;AAAA;AAClB,WACF;AAAA,QACF;AAAA,MACF,CAAA;AAAA,IACF,CAAA;AAAA,IAEA,SAAA,GAAkB;AAChB,MAAA,IAAI,oBAAA,EAAsB;AACxB,QAAA,OAAA,CAAQ,KAAA,GAAQ,oBAAA;AAChB,QAAA,oBAAA,GAAuB,IAAA;AAAA,MACzB;AAAA,IACF;AAAA,GACF;AACF","file":"index.js","sourcesContent":["import React from 'react';\nimport type { CrashSenseConfig, CrashSenseCore, CrashReport } from '@crashsense/types';\nimport { createCrashSense } from '@crashsense/core';\n\nexport const CrashSenseContext = React.createContext<CrashSenseCore | null>(null);\n\ninterface CrashSenseProviderProps {\n config: CrashSenseConfig;\n children: React.ReactNode;\n fallback?: React.ReactNode;\n onCrash?: (report: CrashReport) => void;\n}\n\ninterface CrashSenseProviderState {\n hasError: boolean;\n}\n\nexport class CrashSenseProvider extends React.Component<\n CrashSenseProviderProps,\n CrashSenseProviderState\n> {\n private core: CrashSenseCore | null = null;\n\n constructor(props: CrashSenseProviderProps) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(_error: Error): CrashSenseProviderState {\n return { hasError: true };\n }\n\n componentDidMount(): void {\n const enrichedConfig: CrashSenseConfig = {\n ...this.props.config,\n onCrash: (report: CrashReport) => {\n this.props.onCrash?.(report);\n this.props.config.onCrash?.(report);\n },\n };\n\n this.core = createCrashSense(enrichedConfig);\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n if (!this.core) return;\n\n this.core.captureException(error, {\n componentStack: errorInfo.componentStack ?? '',\n framework: 'react',\n lifecycleStage: 'rendering',\n });\n }\n\n componentWillUnmount(): void {\n this.core?.destroy();\n this.core = null;\n }\n\n render(): React.ReactNode {\n if (this.state.hasError && this.props.fallback !== undefined) {\n return React.createElement(\n CrashSenseContext.Provider,\n { value: this.core },\n this.props.fallback,\n );\n }\n\n return React.createElement(\n CrashSenseContext.Provider,\n { value: this.core },\n this.props.children,\n );\n }\n}\n","import { useContext } from 'react';\nimport type { CrashSenseCore, Breadcrumb, CrashSeverity } from '@crashsense/types';\nimport { CrashSenseContext } from './provider';\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 | null;\n} {\n const core = useContext(CrashSenseContext);\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 { useRef, useEffect } from 'react';\nimport { useCrashSense } from './hooks';\n\nconst DEFAULT_THRESHOLD = 50;\nconst WINDOW_MS = 1000;\n\nexport function useRenderTracker(\n componentName: string,\n threshold: number = DEFAULT_THRESHOLD,\n): void {\n const renderTimestamps = useRef<number[]>([]);\n const warned = useRef(false);\n const { captureMessage } = useCrashSense();\n\n const now = Date.now();\n renderTimestamps.current.push(now);\n\n const cutoff = now - WINDOW_MS;\n renderTimestamps.current = renderTimestamps.current.filter((t) => t > cutoff);\n\n if (renderTimestamps.current.length > threshold && !warned.current) {\n warned.current = true;\n captureMessage(\n `Potential infinite re-render detected in ${componentName}: ${renderTimestamps.current.length} renders in ${WINDOW_MS}ms`,\n 'warning',\n );\n }\n\n useEffect(() => {\n return () => {\n renderTimestamps.current = [];\n warned.current = false;\n };\n }, []);\n}\n","import type { CrashSenseCore } from '@crashsense/types';\n\nexport function createHydrationDetector(core: CrashSenseCore) {\n let originalConsoleError: typeof console.error | null = null;\n\n return {\n install(): void {\n if (typeof console === 'undefined') return;\n\n originalConsoleError = console.error;\n\n console.error = (...args: unknown[]) => {\n originalConsoleError!.apply(console, args);\n\n const message = args.map(String).join(' ');\n if (/hydrat/i.test(message)) {\n core.captureException(\n new Error(`Hydration mismatch: ${message.slice(0, 200)}`),\n {\n source: 'hydration_detector',\n framework: 'react',\n lifecycleStage: 'hydrating',\n },\n );\n }\n };\n },\n\n uninstall(): void {\n if (originalConsoleError) {\n console.error = originalConsoleError;\n originalConsoleError = null;\n }\n },\n };\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,126 @@
1
+ import React, { useContext, useRef, useEffect } from 'react';
2
+ import { createCrashSense } from '@crashsense/core';
3
+ export { createCrashSense } from '@crashsense/core';
4
+
5
+ // src/provider.ts
6
+ var CrashSenseContext = React.createContext(null);
7
+ var CrashSenseProvider = class extends React.Component {
8
+ constructor(props) {
9
+ super(props);
10
+ this.core = null;
11
+ this.state = { hasError: false };
12
+ }
13
+ static getDerivedStateFromError(_error) {
14
+ return { hasError: true };
15
+ }
16
+ componentDidMount() {
17
+ const enrichedConfig = {
18
+ ...this.props.config,
19
+ onCrash: (report) => {
20
+ this.props.onCrash?.(report);
21
+ this.props.config.onCrash?.(report);
22
+ }
23
+ };
24
+ this.core = createCrashSense(enrichedConfig);
25
+ }
26
+ componentDidCatch(error, errorInfo) {
27
+ if (!this.core) return;
28
+ this.core.captureException(error, {
29
+ componentStack: errorInfo.componentStack ?? "",
30
+ framework: "react",
31
+ lifecycleStage: "rendering"
32
+ });
33
+ }
34
+ componentWillUnmount() {
35
+ this.core?.destroy();
36
+ this.core = null;
37
+ }
38
+ render() {
39
+ if (this.state.hasError && this.props.fallback !== void 0) {
40
+ return React.createElement(
41
+ CrashSenseContext.Provider,
42
+ { value: this.core },
43
+ this.props.fallback
44
+ );
45
+ }
46
+ return React.createElement(
47
+ CrashSenseContext.Provider,
48
+ { value: this.core },
49
+ this.props.children
50
+ );
51
+ }
52
+ };
53
+ function useCrashSense() {
54
+ const core = useContext(CrashSenseContext);
55
+ return {
56
+ captureException: (error, context) => {
57
+ core?.captureException(error, context);
58
+ },
59
+ captureMessage: (message, severity) => {
60
+ core?.captureMessage(message, severity);
61
+ },
62
+ addBreadcrumb: (breadcrumb) => {
63
+ core?.addBreadcrumb(breadcrumb);
64
+ },
65
+ core
66
+ };
67
+ }
68
+ var DEFAULT_THRESHOLD = 50;
69
+ var WINDOW_MS = 1e3;
70
+ function useRenderTracker(componentName, threshold = DEFAULT_THRESHOLD) {
71
+ const renderTimestamps = useRef([]);
72
+ const warned = useRef(false);
73
+ const { captureMessage } = useCrashSense();
74
+ const now = Date.now();
75
+ renderTimestamps.current.push(now);
76
+ const cutoff = now - WINDOW_MS;
77
+ renderTimestamps.current = renderTimestamps.current.filter((t) => t > cutoff);
78
+ if (renderTimestamps.current.length > threshold && !warned.current) {
79
+ warned.current = true;
80
+ captureMessage(
81
+ `Potential infinite re-render detected in ${componentName}: ${renderTimestamps.current.length} renders in ${WINDOW_MS}ms`,
82
+ "warning"
83
+ );
84
+ }
85
+ useEffect(() => {
86
+ return () => {
87
+ renderTimestamps.current = [];
88
+ warned.current = false;
89
+ };
90
+ }, []);
91
+ }
92
+
93
+ // src/hydration-detector.ts
94
+ function createHydrationDetector(core) {
95
+ let originalConsoleError = null;
96
+ return {
97
+ install() {
98
+ if (typeof console === "undefined") return;
99
+ originalConsoleError = console.error;
100
+ console.error = (...args) => {
101
+ originalConsoleError.apply(console, args);
102
+ const message = args.map(String).join(" ");
103
+ if (/hydrat/i.test(message)) {
104
+ core.captureException(
105
+ new Error(`Hydration mismatch: ${message.slice(0, 200)}`),
106
+ {
107
+ source: "hydration_detector",
108
+ framework: "react",
109
+ lifecycleStage: "hydrating"
110
+ }
111
+ );
112
+ }
113
+ };
114
+ },
115
+ uninstall() {
116
+ if (originalConsoleError) {
117
+ console.error = originalConsoleError;
118
+ originalConsoleError = null;
119
+ }
120
+ }
121
+ };
122
+ }
123
+
124
+ export { CrashSenseContext, CrashSenseProvider, createHydrationDetector, useCrashSense, useRenderTracker };
125
+ //# sourceMappingURL=index.mjs.map
126
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.ts","../src/hooks.ts","../src/render-tracker.ts","../src/hydration-detector.ts"],"names":[],"mappings":";;;;;AAIO,IAAM,iBAAA,GAAoB,KAAA,CAAM,aAAA,CAAqC,IAAI;AAazE,IAAM,kBAAA,GAAN,cAAiC,KAAA,CAAM,SAAA,CAG5C;AAAA,EAGA,YAAY,KAAA,EAAgC;AAC1C,IAAA,KAAA,CAAM,KAAK,CAAA;AAHb,IAAA,IAAA,CAAQ,IAAA,GAA8B,IAAA;AAIpC,IAAA,IAAA,CAAK,KAAA,GAAQ,EAAE,QAAA,EAAU,KAAA,EAAM;AAAA,EACjC;AAAA,EAEA,OAAO,yBAAyB,MAAA,EAAwC;AACtE,IAAA,OAAO,EAAE,UAAU,IAAA,EAAK;AAAA,EAC1B;AAAA,EAEA,iBAAA,GAA0B;AACxB,IAAA,MAAM,cAAA,GAAmC;AAAA,MACvC,GAAG,KAAK,KAAA,CAAM,MAAA;AAAA,MACd,OAAA,EAAS,CAAC,MAAA,KAAwB;AAChC,QAAA,IAAA,CAAK,KAAA,CAAM,UAAU,MAAM,CAAA;AAC3B,QAAA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,OAAA,GAAU,MAAM,CAAA;AAAA,MACpC;AAAA,KACF;AAEA,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAiB,cAAc,CAAA;AAAA,EAC7C;AAAA,EAEA,iBAAA,CAAkB,OAAc,SAAA,EAAkC;AAChE,IAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AAEhB,IAAA,IAAA,CAAK,IAAA,CAAK,iBAAiB,KAAA,EAAO;AAAA,MAChC,cAAA,EAAgB,UAAU,cAAA,IAAkB,EAAA;AAAA,MAC5C,SAAA,EAAW,OAAA;AAAA,MACX,cAAA,EAAgB;AAAA,KACjB,CAAA;AAAA,EACH;AAAA,EAEA,oBAAA,GAA6B;AAC3B,IAAA,IAAA,CAAK,MAAM,OAAA,EAAQ;AACnB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AAAA,EAEA,MAAA,GAA0B;AACxB,IAAA,IAAI,KAAK,KAAA,CAAM,QAAA,IAAY,IAAA,CAAK,KAAA,CAAM,aAAa,MAAA,EAAW;AAC5D,MAAA,OAAO,KAAA,CAAM,aAAA;AAAA,QACX,iBAAA,CAAkB,QAAA;AAAA,QAClB,EAAE,KAAA,EAAO,IAAA,CAAK,IAAA,EAAK;AAAA,QACnB,KAAK,KAAA,CAAM;AAAA,OACb;AAAA,IACF;AAEA,IAAA,OAAO,KAAA,CAAM,aAAA;AAAA,MACX,iBAAA,CAAkB,QAAA;AAAA,MAClB,EAAE,KAAA,EAAO,IAAA,CAAK,IAAA,EAAK;AAAA,MACnB,KAAK,KAAA,CAAM;AAAA,KACb;AAAA,EACF;AACF;ACtEO,SAAS,aAAA,GAKd;AACA,EAAA,MAAM,IAAA,GAAO,WAAW,iBAAiB,CAAA;AAEzC,EAAA,OAAO;AAAA,IACL,gBAAA,EAAkB,CAAC,KAAA,EAAgB,OAAA,KAAsC;AACvE,MAAA,IAAA,EAAM,gBAAA,CAAiB,OAAO,OAAO,CAAA;AAAA,IACvC,CAAA;AAAA,IACA,cAAA,EAAgB,CAAC,OAAA,EAAiB,QAAA,KAA6B;AAC7D,MAAA,IAAA,EAAM,cAAA,CAAe,SAAS,QAAQ,CAAA;AAAA,IACxC,CAAA;AAAA,IACA,aAAA,EAAe,CAAC,UAAA,KAA8C;AAC5D,MAAA,IAAA,EAAM,cAAc,UAAU,CAAA;AAAA,IAChC,CAAA;AAAA,IACA;AAAA,GACF;AACF;ACrBA,IAAM,iBAAA,GAAoB,EAAA;AAC1B,IAAM,SAAA,GAAY,GAAA;AAEX,SAAS,gBAAA,CACd,aAAA,EACA,SAAA,GAAoB,iBAAA,EACd;AACN,EAAA,MAAM,gBAAA,GAAmB,MAAA,CAAiB,EAAE,CAAA;AAC5C,EAAA,MAAM,MAAA,GAAS,OAAO,KAAK,CAAA;AAC3B,EAAA,MAAM,EAAE,cAAA,EAAe,GAAI,aAAA,EAAc;AAEzC,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,gBAAA,CAAiB,OAAA,CAAQ,KAAK,GAAG,CAAA;AAEjC,EAAA,MAAM,SAAS,GAAA,GAAM,SAAA;AACrB,EAAA,gBAAA,CAAiB,UAAU,gBAAA,CAAiB,OAAA,CAAQ,OAAO,CAAC,CAAA,KAAM,IAAI,MAAM,CAAA;AAE5E,EAAA,IAAI,iBAAiB,OAAA,CAAQ,MAAA,GAAS,SAAA,IAAa,CAAC,OAAO,OAAA,EAAS;AAClE,IAAA,MAAA,CAAO,OAAA,GAAU,IAAA;AACjB,IAAA,cAAA;AAAA,MACE,4CAA4C,aAAa,CAAA,EAAA,EAAK,iBAAiB,OAAA,CAAQ,MAAM,eAAe,SAAS,CAAA,EAAA,CAAA;AAAA,MACrH;AAAA,KACF;AAAA,EACF;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,OAAO,MAAM;AACX,MAAA,gBAAA,CAAiB,UAAU,EAAC;AAC5B,MAAA,MAAA,CAAO,OAAA,GAAU,KAAA;AAAA,IACnB,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AACP;;;AChCO,SAAS,wBAAwB,IAAA,EAAsB;AAC5D,EAAA,IAAI,oBAAA,GAAoD,IAAA;AAExD,EAAA,OAAO;AAAA,IACL,OAAA,GAAgB;AACd,MAAA,IAAI,OAAO,YAAY,WAAA,EAAa;AAEpC,MAAA,oBAAA,GAAuB,OAAA,CAAQ,KAAA;AAE/B,MAAA,OAAA,CAAQ,KAAA,GAAQ,IAAI,IAAA,KAAoB;AACtC,QAAA,oBAAA,CAAsB,KAAA,CAAM,SAAS,IAAI,CAAA;AAEzC,QAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,CAAE,KAAK,GAAG,CAAA;AACzC,QAAA,IAAI,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA,EAAG;AAC3B,UAAA,IAAA,CAAK,gBAAA;AAAA,YACH,IAAI,MAAM,CAAA,oBAAA,EAAuB,OAAA,CAAQ,MAAM,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,YACxD;AAAA,cACE,MAAA,EAAQ,oBAAA;AAAA,cACR,SAAA,EAAW,OAAA;AAAA,cACX,cAAA,EAAgB;AAAA;AAClB,WACF;AAAA,QACF;AAAA,MACF,CAAA;AAAA,IACF,CAAA;AAAA,IAEA,SAAA,GAAkB;AAChB,MAAA,IAAI,oBAAA,EAAsB;AACxB,QAAA,OAAA,CAAQ,KAAA,GAAQ,oBAAA;AAChB,QAAA,oBAAA,GAAuB,IAAA;AAAA,MACzB;AAAA,IACF;AAAA,GACF;AACF","file":"index.mjs","sourcesContent":["import React from 'react';\nimport type { CrashSenseConfig, CrashSenseCore, CrashReport } from '@crashsense/types';\nimport { createCrashSense } from '@crashsense/core';\n\nexport const CrashSenseContext = React.createContext<CrashSenseCore | null>(null);\n\ninterface CrashSenseProviderProps {\n config: CrashSenseConfig;\n children: React.ReactNode;\n fallback?: React.ReactNode;\n onCrash?: (report: CrashReport) => void;\n}\n\ninterface CrashSenseProviderState {\n hasError: boolean;\n}\n\nexport class CrashSenseProvider extends React.Component<\n CrashSenseProviderProps,\n CrashSenseProviderState\n> {\n private core: CrashSenseCore | null = null;\n\n constructor(props: CrashSenseProviderProps) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(_error: Error): CrashSenseProviderState {\n return { hasError: true };\n }\n\n componentDidMount(): void {\n const enrichedConfig: CrashSenseConfig = {\n ...this.props.config,\n onCrash: (report: CrashReport) => {\n this.props.onCrash?.(report);\n this.props.config.onCrash?.(report);\n },\n };\n\n this.core = createCrashSense(enrichedConfig);\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n if (!this.core) return;\n\n this.core.captureException(error, {\n componentStack: errorInfo.componentStack ?? '',\n framework: 'react',\n lifecycleStage: 'rendering',\n });\n }\n\n componentWillUnmount(): void {\n this.core?.destroy();\n this.core = null;\n }\n\n render(): React.ReactNode {\n if (this.state.hasError && this.props.fallback !== undefined) {\n return React.createElement(\n CrashSenseContext.Provider,\n { value: this.core },\n this.props.fallback,\n );\n }\n\n return React.createElement(\n CrashSenseContext.Provider,\n { value: this.core },\n this.props.children,\n );\n }\n}\n","import { useContext } from 'react';\nimport type { CrashSenseCore, Breadcrumb, CrashSeverity } from '@crashsense/types';\nimport { CrashSenseContext } from './provider';\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 | null;\n} {\n const core = useContext(CrashSenseContext);\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 { useRef, useEffect } from 'react';\nimport { useCrashSense } from './hooks';\n\nconst DEFAULT_THRESHOLD = 50;\nconst WINDOW_MS = 1000;\n\nexport function useRenderTracker(\n componentName: string,\n threshold: number = DEFAULT_THRESHOLD,\n): void {\n const renderTimestamps = useRef<number[]>([]);\n const warned = useRef(false);\n const { captureMessage } = useCrashSense();\n\n const now = Date.now();\n renderTimestamps.current.push(now);\n\n const cutoff = now - WINDOW_MS;\n renderTimestamps.current = renderTimestamps.current.filter((t) => t > cutoff);\n\n if (renderTimestamps.current.length > threshold && !warned.current) {\n warned.current = true;\n captureMessage(\n `Potential infinite re-render detected in ${componentName}: ${renderTimestamps.current.length} renders in ${WINDOW_MS}ms`,\n 'warning',\n );\n }\n\n useEffect(() => {\n return () => {\n renderTimestamps.current = [];\n warned.current = false;\n };\n }, []);\n}\n","import type { CrashSenseCore } from '@crashsense/types';\n\nexport function createHydrationDetector(core: CrashSenseCore) {\n let originalConsoleError: typeof console.error | null = null;\n\n return {\n install(): void {\n if (typeof console === 'undefined') return;\n\n originalConsoleError = console.error;\n\n console.error = (...args: unknown[]) => {\n originalConsoleError!.apply(console, args);\n\n const message = args.map(String).join(' ');\n if (/hydrat/i.test(message)) {\n core.captureException(\n new Error(`Hydration mismatch: ${message.slice(0, 200)}`),\n {\n source: 'hydration_detector',\n framework: 'react',\n lifecycleStage: 'hydrating',\n },\n );\n }\n };\n },\n\n uninstall(): void {\n if (originalConsoleError) {\n console.error = originalConsoleError;\n originalConsoleError = null;\n }\n },\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@crashsense/react",
3
+ "version": "0.1.0",
4
+ "description": "CrashSense React adapter — ErrorBoundary, hydration detection, re-render 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/react"
11
+ },
12
+ "homepage": "https://github.com/hoainho/crashsense/tree/main/packages/react",
13
+ "bugs": {
14
+ "url": "https://github.com/hoainho/crashsense/issues"
15
+ },
16
+ "keywords": [
17
+ "crashsense",
18
+ "react",
19
+ "error-boundary",
20
+ "crash-detection",
21
+ "hydration",
22
+ "render-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
+ "react": ">=16.8.0",
51
+ "react-dom": ">=16.8.0"
52
+ },
53
+ "sideEffects": false
54
+ }