fix(ui): recover from corrupt chunk reload markers (#1230)
* fix(ui): recover from corrupt chunk reload markers * test(ui): clarify chunk recovery assertions --------- Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
committed by
GitHub
co-authored by
Isaac Sanchez
parent
eeabc304bd
commit
416a11dcce
@@ -0,0 +1,51 @@
|
|||||||
|
import { describe, expect, test } from 'bun:test';
|
||||||
|
|
||||||
|
import { importWithChunkRecovery } from './chunkLoadRecovery';
|
||||||
|
|
||||||
|
describe('importWithChunkRecovery', () => {
|
||||||
|
test('schedules recovery reload when stored reload marker is corrupt', async () => {
|
||||||
|
const globalWithWindow = globalThis as unknown as { window?: unknown };
|
||||||
|
const previousWindow = globalWithWindow.window;
|
||||||
|
let storedMarker: string | null = null;
|
||||||
|
let reloadCount = 0;
|
||||||
|
|
||||||
|
globalWithWindow.window = {
|
||||||
|
sessionStorage: {
|
||||||
|
getItem: () => '{not json',
|
||||||
|
setItem: (_key: string, value: string) => {
|
||||||
|
storedMarker = value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setTimeout: (callback: () => void) => {
|
||||||
|
callback();
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
reload: () => {
|
||||||
|
reloadCount += 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
let caught: unknown;
|
||||||
|
try {
|
||||||
|
await importWithChunkRecovery(async () => {
|
||||||
|
throw new Error('Failed to fetch dynamically imported module');
|
||||||
|
}, { retries: 0 });
|
||||||
|
} catch (error) {
|
||||||
|
caught = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(caught).toBeInstanceOf(Error);
|
||||||
|
expect(storedMarker).not.toBeNull();
|
||||||
|
expect(reloadCount).toBe(1);
|
||||||
|
} finally {
|
||||||
|
if (previousWindow === undefined) {
|
||||||
|
delete globalWithWindow.window;
|
||||||
|
} else {
|
||||||
|
globalWithWindow.window = previousWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -46,13 +46,26 @@ function scheduleReloadOnce(error: unknown): void {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const signature = reloadMarkerSignature(error);
|
const signature = reloadMarkerSignature(error);
|
||||||
|
|
||||||
|
let marker: { signature?: unknown; timestamp?: unknown } | null = null;
|
||||||
try {
|
try {
|
||||||
const rawMarker = window.sessionStorage.getItem(RELOAD_STORAGE_KEY);
|
const rawMarker = window.sessionStorage.getItem(RELOAD_STORAGE_KEY);
|
||||||
const marker = rawMarker ? JSON.parse(rawMarker) as { signature?: unknown; timestamp?: unknown } : null;
|
if (rawMarker) {
|
||||||
const markerTimestamp = typeof marker?.timestamp === 'number' ? marker.timestamp : 0;
|
try {
|
||||||
if (marker?.signature === signature && now - markerTimestamp < RELOAD_GUARD_MS) {
|
marker = JSON.parse(rawMarker) as { signature?: unknown; timestamp?: unknown };
|
||||||
return;
|
} catch {
|
||||||
|
marker = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const markerTimestamp = typeof marker?.timestamp === 'number' ? marker.timestamp : 0;
|
||||||
|
if (marker?.signature === signature && now - markerTimestamp < RELOAD_GUARD_MS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
window.sessionStorage.setItem(RELOAD_STORAGE_KEY, JSON.stringify({ signature, timestamp: now }));
|
window.sessionStorage.setItem(RELOAD_STORAGE_KEY, JSON.stringify({ signature, timestamp: now }));
|
||||||
} catch {
|
} catch {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Vendored
+2
@@ -15,10 +15,12 @@ declare module "bun:test" {
|
|||||||
toBeGreaterThan(expected: number): void;
|
toBeGreaterThan(expected: number): void;
|
||||||
toBeLessThan(expected: number): void;
|
toBeLessThan(expected: number): void;
|
||||||
toHaveLength(expected: number): void;
|
toHaveLength(expected: number): void;
|
||||||
|
toBeInstanceOf(expected: unknown): void;
|
||||||
not: {
|
not: {
|
||||||
toEqual(expected: unknown): void;
|
toEqual(expected: unknown): void;
|
||||||
toBe(expected: unknown): void;
|
toBe(expected: unknown): void;
|
||||||
toContain(expected: unknown): void;
|
toContain(expected: unknown): void;
|
||||||
|
toBeNull(): void;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export function beforeEach(fn: () => void | Promise<void>): void;
|
export function beforeEach(fn: () => void | Promise<void>): void;
|
||||||
|
|||||||
Reference in New Issue
Block a user