@ovineko/spa-guard 0.0.1-alpha-6 → 0.0.1-alpha-7
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/README.md +1 -1
- package/dist/{chunk-CMBBYLOH.js → chunk-4N3XJ7VS.js} +2 -2
- package/dist/{chunk-T5RWH2HR.js → chunk-BLVJHZST.js} +11 -0
- package/dist/{chunk-563JZA5T.js → chunk-D5NHZ5BD.js} +3 -3
- package/dist/{chunk-PVLEHXOQ.js → chunk-U2DZSTRT.js} +108 -103
- package/dist/{chunk-UVB6PO4N.js → chunk-UXLGA3TG.js} +1 -1
- package/dist/{chunk-2DFYUVHH.js → chunk-WKH2B2XS.js} +1 -1
- package/dist/{chunk-FMEBY5ND.js → chunk-XOCUWXQL.js} +4 -4
- package/dist/common/fallbackHtml.generated.d.ts +1 -1
- package/dist/common/index.js +4 -6
- package/dist/common/reload.d.ts +1 -1
- package/dist/react/index.js +6 -7
- package/dist/react-error-boundary/index.js +7 -8
- package/dist/react-router/index.js +7 -8
- package/dist/runtime/debug/errorDispatchers.d.ts +12 -0
- package/dist/runtime/debug/index.js +18 -3
- package/dist/runtime/index.js +4 -5
- package/dist/vite-plugin/index.js +1 -1
- package/dist-inline/index.js +1 -1
- package/dist-inline-trace/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-OQGJLNZ2.js +0 -13
package/README.md
CHANGED
|
@@ -800,7 +800,7 @@ function createDebugger(options?: {
|
|
|
800
800
|
|
|
801
801
|
- Framework-agnostic vanilla JS (no React dependency)
|
|
802
802
|
- Fixed-position overlay panel with toggle open/close
|
|
803
|
-
- Error scenario buttons: ChunkLoadError, Network Timeout, Sync Runtime Error, Async Runtime Error, Finally Error
|
|
803
|
+
- Error scenario buttons: ChunkLoadError, Network Timeout, Sync Runtime Error, Async Runtime Error, Finally Error, ForceRetry Error, Unhandled Rejection
|
|
804
804
|
- Button visual states (default, loading, triggered)
|
|
805
805
|
- Live spa-guard state display (attempt, isWaiting, isFallbackShown)
|
|
806
806
|
- Scrollable event history with timestamps
|
|
@@ -7,6 +7,15 @@ import {
|
|
|
7
7
|
loggerWindowKey
|
|
8
8
|
} from "./chunk-EDRTFPCN.js";
|
|
9
9
|
|
|
10
|
+
// src/common/errors/ForceRetryError.ts
|
|
11
|
+
var FORCE_RETRY_MAGIC = "__SPA_GUARD_FORCE_RETRY__";
|
|
12
|
+
var ForceRetryError = class extends Error {
|
|
13
|
+
constructor(message, options) {
|
|
14
|
+
super(`${FORCE_RETRY_MAGIC}${message ?? ""}`, options);
|
|
15
|
+
this.name = "ForceRetryError";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
10
19
|
// src/common/events/internal.ts
|
|
11
20
|
if (globalThis.window && !globalThis.window[eventSubscribersWindowKey]) {
|
|
12
21
|
globalThis.window[eventSubscribersWindowKey] = /* @__PURE__ */ new Set();
|
|
@@ -255,6 +264,8 @@ var getRetryInfoForBeacon = () => {
|
|
|
255
264
|
};
|
|
256
265
|
|
|
257
266
|
export {
|
|
267
|
+
FORCE_RETRY_MAGIC,
|
|
268
|
+
ForceRetryError,
|
|
258
269
|
setLogger,
|
|
259
270
|
getLogger,
|
|
260
271
|
emitEvent,
|
|
@@ -7,14 +7,14 @@ import {
|
|
|
7
7
|
sendBeacon,
|
|
8
8
|
shouldForceRetry,
|
|
9
9
|
shouldIgnoreMessages
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-U2DZSTRT.js";
|
|
11
11
|
import {
|
|
12
12
|
defaultErrorFallbackHtml,
|
|
13
13
|
defaultLoadingFallbackHtml
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-UXLGA3TG.js";
|
|
15
15
|
import {
|
|
16
16
|
getRetryInfoForBeacon
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-BLVJHZST.js";
|
|
18
18
|
|
|
19
19
|
// src/common/DefaultErrorFallback.tsx
|
|
20
20
|
import { useLayoutEffect, useMemo, useRef } from "react";
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FORCE_RETRY_MAGIC
|
|
3
|
-
} from "./chunk-OQGJLNZ2.js";
|
|
4
1
|
import {
|
|
5
2
|
getOptions
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UXLGA3TG.js";
|
|
7
4
|
import {
|
|
5
|
+
FORCE_RETRY_MAGIC,
|
|
8
6
|
clearLastReloadTime,
|
|
9
7
|
clearRetryAttemptFromUrl,
|
|
10
8
|
clearRetryStateFromUrl,
|
|
@@ -18,7 +16,7 @@ import {
|
|
|
18
16
|
setLastReloadTime,
|
|
19
17
|
setLastRetryResetInfo,
|
|
20
18
|
shouldResetRetryCycle
|
|
21
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-BLVJHZST.js";
|
|
22
20
|
import {
|
|
23
21
|
RETRY_ATTEMPT_PARAM,
|
|
24
22
|
RETRY_ID_PARAM
|
|
@@ -97,7 +95,7 @@ var sendBeacon = (beacon) => {
|
|
|
97
95
|
const body = JSON.stringify(enrichedBeacon);
|
|
98
96
|
const isSendBeaconAvailable = typeof globalThis.window?.navigator?.sendBeacon === "function";
|
|
99
97
|
const isSentBeacon = isSendBeaconAvailable && globalThis.window.navigator.sendBeacon(options.reportBeacon.endpoint, body);
|
|
100
|
-
if (!isSentBeacon) {
|
|
98
|
+
if (!isSentBeacon && typeof fetch === "function") {
|
|
101
99
|
fetch(options.reportBeacon.endpoint, { body, keepalive: true, method: "POST" }).catch(
|
|
102
100
|
(error) => {
|
|
103
101
|
getLogger()?.beaconSendFailed(error);
|
|
@@ -124,109 +122,116 @@ var attemptReload = (error) => {
|
|
|
124
122
|
getLogger()?.reloadAlreadyScheduled(error);
|
|
125
123
|
return;
|
|
126
124
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
retryState
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
let retryId = retryState?.retryId ?? generateRetryId();
|
|
141
|
-
getLogger()?.retryCycleStarting(retryId, currentAttempt);
|
|
142
|
-
const retryEnabled = isDefaultRetryEnabled();
|
|
143
|
-
emitEvent({
|
|
144
|
-
error,
|
|
145
|
-
isRetrying: retryEnabled && currentAttempt >= 0 && currentAttempt < reloadDelays.length,
|
|
146
|
-
name: "chunk-error"
|
|
147
|
-
});
|
|
148
|
-
if (!retryEnabled) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
if (enableRetryReset && retryState && retryState.retryAttempt > 0 && shouldResetRetryCycle(retryState, reloadDelays, minTimeBetweenResets)) {
|
|
152
|
-
const lastReload = getLastReloadTime();
|
|
153
|
-
const timeSinceReload = lastReload ? Date.now() - lastReload.timestamp : 0;
|
|
154
|
-
clearRetryStateFromUrl();
|
|
155
|
-
clearLastReloadTime();
|
|
156
|
-
setLastRetryResetInfo(retryState.retryId);
|
|
157
|
-
const errorMsg2 = String(error);
|
|
158
|
-
emitEvent(
|
|
159
|
-
{
|
|
160
|
-
name: "retry-reset",
|
|
161
|
-
previousAttempt: retryState.retryAttempt,
|
|
162
|
-
previousRetryId: retryState.retryId,
|
|
163
|
-
timeSinceReload
|
|
164
|
-
},
|
|
165
|
-
{ silent: shouldIgnoreMessages([errorMsg2]) }
|
|
166
|
-
);
|
|
167
|
-
currentAttempt = 0;
|
|
168
|
-
retryId = generateRetryId();
|
|
169
|
-
}
|
|
170
|
-
if (currentAttempt === -1) {
|
|
171
|
-
const errorMsg2 = String(error);
|
|
172
|
-
if (!shouldIgnoreMessages([errorMsg2])) {
|
|
173
|
-
getLogger()?.fallbackAlreadyShown(error);
|
|
125
|
+
reloadScheduled = true;
|
|
126
|
+
try {
|
|
127
|
+
const options = getOptions();
|
|
128
|
+
const reloadDelays = options.reloadDelays ?? [1e3, 2e3, 5e3];
|
|
129
|
+
const useRetryId = options.useRetryId ?? true;
|
|
130
|
+
const enableRetryReset = options.enableRetryReset ?? true;
|
|
131
|
+
const minTimeBetweenResets = options.minTimeBetweenResets ?? 5e3;
|
|
132
|
+
let retryState;
|
|
133
|
+
if (useRetryId) {
|
|
134
|
+
retryState = getRetryStateFromUrl();
|
|
135
|
+
} else {
|
|
136
|
+
const attempt = getRetryAttemptFromUrl();
|
|
137
|
+
retryState = attempt === null ? null : { retryAttempt: attempt, retryId: generateRetryId() };
|
|
174
138
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
139
|
+
let currentAttempt = retryState ? retryState.retryAttempt : 0;
|
|
140
|
+
let retryId = retryState?.retryId ?? generateRetryId();
|
|
141
|
+
getLogger()?.retryCycleStarting(retryId, currentAttempt);
|
|
142
|
+
const retryEnabled = isDefaultRetryEnabled();
|
|
143
|
+
emitEvent({
|
|
144
|
+
error,
|
|
145
|
+
isRetrying: retryEnabled && currentAttempt >= 0 && currentAttempt < reloadDelays.length,
|
|
146
|
+
name: "chunk-error"
|
|
147
|
+
});
|
|
148
|
+
if (!retryEnabled) {
|
|
149
|
+
reloadScheduled = false;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (enableRetryReset && retryState && retryState.retryAttempt > 0 && shouldResetRetryCycle(retryState, reloadDelays, minTimeBetweenResets)) {
|
|
153
|
+
const lastReload = getLastReloadTime();
|
|
154
|
+
const timeSinceReload = lastReload ? Date.now() - lastReload.timestamp : 0;
|
|
155
|
+
clearRetryStateFromUrl();
|
|
156
|
+
clearLastReloadTime();
|
|
157
|
+
setLastRetryResetInfo(retryState.retryId);
|
|
158
|
+
const errorMsg2 = String(error);
|
|
159
|
+
emitEvent(
|
|
160
|
+
{
|
|
161
|
+
name: "retry-reset",
|
|
162
|
+
previousAttempt: retryState.retryAttempt,
|
|
163
|
+
previousRetryId: retryState.retryId,
|
|
164
|
+
timeSinceReload
|
|
165
|
+
},
|
|
166
|
+
{ silent: shouldIgnoreMessages([errorMsg2]) }
|
|
167
|
+
);
|
|
168
|
+
currentAttempt = 0;
|
|
169
|
+
retryId = generateRetryId();
|
|
170
|
+
}
|
|
171
|
+
if (currentAttempt === -1) {
|
|
172
|
+
const errorMsg2 = String(error);
|
|
173
|
+
if (!shouldIgnoreMessages([errorMsg2])) {
|
|
174
|
+
getLogger()?.fallbackAlreadyShown(error);
|
|
175
|
+
}
|
|
176
|
+
reloadScheduled = false;
|
|
177
|
+
showFallbackUI();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (currentAttempt >= reloadDelays.length) {
|
|
181
|
+
const errorMsg2 = String(error);
|
|
182
|
+
emitEvent(
|
|
183
|
+
{
|
|
184
|
+
finalAttempt: currentAttempt,
|
|
185
|
+
name: "retry-exhausted",
|
|
186
|
+
retryId: retryState?.retryId ?? ""
|
|
187
|
+
},
|
|
188
|
+
{ silent: shouldIgnoreMessages([errorMsg2]) }
|
|
189
|
+
);
|
|
190
|
+
sendBeacon({
|
|
191
|
+
errorMessage: "Exceeded maximum reload attempts",
|
|
192
|
+
eventName: "chunk_error_max_reloads",
|
|
193
|
+
retryAttempt: currentAttempt,
|
|
194
|
+
retryId: retryState?.retryId,
|
|
195
|
+
serialized: JSON.stringify({
|
|
196
|
+
error: String(error),
|
|
197
|
+
retryAttempt: currentAttempt,
|
|
198
|
+
retryId: retryState?.retryId
|
|
199
|
+
})
|
|
200
|
+
});
|
|
201
|
+
if (!useRetryId) {
|
|
202
|
+
clearRetryAttemptFromUrl();
|
|
203
|
+
}
|
|
204
|
+
reloadScheduled = false;
|
|
205
|
+
showFallbackUI();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const nextAttempt = currentAttempt + 1;
|
|
209
|
+
const delay = reloadDelays[currentAttempt] ?? 1e3;
|
|
210
|
+
const errorMsg = String(error);
|
|
180
211
|
emitEvent(
|
|
181
212
|
{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
213
|
+
attempt: nextAttempt,
|
|
214
|
+
delay,
|
|
215
|
+
name: "retry-attempt",
|
|
216
|
+
retryId
|
|
185
217
|
},
|
|
186
|
-
{ silent: shouldIgnoreMessages([
|
|
218
|
+
{ silent: shouldIgnoreMessages([errorMsg]) }
|
|
187
219
|
);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
showFallbackUI();
|
|
203
|
-
return;
|
|
220
|
+
getLogger()?.retrySchedulingReload(retryId, nextAttempt, delay);
|
|
221
|
+
setTimeout(() => {
|
|
222
|
+
if (useRetryId && enableRetryReset) {
|
|
223
|
+
setLastReloadTime(retryId, nextAttempt);
|
|
224
|
+
}
|
|
225
|
+
if (useRetryId) {
|
|
226
|
+
const reloadUrl = buildReloadUrl(retryId, nextAttempt);
|
|
227
|
+
globalThis.window.location.href = reloadUrl;
|
|
228
|
+
} else {
|
|
229
|
+
globalThis.window.location.href = buildReloadUrlAttemptOnly(nextAttempt);
|
|
230
|
+
}
|
|
231
|
+
}, delay);
|
|
232
|
+
} catch {
|
|
233
|
+
reloadScheduled = false;
|
|
204
234
|
}
|
|
205
|
-
const nextAttempt = currentAttempt + 1;
|
|
206
|
-
const delay = reloadDelays[currentAttempt] ?? 1e3;
|
|
207
|
-
const errorMsg = String(error);
|
|
208
|
-
emitEvent(
|
|
209
|
-
{
|
|
210
|
-
attempt: nextAttempt,
|
|
211
|
-
delay,
|
|
212
|
-
name: "retry-attempt",
|
|
213
|
-
retryId
|
|
214
|
-
},
|
|
215
|
-
{ silent: shouldIgnoreMessages([errorMsg]) }
|
|
216
|
-
);
|
|
217
|
-
reloadScheduled = true;
|
|
218
|
-
getLogger()?.retrySchedulingReload(retryId, nextAttempt, delay);
|
|
219
|
-
setTimeout(() => {
|
|
220
|
-
if (useRetryId && enableRetryReset) {
|
|
221
|
-
setLastReloadTime(retryId, nextAttempt);
|
|
222
|
-
}
|
|
223
|
-
if (useRetryId) {
|
|
224
|
-
const reloadUrl = buildReloadUrl(retryId, nextAttempt);
|
|
225
|
-
globalThis.window.location.href = reloadUrl;
|
|
226
|
-
} else {
|
|
227
|
-
globalThis.window.location.href = buildReloadUrlAttemptOnly(nextAttempt);
|
|
228
|
-
}
|
|
229
|
-
}, delay);
|
|
230
235
|
};
|
|
231
236
|
var showFallbackUI = () => {
|
|
232
237
|
const options = getOptions();
|
|
@@ -13,7 +13,7 @@ __export(options_exports, {
|
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
// src/common/fallbackHtml.generated.ts
|
|
16
|
-
var defaultErrorFallbackHtml = `<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h1 data-spa-guard-content="heading">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:1rem auto">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center"><button data-spa-guard-action="try-again" type="button" style="display:none">Try again</button> <button data-spa-guard-action="reload" type="button">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:20px;font-size:12px">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>`;
|
|
16
|
+
var defaultErrorFallbackHtml = `<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:monospace}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h1 data-spa-guard-content="heading">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:1rem auto">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center"><button data-spa-guard-action="try-again" type="button" style="display:none">Try again</button> <button data-spa-guard-action="reload" type="button">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:20px;font-size:12px">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>`;
|
|
17
17
|
var defaultLoadingFallbackHtml = `<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h2>Loading...</h2><p data-spa-guard-section="retrying" style="display:none">Retry attempt <span data-spa-guard-content="attempt"></span></p></div></div>`;
|
|
18
18
|
|
|
19
19
|
// src/common/options.ts
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attemptReload,
|
|
3
3
|
isChunkError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-U2DZSTRT.js";
|
|
5
5
|
import {
|
|
6
6
|
getState,
|
|
7
7
|
subscribeToState
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WKH2B2XS.js";
|
|
9
9
|
import {
|
|
10
10
|
getOptions
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-UXLGA3TG.js";
|
|
12
12
|
import {
|
|
13
13
|
emitEvent,
|
|
14
14
|
isDefaultRetryEnabled,
|
|
15
15
|
subscribe
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-BLVJHZST.js";
|
|
17
17
|
import {
|
|
18
18
|
debugSyncErrorEventType
|
|
19
19
|
} from "./chunk-EDRTFPCN.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const defaultErrorFallbackHtml = "<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}</style><div style=\"display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem\"><div style=\"text-align:center\"><h1 data-spa-guard-content=\"heading\">Something went wrong</h1><p data-spa-guard-content=\"message\" style=\"max-width:600px;margin:1rem auto\">Please refresh the page to continue.</p><div style=\"display:flex;gap:.5rem;justify-content:center\"><button data-spa-guard-action=\"try-again\" type=\"button\" style=\"display:none\">Try again</button> <button data-spa-guard-action=\"reload\" type=\"button\">Reload page</button></div><p class=\"spa-guard-error-id\" style=\"margin-top:20px;font-size:12px\">Error ID: <span class=\"spa-guard-retry-id\"></span></p></div></div>";
|
|
1
|
+
export declare const defaultErrorFallbackHtml = "<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:monospace}</style><div style=\"display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem\"><div style=\"text-align:center\"><h1 data-spa-guard-content=\"heading\">Something went wrong</h1><p data-spa-guard-content=\"message\" style=\"max-width:600px;margin:1rem auto\">Please refresh the page to continue.</p><div style=\"display:flex;gap:.5rem;justify-content:center\"><button data-spa-guard-action=\"try-again\" type=\"button\" style=\"display:none\">Try again</button> <button data-spa-guard-action=\"reload\" type=\"button\">Reload page</button></div><p class=\"spa-guard-error-id\" style=\"margin-top:20px;font-size:12px\">Error ID: <span class=\"spa-guard-retry-id\"></span></p></div></div>";
|
|
2
2
|
export declare const defaultLoadingFallbackHtml = "<div style=\"display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem\"><div style=\"text-align:center\"><h2>Loading...</h2><p data-spa-guard-section=\"retrying\" style=\"display:none\">Retry attempt <span data-spa-guard-content=\"attempt\"></span></p></div></div>";
|
package/dist/common/index.js
CHANGED
|
@@ -10,15 +10,13 @@ import {
|
|
|
10
10
|
sendBeacon,
|
|
11
11
|
shouldForceRetry,
|
|
12
12
|
shouldIgnoreMessages
|
|
13
|
-
} from "../chunk-
|
|
14
|
-
import {
|
|
15
|
-
ForceRetryError
|
|
16
|
-
} from "../chunk-OQGJLNZ2.js";
|
|
13
|
+
} from "../chunk-U2DZSTRT.js";
|
|
17
14
|
import {
|
|
18
15
|
getOptions,
|
|
19
16
|
options_exports
|
|
20
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-UXLGA3TG.js";
|
|
21
18
|
import {
|
|
19
|
+
ForceRetryError,
|
|
22
20
|
disableDefaultRetry,
|
|
23
21
|
emitEvent,
|
|
24
22
|
enableDefaultRetry,
|
|
@@ -31,7 +29,7 @@ import {
|
|
|
31
29
|
setLogger,
|
|
32
30
|
subscribe,
|
|
33
31
|
updateRetryStateInUrl
|
|
34
|
-
} from "../chunk-
|
|
32
|
+
} from "../chunk-BLVJHZST.js";
|
|
35
33
|
import "../chunk-EDRTFPCN.js";
|
|
36
34
|
import "../chunk-RP52SPBK.js";
|
|
37
35
|
import {
|
package/dist/common/reload.d.ts
CHANGED
package/dist/react/index.js
CHANGED
|
@@ -4,15 +4,14 @@ import {
|
|
|
4
4
|
useSPAGuardChunkError,
|
|
5
5
|
useSPAGuardEvents,
|
|
6
6
|
useSpaGuardState
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-XOCUWXQL.js";
|
|
8
|
+
import "../chunk-U2DZSTRT.js";
|
|
9
|
+
import "../chunk-4N3XJ7VS.js";
|
|
10
|
+
import "../chunk-WKH2B2XS.js";
|
|
11
|
+
import "../chunk-UXLGA3TG.js";
|
|
11
12
|
import {
|
|
12
13
|
ForceRetryError
|
|
13
|
-
} from "../chunk-
|
|
14
|
-
import "../chunk-UVB6PO4N.js";
|
|
15
|
-
import "../chunk-T5RWH2HR.js";
|
|
14
|
+
} from "../chunk-BLVJHZST.js";
|
|
16
15
|
import "../chunk-EDRTFPCN.js";
|
|
17
16
|
import "../chunk-RP52SPBK.js";
|
|
18
17
|
import "../chunk-MLKGABMK.js";
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DefaultErrorFallback,
|
|
3
3
|
handleErrorWithSpaGuard
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-D5NHZ5BD.js";
|
|
5
5
|
import "../chunk-HUAI4DRW.js";
|
|
6
6
|
import {
|
|
7
7
|
useSpaGuardState
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-XOCUWXQL.js";
|
|
9
9
|
import {
|
|
10
10
|
isChunkError
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
15
|
-
import "../chunk-
|
|
16
|
-
import "../chunk-T5RWH2HR.js";
|
|
11
|
+
} from "../chunk-U2DZSTRT.js";
|
|
12
|
+
import "../chunk-4N3XJ7VS.js";
|
|
13
|
+
import "../chunk-WKH2B2XS.js";
|
|
14
|
+
import "../chunk-UXLGA3TG.js";
|
|
15
|
+
import "../chunk-BLVJHZST.js";
|
|
17
16
|
import "../chunk-EDRTFPCN.js";
|
|
18
17
|
import "../chunk-RP52SPBK.js";
|
|
19
18
|
import "../chunk-MLKGABMK.js";
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DefaultErrorFallback,
|
|
3
3
|
handleErrorWithSpaGuard
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-D5NHZ5BD.js";
|
|
5
5
|
import "../chunk-HUAI4DRW.js";
|
|
6
6
|
import {
|
|
7
7
|
useSpaGuardState
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-XOCUWXQL.js";
|
|
9
9
|
import {
|
|
10
10
|
isChunkError
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
15
|
-
import "../chunk-
|
|
16
|
-
import "../chunk-T5RWH2HR.js";
|
|
11
|
+
} from "../chunk-U2DZSTRT.js";
|
|
12
|
+
import "../chunk-4N3XJ7VS.js";
|
|
13
|
+
import "../chunk-WKH2B2XS.js";
|
|
14
|
+
import "../chunk-UXLGA3TG.js";
|
|
15
|
+
import "../chunk-BLVJHZST.js";
|
|
17
16
|
import "../chunk-EDRTFPCN.js";
|
|
18
17
|
import "../chunk-RP52SPBK.js";
|
|
19
18
|
import "../chunk-MLKGABMK.js";
|
|
@@ -24,6 +24,12 @@ export declare function dispatchChunkLoadError(): void;
|
|
|
24
24
|
* a window "unhandledrejection" event.
|
|
25
25
|
*/
|
|
26
26
|
export declare function dispatchFinallyError(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Dispatches a ForceRetryError via void Promise.reject().
|
|
29
|
+
* Triggers window "unhandledrejection" with a ForceRetryError whose message
|
|
30
|
+
* contains the FORCE_RETRY_MAGIC prefix, exercising the forceRetry path.
|
|
31
|
+
*/
|
|
32
|
+
export declare function dispatchForceRetryError(): void;
|
|
27
33
|
/**
|
|
28
34
|
* Dispatches an unhandled network timeout error after a delay.
|
|
29
35
|
* Uses setTimeout + void Promise.reject() to trigger window "unhandledrejection".
|
|
@@ -36,3 +42,9 @@ export declare function dispatchNetworkTimeout(delayMs?: number): void;
|
|
|
36
42
|
* React Error Boundary can catch it.
|
|
37
43
|
*/
|
|
38
44
|
export declare function dispatchSyncRuntimeError(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Dispatches a plain unhandled promise rejection via void Promise.reject().
|
|
47
|
+
* This is NOT a chunk error and NOT a ForceRetry — it exercises the
|
|
48
|
+
* handleUnhandledRejections config path for generic rejections.
|
|
49
|
+
*/
|
|
50
|
+
export declare function dispatchUnhandledRejection(): void;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
subscribeToState
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-WKH2B2XS.js";
|
|
4
4
|
import {
|
|
5
|
+
ForceRetryError,
|
|
5
6
|
subscribe
|
|
6
|
-
} from "../../chunk-
|
|
7
|
+
} from "../../chunk-BLVJHZST.js";
|
|
7
8
|
import {
|
|
8
9
|
debugSyncErrorEventType
|
|
9
10
|
} from "../../chunk-EDRTFPCN.js";
|
|
@@ -28,6 +29,9 @@ function dispatchFinallyError() {
|
|
|
28
29
|
throw new Error("Failed to fetch dynamically imported module: /finally-error-chunk.js");
|
|
29
30
|
});
|
|
30
31
|
}
|
|
32
|
+
function dispatchForceRetryError() {
|
|
33
|
+
void Promise.reject(new ForceRetryError("Simulated force-retry from spa-guard debug panel"));
|
|
34
|
+
}
|
|
31
35
|
function dispatchNetworkTimeout(delayMs = 3e3) {
|
|
32
36
|
setTimeout(() => {
|
|
33
37
|
void Promise.reject(new TypeError("NetworkError: request timed out"));
|
|
@@ -37,6 +41,11 @@ function dispatchSyncRuntimeError() {
|
|
|
37
41
|
const error = new Error("Simulated sync runtime error from spa-guard debug panel");
|
|
38
42
|
globalThis.dispatchEvent(new CustomEvent(debugSyncErrorEventType, { detail: { error } }));
|
|
39
43
|
}
|
|
44
|
+
function dispatchUnhandledRejection() {
|
|
45
|
+
void Promise.reject(
|
|
46
|
+
new Error("Simulated unhandled promise rejection from spa-guard debug panel")
|
|
47
|
+
);
|
|
48
|
+
}
|
|
40
49
|
|
|
41
50
|
// src/runtime/debug/index.ts
|
|
42
51
|
var SCENARIOS = [
|
|
@@ -44,7 +53,13 @@ var SCENARIOS = [
|
|
|
44
53
|
{ dispatch: () => dispatchNetworkTimeout(100), key: "network-timeout", label: "Network Timeout" },
|
|
45
54
|
{ dispatch: dispatchSyncRuntimeError, key: "sync-runtime-error", label: "Sync Runtime Error" },
|
|
46
55
|
{ dispatch: dispatchAsyncRuntimeError, key: "async-runtime-error", label: "Async Runtime Error" },
|
|
47
|
-
{ dispatch: dispatchFinallyError, key: "finally-error", label: "Finally Error" }
|
|
56
|
+
{ dispatch: dispatchFinallyError, key: "finally-error", label: "Finally Error" },
|
|
57
|
+
{ dispatch: dispatchForceRetryError, key: "force-retry-error", label: "ForceRetry Error" },
|
|
58
|
+
{
|
|
59
|
+
dispatch: dispatchUnhandledRejection,
|
|
60
|
+
key: "unhandled-rejection",
|
|
61
|
+
label: "Unhandled Rejection"
|
|
62
|
+
}
|
|
48
63
|
];
|
|
49
64
|
var POSITION_MAP = {
|
|
50
65
|
"bottom-left": "bottom:16px;left:16px;",
|
package/dist/runtime/index.js
CHANGED
|
@@ -2,16 +2,15 @@ import {
|
|
|
2
2
|
recommendedSetup,
|
|
3
3
|
startVersionCheck,
|
|
4
4
|
stopVersionCheck
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-4N3XJ7VS.js";
|
|
6
6
|
import {
|
|
7
7
|
getState,
|
|
8
8
|
subscribeToState
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-WKH2B2XS.js";
|
|
10
|
+
import "../chunk-UXLGA3TG.js";
|
|
10
11
|
import {
|
|
11
12
|
ForceRetryError
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-UVB6PO4N.js";
|
|
14
|
-
import "../chunk-T5RWH2HR.js";
|
|
13
|
+
} from "../chunk-BLVJHZST.js";
|
|
15
14
|
import "../chunk-EDRTFPCN.js";
|
|
16
15
|
import "../chunk-RP52SPBK.js";
|
|
17
16
|
import "../chunk-MLKGABMK.js";
|
package/dist-inline/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e="@ovineko/spa-guard",t=Symbol.for(e+":event-subscribers"),r=Symbol.for(e+":internal-config"),a=Symbol.for(e+":initialized"),n=Symbol.for(e+":logger"),l="spaGuardRetryId",o="spaGuardRetryAttempt";globalThis.window&&!globalThis.window[t]&&(globalThis.window[t]=new Set),globalThis.window&&!globalThis.window[r]&&(globalThis.window[r]={defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1});var i=globalThis.window?.[t]??new Set,s=globalThis.window?.[r]??{defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1},d=()=>globalThis.window?.[n],c=(e,t)=>{t?.silent||d()?.logEvent(e),i.forEach(t=>{try{t(e)}catch{}})},y=e=>{let t=p(e);return!!t&&[/Failed to fetch dynamically imported module/i,/Importing a module script failed/i,/error loading dynamically imported module/i,/Unable to preload CSS/i,/Loading chunk \d+ failed/i,/Loading CSS chunk \d+ failed/i,/ChunkLoadError/i].some(e=>e.test(t))},p=e=>e instanceof Error?e.message:"string"==typeof e?e:e&&"object"==typeof e&&"message"in e?e.message+"":e&&"object"==typeof e&&"reason"in e?p(e.reason):null,u={checkVersion:{interval:3e5,mode:"html",onUpdate:"reload"},enableRetryReset:!0,errors:{forceRetry:[],ignore:[]},handleUnhandledRejections:{retry:!0,sendBeacon:!0},html:{fallback:{content:'<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h1 data-spa-guard-content="heading">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:1rem auto">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center"><button data-spa-guard-action="try-again" type="button" style="display:none">Try again</button> <button data-spa-guard-action="reload" type="button">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:20px;font-size:12px">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>',selector:"body"},loading:{content:'<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h2>Loading...</h2><p data-spa-guard-section="retrying" style="display:none">Retry attempt <span data-spa-guard-content="attempt"></span></p></div></div>'}},lazyRetry:{callReloadOnFailure:!0,retryDelays:[1e3,2e3]},minTimeBetweenResets:5e3,reloadDelays:[1e3,2e3,5e3],useRetryId:!0},m=()=>{let e=globalThis.window?.__SPA_GUARD_OPTIONS__;return{...u,...e,checkVersion:{...u.checkVersion,...e?.checkVersion},errors:{...u.errors,...e?.errors},handleUnhandledRejections:{...u.handleUnhandledRejections,...e?.handleUnhandledRejections},html:{fallback:{...u.html?.fallback,...e?.html?.fallback},loading:{...u.html?.loading,...e?.html?.loading}},lazyRetry:{...u.lazyRetry,...e?.lazyRetry},reportBeacon:{...u.reportBeacon,...e?.reportBeacon}}},g="__spa_guard_last_reload_timestamp__",h="__spa_guard_last_retry_reset__",f=null,w=null,b=()=>{try{return void 0!==globalThis.window&&typeof sessionStorage<"u"}catch{return!1}},v=()=>{if(b())try{let e=sessionStorage.getItem(g);if(e)return JSON.parse(e)}catch{return f}return f},S=()=>{try{let e=new URLSearchParams(globalThis.window.location.search),t=e.get(l),r=e.get(o);if(t&&r){let e=parseInt(r,10);return Number.isNaN(e)?null:{retryAttempt:e,retryId:t}}return null}catch{return null}},R=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(l),e.searchParams.delete(o),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},I=()=>{if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint32Array(2);return crypto.getRandomValues(e),`${Date.now()}-${e[0].toString(36)}${e[1].toString(36)}`}return`${Date.now()}-${Math.random().toString(36).slice(2,15)}`},T=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(o),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},_=()=>{let e=S();return e?{retryAttempt:e.retryAttempt,retryId:e.retryId}:{}},k="__SPA_GUARD_FORCE_RETRY__",N=e=>{let t=(m().errors?.ignore??[]).filter(e=>""!==e);return 0!==t.length&&e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},A=e=>{let t=[...m().errors?.forceRetry??[],k].filter(e=>""!==e);return e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},D=e=>{if((e=>N([e.errorMessage,e.eventMessage]))(e))return;let t=m();if(!t.reportBeacon?.endpoint)return void d()?.noBeaconEndpoint();let r=t.appName?{...e,appName:t.appName}:e,a=JSON.stringify(r);"function"
|
|
1
|
+
var e="@ovineko/spa-guard",t=Symbol.for(e+":event-subscribers"),r=Symbol.for(e+":internal-config"),a=Symbol.for(e+":initialized"),n=Symbol.for(e+":logger"),l="spaGuardRetryId",o="spaGuardRetryAttempt";globalThis.window&&!globalThis.window[t]&&(globalThis.window[t]=new Set),globalThis.window&&!globalThis.window[r]&&(globalThis.window[r]={defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1});var i=globalThis.window?.[t]??new Set,s=globalThis.window?.[r]??{defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1},d=()=>globalThis.window?.[n],c=(e,t)=>{t?.silent||d()?.logEvent(e),i.forEach(t=>{try{t(e)}catch{}})},y=e=>{let t=p(e);return!!t&&[/Failed to fetch dynamically imported module/i,/Importing a module script failed/i,/error loading dynamically imported module/i,/Unable to preload CSS/i,/Loading chunk \d+ failed/i,/Loading CSS chunk \d+ failed/i,/ChunkLoadError/i].some(e=>e.test(t))},p=e=>e instanceof Error?e.message:"string"==typeof e?e:e&&"object"==typeof e&&"message"in e?e.message+"":e&&"object"==typeof e&&"reason"in e?p(e.reason):null,u={checkVersion:{interval:3e5,mode:"html",onUpdate:"reload"},enableRetryReset:!0,errors:{forceRetry:[],ignore:[]},handleUnhandledRejections:{retry:!0,sendBeacon:!0},html:{fallback:{content:'<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:monospace}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h1 data-spa-guard-content="heading">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:1rem auto">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center"><button data-spa-guard-action="try-again" type="button" style="display:none">Try again</button> <button data-spa-guard-action="reload" type="button">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:20px;font-size:12px">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>',selector:"body"},loading:{content:'<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h2>Loading...</h2><p data-spa-guard-section="retrying" style="display:none">Retry attempt <span data-spa-guard-content="attempt"></span></p></div></div>'}},lazyRetry:{callReloadOnFailure:!0,retryDelays:[1e3,2e3]},minTimeBetweenResets:5e3,reloadDelays:[1e3,2e3,5e3],useRetryId:!0},m=()=>{let e=globalThis.window?.__SPA_GUARD_OPTIONS__;return{...u,...e,checkVersion:{...u.checkVersion,...e?.checkVersion},errors:{...u.errors,...e?.errors},handleUnhandledRejections:{...u.handleUnhandledRejections,...e?.handleUnhandledRejections},html:{fallback:{...u.html?.fallback,...e?.html?.fallback},loading:{...u.html?.loading,...e?.html?.loading}},lazyRetry:{...u.lazyRetry,...e?.lazyRetry},reportBeacon:{...u.reportBeacon,...e?.reportBeacon}}},g="__spa_guard_last_reload_timestamp__",h="__spa_guard_last_retry_reset__",f=null,w=null,b=()=>{try{return void 0!==globalThis.window&&typeof sessionStorage<"u"}catch{return!1}},v=()=>{if(b())try{let e=sessionStorage.getItem(g);if(e)return JSON.parse(e)}catch{return f}return f},S=()=>{try{let e=new URLSearchParams(globalThis.window.location.search),t=e.get(l),r=e.get(o);if(t&&r){let e=parseInt(r,10);return Number.isNaN(e)?null:{retryAttempt:e,retryId:t}}return null}catch{return null}},R=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(l),e.searchParams.delete(o),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},I=()=>{if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint32Array(2);return crypto.getRandomValues(e),`${Date.now()}-${e[0].toString(36)}${e[1].toString(36)}`}return`${Date.now()}-${Math.random().toString(36).slice(2,15)}`},T=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(o),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},_=()=>{let e=S();return e?{retryAttempt:e.retryAttempt,retryId:e.retryId}:{}},k="__SPA_GUARD_FORCE_RETRY__",N=e=>{let t=(m().errors?.ignore??[]).filter(e=>""!==e);return 0!==t.length&&e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},A=e=>{let t=[...m().errors?.forceRetry??[],k].filter(e=>""!==e);return e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},D=e=>{if((e=>N([e.errorMessage,e.eventMessage]))(e))return;let t=m();if(!t.reportBeacon?.endpoint)return void d()?.noBeaconEndpoint();let r=t.appName?{...e,appName:t.appName}:e,a=JSON.stringify(r);("function"!=typeof globalThis.window?.navigator?.sendBeacon||!globalThis.window.navigator.sendBeacon(t.reportBeacon.endpoint,a))&&"function"==typeof fetch&&fetch(t.reportBeacon.endpoint,{body:a,keepalive:!0,method:"POST"}).catch(e=>{d()?.beaconSendFailed(e)})},U=!1,E=e=>{if(U)d()?.reloadAlreadyScheduled(e);else{U=!0;try{let t,r=m(),a=r.reloadDelays??[1e3,2e3,5e3],n=r.useRetryId??!0,i=r.enableRetryReset??!0,y=r.minTimeBetweenResets??5e3;if(n)t=S();else{let e=(()=>{try{let e=new URLSearchParams(globalThis.window.location.search).get(o);if(e){let t=parseInt(e,10);return Number.isNaN(t)?null:t}return null}catch{return null}})();t=null===e?null:{retryAttempt:e,retryId:I()}}let p=t?t.retryAttempt:0,u=t?.retryId??I();d()?.retryCycleStarting(u,p);let _=s.defaultRetryEnabled;if(c({error:e,isRetrying:_&&p>=0&&p<a.length,name:"chunk-error"}),!_)return void(U=!1);if(i&&t&&t.retryAttempt>0&&((e,t,r=5e3)=>{if(0===e.retryAttempt)return!1;let a=v();if(!a||a.retryId!==e.retryId)return!1;let n=(()=>{if(b())try{let e=sessionStorage.getItem(h);if(e)return JSON.parse(e)}catch{return w}return w})();return!(n&&Date.now()-n.timestamp<r)&&Date.now()-a.timestamp>(t[a.attemptNumber-1]??1e3)+3e4})(t,a,y)){let r=v(),a=r?Date.now()-r.timestamp:0;R(),(()=>{if(b())try{sessionStorage.removeItem(g)}catch{}f=null})(),(e=>{let t={previousRetryId:e,timestamp:Date.now()};if(b())try{sessionStorage.setItem(h,JSON.stringify(t))}catch{w=t}else w=t})(t.retryId);let n=e+"";c({name:"retry-reset",previousAttempt:t.retryAttempt,previousRetryId:t.retryId,timeSinceReload:a},{silent:N([n])}),p=0,u=I()}if(-1===p)return N([e+""])||d()?.fallbackAlreadyShown(e),U=!1,void L();if(p>=a.length){let r=e+"";return c({finalAttempt:p,name:"retry-exhausted",retryId:t?.retryId??""},{silent:N([r])}),D({errorMessage:"Exceeded maximum reload attempts",eventName:"chunk_error_max_reloads",retryAttempt:p,retryId:t?.retryId,serialized:JSON.stringify({error:e+"",retryAttempt:p,retryId:t?.retryId})}),n||T(),U=!1,void L()}let k=p+1,A=a[p]??1e3;c({attempt:k,delay:A,name:"retry-attempt",retryId:u},{silent:N([e+""])}),d()?.retrySchedulingReload(u,k,A),setTimeout(()=>{if(n&&i&&((e,t)=>{let r={attemptNumber:t,retryId:e,timestamp:Date.now()};if(b())try{sessionStorage.setItem(g,JSON.stringify(r))}catch{f=r}else f=r})(u,k),n){let e=((e,t)=>{let r=new URL(globalThis.window.location.href);return r.searchParams.set(l,e),r.searchParams.set(o,t+""),r.toString()})(u,k);globalThis.window.location.href=e}else globalThis.window.location.href=(e=>{let t=new URL(globalThis.window.location.href);return t.searchParams.set(o,e+""),t.toString()})(k)},A)}catch{U=!1}}},L=()=>{let e=m(),t=e.html?.fallback?.content,r=e.html?.fallback?.selector??"body";if(t)try{let a=document.querySelector(r);if(!a)return void d()?.fallbackTargetNotFound(r);a.innerHTML=t;let n=e.useRetryId??!0,l=S();l&&-1===l.retryAttempt?(d()?.clearingRetryState(),R()):!n&&!l&&T();let o=a.querySelector('[data-spa-guard-action="reload"]');if(o&&o.addEventListener("click",()=>location.reload()),l){let e=document.getElementsByClassName("spa-guard-retry-id");for(let t of e)t.textContent=l.retryId}c({name:"fallback-ui-shown"})}catch(e){d()?.fallbackInjectFailed(e)}else d()?.noFallbackConfigured()};(()=>{if(s.initialized)return;s.initialized=!0,void 0!==globalThis.window&&(globalThis.window[a]=!0);let e=m(),t=e.reloadDelays??[],r=S();r&&r.retryAttempt>=t.length&&(d()?.retryLimitExceeded(r.retryAttempt,t.length),(e=>{try{let t=new URL(globalThis.window.location.href);t.searchParams.set(l,e),t.searchParams.set(o,"-1"),globalThis.window.history.replaceState(null,"",t.toString())}catch{}})(r.retryId));let n=globalThis.window.addEventListener.bind(globalThis.window);n("error",e=>{if(!N([e.message]))return d()?.capturedError("error",e),y(e)||A([e.message])?(e.preventDefault(),void E(e.error??e)):void D({errorMessage:e.message,eventName:"error",serialized:"",..._()})},!0),n("unhandledrejection",t=>{let r=t.reason+"";if(N([r]))return;if(d()?.capturedError("unhandledrejection",t),y(t.reason))return t.preventDefault(),void E(t.reason);if(A([r]))return t.preventDefault(),void E(t.reason);let a=e.handleUnhandledRejections;!1!==a?.sendBeacon&&D({errorMessage:r,eventName:"unhandledrejection",serialized:"",..._()}),!1!==a?.retry&&(t.preventDefault(),E(t.reason))}),n("securitypolicyviolation",e=>{let t=`${e.violatedDirective}: ${e.blockedURI}`;N([t])||(d()?.capturedError("csp",e.blockedURI,e.violatedDirective),D({eventMessage:t,eventName:"securitypolicyviolation",serialized:"",..._()}))}),n("vite:preloadError",e=>{N([e?.payload?.message||e?.message])||(d()?.capturedError("vite:preloadError",e),e.preventDefault(),E(e?.payload??e))})})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e="@ovineko/spa-guard",t=Symbol.for(e+":event-subscribers"),r=Symbol.for(e+":internal-config"),a=Symbol.for(e+":initialized"),n=Symbol.for(e+":logger"),o="spaGuardRetryId",l="spaGuardRetryAttempt";globalThis.window&&!globalThis.window[t]&&(globalThis.window[t]=new Set),globalThis.window&&!globalThis.window[r]&&(globalThis.window[r]={defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1});var i=globalThis.window?.[t]??new Set,s=globalThis.window?.[r]??{defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1},d=()=>globalThis.window?.[n],c=(e,t)=>{t?.silent||d()?.logEvent(e),i.forEach(t=>{try{t(e)}catch{}})},y=e=>{let t=u(e);return!!t&&[/Failed to fetch dynamically imported module/i,/Importing a module script failed/i,/error loading dynamically imported module/i,/Unable to preload CSS/i,/Loading chunk \d+ failed/i,/Loading CSS chunk \d+ failed/i,/ChunkLoadError/i].some(e=>e.test(t))},u=e=>e instanceof Error?e.message:"string"==typeof e?e:e&&"object"==typeof e&&"message"in e?e.message+"":e&&"object"==typeof e&&"reason"in e?u(e.reason):null,m={checkVersion:{interval:3e5,mode:"html",onUpdate:"reload"},enableRetryReset:!0,errors:{forceRetry:[],ignore:[]},handleUnhandledRejections:{retry:!0,sendBeacon:!0},html:{fallback:{content:'<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h1 data-spa-guard-content="heading">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:1rem auto">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center"><button data-spa-guard-action="try-again" type="button" style="display:none">Try again</button> <button data-spa-guard-action="reload" type="button">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:20px;font-size:12px">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>',selector:"body"},loading:{content:'<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h2>Loading...</h2><p data-spa-guard-section="retrying" style="display:none">Retry attempt <span data-spa-guard-content="attempt"></span></p></div></div>'}},lazyRetry:{callReloadOnFailure:!0,retryDelays:[1e3,2e3]},minTimeBetweenResets:5e3,reloadDelays:[1e3,2e3,5e3],useRetryId:!0},p=()=>{let e=globalThis.window?.__SPA_GUARD_OPTIONS__;return{...m,...e,checkVersion:{...m.checkVersion,...e?.checkVersion},errors:{...m.errors,...e?.errors},handleUnhandledRejections:{...m.handleUnhandledRejections,...e?.handleUnhandledRejections},html:{fallback:{...m.html?.fallback,...e?.html?.fallback},loading:{...m.html?.loading,...e?.html?.loading}},lazyRetry:{...m.lazyRetry,...e?.lazyRetry},reportBeacon:{...m.reportBeacon,...e?.reportBeacon}}},g="__spa_guard_last_reload_timestamp__",h="__spa_guard_last_retry_reset__",f=null,w=null,b=()=>{try{return void 0!==globalThis.window&&typeof sessionStorage<"u"}catch{return!1}},v=()=>{if(b())try{let e=sessionStorage.getItem(g);if(e)return JSON.parse(e)}catch{return f}return f},k=()=>{try{let e=new URLSearchParams(globalThis.window.location.search),t=e.get(o),r=e.get(l);if(t&&r){let e=parseInt(r,10);return Number.isNaN(e)?null:{retryAttempt:e,retryId:t}}return null}catch{return null}},R=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(o),e.searchParams.delete(l),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},S=()=>{if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint32Array(2);return crypto.getRandomValues(e),`${Date.now()}-${e[0].toString(36)}${e[1].toString(36)}`}return`${Date.now()}-${Math.random().toString(36).slice(2,15)}`},$=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(l),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},I=()=>{let e=k();return e?{retryAttempt:e.retryAttempt,retryId:e.retryId}:{}},T="__SPA_GUARD_FORCE_RETRY__",N=e=>{let t=(p().errors?.ignore??[]).filter(e=>""!==e);return 0!==t.length&&e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},E=e=>{let t=[...p().errors?.forceRetry??[],T].filter(e=>""!==e);return e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},U=e=>{if((e=>N([e.errorMessage,e.eventMessage]))(e))return;let t=p();if(!t.reportBeacon?.endpoint)return void d()?.noBeaconEndpoint();let r=t.appName?{...e,appName:t.appName}:e,a=JSON.stringify(r);"function"==typeof globalThis.window?.navigator?.sendBeacon&&globalThis.window.navigator.sendBeacon(t.reportBeacon.endpoint,a)||fetch(t.reportBeacon.endpoint,{body:a,keepalive:!0,method:"POST"}).catch(e=>{d()?.beaconSendFailed(e)})},A=!1,_=e=>{if(A)return void d()?.reloadAlreadyScheduled(e);let t,r=p(),a=r.reloadDelays??[1e3,2e3,5e3],n=r.useRetryId??!0,i=r.enableRetryReset??!0,y=r.minTimeBetweenResets??5e3;if(n)t=k();else{let e=(()=>{try{let e=new URLSearchParams(globalThis.window.location.search).get(l);if(e){let t=parseInt(e,10);return Number.isNaN(t)?null:t}return null}catch{return null}})();t=null===e?null:{retryAttempt:e,retryId:S()}}let u=t?t.retryAttempt:0,m=t?.retryId??S();d()?.retryCycleStarting(m,u);let I=s.defaultRetryEnabled;if(c({error:e,isRetrying:I&&u>=0&&u<a.length,name:"chunk-error"}),!I)return;if(i&&t&&t.retryAttempt>0&&((e,t,r=5e3)=>{if(0===e.retryAttempt)return!1;let a=v();if(!a||a.retryId!==e.retryId)return!1;let n=(()=>{if(b())try{let e=sessionStorage.getItem(h);if(e)return JSON.parse(e)}catch{return w}return w})();return!(n&&Date.now()-n.timestamp<r)&&Date.now()-a.timestamp>(t[a.attemptNumber-1]??1e3)+3e4})(t,a,y)){let r=v(),a=r?Date.now()-r.timestamp:0;R(),(()=>{if(b())try{sessionStorage.removeItem(g)}catch{}f=null})(),(e=>{let t={previousRetryId:e,timestamp:Date.now()};if(b())try{sessionStorage.setItem(h,JSON.stringify(t))}catch{w=t}else w=t})(t.retryId);let n=e+"";c({name:"retry-reset",previousAttempt:t.retryAttempt,previousRetryId:t.retryId,timeSinceReload:a},{silent:N([n])}),u=0,m=S()}if(-1===u)return N([e+""])||d()?.fallbackAlreadyShown(e),void D();if(u>=a.length){let r=e+"";return c({finalAttempt:u,name:"retry-exhausted",retryId:t?.retryId??""},{silent:N([r])}),U({errorMessage:"Exceeded maximum reload attempts",eventName:"chunk_error_max_reloads",retryAttempt:u,retryId:t?.retryId,serialized:JSON.stringify({error:e+"",retryAttempt:u,retryId:t?.retryId})}),n||$(),void D()}let T=u+1,E=a[u]??1e3;c({attempt:T,delay:E,name:"retry-attempt",retryId:m},{silent:N([e+""])}),A=!0,d()?.retrySchedulingReload(m,T,E),setTimeout(()=>{if(n&&i&&((e,t)=>{let r={attemptNumber:t,retryId:e,timestamp:Date.now()};if(b())try{sessionStorage.setItem(g,JSON.stringify(r))}catch{f=r}else f=r})(m,T),n){let e=((e,t)=>{let r=new URL(globalThis.window.location.href);return r.searchParams.set(o,e),r.searchParams.set(l,t+""),r.toString()})(m,T);globalThis.window.location.href=e}else globalThis.window.location.href=(e=>{let t=new URL(globalThis.window.location.href);return t.searchParams.set(l,e+""),t.toString()})(T)},E)},D=()=>{let e=p(),t=e.html?.fallback?.content,r=e.html?.fallback?.selector??"body";if(t)try{let a=document.querySelector(r);if(!a)return void d()?.fallbackTargetNotFound(r);a.innerHTML=t;let n=e.useRetryId??!0,o=k();o&&-1===o.retryAttempt?(d()?.clearingRetryState(),R()):!n&&!o&&$();let l=a.querySelector('[data-spa-guard-action="reload"]');if(l&&l.addEventListener("click",()=>location.reload()),o){let e=document.getElementsByClassName("spa-guard-retry-id");for(let t of e)t.textContent=o.retryId}c({name:"fallback-ui-shown"})}catch(e){d()?.fallbackInjectFailed(e)}else d()?.noFallbackConfigured()},z="[spa-guard]",P={"chunk-error":"error","fallback-ui-shown":"warn","lazy-retry-attempt":"warn","lazy-retry-exhausted":"error","lazy-retry-success":"log","retry-attempt":"warn","retry-exhausted":"error","retry-reset":"log"},x=e=>{if(null==e)return{type:"null",value:e};if("object"!=typeof e)return{type:typeof e,value:e};if(e instanceof Error)return{message:e.message,name:e.name,stack:e.stack,type:"Error",...C(e)};if("reason"in e&&"promise"in e)return{reason:x(e.reason),type:"PromiseRejectionEvent"};if("error"in e&&"message"in e&&"filename"in e)return{colno:e.colno,error:x(e.error),filename:e.filename,lineno:e.lineno,message:e.message,type:"ErrorEvent"};if("violatedDirective"in e&&"blockedURI"in e){let t=e;return{blockedURI:t.blockedURI,columnNumber:t.columnNumber,effectiveDirective:t.effectiveDirective,lineNumber:t.lineNumber,originalPolicy:t.originalPolicy,sourceFile:t.sourceFile,type:"SecurityPolicyViolationEvent",violatedDirective:t.violatedDirective}}if("type"in e&&"target"in e){let t=e;return{eventType:t.type,target:j(t.target),timeStamp:t.timeStamp,type:"Event"}}return{type:"object",value:L(e)}},C=e=>{let t={};for(let r of Object.getOwnPropertyNames(e))if(!["message","name","stack"].includes(r))try{t[r]=e[r]}catch{}return t},j=e=>e?e instanceof HTMLElement?{className:e.className,href:e.href,id:e.id,src:e.src,tagName:e.tagName}:{type:e+""}:null,L=e=>{let t={};for(let r of Object.keys(e))try{let a=e[r];t[r]="object"==typeof a?a+"":a}catch{}return t};((e,t)=>{if(t&&(e=>{void 0!==globalThis.window&&(globalThis.window[n]=e)})(t),s.initialized)return;s.initialized=!0,void 0!==globalThis.window&&(globalThis.window[a]=!0);let r=p(),i=r.reloadDelays??[],c=k();c&&c.retryAttempt>=i.length&&(d()?.retryLimitExceeded(c.retryAttempt,i.length),(e=>{try{let t=new URL(globalThis.window.location.href);t.searchParams.set(o,e),t.searchParams.set(l,"-1"),globalThis.window.history.replaceState(null,"",t.toString())}catch{}})(c.retryId));let u=globalThis.window.addEventListener.bind(globalThis.window);u("error",t=>{if(N([t.message]))return;if(d()?.capturedError("error",t),y(t))return t.preventDefault(),void _(t.error??t);if(E([t.message]))return t.preventDefault(),void _(t.error??t);let r=e(t);U({errorMessage:t.message,eventName:"error",serialized:r,...I()})},!0),u("unhandledrejection",t=>{let a=t.reason+"";if(N([a]))return;if(d()?.capturedError("unhandledrejection",t),y(t.reason))return t.preventDefault(),void _(t.reason);if(E([a]))return t.preventDefault(),void _(t.reason);let n=r.handleUnhandledRejections;if(!1!==n?.sendBeacon){let r=e(t);U({errorMessage:a,eventName:"unhandledrejection",serialized:r,...I()})}!1!==n?.retry&&(t.preventDefault(),_(t.reason))}),u("securitypolicyviolation",t=>{let r=`${t.violatedDirective}: ${t.blockedURI}`;if(N([r]))return;d()?.capturedError("csp",t.blockedURI,t.violatedDirective);let a=e(t);U({eventMessage:r,eventName:"securitypolicyviolation",serialized:a,...I()})}),u("vite:preloadError",e=>{N([e?.payload?.message||e?.message])||(d()?.capturedError("vite:preloadError",e),e.preventDefault(),_(e?.payload??e))})})(e=>{try{let t=x(e);return JSON.stringify(t,null,2)}catch{return JSON.stringify({error:"Failed to serialize error",fallback:e+""})}},{beaconSendFailed(e){console.error(z+" Failed to send beacon:",e)},capturedError(e,...t){console.error(`${z} ${e}:capture:`,...t)},clearingRetryState(){console.log(z+" Clearing retry state from URL to allow clean reload attempt")},error(e,...t){console.error(`${z} ${e}`,...t)},fallbackAlreadyShown(e){console.error(z+" Fallback UI was already shown. Not retrying to prevent infinite loop.",e)},fallbackInjectFailed(e){console.error(z+" Failed to inject fallback UI",e)},fallbackTargetNotFound(e){console.error(`${z} Target element not found for selector: ${e}`)},log(e,...t){console.log(`${z} ${e}`,...t)},logEvent(e){let t=P[e.name],r=(e=>{switch(e.name){case"chunk-error":return`${z} chunk-error: isRetrying=${e.isRetrying}`;case"fallback-ui-shown":return z+" fallback-ui-shown";case"lazy-retry-attempt":return`${z} lazy-retry-attempt: attempt ${e.attempt}/${e.totalAttempts}, delay ${e.delay}ms`;case"lazy-retry-exhausted":return`${z} lazy-retry-exhausted: ${e.totalAttempts} attempts, willReload=${e.willReload}`;case"lazy-retry-success":return`${z} lazy-retry-success: succeeded on attempt ${e.attempt}`;case"retry-attempt":return`${z} retry-attempt: attempt ${e.attempt} in ${e.delay}ms (retryId: ${e.retryId})`;case"retry-exhausted":return`${z} retry-exhausted: finalAttempt=${e.finalAttempt} (retryId: ${e.retryId})`;case"retry-reset":return`${z} retry-reset: ${e.timeSinceReload}ms since last reload (retryId: ${e.previousRetryId})`}})(e);"chunk-error"===e.name?console[t](r,e.error):console[t](r)},noBeaconEndpoint(){console.warn(z+" Report endpoint is not configured")},noFallbackConfigured(){console.error(z+" No fallback UI configured")},reloadAlreadyScheduled(e){console.log(z+" Reload already scheduled, ignoring duplicate chunk error:",e)},retryCycleStarting(e,t){console.log(`${z} Retry cycle starting: retryId=${e}, fromAttempt=${t}`)},retryLimitExceeded(e,t){console.log(`${z} Retry limit exceeded (${e}/${t}), marking as fallback shown`)},retrySchedulingReload(e,t,r){console.log(`${z} Scheduling reload: retryId=${e}, attempt=${t}, delay=${r}ms`)},updatedRetryAttempt(e){console.log(`${z} Updated retry attempt to ${e} in URL for fallback UI`)},versionChangeDetected(e,t){console.warn(`${z} New version available (${e??"unknown"} → ${t}). Please refresh to get the latest version.`)},versionCheckAlreadyRunning(){console.warn(z+" Version check already running")},versionCheckDisabled(){console.warn(z+" Version checking disabled: no version configured")},versionCheckFailed(e){console.error(z+" Version check failed",e)},versionCheckHttpError(e){console.warn(`${z} Version check HTTP error: ${e}`)},versionCheckParseError(){console.warn(z+" Failed to parse version from HTML")},versionCheckPaused(){console.log(z+" Version check paused (tab hidden)")},versionCheckRequiresEndpoint(){console.warn(z+" JSON version check mode requires endpoint")},versionCheckResumed(){console.log(z+" Version check resumed (tab visible)")},versionCheckResumedImmediate(){console.log(z+" Version check resumed with immediate check (tab visible, interval elapsed)")},versionCheckStarted(e,t,r){console.log(`${z} Starting version check (mode: ${e}, interval: ${t}ms, current: ${r})`)},versionCheckStopped(){console.log(z+" Version check stopped")},warn(e,...t){console.warn(`${z} ${e}`,...t)}});
|
|
1
|
+
var e="@ovineko/spa-guard",t=Symbol.for(e+":event-subscribers"),r=Symbol.for(e+":internal-config"),a=Symbol.for(e+":initialized"),n=Symbol.for(e+":logger"),o="spaGuardRetryId",l="spaGuardRetryAttempt";globalThis.window&&!globalThis.window[t]&&(globalThis.window[t]=new Set),globalThis.window&&!globalThis.window[r]&&(globalThis.window[r]={defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1});var i=globalThis.window?.[t]??new Set,s=globalThis.window?.[r]??{defaultRetryEnabled:!0,initialized:!1,inlineScriptLoaded:!1},d=()=>globalThis.window?.[n],c=(e,t)=>{t?.silent||d()?.logEvent(e),i.forEach(t=>{try{t(e)}catch{}})},y=e=>{let t=u(e);return!!t&&[/Failed to fetch dynamically imported module/i,/Importing a module script failed/i,/error loading dynamically imported module/i,/Unable to preload CSS/i,/Loading chunk \d+ failed/i,/Loading CSS chunk \d+ failed/i,/ChunkLoadError/i].some(e=>e.test(t))},u=e=>e instanceof Error?e.message:"string"==typeof e?e:e&&"object"==typeof e&&"message"in e?e.message+"":e&&"object"==typeof e&&"reason"in e?u(e.reason):null,m={checkVersion:{interval:3e5,mode:"html",onUpdate:"reload"},enableRetryReset:!0,errors:{forceRetry:[],ignore:[]},handleUnhandledRejections:{retry:!0,sendBeacon:!0},html:{fallback:{content:'<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:monospace}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h1 data-spa-guard-content="heading">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:1rem auto">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center"><button data-spa-guard-action="try-again" type="button" style="display:none">Try again</button> <button data-spa-guard-action="reload" type="button">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:20px;font-size:12px">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>',selector:"body"},loading:{content:'<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem"><div style="text-align:center"><h2>Loading...</h2><p data-spa-guard-section="retrying" style="display:none">Retry attempt <span data-spa-guard-content="attempt"></span></p></div></div>'}},lazyRetry:{callReloadOnFailure:!0,retryDelays:[1e3,2e3]},minTimeBetweenResets:5e3,reloadDelays:[1e3,2e3,5e3],useRetryId:!0},p=()=>{let e=globalThis.window?.__SPA_GUARD_OPTIONS__;return{...m,...e,checkVersion:{...m.checkVersion,...e?.checkVersion},errors:{...m.errors,...e?.errors},handleUnhandledRejections:{...m.handleUnhandledRejections,...e?.handleUnhandledRejections},html:{fallback:{...m.html?.fallback,...e?.html?.fallback},loading:{...m.html?.loading,...e?.html?.loading}},lazyRetry:{...m.lazyRetry,...e?.lazyRetry},reportBeacon:{...m.reportBeacon,...e?.reportBeacon}}},g="__spa_guard_last_reload_timestamp__",h="__spa_guard_last_retry_reset__",f=null,w=null,b=()=>{try{return void 0!==globalThis.window&&typeof sessionStorage<"u"}catch{return!1}},v=()=>{if(b())try{let e=sessionStorage.getItem(g);if(e)return JSON.parse(e)}catch{return f}return f},k=()=>{try{let e=new URLSearchParams(globalThis.window.location.search),t=e.get(o),r=e.get(l);if(t&&r){let e=parseInt(r,10);return Number.isNaN(e)?null:{retryAttempt:e,retryId:t}}return null}catch{return null}},R=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(o),e.searchParams.delete(l),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},S=()=>{if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint32Array(2);return crypto.getRandomValues(e),`${Date.now()}-${e[0].toString(36)}${e[1].toString(36)}`}return`${Date.now()}-${Math.random().toString(36).slice(2,15)}`},$=()=>{try{let e=new URL(globalThis.window.location.href);e.searchParams.delete(l),globalThis.window.history.replaceState(null,"",e.toString())}catch{}},I=()=>{let e=k();return e?{retryAttempt:e.retryAttempt,retryId:e.retryId}:{}},T="__SPA_GUARD_FORCE_RETRY__",N=e=>{let t=(p().errors?.ignore??[]).filter(e=>""!==e);return 0!==t.length&&e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},E=e=>{let t=[...p().errors?.forceRetry??[],T].filter(e=>""!==e);return e.filter(e=>"string"==typeof e).some(e=>t.some(t=>e.includes(t)))},U=e=>{if((e=>N([e.errorMessage,e.eventMessage]))(e))return;let t=p();if(!t.reportBeacon?.endpoint)return void d()?.noBeaconEndpoint();let r=t.appName?{...e,appName:t.appName}:e,a=JSON.stringify(r);("function"!=typeof globalThis.window?.navigator?.sendBeacon||!globalThis.window.navigator.sendBeacon(t.reportBeacon.endpoint,a))&&"function"==typeof fetch&&fetch(t.reportBeacon.endpoint,{body:a,keepalive:!0,method:"POST"}).catch(e=>{d()?.beaconSendFailed(e)})},A=!1,_=e=>{if(A)d()?.reloadAlreadyScheduled(e);else{A=!0;try{let t,r=p(),a=r.reloadDelays??[1e3,2e3,5e3],n=r.useRetryId??!0,i=r.enableRetryReset??!0,y=r.minTimeBetweenResets??5e3;if(n)t=k();else{let e=(()=>{try{let e=new URLSearchParams(globalThis.window.location.search).get(l);if(e){let t=parseInt(e,10);return Number.isNaN(t)?null:t}return null}catch{return null}})();t=null===e?null:{retryAttempt:e,retryId:S()}}let u=t?t.retryAttempt:0,m=t?.retryId??S();d()?.retryCycleStarting(m,u);let I=s.defaultRetryEnabled;if(c({error:e,isRetrying:I&&u>=0&&u<a.length,name:"chunk-error"}),!I)return void(A=!1);if(i&&t&&t.retryAttempt>0&&((e,t,r=5e3)=>{if(0===e.retryAttempt)return!1;let a=v();if(!a||a.retryId!==e.retryId)return!1;let n=(()=>{if(b())try{let e=sessionStorage.getItem(h);if(e)return JSON.parse(e)}catch{return w}return w})();return!(n&&Date.now()-n.timestamp<r)&&Date.now()-a.timestamp>(t[a.attemptNumber-1]??1e3)+3e4})(t,a,y)){let r=v(),a=r?Date.now()-r.timestamp:0;R(),(()=>{if(b())try{sessionStorage.removeItem(g)}catch{}f=null})(),(e=>{let t={previousRetryId:e,timestamp:Date.now()};if(b())try{sessionStorage.setItem(h,JSON.stringify(t))}catch{w=t}else w=t})(t.retryId);let n=e+"";c({name:"retry-reset",previousAttempt:t.retryAttempt,previousRetryId:t.retryId,timeSinceReload:a},{silent:N([n])}),u=0,m=S()}if(-1===u)return N([e+""])||d()?.fallbackAlreadyShown(e),A=!1,void D();if(u>=a.length){let r=e+"";return c({finalAttempt:u,name:"retry-exhausted",retryId:t?.retryId??""},{silent:N([r])}),U({errorMessage:"Exceeded maximum reload attempts",eventName:"chunk_error_max_reloads",retryAttempt:u,retryId:t?.retryId,serialized:JSON.stringify({error:e+"",retryAttempt:u,retryId:t?.retryId})}),n||$(),A=!1,void D()}let T=u+1,E=a[u]??1e3;c({attempt:T,delay:E,name:"retry-attempt",retryId:m},{silent:N([e+""])}),d()?.retrySchedulingReload(m,T,E),setTimeout(()=>{if(n&&i&&((e,t)=>{let r={attemptNumber:t,retryId:e,timestamp:Date.now()};if(b())try{sessionStorage.setItem(g,JSON.stringify(r))}catch{f=r}else f=r})(m,T),n){let e=((e,t)=>{let r=new URL(globalThis.window.location.href);return r.searchParams.set(o,e),r.searchParams.set(l,t+""),r.toString()})(m,T);globalThis.window.location.href=e}else globalThis.window.location.href=(e=>{let t=new URL(globalThis.window.location.href);return t.searchParams.set(l,e+""),t.toString()})(T)},E)}catch{A=!1}}},D=()=>{let e=p(),t=e.html?.fallback?.content,r=e.html?.fallback?.selector??"body";if(t)try{let a=document.querySelector(r);if(!a)return void d()?.fallbackTargetNotFound(r);a.innerHTML=t;let n=e.useRetryId??!0,o=k();o&&-1===o.retryAttempt?(d()?.clearingRetryState(),R()):!n&&!o&&$();let l=a.querySelector('[data-spa-guard-action="reload"]');if(l&&l.addEventListener("click",()=>location.reload()),o){let e=document.getElementsByClassName("spa-guard-retry-id");for(let t of e)t.textContent=o.retryId}c({name:"fallback-ui-shown"})}catch(e){d()?.fallbackInjectFailed(e)}else d()?.noFallbackConfigured()},z="[spa-guard]",P={"chunk-error":"error","fallback-ui-shown":"warn","lazy-retry-attempt":"warn","lazy-retry-exhausted":"error","lazy-retry-success":"log","retry-attempt":"warn","retry-exhausted":"error","retry-reset":"log"},x=e=>{if(null==e)return{type:"null",value:e};if("object"!=typeof e)return{type:typeof e,value:e};if(e instanceof Error)return{message:e.message,name:e.name,stack:e.stack,type:"Error",...C(e)};if("reason"in e&&"promise"in e)return{reason:x(e.reason),type:"PromiseRejectionEvent"};if("error"in e&&"message"in e&&"filename"in e)return{colno:e.colno,error:x(e.error),filename:e.filename,lineno:e.lineno,message:e.message,type:"ErrorEvent"};if("violatedDirective"in e&&"blockedURI"in e){let t=e;return{blockedURI:t.blockedURI,columnNumber:t.columnNumber,effectiveDirective:t.effectiveDirective,lineNumber:t.lineNumber,originalPolicy:t.originalPolicy,sourceFile:t.sourceFile,type:"SecurityPolicyViolationEvent",violatedDirective:t.violatedDirective}}if("type"in e&&"target"in e){let t=e;return{eventType:t.type,target:j(t.target),timeStamp:t.timeStamp,type:"Event"}}return{type:"object",value:L(e)}},C=e=>{let t={};for(let r of Object.getOwnPropertyNames(e))if(!["message","name","stack"].includes(r))try{t[r]=e[r]}catch{}return t},j=e=>e?e instanceof HTMLElement?{className:e.className,href:e.href,id:e.id,src:e.src,tagName:e.tagName}:{type:e+""}:null,L=e=>{let t={};for(let r of Object.keys(e))try{let a=e[r];t[r]="object"==typeof a?a+"":a}catch{}return t};((e,t)=>{if(t&&(e=>{void 0!==globalThis.window&&(globalThis.window[n]=e)})(t),s.initialized)return;s.initialized=!0,void 0!==globalThis.window&&(globalThis.window[a]=!0);let r=p(),i=r.reloadDelays??[],c=k();c&&c.retryAttempt>=i.length&&(d()?.retryLimitExceeded(c.retryAttempt,i.length),(e=>{try{let t=new URL(globalThis.window.location.href);t.searchParams.set(o,e),t.searchParams.set(l,"-1"),globalThis.window.history.replaceState(null,"",t.toString())}catch{}})(c.retryId));let u=globalThis.window.addEventListener.bind(globalThis.window);u("error",t=>{if(N([t.message]))return;if(d()?.capturedError("error",t),y(t))return t.preventDefault(),void _(t.error??t);if(E([t.message]))return t.preventDefault(),void _(t.error??t);let r=e(t);U({errorMessage:t.message,eventName:"error",serialized:r,...I()})},!0),u("unhandledrejection",t=>{let a=t.reason+"";if(N([a]))return;if(d()?.capturedError("unhandledrejection",t),y(t.reason))return t.preventDefault(),void _(t.reason);if(E([a]))return t.preventDefault(),void _(t.reason);let n=r.handleUnhandledRejections;if(!1!==n?.sendBeacon){let r=e(t);U({errorMessage:a,eventName:"unhandledrejection",serialized:r,...I()})}!1!==n?.retry&&(t.preventDefault(),_(t.reason))}),u("securitypolicyviolation",t=>{let r=`${t.violatedDirective}: ${t.blockedURI}`;if(N([r]))return;d()?.capturedError("csp",t.blockedURI,t.violatedDirective);let a=e(t);U({eventMessage:r,eventName:"securitypolicyviolation",serialized:a,...I()})}),u("vite:preloadError",e=>{N([e?.payload?.message||e?.message])||(d()?.capturedError("vite:preloadError",e),e.preventDefault(),_(e?.payload??e))})})(e=>{try{let t=x(e);return JSON.stringify(t,null,2)}catch{return JSON.stringify({error:"Failed to serialize error",fallback:e+""})}},{beaconSendFailed(e){console.error(z+" Failed to send beacon:",e)},capturedError(e,...t){console.error(`${z} ${e}:capture:`,...t)},clearingRetryState(){console.log(z+" Clearing retry state from URL to allow clean reload attempt")},error(e,...t){console.error(`${z} ${e}`,...t)},fallbackAlreadyShown(e){console.error(z+" Fallback UI was already shown. Not retrying to prevent infinite loop.",e)},fallbackInjectFailed(e){console.error(z+" Failed to inject fallback UI",e)},fallbackTargetNotFound(e){console.error(`${z} Target element not found for selector: ${e}`)},log(e,...t){console.log(`${z} ${e}`,...t)},logEvent(e){let t=P[e.name],r=(e=>{switch(e.name){case"chunk-error":return`${z} chunk-error: isRetrying=${e.isRetrying}`;case"fallback-ui-shown":return z+" fallback-ui-shown";case"lazy-retry-attempt":return`${z} lazy-retry-attempt: attempt ${e.attempt}/${e.totalAttempts}, delay ${e.delay}ms`;case"lazy-retry-exhausted":return`${z} lazy-retry-exhausted: ${e.totalAttempts} attempts, willReload=${e.willReload}`;case"lazy-retry-success":return`${z} lazy-retry-success: succeeded on attempt ${e.attempt}`;case"retry-attempt":return`${z} retry-attempt: attempt ${e.attempt} in ${e.delay}ms (retryId: ${e.retryId})`;case"retry-exhausted":return`${z} retry-exhausted: finalAttempt=${e.finalAttempt} (retryId: ${e.retryId})`;case"retry-reset":return`${z} retry-reset: ${e.timeSinceReload}ms since last reload (retryId: ${e.previousRetryId})`}})(e);"chunk-error"===e.name?console[t](r,e.error):console[t](r)},noBeaconEndpoint(){console.warn(z+" Report endpoint is not configured")},noFallbackConfigured(){console.error(z+" No fallback UI configured")},reloadAlreadyScheduled(e){console.log(z+" Reload already scheduled, ignoring duplicate chunk error:",e)},retryCycleStarting(e,t){console.log(`${z} Retry cycle starting: retryId=${e}, fromAttempt=${t}`)},retryLimitExceeded(e,t){console.log(`${z} Retry limit exceeded (${e}/${t}), marking as fallback shown`)},retrySchedulingReload(e,t,r){console.log(`${z} Scheduling reload: retryId=${e}, attempt=${t}, delay=${r}ms`)},updatedRetryAttempt(e){console.log(`${z} Updated retry attempt to ${e} in URL for fallback UI`)},versionChangeDetected(e,t){console.warn(`${z} New version available (${e??"unknown"} → ${t}). Please refresh to get the latest version.`)},versionCheckAlreadyRunning(){console.warn(z+" Version check already running")},versionCheckDisabled(){console.warn(z+" Version checking disabled: no version configured")},versionCheckFailed(e){console.error(z+" Version check failed",e)},versionCheckHttpError(e){console.warn(`${z} Version check HTTP error: ${e}`)},versionCheckParseError(){console.warn(z+" Failed to parse version from HTML")},versionCheckPaused(){console.log(z+" Version check paused (tab hidden)")},versionCheckRequiresEndpoint(){console.warn(z+" JSON version check mode requires endpoint")},versionCheckResumed(){console.log(z+" Version check resumed (tab visible)")},versionCheckResumedImmediate(){console.log(z+" Version check resumed with immediate check (tab visible, interval elapsed)")},versionCheckStarted(e,t,r){console.log(`${z} Starting version check (mode: ${e}, interval: ${t}ms, current: ${r})`)},versionCheckStopped(){console.log(z+" Version check stopped")},warn(e,...t){console.warn(`${z} ${e}`,...t)}});
|
package/package.json
CHANGED
package/dist/chunk-OQGJLNZ2.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// src/common/errors/ForceRetryError.ts
|
|
2
|
-
var FORCE_RETRY_MAGIC = "__SPA_GUARD_FORCE_RETRY__";
|
|
3
|
-
var ForceRetryError = class extends Error {
|
|
4
|
-
constructor(message, options) {
|
|
5
|
-
super(`${FORCE_RETRY_MAGIC}${message ?? ""}`, options);
|
|
6
|
-
this.name = "ForceRetryError";
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
FORCE_RETRY_MAGIC,
|
|
12
|
-
ForceRetryError
|
|
13
|
-
};
|