mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-30 07:05:24 +00:00
fix: retry a plugin bundle that was not ready yet (#1708)
This commit is contained in:
1 parent
95a00d841c
commit
b211311530
3 files changed
+65
-3
No files matched your search
@@ -85,13 +85,47 @@ function absolute(url: string): string {
|
||||
return new URL(url, window.location.href).href;
|
||||
}
|
||||
|
||||
const IMPORT_RETRY_DELAYS_MS = [500, 1500, 3000];
|
||||
|
||||
/**
|
||||
* A bundle asked for while its plugin is still starting (a slow desktop boot)
|
||||
* answers 404, and the browser then keeps that failure for the URL. So a
|
||||
* failed import is tried again a few times, each under a new URL.
|
||||
*/
|
||||
export async function importWithRetry<T>(
|
||||
url: string,
|
||||
load: (url: string) => Promise<T>,
|
||||
sleep: (ms: number) => Promise<void> = (ms) =>
|
||||
new Promise((resolve) => setTimeout(resolve, ms)),
|
||||
): Promise<T> {
|
||||
for (let attempt = 0; ; attempt++) {
|
||||
const next =
|
||||
attempt === 0
|
||||
? url
|
||||
: `${url}${url.includes("?") ? "&" : "?"}retry=${attempt}`;
|
||||
try {
|
||||
return await load(next);
|
||||
} catch (error) {
|
||||
// A bundle that loaded but threw is broken, not late.
|
||||
const fetchFailed =
|
||||
error instanceof TypeError &&
|
||||
/dynamically imported module|importing a module script/i.test(
|
||||
error.message,
|
||||
);
|
||||
if (!fetchFailed || attempt >= IMPORT_RETRY_DELAYS_MS.length) throw error;
|
||||
await sleep(IMPORT_RETRY_DELAYS_MS[attempt]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultDeps: PluginLoaderDeps = {
|
||||
fetchPlugins: getPlugins,
|
||||
|
||||
async importFrontend(summary) {
|
||||
return (await import(
|
||||
/* @vite-ignore */ absolute(assetUrl(summary, "frontend.js"))
|
||||
)) as FrontendModule;
|
||||
return importWithRetry(
|
||||
absolute(assetUrl(summary, "frontend.js")),
|
||||
(url) => import(/* @vite-ignore */ url) as Promise<FrontendModule>,
|
||||
);
|
||||
},
|
||||
|
||||
async loadLocale(summary, file) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
startPluginRuntime,
|
||||
startPreLoginPlugins,
|
||||
syncPlugins,
|
||||
importWithRetry,
|
||||
} from "@/plugin-host/loader";
|
||||
import {
|
||||
getPluginRecord,
|
||||
@@ -522,3 +523,29 @@ describe("layouts and themes", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("importWithRetry", () => {
|
||||
const noWait = async () => {};
|
||||
|
||||
it("tries a bundle that failed to load again under a new URL", async () => {
|
||||
const load = vi
|
||||
.fn()
|
||||
.mockRejectedValueOnce(
|
||||
new TypeError("Failed to fetch dynamically imported module: x"),
|
||||
)
|
||||
.mockResolvedValueOnce({ ok: true });
|
||||
await expect(
|
||||
importWithRetry("http://h/plugin-assets/a/frontend.js?v=1", load, noWait),
|
||||
).resolves.toEqual({ ok: true });
|
||||
expect(load.mock.calls.map((call) => call[0])).toEqual([
|
||||
"http://h/plugin-assets/a/frontend.js?v=1",
|
||||
"http://h/plugin-assets/a/frontend.js?v=1&retry=1",
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not retry a bundle that threw while running", async () => {
|
||||
const load = vi.fn().mockRejectedValue(new Error("boom"));
|
||||
await expect(importWithRetry("u", load, noWait)).rejects.toThrow("boom");
|
||||
expect(load).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user