@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 +16 -16
- package/dist/chunk-CSN7MQGX.js +295 -0
- package/dist/{chunk-SZS6GOPK.js → chunk-T72DERME.js} +1 -1
- package/dist/{chunk-YSKH5K6P.js → chunk-X6E7KUDK.js} +160 -0
- package/dist/{chunk-74K44EMP.js → chunk-XFILAQ7P.js} +18 -2
- package/dist/{chunk-3ISQYZVD.js → chunk-Z75UPJWV.js} +1 -1
- package/dist/{chunk-4FKSMB5F.js → chunk-ZVYB2746.js} +7 -289
- package/dist/common/constants.d.ts +1 -0
- package/dist/common/index.js +4 -5
- package/dist/common/options.d.ts +7 -0
- package/dist/common/reload.d.ts +1 -0
- package/dist/runtime/debug/errorDispatchers.d.ts +6 -0
- package/dist/runtime/debug/index.js +7 -4
- package/dist/runtime/index.js +82 -66
- package/package.json +1 -1
- package/dist/chunk-DOTM7FSY.js +0 -162
package/dist/_internal.js
CHANGED
|
@@ -1,25 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
attemptReload,
|
|
3
2
|
createLogger,
|
|
4
3
|
isChunkError,
|
|
5
4
|
listenInternal,
|
|
6
|
-
|
|
7
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
+
};
|
|
@@ -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
|
-
|
|
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
|
};
|