From 6489af44d5f397c249c0063a84c5cf51761ef495 Mon Sep 17 00:00:00 2001
From: Ivan <8611739+IRBorisov@users.noreply.github.com>
Date: Sun, 9 Nov 2025 16:04:04 +0300
Subject: [PATCH] F: Improve dynamic loading error handling
---
rsconcept/frontend/src/app/error-fallback.tsx | 19 ++++++++++++++
rsconcept/frontend/src/main.tsx | 26 ++++++++++++++-----
rsconcept/frontend/src/utils/utils.ts | 13 ++++++++++
3 files changed, 51 insertions(+), 7 deletions(-)
diff --git a/rsconcept/frontend/src/app/error-fallback.tsx b/rsconcept/frontend/src/app/error-fallback.tsx
index 7e57d970..3a71fb6e 100644
--- a/rsconcept/frontend/src/app/error-fallback.tsx
+++ b/rsconcept/frontend/src/app/error-fallback.tsx
@@ -1,17 +1,36 @@
'use client';
+import { useEffect } from 'react';
import { useNavigate, useRouteError } from 'react-router';
import { Button } from '@/components/control';
import { InfoError } from '@/components/info-error';
+import { isStaleBundleError } from '@/utils/utils';
export function ErrorFallback() {
const error = useRouteError();
const router = useNavigate();
+ useEffect(() => {
+ if (isStaleBundleError(error)) {
+ console.warn('Detected stale bundle — reloading...');
+ window.location.reload();
+ }
+ }, [error]);
+
function resetErrorBoundary() {
Promise.resolve(router('/')).catch(console.error);
}
+
+ if (isStaleBundleError(error)) {
+ return (
+
+
Обновление страницы...
+
Обнаружена устаревшая версия приложения. Перезагрузка страницы...
+
+ );
+ }
+
return (
Что-то пошло не так!
diff --git a/rsconcept/frontend/src/main.tsx b/rsconcept/frontend/src/main.tsx
index e3212551..1d432879 100644
--- a/rsconcept/frontend/src/main.tsx
+++ b/rsconcept/frontend/src/main.tsx
@@ -14,15 +14,27 @@ if (typeof window !== 'undefined' && import.meta.env.DEV) {
}
if (typeof window !== 'undefined') {
- window.addEventListener('error', (event: ErrorEvent) => {
- const error = event.error as Error;
- if (
- error instanceof Error &&
- typeof error.message === 'string' &&
- error.message.includes('Failed to fetch dynamically imported module')
- ) {
+ function handleStaleBundleError(error: unknown): boolean {
+ if (error instanceof Error && error.message.includes('Failed to fetch dynamically imported module')) {
console.warn('Detected stale bundle — reloading...');
window.location.reload();
+ return true;
+ }
+ if (typeof error === 'string' && error.includes('Failed to fetch dynamically imported module')) {
+ console.warn('Detected stale bundle — reloading...');
+ window.location.reload();
+ return true;
+ }
+ return false;
+ }
+
+ window.addEventListener('error', (event: ErrorEvent) => {
+ handleStaleBundleError(event.error);
+ });
+
+ window.addEventListener('unhandledrejection', (event: PromiseRejectionEvent) => {
+ if (handleStaleBundleError(event.reason)) {
+ event.preventDefault();
}
});
}
diff --git a/rsconcept/frontend/src/utils/utils.ts b/rsconcept/frontend/src/utils/utils.ts
index 0ba3b7d7..b827ad56 100644
--- a/rsconcept/frontend/src/utils/utils.ts
+++ b/rsconcept/frontend/src/utils/utils.ts
@@ -8,6 +8,19 @@ import { type AxiosError, type AxiosHeaderValue, type AxiosResponse, isAxiosErro
import { PARAMETER } from './constants';
import { infoMsg, promptText } from './labels';
+/**
+ * Check if error is stale bundle error.
+ */
+export function isStaleBundleError(error: unknown): boolean {
+ if (error instanceof Error) {
+ return error.message.includes('Failed to fetch dynamically imported module');
+ }
+ if (typeof error === 'string') {
+ return error.includes('Failed to fetch dynamically imported module');
+ }
+ return false;
+}
+
/**
* Wrapper class for generalized text matching.
*