doxa-maps/library/composables/retryableAsyncComponent.js
Doxa Maps e19697673d
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
Doxa Maps — the map bundler, 7 production plugins and the Experimental shelf
2026-09-25 20:07:59 +00:00

30 lines
1.4 KiB
JavaScript

/**
* retryableAsyncComponent.js — SHARED (@map/composables/retryableAsyncComponent.js).
*
* ★ WHY (card BUG-LAZY-PLUGIN-DEATH-ON-A-SINGLE-FAILED-IMPORT). Vue's defineAsyncComponent
* caches a REJECTED loader promise: once a dynamic import() fails, that component is dead for
* the life of the page — every later render replays the same rejection. The agent hit exactly
* this: one net::ERR_NETWORK_CHANGED event (wifi switch / VPN / sleep-wake, a TRANSPORT blip,
* not a 404) failed five unrelated module fetches at the same moment, and doxa-simple-map +
* doxa-research-map stayed "not loading" until a full reload — a working product reading as
* two broken plugins.
*
* This is the ONE shared wrapper every lazy component goes through instead. It uses
* defineAsyncComponent's own onError contract — retry() re-invokes the loader, which issues a
* FRESH import() request — with 3 attempts and a short growing backoff, which is precisely the
* shape a transient network blip needs. If all attempts fail (a real 404, a dead server), it
* fails for good: retrying forever would hide genuine breakage.
*
* Usage (drop-in): retryableAsyncComponent(() => import('./Heavy.vue'))
*/
import { defineAsyncComponent } from 'vue'
export function retryableAsyncComponent(loader) {
return defineAsyncComponent({
loader,
onError(error, retry, fail, attempts) {
if (attempts <= 3) setTimeout(retry, attempts * 500)
else fail()
},
})
}