Conversation
- Bump cache version from v1 to v2 - Add cleanup logic to remove stale WASM files during activation - Create ASSET_URLS set for efficient lookup of valid cached assets
- Change start_url from '/' to './' - Change favicon src from '/favicon.svg' to './favicon.svg'
- Filter and sort WASM files by modification time - Select newest file when multiple WASM files exist in lib directory
- Wrap WASM loading in try-catch block - Display user-friendly error message when WASM fails to load
There was a problem hiding this comment.
Pull request overview
This PR fixes offline functionality for the SL web page PWA by addressing service worker registration failures and implementing proper cache management.
Changes:
- Service worker improvements: Bumped cache version to v2, added cleanup logic to remove stale WASM files while preserving current assets
- GitHub Pages compatibility: Changed manifest.json paths from absolute (
/) to relative (./) for proper PWA installation - Build process enhancement: Added logic to select the newest WASM file by modification time when multiple artifacts exist
- Error handling: Added try-catch wrapper around WASM loading with user-friendly error messages
- Code formatting: Applied prettier formatting to JSX components across multiple files
Reviewed changes
Copilot reviewed 6 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/page/src/service-worker.ts | Bumped cache version to v2, added ASSET_URLS set for efficient lookup, implemented cleanup logic to remove stale WASM files during activation |
| apps/page/manifest.json | Changed start_url and icon src from absolute to relative paths for GitHub Pages compatibility |
| apps/page/build.ts | Added logic to select newest WASM file by mtime when multiple files exist, preventing stale artifact selection |
| apps/page/src/components/slTerminal.tsx | Wrapped WASM loading in try-catch block with user-friendly error message display, applied prettier formatting |
| apps/page/src/components/terminal.tsx | Applied prettier formatting to JSX attributes |
| apps/page/src/components/home/Installation.tsx | Applied prettier formatting to JSX attributes |
| apps/page/src/components/home/Home.tsx | Applied prettier formatting to JSX attributes |
| apps/page/src/components/home/Form.tsx | Applied prettier formatting to JSX attributes |
| const stats = await Promise.all( | ||
| wasmFiles.map(async (file) => { | ||
| const stat = await Bun.file(path.join(libDir, file)).stat(); | ||
| return { file, mtime: stat?.mtime ?? 0 }; | ||
| }) | ||
| ); | ||
| stats.sort( | ||
| (a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0) | ||
| ); |
There was a problem hiding this comment.
The mtime fallback value (0) could result in unexpected behavior. When stat.mtime is undefined or null, using 0 as a fallback means the file will be treated as created at Unix epoch (January 1, 1970). This could cause a stale file to be selected if its mtime fails to be read while newer files have valid timestamps.
Consider handling the error case explicitly by either:
- Filtering out files where stat is null/undefined before sorting
- Using a more defensive approach like throwing an error if stat is unavailable
- At minimum, logging a warning when mtime cannot be determined
| const stats = await Promise.all( | |
| wasmFiles.map(async (file) => { | |
| const stat = await Bun.file(path.join(libDir, file)).stat(); | |
| return { file, mtime: stat?.mtime ?? 0 }; | |
| }) | |
| ); | |
| stats.sort( | |
| (a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0) | |
| ); | |
| const stats = ( | |
| await Promise.all( | |
| wasmFiles.map(async (file) => { | |
| const stat = await Bun.file(path.join(libDir, file)).stat(); | |
| if (!stat || !stat.mtime) { | |
| console.warn(`Warning: could not determine mtime for WASM file "${file}", skipping it.`); | |
| return null; | |
| } | |
| return { file, mtime: stat.mtime }; | |
| }) | |
| ) | |
| ).filter( | |
| (entry): entry is { file: string; mtime: Date } => entry !== null | |
| ); | |
| if (stats.length === 0) { | |
| console.error('No WASM file with a valid modification time found in lib directory'); | |
| process.exit(1); | |
| } | |
| stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime()); |
| return { file, mtime: stat?.mtime ?? 0 }; | ||
| }) | ||
| ); | ||
| stats.sort( | ||
| (a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0) | ||
| ); | ||
| wasmFile = stats[0].file; | ||
| console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`); |
There was a problem hiding this comment.
The type check for mtime in the sort comparator is inconsistent with the fallback value. Line 56 uses stat?.mtime ?? 0 where 0 is a number, but line 60 checks instanceof Date and falls back to 0 again. If mtime is a Date object, the code works correctly. However, if stat is null/undefined and the fallback is 0 (a number), then the instanceof check fails and it falls back to 0 again, which works coincidentally.
This creates confusion about the expected type of mtime. Consider either:
- Using consistent type handling - if mtime should be a Date, handle the null case by creating a Date(0) instead of using raw 0
- Adding a type guard or assertion to clarify the expected type
- Simplifying the logic to handle numbers uniformly if that's the intended type
| return { file, mtime: stat?.mtime ?? 0 }; | |
| }) | |
| ); | |
| stats.sort( | |
| (a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0) | |
| ); | |
| wasmFile = stats[0].file; | |
| console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`); | |
| const mtime = stat?.mtime instanceof Date ? stat.mtime : new Date(0); | |
| return { file, mtime }; | |
| }) | |
| ); | |
| stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime()); | |
| wasmFile = stats[0].file; | |
| console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`); | |
| console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`); |
Summary
Fix offline mode for the SL web page PWA.
Problem
The service worker was failing to register, preventing the page from working offline. Additionally, stale WASM files could accumulate in the cache.
Changes
Testing
Verified using Playwright:
sl-page-v2Before fix: Service worker failed to register with script evaluation error, offline navigation failed with ERR_INTERNET_DISCONNECTED.