@ovineko/spa-guard 0.0.2-alpha-1 → 0.0.4

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.
Files changed (67) hide show
  1. package/README.md +22 -173
  2. package/dist/ForceRetryError-BWLv3UVK.d.mts +6 -0
  3. package/dist/_internal.d.ts +149 -20
  4. package/dist/_internal.js +113 -170
  5. package/dist/chunk-CfYAbeIz.mjs +13 -0
  6. package/dist/common/index.d.ts +29 -9
  7. package/dist/common/index.js +47 -83
  8. package/dist/errorDispatchers-Cl_pa0DT.mjs +105 -0
  9. package/dist/i18n/index.d.ts +2 -21
  10. package/dist/i18n/index.js +344 -341
  11. package/dist/index-DL8CfPXg.d.mts +26 -0
  12. package/dist/index-rPxPv6iu.d.mts +16 -0
  13. package/dist/logger-Cp1Eyk6S.mjs +534 -0
  14. package/dist/retryOrchestrator-DNGIHV2M.mjs +758 -0
  15. package/dist/retryOrchestrator-mn9XcIVu.d.mts +257 -0
  16. package/dist/runtime/debug/index.d.ts +6 -4
  17. package/dist/runtime/debug/index.js +218 -238
  18. package/dist/runtime/index.d.ts +66 -8
  19. package/dist/runtime/index.js +279 -367
  20. package/dist/schema/index.d.ts +2 -13
  21. package/dist/schema/index.js +1 -0
  22. package/dist/schema/parse.d.ts +6 -2
  23. package/dist/schema/parse.js +29 -44
  24. package/dist/spinner-BbZVKZ-6.mjs +60 -0
  25. package/dist/spinner-X23gI09z.d.mts +37 -0
  26. package/dist/state-I20jENMD.mjs +93 -0
  27. package/dist/types-DrN8pgyc.d.mts +107 -0
  28. package/package.json +1 -4
  29. package/dist/chunk-3UJ67DPX.js +0 -98
  30. package/dist/chunk-GE63YJOT.js +0 -865
  31. package/dist/chunk-MLKGABMK.js +0 -9
  32. package/dist/chunk-PERG4557.js +0 -74
  33. package/dist/chunk-VZ2DLGXX.js +0 -111
  34. package/dist/chunk-XIFXSNSD.js +0 -678
  35. package/dist/common/checkVersion.d.ts +0 -5
  36. package/dist/common/constants.d.ts +0 -14
  37. package/dist/common/errors/BeaconError.d.ts +0 -12
  38. package/dist/common/errors/ForceRetryError.d.ts +0 -5
  39. package/dist/common/events/index.d.ts +0 -2
  40. package/dist/common/events/internal.d.ts +0 -13
  41. package/dist/common/events/types.d.ts +0 -104
  42. package/dist/common/fallbackRendering.d.ts +0 -23
  43. package/dist/common/fallbackState.d.ts +0 -3
  44. package/dist/common/handleErrorWithSpaGuard.d.ts +0 -18
  45. package/dist/common/html.generated.d.ts +0 -3
  46. package/dist/common/i18n.d.ts +0 -23
  47. package/dist/common/isChunkError.d.ts +0 -1
  48. package/dist/common/isStaticAssetError.d.ts +0 -3
  49. package/dist/common/lastReloadTime.d.ts +0 -17
  50. package/dist/common/listen/index.d.ts +0 -1
  51. package/dist/common/listen/internal.d.ts +0 -2
  52. package/dist/common/log.d.ts +0 -1
  53. package/dist/common/logger.d.ts +0 -30
  54. package/dist/common/options.d.ts +0 -182
  55. package/dist/common/parseVersion.d.ts +0 -9
  56. package/dist/common/retryImport.d.ts +0 -43
  57. package/dist/common/retryOrchestrator.d.ts +0 -35
  58. package/dist/common/retryState.d.ts +0 -11
  59. package/dist/common/sendBeacon.d.ts +0 -2
  60. package/dist/common/serializeError.d.ts +0 -1
  61. package/dist/common/shouldIgnore.d.ts +0 -13
  62. package/dist/common/spinner.d.ts +0 -8
  63. package/dist/common/staticAssetRecovery.d.ts +0 -2
  64. package/dist/i18n/translations.d.ts +0 -2
  65. package/dist/runtime/debug/errorDispatchers.d.ts +0 -63
  66. package/dist/runtime/recommendedSetup.d.ts +0 -36
  67. package/dist/runtime/state.d.ts +0 -20
