@ovineko/spa-guard 0.0.1-alpha-23 → 0.0.1-alpha-25

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/dist/_internal.js CHANGED
@@ -1,25 +1,14 @@
1
1
  import {
2
- attemptReload,
3
2
  createLogger,
4
3
  isChunkError,
5
4
  listenInternal,
6
- sendBeacon,
7
- serializeError,
8
- shouldForceRetry,
9
- shouldIgnoreMessages
10
- } from "./chunk-4FKSMB5F.js";
5
+ serializeError
6
+ } from "./chunk-ZVYB2746.js";
11
7
  import {
12
8
  SPINNER_ID,
13
9
  defaultSpinnerSvg,
14
10
  extractVersionFromHtml
15
- } from "./chunk-3ISQYZVD.js";
16
- import {
17
- applyI18n,
18
- defaultErrorFallbackHtml,
19
- defaultLoadingFallbackHtml,
20
- getI18n,
21
- getOptions
22
- } from "./chunk-DOTM7FSY.js";
11
+ } from "./chunk-Z75UPJWV.js";
23
12
  import {
24
13
  dispatchAsyncRuntimeError,
25
14
  dispatchChunkLoadError,
@@ -28,17 +17,28 @@ import {
28
17
  dispatchNetworkTimeout,
29
18
  dispatchSyncRuntimeError,
30
19
  dispatchUnhandledRejection
31
- } from "./chunk-74K44EMP.js";
20
+ } from "./chunk-XFILAQ7P.js";
32
21
  import {
22
+ attemptReload,
23
+ sendBeacon,
24
+ shouldForceRetry,
25
+ shouldIgnoreMessages
26
+ } from "./chunk-CSN7MQGX.js";
27
+ import {
28
+ applyI18n,
33
29
  debugSyncErrorEventType,
30
+ defaultErrorFallbackHtml,
31
+ defaultLoadingFallbackHtml,
34
32
  disableDefaultRetry,
35
33
  emitEvent,
36
34
  enableDefaultRetry,
35
+ getI18n,
36
+ getOptions,
37
37
  getRetryInfoForBeacon,
38
38
  isDefaultRetryEnabled,
39
39
  optionsWindowKey,
40
40
  subscribe
41
- } from "./chunk-YSKH5K6P.js";
41
+ } from "./chunk-X6E7KUDK.js";
42
42
  import "./chunk-MLKGABMK.js";
43
43
 
44
44
  // src/common/handleErrorWithSpaGuard.ts
