@flareapp/react 2.6.0 → 2.8.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 +65 -11
- package/dist/{flareReactErrorHandler-4Vgqlths.d.cts → flareReactErrorHandler-Ce0sCyMK.d.mts} +5 -2
- package/dist/{flareReactErrorHandler-BcVYRMtC.d.mts → flareReactErrorHandler-CnFo8otu.d.cts} +5 -2
- package/dist/{flareReactErrorHandler-BKUV8piX.cjs → flareReactErrorHandler-DBmb7u7U.cjs} +80 -74
- package/dist/{flareReactErrorHandler-CfcBSmhG.mjs → flareReactErrorHandler-sRmFoJAG.mjs} +79 -73
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/inject.cjs +1 -1
- package/dist/inject.d.cts +1 -1
- package/dist/inject.d.mts +1 -1
- package/dist/inject.mjs +1 -1
- package/dist/profiler.cjs +63 -0
- package/dist/profiler.d.cts +19 -0
- package/dist/profiler.d.mts +19 -0
- package/dist/profiler.mjs +60 -0
- package/dist/react-router.cjs +94 -0
- package/dist/react-router.d.cts +54 -0
- package/dist/react-router.d.mts +54 -0
- package/dist/react-router.mjs +91 -0
- package/dist/tanstack-router.cjs +89 -0
- package/dist/tanstack-router.d.cts +45 -0
- package/dist/tanstack-router.d.mts +45 -0
- package/dist/tanstack-router.mjs +86 -0
- package/package.json +47 -5
package/dist/inject.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as FlareErrorBoundaryFallbackProps, c as FlareReactContext, i as FlareErrorBoundary, l as MinifiedReactError, n as FlareReactErrorHandlerOptions, o as FlareErrorBoundaryProps, r as flareReactErrorHandler, s as ComponentStackFrame, t as FlareReactErrorHandlerCallback } from "./flareReactErrorHandler-
|
|
1
|
+
import { a as FlareErrorBoundaryFallbackProps, c as FlareReactContext, i as FlareErrorBoundary, l as MinifiedReactError, n as FlareReactErrorHandlerOptions, o as FlareErrorBoundaryProps, r as flareReactErrorHandler, s as ComponentStackFrame, t as FlareReactErrorHandlerCallback } from "./flareReactErrorHandler-Ce0sCyMK.mjs";
|
|
2
2
|
export { type ComponentStackFrame, FlareErrorBoundary, type FlareErrorBoundaryFallbackProps, type FlareErrorBoundaryProps, type FlareReactContext, type FlareReactErrorHandlerCallback, type FlareReactErrorHandlerOptions, type MinifiedReactError, flareReactErrorHandler };
|
package/dist/inject.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as FlareErrorBoundary, t as flareReactErrorHandler } from "./flareReactErrorHandler-
|
|
1
|
+
import { n as FlareErrorBoundary, t as flareReactErrorHandler } from "./flareReactErrorHandler-sRmFoJAG.mjs";
|
|
2
2
|
|
|
3
3
|
export { FlareErrorBoundary, flareReactErrorHandler };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_flareReactErrorHandler = require('./flareReactErrorHandler-DBmb7u7U.cjs');
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let _flareapp_js_browser = require("@flareapp/js/browser");
|
|
5
|
+
|
|
6
|
+
//#region src/profiler.ts
|
|
7
|
+
const useMountEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
|
|
8
|
+
const FlareProfilerContext = (0, react.createContext)(null);
|
|
9
|
+
/** Records one `browser_component` span for its mount, nested under the nearest profiled ancestor
|
|
10
|
+
* or the active `browser_pageload` / `browser_navigation` root. */
|
|
11
|
+
function FlareProfiler({ name, children }) {
|
|
12
|
+
const context = (0, react.useContext)(FlareProfilerContext);
|
|
13
|
+
const parentRef = (0, react.useRef)(void 0);
|
|
14
|
+
if (parentRef.current === void 0) try {
|
|
15
|
+
parentRef.current = (0, _flareapp_js_browser.resolveComponentParent)(context, (0, _flareapp_js_browser.activeComponentRoot)());
|
|
16
|
+
} catch {
|
|
17
|
+
parentRef.current = null;
|
|
18
|
+
}
|
|
19
|
+
const parent = parentRef.current;
|
|
20
|
+
const ownRef = (0, react.useRef)(null);
|
|
21
|
+
if (parent && ownRef.current === null) try {
|
|
22
|
+
const spanId = (0, _flareapp_js_browser.reserveSpanId)(parent.traceId);
|
|
23
|
+
ownRef.current = spanId ? {
|
|
24
|
+
spanId,
|
|
25
|
+
startNano: (0, _flareapp_js_browser.nowNano)()
|
|
26
|
+
} : null;
|
|
27
|
+
} catch {}
|
|
28
|
+
const providedRef = (0, react.useRef)(void 0);
|
|
29
|
+
if (providedRef.current === void 0) providedRef.current = parent && ownRef.current ? {
|
|
30
|
+
traceId: parent.traceId,
|
|
31
|
+
parentSpanId: ownRef.current.spanId
|
|
32
|
+
} : null;
|
|
33
|
+
const hasRecorded = (0, react.useRef)(false);
|
|
34
|
+
useMountEffect(() => {
|
|
35
|
+
const own = ownRef.current;
|
|
36
|
+
if (!parent || !own || hasRecorded.current) return;
|
|
37
|
+
hasRecorded.current = true;
|
|
38
|
+
try {
|
|
39
|
+
(0, _flareapp_js_browser.recordComponentSpan)({
|
|
40
|
+
name,
|
|
41
|
+
spanId: own.spanId,
|
|
42
|
+
parent,
|
|
43
|
+
startTimeUnixNano: own.startNano,
|
|
44
|
+
endTimeUnixNano: (0, _flareapp_js_browser.nowNano)()
|
|
45
|
+
});
|
|
46
|
+
} catch {}
|
|
47
|
+
}, []);
|
|
48
|
+
if (providedRef.current === null) return children ?? null;
|
|
49
|
+
return (0, react.createElement)(FlareProfilerContext.Provider, { value: providedRef.current }, children ?? null);
|
|
50
|
+
}
|
|
51
|
+
/** Wraps `Component` in a `FlareProfiler`. Name it explicitly when the component is anonymous or minified. */
|
|
52
|
+
function withFlareProfiler(Component, options) {
|
|
53
|
+
const name = options?.name || Component.displayName || Component.name || "Unknown";
|
|
54
|
+
function Profiled(props) {
|
|
55
|
+
return (0, react.createElement)(FlareProfiler, { name }, (0, react.createElement)(Component, props));
|
|
56
|
+
}
|
|
57
|
+
Profiled.displayName = `withFlareProfiler(${name})`;
|
|
58
|
+
return Profiled;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
exports.FlareProfiler = FlareProfiler;
|
|
63
|
+
exports.withFlareProfiler = withFlareProfiler;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ComponentType, FunctionComponent, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/profiler.d.ts
|
|
4
|
+
type FlareProfilerProps = {
|
|
5
|
+
/** The span's name, as it appears in the trace. */name: string;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
/** Records one `browser_component` span for its mount, nested under the nearest profiled ancestor
|
|
9
|
+
* or the active `browser_pageload` / `browser_navigation` root. */
|
|
10
|
+
declare function FlareProfiler({
|
|
11
|
+
name,
|
|
12
|
+
children
|
|
13
|
+
}: FlareProfilerProps): ReactNode;
|
|
14
|
+
/** Wraps `Component` in a `FlareProfiler`. Name it explicitly when the component is anonymous or minified. */
|
|
15
|
+
declare function withFlareProfiler<P extends object>(Component: ComponentType<P>, options?: {
|
|
16
|
+
name?: string;
|
|
17
|
+
}): FunctionComponent<P>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { FlareProfiler, FlareProfilerProps, withFlareProfiler };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ComponentType, FunctionComponent, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/profiler.d.ts
|
|
4
|
+
type FlareProfilerProps = {
|
|
5
|
+
/** The span's name, as it appears in the trace. */name: string;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
/** Records one `browser_component` span for its mount, nested under the nearest profiled ancestor
|
|
9
|
+
* or the active `browser_pageload` / `browser_navigation` root. */
|
|
10
|
+
declare function FlareProfiler({
|
|
11
|
+
name,
|
|
12
|
+
children
|
|
13
|
+
}: FlareProfilerProps): ReactNode;
|
|
14
|
+
/** Wraps `Component` in a `FlareProfiler`. Name it explicitly when the component is anonymous or minified. */
|
|
15
|
+
declare function withFlareProfiler<P extends object>(Component: ComponentType<P>, options?: {
|
|
16
|
+
name?: string;
|
|
17
|
+
}): FunctionComponent<P>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { FlareProfiler, FlareProfilerProps, withFlareProfiler };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createContext, createElement, useContext, useEffect, useLayoutEffect, useRef } from "react";
|
|
2
|
+
import { activeComponentRoot, nowNano, recordComponentSpan, reserveSpanId, resolveComponentParent } from "@flareapp/js/browser";
|
|
3
|
+
|
|
4
|
+
//#region src/profiler.ts
|
|
5
|
+
const useMountEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
6
|
+
const FlareProfilerContext = createContext(null);
|
|
7
|
+
/** Records one `browser_component` span for its mount, nested under the nearest profiled ancestor
|
|
8
|
+
* or the active `browser_pageload` / `browser_navigation` root. */
|
|
9
|
+
function FlareProfiler({ name, children }) {
|
|
10
|
+
const context = useContext(FlareProfilerContext);
|
|
11
|
+
const parentRef = useRef(void 0);
|
|
12
|
+
if (parentRef.current === void 0) try {
|
|
13
|
+
parentRef.current = resolveComponentParent(context, activeComponentRoot());
|
|
14
|
+
} catch {
|
|
15
|
+
parentRef.current = null;
|
|
16
|
+
}
|
|
17
|
+
const parent = parentRef.current;
|
|
18
|
+
const ownRef = useRef(null);
|
|
19
|
+
if (parent && ownRef.current === null) try {
|
|
20
|
+
const spanId = reserveSpanId(parent.traceId);
|
|
21
|
+
ownRef.current = spanId ? {
|
|
22
|
+
spanId,
|
|
23
|
+
startNano: nowNano()
|
|
24
|
+
} : null;
|
|
25
|
+
} catch {}
|
|
26
|
+
const providedRef = useRef(void 0);
|
|
27
|
+
if (providedRef.current === void 0) providedRef.current = parent && ownRef.current ? {
|
|
28
|
+
traceId: parent.traceId,
|
|
29
|
+
parentSpanId: ownRef.current.spanId
|
|
30
|
+
} : null;
|
|
31
|
+
const hasRecorded = useRef(false);
|
|
32
|
+
useMountEffect(() => {
|
|
33
|
+
const own = ownRef.current;
|
|
34
|
+
if (!parent || !own || hasRecorded.current) return;
|
|
35
|
+
hasRecorded.current = true;
|
|
36
|
+
try {
|
|
37
|
+
recordComponentSpan({
|
|
38
|
+
name,
|
|
39
|
+
spanId: own.spanId,
|
|
40
|
+
parent,
|
|
41
|
+
startTimeUnixNano: own.startNano,
|
|
42
|
+
endTimeUnixNano: nowNano()
|
|
43
|
+
});
|
|
44
|
+
} catch {}
|
|
45
|
+
}, []);
|
|
46
|
+
if (providedRef.current === null) return children ?? null;
|
|
47
|
+
return createElement(FlareProfilerContext.Provider, { value: providedRef.current }, children ?? null);
|
|
48
|
+
}
|
|
49
|
+
/** Wraps `Component` in a `FlareProfiler`. Name it explicitly when the component is anonymous or minified. */
|
|
50
|
+
function withFlareProfiler(Component, options) {
|
|
51
|
+
const name = options?.name || Component.displayName || Component.name || "Unknown";
|
|
52
|
+
function Profiled(props) {
|
|
53
|
+
return createElement(FlareProfiler, { name }, createElement(Component, props));
|
|
54
|
+
}
|
|
55
|
+
Profiled.displayName = `withFlareProfiler(${name})`;
|
|
56
|
+
return Profiled;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
export { FlareProfiler, withFlareProfiler };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_flareReactErrorHandler = require('./flareReactErrorHandler-DBmb7u7U.cjs');
|
|
3
|
+
let _flareapp_js_browser = require("@flareapp/js/browser");
|
|
4
|
+
|
|
5
|
+
//#region src/react-router.ts
|
|
6
|
+
/**
|
|
7
|
+
* Rebuild the parameterized template (`/product/:id`) by joining each match's declared `route.path`.
|
|
8
|
+
* Follows Sentry's getNormalizedName, but reads the router's already-resolved `state.matches` rather
|
|
9
|
+
* than matching again.
|
|
10
|
+
*/
|
|
11
|
+
function routeNameFromMatches(matches) {
|
|
12
|
+
if (!matches || matches.length === 0) return;
|
|
13
|
+
let path = "";
|
|
14
|
+
for (const match of matches) {
|
|
15
|
+
const routePath = match.route?.path;
|
|
16
|
+
if (!routePath) continue;
|
|
17
|
+
path = routePath[0] === "/" ? routePath : (path.endsWith("/") ? path : path + "/") + routePath;
|
|
18
|
+
}
|
|
19
|
+
if (!path) return;
|
|
20
|
+
if (path[0] !== "/") path = "/" + path;
|
|
21
|
+
return path.replace(/\/{2,}/g, "/");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Trace a React Router v7 data router (createBrowserRouter / createHashRouter / createMemoryRouter):
|
|
25
|
+
* name the browser_pageload root from the initial route, and open a parameterized, held
|
|
26
|
+
* browser_navigation root per route change, named once the router settles. Returns a cleanup that
|
|
27
|
+
* unsubscribes and unregisters. Safe to call before or after tracing is enabled; no-ops when off.
|
|
28
|
+
* Calling it twice on the same router replaces the first instrumentation.
|
|
29
|
+
*/
|
|
30
|
+
function traceReactRouter(router) {
|
|
31
|
+
if (typeof router?.subscribe !== "function") return () => {};
|
|
32
|
+
return (0, _flareapp_js_browser.instrumentOnce)(router, (track) => install(router, track));
|
|
33
|
+
}
|
|
34
|
+
function install(router, track) {
|
|
35
|
+
const nav = (0, _flareapp_js_browser.registerNavigationSource)();
|
|
36
|
+
track(() => nav.unregister());
|
|
37
|
+
function routeNameFor(state) {
|
|
38
|
+
return (0, _flareapp_js_browser.routeName)(() => routeNameFromMatches(state.matches), state.location.pathname, hrefOf(state.location));
|
|
39
|
+
}
|
|
40
|
+
function hrefOf(loc) {
|
|
41
|
+
return (0, _flareapp_js_browser.resolveHref)(() => router.createHref?.(loc), keyOf(loc));
|
|
42
|
+
}
|
|
43
|
+
function keyOf(loc) {
|
|
44
|
+
return (loc.pathname || "") + (loc.search || "") + (loc.hash || "");
|
|
45
|
+
}
|
|
46
|
+
let sawInitialSettle = false;
|
|
47
|
+
let inFlight = false;
|
|
48
|
+
let lastLocationKey = keyOf(router.state.location);
|
|
49
|
+
try {
|
|
50
|
+
if (router.state.matches.length > 0) nav.setActiveRouteName(routeNameFor(router.state));
|
|
51
|
+
sawInitialSettle = router.state.initialized === true;
|
|
52
|
+
} catch {}
|
|
53
|
+
function onState(state) {
|
|
54
|
+
if (!sawInitialSettle) {
|
|
55
|
+
lastLocationKey = keyOf(state.location);
|
|
56
|
+
if (state.matches.length > 0) nav.setActiveRouteName(routeNameFor(state));
|
|
57
|
+
if (state.initialized) sawInitialSettle = true;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const navState = state.navigation.state;
|
|
61
|
+
if (!inFlight && navState !== "idle") {
|
|
62
|
+
inFlight = true;
|
|
63
|
+
const destination = state.navigation.location ?? state.location;
|
|
64
|
+
nav.startNavigation({
|
|
65
|
+
path: destination.pathname,
|
|
66
|
+
url: hrefOf(destination),
|
|
67
|
+
hold: true
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (inFlight && navState === "idle") {
|
|
72
|
+
inFlight = false;
|
|
73
|
+
lastLocationKey = keyOf(state.location);
|
|
74
|
+
nav.settleNavigation(routeNameFor(state));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!inFlight && navState === "idle") {
|
|
78
|
+
const locationKey = keyOf(state.location);
|
|
79
|
+
if (locationKey !== lastLocationKey) {
|
|
80
|
+
lastLocationKey = locationKey;
|
|
81
|
+
nav.startNavigation({
|
|
82
|
+
path: state.location.pathname,
|
|
83
|
+
url: hrefOf(state.location)
|
|
84
|
+
});
|
|
85
|
+
nav.settleNavigation(routeNameFor(state));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
track(router.subscribe((0, _flareapp_js_browser.insulate)(onState)));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
exports.routeNameFromMatches = routeNameFromMatches;
|
|
94
|
+
exports.traceReactRouter = traceReactRouter;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//#region src/vendor/reactRouterTypes.d.ts
|
|
2
|
+
type ReactRouterLocationLike = {
|
|
3
|
+
pathname: string;
|
|
4
|
+
search?: string;
|
|
5
|
+
hash?: string;
|
|
6
|
+
state?: unknown;
|
|
7
|
+
};
|
|
8
|
+
type ReactRouterRouteLike = {
|
|
9
|
+
path?: string;
|
|
10
|
+
index?: boolean;
|
|
11
|
+
id?: string;
|
|
12
|
+
};
|
|
13
|
+
type ReactRouterMatchLike = {
|
|
14
|
+
route: ReactRouterRouteLike;
|
|
15
|
+
pathname: string;
|
|
16
|
+
params?: Record<string, string | undefined>;
|
|
17
|
+
};
|
|
18
|
+
type ReactRouterNavigationLike = {
|
|
19
|
+
state: 'idle' | 'loading' | 'submitting';
|
|
20
|
+
location?: ReactRouterLocationLike;
|
|
21
|
+
};
|
|
22
|
+
type ReactRouterStateLike = {
|
|
23
|
+
location: ReactRouterLocationLike;
|
|
24
|
+
matches: ReactRouterMatchLike[];
|
|
25
|
+
navigation: ReactRouterNavigationLike;
|
|
26
|
+
initialized?: boolean;
|
|
27
|
+
};
|
|
28
|
+
type ReactRouterLike = {
|
|
29
|
+
subscribe(cb: (state: ReactRouterStateLike) => void): () => void;
|
|
30
|
+
state: ReactRouterStateLike;
|
|
31
|
+
/**
|
|
32
|
+
* Applies the router's `basename` (and, for a hash router, the `#` prefix) to a location.
|
|
33
|
+
* `state.location.pathname` has both stripped. Optional so a hand-built router still types.
|
|
34
|
+
*/
|
|
35
|
+
createHref?(location: ReactRouterLocationLike): string;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/react-router.d.ts
|
|
39
|
+
/**
|
|
40
|
+
* Rebuild the parameterized template (`/product/:id`) by joining each match's declared `route.path`.
|
|
41
|
+
* Follows Sentry's getNormalizedName, but reads the router's already-resolved `state.matches` rather
|
|
42
|
+
* than matching again.
|
|
43
|
+
*/
|
|
44
|
+
declare function routeNameFromMatches(matches: ReactRouterMatchLike[] | undefined): string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Trace a React Router v7 data router (createBrowserRouter / createHashRouter / createMemoryRouter):
|
|
47
|
+
* name the browser_pageload root from the initial route, and open a parameterized, held
|
|
48
|
+
* browser_navigation root per route change, named once the router settles. Returns a cleanup that
|
|
49
|
+
* unsubscribes and unregisters. Safe to call before or after tracing is enabled; no-ops when off.
|
|
50
|
+
* Calling it twice on the same router replaces the first instrumentation.
|
|
51
|
+
*/
|
|
52
|
+
declare function traceReactRouter(router: ReactRouterLike): () => void;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { type ReactRouterLike, type ReactRouterLocationLike, type ReactRouterMatchLike, type ReactRouterNavigationLike, type ReactRouterRouteLike, type ReactRouterStateLike, routeNameFromMatches, traceReactRouter };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//#region src/vendor/reactRouterTypes.d.ts
|
|
2
|
+
type ReactRouterLocationLike = {
|
|
3
|
+
pathname: string;
|
|
4
|
+
search?: string;
|
|
5
|
+
hash?: string;
|
|
6
|
+
state?: unknown;
|
|
7
|
+
};
|
|
8
|
+
type ReactRouterRouteLike = {
|
|
9
|
+
path?: string;
|
|
10
|
+
index?: boolean;
|
|
11
|
+
id?: string;
|
|
12
|
+
};
|
|
13
|
+
type ReactRouterMatchLike = {
|
|
14
|
+
route: ReactRouterRouteLike;
|
|
15
|
+
pathname: string;
|
|
16
|
+
params?: Record<string, string | undefined>;
|
|
17
|
+
};
|
|
18
|
+
type ReactRouterNavigationLike = {
|
|
19
|
+
state: 'idle' | 'loading' | 'submitting';
|
|
20
|
+
location?: ReactRouterLocationLike;
|
|
21
|
+
};
|
|
22
|
+
type ReactRouterStateLike = {
|
|
23
|
+
location: ReactRouterLocationLike;
|
|
24
|
+
matches: ReactRouterMatchLike[];
|
|
25
|
+
navigation: ReactRouterNavigationLike;
|
|
26
|
+
initialized?: boolean;
|
|
27
|
+
};
|
|
28
|
+
type ReactRouterLike = {
|
|
29
|
+
subscribe(cb: (state: ReactRouterStateLike) => void): () => void;
|
|
30
|
+
state: ReactRouterStateLike;
|
|
31
|
+
/**
|
|
32
|
+
* Applies the router's `basename` (and, for a hash router, the `#` prefix) to a location.
|
|
33
|
+
* `state.location.pathname` has both stripped. Optional so a hand-built router still types.
|
|
34
|
+
*/
|
|
35
|
+
createHref?(location: ReactRouterLocationLike): string;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/react-router.d.ts
|
|
39
|
+
/**
|
|
40
|
+
* Rebuild the parameterized template (`/product/:id`) by joining each match's declared `route.path`.
|
|
41
|
+
* Follows Sentry's getNormalizedName, but reads the router's already-resolved `state.matches` rather
|
|
42
|
+
* than matching again.
|
|
43
|
+
*/
|
|
44
|
+
declare function routeNameFromMatches(matches: ReactRouterMatchLike[] | undefined): string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Trace a React Router v7 data router (createBrowserRouter / createHashRouter / createMemoryRouter):
|
|
47
|
+
* name the browser_pageload root from the initial route, and open a parameterized, held
|
|
48
|
+
* browser_navigation root per route change, named once the router settles. Returns a cleanup that
|
|
49
|
+
* unsubscribes and unregisters. Safe to call before or after tracing is enabled; no-ops when off.
|
|
50
|
+
* Calling it twice on the same router replaces the first instrumentation.
|
|
51
|
+
*/
|
|
52
|
+
declare function traceReactRouter(router: ReactRouterLike): () => void;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { type ReactRouterLike, type ReactRouterLocationLike, type ReactRouterMatchLike, type ReactRouterNavigationLike, type ReactRouterRouteLike, type ReactRouterStateLike, routeNameFromMatches, traceReactRouter };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { instrumentOnce, insulate, registerNavigationSource, resolveHref, routeName } from "@flareapp/js/browser";
|
|
2
|
+
|
|
3
|
+
//#region src/react-router.ts
|
|
4
|
+
/**
|
|
5
|
+
* Rebuild the parameterized template (`/product/:id`) by joining each match's declared `route.path`.
|
|
6
|
+
* Follows Sentry's getNormalizedName, but reads the router's already-resolved `state.matches` rather
|
|
7
|
+
* than matching again.
|
|
8
|
+
*/
|
|
9
|
+
function routeNameFromMatches(matches) {
|
|
10
|
+
if (!matches || matches.length === 0) return;
|
|
11
|
+
let path = "";
|
|
12
|
+
for (const match of matches) {
|
|
13
|
+
const routePath = match.route?.path;
|
|
14
|
+
if (!routePath) continue;
|
|
15
|
+
path = routePath[0] === "/" ? routePath : (path.endsWith("/") ? path : path + "/") + routePath;
|
|
16
|
+
}
|
|
17
|
+
if (!path) return;
|
|
18
|
+
if (path[0] !== "/") path = "/" + path;
|
|
19
|
+
return path.replace(/\/{2,}/g, "/");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Trace a React Router v7 data router (createBrowserRouter / createHashRouter / createMemoryRouter):
|
|
23
|
+
* name the browser_pageload root from the initial route, and open a parameterized, held
|
|
24
|
+
* browser_navigation root per route change, named once the router settles. Returns a cleanup that
|
|
25
|
+
* unsubscribes and unregisters. Safe to call before or after tracing is enabled; no-ops when off.
|
|
26
|
+
* Calling it twice on the same router replaces the first instrumentation.
|
|
27
|
+
*/
|
|
28
|
+
function traceReactRouter(router) {
|
|
29
|
+
if (typeof router?.subscribe !== "function") return () => {};
|
|
30
|
+
return instrumentOnce(router, (track) => install(router, track));
|
|
31
|
+
}
|
|
32
|
+
function install(router, track) {
|
|
33
|
+
const nav = registerNavigationSource();
|
|
34
|
+
track(() => nav.unregister());
|
|
35
|
+
function routeNameFor(state) {
|
|
36
|
+
return routeName(() => routeNameFromMatches(state.matches), state.location.pathname, hrefOf(state.location));
|
|
37
|
+
}
|
|
38
|
+
function hrefOf(loc) {
|
|
39
|
+
return resolveHref(() => router.createHref?.(loc), keyOf(loc));
|
|
40
|
+
}
|
|
41
|
+
function keyOf(loc) {
|
|
42
|
+
return (loc.pathname || "") + (loc.search || "") + (loc.hash || "");
|
|
43
|
+
}
|
|
44
|
+
let sawInitialSettle = false;
|
|
45
|
+
let inFlight = false;
|
|
46
|
+
let lastLocationKey = keyOf(router.state.location);
|
|
47
|
+
try {
|
|
48
|
+
if (router.state.matches.length > 0) nav.setActiveRouteName(routeNameFor(router.state));
|
|
49
|
+
sawInitialSettle = router.state.initialized === true;
|
|
50
|
+
} catch {}
|
|
51
|
+
function onState(state) {
|
|
52
|
+
if (!sawInitialSettle) {
|
|
53
|
+
lastLocationKey = keyOf(state.location);
|
|
54
|
+
if (state.matches.length > 0) nav.setActiveRouteName(routeNameFor(state));
|
|
55
|
+
if (state.initialized) sawInitialSettle = true;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const navState = state.navigation.state;
|
|
59
|
+
if (!inFlight && navState !== "idle") {
|
|
60
|
+
inFlight = true;
|
|
61
|
+
const destination = state.navigation.location ?? state.location;
|
|
62
|
+
nav.startNavigation({
|
|
63
|
+
path: destination.pathname,
|
|
64
|
+
url: hrefOf(destination),
|
|
65
|
+
hold: true
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (inFlight && navState === "idle") {
|
|
70
|
+
inFlight = false;
|
|
71
|
+
lastLocationKey = keyOf(state.location);
|
|
72
|
+
nav.settleNavigation(routeNameFor(state));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!inFlight && navState === "idle") {
|
|
76
|
+
const locationKey = keyOf(state.location);
|
|
77
|
+
if (locationKey !== lastLocationKey) {
|
|
78
|
+
lastLocationKey = locationKey;
|
|
79
|
+
nav.startNavigation({
|
|
80
|
+
path: state.location.pathname,
|
|
81
|
+
url: hrefOf(state.location)
|
|
82
|
+
});
|
|
83
|
+
nav.settleNavigation(routeNameFor(state));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
track(router.subscribe(insulate(onState)));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { routeNameFromMatches, traceReactRouter };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_flareReactErrorHandler = require('./flareReactErrorHandler-DBmb7u7U.cjs');
|
|
3
|
+
let _flareapp_js_browser = require("@flareapp/js/browser");
|
|
4
|
+
|
|
5
|
+
//#region src/tanstack-router.ts
|
|
6
|
+
/**
|
|
7
|
+
* How long a held navigation root waits for `onResolved` before settling itself. Exported so the suite
|
|
8
|
+
* drives it instead of hardcoding the number; not part of the supported surface.
|
|
9
|
+
*/
|
|
10
|
+
const STALE_NAVIGATION_TIMEOUT_MS = 5e3;
|
|
11
|
+
/**
|
|
12
|
+
* Trace a TanStack Router instance: name the `browser_pageload` root from the
|
|
13
|
+
* initial route and open a parameterized `browser_navigation` root per route
|
|
14
|
+
* change. Returns a cleanup that unsubscribes and unregisters. Safe to call
|
|
15
|
+
* before or after tracing is enabled; no-ops when tracing is off. Calling it
|
|
16
|
+
* twice on the same router replaces the first instrumentation rather than
|
|
17
|
+
* stacking a second set of subscriptions.
|
|
18
|
+
*/
|
|
19
|
+
function traceTanStackRouter(router) {
|
|
20
|
+
if (typeof router?.subscribe !== "function") return () => {};
|
|
21
|
+
return (0, _flareapp_js_browser.instrumentOnce)(router, (track) => install(router, track));
|
|
22
|
+
}
|
|
23
|
+
function install(router, track) {
|
|
24
|
+
const nav = (0, _flareapp_js_browser.registerNavigationSource)();
|
|
25
|
+
track(() => nav.unregister());
|
|
26
|
+
function hrefOf(loc) {
|
|
27
|
+
return (0, _flareapp_js_browser.resolveHref)(() => loc.publicHref ?? loc.href, loc.pathname);
|
|
28
|
+
}
|
|
29
|
+
function routeNameFor(loc) {
|
|
30
|
+
return (0, _flareapp_js_browser.routeName)(() => {
|
|
31
|
+
const matches = router.matchRoutes(loc.pathname, loc.search, {
|
|
32
|
+
preload: false,
|
|
33
|
+
throwOnError: false
|
|
34
|
+
});
|
|
35
|
+
if (!matches.some((m) => m.routeId !== "__root__")) return;
|
|
36
|
+
const last = matches[matches.length - 1];
|
|
37
|
+
return last?.fullPath || last?.routeId;
|
|
38
|
+
}, loc.pathname, hrefOf(loc));
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
nav.setActiveRouteName(routeNameFor(router.state.location));
|
|
42
|
+
} catch {}
|
|
43
|
+
let inFlight = false;
|
|
44
|
+
let destination = null;
|
|
45
|
+
let staleTimer = null;
|
|
46
|
+
function clearStaleTimer() {
|
|
47
|
+
if (staleTimer !== null) {
|
|
48
|
+
clearTimeout(staleTimer);
|
|
49
|
+
staleTimer = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function settle(location) {
|
|
53
|
+
clearStaleTimer();
|
|
54
|
+
inFlight = false;
|
|
55
|
+
destination = null;
|
|
56
|
+
nav.settleNavigation(routeNameFor(location));
|
|
57
|
+
}
|
|
58
|
+
track(clearStaleTimer);
|
|
59
|
+
track(router.subscribe("onBeforeLoad", (0, _flareapp_js_browser.insulate)((event) => {
|
|
60
|
+
if (event.fromLocation === void 0) return;
|
|
61
|
+
if (event.hrefChanged === false) return;
|
|
62
|
+
if (event.toLocation.state === event.fromLocation.state) return;
|
|
63
|
+
if (!inFlight) {
|
|
64
|
+
inFlight = true;
|
|
65
|
+
nav.startNavigation({
|
|
66
|
+
path: event.toLocation.pathname,
|
|
67
|
+
hold: true
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
destination = event.toLocation;
|
|
71
|
+
nav.setActiveRouteName(routeNameFor(event.toLocation));
|
|
72
|
+
clearStaleTimer();
|
|
73
|
+
staleTimer = setTimeout((0, _flareapp_js_browser.insulate)(() => {
|
|
74
|
+
staleTimer = null;
|
|
75
|
+
if (destination) settle(destination);
|
|
76
|
+
}), STALE_NAVIGATION_TIMEOUT_MS);
|
|
77
|
+
})));
|
|
78
|
+
track(router.subscribe("onResolved", (0, _flareapp_js_browser.insulate)((event) => {
|
|
79
|
+
if (event.fromLocation === void 0) {
|
|
80
|
+
nav.setActiveRouteName(routeNameFor(event.toLocation));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (inFlight) settle(event.toLocation);
|
|
84
|
+
})));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
exports.STALE_NAVIGATION_TIMEOUT_MS = STALE_NAVIGATION_TIMEOUT_MS;
|
|
89
|
+
exports.traceTanStackRouter = traceTanStackRouter;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/vendor/tanstackRouterTypes.d.ts
|
|
2
|
+
type TanStackLocationLike = {
|
|
3
|
+
pathname: string;
|
|
4
|
+
search: unknown;
|
|
5
|
+
href?: string;
|
|
6
|
+
publicHref?: string;
|
|
7
|
+
state?: unknown;
|
|
8
|
+
};
|
|
9
|
+
type TanStackNavEventLike = {
|
|
10
|
+
fromLocation?: TanStackLocationLike;
|
|
11
|
+
toLocation: TanStackLocationLike;
|
|
12
|
+
hrefChanged?: boolean;
|
|
13
|
+
};
|
|
14
|
+
type TanStackMatchLike = {
|
|
15
|
+
routeId?: string;
|
|
16
|
+
fullPath?: string;
|
|
17
|
+
};
|
|
18
|
+
type TanStackRouterLike = {
|
|
19
|
+
subscribe(eventType: 'onBeforeLoad' | 'onResolved', cb: (event: TanStackNavEventLike) => void): () => void;
|
|
20
|
+
matchRoutes(pathname: string, search: unknown, opts?: {
|
|
21
|
+
preload?: boolean;
|
|
22
|
+
throwOnError?: boolean;
|
|
23
|
+
}): TanStackMatchLike[];
|
|
24
|
+
state: {
|
|
25
|
+
location: TanStackLocationLike;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/tanstack-router.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* How long a held navigation root waits for `onResolved` before settling itself. Exported so the suite
|
|
32
|
+
* drives it instead of hardcoding the number; not part of the supported surface.
|
|
33
|
+
*/
|
|
34
|
+
declare const STALE_NAVIGATION_TIMEOUT_MS = 5000;
|
|
35
|
+
/**
|
|
36
|
+
* Trace a TanStack Router instance: name the `browser_pageload` root from the
|
|
37
|
+
* initial route and open a parameterized `browser_navigation` root per route
|
|
38
|
+
* change. Returns a cleanup that unsubscribes and unregisters. Safe to call
|
|
39
|
+
* before or after tracing is enabled; no-ops when tracing is off. Calling it
|
|
40
|
+
* twice on the same router replaces the first instrumentation rather than
|
|
41
|
+
* stacking a second set of subscriptions.
|
|
42
|
+
*/
|
|
43
|
+
declare function traceTanStackRouter(router: TanStackRouterLike): () => void;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { STALE_NAVIGATION_TIMEOUT_MS, type TanStackLocationLike, type TanStackMatchLike, type TanStackNavEventLike, type TanStackRouterLike, traceTanStackRouter };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/vendor/tanstackRouterTypes.d.ts
|
|
2
|
+
type TanStackLocationLike = {
|
|
3
|
+
pathname: string;
|
|
4
|
+
search: unknown;
|
|
5
|
+
href?: string;
|
|
6
|
+
publicHref?: string;
|
|
7
|
+
state?: unknown;
|
|
8
|
+
};
|
|
9
|
+
type TanStackNavEventLike = {
|
|
10
|
+
fromLocation?: TanStackLocationLike;
|
|
11
|
+
toLocation: TanStackLocationLike;
|
|
12
|
+
hrefChanged?: boolean;
|
|
13
|
+
};
|
|
14
|
+
type TanStackMatchLike = {
|
|
15
|
+
routeId?: string;
|
|
16
|
+
fullPath?: string;
|
|
17
|
+
};
|
|
18
|
+
type TanStackRouterLike = {
|
|
19
|
+
subscribe(eventType: 'onBeforeLoad' | 'onResolved', cb: (event: TanStackNavEventLike) => void): () => void;
|
|
20
|
+
matchRoutes(pathname: string, search: unknown, opts?: {
|
|
21
|
+
preload?: boolean;
|
|
22
|
+
throwOnError?: boolean;
|
|
23
|
+
}): TanStackMatchLike[];
|
|
24
|
+
state: {
|
|
25
|
+
location: TanStackLocationLike;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/tanstack-router.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* How long a held navigation root waits for `onResolved` before settling itself. Exported so the suite
|
|
32
|
+
* drives it instead of hardcoding the number; not part of the supported surface.
|
|
33
|
+
*/
|
|
34
|
+
declare const STALE_NAVIGATION_TIMEOUT_MS = 5000;
|
|
35
|
+
/**
|
|
36
|
+
* Trace a TanStack Router instance: name the `browser_pageload` root from the
|
|
37
|
+
* initial route and open a parameterized `browser_navigation` root per route
|
|
38
|
+
* change. Returns a cleanup that unsubscribes and unregisters. Safe to call
|
|
39
|
+
* before or after tracing is enabled; no-ops when tracing is off. Calling it
|
|
40
|
+
* twice on the same router replaces the first instrumentation rather than
|
|
41
|
+
* stacking a second set of subscriptions.
|
|
42
|
+
*/
|
|
43
|
+
declare function traceTanStackRouter(router: TanStackRouterLike): () => void;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { STALE_NAVIGATION_TIMEOUT_MS, type TanStackLocationLike, type TanStackMatchLike, type TanStackNavEventLike, type TanStackRouterLike, traceTanStackRouter };
|