@@ -0,0 +1,26 @@
1
+ //#region src/i18n/translations.d.ts
2
+ declare const translations: Record<string, SpaGuardTranslations>;
3
+ //#endregion
4
+ //#region src/i18n/index.d.ts
5
+ interface SpaGuardTranslations {
6
+ heading: string;
7
+ loading: string;
8
+ message: string;
9
+ reload: string;
10
+ retrying: string;
11
+ rtl?: boolean;
12
+ tryAgain: string;
13
+ }
14
+ /**
15
+ * Match a language code or Accept-Language header against available translations.
16
+ *
17
+ * - If `input` is undefined, returns `"en"`.
18
+ * - If `input` contains `,` or `;q=`, it's parsed as an Accept-Language header
19
+ * with quality values sorted descending.
20
+ * - Otherwise it's treated as a direct language code.
21
+ *
22
+ * Resolution: exact match → prefix match → `"en"` (or first available if `"en"` not in list).
23
+ */
24
+ declare function matchLang(input: string | undefined, available?: string[]): string;
25
+ //#endregion
26
+ export { matchLang as n, translations as r, SpaGuardTranslations as t };
@@ -0,0 +1,16 @@
1
+ //#region src/schema/index.d.ts
2
+ interface BeaconSchema {
3
+ appName?: string;
4
+ errorContext?: string;
5
+ errorMessage?: string;
6
+ errorType?: string;
7
+ eventMessage?: string;
8
+ eventName?: string;
9
+ httpStatus?: number;
10
+ retryAttempt?: number;
11
+ retryId?: string;
12
+ serialized?: string;
13
+ url?: string;
14
+ }
15
+ //#endregion
16
+ export { BeaconSchema as t };
@@ -0,0 +1,534 @@
1
+ import { A as markInitialized, D as getLogger, I as staticAssetRecoveryKey, T as emitEvent, a as triggerRetry, c as shouldIgnoreMessages, g as getOptions, j as setLogger, k as isInitialized, m as getRetryInfoForBeacon, o as sendBeacon, s as shouldForceRetry } from "./retryOrchestrator-DNGIHV2M.mjs";
2
+ //#region src/common/isChunkError.ts
3
+ const isChunkError = (error) => {
4
+ const message = getErrorMessage(error);
5
+ if (!message) return false;
6
+ return [
7
+ /Failed to fetch dynamically imported module/i,
8
+ /Importing a module script failed/i,
9
+ /error loading dynamically imported module/i,
10
+ /Unable to preload CSS/i,
11
+ /Loading chunk \d+ failed/i,
12
+ /Loading CSS chunk \d+ failed/i,
13
+ /ChunkLoadError/i
14
+ ].some((pattern) => pattern.test(message));
15
+ };
16
+ const getErrorMessage = (error) => {
17
+ if (error instanceof Error) return error.message;
18
+ if (typeof error === "string") return error;
19
+ if (error && typeof error === "object" && "message" in error) return String(error.message);
20
+ if (error && typeof error === "object" && "reason" in error) return getErrorMessage(error.reason);
21
+ return null;
22
+ };
23
+ //#endregion
24
+ //#region src/common/serializeError.ts
25
+ const serializeError = (error) => {
26
+ try {
27
+ const serialized = serializeErrorInternal(error);
28
+ return JSON.stringify(serialized, null, 2);
29
+ } catch {
30
+ return JSON.stringify({
31
+ error: "Failed to serialize error",
32
+ fallback: String(error)
33
+ });
34
+ }
35
+ };
36
+ const MAX_DEPTH = 4;
37
+ const MAX_KEYS = 20;
38
+ const MAX_STRING_LEN = 500;
39
+ const truncate = (str) => str.length > MAX_STRING_LEN ? str.slice(0, MAX_STRING_LEN) + "…" : str;
40
+ const serializeErrorInternal = (error) => {
41
+ if (error === null || error === void 0) return {
42
+ type: "null",
43
+ value: error
44
+ };
45
+ if (typeof error !== "object") return {
46
+ type: typeof error,
47
+ value: error
48
+ };
49
+ if (error instanceof Error) return {
50
+ message: error.message,
51
+ name: error.name,
52
+ stack: error.stack ? truncate(error.stack) : void 0,
53
+ type: "Error",
54
+ ...extractErrorProperties(error)
55
+ };
56
+ if ("reason" in error && "promise" in error) {
57
+ const evt = error;
58
+ const reason = evt.reason;
59
+ const pageUrl = typeof window !== "undefined" && typeof window.location?.href === "string" ? window.location.href : void 0;
60
+ return {
61
+ constructorName: reason?.constructor?.name,
62
+ isTrusted: evt.isTrusted,
63
+ pageUrl,
64
+ reason: serializeRejectionReason(reason, /* @__PURE__ */ new WeakSet(), 0),
65
+ timeStamp: evt.timeStamp,
66
+ type: "PromiseRejectionEvent"
67
+ };
68
+ }
69
+ if ("error" in error && "message" in error && "filename" in error) return {
70
+ colno: error.colno,
71
+ error: serializeErrorInternal(error.error),
72
+ filename: error.filename,
73
+ lineno: error.lineno,
74
+ message: error.message,
75
+ type: "ErrorEvent"
76
+ };
77
+ if ("violatedDirective" in error && "blockedURI" in error) {
78
+ const evt = error;
79
+ return {
80
+ blockedURI: evt.blockedURI,
81
+ columnNumber: evt.columnNumber,
82
+ effectiveDirective: evt.effectiveDirective,
83
+ lineNumber: evt.lineNumber,
84
+ originalPolicy: evt.originalPolicy,
85
+ sourceFile: evt.sourceFile,
86
+ type: "SecurityPolicyViolationEvent",
87
+ violatedDirective: evt.violatedDirective
88
+ };
89
+ }
90
+ if ("type" in error && "target" in error) {
91
+ const evt = error;
92
+ return {
93
+ eventType: evt.type,
94
+ target: extractEventTarget(evt.target),
95
+ timeStamp: evt.timeStamp,
96
+ type: "Event"
97
+ };
98
+ }
99
+ return {
100
+ type: "object",
101
+ value: extractOwnProperties(error)
102
+ };
103
+ };
104
+ const serializeRejectionReason = (reason, visited, depth) => {
105
+ if (reason === null || reason === void 0) return {
106
+ type: "null",
107
+ value: reason
108
+ };
109
+ if (typeof reason !== "object") {
110
+ const val = typeof reason === "string" ? truncate(reason) : reason;
111
+ return {
112
+ type: typeof reason,
113
+ value: val
114
+ };
115
+ }
116
+ if (visited.has(reason)) return { type: "circular" };
117
+ visited.add(reason);
118
+ if (typeof AggregateError !== "undefined" && reason instanceof AggregateError) return {
119
+ errors: reason.errors.slice(0, 3).map((e) => serializeSafeError(e, visited, depth + 1)),
120
+ message: truncate(reason.message),
121
+ name: reason.name,
122
+ stack: reason.stack ? truncate(reason.stack) : void 0,
123
+ type: "Error"
124
+ };
125
+ if (typeof DOMException !== "undefined" && reason instanceof DOMException) return {
126
+ code: reason.code,
127
+ message: truncate(reason.message),
128
+ name: reason.name,
129
+ type: "Error"
130
+ };
131
+ const reasonObj = reason;
132
+ if (reasonObj.response != null) {
133
+ const response = reasonObj.response;
134
+ const result = { type: "HttpError" };
135
+ if (response.status !== void 0) result.status = response.status;
136
+ if (response.statusText !== void 0) result.statusText = typeof response.statusText === "string" ? truncate(response.statusText) : response.statusText;
137
+ if (response.url !== void 0) result.url = typeof response.url === "string" ? truncate(response.url) : response.url;
138
+ if (response.method !== void 0) result.method = response.method;
139
+ if (response.type !== void 0) result.responseType = response.type;
140
+ try {
141
+ const headers = response.headers;
142
+ if (headers) {
143
+ let xRequestId;
144
+ if (typeof headers.get === "function") xRequestId = headers.get("X-Request-ID") ?? headers.get("x-request-id");
145
+ else if (typeof headers === "object") xRequestId = headers["X-Request-ID"] ?? headers["x-request-id"];
146
+ if (xRequestId) result.xRequestId = truncate(String(xRequestId));
147
+ }
148
+ } catch {}
149
+ const reqSource = reasonObj.config ?? reasonObj.request;
150
+ if (reqSource != null) {
151
+ const req = {};
152
+ if (reqSource.method !== void 0) req.method = reqSource.method;
153
+ if (reqSource.url !== void 0) req.url = typeof reqSource.url === "string" ? truncate(reqSource.url) : reqSource.url;
154
+ if (reqSource.baseURL !== void 0) req.baseURL = typeof reqSource.baseURL === "string" ? truncate(reqSource.baseURL) : reqSource.baseURL;
155
+ result.request = req;
156
+ }
157
+ return result;
158
+ }
159
+ if (reason instanceof Error) return serializeSafeError(reason, visited, depth);
160
+ return {
161
+ type: "object",
162
+ value: extractBoundedObject(reasonObj, visited, depth)
163
+ };
164
+ };
165
+ const serializeSafeError = (error, visited, depth) => {
166
+ if (!(error instanceof Error)) return serializeRejectionReason(error, visited, depth + 1);
167
+ const result = {
168
+ message: truncate(error.message),
169
+ name: error.name,
170
+ stack: error.stack ? truncate(error.stack) : void 0,
171
+ type: "Error"
172
+ };
173
+ if (depth < MAX_DEPTH && error.cause !== void 0) {
174
+ const cause = error.cause;
175
+ if (typeof cause === "object" && cause !== null) {
176
+ if (!visited.has(cause)) result.cause = serializeRejectionReason(cause, visited, depth + 1);
177
+ } else result.cause = cause;
178
+ }
179
+ return result;
180
+ };
181
+ const extractBoundedObject = (obj, visited, depth) => {
182
+ const result = {};
183
+ let keyCount = 0;
184
+ for (const key of Object.keys(obj)) {
185
+ if (keyCount >= MAX_KEYS) break;
186
+ try {
187
+ const value = obj[key];
188
+ if (value === null || value === void 0 || typeof value !== "object") result[key] = typeof value === "string" ? truncate(value) : value;
189
+ else if (depth < MAX_DEPTH && !visited.has(value)) {
190
+ visited.add(value);
191
+ result[key] = extractBoundedObject(value, visited, depth + 1);
192
+ } else result[key] = "[object]";
193
+ } catch {}
194
+ keyCount++;
195
+ }
196
+ return result;
197
+ };
198
+ const extractErrorProperties = (error) => {
199
+ const props = {};
200
+ let keyCount = 0;
201
+ for (const key of Object.getOwnPropertyNames(error)) {
202
+ if (keyCount >= MAX_KEYS) break;
203
+ if (![
204
+ "message",
205
+ "name",
206
+ "stack"
207
+ ].includes(key)) {
208
+ try {
209
+ const value = error[key];
210
+ if (value === null || value === void 0 || typeof value !== "object") props[key] = typeof value === "string" ? truncate(value) : value;
211
+ else props[key] = "[object]";
212
+ } catch {}
213
+ keyCount++;
214
+ }
215
+ }
216
+ return props;
217
+ };
218
+ const extractEventTarget = (target) => {
219
+ if (!target) return null;
220
+ if (target instanceof HTMLElement) return {
221
+ className: target.className,
222
+ href: target.href,
223
+ id: target.id,
224
+ src: target.src,
225
+ tagName: target.tagName
226
+ };
227
+ return { type: String(target) };
228
+ };
229
+ const extractOwnProperties = (obj) => {
230
+ const props = {};
231
+ let keyCount = 0;
232
+ for (const key of Object.keys(obj)) {
233
+ if (keyCount >= MAX_KEYS) break;
234
+ try {
235
+ const value = obj[key];
236
+ if (value === null || value === void 0 || typeof value !== "object") props[key] = typeof value === "string" ? truncate(value) : value;
237
+ else props[key] = "[object]";
238
+ } catch {}
239
+ keyCount++;
240
+ }
241
+ return props;
242
+ };
243
+ //#endregion
244
+ //#region src/common/isStaticAssetError.ts
245
+ const HASHED_ASSET_RE = /[-._][a-zA-Z0-9]{6,}\.(js|mjs|css)$/i;
246
+ const isHashedAssetUrl = (url) => {
247
+ try {
248
+ const pathname = new URL(url).pathname;
249
+ return HASHED_ASSET_RE.test(pathname);
250
+ } catch {
251
+ return HASHED_ASSET_RE.test(url);
252
+ }
253
+ };
254
+ const isStaticAssetError = (event) => {
255
+ const target = event.target;
256
+ if (target instanceof HTMLScriptElement) return isHashedAssetUrl(target.src);
257
+ if (target instanceof HTMLLinkElement) return isHashedAssetUrl(target.href);
258
+ return false;
259
+ };
260
+ const checkResourceStatus = (url) => {
261
+ if (typeof performance === "undefined" || typeof performance.getEntriesByName !== "function") return true;
262
+ const entries = performance.getEntriesByName(url, "resource");
263
+ if (entries.length === 0) return true;
264
+ const entry = entries.at(-1);
265
+ if (entry.responseStatus >= 400) return true;
266
+ if (entry.transferSize === 0 && entry.decodedBodySize === 0) return true;
267
+ return false;
268
+ };
269
+ const isLikely404 = (url, timeSinceNavMs) => {
270
+ if (url !== void 0) return checkResourceStatus(url);
271
+ return (timeSinceNavMs ?? (typeof performance === "undefined" ? 0 : performance.now())) > 3e4;
272
+ };
273
+ const getAssetUrl = (event) => {
274
+ const target = event.target;
275
+ if (target instanceof HTMLScriptElement) return target.src;
276
+ if (target instanceof HTMLLinkElement) return target.href;
277
+ return "";
278
+ };
279
+ //#endregion
280
+ //#region src/common/staticAssetRecovery.ts
281
+ const getState = () => {
282
+ if (globalThis.window === void 0) return {
283
+ failedAssets: /* @__PURE__ */ new Set(),
284
+ recoveryTimer: null
285
+ };
286
+ if (!globalThis.window[staticAssetRecoveryKey]) globalThis.window[staticAssetRecoveryKey] = {
287
+ failedAssets: /* @__PURE__ */ new Set(),
288
+ recoveryTimer: null
289
+ };
290
+ return globalThis.window[staticAssetRecoveryKey];
291
+ };
292
+ const handleStaticAssetFailure = (url) => {
293
+ if (globalThis.window === void 0) return;
294
+ const state = getState();
295
+ state.failedAssets.add(url);
296
+ if (state.recoveryTimer !== null) return;
297
+ const delay = getOptions().staticAssets?.recoveryDelay ?? 500;
298
+ state.recoveryTimer = setTimeout(() => {
299
+ const s = getState();
300
+ s.recoveryTimer = null;
301
+ const assets = [...s.failedAssets];
302
+ s.failedAssets = /* @__PURE__ */ new Set();
303
+ triggerRetry({
304
+ cacheBust: true,
305
+ error: /* @__PURE__ */ new Error(`Static asset load failed: ${assets.join(", ")}`),
306
+ source: "static-asset-error"
307
+ });
308
+ }, delay);
309
+ };
310
+ //#endregion
311
+ //#region src/common/listen/internal.ts
312
+ const listenInternal = (serializeError, logger) => {
313
+ if (globalThis.window === void 0) return;
314
+ if (isInitialized()) return;
315
+ if (logger) setLogger(logger);
316
+ markInitialized();
317
+ const wa = globalThis.window.addEventListener.bind(globalThis.window);
318
+ wa("error", (event) => {
319
+ const assetUrl = getAssetUrl(event);
320
+ if (isStaticAssetError(event) && isLikely404(assetUrl)) {
321
+ if (shouldIgnoreMessages([assetUrl, event.message])) return;
322
+ event.preventDefault();
323
+ emitEvent({
324
+ name: "static-asset-load-failed",
325
+ url: assetUrl
326
+ });
327
+ if (getOptions().staticAssets?.autoRecover !== false) handleStaticAssetFailure(assetUrl);
328
+ return;
329
+ }
330
+ if (shouldIgnoreMessages([event.message])) return;
331
+ getLogger()?.capturedError("error", event);
332
+ if (isChunkError(event)) {
333
+ event.preventDefault();
334
+ triggerRetry({
335
+ error: event.error ?? event,
336
+ source: "chunk-error"
337
+ });
338
+ return;
339
+ }
340
+ if (shouldForceRetry([event.message])) {
341
+ event.preventDefault();
342
+ triggerRetry({
343
+ error: event.error ?? event,
344
+ source: "force-retry"
345
+ });
346
+ return;
347
+ }
348
+ const serialized = serializeError(event);
349
+ sendBeacon({
350
+ errorMessage: event.message,
351
+ eventName: "error",
352
+ serialized,
353
+ ...getRetryInfoForBeacon()
354
+ });
355
+ }, true);
356
+ wa("unhandledrejection", (event) => {
357
+ const errorMessage = String(event.reason);
358
+ if (shouldIgnoreMessages([errorMessage])) return;
359
+ getLogger()?.capturedError("unhandledrejection", event);
360
+ if (isChunkError(event.reason)) {
361
+ event.preventDefault();
362
+ triggerRetry({
363
+ error: event.reason,
364
+ source: "chunk-error"
365
+ });
366
+ return;
367
+ }
368
+ if (shouldForceRetry([errorMessage])) {
369
+ event.preventDefault();
370
+ triggerRetry({
371
+ error: event.reason,
372
+ source: "force-retry"
373
+ });
374
+ return;
375
+ }
376
+ const rejectionConfig = getOptions().handleUnhandledRejections;
377
+ if (rejectionConfig?.sendBeacon !== false) sendBeacon({
378
+ errorMessage,
379
+ eventName: "unhandledrejection",
380
+ serialized: serializeError(event),
381
+ ...getRetryInfoForBeacon()
382
+ });
383
+ if (rejectionConfig?.retry !== false) {
384
+ event.preventDefault();
385
+ triggerRetry({
386
+ error: event.reason,
387
+ source: "unhandled-rejection"
388
+ });
389
+ }
390
+ });
391
+ wa("securitypolicyviolation", (event) => {
392
+ const eventMessage = `${event.violatedDirective}: ${event.blockedURI}`;
393
+ if (shouldIgnoreMessages([eventMessage])) return;
394
+ getLogger()?.capturedError("csp", event.blockedURI, event.violatedDirective);
395
+ sendBeacon({
396
+ eventMessage,
397
+ eventName: "securitypolicyviolation",
398
+ serialized: serializeError(event),
399
+ ...getRetryInfoForBeacon()
400
+ });
401
+ });
402
+ wa("vite:preloadError", (event) => {
403
+ const payload = event?.payload;
404
+ if (shouldIgnoreMessages([payload?.message || event?.message])) return;
405
+ getLogger()?.capturedError("vite:preloadError", event);
406
+ event.preventDefault();
407
+ triggerRetry({
408
+ error: payload ?? event,
409
+ source: "vite:preloadError"
410
+ });
411
+ });
412
+ };
413
+ //#endregion
414
+ //#region src/common/logger.ts
415
+ const PREFIX = "[spa-guard]";
416
+ const eventLogConfig = {
417
+ "chunk-error": "error",
418
+ "fallback-ui-not-rendered": "error",
419
+ "fallback-ui-shown": "warn",
420
+ "lazy-retry-attempt": "warn",
421
+ "lazy-retry-exhausted": "error",
422
+ "lazy-retry-start": "log",
423
+ "lazy-retry-success": "log",
424
+ "retry-attempt": "warn",
425
+ "retry-exhausted": "error",
426
+ "retry-reset": "log",
427
+ "static-asset-load-failed": "error"
428
+ };
429
+ const formatEvent = (event) => {
430
+ switch (event.name) {
431
+ case "chunk-error": return `${PREFIX} chunk-error: isRetrying=${event.isRetrying}`;
432
+ case "fallback-ui-not-rendered": {
433
+ const selectorPart = event.selector ? ` selector=${event.selector}` : "";
434
+ return `${PREFIX} fallback-ui-not-rendered: reason=${event.reason}${selectorPart}`;
435
+ }
436
+ case "fallback-ui-shown": return `${PREFIX} fallback-ui-shown`;
437
+ case "lazy-retry-attempt": return `${PREFIX} lazy-retry-attempt: attempt ${event.attempt}/${event.totalAttempts}, delay ${event.delay}ms`;
438
+ case "lazy-retry-exhausted": return `${PREFIX} lazy-retry-exhausted: ${event.totalAttempts} attempts, willReload=${event.willReload}`;
439
+ case "lazy-retry-start": return `${PREFIX} lazy-retry-start: totalAttempts=${event.totalAttempts}`;
440
+ case "lazy-retry-success": {
441
+ const timePart = event.totalTime === void 0 ? "" : `, totalTime=${event.totalTime}ms`;
442
+ return `${PREFIX} lazy-retry-success: succeeded on attempt ${event.attempt}${timePart}`;
443
+ }
444
+ case "retry-attempt": return `${PREFIX} retry-attempt: attempt ${event.attempt} in ${event.delay}ms (retryId: ${event.retryId})`;
445
+ case "retry-exhausted": return `${PREFIX} retry-exhausted: finalAttempt=${event.finalAttempt} (retryId: ${event.retryId})`;
446
+ case "retry-reset": return `${PREFIX} retry-reset: ${event.timeSinceReload}ms since last reload (retryId: ${event.previousRetryId})`;
447
+ case "static-asset-load-failed": return `${PREFIX} static-asset-load-failed: ${event.url}`;
448
+ }
449
+ };
450
+ const createLogger = () => ({
451
+ beaconSendFailed(error) {
452
+ console.error(`${PREFIX} Failed to send beacon:`, error);
453
+ },
454
+ capturedError(type, ...args) {
455
+ console.error(`${PREFIX} ${type}:capture:`, ...args);
456
+ },
457
+ error(msg, ...args) {
458
+ console.error(`${PREFIX} ${msg}`, ...args);
459
+ },
460
+ fallbackAlreadyShown(error) {
461
+ console.error(`${PREFIX} Fallback UI was already shown. Not retrying to prevent infinite loop.`, error);
462
+ },
463
+ fallbackInjectFailed(error) {
464
+ console.error(`${PREFIX} Failed to inject fallback UI`, error);
465
+ },
466
+ fallbackTargetNotFound(selector) {
467
+ console.error(`${PREFIX} Target element not found for selector: ${selector}`);
468
+ },
469
+ log(msg, ...args) {
470
+ console.log(`${PREFIX} ${msg}`, ...args);
471
+ },
472
+ logEvent(event) {
473
+ const level = eventLogConfig[event.name];
474
+ const message = formatEvent(event);
475
+ if (event.name === "chunk-error" || event.name === "lazy-retry-attempt" || event.name === "lazy-retry-exhausted") console[level](message, event.error);
476
+ else console[level](message);
477
+ },
478
+ noBeaconEndpoint() {
479
+ console.warn(`${PREFIX} Report endpoint is not configured`);
480
+ },
481
+ noFallbackConfigured() {
482
+ console.error(`${PREFIX} No fallback UI configured`);
483
+ },
484
+ reloadAlreadyScheduled(error) {
485
+ console.log(`${PREFIX} Reload already scheduled, ignoring duplicate chunk error:`, error);
486
+ },
487
+ retryCycleStarting(retryId, fromAttempt) {
488
+ console.log(`${PREFIX} Retry cycle starting: retryId=${retryId}, fromAttempt=${fromAttempt}`);
489
+ },
490
+ retrySchedulingReload(retryId, attempt, delay) {
491
+ console.log(`${PREFIX} Scheduling reload: retryId=${retryId}, attempt=${attempt}, delay=${delay}ms`);
492
+ },
493
+ versionChangeDetected(oldVersion, latestVersion) {
494
+ console.warn(`${PREFIX} New version available (${oldVersion ?? "unknown"} → ${latestVersion}). Please refresh to get the latest version.`);
495
+ },
496
+ versionCheckAlreadyRunning() {
497
+ console.warn(`${PREFIX} Version check already running`);
498
+ },
499
+ versionCheckDisabled() {
500
+ console.warn(`${PREFIX} Version checking disabled: no version configured`);
501
+ },
502
+ versionCheckFailed(error) {
503
+ console.error(`${PREFIX} Version check failed`, error);
504
+ },
505
+ versionCheckHttpError(status) {
506
+ console.warn(`${PREFIX} Version check HTTP error: ${status}`);
507
+ },
508
+ versionCheckParseError() {
509
+ console.warn(`${PREFIX} Failed to parse version from HTML`);
510
+ },
511
+ versionCheckPaused() {
512
+ console.log(`${PREFIX} Version check paused (tab hidden)`);
513
+ },
514
+ versionCheckRequiresEndpoint() {
515
+ console.warn(`${PREFIX} JSON version check mode requires endpoint`);
516
+ },
517
+ versionCheckResumed() {
518
+ console.log(`${PREFIX} Version check resumed (tab visible)`);
519
+ },
520
+ versionCheckResumedImmediate() {
521
+ console.log(`${PREFIX} Version check resumed with immediate check (tab visible, interval elapsed)`);
522
+ },
523
+ versionCheckStarted(mode, interval, version) {
524
+ console.log(`${PREFIX} Starting version check (mode: ${mode}, interval: ${interval}ms, current: ${version})`);
525
+ },
526
+ versionCheckStopped() {
527
+ console.log(`${PREFIX} Version check stopped`);
528
+ },
529
+ warn(msg, ...args) {
530
+ console.warn(`${PREFIX} ${msg}`, ...args);
531
+ }
532
+ });
533
+ //#endregion
534
+ export { isChunkError as i, listenInternal as n, serializeError as r, createLogger as t };