@@ -0,0 +1,295 @@
1
+ import {
2
+ FORCE_RETRY_MAGIC,
3
+ RETRY_ATTEMPT_PARAM,
4
+ RETRY_ID_PARAM,
5
+ applyI18n,
6
+ clearLastReloadTime,
7
+ clearRetryAttemptFromUrl,
8
+ clearRetryStateFromUrl,
9
+ emitEvent,
10
+ generateRetryId,
11
+ getI18n,
12
+ getLastReloadTime,
13
+ getLogger,
14
+ getOptions,
15
+ getRetryAttemptFromUrl,
16
+ getRetryStateFromUrl,
17
+ isDefaultRetryEnabled,
18
+ setLastReloadTime,
19
+ setLastRetryResetInfo,
20
+ shouldResetRetryCycle
21
+ } from "./chunk-X6E7KUDK.js";
22
+
23
+ // src/common/shouldIgnore.ts
24
+ var shouldIgnoreMessages = (messages) => {
25
+ const options = getOptions();
26
+ const ignorePatterns = (options.errors?.ignore ?? []).filter((p) => p !== "");
27
+ if (ignorePatterns.length === 0) {
28
+ return false;
29
+ }
30
+ const validMessages = messages.filter((msg) => typeof msg === "string");
31
+ return validMessages.some(
32
+ (message) => ignorePatterns.some((pattern) => message.includes(pattern))
33
+ );
34
+ };
35
+ var shouldForceRetry = (messages) => {
36
+ const options = getOptions();
37
+ const forceRetryPatterns = [...options.errors?.forceRetry ?? [], FORCE_RETRY_MAGIC].filter(
38
+ (p) => p !== ""
39
+ );
40
+ const validMessages = messages.filter((msg) => typeof msg === "string");
41
+ return validMessages.some(
42
+ (message) => forceRetryPatterns.some((pattern) => message.includes(pattern))
43
+ );
44
+ };
45
+ var shouldIgnoreBeacon = (beacon) => {
46
+ return shouldIgnoreMessages([beacon.errorMessage, beacon.eventMessage]);
47
+ };
48
+
49
+ // src/common/sendBeacon.ts
50
+ var sendBeacon = (beacon) => {
51
+ if (shouldIgnoreBeacon(beacon)) {
52
+ return;
53
+ }
54
+ const options = getOptions();
55
+ if (!options.reportBeacon?.endpoint) {
56
+ getLogger()?.noBeaconEndpoint();
57
+ return;
58
+ }
59
+ const enrichedBeacon = options.appName ? { ...beacon, appName: options.appName } : beacon;
60
+ const body = JSON.stringify(enrichedBeacon);
61
+ const isSendBeaconAvailable = typeof globalThis.window?.navigator?.sendBeacon === "function";
62
+ const isSentBeacon = isSendBeaconAvailable && globalThis.window.navigator.sendBeacon(options.reportBeacon.endpoint, body);
63
+ if (!isSentBeacon && typeof fetch === "function") {
64
+ fetch(options.reportBeacon.endpoint, { body, keepalive: true, method: "POST" }).catch(
65
+ (error) => {
66
+ getLogger()?.beaconSendFailed(error);
67
+ }
68
+ );
69
+ }
70
+ };
71
+
72
+ // src/common/reload.ts
73
+ var buildReloadUrl = (retryId, retryAttempt) => {
74
+ const url = new URL(globalThis.window.location.href);
75
+ url.searchParams.set(RETRY_ID_PARAM, retryId);
76
+ url.searchParams.set(RETRY_ATTEMPT_PARAM, String(retryAttempt));
77
+ return url.toString();
78
+ };
79
+ var buildReloadUrlAttemptOnly = (retryAttempt) => {
80
+ const url = new URL(globalThis.window.location.href);
81
+ url.searchParams.set(RETRY_ATTEMPT_PARAM, String(retryAttempt));
82
+ return url.toString();
83
+ };
84
+ var reloadScheduled = false;
85
+ var attemptReload = (error) => {
86
+ if (reloadScheduled) {
87
+ getLogger()?.reloadAlreadyScheduled(error);
88
+ return;
89
+ }
90
+ reloadScheduled = true;
91
+ try {
92
+ const options = getOptions();
93
+ const reloadDelays = options.reloadDelays ?? [1e3, 2e3, 5e3];
94
+ const useRetryId = options.useRetryId ?? true;
95
+ const enableRetryReset = options.enableRetryReset ?? true;
96
+ const minTimeBetweenResets = options.minTimeBetweenResets ?? 5e3;
97
+ let retryState;
98
+ if (useRetryId) {
99
+ retryState = getRetryStateFromUrl();
100
+ } else {
101
+ const attempt = getRetryAttemptFromUrl();
102
+ retryState = attempt === null ? null : { retryAttempt: attempt, retryId: generateRetryId() };
103
+ }
104
+ let currentAttempt = retryState ? retryState.retryAttempt : 0;
105
+ let retryId = retryState?.retryId ?? generateRetryId();
106
+ getLogger()?.retryCycleStarting(retryId, currentAttempt);
107
+ const retryEnabled = isDefaultRetryEnabled();
108
+ emitEvent({
109
+ error,
110
+ isRetrying: retryEnabled && currentAttempt >= 0 && currentAttempt < reloadDelays.length,
111
+ name: "chunk-error"
112
+ });
113
+ if (!retryEnabled) {
114
+ reloadScheduled = false;
115
+ return;
116
+ }
117
+ if (enableRetryReset && retryState && retryState.retryAttempt > 0 && shouldResetRetryCycle(retryState, reloadDelays, minTimeBetweenResets)) {
118
+ const lastReload = getLastReloadTime();
119
+ const timeSinceReload = lastReload ? Date.now() - lastReload.timestamp : 0;
120
+ clearRetryStateFromUrl();
121
+ clearLastReloadTime();
122
+ setLastRetryResetInfo(retryState.retryId);
123
+ const errorMsg2 = String(error);
124
+ emitEvent(
125
+ {
126
+ name: "retry-reset",
127
+ previousAttempt: retryState.retryAttempt,
128
+ previousRetryId: retryState.retryId,
129
+ timeSinceReload
130
+ },
131
+ { silent: shouldIgnoreMessages([errorMsg2]) }
132
+ );
133
+ currentAttempt = 0;
134
+ retryId = generateRetryId();
135
+ }
136
+ if (currentAttempt === -1) {
137
+ const errorMsg2 = String(error);
138
+ if (!shouldIgnoreMessages([errorMsg2])) {
139
+ getLogger()?.fallbackAlreadyShown(error);
140
+ }
141
+ reloadScheduled = false;
142
+ showFallbackUI();
143
+ return;
144
+ }
145
+ if (currentAttempt >= reloadDelays.length) {
146
+ const errorMsg2 = String(error);
147
+ emitEvent(
148
+ {
149
+ finalAttempt: currentAttempt,
150
+ name: "retry-exhausted",
151
+ retryId: retryState?.retryId ?? ""
152
+ },
153
+ { silent: shouldIgnoreMessages([errorMsg2]) }
154
+ );
155
+ sendBeacon({
156
+ errorMessage: "Exceeded maximum reload attempts",
157
+ eventName: "chunk_error_max_reloads",
158
+ retryAttempt: currentAttempt,
159
+ retryId: retryState?.retryId,
160
+ serialized: JSON.stringify({
161
+ error: String(error),
162
+ retryAttempt: currentAttempt,
163
+ retryId: retryState?.retryId
164
+ })
165
+ });
166
+ if (!useRetryId) {
167
+ clearRetryAttemptFromUrl();
168
+ }
169
+ reloadScheduled = false;
170
+ showFallbackUI();
171
+ return;
172
+ }
173
+ const nextAttempt = currentAttempt + 1;
174
+ const delay = reloadDelays[currentAttempt] ?? 1e3;
175
+ const errorMsg = String(error);
176
+ emitEvent(
177
+ {
178
+ attempt: nextAttempt,
179
+ delay,
180
+ name: "retry-attempt",
181
+ retryId
182
+ },
183
+ { silent: shouldIgnoreMessages([errorMsg]) }
184
+ );
185
+ getLogger()?.retrySchedulingReload(retryId, nextAttempt, delay);
186
+ showLoadingUI(nextAttempt);
187
+ setTimeout(() => {
188
+ if (useRetryId && enableRetryReset) {
189
+ setLastReloadTime(retryId, nextAttempt);
190
+ }
191
+ if (useRetryId) {
192
+ const reloadUrl = buildReloadUrl(retryId, nextAttempt);
193
+ globalThis.window.location.href = reloadUrl;
194
+ } else {
195
+ globalThis.window.location.href = buildReloadUrlAttemptOnly(nextAttempt);
196
+ }
197
+ }, delay);
198
+ } catch {
199
+ reloadScheduled = false;
200
+ }
201
+ };
202
+ var showLoadingUI = (attempt) => {
203
+ const options = getOptions();
204
+ const loadingHtml = options.html?.loading?.content;
205
+ const selector = options.html?.fallback?.selector ?? "body";
206
+ if (!loadingHtml) {
207
+ return;
208
+ }
209
+ try {
210
+ const targetElement = document.querySelector(selector);
211
+ if (!targetElement) {
212
+ return;
213
+ }
214
+ const container = document.createElement("div");
215
+ container.innerHTML = loadingHtml;
216
+ const spinnerEl = container.querySelector("[data-spa-guard-spinner]");
217
+ if (spinnerEl) {
218
+ if (options.spinner?.disabled) {
219
+ spinnerEl.remove();
220
+ } else if (options.spinner?.content) {
221
+ spinnerEl.innerHTML = options.spinner.content;
222
+ }
223
+ }
224
+ const retryingSection = container.querySelector(
225
+ '[data-spa-guard-section="retrying"]'
226
+ );
227
+ if (retryingSection) {
228
+ retryingSection.style.display = "block";
229
+ }
230
+ const attemptEl = container.querySelector('[data-spa-guard-content="attempt"]');
231
+ if (attemptEl) {
232
+ attemptEl.textContent = String(attempt);
233
+ }
234
+ const t = getI18n();
235
+ if (t) {
236
+ applyI18n(container, t);
237
+ }
238
+ targetElement.innerHTML = container.innerHTML;
239
+ } catch {
240
+ }
241
+ };
242
+ var showFallbackUI = () => {
243
+ const options = getOptions();
244
+ const fallbackHtml = options.html?.fallback?.content;
245
+ const selector = options.html?.fallback?.selector ?? "body";
246
+ if (!fallbackHtml) {
247
+ getLogger()?.noFallbackConfigured();
248
+ return;
249
+ }
250
+ try {
251
+ const targetElement = document.querySelector(selector);
252
+ if (!targetElement) {
253
+ getLogger()?.fallbackTargetNotFound(selector);
254
+ return;
255
+ }
256
+ const container = document.createElement("div");
257
+ container.innerHTML = fallbackHtml;
258
+ const t = getI18n();
259
+ if (t) {
260
+ applyI18n(container, t);
261
+ }
262
+ targetElement.innerHTML = container.innerHTML;
263
+ const useRetryId = options.useRetryId ?? true;
264
+ const retryState = getRetryStateFromUrl();
265
+ if (retryState && retryState.retryAttempt === -1) {
266
+ getLogger()?.clearingRetryState();
267
+ clearRetryStateFromUrl();
268
+ } else if (!useRetryId && !retryState) {
269
+ clearRetryAttemptFromUrl();
270
+ }
271
+ const reloadBtn = targetElement.querySelector('[data-spa-guard-action="reload"]');
272
+ if (reloadBtn) {
273
+ reloadBtn.addEventListener("click", () => location.reload());
274
+ }
275
+ if (retryState) {
276
+ const retryIdElements = document.getElementsByClassName("spa-guard-retry-id");
277
+ for (const element of retryIdElements) {
278
+ element.textContent = retryState.retryId;
279
+ }
280
+ }
281
+ emitEvent({
282
+ name: "fallback-ui-shown"
283
+ });
284
+ } catch (error) {
285
+ getLogger()?.fallbackInjectFailed(error);
286
+ }
287
+ };
288
+
289
+ export {
290
+ shouldIgnoreMessages,
291
+ shouldForceRetry,
292
+ sendBeacon,
293
+ attemptReload,
294
+ showFallbackUI
295
+ };
@@ -3,7 +3,7 @@ import {
3
3
  getRetryAttemptFromUrl,
4
4
  getRetryStateFromUrl,
5
5
  subscribe
6
- } from "./chunk-YSKH5K6P.js";
6
+ } from "./chunk-X6E7KUDK.js";
7
7
 
