@ovineko/spa-guard 0.0.1-alpha-22 → 0.0.1-alpha-24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/_internal.js CHANGED
@@ -1,25 +1,14 @@
1
- import {
2
- attemptReload,
3
- createLogger,
4
- isChunkError,
5
- listenInternal,
6
- sendBeacon,
7
- serializeError,
8
- shouldForceRetry,
9
- shouldIgnoreMessages
10
- } from "./chunk-4FKSMB5F.js";
11
1
  import {
12
2
  SPINNER_ID,
13
3
  defaultSpinnerSvg,
14
4
  extractVersionFromHtml
15
- } from "./chunk-3ISQYZVD.js";
5
+ } from "./chunk-UF7QAEI7.js";
16
6
  import {
17
- applyI18n,
18
- defaultErrorFallbackHtml,
19
- defaultLoadingFallbackHtml,
20
- getI18n,
21
- getOptions
22
- } from "./chunk-DOTM7FSY.js";
7
+ createLogger,
8
+ isChunkError,
9
+ listenInternal,
10
+ serializeError
11
+ } from "./chunk-DUR4QNQE.js";
23
12
  import {
24
13
  dispatchAsyncRuntimeError,
25
14
  dispatchChunkLoadError,
@@ -28,17 +17,28 @@ import {
28
17
  dispatchNetworkTimeout,
29
18
  dispatchSyncRuntimeError,
30
19
  dispatchUnhandledRejection
31
- } from "./chunk-74K44EMP.js";
20
+ } from "./chunk-H4DWKO5I.js";
32
21
  import {
22
+ attemptReload,
23
+ sendBeacon,
24
+ shouldForceRetry,
25
+ shouldIgnoreMessages
26
+ } from "./chunk-VZCUDNEG.js";
27
+ import {
28
+ applyI18n,
33
29
  debugSyncErrorEventType,
30
+ defaultErrorFallbackHtml,
31
+ defaultLoadingFallbackHtml,
34
32
  disableDefaultRetry,
35
33
  emitEvent,
36
34
  enableDefaultRetry,
35
+ getI18n,
36
+ getOptions,
37
37
  getRetryInfoForBeacon,
38
38
  isDefaultRetryEnabled,
39
39
  optionsWindowKey,
40
40
  subscribe
41
- } from "./chunk-YSKH5K6P.js";
41
+ } from "./chunk-5DE7AKYB.js";
42
42
  import "./chunk-MLKGABMK.js";
43
43
 
44
44
  // src/common/handleErrorWithSpaGuard.ts
@@ -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
 
@@ -68,6 +72,151 @@ var isDefaultRetryEnabled = () => {
68
72
  return internalConfig.defaultRetryEnabled;
69
73
  };
70
74
 
