@flareapp/inertia 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 +139 -0
- package/dist/index.cjs +128 -0
- package/dist/index.d.cts +38 -0
- package/dist/index.d.mts +38 -0
- package/dist/index.mjs +127 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# @flareapp/inertia
|
|
2
|
+
|
|
3
|
+
Performance tracing for [Inertia.js](https://inertiajs.com) apps, reporting to
|
|
4
|
+
[flareapp.io](https://flareapp.io).
|
|
5
|
+
|
|
6
|
+
Every Inertia visit opens a `browser_navigation` span named after the page component, so navigations show up
|
|
7
|
+
in Flare's performance monitoring the same way a server request does. Works with every Inertia adapter: the
|
|
8
|
+
router is passed in, so the Vue, React and Svelte adapters all use the identical call.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @flareapp/inertia
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`@flareapp/js` is a peer dependency and must be installed and initialized separately.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
Call `traceInertiaRouter` once at boot, **before** `createInertiaApp` runs.
|
|
21
|
+
|
|
22
|
+
Vue:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { createInertiaApp, router } from '@inertiajs/vue3';
|
|
26
|
+
import { flare } from '@flareapp/js';
|
|
27
|
+
import { traceInertiaRouter } from '@flareapp/inertia';
|
|
28
|
+
import { createApp, h } from 'vue';
|
|
29
|
+
|
|
30
|
+
flare.configure({ enableTracing: true, tracesSampleRate: 1 });
|
|
31
|
+
flare.light('YOUR_FLARE_API_KEY');
|
|
32
|
+
|
|
33
|
+
traceInertiaRouter(router);
|
|
34
|
+
|
|
35
|
+
createInertiaApp({
|
|
36
|
+
resolve: (name) => {
|
|
37
|
+
const pages = import.meta.glob('./Pages/**/*.vue');
|
|
38
|
+
return pages[`./Pages/${name}.vue`]();
|
|
39
|
+
},
|
|
40
|
+
setup({ el, App, props, plugin }) {
|
|
41
|
+
createApp({ render: () => h(App, props) })
|
|
42
|
+
.use(plugin)
|
|
43
|
+
.mount(el);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
React, identical apart from the import:
|
|
49
|
+
|
|
50
|
+
```jsx
|
|
51
|
+
import { createInertiaApp, router } from '@inertiajs/react';
|
|
52
|
+
import { flare } from '@flareapp/js';
|
|
53
|
+
import { traceInertiaRouter } from '@flareapp/inertia';
|
|
54
|
+
import { createRoot } from 'react-dom/client';
|
|
55
|
+
|
|
56
|
+
flare.configure({ enableTracing: true, tracesSampleRate: 1 });
|
|
57
|
+
flare.light('YOUR_FLARE_API_KEY');
|
|
58
|
+
|
|
59
|
+
traceInertiaRouter(router);
|
|
60
|
+
|
|
61
|
+
createInertiaApp({
|
|
62
|
+
resolve: (name) => {
|
|
63
|
+
const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true });
|
|
64
|
+
return pages[`./Pages/${name}.jsx`];
|
|
65
|
+
},
|
|
66
|
+
setup({ el, App, props }) {
|
|
67
|
+
createRoot(el).render(<App {...props} />);
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Why the call has to come first
|
|
73
|
+
|
|
74
|
+
Inertia fires a `navigate` event for the initial page load. That event is how the integration learns the name
|
|
75
|
+
of the page the browser landed on. Calling `traceInertiaRouter` after Inertia has booted misses it, and the
|
|
76
|
+
first back/forward step is then mistaken for the initial load.
|
|
77
|
+
|
|
78
|
+
## Span naming
|
|
79
|
+
|
|
80
|
+
Navigation spans are named after the page component from Inertia's page object, for example `Products/Show`.
|
|
81
|
+
That keeps names low-cardinality so Flare can aggregate them, unlike the raw URL `/products/42`. When a
|
|
82
|
+
response carries no component name, the URL path is used instead.
|
|
83
|
+
|
|
84
|
+
## What does not get a navigation span
|
|
85
|
+
|
|
86
|
+
Inertia sends a request for plenty of things that do not move you to another page. None of these open a
|
|
87
|
+
navigation span:
|
|
88
|
+
|
|
89
|
+
- prefetches, including `<Link prefetch>` on hover, mount or viewport
|
|
90
|
+
- deferred props loaded after the page arrives
|
|
91
|
+
- polling, via `usePoll` or `router.poll`
|
|
92
|
+
- infinite scroll fetching the next page
|
|
93
|
+
- any other `router.reload()`
|
|
94
|
+
|
|
95
|
+
The requests themselves are still traced. Inertia sends them over XHR, which Flare instruments, so each one
|
|
96
|
+
shows up as a child span under whichever page it belongs to, with its real timing.
|
|
97
|
+
|
|
98
|
+
An asynchronous visit that does take you to a different page, such as
|
|
99
|
+
`router.visit('/cart', { async: true })`, is a navigation and does open a span.
|
|
100
|
+
|
|
101
|
+
Clicking a second link before the first page arrives is also one navigation, not two. The span runs from the
|
|
102
|
+
first click to the page that actually loaded, which is what the person waiting for it experienced.
|
|
103
|
+
|
|
104
|
+
## Prefetched navigations report near-zero duration
|
|
105
|
+
|
|
106
|
+
A click on a `<Link prefetch>` that is served from Inertia's prefetch cache is currently reported as an instant
|
|
107
|
+
navigation. Inertia fires neither `start` nor `finish` for it, only `navigate` and `success`, so the integration
|
|
108
|
+
cannot tell it apart from a back/forward step and opens and settles the span in the same tick. The navigation
|
|
109
|
+
still shows up as a `browser_navigation` span, but its duration should not be read as the time the user actually
|
|
110
|
+
waited. `<Link prefetch>` defaults to hover as its trigger, so this is a common path, not an edge case.
|
|
111
|
+
|
|
112
|
+
## Cleanup
|
|
113
|
+
|
|
114
|
+
`traceInertiaRouter` returns a function that removes its listeners:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
const stopTracing = traceInertiaRouter(router);
|
|
118
|
+
|
|
119
|
+
stopTracing();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Calling `traceInertiaRouter` twice on the same router replaces the first instrumentation rather than stacking
|
|
123
|
+
a second set of listeners, so Vite HMR does not accumulate them.
|
|
124
|
+
|
|
125
|
+
## Requirements
|
|
126
|
+
|
|
127
|
+
- `@flareapp/js` with `enableTracing: true`
|
|
128
|
+
- Inertia v2 (any adapter). v1 works, but it has no `prefetch` or `async` visit flags, so background work
|
|
129
|
+
driven by `router.reload()` (polling, deferred props, infinite scroll) cannot be told apart from a real
|
|
130
|
+
navigation, and each tick reports its own `browser_navigation` span.
|
|
131
|
+
|
|
132
|
+
## Documentation
|
|
133
|
+
|
|
134
|
+
Full documentation on performance tracing is available at
|
|
135
|
+
[flareapp.io/docs/javascript/general/installation](https://flareapp.io/docs/javascript/general/installation).
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
The MIT License (MIT). Please see [License File](../../LICENSE.md) for more information.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let _flareapp_js_browser = require("@flareapp/js/browser");
|
|
3
|
+
|
|
4
|
+
//#region src/traceInertiaRouter.ts
|
|
5
|
+
/** Resolve a router-reported location (a `URL` on visits, a relative string on pages) to a full href
|
|
6
|
+
* plus its path. Both are undefined outside a browser or for an unparseable value. */
|
|
7
|
+
function locationOf(raw) {
|
|
8
|
+
if (raw == null) return {};
|
|
9
|
+
const url = (0, _flareapp_js_browser.absoluteUrl)(String(raw));
|
|
10
|
+
if (!url) return {};
|
|
11
|
+
return {
|
|
12
|
+
href: url.href,
|
|
13
|
+
path: url.pathname
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** Background work fires the same `start` and `finish` a real visit does, so opening a root for one both
|
|
17
|
+
* invents a navigation and ends the root that was live. */
|
|
18
|
+
function isBackgroundVisit(visit) {
|
|
19
|
+
if (!visit) return false;
|
|
20
|
+
if (visit.prefetch) return true;
|
|
21
|
+
const { path } = locationOf(visit.url);
|
|
22
|
+
return !!visit.async && path !== void 0 && path === (0, _flareapp_js_browser.currentPath)();
|
|
23
|
+
}
|
|
24
|
+
/** `component` ('Products/Show') is Inertia's route identifier, and there is a small fixed set of them,
|
|
25
|
+
* so reports group by it the way the other integrations' route templates do. */
|
|
26
|
+
function routeNameFor(page) {
|
|
27
|
+
const { href, path } = locationOf(page?.url);
|
|
28
|
+
return (0, _flareapp_js_browser.routeName)(() => page?.component, path ?? (0, _flareapp_js_browser.currentPath)(), href);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Trace an Inertia router: open a held `browser_navigation` root per visit, settled once the page
|
|
32
|
+
* arrives. Call it before Inertia boots, so the initial `navigate` is seen. Returns a cleanup that
|
|
33
|
+
* removes the listeners and unregisters. The runtime guard protects untyped JavaScript callers.
|
|
34
|
+
*/
|
|
35
|
+
function traceInertiaRouter(router) {
|
|
36
|
+
if (!isInertiaRouter(router)) return () => {};
|
|
37
|
+
return (0, _flareapp_js_browser.instrumentOnce)(router, (track) => install(router, track));
|
|
38
|
+
}
|
|
39
|
+
function isInertiaRouter(router) {
|
|
40
|
+
return !!router && typeof router.on === "function";
|
|
41
|
+
}
|
|
42
|
+
function install(router, track) {
|
|
43
|
+
const nav = (0, _flareapp_js_browser.registerNavigationSource)();
|
|
44
|
+
track(() => nav.unregister());
|
|
45
|
+
let inFlight = false;
|
|
46
|
+
let inFlightPath;
|
|
47
|
+
let sawInitial = false;
|
|
48
|
+
let lastComponent;
|
|
49
|
+
const nameFor = (page) => {
|
|
50
|
+
const route = routeNameFor(page);
|
|
51
|
+
if (route.source === "route") lastComponent = {
|
|
52
|
+
path: locationOf(page?.url).path,
|
|
53
|
+
name: route.name
|
|
54
|
+
};
|
|
55
|
+
return route;
|
|
56
|
+
};
|
|
57
|
+
const settle = (page) => {
|
|
58
|
+
inFlight = false;
|
|
59
|
+
inFlightPath = void 0;
|
|
60
|
+
nav.settleNavigation(nameFor(page));
|
|
61
|
+
};
|
|
62
|
+
const on = (event, handler) => track(router.on(event, (0, _flareapp_js_browser.insulate)(handler)));
|
|
63
|
+
on("start", (event) => {
|
|
64
|
+
const visit = event?.detail?.visit;
|
|
65
|
+
if (isBackgroundVisit(visit)) return;
|
|
66
|
+
const { href, path } = locationOf(visit?.url);
|
|
67
|
+
if (inFlight) {
|
|
68
|
+
inFlightPath = path;
|
|
69
|
+
nav.setActiveRouteName({
|
|
70
|
+
name: path ?? (0, _flareapp_js_browser.currentPath)(),
|
|
71
|
+
source: "url",
|
|
72
|
+
url: href
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
sawInitial = true;
|
|
77
|
+
inFlight = true;
|
|
78
|
+
inFlightPath = path;
|
|
79
|
+
nav.startNavigation({
|
|
80
|
+
path,
|
|
81
|
+
url: href,
|
|
82
|
+
hold: true
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
on("navigate", (event) => {
|
|
86
|
+
const page = event?.detail?.page;
|
|
87
|
+
if (inFlight) {
|
|
88
|
+
settle(page);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!sawInitial) {
|
|
92
|
+
sawInitial = true;
|
|
93
|
+
nav.setActiveRouteName(nameFor(page));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const { href, path } = locationOf(page?.url);
|
|
97
|
+
nav.startNavigation({
|
|
98
|
+
path,
|
|
99
|
+
url: href
|
|
100
|
+
});
|
|
101
|
+
settle(page);
|
|
102
|
+
});
|
|
103
|
+
on("success", (event) => {
|
|
104
|
+
if (!inFlight) return;
|
|
105
|
+
const page = event?.detail?.page;
|
|
106
|
+
if (locationOf(page?.url).path !== inFlightPath) return;
|
|
107
|
+
settle(page);
|
|
108
|
+
});
|
|
109
|
+
/** Whether `visit` is the one that opened the currently held root, i.e. `finish` may settle it. */
|
|
110
|
+
const belongsToThisNavigation = (visit) => {
|
|
111
|
+
if (isBackgroundVisit(visit)) return false;
|
|
112
|
+
if (visit && locationOf(visit.url).path !== inFlightPath) return false;
|
|
113
|
+
if (visit?.interrupted) return false;
|
|
114
|
+
return true;
|
|
115
|
+
};
|
|
116
|
+
on("finish", (event) => {
|
|
117
|
+
if (!inFlight) return;
|
|
118
|
+
if (!belongsToThisNavigation(event?.detail?.visit)) return;
|
|
119
|
+
inFlight = false;
|
|
120
|
+
inFlightPath = void 0;
|
|
121
|
+
const { href, path } = locationOf((0, _flareapp_js_browser.currentPath)());
|
|
122
|
+
const knownComponentName = lastComponent?.path === path ? lastComponent?.name : void 0;
|
|
123
|
+
nav.settleNavigation((0, _flareapp_js_browser.routeName)(() => knownComponentName, path ?? (0, _flareapp_js_browser.currentPath)(), href));
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
//#endregion
|
|
128
|
+
exports.traceInertiaRouter = traceInertiaRouter;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/vendor/inertiaTypes.d.ts
|
|
2
|
+
/** The page object Inertia ships with every response. `component` is the page component name
|
|
3
|
+
* ('Products/Show'), `url` a relative string ('/products/42'). */
|
|
4
|
+
type InertiaPageLike = {
|
|
5
|
+
component?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
};
|
|
8
|
+
/** `visit.url` is a `URL` instance in @inertiajs/core. A string is tolerated for older versions.
|
|
9
|
+
* `prefetch` and `async` are how a background visit gives itself away: see `isBackgroundVisit`.
|
|
10
|
+
* `interrupted` and `cancelled` are set on the visit `finish` carries, and only the first of the two
|
|
11
|
+
* means a successor visit is already on its way. */
|
|
12
|
+
type InertiaVisitLike = {
|
|
13
|
+
url?: URL | string;
|
|
14
|
+
prefetch?: boolean;
|
|
15
|
+
async?: boolean;
|
|
16
|
+
interrupted?: boolean;
|
|
17
|
+
cancelled?: boolean;
|
|
18
|
+
};
|
|
19
|
+
type InertiaEventLike = {
|
|
20
|
+
detail?: {
|
|
21
|
+
visit?: InertiaVisitLike;
|
|
22
|
+
page?: InertiaPageLike;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
type InertiaEventName = 'start' | 'navigate' | 'success' | 'finish';
|
|
26
|
+
type InertiaRouterLike = {
|
|
27
|
+
on(event: InertiaEventName, callback: (event: InertiaEventLike) => void): () => void;
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/traceInertiaRouter.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* Trace an Inertia router: open a held `browser_navigation` root per visit, settled once the page
|
|
33
|
+
* arrives. Call it before Inertia boots, so the initial `navigate` is seen. Returns a cleanup that
|
|
34
|
+
* removes the listeners and unregisters. The runtime guard protects untyped JavaScript callers.
|
|
35
|
+
*/
|
|
36
|
+
declare function traceInertiaRouter(router: InertiaRouterLike): () => void;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { type InertiaEventLike, type InertiaEventName, type InertiaPageLike, type InertiaRouterLike, type InertiaVisitLike, traceInertiaRouter };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/vendor/inertiaTypes.d.ts
|
|
2
|
+
/** The page object Inertia ships with every response. `component` is the page component name
|
|
3
|
+
* ('Products/Show'), `url` a relative string ('/products/42'). */
|
|
4
|
+
type InertiaPageLike = {
|
|
5
|
+
component?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
};
|
|
8
|
+
/** `visit.url` is a `URL` instance in @inertiajs/core. A string is tolerated for older versions.
|
|
9
|
+
* `prefetch` and `async` are how a background visit gives itself away: see `isBackgroundVisit`.
|
|
10
|
+
* `interrupted` and `cancelled` are set on the visit `finish` carries, and only the first of the two
|
|
11
|
+
* means a successor visit is already on its way. */
|
|
12
|
+
type InertiaVisitLike = {
|
|
13
|
+
url?: URL | string;
|
|
14
|
+
prefetch?: boolean;
|
|
15
|
+
async?: boolean;
|
|
16
|
+
interrupted?: boolean;
|
|
17
|
+
cancelled?: boolean;
|
|
18
|
+
};
|
|
19
|
+
type InertiaEventLike = {
|
|
20
|
+
detail?: {
|
|
21
|
+
visit?: InertiaVisitLike;
|
|
22
|
+
page?: InertiaPageLike;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
type InertiaEventName = 'start' | 'navigate' | 'success' | 'finish';
|
|
26
|
+
type InertiaRouterLike = {
|
|
27
|
+
on(event: InertiaEventName, callback: (event: InertiaEventLike) => void): () => void;
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/traceInertiaRouter.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* Trace an Inertia router: open a held `browser_navigation` root per visit, settled once the page
|
|
33
|
+
* arrives. Call it before Inertia boots, so the initial `navigate` is seen. Returns a cleanup that
|
|
34
|
+
* removes the listeners and unregisters. The runtime guard protects untyped JavaScript callers.
|
|
35
|
+
*/
|
|
36
|
+
declare function traceInertiaRouter(router: InertiaRouterLike): () => void;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { type InertiaEventLike, type InertiaEventName, type InertiaPageLike, type InertiaRouterLike, type InertiaVisitLike, traceInertiaRouter };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { absoluteUrl, currentPath, instrumentOnce, insulate, registerNavigationSource, routeName } from "@flareapp/js/browser";
|
|
2
|
+
|
|
3
|
+
//#region src/traceInertiaRouter.ts
|
|
4
|
+
/** Resolve a router-reported location (a `URL` on visits, a relative string on pages) to a full href
|
|
5
|
+
* plus its path. Both are undefined outside a browser or for an unparseable value. */
|
|
6
|
+
function locationOf(raw) {
|
|
7
|
+
if (raw == null) return {};
|
|
8
|
+
const url = absoluteUrl(String(raw));
|
|
9
|
+
if (!url) return {};
|
|
10
|
+
return {
|
|
11
|
+
href: url.href,
|
|
12
|
+
path: url.pathname
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Background work fires the same `start` and `finish` a real visit does, so opening a root for one both
|
|
16
|
+
* invents a navigation and ends the root that was live. */
|
|
17
|
+
function isBackgroundVisit(visit) {
|
|
18
|
+
if (!visit) return false;
|
|
19
|
+
if (visit.prefetch) return true;
|
|
20
|
+
const { path } = locationOf(visit.url);
|
|
21
|
+
return !!visit.async && path !== void 0 && path === currentPath();
|
|
22
|
+
}
|
|
23
|
+
/** `component` ('Products/Show') is Inertia's route identifier, and there is a small fixed set of them,
|
|
24
|
+
* so reports group by it the way the other integrations' route templates do. */
|
|
25
|
+
function routeNameFor(page) {
|
|
26
|
+
const { href, path } = locationOf(page?.url);
|
|
27
|
+
return routeName(() => page?.component, path ?? currentPath(), href);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Trace an Inertia router: open a held `browser_navigation` root per visit, settled once the page
|
|
31
|
+
* arrives. Call it before Inertia boots, so the initial `navigate` is seen. Returns a cleanup that
|
|
32
|
+
* removes the listeners and unregisters. The runtime guard protects untyped JavaScript callers.
|
|
33
|
+
*/
|
|
34
|
+
function traceInertiaRouter(router) {
|
|
35
|
+
if (!isInertiaRouter(router)) return () => {};
|
|
36
|
+
return instrumentOnce(router, (track) => install(router, track));
|
|
37
|
+
}
|
|
38
|
+
function isInertiaRouter(router) {
|
|
39
|
+
return !!router && typeof router.on === "function";
|
|
40
|
+
}
|
|
41
|
+
function install(router, track) {
|
|
42
|
+
const nav = registerNavigationSource();
|
|
43
|
+
track(() => nav.unregister());
|
|
44
|
+
let inFlight = false;
|
|
45
|
+
let inFlightPath;
|
|
46
|
+
let sawInitial = false;
|
|
47
|
+
let lastComponent;
|
|
48
|
+
const nameFor = (page) => {
|
|
49
|
+
const route = routeNameFor(page);
|
|
50
|
+
if (route.source === "route") lastComponent = {
|
|
51
|
+
path: locationOf(page?.url).path,
|
|
52
|
+
name: route.name
|
|
53
|
+
};
|
|
54
|
+
return route;
|
|
55
|
+
};
|
|
56
|
+
const settle = (page) => {
|
|
57
|
+
inFlight = false;
|
|
58
|
+
inFlightPath = void 0;
|
|
59
|
+
nav.settleNavigation(nameFor(page));
|
|
60
|
+
};
|
|
61
|
+
const on = (event, handler) => track(router.on(event, insulate(handler)));
|
|
62
|
+
on("start", (event) => {
|
|
63
|
+
const visit = event?.detail?.visit;
|
|
64
|
+
if (isBackgroundVisit(visit)) return;
|
|
65
|
+
const { href, path } = locationOf(visit?.url);
|
|
66
|
+
if (inFlight) {
|
|
67
|
+
inFlightPath = path;
|
|
68
|
+
nav.setActiveRouteName({
|
|
69
|
+
name: path ?? currentPath(),
|
|
70
|
+
source: "url",
|
|
71
|
+
url: href
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
sawInitial = true;
|
|
76
|
+
inFlight = true;
|
|
77
|
+
inFlightPath = path;
|
|
78
|
+
nav.startNavigation({
|
|
79
|
+
path,
|
|
80
|
+
url: href,
|
|
81
|
+
hold: true
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
on("navigate", (event) => {
|
|
85
|
+
const page = event?.detail?.page;
|
|
86
|
+
if (inFlight) {
|
|
87
|
+
settle(page);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (!sawInitial) {
|
|
91
|
+
sawInitial = true;
|
|
92
|
+
nav.setActiveRouteName(nameFor(page));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const { href, path } = locationOf(page?.url);
|
|
96
|
+
nav.startNavigation({
|
|
97
|
+
path,
|
|
98
|
+
url: href
|
|
99
|
+
});
|
|
100
|
+
settle(page);
|
|
101
|
+
});
|
|
102
|
+
on("success", (event) => {
|
|
103
|
+
if (!inFlight) return;
|
|
104
|
+
const page = event?.detail?.page;
|
|
105
|
+
if (locationOf(page?.url).path !== inFlightPath) return;
|
|
106
|
+
settle(page);
|
|
107
|
+
});
|
|
108
|
+
/** Whether `visit` is the one that opened the currently held root, i.e. `finish` may settle it. */
|
|
109
|
+
const belongsToThisNavigation = (visit) => {
|
|
110
|
+
if (isBackgroundVisit(visit)) return false;
|
|
111
|
+
if (visit && locationOf(visit.url).path !== inFlightPath) return false;
|
|
112
|
+
if (visit?.interrupted) return false;
|
|
113
|
+
return true;
|
|
114
|
+
};
|
|
115
|
+
on("finish", (event) => {
|
|
116
|
+
if (!inFlight) return;
|
|
117
|
+
if (!belongsToThisNavigation(event?.detail?.visit)) return;
|
|
118
|
+
inFlight = false;
|
|
119
|
+
inFlightPath = void 0;
|
|
120
|
+
const { href, path } = locationOf(currentPath());
|
|
121
|
+
const knownComponentName = lastComponent?.path === path ? lastComponent?.name : void 0;
|
|
122
|
+
nav.settleNavigation(routeName(() => knownComponentName, path ?? currentPath(), href));
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
export { traceInertiaRouter };
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flareapp/inertia",
|
|
3
|
+
"version": "2.8.0",
|
|
4
|
+
"description": "Inertia.js performance tracing for flareapp.io",
|
|
5
|
+
"homepage": "https://flareapp.io",
|
|
6
|
+
"bugs": "https://github.com/spatie/flare-client-js/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/spatie/flare-client-js.git"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Spatie",
|
|
14
|
+
"email": "info@spatie.be"
|
|
15
|
+
},
|
|
16
|
+
"contributors": [
|
|
17
|
+
"Dries Heyninck <dries@spatie.be>"
|
|
18
|
+
],
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.mjs",
|
|
25
|
+
"types": "./dist/index.d.cts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"import": {
|
|
29
|
+
"types": "./dist/index.d.mts",
|
|
30
|
+
"default": "./dist/index.mjs"
|
|
31
|
+
},
|
|
32
|
+
"require": {
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
34
|
+
"default": "./dist/index.cjs"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"prepublishOnly": "npm run build",
|
|
40
|
+
"build": "tsdown src/index.ts --format cjs,esm --dts --clean",
|
|
41
|
+
"typescript": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"release": "release-it"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@flareapp/js": "file:../js",
|
|
47
|
+
"@flareapp/test-helpers": "*",
|
|
48
|
+
"@inertiajs/core": "^2.3.27",
|
|
49
|
+
"jsdom": "^26.1.0",
|
|
50
|
+
"tsdown": "^0.20.3",
|
|
51
|
+
"typescript": "^5.7.0",
|
|
52
|
+
"vitest": "^4.0.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@flareapp/js": "^2.8.0"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
}
|
|
60
|
+
}
|