8
8
  // src/runtime/state.ts
9
9
  var getInitialStateFromUrl = () => {
@@ -1,3 +1,7 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-MLKGABMK.js";
4
+
1
5
  // package.json
2
6
  var name = "@ovineko/spa-guard";
3
7
 
@@ -9,6 +13,7 @@ var initializedKey = /* @__PURE__ */ Symbol.for(`${name}:initialized`);
9
13
  var loggerWindowKey = /* @__PURE__ */ Symbol.for(`${name}:logger`);
10
14
  var RETRY_ID_PARAM = "spaGuardRetryId";
11
15
  var RETRY_ATTEMPT_PARAM = "spaGuardRetryAttempt";
16
+ var versionCheckStateWindowKey = /* @__PURE__ */ Symbol.for(`${name}:version-check-state`);
12
17
  var debugSyncErrorEventType = "spa-guard:debug-sync-error";
13
18
 
14
19
  // src/common/events/internal.ts
@@ -68,6 +73,152 @@ var isDefaultRetryEnabled = () => {
68
73
  return internalConfig.defaultRetryEnabled;
69
74
  };
70
75
 
76
+ // src/common/i18n.ts
77
+ function applyI18n(container, t) {
78
+ const contentEls = container.querySelectorAll("[data-spa-guard-content]");
79
+ for (const el of contentEls) {
80
+ const key = el.dataset.spaGuardContent;
81
+ if (key && key in t) {
82
+ const value = t[key];
83
+ if (typeof value === "string") {
84
+ el.textContent = value;
85
+ }
86
+ }
87
+ }
88
+ const actionEls = container.querySelectorAll("[data-spa-guard-action]");
89
+ for (const el of actionEls) {
90
+ const action = el.dataset.spaGuardAction;
91
+ const tKey = action === "try-again" ? "tryAgain" : action;
92
+ if (tKey && tKey in t) {
93
+ const value = t[tKey];
94
+ if (typeof value === "string") {
95
+ el.textContent = value;
96
+ }
97
+ }
98
+ }
99
+ if (t.rtl) {
100
+ for (const child of container.children) {
101
+ if (child instanceof HTMLElement && child.tagName !== "STYLE") {
102
+ child.style.direction = "rtl";
103
+ }
104
+ }
105
+ }
106
+ }
107
+ function getI18n() {
108
+ try {
109
+ const el = document.querySelector('meta[name="spa-guard-i18n"]');
110
+ if (!el) {
111
+ return null;
112
+ }
113
+ const content = el.getAttribute("content");
114
+ if (!content) {
115
+ return null;
116
+ }
117
+ return JSON.parse(content);
118
+ } catch {
119
+ return null;
120
+ }
121
+ }
122
+ function setTranslations(translations) {
123
+ let el = document.querySelector('meta[name="spa-guard-i18n"]');
124
+ if (!el) {
125
+ el = document.createElement("meta");
126
+ el.setAttribute("name", "spa-guard-i18n");
127
+ document.head.append(el);
128
+ }
129
+ el.setAttribute("content", JSON.stringify(translations));
130
+ }
131
+
132
+ // src/common/html.generated.ts
133
+ var defaultErrorFallbackHtml = `<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",Menlo,monospace}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif"><div style="text-align:center;max-width:480px"><div style="margin-bottom:1.5rem"><svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none" viewBox="0 0 24 24" stroke="#b0b0b0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div><h1 data-spa-guard-content="heading" style="font-size:1.375rem;font-weight:600;margin:0 0 .5rem;color:#1a1a1a;line-height:1.3">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:0 auto 1.5rem;color:#666;font-size:.9375rem;line-height:1.5">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap"><button data-spa-guard-action="try-again" type="button" style="display:none;padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;border:1px solid #d0d0d0;background:#fff;color:#333;cursor:pointer;line-height:1.5">Try again</button> <button data-spa-guard-action="reload" type="button" style="padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;border:1px solid transparent;background:#111;color:#fff;cursor:pointer;line-height:1.5">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:1.5rem;font-size:.6875rem;color:#999">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>`;
134
+ var defaultLoadingFallbackHtml = `<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif"><div style="text-align:center"><div data-spa-guard-spinner style="margin-bottom:1.25rem"></div><h2 data-spa-guard-content="loading" style="font-size:1.125rem;font-weight:600;margin:0 0 .25rem;color:#1a1a1a">Loading...</h2><p data-spa-guard-section="retrying" style="display:none;font-size:.8125rem;color:#999;margin:.5rem 0 0"><span data-spa-guard-content="retrying">Retry attempt</span> <span data-spa-guard-content="attempt"></span></p></div></div>`;
135
+ var defaultSpinnerHtml = `<svg width="40" height="40" viewBox="0 0 40 40" style="animation:spa-guard-spin .8s linear infinite"><circle cx="20" cy="20" r="16" fill="none" stroke="#e8e8e8" stroke-width="3"/><circle cx="20" cy="20" r="16" fill="none" stroke="#666" stroke-width="3" stroke-dasharray="80" stroke-dashoffset="60" stroke-linecap="round"/></svg><style>@keyframes spa-guard-spin{to{transform:rotate(360deg)}}</style>`;
136
+
137
+ // src/common/options.ts
138
+ var options_exports = {};
139
+ __export(options_exports, {
140
+ getOptions: () => getOptions,
141
+ optionsWindowKey: () => optionsWindowKey
142
+ });
143
+ var defaultOptions = {
144
+ checkVersion: {
145
+ cache: "no-store",
146
+ interval: 3e5,
147
+ mode: "html",
148
+ onUpdate: "reload"
149
+ },
150
+ enableRetryReset: true,
151
+ errors: {
152
+ forceRetry: [],
153
+ ignore: []
154
+ },
155
+ handleUnhandledRejections: {
156
+ retry: true,
157
+ sendBeacon: true
158
+ },
159
+ html: {
160
+ fallback: {
161
+ content: defaultErrorFallbackHtml,
162
+ selector: "body"
163
+ },
164
+ loading: {
165
+ content: defaultLoadingFallbackHtml
166
+ }
167
+ },
168
+ lazyRetry: {
169
+ callReloadOnFailure: true,
170
+ retryDelays: [1e3, 2e3]
171
+ },
172
+ minTimeBetweenResets: 5e3,
173
+ reloadDelays: [1e3, 2e3, 5e3],
174
+ spinner: {
175
+ background: "#fff",
176
+ disabled: false
177
+ },
178
+ useRetryId: true
179
+ };
180
+ var getOptions = () => {
181
+ const windowOptions = globalThis.window?.[optionsWindowKey];
182
+ return {
183
+ ...defaultOptions,
184
+ ...windowOptions,
185
+ checkVersion: {
186
+ ...defaultOptions.checkVersion,
187
+ ...windowOptions?.checkVersion
188
+ },
189
+ errors: {
190
+ ...defaultOptions.errors,
191
+ ...windowOptions?.errors
192
+ },
193
+ handleUnhandledRejections: {
194
+ ...defaultOptions.handleUnhandledRejections,
195
+ ...windowOptions?.handleUnhandledRejections
196
+ },
197
+ html: {
198
+ fallback: {
199
+ ...defaultOptions.html?.fallback,
200
+ ...windowOptions?.html?.fallback
201
+ },
202
+ loading: {
203
+ ...defaultOptions.html?.loading,
204
+ ...windowOptions?.html?.loading
205
+ }
206
+ },
207
+ lazyRetry: {
208
+ ...defaultOptions.lazyRetry,
209
+ ...windowOptions?.lazyRetry
210
+ },
211
+ reportBeacon: {
212
+ ...defaultOptions.reportBeacon,
213
+ ...windowOptions?.reportBeacon
214
+ },
215
+ spinner: {
216
+ ...defaultOptions.spinner,
217
+ ...windowOptions?.spinner
218
+ }
219
+ };
220
+ };
221
+
71
222
  // src/common/errors/ForceRetryError.ts
72
223
  var FORCE_RETRY_MAGIC = "__SPA_GUARD_FORCE_RETRY__";
73
224
  var ForceRetryError = class extends Error {
@@ -269,6 +420,7 @@ export {
269
420
  optionsWindowKey,
270
421
  RETRY_ID_PARAM,
271
422
  RETRY_ATTEMPT_PARAM,
423
+ versionCheckStateWindowKey,
272
424
  debugSyncErrorEventType,
273
425
  setLogger,
274
426
  getLogger,
@@ -279,12 +431,20 @@ export {
279
431
  disableDefaultRetry,
280
432
  enableDefaultRetry,
281
433
  isDefaultRetryEnabled,
434
+ applyI18n,
435
+ getI18n,
436
+ setTranslations,
282
437
  setLastReloadTime,
283
438
  getLastReloadTime,
284
439
  clearLastReloadTime,
285
440
  shouldResetRetryCycle,
286
441
  setLastRetryResetInfo,
287
442
  getLastRetryResetInfo,
443
+ defaultErrorFallbackHtml,
444
+ defaultLoadingFallbackHtml,
445
+ defaultSpinnerHtml,
446
+ getOptions,
447
+ options_exports,
288
448
  getRetryStateFromUrl,
289
449
  clearRetryStateFromUrl,
290
450
  updateRetryStateInUrl,
@@ -1,7 +1,12 @@
1
+ import {
2
+ showFallbackUI
3
+ } from "./chunk-CSN7MQGX.js";
1
4
  import {
2
5
  ForceRetryError,
3
- debugSyncErrorEventType
4
- } from "./chunk-YSKH5K6P.js";
6
+ debugSyncErrorEventType,
7
+ emitEvent,
8
+ getOptions
9
+ } from "./chunk-X6E7KUDK.js";
5
10
 
6
11
  // src/runtime/debug/errorDispatchers.ts
7
12
  function dispatchAsyncRuntimeError() {
@@ -29,6 +34,16 @@ function dispatchNetworkTimeout(delayMs = 3e3) {
29
34
  void Promise.reject(new TypeError("NetworkError: request timed out"));
30
35
  }, delayMs);
31
36
  }
37
+ function dispatchRetryExhausted() {
38
+ const options = getOptions();
39
+ const reloadDelays = options.reloadDelays ?? [1e3, 2e3, 5e3];
40
+ emitEvent({
41
+ finalAttempt: reloadDelays.length,
42
+ name: "retry-exhausted",
43
+ retryId: ""
44
+ });
45
+ showFallbackUI();
46
+ }
32
47
  function dispatchSyncRuntimeError() {
33
48
  const error = new Error("Simulated sync runtime error from spa-guard debug panel");
34
49
  globalThis.dispatchEvent(new CustomEvent(debugSyncErrorEventType, { detail: { error } }));
@@ -45,6 +60,7 @@ export {
45
60
  dispatchFinallyError,
46
61
  dispatchForceRetryError,
47
62
  dispatchNetworkTimeout,
63
+ dispatchRetryExhausted,
48
64
  dispatchSyncRuntimeError,
49
65
  dispatchUnhandledRejection
50
66
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defaultSpinnerHtml,
3
3
  getOptions
4
- } from "./chunk-DOTM7FSY.js";
4
+ } from "./chunk-X6E7KUDK.js";
5
5
 
6
6
  // src/common/parseVersion.ts
7
7
  function extractVersionFromHtml(html) {