75
+ // src/common/i18n.ts
76
+ function applyI18n(container, t) {
77
+ const contentEls = container.querySelectorAll("[data-spa-guard-content]");
78
+ for (const el of contentEls) {
79
+ const key = el.dataset.spaGuardContent;
80
+ if (key && key in t) {
81
+ const value = t[key];
82
+ if (typeof value === "string") {
83
+ el.textContent = value;
84
+ }
85
+ }
86
+ }
87
+ const actionEls = container.querySelectorAll("[data-spa-guard-action]");
88
+ for (const el of actionEls) {
89
+ const action = el.dataset.spaGuardAction;
90
+ const tKey = action === "try-again" ? "tryAgain" : action;
91
+ if (tKey && tKey in t) {
92
+ const value = t[tKey];
93
+ if (typeof value === "string") {
94
+ el.textContent = value;
95
+ }
96
+ }
97
+ }
98
+ if (t.rtl) {
99
+ for (const child of container.children) {
100
+ if (child instanceof HTMLElement && child.tagName !== "STYLE") {
101
+ child.style.direction = "rtl";
102
+ }
103
+ }
104
+ }
105
+ }
106
+ function getI18n() {
107
+ try {
108
+ const el = document.querySelector('meta[name="spa-guard-i18n"]');
109
+ if (!el) {
110
+ return null;
111
+ }
112
+ const content = el.getAttribute("content");
113
+ if (!content) {
114
+ return null;
115
+ }
116
+ return JSON.parse(content);
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+ function setTranslations(translations) {
122
+ let el = document.querySelector('meta[name="spa-guard-i18n"]');
123
+ if (!el) {
124
+ el = document.createElement("meta");
125
+ el.setAttribute("name", "spa-guard-i18n");
126
+ document.head.append(el);
127
+ }
128
+ el.setAttribute("content", JSON.stringify(translations));
129
+ }
130
+
131
+ // src/common/html.generated.ts
132
+ 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>`;
133
+ 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>`;
134
+ 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>`;
135
+
136
+ // src/common/options.ts
137
+ var options_exports = {};
138
+ __export(options_exports, {
139
+ getOptions: () => getOptions,
140
+ optionsWindowKey: () => optionsWindowKey
141
+ });
142
+ var defaultOptions = {
143
+ checkVersion: {
144
+ interval: 3e5,
145
+ mode: "html",
146
+ onUpdate: "reload"
147
+ },
148
+ enableRetryReset: true,
149
+ errors: {
150
+ forceRetry: [],
151
+ ignore: []
152
+ },
153
+ handleUnhandledRejections: {
154
+ retry: true,
155
+ sendBeacon: true
156
+ },
157
+ html: {
158
+ fallback: {
159
+ content: defaultErrorFallbackHtml,
160
+ selector: "body"
161
+ },
162
+ loading: {
163
+ content: defaultLoadingFallbackHtml
164
+ }
165
+ },
166
+ lazyRetry: {
167
+ callReloadOnFailure: true,
168
+ retryDelays: [1e3, 2e3]
169
+ },
170
+ minTimeBetweenResets: 5e3,
171
+ reloadDelays: [1e3, 2e3, 5e3],
172
+ spinner: {
173
+ background: "#fff",
174
+ disabled: false
175
+ },
176
+ useRetryId: true
177
+ };
178
+ var getOptions = () => {
179
+ const windowOptions = globalThis.window?.[optionsWindowKey];
180
+ return {
181
+ ...defaultOptions,
182
+ ...windowOptions,
183
+ checkVersion: {
184
+ ...defaultOptions.checkVersion,
185
+ ...windowOptions?.checkVersion
186
+ },
187
+ errors: {
188
+ ...defaultOptions.errors,
189
+ ...windowOptions?.errors
190
+ },
191
+ handleUnhandledRejections: {
192
+ ...defaultOptions.handleUnhandledRejections,
193
+ ...windowOptions?.handleUnhandledRejections
194
+ },
195
+ html: {
196
+ fallback: {
197
+ ...defaultOptions.html?.fallback,
198
+ ...windowOptions?.html?.fallback
199
+ },
200
+ loading: {
201
+ ...defaultOptions.html?.loading,
202
+ ...windowOptions?.html?.loading
203
+ }
204
+ },
205
+ lazyRetry: {
206
+ ...defaultOptions.lazyRetry,
207
+ ...windowOptions?.lazyRetry
208
+ },
209
+ reportBeacon: {
210
+ ...defaultOptions.reportBeacon,
211
+ ...windowOptions?.reportBeacon
212
+ },
213
+ spinner: {
214
+ ...defaultOptions.spinner,
215
+ ...windowOptions?.spinner
216
+ }
217
+ };
218
+ };
219
+
71
220
  // src/common/errors/ForceRetryError.ts
72
221
  var FORCE_RETRY_MAGIC = "__SPA_GUARD_FORCE_RETRY__";
73
222
  var ForceRetryError = class extends Error {
@@ -279,12 +428,20 @@ export {
279
428
  disableDefaultRetry,
280
429
  enableDefaultRetry,
281
430
  isDefaultRetryEnabled,
431
+ applyI18n,
432
+ getI18n,
433
+ setTranslations,
282
434
  setLastReloadTime,
283
435
  getLastReloadTime,
284
436
  clearLastReloadTime,
285
437
  shouldResetRetryCycle,
286
438
  setLastRetryResetInfo,
287
439
  getLastRetryResetInfo,
440
+ defaultErrorFallbackHtml,
441
+ defaultLoadingFallbackHtml,
442
+ defaultSpinnerHtml,
443
+ getOptions,
444
+ options_exports,
288
445
  getRetryStateFromUrl,
289
446
  clearRetryStateFromUrl,
290
447
  updateRetryStateInUrl,
@@ -3,7 +3,7 @@ import {
3
3
  getRetryAttemptFromUrl,
4
4
  getRetryStateFromUrl,
5
5
  subscribe
6
- } from "./chunk-YSKH5K6P.js";
6
+ } from "./chunk-5DE7AKYB.js";
7
7
 
8
8
  // src/runtime/state.ts
9
9
  var getInitialStateFromUrl = () => {
@@ -1,31 +1,19 @@
1
1
  import {
2
- applyI18n,
3
- getI18n,
4
- getOptions
5
- } from "./chunk-DOTM7FSY.js";
2
+ attemptReload,
3
+ sendBeacon,
4
+ shouldForceRetry,
5
+ shouldIgnoreMessages
6
+ } from "./chunk-VZCUDNEG.js";
6
7
  import {
7
- FORCE_RETRY_MAGIC,
8
- RETRY_ATTEMPT_PARAM,
9
- RETRY_ID_PARAM,
10
- clearLastReloadTime,
11
- clearRetryAttemptFromUrl,
12
- clearRetryStateFromUrl,
13
- emitEvent,
14
- generateRetryId,
15
- getLastReloadTime,
16
8
  getLogger,
17
- getRetryAttemptFromUrl,
9
+ getOptions,
18
10
  getRetryInfoForBeacon,
19
11
  getRetryStateFromUrl,
20
- isDefaultRetryEnabled,
21
12
  isInitialized,
22
13
  markInitialized,
23
- setLastReloadTime,
24
- setLastRetryResetInfo,
25
14
  setLogger,
26
- shouldResetRetryCycle,
27
15
  updateRetryStateInUrl
28
- } from "./chunk-YSKH5K6P.js";
16
+ } from "./chunk-5DE7AKYB.js";
29
17
 
30
18
  // src/common/isChunkError.ts
31
19
  var isChunkError = (error) => {
@@ -60,272 +48,6 @@ var getErrorMessage = (error) => {
60
48
  return null;
61
49
  };
62
50
 
63
- // src/common/shouldIgnore.ts
64
- var shouldIgnoreMessages = (messages) => {
65
- const options = getOptions();
66
- const ignorePatterns = (options.errors?.ignore ?? []).filter((p) => p !== "");
67
- if (ignorePatterns.length === 0) {
68
- return false;
69
- }
70
- const validMessages = messages.filter((msg) => typeof msg === "string");
71
- return validMessages.some(
72
- (message) => ignorePatterns.some((pattern) => message.includes(pattern))
73
- );
74
- };
75
- var shouldForceRetry = (messages) => {
76
- const options = getOptions();
77
- const forceRetryPatterns = [...options.errors?.forceRetry ?? [], FORCE_RETRY_MAGIC].filter(
78
- (p) => p !== ""
79
- );
80
- const validMessages = messages.filter((msg) => typeof msg === "string");
81
- return validMessages.some(
82
- (message) => forceRetryPatterns.some((pattern) => message.includes(pattern))
83
- );
84
- };
85
- var shouldIgnoreBeacon = (beacon) => {
86
- return shouldIgnoreMessages([beacon.errorMessage, beacon.eventMessage]);
87
- };
88
-
89
- // src/common/sendBeacon.ts
90
- var sendBeacon = (beacon) => {
91
- if (shouldIgnoreBeacon(beacon)) {
92
- return;
93
- }
94
- const options = getOptions();
95
- if (!options.reportBeacon?.endpoint) {
96
- getLogger()?.noBeaconEndpoint();
97
- return;
98
- }
99
- const enrichedBeacon = options.appName ? { ...beacon, appName: options.appName } : beacon;
100
- const body = JSON.stringify(enrichedBeacon);
101
- const isSendBeaconAvailable = typeof globalThis.window?.navigator?.sendBeacon === "function";
102
- const isSentBeacon = isSendBeaconAvailable && globalThis.window.navigator.sendBeacon(options.reportBeacon.endpoint, body);
103
- if (!isSentBeacon && typeof fetch === "function") {
104
- fetch(options.reportBeacon.endpoint, { body, keepalive: true, method: "POST" }).catch(
105
- (error) => {
106
- getLogger()?.beaconSendFailed(error);
107
- }
108
- );
109
- }
110
- };
111
-
112
- // src/common/reload.ts
113
- var buildReloadUrl = (retryId, retryAttempt) => {
114
- const url = new URL(globalThis.window.location.href);
115
- url.searchParams.set(RETRY_ID_PARAM, retryId);
116
- url.searchParams.set(RETRY_ATTEMPT_PARAM, String(retryAttempt));
117
- return url.toString();
118
- };
119
- var buildReloadUrlAttemptOnly = (retryAttempt) => {
120
- const url = new URL(globalThis.window.location.href);
121
- url.searchParams.set(RETRY_ATTEMPT_PARAM, String(retryAttempt));
122
- return url.toString();
123
- };
124
- var reloadScheduled = false;
125
- var attemptReload = (error) => {
126
- if (reloadScheduled) {
127
- getLogger()?.reloadAlreadyScheduled(error);
128
- return;
129
- }
130
- reloadScheduled = true;
131
- try {
132
- const options = getOptions();
133
- const reloadDelays = options.reloadDelays ?? [1e3, 2e3, 5e3];
134
- const useRetryId = options.useRetryId ?? true;
135
- const enableRetryReset = options.enableRetryReset ?? true;
136
- const minTimeBetweenResets = options.minTimeBetweenResets ?? 5e3;
137
- let retryState;
138
- if (useRetryId) {
139
- retryState = getRetryStateFromUrl();
140
- } else {
141
- const attempt = getRetryAttemptFromUrl();
142
- retryState = attempt === null ? null : { retryAttempt: attempt, retryId: generateRetryId() };
143
- }
144
- let currentAttempt = retryState ? retryState.retryAttempt : 0;
145
- let retryId = retryState?.retryId ?? generateRetryId();
146
- getLogger()?.retryCycleStarting(retryId, currentAttempt);
147
- const retryEnabled = isDefaultRetryEnabled();
148
- emitEvent({
149
- error,
150
- isRetrying: retryEnabled && currentAttempt >= 0 && currentAttempt < reloadDelays.length,
151
- name: "chunk-error"
152
- });
153
- if (!retryEnabled) {
154
- reloadScheduled = false;
155
- return;
156
- }
157
- if (enableRetryReset && retryState && retryState.retryAttempt > 0 && shouldResetRetryCycle(retryState, reloadDelays, minTimeBetweenResets)) {
158
- const lastReload = getLastReloadTime();
159
- const timeSinceReload = lastReload ? Date.now() - lastReload.timestamp : 0;
160
- clearRetryStateFromUrl();
161
- clearLastReloadTime();
162
- setLastRetryResetInfo(retryState.retryId);
163
- const errorMsg2 = String(error);
164
- emitEvent(
165
- {
166
- name: "retry-reset",
167
- previousAttempt: retryState.retryAttempt,
168
- previousRetryId: retryState.retryId,
169
- timeSinceReload
170
- },
171
- { silent: shouldIgnoreMessages([errorMsg2]) }
172
- );
173
- currentAttempt = 0;
174
- retryId = generateRetryId();
175
- }
176
- if (currentAttempt === -1) {
177
- const errorMsg2 = String(error);
178
- if (!shouldIgnoreMessages([errorMsg2])) {
179
- getLogger()?.fallbackAlreadyShown(error);
180
- }
181
- reloadScheduled = false;
182
- showFallbackUI();
183
- return;
184
- }
185
- if (currentAttempt >= reloadDelays.length) {
186
- const errorMsg2 = String(error);
187
- emitEvent(
188
- {
189
- finalAttempt: currentAttempt,
190
- name: "retry-exhausted",
191
- retryId: retryState?.retryId ?? ""
192
- },
193
- { silent: shouldIgnoreMessages([errorMsg2]) }
194
- );
195
- sendBeacon({
196
- errorMessage: "Exceeded maximum reload attempts",
197
- eventName: "chunk_error_max_reloads",
198
- retryAttempt: currentAttempt,
199
- retryId: retryState?.retryId,
200
- serialized: JSON.stringify({
201
- error: String(error),
202
- retryAttempt: currentAttempt,
203
- retryId: retryState?.retryId
204
- })
205
- });
206
- if (!useRetryId) {
207
- clearRetryAttemptFromUrl();
208
- }
209
- reloadScheduled = false;
210
- showFallbackUI();
211
- return;
212
- }
213
- const nextAttempt = currentAttempt + 1;
214
- const delay = reloadDelays[currentAttempt] ?? 1e3;
215
- const errorMsg = String(error);
216
- emitEvent(
217
- {
218
- attempt: nextAttempt,
219
- delay,
220
- name: "retry-attempt",
221
- retryId
222
- },
223
- { silent: shouldIgnoreMessages([errorMsg]) }
224
- );
225
- getLogger()?.retrySchedulingReload(retryId, nextAttempt, delay);
226
- showLoadingUI(nextAttempt);
227
- setTimeout(() => {
228
- if (useRetryId && enableRetryReset) {
229
- setLastReloadTime(retryId, nextAttempt);
230
- }
231
- if (useRetryId) {
232
- const reloadUrl = buildReloadUrl(retryId, nextAttempt);
233
- globalThis.window.location.href = reloadUrl;
234
- } else {
235
- globalThis.window.location.href = buildReloadUrlAttemptOnly(nextAttempt);
236
- }
237
- }, delay);
238
- } catch {
239
- reloadScheduled = false;
240
- }
241
- };
242
- var showLoadingUI = (attempt) => {
243
- const options = getOptions();
244
- const loadingHtml = options.html?.loading?.content;
245
- const selector = options.html?.fallback?.selector ?? "body";
246
- if (!loadingHtml) {
247
- return;
248
- }
249
- try {
250
- const targetElement = document.querySelector(selector);
251
- if (!targetElement) {
252
- return;
253
- }
254
- const container = document.createElement("div");
255
- container.innerHTML = loadingHtml;
256
- const spinnerEl = container.querySelector("[data-spa-guard-spinner]");
257
- if (spinnerEl) {
258
- if (options.spinner?.disabled) {
259
- spinnerEl.remove();
260
- } else if (options.spinner?.content) {
261
- spinnerEl.innerHTML = options.spinner.content;
262
- }
263
- }
264
- const retryingSection = container.querySelector(
265
- '[data-spa-guard-section="retrying"]'
266
- );
267
- if (retryingSection) {
268
- retryingSection.style.display = "block";
269
- }
270
- const attemptEl = container.querySelector('[data-spa-guard-content="attempt"]');
271
- if (attemptEl) {
272
- attemptEl.textContent = String(attempt);
273
- }
274
- const t = getI18n();
275
- if (t) {
276
- applyI18n(container, t);
277
- }
278
- targetElement.innerHTML = container.innerHTML;
279
- } catch {
280
- }
281
- };
282
- var showFallbackUI = () => {
283
- const options = getOptions();
284
- const fallbackHtml = options.html?.fallback?.content;
285
- const selector = options.html?.fallback?.selector ?? "body";
286
- if (!fallbackHtml) {
287
- getLogger()?.noFallbackConfigured();
288
- return;
289
- }
290
- try {
291
- const targetElement = document.querySelector(selector);
292
- if (!targetElement) {
293
- getLogger()?.fallbackTargetNotFound(selector);
294
- return;
295
- }
296
- const container = document.createElement("div");
297
- container.innerHTML = fallbackHtml;
298
- const t = getI18n();
299
- if (t) {
300
- applyI18n(container, t);
301
- }
302
- targetElement.innerHTML = container.innerHTML;
303
- const useRetryId = options.useRetryId ?? true;
304
- const retryState = getRetryStateFromUrl();
305
- if (retryState && retryState.retryAttempt === -1) {
306
- getLogger()?.clearingRetryState();
307
- clearRetryStateFromUrl();
308
- } else if (!useRetryId && !retryState) {
309
- clearRetryAttemptFromUrl();
310
- }
311
- const reloadBtn = targetElement.querySelector('[data-spa-guard-action="reload"]');
312
- if (reloadBtn) {
313
- reloadBtn.addEventListener("click", () => location.reload());
314
- }
315
- if (retryState) {
316
- const retryIdElements = document.getElementsByClassName("spa-guard-retry-id");
317
- for (const element of retryIdElements) {
318
- element.textContent = retryState.retryId;
319
- }
320
- }
321
- emitEvent({
322
- name: "fallback-ui-shown"
323
- });
324
- } catch (error) {
325
- getLogger()?.fallbackInjectFailed(error);
326
- }
327
- };
328
-
329
51
  // src/common/serializeError.ts
330
52
  var serializeError = (error) => {
331
53
  try {
@@ -685,10 +407,6 @@ var createLogger = () => ({
685
407
 
686
408
  export {
687
409
  isChunkError,
688
- shouldIgnoreMessages,
689
- shouldForceRetry,
690
- sendBeacon,
691
- attemptReload,
692
410
  serializeError,
693
411
  listenInternal,
694
412
  createLogger
@@ -1,7 +1,12 @@
1
+ import {
2
+ showFallbackUI
3
+ } from "./chunk-VZCUDNEG.js";
1
4
  import {
2
5
  ForceRetryError,
3
- debugSyncErrorEventType
4
- } from "./chunk-YSKH5K6P.js";
6
+ debugSyncErrorEventType,
7
+ emitEvent,
8
+ getOptions
9
+ } from "./chunk-5DE7AKYB.js";
5
10
 
6
11
  // src/runtime/debug/errorDispatchers.ts
7
12
  function dispatchAsyncRuntimeError() {
@@ -29,6 +34,16 @@ function dispatchNetworkTimeout(delayMs = 3e3) {
29
34
  void Promise.reject(new TypeError("NetworkError: request timed out"));
30
35
  }, delayMs);
31
36
  }
37
+ function dispatchRetryExhausted() {
38
+ const options = getOptions();
39
+ const reloadDelays = options.reloadDelays ?? [1e3, 2e3, 5e3];
40
+ emitEvent({
41
+ finalAttempt: reloadDelays.length,
42
+ name: "retry-exhausted",
43
+ retryId: ""
44
+ });
45
+ showFallbackUI();
46
+ }
32
47
  function dispatchSyncRuntimeError() {
33
48
  const error = new Error("Simulated sync runtime error from spa-guard debug panel");
34
49
  globalThis.dispatchEvent(new CustomEvent(debugSyncErrorEventType, { detail: { error } }));
@@ -45,6 +60,7 @@ export {
45
60
  dispatchFinallyError,
46
61
  dispatchForceRetryError,
47
62
  dispatchNetworkTimeout,
63
+ dispatchRetryExhausted,
48
64
  dispatchSyncRuntimeError,
49
65
  dispatchUnhandledRejection
50
66
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defaultSpinnerHtml,
3
3
  getOptions
4
- } from "./chunk-DOTM7FSY.js";
4
+ } from "./chunk-5DE7AKYB.js";
5
5
 
6
6
  // src/common/parseVersion.ts
7
7
  function extractVersionFromHtml(html) {
@@ -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-5DE7AKYB.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
+ };
@@ -2,18 +2,17 @@ import {
2
2
  createLogger,
3
3
  listenInternal,
4
4
  serializeError
5
- } from "../chunk-4FKSMB5F.js";
6
- import {
7
- options_exports
8
- } from "../chunk-DOTM7FSY.js";
5
+ } from "../chunk-DUR4QNQE.js";
6
+ import "../chunk-VZCUDNEG.js";
9
7
  import {
10
8
  ForceRetryError,
11
9
  disableDefaultRetry,
12
10
  emitEvent,
13
11
  enableDefaultRetry,
14
12
  isDefaultRetryEnabled,
13
+ options_exports,
15
14
  subscribe
16
- } from "../chunk-YSKH5K6P.js";
15
+ } from "../chunk-5DE7AKYB.js";
17
16
  import {
18
17
  __export
19
18
  } from "../chunk-MLKGABMK.js";
@@ -1,3 +1,4 @@
1
1
  /** @internal */
2
2
  export declare const resetReloadScheduled: () => void;
3
3
  export declare const attemptReload: (error: unknown) => void;
4
+ export declare const showFallbackUI: () => void;
@@ -35,6 +35,12 @@ export declare function dispatchForceRetryError(): void;
35
35
  * Uses setTimeout + void Promise.reject() to trigger window "unhandledrejection".
36
36
  */
37
37
  export declare function dispatchNetworkTimeout(delayMs?: number): void;
38
+ /**
39
+ * Simulates the retry-exhausted state by emitting the "retry-exhausted" event
40
+ * with finalAttempt equal to the configured reloadDelays length, then renders
41
+ * the fallback UI into the DOM.
42
+ */
43
+ export declare function dispatchRetryExhausted(): void;
38
44
  /**
39
45
  * Dispatches a sync runtime error via CustomEvent.
40
46
  * DebugSyncErrorTrigger (a React component) listens for this event,
@@ -4,15 +4,17 @@ import {
4
4
  dispatchFinallyError,
5
5
  dispatchForceRetryError,
6
6
  dispatchNetworkTimeout,
7
+ dispatchRetryExhausted,
7
8
  dispatchSyncRuntimeError,
8
9
  dispatchUnhandledRejection
9
- } from "../../chunk-74K44EMP.js";
10
+ } from "../../chunk-H4DWKO5I.js";
10
11
  import {
11
12
  subscribeToState
12
- } from "../../chunk-SZS6GOPK.js";
13
+ } from "../../chunk-CQ5IVYGC.js";
14
+ import "../../chunk-VZCUDNEG.js";
13
15
  import {
14
16
  subscribe
15
- } from "../../chunk-YSKH5K6P.js";
17
+ } from "../../chunk-5DE7AKYB.js";
16
18
  import "../../chunk-MLKGABMK.js";
17
19
 
18
20
  // src/runtime/debug/index.ts
@@ -27,7 +29,8 @@ var SCENARIOS = [
27
29
  dispatch: dispatchUnhandledRejection,
28
30
  key: "unhandled-rejection",
29
31
  label: "Unhandled Rejection"
30
- }
32
+ },
33
+ { dispatch: dispatchRetryExhausted, key: "exhaust-retries", label: "Exhaust Retries" }
31
34
  ];
32
35
  var POSITION_MAP = {
33
36
  "bottom-left": "bottom:16px;left:16px;",
@@ -3,19 +3,17 @@ import {
3
3
  extractVersionFromHtml,
4
4
  getSpinnerHtml,
5
5
  showSpinner
6
- } from "../chunk-3ISQYZVD.js";
7
- import {
8
- getOptions,
9
- setTranslations
10
- } from "../chunk-DOTM7FSY.js";
6
+ } from "../chunk-UF7QAEI7.js";
11
7
  import {
12
8
  getState,
13
9
  subscribeToState
14
- } from "../chunk-SZS6GOPK.js";
10
+ } from "../chunk-CQ5IVYGC.js";
15
11
  import {
16
12
  ForceRetryError,
17
- getLogger
18
- } from "../chunk-YSKH5K6P.js";
13
+ getLogger,
14
+ getOptions,
15
+ setTranslations
16
+ } from "../chunk-5DE7AKYB.js";
19
17
  import "../chunk-MLKGABMK.js";
20
18
 
21
19
  // src/common/checkVersion.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovineko/spa-guard",
3
- "version": "0.0.1-alpha-22",
3
+ "version": "0.0.1-alpha-24",
4
4
  "description": "Chunk load error handling for SPAs — core runtime, error handling, schema, i18n",
5
5
  "keywords": [
6
6
  "spa",
@@ -1,162 +0,0 @@
1
- import {
2
- optionsWindowKey
3
- } from "./chunk-YSKH5K6P.js";
4
- import {
5
- __export
6
- } from "./chunk-MLKGABMK.js";
7
-
8
- // src/common/i18n.ts
9
- function applyI18n(container, t) {
10
- const contentEls = container.querySelectorAll("[data-spa-guard-content]");
11
- for (const el of contentEls) {
12
- const key = el.dataset.spaGuardContent;
13
- if (key && key in t) {
14
- const value = t[key];
15
- if (typeof value === "string") {
16
- el.textContent = value;
17
- }
18
- }
19
- }
20
- const actionEls = container.querySelectorAll("[data-spa-guard-action]");
21
- for (const el of actionEls) {
22
- const action = el.dataset.spaGuardAction;
23
- const tKey = action === "try-again" ? "tryAgain" : action;
24
- if (tKey && tKey in t) {
25
- const value = t[tKey];
26
- if (typeof value === "string") {
27
- el.textContent = value;
28
- }
29
- }
30
- }
31
- if (t.rtl) {
32
- for (const child of container.children) {
33
- if (child instanceof HTMLElement && child.tagName !== "STYLE") {
34
- child.style.direction = "rtl";
35
- }
36
- }
37
- }
38
- }
39
- function getI18n() {
40
- try {
41
- const el = document.querySelector('meta[name="spa-guard-i18n"]');
42
- if (!el) {
43
- return null;
44
- }
45
- const content = el.getAttribute("content");
46
- if (!content) {
47
- return null;
48
- }
49
- return JSON.parse(content);
50
- } catch {
51
- return null;
52
- }
53
- }
54
- function setTranslations(translations) {
55
- let el = document.querySelector('meta[name="spa-guard-i18n"]');
56
- if (!el) {
57
- el = document.createElement("meta");
58
- el.setAttribute("name", "spa-guard-i18n");
59
- document.head.append(el);
60
- }
61
- el.setAttribute("content", JSON.stringify(translations));
62
- }
63
-
64
- // src/common/html.generated.ts
65
- 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>`;
66
- 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>`;
67
- 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>`;
68
-
69
- // src/common/options.ts
70
- var options_exports = {};
71
- __export(options_exports, {
72
- getOptions: () => getOptions,
73
- optionsWindowKey: () => optionsWindowKey
74
- });
75
- var defaultOptions = {
76
- checkVersion: {
77
- interval: 3e5,
78
- mode: "html",
79
- onUpdate: "reload"
80
- },
81
- enableRetryReset: true,
82
- errors: {
83
- forceRetry: [],
84
- ignore: []
85
- },
86
- handleUnhandledRejections: {
87
- retry: true,
88
- sendBeacon: true
89
- },
90
- html: {
91
- fallback: {
92
- content: defaultErrorFallbackHtml,
93
- selector: "body"
94
- },
95
- loading: {
96
- content: defaultLoadingFallbackHtml
97
- }
98
- },
99
- lazyRetry: {
100
- callReloadOnFailure: true,
101
- retryDelays: [1e3, 2e3]
102
- },
103
- minTimeBetweenResets: 5e3,
104
- reloadDelays: [1e3, 2e3, 5e3],
105
- spinner: {
106
- background: "#fff",
107
- disabled: false
108
- },
109
- useRetryId: true
110
- };
111
- var getOptions = () => {
112
- const windowOptions = globalThis.window?.[optionsWindowKey];
113
- return {
114
- ...defaultOptions,
115
- ...windowOptions,
116
- checkVersion: {
117
- ...defaultOptions.checkVersion,
118
- ...windowOptions?.checkVersion
119
- },
120
- errors: {
121
- ...defaultOptions.errors,
122
- ...windowOptions?.errors
123
- },
124
- handleUnhandledRejections: {
125
- ...defaultOptions.handleUnhandledRejections,
126
- ...windowOptions?.handleUnhandledRejections
127
- },
128
- html: {
129
- fallback: {
130
- ...defaultOptions.html?.fallback,
131
- ...windowOptions?.html?.fallback
132
- },
133
- loading: {
134
- ...defaultOptions.html?.loading,
135
- ...windowOptions?.html?.loading
136
- }
137
- },
138
- lazyRetry: {
139
- ...defaultOptions.lazyRetry,
140
- ...windowOptions?.lazyRetry
141
- },
142
- reportBeacon: {
143
- ...defaultOptions.reportBeacon,
144
- ...windowOptions?.reportBeacon
145
- },
146
- spinner: {
147
- ...defaultOptions.spinner,
148
- ...windowOptions?.spinner
149
- }
150
- };
151
- };
152
-
153
- export {
154
- applyI18n,
155
- getI18n,
156
- setTranslations,
157
- defaultErrorFallbackHtml,
158
- defaultLoadingFallbackHtml,
159
- defaultSpinnerHtml,
160
- getOptions,
161
- options_exports
162
- };