@autobusal/providers 1.27.0 → 1.28.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/CHANGELOG.md +38 -0
- package/Setup/staleChunk.ts +99 -0
- package/index.ts +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.28.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`registerStaleChunkRecovery()`** — a tab left running a bundle that no
|
|
8
|
+
longer exists now reloads itself onto the page it was asked for, instead of
|
|
9
|
+
freezing.
|
|
10
|
+
|
|
11
|
+
Every route is `lazy(() => import(...))`, the build emits content-hashed
|
|
12
|
+
chunks, and it **empties the output directory** — so a new build deletes the
|
|
13
|
+
old chunk names outright (watched `index-DFPpSljG.js` disappear across one).
|
|
14
|
+
A tab still holding the previous `index.html` goes on asking for those names,
|
|
15
|
+
so the next navigation fetches a chunk that no longer resolves, the import
|
|
16
|
+
rejects, and the routed component never mounts — while the URL has *already*
|
|
17
|
+
changed. The address bar says one page and the screen shows the last one.
|
|
18
|
+
From the outside: "the page does not react at all, but the url seems to
|
|
19
|
+
change."
|
|
20
|
+
|
|
21
|
+
One listener on Vite's `vite:preloadError` covers all 141 lazy routes, which
|
|
22
|
+
is why this is a small module rather than a change to 141 call sites.
|
|
23
|
+
|
|
24
|
+
**Silent, not a prompt.** Every lazy chunk in both apps is a *route* —
|
|
25
|
+
checked, there is no lazily-loaded in-page component — so a chunk is only
|
|
26
|
+
ever fetched while navigating, which means the user has already left the page
|
|
27
|
+
they were on. Reloading to the destination costs them nothing the click was
|
|
28
|
+
not already costing them, and a "new version available?" dialog would be
|
|
29
|
+
asking permission to do the thing they just asked for.
|
|
30
|
+
|
|
31
|
+
**It cannot loop.** A missing chunk is usually a stale bundle but might be a
|
|
32
|
+
broken deploy, and reloading into a repeating failure would be far worse than
|
|
33
|
+
the bug. Each path gets at most one reload per tab; after that the error is
|
|
34
|
+
left to surface. `sessionStorage`, so the budget is per tab.
|
|
35
|
+
|
|
36
|
+
Verified by reproducing it: a tab pinned to an older bundle, its chunk
|
|
37
|
+
orphaned by a rebuild, then a client-side click — `vite:preloadError` fired,
|
|
38
|
+
the document was replaced, and the tab landed on `/operators` running the new
|
|
39
|
+
bundle.
|
|
40
|
+
|
|
3
41
|
## 1.27.0
|
|
4
42
|
|
|
5
43
|
### Added
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recover a tab left running a bundle that no longer exists.
|
|
3
|
+
*
|
|
4
|
+
* Ferjolt Ozuni - Date: 2026-08-06
|
|
5
|
+
*
|
|
6
|
+
* THE FAILURE. Every route is `lazy(() => import(...))`, the build emits
|
|
7
|
+
* content-hashed chunks, and it EMPTIES the output directory - so the moment
|
|
8
|
+
* a new build lands, `index-DFPpSljG.js` and its siblings stop existing.
|
|
9
|
+
* Confirmed by watching that exact file disappear across a build. Any tab
|
|
10
|
+
* still holding the previous index.html goes on asking for the old names, so
|
|
11
|
+
* the next navigation fetches a chunk that 404s, the dynamic import rejects,
|
|
12
|
+
* the routed component never mounts - and the URL has ALREADY changed. The
|
|
13
|
+
* address bar says one page, the screen shows the last one, and only a manual
|
|
14
|
+
* refresh escapes. That is exactly what it looked like from the outside:
|
|
15
|
+
* "the page does not react at all, but the url seems to change".
|
|
16
|
+
*
|
|
17
|
+
* WHY A SILENT RELOAD RATHER THAN A PROMPT. Every lazy chunk in both apps is
|
|
18
|
+
* a ROUTE - checked, there is not one lazily-loaded in-page component - so a
|
|
19
|
+
* chunk is only ever fetched while the user is navigating, which means they
|
|
20
|
+
* have already left the page they were on. Reloading to the destination
|
|
21
|
+
* therefore costs them nothing that the click was not already costing them.
|
|
22
|
+
* A "new version available, reload?" dialog would be asking permission to do
|
|
23
|
+
* the thing they just asked for.
|
|
24
|
+
*
|
|
25
|
+
* WHY IT CANNOT LOOP. A missing chunk is usually a stale bundle, but it might
|
|
26
|
+
* be a broken deploy or a bad cache - and reloading into a failure that
|
|
27
|
+
* repeats would spin forever, which is far worse than the bug being fixed.
|
|
28
|
+
* Each URL gets AT MOST ONE reload per tab: if the same route fails again the
|
|
29
|
+
* error is left to surface. sessionStorage rather than localStorage, so the
|
|
30
|
+
* budget is per tab and does not follow somebody around for weeks.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const KEY = 'chunk-reload';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Which paths this tab has already spent its reload on.
|
|
37
|
+
*/
|
|
38
|
+
const attempted = (): string[] => {
|
|
39
|
+
try {
|
|
40
|
+
const raw = sessionStorage.getItem(KEY);
|
|
41
|
+
|
|
42
|
+
return raw ? JSON.parse(raw) : [];
|
|
43
|
+
} catch {
|
|
44
|
+
// Storage denied (private mode) or a hand-mangled value. Answering
|
|
45
|
+
// "nothing attempted" is safe here only because remember() below has to
|
|
46
|
+
// write before it approves anything, and that write fails for the same
|
|
47
|
+
// reason - so an unreadable store still ends in a refusal, not a loop.
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const remember = (path: string): boolean => {
|
|
53
|
+
try {
|
|
54
|
+
const paths = attempted();
|
|
55
|
+
|
|
56
|
+
if (paths.includes(path)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
sessionStorage.setItem(KEY, JSON.stringify([...paths, path]));
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
// Storage unavailable means no way to remember an attempt, and therefore
|
|
65
|
+
// no way to stop a second one. Refuse rather than risk reloading forever.
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Listen for Vite's dynamic-import failure.
|
|
72
|
+
*
|
|
73
|
+
* `vite:preloadError` is emitted by the preload helper the build wraps every
|
|
74
|
+
* `import()` in, so ONE listener covers all 141 lazy routes - which is the
|
|
75
|
+
* only reason this is a ten-line module rather than a change to 141 call
|
|
76
|
+
* sites. Calling preventDefault stops Vite rethrowing, since we are handling
|
|
77
|
+
* it by replacing the document.
|
|
78
|
+
*/
|
|
79
|
+
const registerStaleChunkRecovery = (): void => {
|
|
80
|
+
window.addEventListener('vite:preloadError', (event) => {
|
|
81
|
+
const path = window.location.pathname + window.location.search;
|
|
82
|
+
|
|
83
|
+
if (!remember(path)) {
|
|
84
|
+
// Spent already. Let it throw so the error boundary says something,
|
|
85
|
+
// rather than reloading into the same wall a second time.
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
|
|
91
|
+
// reload() and not assign(): the URL is already the destination - React
|
|
92
|
+
// Router changes it before rendering the route whose chunk just failed -
|
|
93
|
+
// so this fetches a fresh index.html and lands on the page that was asked
|
|
94
|
+
// for, with the current chunk names.
|
|
95
|
+
window.location.reload();
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export default registerStaleChunkRecovery;
|
package/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import ErrorBoundary from './Errors/ErrorBoundary';
|
|
|
4
4
|
import Message from './Errors/Message';
|
|
5
5
|
import useMenuStore from './stores/menu';
|
|
6
6
|
import { useUserStore } from './stores/user';
|
|
7
|
+
import registerStaleChunkRecovery from './Setup/staleChunk';
|
|
7
8
|
|
|
8
9
|
export {
|
|
9
10
|
apiClient,
|
|
@@ -11,7 +12,8 @@ export {
|
|
|
11
12
|
ErrorBoundary,
|
|
12
13
|
Message,
|
|
13
14
|
useMenuStore,
|
|
14
|
-
useUserStore
|
|
15
|
+
useUserStore,
|
|
16
|
+
registerStaleChunkRecovery
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export default Providers;
|